Skip to content

Commit b642b2c

Browse files
committed
Merge branch 'release/1.7.49.3'
2 parents 1a9a601 + ae147fa commit b642b2c

File tree

7 files changed

+146
-101
lines changed

7 files changed

+146
-101
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# v1.7.49.3
2+
## 09/02/2025
3+
4+
1. [](#bugfix)
5+
* Fixed an error in ZipArchive that was causing issues on some systems
6+
* Fixed namespace change for `Cron\Expression`
7+
* Removed broken cron install field... use 'instructions' instead
8+
* Fixed duplicate jobs listing in some CLI commands
9+
110
# v1.7.49.2
211
## 08/28/2025
312

system/blueprints/config/scheduler.yaml

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -324,68 +324,6 @@ form:
324324
</script>
325325
markdown: false
326326

327-
cron_setup:
328-
type: display
329-
label: Cron Setup Commands
330-
content: |
331-
<script src="{{ url('plugin://admin/themes/grav/js/clipboard-helper.js') }}"></script>
332-
<div class="cron-setup-commands">
333-
<script>
334-
335-
(function() {
336-
// Get the Grav root path from the current location
337-
var pathParts = window.location.pathname.split('/');
338-
var gravPath = '/path/to/grav'; // Default fallback
339-
340-
// Try to determine actual path (assuming we're in /admin or similar)
341-
if (typeof GravAdmin !== 'undefined' && GravAdmin.config && GravAdmin.config.base_url_relative) {
342-
// Remove admin path to get to root
343-
gravPath = window.location.pathname.replace(/\/admin.*$/, '') || '/';
344-
if (gravPath === '/') {
345-
gravPath = '/var/www/html'; // Common server path
346-
}
347-
}
348-
349-
// For local development, use the actual path
350-
if (window.location.hostname === 'trilby.local') {
351-
gravPath = '/Users/rhuk/workspace/trilby/grav-editor-pro';
352-
}
353-
354-
var quickInstallCmd = "(crontab -l 2>/dev/null; echo '* * * * * cd " + gravPath + " && bin/grav scheduler 1>> /dev/null 2>&1') | crontab -";
355-
var manualEntryCmd = "* * * * * cd " + gravPath + " && bin/grav scheduler 1>> /dev/null 2>&1";
356-
357-
document.addEventListener('DOMContentLoaded', function() {
358-
var quickInput = document.getElementById('cron-quick-install');
359-
var manualInput = document.getElementById('cron-manual-entry');
360-
361-
if (quickInput) quickInput.value = quickInstallCmd;
362-
if (manualInput) manualInput.value = manualEntryCmd;
363-
});
364-
})();
365-
</script>
366-
367-
<div style="margin-bottom: 1rem;">
368-
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Quick Install (adds to existing crontab):</label>
369-
<div class="form-input-wrapper form-input-addon-wrapper">
370-
<input type="text" id="cron-quick-install" readonly value="Loading..." style="font-family: monospace; background: #f5f5f5;">
371-
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
372-
</div>
373-
</div>
374-
375-
<div style="margin-bottom: 1rem;">
376-
<label style="display: block; margin-bottom: 0.25rem; font-weight: 500;">Manual Entry (add to crontab -e):</label>
377-
<div class="form-input-wrapper form-input-addon-wrapper">
378-
<input type="text" id="cron-manual-entry" readonly value="Loading..." style="font-family: monospace; background: #f5f5f5;">
379-
<div class="form-input-addon form-input-append" style="cursor: pointer;" onclick="GravClipboard.copy(this)"><i class="fa fa-copy"></i> Copy</div>
380-
</div>
381-
</div>
382-
383-
<div class="alert alert-info" style="margin-top: 0.5rem;">
384-
<i class="fa fa-info-circle"></i> <strong>Note:</strong> These commands will run the scheduler every minute. Adjust the path if needed before copying.
385-
</div>
386-
</div>
387-
markdown: false
388-
389327
trigger_methods:
390328
type: display
391329
label: Active Triggers

system/defines.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
// Some standard defines
1111
define('GRAV', true);
12-
define('GRAV_VERSION', '1.7.49.2');
12+
define('GRAV_VERSION', '1.7.49.3');
1313
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
1414
define('GRAV_TESTING', false);
1515

system/src/Grav/Common/Filesystem/ZipArchiver.php

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,21 @@ public function compress($source, callable $status = null)
6464
}
6565

6666
$zip = new ZipArchive();
67-
if (!$zip->open($this->archive_file, ZipArchive::CREATE)) {
68-
throw new InvalidArgumentException('ZipArchiver:' . $this->archive_file . ' cannot be created...');
67+
$result = $zip->open($this->archive_file, ZipArchive::CREATE);
68+
if ($result !== true) {
69+
$error = 'unknown error';
70+
if ($result === ZipArchive::ER_NOENT) {
71+
$error = 'file does not exist';
72+
} elseif ($result === ZipArchive::ER_EXISTS) {
73+
$error = 'file already exists';
74+
} elseif ($result === ZipArchive::ER_OPEN) {
75+
$error = 'cannot open file';
76+
} elseif ($result === ZipArchive::ER_READ) {
77+
$error = 'read error';
78+
} elseif ($result === ZipArchive::ER_SEEK) {
79+
$error = 'seek error';
80+
}
81+
throw new InvalidArgumentException('ZipArchiver: ' . $this->archive_file . ' cannot be created: ' . $error);
6982
}
7083

7184
$files = $this->getArchiveFiles($rootPath);
@@ -112,8 +125,21 @@ public function addEmptyFolders($folders, callable $status = null)
112125
}
113126

114127
$zip = new ZipArchive();
115-
if (!$zip->open($this->archive_file)) {
116-
throw new InvalidArgumentException('ZipArchiver: ' . $this->archive_file . ' cannot be opened...');
128+
$result = $zip->open($this->archive_file);
129+
if ($result !== true) {
130+
$error = 'unknown error';
131+
if ($result === ZipArchive::ER_NOENT) {
132+
$error = 'file does not exist';
133+
} elseif ($result === ZipArchive::ER_EXISTS) {
134+
$error = 'file already exists';
135+
} elseif ($result === ZipArchive::ER_OPEN) {
136+
$error = 'cannot open file';
137+
} elseif ($result === ZipArchive::ER_READ) {
138+
$error = 'read error';
139+
} elseif ($result === ZipArchive::ER_SEEK) {
140+
$error = 'seek error';
141+
}
142+
throw new InvalidArgumentException('ZipArchiver: ' . $this->archive_file . ' cannot be opened: ' . $error);
117143
}
118144

119145
$status && $status([
@@ -122,7 +148,12 @@ public function addEmptyFolders($folders, callable $status = null)
122148
]);
123149

124150
foreach ($folders as $folder) {
125-
$zip->addEmptyDir($folder);
151+
if ($zip->addEmptyDir($folder) === false) {
152+
$status && $status([
153+
'type' => 'message',
154+
'message' => 'Warning: Could not add empty directory: ' . $folder
155+
]);
156+
}
126157
$status && $status([
127158
'type' => 'progress',
128159
]);

system/src/Grav/Common/Scheduler/Job.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace Grav\Common\Scheduler;
1111

1212
use Closure;
13-
use Dragonmantank\Cron\CronExpression;
13+
use Cron\CronExpression;
1414
use DateTime;
1515
use Grav\Common\Grav;
1616
use InvalidArgumentException;

system/src/Grav/Common/Scheduler/Scheduler.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,33 @@ public function __construct()
112112
*/
113113
public function loadSavedJobs()
114114
{
115+
// Only load saved jobs if they haven't been loaded yet
116+
if (!empty($this->saved_jobs)) {
117+
return $this;
118+
}
119+
115120
$this->saved_jobs = [];
116121
$saved_jobs = (array) Grav::instance()['config']->get('scheduler.custom_jobs', []);
117122

118123
foreach ($saved_jobs as $id => $j) {
119124
$args = $j['args'] ?? [];
120125
$id = Grav::instance()['inflector']->hyphenize($id);
126+
127+
// Check if job already exists to prevent duplicates
128+
$existingJob = null;
129+
foreach ($this->jobs as $existingJobItem) {
130+
if ($existingJobItem->getId() === $id) {
131+
$existingJob = $existingJobItem;
132+
break;
133+
}
134+
}
135+
136+
if ($existingJob) {
137+
// Job already exists, just update saved_jobs reference
138+
$this->saved_jobs[] = $existingJob;
139+
continue;
140+
}
141+
121142
$job = $this->addCommand($j['command'], $args, $id);
122143

123144
if (isset($j['at'])) {

system/src/Grav/Console/Cli/SchedulerCommand.php

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Grav\Console\Cli;
1111

12-
use Dragonmantank\Cron\CronExpression;
12+
use Cron\CronExpression;
1313
use Grav\Common\Grav;
1414
use Grav\Common\Utils;
1515
use Grav\Common\Scheduler\Scheduler;
@@ -82,8 +82,75 @@ protected function serve(): int
8282
$error = 0;
8383

8484
$run = $input->getOption('run');
85+
$showDetails = $input->getOption('details');
86+
$showJobs = $input->getOption('jobs');
87+
88+
// Handle running jobs first if -r flag is present
89+
if ($run !== false) {
90+
if ($run === null || $run === '') {
91+
// Run all jobs when -r is provided without a specific job ID
92+
$io->title('Force Run All Jobs');
93+
94+
$jobs = $scheduler->getAllJobs();
95+
$hasOutput = false;
96+
97+
foreach ($jobs as $job) {
98+
if ($job->getEnabled()) {
99+
$io->section('Running: ' . $job->getId());
100+
$job->inForeground()->run();
101+
102+
if ($job->isSuccessful()) {
103+
$io->success('Job ' . $job->getId() . ' ran successfully');
104+
} else {
105+
$error = 1;
106+
$io->error('Job ' . $job->getId() . ' failed to run');
107+
}
108+
109+
$output = $job->getOutput();
110+
if ($output) {
111+
$io->write($output);
112+
$hasOutput = true;
113+
}
114+
}
115+
}
116+
117+
if (!$hasOutput) {
118+
$io->note('All enabled jobs completed');
119+
}
120+
} else {
121+
// Run specific job
122+
$io->title('Force Run Job: ' . $run);
123+
124+
$job = $scheduler->getJob($run);
125+
126+
if ($job) {
127+
$job->inForeground()->run();
128+
129+
if ($job->isSuccessful()) {
130+
$io->success('Job ran successfully...');
131+
} else {
132+
$error = 1;
133+
$io->error('Job failed to run successfully...');
134+
}
135+
136+
$output = $job->getOutput();
137+
138+
if ($output) {
139+
$io->write($output);
140+
}
141+
} else {
142+
$error = 1;
143+
$io->error('Could not find a job with id: ' . $run);
144+
}
145+
}
146+
147+
// Add separator if we're going to show details after
148+
if ($showDetails) {
149+
$io->newLine();
150+
}
151+
}
85152

86-
if ($input->getOption('jobs')) {
153+
if ($showJobs) {
87154
// Show jobs list
88155

89156
$jobs = $scheduler->getAllJobs();
@@ -124,7 +191,9 @@ protected function serve(): int
124191
$io->newLine();
125192
$io->note('For error details run "bin/grav scheduler -d"');
126193
$io->newLine();
127-
} elseif ($input->getOption('details')) {
194+
}
195+
196+
if ($showDetails) {
128197
$jobs = $scheduler->getAllJobs();
129198
$job_states = (array)$scheduler->getJobStates()->content();
130199

@@ -162,31 +231,9 @@ protected function serve(): int
162231

163232
$table->setRows($rows);
164233
$table->render();
165-
} elseif ($run !== false && $run !== null) {
166-
$io->title('Force Run Job: ' . $run);
167-
168-
$job = $scheduler->getJob($run);
169-
170-
if ($job) {
171-
$job->inForeground()->run();
172-
173-
if ($job->isSuccessful()) {
174-
$io->success('Job ran successfully...');
175-
} else {
176-
$error = 1;
177-
$io->error('Job failed to run successfully...');
178-
}
179-
180-
$output = $job->getOutput();
181-
182-
if ($output) {
183-
$io->write($output);
184-
}
185-
} else {
186-
$error = 1;
187-
$io->error('Could not find a job with id: ' . $run);
188-
}
189-
} elseif ($input->getOption('install')) {
234+
}
235+
236+
if ($input->getOption('install')) {
190237
$io->title('Install Scheduler');
191238

192239
$verb = 'install';
@@ -207,10 +254,9 @@ protected function serve(): int
207254
$io->note("To $verb, create a scheduled task in Windows.");
208255
$io->text('Learn more at https://learn.getgrav.org/advanced/scheduler');
209256
}
210-
} else {
211-
// Run scheduler
212-
$force = $run === null;
213-
$scheduler->run(null, $force);
257+
} elseif (!$showJobs && !$showDetails && $run === false) {
258+
// Run scheduler only if no other options were provided
259+
$scheduler->run(null, true);
214260

215261
if ($input->getOption('verbose')) {
216262
$io->title('Running Scheduled Jobs');

0 commit comments

Comments
 (0)