Skip to content

Commit 4adfd20

Browse files
Merge branch 'develop' of github.com:ProcessMaker/processmaker into observation/FOUR-26310
2 parents 8336f3e + 972c352 commit 4adfd20

38 files changed

+58957
-423
lines changed

ProcessMaker/Application.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace ProcessMaker;
44

55
use Igaster\LaravelTheme\Facades\Theme;
6+
use Illuminate\Filesystem\Filesystem;
67
use Illuminate\Foundation\Application as IlluminateApplication;
8+
use Illuminate\Foundation\PackageManifest;
79
use Illuminate\Support\Facades\Auth;
810

911
/**
@@ -78,13 +80,14 @@ public function path($path = '')
7880
return $this->basePath . DIRECTORY_SEPARATOR . 'ProcessMaker' . ($path ? DIRECTORY_SEPARATOR . $path : $path);
7981
}
8082

81-
public function setStoragePath($path)
83+
public function registerConfiguredProviders()
8284
{
83-
$this->storagePath = $path;
84-
}
85+
// Must be rebound before registerConfiguredProviders() runs but after bootstrapping is done
86+
// so we can access storage and cache facades.
87+
$this->singleton(PackageManifest::class, fn () => new LicensedPackageManifest(
88+
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
89+
));
8590

86-
public function getStoragePath()
87-
{
88-
return $this->storagePath;
91+
parent::registerConfiguredProviders();
8992
}
9093
}

ProcessMaker/Console/Commands/SyncTranslations.php

Lines changed: 104 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
use Illuminate\Console\Command;
66
use Illuminate\Support\Arr;
7-
use Illuminate\Support\Facades\File;
7+
use ProcessMaker\Helpers\SyncJsonTranslations;
8+
use ProcessMaker\Helpers\SyncPhpTranslations;
9+
use Symfony\Component\Console\Helper\Table;
10+
use Symfony\Component\Console\Helper\TableSeparator;
811

912
class SyncTranslations extends Command
1013
{
@@ -31,231 +34,133 @@ class SyncTranslations extends Command
3134
*/
3235
public function handle()
3336
{
34-
// Check if exists resources core
35-
$translationsCore = base_path() . '/resources-core';
36-
$existsLangOrig = $this->fileExists(lang_path() . '.orig');
37+
$this->info('Starting translation synchronization...');
38+
$this->newLine();
3739

38-
if (!$this->fileExists($translationsCore)) {
39-
$this->error('The folder resources-core not exists.');
40+
$jsonResults = (new SyncJsonTranslations())->sync();
41+
// dump("JSON Results: ", $jsonResults);
42+
$phpResults = (new SyncPhpTranslations())->sync();
4043

41-
return;
42-
}
43-
//Search files
44-
$this->listFiles($translationsCore . '/lang');
45-
46-
// updating languages by default
47-
foreach ($this->files as $pathFile) {
48-
if (!(str_contains($pathFile, '.json') || str_contains($pathFile, '.php')) || str_contains($pathFile, '.bak.')) {
49-
continue;
50-
}
51-
// updating resources/lang
52-
$this->syncFile(str_replace('/resources-core/', '/resources/', $pathFile), $pathFile);
53-
if ($existsLangOrig) {
54-
// updating resources/lang.orig
55-
$this->syncFile(str_replace(['/resources-core/', '/lang/'], ['/resources/', '/lang.orig/'], $pathFile), $pathFile);
56-
}
57-
}
44+
$this->displayJsonResults($jsonResults);
45+
$this->newLine();
46+
$this->displayPhpResults($phpResults);
5847

59-
// updating all languages with new labels
60-
$this->files = [];
61-
$translationsCore = lang_path();
62-
$this->listFiles($translationsCore);
63-
foreach ($this->files as $pathFile) {
64-
if (!(str_contains($pathFile, '.json') || str_contains($pathFile, '.php')) || str_contains($pathFile, '.bak.')) {
65-
continue;
66-
}
67-
// updating resources/lang
68-
$backup = str_replace('/resources/', '/resources-core/', preg_replace('/(?<=lang).+?(?=json)/', '/en.', $pathFile));
69-
$path1 = explode('/lang/', $backup);
70-
$path2 = explode('/', $path1[1]);
71-
if (is_array($path2)) {
72-
$backup = str_replace('/' . $path2[0] . '/', '/en/', $backup);
73-
}
48+
$this->newLine();
49+
$this->info('Translation synchronization completed!');
7450

75-
$this->syncFile($pathFile, $backup);
76-
if ($existsLangOrig) {
77-
// updating resources/lang.orig
78-
$this->syncFile(str_replace('/lang/', '/lang.orig/', $pathFile), $backup);
79-
}
80-
}
51+
return 0;
8152
}
8253

83-
private function listFiles($dir)
54+
/**
55+
* Display JSON translation results in a table format
56+
*
57+
* @param array $results
58+
* @return void
59+
*/
60+
private function displayJsonResults(array $results): void
8461
{
85-
$files = scandir($dir);
62+
$this->info('JSON Translation Files:');
8663

87-
foreach ($files as $value) {
88-
$path = $dir . '/' . $value;
89-
if (!is_dir($path)) {
90-
$this->files[] = $path;
91-
} elseif ($value != '.' && $value != '..') {
92-
$this->listFiles($path);
64+
$table = new Table($this->output);
65+
$table->setHeaders(['Language', 'File', 'Action', 'New Keys', 'Total Keys', 'Backup', 'Status']);
66+
$additionalChanges = Arr::get($results, 'en.otherLanguageResults');
67+
68+
foreach ($results as $language => $result) {
69+
if (isset($additionalChanges[$language])) {
70+
if ($result['action'] !== 'no_changes') {
71+
$this->addRowToTable($table, $result, $language);
72+
}
73+
$this->addRowToTable($table, $additionalChanges[$language], $language);
74+
} else {
75+
$this->addRowToTable($table, $result, $language);
9376
}
9477
}
95-
}
9678

97-
private function fileExists($path)
98-
{
99-
return File::exists($path);
79+
$table->render();
10080
}
10181

102-
private function parseFile($path)
82+
private function addRowToTable(Table $table, array $result, string $language)
10383
{
104-
$pathInfo = pathinfo($path);
105-
106-
$lines = [];
107-
108-
try {
109-
if ($pathInfo['extension'] === 'json') {
110-
$lines = json_decode(file_get_contents($path), true);
111-
} elseif ($pathInfo['extension'] === 'php') {
112-
$lines = include $path;
113-
}
114-
} catch (\Exception $e) {
115-
$lines = [];
116-
$this->error($path . '. Not found.');
84+
$status = $result['error'] ? '<error>Error</error>' : '<info>Success</info>';
85+
$action = $this->formatAction($result['action']);
86+
$backup = $result['backup_created'] ? '<comment>Yes</comment>' : 'No';
87+
88+
$table->addRow([
89+
$language,
90+
$result['filename'],
91+
$action,
92+
$result['new_keys'],
93+
$result['total_keys'],
94+
$backup,
95+
$status,
96+
]);
97+
98+
if ($result['error']) {
99+
$table->addRow(new TableSeparator());
100+
$table->addRow(['', '', '', '', '', '', '<error>' . $result['error'] . '</error>']);
117101
}
118-
119-
$lines = Arr::dot($lines);
120-
121-
return collect($lines);
122102
}
123103

124104
/**
125-
* Synchronize translations between target and backup files
105+
* Display PHP translation results in a table format
126106
*
127-
* @param string $target Path to target file
128-
* @param string $backup Path to backup file
129-
* @return bool
130-
* @throws \Exception
107+
* @param array $results
108+
* @return void
131109
*/
132-
private function syncFile($target, $backup)
110+
private function displayPhpResults(array $results): void
133111
{
134-
if (str_contains($target, '.bak.')) {
135-
// Clean up old backup if everything succeeded
136-
if (file_exists($target)) {
137-
unlink($target);
138-
$this->info('Removed bak: ' . $target);
139-
}
140-
$this->info("Skipping backup file: {$target}");
141-
142-
return true;
143-
}
144-
// Create backup before modifications
145-
$backupPath = $target . '.bak.' . date('Y-m-d-His');
146-
try {
147-
if (!copy($target, $backupPath)) {
148-
$this->error("Failed to create backup file: {$backupPath}");
149-
150-
return false;
112+
$this->info('PHP Translation Files:');
113+
114+
$table = new Table($this->output);
115+
$table->setHeaders(['Language', 'Files Processed', 'Copied', 'Merged', 'No Changes', 'Errors', 'Status']);
116+
117+
foreach ($results as $language => $result) {
118+
$errorCount = count($result['errors']);
119+
$status = $errorCount > 0 ? '<error>Errors</error>' : '<info>Success</info>';
120+
121+
$table->addRow([
122+
$language,
123+
$result['files_processed'],
124+
$result['files_copied'],
125+
$result['files_merged'],
126+
$result['files_no_changes'],
127+
$errorCount,
128+
$status,
129+
]);
130+
131+
// Add error details if any
132+
if ($errorCount > 0) {
133+
$table->addRow(new TableSeparator());
134+
foreach ($result['errors'] as $error) {
135+
$table->addRow(['', '', '', '', '', '', '<error>' . $error . '</error>']);
136+
}
151137
}
152-
$this->info("Backup created: {$backupPath}");
153-
} catch (\Exception $e) {
154-
$this->error('Error creating backup: ' . $e->getMessage());
155-
156-
return false;
157-
}
158-
159-
$pathInfo = pathinfo($target);
160-
161-
try {
162-
$targetTranslations = $this->parseFile($target);
163-
$origin = $this->parseFile($backup);
164-
} catch (\Exception $e) {
165-
$this->error('Error parsing files: ' . $e->getMessage());
166-
167-
return false;
168138
}
169139

170-
// Get keys that are in origin but not in target
171-
$diff = $origin->diffKeys($targetTranslations);
172-
173-
if ($diff->isNotEmpty()) {
174-
$this->info('Found ' . $diff->count() . " new translations to add in {$target}");
175-
176-
// only files en.json to en.json have translations others are empty
177-
$clear = true;
178-
if (str_contains($target, 'en.json') && str_contains($backup, 'en.json')) {
179-
$clear = false;
180-
}
181-
182-
// Add new keys to targetTranslations
183-
foreach ($diff as $key => $value) {
184-
$targetTranslations[$key] = $clear ? '' : $value;
185-
}
186-
}
187-
188-
try {
189-
$contents = $this->generateFile($targetTranslations, $pathInfo['extension']);
190-
191-
// Validate content before saving
192-
if (empty($contents)) {
193-
throw new \Exception('Generated content is empty');
194-
}
195-
196-
// Use atomic file writing
197-
$tempFile = $target . '.tmp';
198-
if (file_put_contents($tempFile, $contents) === false) {
199-
throw new \Exception('Failed to write temporary file');
200-
}
201-
202-
if (!rename($tempFile, $target)) {
203-
unlink($tempFile);
204-
throw new \Exception('Failed to move temporary file');
205-
}
206-
207-
$this->info("Successfully updated: {$target}");
208-
209-
// Clean up old backup if everything succeeded
210-
if (file_exists($backupPath)) {
211-
unlink($backupPath);
212-
$this->info('Removed backup file after successful update');
213-
}
214-
215-
if ($pathInfo['extension'] == 'php') {
216-
$this->clearCache();
217-
}
218-
219-
return true;
220-
} catch (\Exception $e) {
221-
// Restore from backup if something went wrong
222-
if (file_exists($backupPath)) {
223-
copy($backupPath, $target);
224-
$this->info('Restored from backup due to error');
225-
}
226-
$this->error('Error saving file: ' . $e->getMessage());
227-
228-
return false;
229-
}
140+
$table->render();
230141
}
231142

232-
private function generateFile($lines, $type)
233-
{
234-
$array = [];
235-
236-
foreach ($lines as $key => $line) {
237-
$array[$key] = $line;
238-
}
239-
240-
if ($type === 'json') {
241-
return json_encode($array, JSON_PRETTY_PRINT);
242-
} elseif ($type === 'php') {
243-
$contents = "<?php\n\nreturn [\n\n";
244-
foreach ($array as $key => $value) {
245-
$key = addslashes($key);
246-
$value = addslashes($value);
247-
$contents .= "\t'$key' => '$value',\n";
248-
}
249-
$contents .= '];';
250-
251-
return $contents;
252-
}
253-
}
254-
255-
private function clearCache()
143+
/**
144+
* Format action for display
145+
*
146+
* @param string $action
147+
* @return string
148+
*/
149+
private function formatAction(string $action): string
256150
{
257-
if (function_exists('opcache_reset')) {
258-
return opcache_reset();
151+
switch ($action) {
152+
case 'copied':
153+
return '<comment>Copied</comment>';
154+
case 'merged':
155+
return '<info>Merged</info>';
156+
case 'updated':
157+
return '<fg=cyan>Updated</fg=cyan>';
158+
case 'no_changes':
159+
return '<fg=gray>No Changes</fg=gray>';
160+
case 'error':
161+
return '<error>Error</error>';
162+
default:
163+
return $action;
259164
}
260165
}
261166
}

0 commit comments

Comments
 (0)