diff options
Diffstat (limited to 'app/Core')
27 files changed, 476 insertions, 62 deletions
diff --git a/app/Core/Base.php b/app/Core/Base.php index 44dfaa39..17ed5b33 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -17,6 +17,7 @@ use Pimple\Container; * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic * @property \Kanboard\Core\Action\ActionManager $actionManager * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager + * @property \Kanboard\Core\ExternalTask\ExternalTaskManager $externalTaskManager * @property \Kanboard\Core\Cache\MemoryCache $memoryCache * @property \Kanboard\Core\Cache\BaseCache $cacheDriver * @property \Kanboard\Core\Event\EventManager $eventManager @@ -57,9 +58,25 @@ use Pimple\Container; * @property \Kanboard\Core\Paginator $paginator * @property \Kanboard\Core\Template $template * @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator - * @property \Kanboard\Decorator\columnRestrictionCacheDecorator $columnRestrictionCacheDecorator + * @property \Kanboard\Decorator\UserCacheDecorator $userCacheDecorator + * @property \Kanboard\Decorator\ColumnRestrictionCacheDecorator $columnRestrictionCacheDecorator * @property \Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator $columnMoveRestrictionCacheDecorator * @property \Kanboard\Decorator\ProjectRoleRestrictionCacheDecorator $projectRoleRestrictionCacheDecorator + * @property \Kanboard\Formatter\BoardColumnFormatter $boardColumnFormatter + * @property \Kanboard\Formatter\BoardFormatter $boardFormatter + * @property \Kanboard\Formatter\BoardSwimlaneFormatter $boardSwimlaneFormatter + * @property \Kanboard\Formatter\BoardTaskFormatter $boardTaskFormatter + * @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter + * @property \Kanboard\Formatter\ProjectActivityEventFormatter $projectActivityEventFormatter + * @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter + * @property \Kanboard\Formatter\SubtaskTimeTrackingCalendarFormatter $subtaskTimeTrackingCalendarFormatter + * @property \Kanboard\Formatter\TaskAutoCompleteFormatter $taskAutoCompleteFormatter + * @property \Kanboard\Formatter\TaskCalendarFormatter $taskCalendarFormatter + * @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter + * @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter + * @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter + * @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter + * @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter * @property \Kanboard\Model\ActionModel $actionModel * @property \Kanboard\Model\ActionParameterModel $actionParameterModel * @property \Kanboard\Model\AvatarFileModel $avatarFileModel @@ -77,6 +94,7 @@ use Pimple\Container; * @property \Kanboard\Model\ProjectFileModel $projectFileModel * @property \Kanboard\Model\GroupModel $groupModel * @property \Kanboard\Model\GroupMemberModel $groupMemberModel + * @property \Kanboard\Model\InviteModel $inviteModel * @property \Kanboard\Model\LanguageModel $languageModel * @property \Kanboard\Model\LastLoginModel $lastLoginModel * @property \Kanboard\Model\LinkModel $linkModel @@ -125,7 +143,6 @@ use Pimple\Container; * @property \Kanboard\Model\TransitionModel $transitionModel * @property \Kanboard\Model\UserModel $userModel * @property \Kanboard\Model\UserLockingModel $userLockingModel - * @property \Kanboard\Model\UserMentionModel $userMentionModel * @property \Kanboard\Model\UserNotificationModel $userNotificationModel * @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel @@ -177,6 +194,7 @@ use Pimple\Container; * @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob * @property \Kanboard\Job\NotificationJob $notificationJob * @property \Kanboard\Job\ProjectMetricJob $projectMetricJob + * @property \Kanboard\Job\UserMentionJob $userMentionJob * @property \Psr\Log\LoggerInterface $logger * @property \PicoDb\Database $db * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher diff --git a/app/Core/DateParser.php b/app/Core/DateParser.php index b9aa9230..9d012d12 100644 --- a/app/Core/DateParser.php +++ b/app/Core/DateParser.php @@ -13,7 +13,6 @@ use DateTime; class DateParser extends Base { const DATE_FORMAT = 'm/d/Y'; - const DATE_TIME_FORMAT = 'm/d/Y H:i'; const TIME_FORMAT = 'H:i'; /** @@ -35,7 +34,7 @@ class DateParser extends Base */ public function getUserDateTimeFormat() { - return $this->configModel->get('application_datetime_format', DateParser::DATE_TIME_FORMAT); + return $this->getUserDateFormat().' '.$this->getUserTimeFormat(); } /** @@ -292,11 +291,9 @@ class DateParser extends Base { foreach ($fields as $field) { if (! empty($values[$field])) { - if (! ctype_digit($values[$field])) { - $values[$field] = strtotime($values[$field]); + if (ctype_digit($values[$field])) { + $values[$field] = date($format, $values[$field]); } - - $values[$field] = date($format, $values[$field]); } else { $values[$field] = ''; } diff --git a/app/Core/ExternalTask/AccessForbiddenException.php b/app/Core/ExternalTask/AccessForbiddenException.php new file mode 100644 index 00000000..2b5ebd33 --- /dev/null +++ b/app/Core/ExternalTask/AccessForbiddenException.php @@ -0,0 +1,13 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +/** + * Class AccessForbiddenException + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +class AccessForbiddenException extends ExternalTaskException +{ +} diff --git a/app/Core/ExternalTask/ExternalTaskException.php b/app/Core/ExternalTask/ExternalTaskException.php new file mode 100644 index 00000000..07e5665d --- /dev/null +++ b/app/Core/ExternalTask/ExternalTaskException.php @@ -0,0 +1,15 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +use Exception; + +/** + * Class NotFoundException + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +class ExternalTaskException extends Exception +{ +} diff --git a/app/Core/ExternalTask/ExternalTaskInterface.php b/app/Core/ExternalTask/ExternalTaskInterface.php new file mode 100644 index 00000000..084af509 --- /dev/null +++ b/app/Core/ExternalTask/ExternalTaskInterface.php @@ -0,0 +1,26 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +/** + * Interface ExternalTaskInterface + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +interface ExternalTaskInterface +{ + /** + * Return Uniform Resource Identifier for the task + * + * @return string + */ + public function getUri(); + + /** + * Return a dict to populate the task form + * + * @return array + */ + public function getFormValues(); +} diff --git a/app/Core/ExternalTask/ExternalTaskManager.php b/app/Core/ExternalTask/ExternalTaskManager.php new file mode 100644 index 00000000..102ec459 --- /dev/null +++ b/app/Core/ExternalTask/ExternalTaskManager.php @@ -0,0 +1,58 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +/** + * Class ExternalTaskManager + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +class ExternalTaskManager +{ + protected $providers = array(); + + /** + * Register a new task provider + * + * @param ExternalTaskProviderInterface $externalTaskProvider + * @return $this + */ + public function register(ExternalTaskProviderInterface $externalTaskProvider) + { + $this->providers[$externalTaskProvider->getName()] = $externalTaskProvider; + return $this; + } + + /** + * Get task provider + * + * @param string $name + * @return ExternalTaskProviderInterface|null + * @throws ProviderNotFoundException + */ + public function getProvider($name) + { + if (isset($this->providers[$name])) { + return $this->providers[$name]; + } + + throw new ProviderNotFoundException('Unable to load this provider: '.$name); + } + + /** + * Get list of task providers + * + * @return array + */ + public function getProvidersList() + { + $providers = array_keys($this->providers); + + if (count($providers)) { + return array_combine($providers, $providers); + } + + return array(); + } +} diff --git a/app/Core/ExternalTask/ExternalTaskProviderInterface.php b/app/Core/ExternalTask/ExternalTaskProviderInterface.php new file mode 100644 index 00000000..f67f7552 --- /dev/null +++ b/app/Core/ExternalTask/ExternalTaskProviderInterface.php @@ -0,0 +1,77 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +/** + * Interface ExternalTaskProviderInterface + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +interface ExternalTaskProviderInterface +{ + /** + * Get provider name (visible in the user interface) + * + * @access public + * @return string + */ + public function getName(); + + /** + * Retrieve task from external system or cache + * + * @access public + * @throws \Kanboard\Core\ExternalTask\ExternalTaskException + * @param string $uri + * @return ExternalTaskInterface + */ + public function fetch($uri); + + /** + * Save external task to another system + * + * @throws \Kanboard\Core\ExternalTask\ExternalTaskException + * @param string $uri + * @param array $formValues + * @param array $formErrors + * @return bool + */ + public function save($uri, array $formValues, array &$formErrors); + + /** + * Get task import template name + * + * @return string + */ + public function getImportFormTemplate(); + + /** + * Get creation form template + * + * @return string + */ + public function getCreationFormTemplate(); + + /** + * Get modification form template + * + * @return string + */ + public function getModificationFormTemplate(); + + /** + * Get task view template name + * + * @return string + */ + public function getViewTemplate(); + + /** + * Build external task URI based on import form values + * + * @param array $formValues + * @return string + */ + public function buildTaskUri(array $formValues); +} diff --git a/app/Core/ExternalTask/NotFoundException.php b/app/Core/ExternalTask/NotFoundException.php new file mode 100644 index 00000000..34eff8ea --- /dev/null +++ b/app/Core/ExternalTask/NotFoundException.php @@ -0,0 +1,13 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +/** + * Class NotFoundException + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +class NotFoundException extends ExternalTaskException +{ +} diff --git a/app/Core/ExternalTask/ProviderNotFoundException.php b/app/Core/ExternalTask/ProviderNotFoundException.php new file mode 100644 index 00000000..6ad1fae1 --- /dev/null +++ b/app/Core/ExternalTask/ProviderNotFoundException.php @@ -0,0 +1,13 @@ +<?php + +namespace Kanboard\Core\ExternalTask; + +/** + * Class ProviderNotFoundException + * + * @package Kanboard\Core\ExternalTask + * @author Frederic Guillot + */ +class ProviderNotFoundException extends ExternalTaskException +{ +} diff --git a/app/Core/Filter/FormatterInterface.php b/app/Core/Filter/FormatterInterface.php index b7c04c51..0ff84976 100644 --- a/app/Core/Filter/FormatterInterface.php +++ b/app/Core/Filter/FormatterInterface.php @@ -17,7 +17,7 @@ interface FormatterInterface * * @access public * @param Table $query - * @return FormatterInterface + * @return $this */ public function withQuery(Table $query); diff --git a/app/Core/Helper.php b/app/Core/Helper.php index b5c560af..ab7c3b7b 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -12,6 +12,7 @@ use Pimple\Container; * * @property \Kanboard\Helper\AppHelper $app * @property \Kanboard\Helper\AssetHelper $asset + * @property \Kanboard\Helper\AvatarHelper $avatar * @property \Kanboard\Helper\BoardHelper $board * @property \Kanboard\Helper\CalendarHelper $calendar * @property \Kanboard\Helper\DateHelper $dt @@ -19,6 +20,7 @@ use Pimple\Container; * @property \Kanboard\Helper\FormHelper $form * @property \Kanboard\Helper\HookHelper $hook * @property \Kanboard\Helper\ICalHelper $ical + * @property \Kanboard\Helper\ModalHelper $modal * @property \Kanboard\Helper\ModelHelper $model * @property \Kanboard\Helper\SubtaskHelper $subtask * @property \Kanboard\Helper\TaskHelper $task diff --git a/app/Core/Http/Request.php b/app/Core/Http/Request.php index 2e84958d..44bfdbe6 100644 --- a/app/Core/Http/Request.php +++ b/app/Core/Http/Request.php @@ -105,7 +105,7 @@ class Request extends Base { if (! empty($this->post) && ! empty($this->post['csrf_token']) && $this->token->validateCSRFToken($this->post['csrf_token'])) { unset($this->post['csrf_token']); - return $this->post; + return $this->filterValues($this->post); } return array(); @@ -344,4 +344,17 @@ class Request extends Base { return isset($this->server[$variable]) ? $this->server[$variable] : ''; } + + protected function filterValues(array $values) + { + foreach ($values as $key => $value) { + + // IE11 Workaround when submitting multipart/form-data + if (strpos($key, '-----------------------------') === 0) { + unset($values[$key]); + } + } + + return $values; + } } diff --git a/app/Core/Http/Response.php b/app/Core/Http/Response.php index 0f16fb65..0af763a6 100644 --- a/app/Core/Http/Response.php +++ b/app/Core/Http/Response.php @@ -129,6 +129,18 @@ class Response extends Base } /** + * Add P3P headers for Internet Explorer + * + * @access public + * @return $this + */ + public function withP3P() + { + $this->withHeader('P3P', 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'); + return $this; + } + + /** * Set HTTP response body * * @access public diff --git a/app/Core/Mail/Transport/Mail.php b/app/Core/Mail/Transport/Mail.php index d27925f0..c99cc8ba 100644 --- a/app/Core/Mail/Transport/Mail.php +++ b/app/Core/Mail/Transport/Mail.php @@ -33,8 +33,8 @@ class Mail extends Base implements ClientInterface $message = Swift_Message::newInstance() ->setSubject($subject) ->setFrom(array($this->helper->mail->getMailSenderAddress() => $author)) - ->setBody($html, 'text/html') - ->setTo(array($email => $name)); + ->setTo(array($email => $name)) + ->setBody($html, 'text/html'); Swift_Mailer::newInstance($this->getTransport())->send($message); } catch (Swift_TransportException $e) { diff --git a/app/Core/Mail/Transport/Smtp.php b/app/Core/Mail/Transport/Smtp.php index 1f4e54ce..815dde5d 100644 --- a/app/Core/Mail/Transport/Smtp.php +++ b/app/Core/Mail/Transport/Smtp.php @@ -24,6 +24,7 @@ class Smtp extends Mail $transport->setUsername(MAIL_SMTP_USERNAME); $transport->setPassword(MAIL_SMTP_PASSWORD); $transport->setEncryption(MAIL_SMTP_ENCRYPTION); + if (HTTP_VERIFY_SSL_CERTIFICATE === false) { $transport->setStreamOptions(array( 'ssl' => array( diff --git a/app/Core/Markdown.php b/app/Core/Markdown.php index b5abe5ed..4487bf2a 100644 --- a/app/Core/Markdown.php +++ b/app/Core/Markdown.php @@ -86,18 +86,23 @@ class Markdown extends Parsedown */ protected function inlineUserLink(array $Excerpt) { - if (! $this->isPublicLink && preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) { - $user_id = $this->container['userModel']->getIdByUsername($matches[1]); + if (! $this->isPublicLink && preg_match('/^@([^\s,!:?]+)/', $Excerpt['text'], $matches)) { + $username = rtrim($matches[1], '.'); + $user = $this->container['userCacheDecorator']->getByUsername($username); - if (! empty($user_id)) { - $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user_id)); + if (! empty($user)) { + $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user['id'])); return array( - 'extent' => strlen($matches[0]), + 'extent' => strlen($username) + 1, 'element' => array( - 'name' => 'a', - 'text' => $matches[0], - 'attributes' => array('href' => $url, 'class' => 'user-mention-link'), + 'name' => 'a', + 'text' => '@' . $username, + 'attributes' => array( + 'href' => $url, + 'class' => 'user-mention-link', + 'title' => $user['name'] ?: $user['username'], + ), ), ); } @@ -125,7 +130,10 @@ class Markdown extends Parsedown array( 'token' => $token, 'task_id' => $task_id, - ) + ), + false, + '', + true ); } diff --git a/app/Core/Paginator.php b/app/Core/Paginator.php index cfe89938..9075a713 100644 --- a/app/Core/Paginator.php +++ b/app/Core/Paginator.php @@ -232,6 +232,17 @@ class Paginator } /** + * Get the number of current page + * + * @access public + * @return integer + */ + public function getPage() + { + return $this->page; + } + + /** * Set the default column order * * @access public @@ -271,6 +282,16 @@ class Paginator } /** + * Get the maximum number of items per page. + * + * @return int + */ + public function getMax() + { + return $this->limit; + } + + /** * Return true if the collection is empty * * @access public @@ -353,7 +374,9 @@ class Paginator '← '.t('Previous'), $this->controller, $this->action, - $this->getUrlParams($this->page - 1, $this->order, $this->direction) + $this->getUrlParams($this->page - 1, $this->order, $this->direction), + false, + 'js-modal-replace' ); } else { $html .= '← '.t('Previous'); @@ -379,7 +402,9 @@ class Paginator t('Next').' →', $this->controller, $this->action, - $this->getUrlParams($this->page + 1, $this->order, $this->direction) + $this->getUrlParams($this->page + 1, $this->order, $this->direction), + false, + 'js-modal-replace' ); } else { $html .= t('Next').' →'; @@ -391,6 +416,17 @@ class Paginator } /** + * Generate the page showing. + * + * @access public + * @return string + */ + public function generatPageShowing() + { + return '<span class="pagination-showing">'.t('Showing %d-%d of %d', (($this->getPage() - 1) * $this->getMax() + 1), min($this->getTotal(), $this->getPage() * $this->getMax()), $this->getTotal()).'</span>'; + } + + /** * Return true if there is no pagination to show * * @access public @@ -413,6 +449,7 @@ class Paginator if (! $this->hasNothingtoShow()) { $html .= '<div class="pagination">'; + $html .= $this->generatPageShowing(); $html .= $this->generatePreviousLink(); $html .= $this->generateNextLink(); $html .= '</div>'; @@ -453,7 +490,9 @@ class Paginator $label, $this->controller, $this->action, - $this->getUrlParams($this->page, $column, $direction) + $this->getUrlParams($this->page, $column, $direction), + false, + 'js-modal-replace' ); } } diff --git a/app/Core/Plugin/Base.php b/app/Core/Plugin/Base.php index 9d8167a9..e0b5954a 100644 --- a/app/Core/Plugin/Base.php +++ b/app/Core/Plugin/Base.php @@ -131,4 +131,17 @@ abstract class Base extends \Kanboard\Core\Base { return ''; } + + /** + * Get application compatibility version + * + * Examples: >=1.0.36, 1.0.37, APP_VERSION + * + * @access public + * @return string + */ + public function getCompatibleVersion() + { + return APP_VERSION; + } } diff --git a/app/Core/Plugin/Directory.php b/app/Core/Plugin/Directory.php index 27c3514e..dc32e655 100644 --- a/app/Core/Plugin/Directory.php +++ b/app/Core/Plugin/Directory.php @@ -36,18 +36,7 @@ class Directory extends BaseCore */ public function isCompatible(array $plugin, $appVersion = APP_VERSION) { - if (strpos($appVersion, 'master') !== false) { - return true; - } - - foreach (array('>=', '>') as $operator) { - if (strpos($plugin['compatible_version'], $operator) === 0) { - $pluginVersion = substr($plugin['compatible_version'], strlen($operator)); - return version_compare($appVersion, $pluginVersion, $operator); - } - } - - return $plugin['compatible_version'] === $appVersion; + return Version::isCompatible($plugin['compatible_version'], $appVersion); } /** diff --git a/app/Core/Plugin/Loader.php b/app/Core/Plugin/Loader.php index f2f6add7..38f41d39 100644 --- a/app/Core/Plugin/Loader.php +++ b/app/Core/Plugin/Loader.php @@ -4,6 +4,7 @@ namespace Kanboard\Core\Plugin; use Composer\Autoload\ClassLoader; use DirectoryIterator; +use Exception; use LogicException; use Kanboard\Core\Tool; @@ -22,6 +23,7 @@ class Loader extends \Kanboard\Core\Base * @var array */ protected $plugins = array(); + protected $incompatiblePlugins = array(); /** * Get list of loaded plugins @@ -35,6 +37,17 @@ class Loader extends \Kanboard\Core\Base } /** + * Get list of not compatible plugins + * + * @access public + * @return Base[] + */ + public function getIncompatiblePlugins() + { + return $this->incompatiblePlugins; + } + + /** * Scan plugin folder and load plugins * * @access public @@ -51,8 +64,7 @@ class Loader extends \Kanboard\Core\Base foreach ($dir as $fileInfo) { if ($fileInfo->isDir() && substr($fileInfo->getFilename(), 0, 1) !== '.') { $pluginName = $fileInfo->getFilename(); - $this->loadSchema($pluginName); - $this->initializePlugin($pluginName, $this->loadPlugin($pluginName)); + $this->initializePlugin($pluginName); } } } @@ -85,7 +97,7 @@ class Loader extends \Kanboard\Core\Base $className = '\Kanboard\Plugin\\'.$pluginName.'\\Plugin'; if (! class_exists($className)) { - throw new LogicException('Unable to load this plugin class '.$className); + throw new LogicException('Unable to load this plugin class: '.$className); } return new $className($this->container); @@ -96,18 +108,30 @@ class Loader extends \Kanboard\Core\Base * * @access public * @param string $pluginName - * @param Base $plugin */ - public function initializePlugin($pluginName, Base $plugin) + public function initializePlugin($pluginName) { - if (method_exists($plugin, 'onStartup')) { - $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup')); - } + try { + $plugin = $this->loadPlugin($pluginName); - Tool::buildDIC($this->container, $plugin->getClasses()); - Tool::buildDICHelpers($this->container, $plugin->getHelpers()); + if (Version::isCompatible($plugin->getCompatibleVersion(), APP_VERSION)) { + $this->loadSchema($pluginName); + + if (method_exists($plugin, 'onStartup')) { + $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup')); + } - $plugin->initialize(); - $this->plugins[$pluginName] = $plugin; + Tool::buildDIC($this->container, $plugin->getClasses()); + Tool::buildDICHelpers($this->container, $plugin->getHelpers()); + + $plugin->initialize(); + $this->plugins[$pluginName] = $plugin; + } else { + $this->incompatiblePlugins[$pluginName] = $plugin; + $this->logger->error($pluginName.' is not compatible with this version'); + } + } catch (Exception $e) { + $this->logger->critical($pluginName.': '.$e->getMessage()); + } } } diff --git a/app/Core/Plugin/PluginException.php b/app/Core/Plugin/PluginException.php new file mode 100644 index 00000000..fae7de35 --- /dev/null +++ b/app/Core/Plugin/PluginException.php @@ -0,0 +1,15 @@ +<?php + +namespace Kanboard\Core\Plugin; + +use Exception; + +/** + * Class PluginException + * + * @package Kanboard\Core\Plugin + * @author Frederic Guillot + */ +class PluginException extends Exception +{ +} diff --git a/app/Core/Plugin/PluginInstallerException.php b/app/Core/Plugin/PluginInstallerException.php index 7d356c9b..31745f22 100644 --- a/app/Core/Plugin/PluginInstallerException.php +++ b/app/Core/Plugin/PluginInstallerException.php @@ -2,14 +2,12 @@ namespace Kanboard\Core\Plugin; -use Exception; - /** * Class PluginInstallerException * * @package Kanboard\Core\Plugin * @author Frederic Guillot */ -class PluginInstallerException extends Exception +class PluginInstallerException extends PluginException { } diff --git a/app/Core/Plugin/Version.php b/app/Core/Plugin/Version.php new file mode 100644 index 00000000..ba5e0443 --- /dev/null +++ b/app/Core/Plugin/Version.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Core\Plugin; + +/** + * Class Version + * + * @package Kanboard\Core\Plugin + * @author Frederic Guillot + */ +class Version +{ + /** + * Check plugin version compatibility with application version + * + * @param string $pluginCompatibleVersion + * @param string $appVersion + * @return bool + */ + public static function isCompatible($pluginCompatibleVersion, $appVersion = APP_VERSION) + { + if (strpos($appVersion, 'master') !== false) { + return true; + } + + $appVersion = str_replace('v', '', $appVersion); + $pluginCompatibleVersion = str_replace('v', '', $pluginCompatibleVersion); + + foreach (array('>=', '>', '<=', '<') as $operator) { + if (strpos($pluginCompatibleVersion, $operator) === 0) { + $pluginVersion = substr($pluginCompatibleVersion, strlen($operator)); + return version_compare($appVersion, $pluginVersion, $operator); + } + } + + return $pluginCompatibleVersion === $appVersion; + } +} diff --git a/app/Core/Queue/QueueManager.php b/app/Core/Queue/QueueManager.php index dcf0ebf5..1d7c2d1e 100644 --- a/app/Core/Queue/QueueManager.php +++ b/app/Core/Queue/QueueManager.php @@ -64,7 +64,7 @@ class QueueManager extends Base public function listen() { if ($this->queue === null) { - throw new LogicException('No queue driver defined!'); + throw new LogicException('No queue driver defined or unable to connect to broker!'); } while ($job = $this->queue->pull()) { diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php index 9e93602c..e6478d8d 100644 --- a/app/Core/Session/SessionStorage.php +++ b/app/Core/Session/SessionStorage.php @@ -19,6 +19,7 @@ namespace Kanboard\Core\Session; * @property bool $hasSubtaskInProgress * @property bool $hasRememberMe * @property bool $boardCollapsed + * @property string $scope * @property bool $twoFactorBeforeCodeCalled * @property string $twoFactorSecret * @property string $oauthState diff --git a/app/Core/Tool.php b/app/Core/Tool.php index 9b8820eb..6e457641 100644 --- a/app/Core/Tool.php +++ b/app/Core/Tool.php @@ -41,7 +41,7 @@ class Tool } /** - * Build dependency injection container from an array + * Build dependency injection containers from an array * * @static * @access public @@ -64,6 +64,29 @@ class Tool } /** + * Build dependency injection container from an array + * + * @static + * @access public + * @param Container $container + * @param array $namespaces + * @return Container + */ + public static function buildFactories(Container $container, array $namespaces) + { + foreach ($namespaces as $namespace => $classes) { + foreach ($classes as $name) { + $class = '\\Kanboard\\'.$namespace.'\\'.$name; + $container[lcfirst($name)] = $container->factory(function ($c) use ($class) { + return new $class($c); + }); + } + } + + return $container; + } + + /** * Build dependency injection container for custom helpers from an array * * @static diff --git a/app/Core/Translator.php b/app/Core/Translator.php index 113c0dc6..ac2e2aae 100644 --- a/app/Core/Translator.php +++ b/app/Core/Translator.php @@ -11,13 +11,6 @@ namespace Kanboard\Core; class Translator { /** - * Locale path - * - * @var string - */ - const PATH = 'app/Locale'; - - /** * Locale * * @static @@ -171,9 +164,13 @@ class Translator * @param string $language Locale code: fr_FR * @param string $path Locale folder */ - public static function load($language, $path = self::PATH) + public static function load($language, $path = '') { - $filename = $path.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'translations.php'; + if ($path === '') { + $path = self::getDefaultFolder(); + } + + $filename = implode(DIRECTORY_SEPARATOR, array($path, $language, 'translations.php')); if (file_exists($filename)) { self::$locales = array_merge(self::$locales, require($filename)); @@ -190,4 +187,15 @@ class Translator { self::$locales = array(); } + + /** + * Get default locales folder + * + * @access public + * @return string + */ + public static function getDefaultFolder() + { + return implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', 'Locale')); + } } |