Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
All notable changes to this project will be documented in this file.

## [unreleased]
### Fixed
- Changed sql query to be ansi safe

### Added
- Show locale date as tooltip for relative date time information
- Add popover to counter to show voters by answer
- Add forced display mode to poll configuration

### Changed
- Updated job control for the administration
- Refactored some elements flex to grid
- Rearranged Job control in admin settings

## [8.5.0] - 2025-10-03
### Fixed
Expand Down
32 changes: 1 addition & 31 deletions lib/Controller/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
namespace OCA\Polls\Controller;

use OCA\Polls\AppConstants;
use OCA\Polls\Cron\AutoReminderCron;
use OCA\Polls\Cron\JanitorCron;
use OCA\Polls\Cron\NotificationCron;
use OCA\Polls\Service\PollService;
use OCA\Polls\Service\SystemService;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
Expand All @@ -36,9 +33,6 @@ public function __construct(
private IURLGenerator $urlGenerator,
private PollService $pollService,
private IEventDispatcher $eventDispatcher,
private AutoReminderCron $autoReminderCron,
private JanitorCron $janitorCron,
private NotificationCron $notificationCron,
private SystemService $systemService,
) {
parent::__construct($appName, $request);
Expand Down Expand Up @@ -71,7 +65,7 @@ public function getJobs(): JSONResponse {
*/
#[FrontpageRoute(verb: 'POST', url: '/administration/job/run')]
public function runJob($job): JSONResponse {
return $this->response(fn () => $this->systemService->runJob($job));
return $this->response(fn () => ['job' => $this->systemService->runJob($job)]);
}

/**
Expand All @@ -92,28 +86,4 @@ public function takeover(int $pollId): JSONResponse {
'poll' => $this->pollService->takeover($pollId)
]);
}

/**
* Run auto reminder job
*/
#[FrontpageRoute(verb: 'GET', url: '/administration/autoreminder/run')]
public function runAutoReminderJob(): JSONResponse {
return $this->response(fn () => $this->autoReminderCron->manuallyRun());
}

/**
* Run janitor job
*/
#[FrontpageRoute(verb: 'GET', url: '/administration/janitor/run')]
public function runJanitorJob(): JSONResponse {
return $this->response(fn () => $this->janitorCron->manuallyRun());
}

/**
* Run notification job
*/
#[FrontpageRoute(verb: 'GET', url: '/administration/notification/run')]
public function runNotificationJob(): JSONResponse {
return $this->response(fn () => $this->notificationCron->manuallyRun());
}
}
12 changes: 4 additions & 8 deletions lib/Cron/AutoReminderCron.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,24 @@
use OCA\Polls\Attributes\ManuallyRunnableCronJob;
use OCA\Polls\Service\MailService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\ISession;
use Psr\Log\LoggerInterface;

/**
* @psalm-api
*/
#[ManuallyRunnableCronJob]
class AutoReminderCron extends TimedCronJob {
class AutoReminderCron extends TimedJob {
public function __construct(
protected ITimeFactory $time,
protected LoggerInterface $logger,
private MailService $mailService,
private ISession $session,
protected bool $supportsManualRun = true,
) {
parent::setInterval(30); // run every 30 minutes
parent::__construct($time);
parent::setInterval(300); // run every 5 minutes
}

/**
Expand All @@ -45,10 +47,4 @@ protected function run($argument) {
$this->session->remove(AppConstants::SESSION_KEY_CRON_JOB);
}
}

public function manuallyRun(): string {
$this->logger->Info('AutoReminderCron will manually run.');
$this->run(null);
return 'AutoReminderCron was manually executed.';
}
}
14 changes: 6 additions & 8 deletions lib/Cron/JanitorCron.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@
use OCA\Polls\Db\V5\TableManager;
use OCA\Polls\Db\VoteMapper;
use OCA\Polls\Model\Settings\AppSettings;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\ISession;
use Psr\Log\LoggerInterface;

/**
* @psalm-api
*/
#[ManuallyRunnableCronJob]
class JanitorCron extends TimedCronJob {
class JanitorCron extends TimedJob {
// private AppSettings $appSettings;

public function __construct(
protected ITimeFactory $time,
protected LoggerInterface $logger,
private ISession $session,
private CommentMapper $commentMapper,
Expand All @@ -42,6 +45,8 @@ public function __construct(
private AppSettings $appSettings,
protected bool $supportsManualRun = true,
) {
parent::__construct($time);
parent::setInterval(86400); // run once a day
}

/**
Expand Down Expand Up @@ -125,11 +130,4 @@ protected function run($argument) {
$this->session->remove(AppConstants::SESSION_KEY_CRON_JOB);
}
}

public function manuallyRun(): string {
$this->logger->info('JanitorCron manually run.');
$this->run(null);
return 'JanitorCron manually run.';
}

}
13 changes: 5 additions & 8 deletions lib/Cron/NotificationCron.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,24 @@
use OCA\Polls\AppConstants;
use OCA\Polls\Attributes\ManuallyRunnableCronJob;
use OCA\Polls\Service\MailService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\ISession;
use Psr\Log\LoggerInterface;

/**
* @psalm-api
*/
#[ManuallyRunnableCronJob]
class NotificationCron extends TimedCronJob {
class NotificationCron extends TimedJob {
public function __construct(
protected ITimeFactory $time,
protected LoggerInterface $logger,
private ISession $session,
private MailService $mailService,
protected bool $supportsManualRun = true,
) {
// parent::__construct($time);
parent::__construct($time);
parent::setInterval(300); // run every 5 minutes
}

Expand All @@ -48,10 +51,4 @@ protected function run($argument) {
}

}

public function manuallyRun(): string {
$this->logger->info('NotificationCron manually run.');
$this->run(null);
return 'NotificationCron manually run.';
}
}
43 changes: 0 additions & 43 deletions lib/Cron/TimedCronJob.php

This file was deleted.

37 changes: 19 additions & 18 deletions lib/Service/SystemService.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Exception;
use OCA\Polls\AppConstants;
use OCA\Polls\Attributes\ManuallyRunnableCronJob;
use OCA\Polls\Cron\TimedCronJob;
use OCA\Polls\Db\Share;
use OCA\Polls\Db\ShareMapper;
use OCA\Polls\Db\UserMapper;
Expand All @@ -31,7 +30,6 @@
use OCP\Collaboration\Collaborators\ISearch;
use OCP\L10N\IFactory;
use OCP\Share\IShare;
use phpDocumentor\Reflection\Types\This;
use Psr\Log\LoggerInterface;

class SystemService {
Expand Down Expand Up @@ -271,31 +269,34 @@ public function validatePublicUsernameByToken(string $userName, string $token):
* @param class-string<IJob> $className The class name of the cron job to run
* @throws Exception
*/
public function runJob($className): string {
public function runJob($className): array {
$jobs = $this->jobList->getJobsIterator($className, null, 0);

foreach ($jobs as $job) {
$jobClassName = get_class($job);
$reflection_class = new \ReflectionClass($jobClassName);

if ($reflection_class->getNamespaceName() !== 'OCA\\Polls\\Cron') {
if (
$reflection_class->getNamespaceName() !== 'OCA\\Polls\\Cron'
|| $jobClassName !== $className
) {
continue;
}

if (get_class($job) === $className) {
/** @var TimedCronJob $job */
$job->setLastRun(time());
$this->logger->info('{job}: started manually.', ['job' => $reflection_class->getShortName()]);
$job->setLastRun(0);
$job->start($this->jobList);
$job->setLastRun(time());

if (
!empty($reflection_class->getAttributes(ManuallyRunnableCronJob::class))
&& $reflection_class->hasMethod('manuallyRun')
) {
return $job->manuallyRun();
} else {
$this->logger->error('Job {job} does not support manual execution', ['job' => $jobClassName]);
throw new Exception('Job does not support manual execution');
}
}
return [
'id' => $job->getId(),
'className' => get_class($job),
'lastRun' => $job->getLastRun(),
'argument' => $job->getArgument(),
'nameSpace' => $reflection_class->getNamespaceName(),
'name' => $reflection_class->getShortName(),
'manuallyRunnable' => !empty($reflection_class->getAttributes(ManuallyRunnableCronJob::class)),
];
}
throw new Exception('Job not found');
}
Expand All @@ -315,7 +316,7 @@ public function getCronJobs(): array {
continue;
}

$joblist[] = [
$joblist[$reflection_class->getShortName()] = [
'id' => $job->getId(),
'className' => get_class($job),
'lastRun' => $job->getLastRun(),
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"build": "vite --mode production build",
"dev": "NODE_ENV=development vite --mode development build",
"dev:win": "set NODE_ENV=development && vite --mode development build",
"format": "prettier --check .",
"format": "prettier --debug-check .",
"format:fix": "prettier --write .",
"lint": "eslint --ext .js,.ts,.vue src",
"lint:fix": "eslint --ext .js,.ts,.vue src --fix",
Expand Down Expand Up @@ -93,4 +93,4 @@
"role": "Developer"
}
]
}
}
4 changes: 2 additions & 2 deletions src/Api/modules/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { httpInstance, createCancelTokenHandler } from './HttpApi'
import type { Job, JobsList } from './api.types'

const adminJobs = {
getJobsList(): Promise<AxiosResponse<JobsList>> {
getJobsList(): Promise<AxiosResponse<{ jobs: JobsList }>> {
return httpInstance.request({
method: 'GET',
url: 'administration/jobs',
Expand All @@ -19,7 +19,7 @@ const adminJobs = {
})
},

runJob(job: Job) {
runJob(job: Job): Promise<AxiosResponse<{ job: Job }>> {
return httpInstance.request({
method: 'POST',
url: 'administration/job/run',
Expand Down
4 changes: 1 addition & 3 deletions src/Api/modules/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ export type Job = {
name: string
manuallyRunnable: boolean
}
export type JobsList = {
jobs: Job[]
}
export type JobsList = Record<string, Job>

export type ApiEmailAdressList = {
displayName: string
Expand Down
4 changes: 1 addition & 3 deletions src/components/Configuration/ConfigDangerArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'

import { t } from '@nextcloud/l10n'
import { showError } from '@nextcloud/dialogs'
import NcButton from '@nextcloud/vue/components/NcButton'
Expand All @@ -24,9 +25,6 @@ const pollStore = usePollStore()
const showDeleteDialog = ref(false)
const showTransferDialog = ref(false)

/**
*
*/
function toggleArchive() {
try {
pollStore.toggleArchive({ pollId: pollStore.id })
Expand Down
3 changes: 2 additions & 1 deletion src/components/Export/ExportPoll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useOptionsStore } from '../../stores/options'
import type { AxiosError } from '@nextcloud/axios'
import type { Option } from '../../stores/options.types'
import type { Answer } from '../../stores/votes.types'
import { Logger } from '@/helpers/modules/logger'

type ArrayStyle = 'symbols' | 'raw' | 'generic'
type ExportFormat = 'html' | 'xlsx' | 'ods' | 'csv'
Expand Down Expand Up @@ -159,7 +160,7 @@ async function exportFile(exportFormat: ExportFormat) {
`pollStore.${exportFormat}`,
)
} catch (error) {
console.error(error)
Logger.error('Error exporting file.', { error })
showError(t('polls', 'Error exporting file.'))
}
}
Expand Down
Loading