看板初始化提交

This commit is contained in:
zephyr
2026-06-01 21:23:12 -07:00
commit 27411ebedc
1827 changed files with 192340 additions and 0 deletions
+117
View File
@@ -0,0 +1,117 @@
<?php
namespace Kanboard\Subscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Kanboard\Core\Security\AuthenticationManager;
use Kanboard\Core\Session\SessionManager;
use Kanboard\Event\AuthSuccessEvent;
use Kanboard\Event\AuthFailureEvent;
/**
* Authentication Subscriber
*
* @package subscriber
* @author Frederic Guillot
*/
class AuthSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
/**
* Get event listeners
*
* @static
* @access public
* @return array
*/
public static function getSubscribedEvents()
{
return array(
AuthenticationManager::EVENT_SUCCESS => 'afterLogin',
AuthenticationManager::EVENT_FAILURE => 'onLoginFailure',
SessionManager::EVENT_DESTROY => 'afterLogout',
);
}
/**
* After Login callback
*
* @access public
* @param AuthSuccessEvent $event
*/
public function afterLogin(AuthSuccessEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$userAgent = $this->request->getUserAgent();
$ipAddress = $this->request->getIpAddress();
$this->userLockingModel->resetFailedLogin($this->userSession->getUsername());
$this->captchaModel->resetFailedLogin($ipAddress);
$this->lastLoginModel->create(
$event->getAuthType(),
$this->userSession->getId(),
$ipAddress,
$userAgent
);
if ($event->getAuthType() === 'RememberMe') {
$this->userSession->setPostAuthenticationAsValidated();
}
if (REMEMBER_ME_AUTH && session_is_true('hasRememberMe') && ! $this->userSession->hasPostAuthentication()) {
$session = $this->rememberMeSessionModel->create($this->userSession->getId(), $ipAddress, $userAgent);
$this->rememberMeCookie->write($session['token'], $session['sequence'], $session['expiration']);
}
}
/**
* Destroy RememberMe session on logout
*
* @access public
*/
public function afterLogout()
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$credentials = $this->rememberMeCookie->read();
if ($credentials !== false) {
$session = $this->rememberMeSessionModel->find($credentials['token'], $credentials['sequence']);
if (! empty($session)) {
$this->rememberMeSessionModel->remove($session['id']);
}
$this->rememberMeCookie->remove();
}
}
/**
* Increment failed login counter
*
* @access public
* @param AuthFailureEvent $event
*/
public function onLoginFailure(AuthFailureEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$username = $event->getUsername();
$ipAddress = $this->request->getIpAddress();
// IP-based captcha
$this->captchaModel->incrementFailedLogin($ipAddress);
if (! empty($username)) {
// log login failure in web server log to allow fail2ban usage
error_log('Kanboard: user '.$username.' authentication failure with IP address: '.$ipAddress);
$this->userLockingModel->incrementFailedLogin($username);
if ($this->userLockingModel->getFailedLogin($username) > BRUTEFORCE_LOCKDOWN) {
$this->userLockingModel->lock($username, BRUTEFORCE_LOCKDOWN_DURATION);
}
} else {
// log login failure in web server log to allow fail2ban usage
error_log('Kanboard: user Unknown authentication failure with IP address: '.$ipAddress);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Core\Base;
/**
* Base class for subscribers
*
* @package subscriber
* @author Frederic Guillot
*/
class BaseSubscriber extends Base
{
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Kanboard\Subscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class BootstrapSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
'app.bootstrap' => 'execute',
);
}
public function execute()
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$this->languageModel->loadCurrentLanguage();
$this->timezoneModel->setCurrentTimezone();
$this->actionManager->attachEvents();
if ($this->userSession->isLogged()) {
session_set('hasSubtaskInProgress', $this->subtaskStatusModel->hasSubtaskInProgress($this->userSession->getId()));
}
}
public function __destruct()
{
if (DEBUG) {
foreach ($this->db->getLogMessages() as $message) {
$this->logger->debug('SQL: ' . $message);
}
$this->logger->debug('APP: nb_queries={nb}', array('nb' => $this->db->getStatementHandler()->getNbQueries()));
$this->logger->debug('APP: rendering_time={time}', array('time' => microtime(true) - $this->request->getStartTime()));
$this->logger->debug('APP: memory_usage='.$this->helper->text->bytes(memory_get_usage()));
$this->logger->debug('APP: uri='.$this->request->getUri());
$this->logger->debug('###############################################');
}
}
}
@@ -0,0 +1,49 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Core\User\UserProfile;
use Kanboard\Event\UserProfileSyncEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Class LdapUserPhotoSubscriber
*
* @package Kanboard\Subscriber
* @author Frederic Guillot
*/
class LdapUserPhotoSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
/**
* Get event listeners
*
* @static
* @access public
* @return array
*/
public static function getSubscribedEvents()
{
return array(
UserProfile::EVENT_USER_PROFILE_AFTER_SYNC => 'syncUserPhoto',
);
}
/**
* Save the user profile photo from LDAP to the object storage
*
* @access public
* @param UserProfileSyncEvent $event
*/
public function syncUserPhoto(UserProfileSyncEvent $event)
{
if (is_a($event->getUser(), 'Kanboard\User\LdapUserProvider')) {
$profile = $event->getProfile();
$photo = $event->getUser()->getPhoto();
if (empty($profile['avatar_path']) && ! empty($photo)) {
$this->logger->info('Saving user photo from LDAP profile');
$this->avatarFileModel->uploadImageContent($profile['id'], $photo);
}
}
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Event\GenericEvent;
use Kanboard\Model\TaskLinkModel;
use Kanboard\Model\TaskModel;
use Kanboard\Model\CommentModel;
use Kanboard\Model\SubtaskModel;
use Kanboard\Model\TaskFileModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class NotificationSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
TaskModel::EVENT_USER_MENTION => 'handleEvent',
TaskModel::EVENT_CREATE => 'handleEvent',
TaskModel::EVENT_UPDATE => 'handleEvent',
TaskModel::EVENT_CLOSE => 'handleEvent',
TaskModel::EVENT_OPEN => 'handleEvent',
TaskModel::EVENT_MOVE_COLUMN => 'handleEvent',
TaskModel::EVENT_MOVE_PROJECT => 'handleEvent',
TaskModel::EVENT_MOVE_POSITION => 'handleEvent',
TaskModel::EVENT_MOVE_SWIMLANE => 'handleEvent',
TaskModel::EVENT_ASSIGNEE_CHANGE => 'handleEvent',
SubtaskModel::EVENT_CREATE => 'handleEvent',
SubtaskModel::EVENT_UPDATE => 'handleEvent',
SubtaskModel::EVENT_DELETE => 'handleEvent',
CommentModel::EVENT_CREATE => 'handleEvent',
CommentModel::EVENT_UPDATE => 'handleEvent',
CommentModel::EVENT_DELETE => 'handleEvent',
CommentModel::EVENT_USER_MENTION => 'handleEvent',
TaskFileModel::EVENT_CREATE => 'handleEvent',
TaskFileModel::EVENT_DESTROY => 'handleEvent',
TaskLinkModel::EVENT_CREATE_UPDATE => 'handleEvent',
TaskLinkModel::EVENT_DELETE => 'handleEvent',
);
}
public function handleEvent(GenericEvent $event, $eventName)
{
$this->logger->debug('Subscriber executed: ' . __METHOD__);
$this->queueManager->push($this->notificationJob->withParams($event, $eventName));
}
}
@@ -0,0 +1,27 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Event\TaskEvent;
use Kanboard\Model\TaskModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProjectDailySummarySubscriber extends BaseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE => 'execute',
TaskModel::EVENT_CLOSE => 'execute',
TaskModel::EVENT_OPEN => 'execute',
TaskModel::EVENT_MOVE_COLUMN => 'execute',
TaskModel::EVENT_MOVE_SWIMLANE => 'execute',
);
}
public function execute(TaskEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$this->queueManager->push($this->projectMetricJob->withParams($event['task']['project_id']));
}
}
@@ -0,0 +1,33 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Event\GenericEvent;
use Kanboard\Model\TaskModel;
use Kanboard\Model\SubtaskModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProjectModificationDateSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE => 'execute',
TaskModel::EVENT_CLOSE => 'execute',
TaskModel::EVENT_OPEN => 'execute',
TaskModel::EVENT_MOVE_SWIMLANE => 'execute',
TaskModel::EVENT_MOVE_COLUMN => 'execute',
TaskModel::EVENT_MOVE_POSITION => 'execute',
TaskModel::EVENT_MOVE_PROJECT => 'execute',
TaskModel::EVENT_ASSIGNEE_CHANGE => 'execute',
SubtaskModel::EVENT_CREATE_UPDATE => 'execute',
SubtaskModel::EVENT_DELETE => 'execute',
);
}
public function execute(GenericEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$this->projectModel->updateModificationDate($event['task']['project_id']);
}
}
@@ -0,0 +1,42 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Event\TaskEvent;
use Kanboard\Model\TaskModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RecurringTaskSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN => 'onMove',
TaskModel::EVENT_CLOSE => 'onClose',
);
}
public function onMove(TaskEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$task = $event['task'];
if ($task['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) {
if ($task['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_FIRST_COLUMN && $this->columnModel->getFirstColumnId($task['project_id']) == $event['src_column_id']) {
$this->taskRecurrenceModel->duplicateRecurringTask($task['id']);
} elseif ($task['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_LAST_COLUMN && $this->columnModel->getLastColumnId($task['project_id']) == $event['dst_column_id']) {
$this->taskRecurrenceModel->duplicateRecurringTask($task['id']);
}
}
}
public function onClose(TaskEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$task = $event['task'];
if ($task['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING && $task['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_CLOSE) {
$this->taskRecurrenceModel->duplicateRecurringTask($event['task_id']);
}
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Kanboard\Subscriber;
use Kanboard\Event\TaskEvent;
use Kanboard\Model\TaskModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class TransitionSubscriber extends BaseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN => 'execute',
);
}
public function execute(TaskEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
$user_id = $this->userSession->getId();
if (! empty($user_id)) {
$this->transitionModel->save($user_id, $event->getAll());
}
}
}