Add external links for tasks with plugin api

This commit is contained in:
Frederic Guillot 2016-01-30 20:38:20 -05:00
parent ec66a779c9
commit 5c92f46786
81 changed files with 2544 additions and 75 deletions

View File

@ -10,6 +10,7 @@ Breaking changes:
New features:
* Add external links for tasks with plugin api
* Add project owner (Directly Responsible Individual)
* Add configurable task priority
* Add Greek translation

View File

@ -189,9 +189,15 @@ abstract class Base extends \Kanboard\Core\Base
*/
protected function taskLayout($template, array $params)
{
$params['ajax'] = $this->request->isAjax() || $this->request->getIntegerParam('ajax') === 1;
$content = $this->template->render($template, $params);
$params['task_content_for_layout'] = $content;
if ($params['ajax']) {
return $content;
}
$params['title'] = $params['task']['project_name'].' > '.$params['task']['title'];
$params['task_content_for_layout'] = $content;
$params['board_selector'] = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
return $this->template->layout('task/layout', $params);
@ -319,7 +325,8 @@ abstract class Base extends \Kanboard\Core\Base
* @param array &$project
* @return string
*/
protected function getProjectDescription(array &$project) {
protected function getProjectDescription(array &$project)
{
if ($project['owner_id'] > 0) {
$description = t('Project owner: ').'**'.$this->template->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL;

View File

@ -24,6 +24,20 @@ class BoardTooltip extends Base
)));
}
/**
* Get links on mouseover
*
* @access public
*/
public function externallinks()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_external_links', array(
'links' => $this->taskExternalLink->getAll($task['id']),
'task' => $task,
)));
}
/**
* Get subtasks on mouseover
*

View File

@ -41,7 +41,6 @@ class Comment extends Base
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
if (empty($values)) {
$values = array(
@ -50,15 +49,6 @@ class Comment extends Base
);
}
if ($ajax) {
$this->response->html($this->template->render('comment/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'ajax' => $ajax,
)));
}
$this->response->html($this->taskLayout('comment/create', array(
'values' => $values,
'errors' => $errors,

View File

@ -0,0 +1,185 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
/**
* Task External Link Controller
*
* @package controller
* @author Frederic Guillot
*/
class TaskExternalLink extends Base
{
/**
* Creation form
*
* @access public
*/
public function show()
{
$task = $this->getTask();
$this->response->html($this->taskLayout('task_external_link/show', array(
'links' => $this->taskExternalLink->getAll($task['id']),
'task' => $task,
'title' => t('List of external links'),
)));
}
/**
* First creation form
*
* @access public
*/
public function find(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$this->response->html($this->taskLayout('task_external_link/find', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'types' => $this->externalLinkManager->getTypes(),
)));
}
/**
* Second creation form
*
* @access public
*/
public function create()
{
try {
$task = $this->getTask();
$values = $this->request->getValues();
$provider = $this->externalLinkManager->setUserInput($values)->find();
$link = $provider->getLink();
$this->response->html($this->taskLayout('task_external_link/create', array(
'values' => array(
'title' => $link->getTitle(),
'url' => $link->getUrl(),
'link_type' => $provider->getType(),
),
'dependencies' => $provider->getDependencies(),
'errors' => array(),
'task' => $task,
)));
} catch (ExternalLinkProviderNotFound $e) {
$errors = array('text' => array(t('Unable to fetch link information.')));
$this->find($values, $errors);
}
}
/**
* Save link
*
* @access public
*/
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
if ($valid && $this->taskExternalLink->create($values)) {
$this->flash->success(t('Link added successfully.'));
return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
}
$this->edit($values, $errors);
}
/**
* Edit form
*
* @access public
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$link_id = $this->request->getIntegerParam('link_id');
if ($link_id > 0) {
$values = $this->taskExternalLink->getById($link_id);
}
if (empty($values)) {
return $this->notfound();
}
$provider = $this->externalLinkManager->getProvider($values['link_type']);
$this->response->html($this->taskLayout('task_external_link/edit', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'dependencies' => $provider->getDependencies(),
)));
}
/**
* Update link
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$values = $this->request->getValues();
list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
if ($valid && $this->taskExternalLink->update($values)) {
$this->flash->success(t('Link updated successfully.'));
return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
$this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a link
*
* @access public
*/
public function confirm()
{
$task = $this->getTask();
$link_id = $this->request->getIntegerParam('link_id');
$link = $this->taskExternalLink->getById($link_id);
if (empty($link)) {
return $this->notfound();
}
$this->response->html($this->taskLayout('task_external_link/remove', array(
'link' => $link,
'task' => $task,
)));
}
/**
* Remove a link
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$task = $this->getTask();
if ($this->taskExternalLink->remove($this->request->getIntegerParam('link_id'))) {
$this->flash->success(t('Link removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
}

View File

@ -28,6 +28,25 @@ class Tasklink extends Base
return $link;
}
/**
* Show links
*
* @access public
*/
public function show()
{
$task = $this->getTask();
$project = $this->project->getById($task['project_id']);
$this->response->html($this->taskLayout('tasklink/show', array(
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
'task' => $task,
'project' => $project,
'editable' => true,
'is_public' => false,
)));
}
/**
* Creation form
*
@ -36,18 +55,6 @@ class Tasklink extends Base
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
if ($ajax && empty($errors)) {
$this->response->html($this->template->render('tasklink/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'labels' => $this->link->getList(0, false),
'title' => t('Add a new link'),
'ajax' => $ajax,
)));
}
$this->response->html($this->taskLayout('tasklink/create', array(
'values' => $values,
@ -76,10 +83,10 @@ class Tasklink extends Base
$this->flash->success(t('Link added successfully.'));
if ($ajax) {
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
return $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
}
$errors = array('title' => array(t('The exact same link already exists')));
@ -130,7 +137,7 @@ class Tasklink extends Base
if ($valid) {
if ($this->taskLink->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
$this->flash->success(t('Link updated successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
}
$this->flash->failure(t('Unable to update your link.'));

View File

@ -80,14 +80,9 @@ class Taskmodification extends Base
'values' => $values,
'errors' => $errors,
'task' => $task,
'ajax' => $ajax,
);
if ($ajax) {
$this->response->html($this->template->render('task_modification/edit_description', $params));
} else {
$this->response->html($this->taskLayout('task_modification/edit_description', $params));
}
$this->response->html($this->taskLayout('task_modification/edit_description', $params));
}
/**

View File

@ -16,6 +16,7 @@ use Pimple\Container;
* @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic
* @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic
* @property \Kanboard\Core\Action\ActionManager $actionManager
* @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager
* @property \Kanboard\Core\Cache\MemoryCache $memoryCache
* @property \Kanboard\Core\Event\EventManager $eventManager
* @property \Kanboard\Core\Group\GroupManager $groupManager
@ -97,6 +98,7 @@ use Pimple\Container;
* @property \Kanboard\Model\TaskCreation $taskCreation
* @property \Kanboard\Model\TaskDuplication $taskDuplication
* @property \Kanboard\Model\TaskExport $taskExport
* @property \Kanboard\Model\TaskExternalLink $taskExternalLink
* @property \Kanboard\Model\TaskImport $taskImport
* @property \Kanboard\Model\TaskFinder $taskFinder
* @property \Kanboard\Model\TaskFilter $taskFilter
@ -132,6 +134,7 @@ use Pimple\Container;
* @property \Kanboard\Validator\SubtaskValidator $subtaskValidator
* @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator
* @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
* @property \Kanboard\Validator\TaskExternalLinkValidator $taskExternalLinkValidator
* @property \Kanboard\Validator\TaskValidator $taskValidator
* @property \Kanboard\Validator\UserValidator $userValidator
* @property \Psr\Log\LoggerInterface $logger

View File

@ -0,0 +1,36 @@
<?php
namespace Kanboard\Core\ExternalLink;
/**
* External Link Interface
*
* @package externalLink
* @author Frederic Guillot
*/
interface ExternalLinkInterface
{
/**
* Get link title
*
* @access public
* @return string
*/
public function getTitle();
/**
* Get link URL
*
* @access public
* @return string
*/
public function getUrl();
/**
* Set link URL
*
* @access public
* @param string $url
*/
public function setUrl($url);
}

View File

@ -0,0 +1,171 @@
<?php
namespace Kanboard\Core\ExternalLink;
use Kanboard\Core\Base;
/**
* External Link Manager
*
* @package externalLink
* @author Frederic Guillot
*/
class ExternalLinkManager extends Base
{
/**
* Automatic type value
*
* @var string
*/
const TYPE_AUTO = 'auto';
/**
* Registered providers
*
* @access private
* @var array
*/
private $providers = array();
/**
* Type chosen by the user
*
* @access private
* @var string
*/
private $userInputType = '';
/**
* Text entered by the user
*
* @access private
* @var string
*/
private $userInputText = '';
/**
* Register a new provider
*
* Providers are registered in a LIFO queue
*
* @access public
* @param ExternalLinkProviderInterface $provider
* @return ExternalLinkManager
*/
public function register(ExternalLinkProviderInterface $provider)
{
array_unshift($this->providers, $provider);
return $this;
}
/**
* Get provider
*
* @access public
* @param string $type
* @throws ExternalLinkProviderNotFound
* @return ExternalLinkProviderInterface
*/
public function getProvider($type)
{
foreach ($this->providers as $provider) {
if ($provider->getType() === $type) {
return $provider;
}
}
throw new ExternalLinkProviderNotFound('Unable to find link provider: '.$type);
}
/**
* Get link types
*
* @access public
* @return array
*/
public function getTypes()
{
$types = array();
foreach ($this->providers as $provider) {
$types[$provider->getType()] = $provider->getName();
}
asort($types);
return array(self::TYPE_AUTO => t('Auto')) + $types;
}
/**
* Get dependency label from a provider
*
* @access public
* @param string $type
* @param string $dependency
* @return string
*/
public function getDependencyLabel($type, $dependency)
{
$provider = $this->getProvider($type);
$dependencies = $provider->getDependencies();
return isset($dependencies[$dependency]) ? $dependencies[$dependency] : $dependency;
}
/**
* Find a provider that match
*
* @access public
* @throws ExternalLinkProviderNotFound
* @return ExternalLinkProviderInterface
*/
public function find()
{
$provider = null;
if ($this->userInputType === self::TYPE_AUTO) {
$provider = $this->findProvider();
} else {
$provider = $this->getProvider($this->userInputType);
$provider->setUserTextInput($this->userInputText);
}
if ($provider === null) {
throw new ExternalLinkProviderNotFound('Unable to find link information from provided information');
}
return $provider;
}
/**
* Set form values
*
* @access public
* @param array $values
* @return ExternalLinkManager
*/
public function setUserInput(array $values)
{
$this->userInputType = empty($values['type']) ? self::TYPE_AUTO : $values['type'];
$this->userInputText = empty($values['text']) ? '' : trim($values['text']);
return $this;
}
/**
* Find a provider that user input
*
* @access private
* @return ExternalLinkProviderInterface
*/
private function findProvider()
{
foreach ($this->providers as $provider) {
$provider->setUserTextInput($this->userInputText);
if ($provider->match()) {
return $provider;
}
}
return null;
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Kanboard\Core\ExternalLink;
/**
* External Link Provider Interface
*
* @package externalLink
* @author Frederic Guillot
*/
interface ExternalLinkProviderInterface
{
/**
* Get provider name (label)
*
* @access public
* @return string
*/
public function getName();
/**
* Get link type (will be saved in the database)
*
* @access public
* @return string
*/
public function getType();
/**
* Get a dictionary of supported dependency types by the provider
*
* Example:
*
* [
* 'related' => t('Related'),
* 'child' => t('Child'),
* 'parent' => t('Parent'),
* 'self' => t('Self'),
* ]
*
* The dictionary key is saved in the database.
*
* @access public
* @return array
*/
public function getDependencies();
/**
* Set text entered by the user
*
* @access public
* @param string $input
*/
public function setUserTextInput($input);
/**
* Return true if the provider can parse correctly the user input
*
* @access public
* @return boolean
*/
public function match();
/**
* Get the link found with the properties
*
* @access public
* @return ExternalLinkInterface
*/
public function getLink();
}

View File

@ -0,0 +1,15 @@
<?php
namespace Kanboard\Core\ExternalLink;
use Exception;
/**
* External Link Provider Not Found Exception
*
* @package externalLink
* @author Frederic Guillot
*/
class ExternalLinkProviderNotFound extends Exception
{
}

View File

@ -33,6 +33,19 @@ class Client extends Base
*/
const HTTP_USER_AGENT = 'Kanboard';
/**
* Send a GET HTTP request
*
* @access public
* @param string $url
* @param string[] $headers
* @return string
*/
public function get($url, array $headers = array())
{
return $this->doRequest('GET', $url, '', $headers);
}
/**
* Send a GET HTTP request and parse JSON response
*

View File

@ -68,11 +68,12 @@ class Response extends Base
*
* @access public
* @param string $url Redirection URL
* @param boolean $self If Ajax request and true: refresh the current page
*/
public function redirect($url)
public function redirect($url, $self = false)
{
if ($this->request->getServerVariable('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest') {
header('X-Ajax-Redirect: '.$url);
header('X-Ajax-Redirect: '.($self ? 'self' : $url));
} else {
header('Location: '.$url);
}

View File

@ -0,0 +1,26 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkInterface;
/**
* Attachment Link
*
* @package externalLink
* @author Frederic Guillot
*/
class AttachmentLink extends BaseLink implements ExternalLinkInterface
{
/**
* Get link title
*
* @access public
* @return string
*/
public function getTitle()
{
$path = parse_url($this->url, PHP_URL_PATH);
return basename($path);
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface;
/**
* Attachment Link Provider
*
* @package externalLink
* @author Frederic Guillot
*/
class AttachmentLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface
{
/**
* File extensions that are not attachments
*
* @access protected
* @var array
*/
protected $extensions = array(
'html',
'htm',
'xhtml',
'php',
'jsp',
'do',
'action',
'asp',
'aspx',
'cgi',
);
/**
* Get provider name
*
* @access public
* @return string
*/
public function getName()
{
return t('Attachment');
}
/**
* Get link type
*
* @access public
* @return string
*/
public function getType()
{
return 'attachment';
}
/**
* Get a dictionary of supported dependency types by the provider
*
* @access public
* @return array
*/
public function getDependencies()
{
return array(
'related' => t('Related'),
);
}
/**
* Return true if the provider can parse correctly the user input
*
* @access public
* @return boolean
*/
public function match()
{
if (preg_match('/^https?:\/\/.*\.([^\/]+)$/', $this->userInput, $matches)) {
return $this->isValidExtension($matches[1]);
}
return false;
}
/**
* Get the link found with the properties
*
* @access public
* @return ExternalLinkInterface
*/
public function getLink()
{
$link = new AttachmentLink($this->container);
$link->setUrl($this->userInput);
return $link;
}
/**
* Check file extension
*
* @access protected
* @param string $extension
* @return boolean
*/
protected function isValidExtension($extension)
{
$extension = strtolower($extension);
foreach ($this->extensions as $ext) {
if ($extension === $ext) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\Base;
/**
* Base Link
*
* @package externalLink
* @author Frederic Guillot
*/
abstract class BaseLink extends Base
{
/**
* URL
*
* @access protected
* @var string
*/
protected $url = '';
/**
* Get link URL
*
* @access public
* @return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set link URL
*
* @access public
* @param string $url
*/
public function setUrl($url)
{
$this->url = $url;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\Base;
/**
* Base Link Provider
*
* @package externalLink
* @author Frederic Guillot
*/
abstract class BaseLinkProvider extends Base
{
/**
* User input
*
* @access protected
* @var string
*/
protected $userInput = '';
/**
* Set text entered by the user
*
* @access public
* @param string $input
*/
public function setUserTextInput($input)
{
$this->userInput = trim($input);
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkInterface;
/**
* Web Link
*
* @package externalLink
* @author Frederic Guillot
*/
class WebLink extends BaseLink implements ExternalLinkInterface
{
/**
* Get link title
*
* @access public
* @return string
*/
public function getTitle()
{
$html = $this->httpClient->get($this->url);
if (preg_match('/<title>(.*)<\/title>/siU', $html, $matches)) {
return trim($matches[1]);
}
$components = parse_url($this->url);
if (! empty($components['host']) && ! empty($components['path'])) {
return $components['host'].$components['path'];
}
return t('Title not found');
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Kanboard\ExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface;
/**
* Web Link Provider
*
* @package externalLink
* @author Frederic Guillot
*/
class WebLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface
{
/**
* Get provider name
*
* @access public
* @return string
*/
public function getName()
{
return t('Web Link');
}
/**
* Get link type
*
* @access public
* @return string
*/
public function getType()
{
return 'weblink';
}
/**
* Get a dictionary of supported dependency types by the provider
*
* @access public
* @return array
*/
public function getDependencies()
{
return array(
'related' => t('Related'),
);
}
/**
* Return true if the provider can parse correctly the user input
*
* @access public
* @return boolean
*/
public function match()
{
$startWithHttp = strpos($this->userInput, 'http://') === 0 || strpos($this->userInput, 'https://') === 0;
$validUrl = filter_var($this->userInput, FILTER_VALIDATE_URL);
return $startWithHttp && $validUrl;
}
/**
* Get the link found with the properties
*
* @access public
* @return ExternalLinkInterface
*/
public function getLink()
{
$link = new WebLink($this->container);
$link->setUrl($this->userInput);
return $link;
}
}

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
'Highest priority' => 'Höchste Priorität',
'If you put zero to the low and high priority, this feature will be disabled.' => 'Wenn Sie Null bei höchster und niedrigster Priorität eintragen, wird diese Funktion deaktiviert.',
'Priority: %d' => 'Priorität: %d',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1100,4 +1100,30 @@ return array(
'Highest priority' => 'Priorité haute',
'If you put zero to the low and high priority, this feature will be disabled.' => 'Si vous mettez zéro pour la priorité basse et haute, cette fonctionnalité sera désactivée.',
'Priority: %d' => 'Priorité : %d',
'Close a task when there is no activity' => 'Fermer une tâche sans activité',
'Duration in days' => 'Durée en jours',
'Send email when there is no activity on a task' => 'Envoyer un email lorsqu\'il n\'y a pas d\'activité sur une tâche',
'List of external links' => 'Liste des liens externes',
'Unable to fetch link information.' => 'Impossible de récupérer les informations sur le lien.',
'Daily background job for tasks' => 'Tâche planifée quotidienne pour les tâches',
'Auto' => 'Auto',
'Related' => 'Relié',
'Attachment' => 'Pièce-jointe',
'Title not found' => 'Titre non trouvé',
'Web Link' => 'Lien web',
'External links' => 'Liens externes',
'Add external link' => 'Ajouter un lien externe',
'Type' => 'Type',
'Dependency' => 'Dépendance',
'View internal links' => 'Voir les liens internes',
'View external links' => 'Voir les liens externes',
'Add internal link' => 'Ajouter un lien interne',
'Add a new external link' => 'Ajouter un nouveau lien externe',
'Edit external link' => 'Modifier un lien externe',
'External link' => 'Lien externe',
'Copy and paste your link here...' => 'Copier-coller vôtre lien ici...',
'URL' => 'URL',
'There is no external link for the moment.' => 'Il n\'y a pas de lien externe pour le moment.',
'Internal links' => 'Liens internes',
'There is no internal link for the moment.' => 'Il n\'y a pas de lien interne pour le moment.',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
'Highest priority' => 'Priorità massima',
'If you put zero to the low and high priority, this feature will be disabled.' => 'Se imposti a zero la priorità massima e minima, questa funzionalità sarà disabilitata.',
'Priority: %d' => 'Priorità: %d',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
'Highest priority' => 'Prioridade mais alta',
'If you put zero to the low and high priority, this feature will be disabled.' => 'Se colocar zero na prioridade baixa ou alta, essa funcionalidade será desactivada.',
'Priority: %d' => 'Prioridade: %d',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
'Highest priority' => 'Нивысший приоритет',
'If you put zero to the low and high priority, this feature will be disabled.' => 'Если Вы введете 0 для наименьшего и наивысшего приоритета, этот функционал будет отключен.',
'Priority: %d' => 'Приоритет: %d',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -1097,4 +1097,30 @@ return array(
// 'Highest priority' => '',
// 'If you put zero to the low and high priority, this feature will be disabled.' => '',
// 'Priority: %d' => '',
// 'Close a task when there is no activity' => '',
// 'Duration in days' => '',
// 'Send email when there is no activity on a task' => '',
// 'List of external links' => '',
// 'Unable to fetch link information.' => '',
// 'Daily background job for tasks' => '',
// 'Auto' => '',
// 'Related' => '',
// 'Attachment' => '',
// 'Title not found' => '',
// 'Web Link' => '',
// 'External links' => '',
// 'Add external link' => '',
// 'Type' => '',
// 'Dependency' => '',
// 'View internal links' => '',
// 'View external links' => '',
// 'Add internal link' => '',
// 'Add a new external link' => '',
// 'Edit external link' => '',
// 'External link' => '',
// 'Copy and paste your link here...' => '',
// 'URL' => '',
// 'There is no external link for the moment.' => '',
// 'Internal links' => '',
// 'There is no internal link for the moment.' => '',
);

View File

@ -0,0 +1,99 @@
<?php
namespace Kanboard\Model;
/**
* Task External Link Model
*
* @package model
* @author Frederic Guillot
*/
class TaskExternalLink extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'task_has_external_links';
/**
* Get all links
*
* @access public
* @param integer $task_id
* @return array
*/
public function getAll($task_id)
{
$types = $this->externalLinkManager->getTypes();
$links = $this->db->table(self::TABLE)
->columns(self::TABLE.'.*', User::TABLE.'.name AS creator_name', User::TABLE.'.username AS creator_username')
->eq('task_id', $task_id)
->asc('title')
->join(User::TABLE, 'id', 'creator_id')
->findAll();
foreach ($links as &$link) {
$link['dependency_label'] = $this->externalLinkManager->getDependencyLabel($link['link_type'], $link['dependency']);
$link['type'] = isset($types[$link['link_type']]) ? $types[$link['link_type']] : t('Unknown');
}
return $links;
}
/**
* Get link
*
* @access public
* @param integer $link_id
* @return array
*/
public function getById($link_id)
{
return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne();
}
/**
* Add a new link in the database
*
* @access public
* @param array $values Form values
* @return boolean|integer
*/
public function create(array $values)
{
unset($values['id']);
$values['creator_id'] = $this->userSession->getId();
$values['date_creation'] = time();
$values['date_modification'] = $values['date_creation'];
return $this->persist(self::TABLE, $values);
}
/**
* Modify external link
*
* @access public
* @param array $values Form values
* @return boolean
*/
public function update(array $values)
{
$values['date_modification'] = time();
return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
}
/**
* Remove a link
*
* @access public
* @param integer $link_id
* @return boolean
*/
public function remove($link_id)
{
return $this->db->table(self::TABLE)->eq('id', $link_id)->remove();
}
}

View File

@ -88,11 +88,12 @@ class TaskFinder extends Base
return $this->db
->table(Task::TABLE)
->columns(
'(SELECT count(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments',
'(SELECT count(*) FROM '.File::TABLE.' WHERE task_id=tasks.id) AS nb_files',
'(SELECT count(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks',
'(SELECT count(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks',
'(SELECT count(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links',
'(SELECT COUNT(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments',
'(SELECT COUNT(*) FROM '.File::TABLE.' WHERE task_id=tasks.id) AS nb_files',
'(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks',
'(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks',
'(SELECT COUNT(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links',
'(SELECT COUNT(*) FROM '.TaskExternalLink::TABLE.' WHERE '.TaskExternalLink::TABLE.'.task_id = tasks.id) AS nb_external_links',
'(SELECT DISTINCT 1 FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id AND '.TaskLink::TABLE.'.link_id = 9) AS is_milestone',
'tasks.id',
'tasks.reference',

View File

@ -265,7 +265,7 @@ class User extends Base
*
* @access public
* @param array $values Form values
* @return array
* @return boolean
*/
public function update(array $values)
{

View File

@ -6,7 +6,25 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
const VERSION = 103;
const VERSION = 104;
function version_104(PDO $pdo)
{
$pdo->exec("
CREATE TABLE task_has_external_links (
id INT NOT NULL AUTO_INCREMENT,
link_type VARCHAR(100) NOT NULL,
dependency VARCHAR(100) NOT NULL,
title VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
date_creation INT NOT NULL,
date_modification INT NOT NULL,
task_id INT NOT NULL,
creator_id INT DEFAULT 0,
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
) ENGINE=InnoDB CHARSET=utf8
");
}
function version_103(PDO $pdo)
{

View File

@ -6,7 +6,25 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
const VERSION = 83;
const VERSION = 84;
function version_84(PDO $pdo)
{
$pdo->exec("
CREATE TABLE task_has_external_links (
id SERIAL,
link_type VARCHAR(100) NOT NULL,
dependency VARCHAR(100) NOT NULL,
title VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
date_creation INT NOT NULL,
date_modification INT NOT NULL,
task_id INT NOT NULL,
creator_id INT DEFAULT 0,
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
)
");
}
function version_83(PDO $pdo)
{

View File

@ -6,7 +6,25 @@ use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
use PDO;
const VERSION = 95;
const VERSION = 96;
function version_96(PDO $pdo)
{
$pdo->exec("
CREATE TABLE task_has_external_links (
id INTEGER PRIMARY KEY,
link_type TEXT NOT NULL,
dependency TEXT NOT NULL,
title TEXT NOT NULL,
url TEXT NOT NULL,
date_creation INTEGER NOT NULL,
date_modification INTEGER NOT NULL,
task_id INTEGER NOT NULL,
creator_id INTEGER DEFAULT 0,
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE
)
");
}
function version_95(PDO $pdo)
{

View File

@ -89,6 +89,9 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('Taskduplication', '*', Role::PROJECT_MEMBER);
$acl->add('TaskImport', '*', Role::PROJECT_MANAGER);
$acl->add('Tasklink', '*', Role::PROJECT_MEMBER);
$acl->add('Tasklink', array('show'), Role::PROJECT_VIEWER);
$acl->add('TaskExternalLink', '*', Role::PROJECT_MEMBER);
$acl->add('TaskExternalLink', array('show'), Role::PROJECT_VIEWER);
$acl->add('Taskmodification', '*', Role::PROJECT_MEMBER);
$acl->add('Taskstatus', '*', Role::PROJECT_MEMBER);
$acl->add('Timer', '*', Role::PROJECT_MEMBER);

View File

@ -61,6 +61,7 @@ class ClassProvider implements ServiceProviderInterface
'TaskCreation',
'TaskDuplication',
'TaskExport',
'TaskExternalLink',
'TaskFinder',
'TaskFilter',
'TaskLink',
@ -97,6 +98,7 @@ class ClassProvider implements ServiceProviderInterface
'CommentValidator',
'CurrencyValidator',
'CustomFilterValidator',
'ExternalLinkValidator',
'GroupValidator',
'LinkValidator',
'PasswordResetValidator',

View File

@ -0,0 +1,34 @@
<?php
namespace Kanboard\ServiceProvider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\ExternalLink\ExternalLinkManager;
use Kanboard\ExternalLink\WebLinkProvider;
use Kanboard\ExternalLink\AttachmentLinkProvider;
/**
* External Link Provider
*
* @package serviceProvider
* @author Frederic Guillot
*/
class ExternalLinkProvider implements ServiceProviderInterface
{
/**
* Register providers
*
* @access public
* @param \Pimple\Container $container
* @return \Pimple\Container
*/
public function register(Container $container)
{
$container['externalLinkManager'] = new ExternalLinkManager($container);
$container['externalLinkManager']->register(new WebLinkProvider($container));
$container['externalLinkManager']->register(new AttachmentLinkProvider($container));
return $container;
}
}

View File

@ -106,11 +106,18 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('project/:project_id/task/:task_id/screenshot', 'file', 'screenshot');
$container['route']->addRoute('project/:project_id/task/:task_id/upload', 'file', 'create');
$container['route']->addRoute('project/:project_id/task/:task_id/comment', 'comment', 'create');
$container['route']->addRoute('project/:project_id/task/:task_id/links', 'tasklink', 'show');
$container['route']->addRoute('project/:project_id/task/:task_id/link', 'tasklink', 'create');
$container['route']->addRoute('project/:project_id/task/:task_id/transitions', 'task', 'transitions');
$container['route']->addRoute('project/:project_id/task/:task_id/analytics', 'task', 'analytics');
$container['route']->addRoute('project/:project_id/task/:task_id/remove', 'task', 'remove');
$container['route']->addRoute('project/:project_id/task/:task_id/links/external', 'TaskExternalLink', 'show');
$container['route']->addRoute('project/:project_id/task/:task_id/link/external/new', 'TaskExternalLink', 'find');
$container['route']->addRoute('project/:project_id/task/:task_id/link/external/save', 'TaskExternalLink', 'create');
$container['route']->addRoute('project/:project_id/task/:task_id/link/external/:link_id', 'TaskExternalLink', 'edit');
$container['route']->addRoute('project/:project_id/task/:task_id/link/external/:link_id/remove', 'TaskExternalLink', 'confirm');
$container['route']->addRoute('project/:project_id/task/:task_id/edit', 'taskmodification', 'edit');
$container['route']->addRoute('project/:project_id/task/:task_id/description', 'taskmodification', 'description');
$container['route']->addRoute('project/:project_id/task/:task_id/recurrence', 'taskmodification', 'recurrence');

View File

@ -35,7 +35,11 @@
<?php endif ?>
<?php if (! empty($task['nb_links'])): ?>
<span title="<?= t('Links') ?>" class="tooltip" data-href="<?= $this->url->href('BoardTooltip', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork"></i>&nbsp;<?= $task['nb_links'] ?></span>
<span title="<?= t('Links') ?>" class="tooltip" data-href="<?= $this->url->href('BoardTooltip', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork fa-fw"></i><?= $task['nb_links'] ?></span>
<?php endif ?>
<?php if (! empty($task['nb_external_links'])): ?>
<span title="<?= t('External links') ?>" class="tooltip" data-href="<?= $this->url->href('BoardTooltip', 'externallinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-external-link fa-fw"></i><?= $task['nb_external_links'] ?></span>
<?php endif ?>
<?php if (! empty($task['nb_subtasks'])): ?>

View File

@ -7,6 +7,7 @@
<li><i class="fa fa-pencil-square-o fa-fw"></i>&nbsp;<?= $this->url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
<li><i class="fa fa-comment-o fa-fw"></i>&nbsp;<?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
<li><i class="fa fa-code-fork fa-fw"></i>&nbsp;<?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
<li><i class="fa fa-external-link fa-fw"></i>&nbsp;<?= $this->url->link(t('Add external link'), 'TaskExternalLink', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
<li><i class="fa fa-camera fa-fw"></i>&nbsp;<?= $this->url->link(t('Add a screenshot'), 'BoardPopover', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
<?php if ($task['is_active'] == 1): ?>
<li><i class="fa fa-close fa-fw"></i>&nbsp;<?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?></li>

View File

@ -0,0 +1,20 @@
<table class="table-striped table-small">
<tr>
<th class="column-20"><?= t('Type') ?></th>
<th class="column-80"><?= t('Title') ?></th>
<th class="column-10"><?= t('Dependency') ?></th>
</tr>
<?php foreach ($links as $link): ?>
<tr>
<td>
<?= $link['type'] ?>
</td>
<td>
<a href="<?= $link['url'] ?>" target="_blank"><?= $this->e($link['title']) ?></a>
</td>
<td>
<?= $this->e($link['dependency_label']) ?>
</td>
</tr>
<?php endforeach ?>
</table>

View File

@ -2,7 +2,7 @@
<h2><?= t('Add a comment') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('comment', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => isset($ajax))) ?>" autocomplete="off" class="form-comment">
<form method="post" action="<?= $this->url->href('comment', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off" class="form-comment">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', $values) ?>
<?= $this->form->hidden('user_id', $values) ?>
@ -41,7 +41,7 @@
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
<?php if (! isset($skip_cancel)): ?>
<?= t('or') ?>
<?php if (isset($ajax)): ?>
<?php if ($ajax): ?>
<?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id'])) ?>
<?php else: ?>
<?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>

View File

@ -28,7 +28,8 @@
'task_id' => $task['id'],
),
'errors' => array(),
'task' => $task
'task' => $task,
'ajax' => $ajax,
)) ?>
<?php endif ?>
</div>

View File

@ -13,14 +13,6 @@
<?= $this->render('task/description', array('task' => $task)) ?>
<?= $this->render('tasklink/show', array(
'task' => $task,
'links' => $links,
'link_label_list' => $link_label_list,
'editable' => $this->user->hasProjectAccess('tasklink', 'edit', $project['id']),
'is_public' => false,
)) ?>
<?= $this->render('subtask/show', array(
'task' => $task,
'subtasks' => $subtasks,
@ -29,6 +21,14 @@
'editable' => $this->user->hasProjectAccess('subtask', 'edit', $project['id']),
)) ?>
<?= $this->render('tasklink/show', array(
'task' => $task,
'links' => $links,
'link_label_list' => $link_label_list,
'editable' => $this->user->hasProjectAccess('tasklink', 'edit', $project['id']),
'is_public' => false,
)) ?>
<?= $this->render('task/time_tracking_summary', array('task' => $task)) ?>
<?= $this->render('file/show', array(
@ -42,4 +42,5 @@
'comments' => $comments,
'project' => $project,
'editable' => $this->user->hasProjectAccess('comment', 'edit', $project['id']),
'ajax' => $ajax,
)) ?>

View File

@ -21,6 +21,25 @@
<?= $this->hook->render('template:task:sidebar:information') ?>
</ul>
<h2><?= t('Links') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('tasklink', 'show') ?>>
<?= $this->url->link(t('View internal links'), 'tasklink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('TaskExternalLink', 'show') ?>>
<?= $this->url->link(t('View external links'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('tasklink', 'create', $task['project_id'])): ?>
<li <?= $this->app->checkMenuSelection('tasklink', 'create') ?>>
<?= $this->url->link(t('Add internal link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('TaskExternalLink', 'find') ?>>
<?= $this->url->link(t('Add external link'), 'TaskExternalLink', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<?php endif ?>
</ul>
<?php if ($this->user->hasProjectAccess('taskmodification', 'edit', $task['project_id'])): ?>
<h2><?= t('Actions') ?></h2>
<ul>
@ -36,9 +55,6 @@
<li <?= $this->app->checkMenuSelection('subtask', 'create') ?>>
<?= $this->url->link(t('Add a sub-task'), 'subtask', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('tasklink', 'create') ?>>
<?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('comment', 'create') ?>>
<?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>

View File

@ -10,7 +10,7 @@
</div>
<?php endif ?>
<form id="task-form" method="post" action="<?= $this->url->href('taskcreation', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off">
<form id="task-form" class="popover-form" method="post" action="<?= $this->url->href('taskcreation', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>

View File

@ -0,0 +1,13 @@
<div class="page-header">
<h2><?= t('Add a new external link') ?></h2>
</div>
<form class="popover-form" action="<?= $this->url->href('TaskExternalLink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" method="post" autocomplete="off">
<?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue">
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
</div>
</form>

View File

@ -0,0 +1,13 @@
<div class="page-header">
<h2><?= t('Edit external link') ?></h2>
</div>
<form class="popover-form" action="<?= $this->url->href('TaskExternalLink', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" method="post" autocomplete="off">
<?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue">
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
</div>
</form>

View File

@ -0,0 +1,32 @@
<div class="page-header">
<h2><?= t('Add a new external link') ?></h2>
</div>
<form class="popover-form" action="<?= $this->url->href('TaskExternalLink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" method="post" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
<?= $this->form->label(t('External link'), 'text') ?>
<?= $this->form->text(
'text',
$values,
$errors,
array(
'required',
'autofocus',
'placeholder="'.t('Copy and paste your link here...').'"',
)) ?>
<?= $this->form->label(t('Link type'), 'type') ?>
<?= $this->form->select('type', $types, $values) ?>
<div class="form-actions">
<input type="submit" value="<?= t('Next') ?>" class="btn btn-blue"/>
<?= t('or') ?>
<?php if ($ajax): ?>
<?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id']), false, 'close-popover') ?>
<?php else: ?>
<?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
<?php endif ?>
</div>
</form>

View File

@ -0,0 +1,13 @@
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->hidden('link_type', $values) ?>
<?= $this->form->label(t('URL'), 'url') ?>
<?= $this->form->text('url', $values, $errors, array('required')) ?>
<?= $this->form->label(t('Title'), 'title') ?>
<?= $this->form->text('title', $values, $errors, array('required')) ?>
<?= $this->form->label(t('Dependency'), 'dependency') ?>
<?= $this->form->select('dependency', $dependencies, $values, $errors) ?>

View File

@ -0,0 +1,15 @@
<div class="page-header">
<h2><?= t('Remove a link') ?></h2>
</div>
<div class="confirm">
<p class="alert alert-info">
<?= t('Do you really want to remove this link: "%s"?', $link['title']) ?>
</p>
<div class="form-actions">
<?= $this->url->link(t('Yes'), 'TaskExternalLink', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?>
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</div>
</div>

View File

@ -0,0 +1,50 @@
<div class="page-header">
<h2><?= t('External links') ?></h2>
</div>
<?php if (empty($links)): ?>
<p class="alert"><?= t('There is no external link for the moment.') ?></p>
<?php else: ?>
<table class="table-stripped table-small">
<tr>
<th class="column-10"><?= t('Type') ?></th>
<th><?= t('Title') ?></th>
<th class="column-10"><?= t('Dependency') ?></th>
<th class="column-15"><?= t('Creator') ?></th>
<th class="column-15"><?= t('Date') ?></th>
<?php if ($this->user->hasProjectAccess('TaskExternalLink', 'edit', $task['project_id'])): ?>
<th class="column-5"><?= t('Action') ?></th>
<?php endif ?>
</tr>
<?php foreach ($links as $link): ?>
<tr>
<td>
<?= $link['type'] ?>
</td>
<td>
<a href="<?= $link['url'] ?>" target="_blank"><?= $this->e($link['title']) ?></a>
</td>
<td>
<?= $this->e($link['dependency_label']) ?>
</td>
<td>
<?= $this->e($link['creator_name'] ?: $link['creator_username']) ?>
</td>
<td>
<?= dt('%B %e, %Y', $link['date_creation']) ?>
</td>
<?php if ($this->user->hasProjectAccess('TaskExternalLink', 'edit', $task['project_id'])): ?>
<td>
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li><?= $this->url->link(t('Edit'), 'TaskExternalLink', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?></li>
<li><?= $this->url->link(t('Remove'), 'TaskExternalLink', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?></li>
</ul>
</div>
</td>
<?php endif ?>
</tr>
<?php endforeach ?>
</table>
<?php endif ?>

View File

@ -2,7 +2,7 @@
<h2><?= t('Add a new link') ?></h2>
</div>
<form action="<?= $this->url->href('tasklink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => isset($ajax))) ?>" method="post" autocomplete="off">
<form action="<?= $this->url->href('tasklink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" method="post" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
@ -28,7 +28,7 @@
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
<?= t('or') ?>
<?php if (isset($ajax)): ?>
<?php if ($ajax): ?>
<?= $this->url->link(t('cancel'), 'board', 'show', array('project_id' => $task['project_id']), false, 'close-popover') ?>
<?php else: ?>
<?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>

View File

@ -1,15 +1,17 @@
<?php if (! empty($links)): ?>
<div class="page-header">
<h2><?= t('Links') ?></h2>
<h2><?= t('Internal links') ?></h2>
</div>
<table id="links">
<?php if (empty($links)): ?>
<p class="alert"><?= t('There is no internal link for the moment.') ?></p>
<?php else: ?>
<table id="links" class="table-small table-stripped">
<tr>
<th class="column-20"><?= t('Label') ?></th>
<th class="column-30"><?= t('Task') ?></th>
<th class="column-20"><?= t('Project') ?></th>
<th><?= t('Column') ?></th>
<th><?= t('Assignee') ?></th>
<?php if ($editable): ?>
<?php if ($editable && $this->user->hasProjectAccess('Tasklink', 'edit', $task['project_id'])): ?>
<th class="column-5"><?= t('Action') ?></th>
<?php endif ?>
</tr>
@ -64,7 +66,7 @@
<?php endif ?>
<?php endif ?>
</td>
<?php if ($editable): ?>
<?php if ($editable && $this->user->hasProjectAccess('Tasklink', 'edit', $task['project_id'])): ?>
<td>
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>

View File

@ -0,0 +1,76 @@
<?php
namespace Kanboard\Validator;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* External Link Validator
*
* @package validator
* @author Frederic Guillot
*/
class ExternalLinkValidator extends Base
{
/**
* Validate creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values)
{
$v = new Validator($values, $this->commonValidationRules());
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Common validation rules
*
* @access private
* @return array
*/
private function commonValidationRules()
{
return array(
new Validators\Required('url', t('Field required')),
new Validators\MaxLength('url', t('The maximum length is %d characters', 255), 255),
new Validators\Required('title', t('Field required')),
new Validators\MaxLength('title', t('The maximum length is %d characters', 255), 255),
new Validators\Required('link_type', t('Field required')),
new Validators\MaxLength('link_type', t('The maximum length is %d characters', 100), 100),
new Validators\Required('dependency', t('Field required')),
new Validators\MaxLength('dependency', t('The maximum length is %d characters', 100), 100),
new Validators\Integer('id', t('This value must be an integer')),
new Validators\Required('task_id', t('Field required')),
new Validators\Integer('task_id', t('This value must be an integer')),
);
}
}

View File

@ -34,4 +34,5 @@ $container->register(new Kanboard\ServiceProvider\EventDispatcherProvider);
$container->register(new Kanboard\ServiceProvider\GroupProvider);
$container->register(new Kanboard\ServiceProvider\RouteProvider);
$container->register(new Kanboard\ServiceProvider\ActionProvider);
$container->register(new Kanboard\ServiceProvider\ExternalLinkProvider);
$container->register(new Kanboard\ServiceProvider\PluginProvider);

File diff suppressed because one or more lines are too long

View File

@ -55,22 +55,25 @@ Popover.prototype.listen = function() {
Popover.prototype.afterOpen = function() {
var self = this;
var taskForm = $("#task-form");
var popoverForm = $(".popover-form");
if (taskForm) {
taskForm.on("submit", function(e) {
if (popoverForm) {
popoverForm.on("submit", function(e) {
e.preventDefault();
$.ajax({
type: "POST",
url: taskForm.attr("action"),
data: taskForm.serialize(),
url: popoverForm.attr("action"),
data: popoverForm.serialize(),
success: function(data, textStatus, request) {
if (request.getResponseHeader("X-Ajax-Redirect")) {
window.location = request.getResponseHeader("X-Ajax-Redirect");
var redirect = request.getResponseHeader("X-Ajax-Redirect");
if (redirect) {
window.location = redirect === 'self' ? window.location.href : redirect;
}
else {
$("#popover-content").html(data);
$("input[autofocus]").focus();
self.afterOpen();
}
}

View File

@ -0,0 +1,120 @@
<?php
require_once __DIR__.'/../../Base.php';
use Kanboard\Core\ExternalLink\ExternalLinkManager;
use Kanboard\ExternalLink\WebLinkProvider;
use Kanboard\ExternalLink\AttachmentLinkProvider;
class ExternalLinkManagerTest extends Base
{
public function testRegister()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$this->assertInstanceOf(get_class($webLinkProvider), $externalLinkManager->getProvider($webLinkProvider->getType()));
$this->assertInstanceOf(get_class($attachmentLinkProvider), $externalLinkManager->getProvider($attachmentLinkProvider->getType()));
}
public function testGetProviderNotFound()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$this->setExpectedException('\Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound');
$externalLinkManager->getProvider('not found');
}
public function testGetTypes()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$this->assertEquals(array(ExternalLinkManager::TYPE_AUTO => 'Auto'), $externalLinkManager->getTypes());
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$this->assertEquals(
array(ExternalLinkManager::TYPE_AUTO => 'Auto', 'attachment' => 'Attachment', 'weblink' => 'Web Link'),
$externalLinkManager->getTypes()
);
}
public function testGetDependencyLabel()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$this->assertSame('Related', $externalLinkManager->getDependencyLabel($webLinkProvider->getType(), 'related'));
$this->assertSame('custom', $externalLinkManager->getDependencyLabel($webLinkProvider->getType(), 'custom'));
}
public function testFindProviderNotFound()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$this->setExpectedException('\Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound');
$externalLinkManager->find();
}
public function testFindProvider()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$externalLinkManager->setUserInput(array('text' => 'https://google.com/', 'type' => ExternalLinkManager::TYPE_AUTO));
$this->assertSame($webLinkProvider, $externalLinkManager->find());
$externalLinkManager->setUserInput(array('text' => 'https://google.com/file.pdf', 'type' => ExternalLinkManager::TYPE_AUTO));
$this->assertSame($attachmentLinkProvider, $externalLinkManager->find());
}
public function testFindProviderWithSelectedType()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$externalLinkManager->setUserInput(array('text' => 'https://google.com/', 'type' => $webLinkProvider->getType()));
$this->assertSame($webLinkProvider, $externalLinkManager->find());
$externalLinkManager->setUserInput(array('text' => 'https://google.com/file.pdf', 'type' => $attachmentLinkProvider->getType()));
$this->assertSame($attachmentLinkProvider, $externalLinkManager->find());
}
public function testFindProviderWithSelectedTypeNotFound()
{
$externalLinkManager = new ExternalLinkManager($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$externalLinkManager->register($webLinkProvider);
$externalLinkManager->register($attachmentLinkProvider);
$this->setExpectedException('\Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound');
$externalLinkManager->setUserInput(array('text' => 'https://google.com/', 'type' => 'not found'));
$externalLinkManager->find();
}
}

View File

@ -0,0 +1,64 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\ExternalLink\AttachmentLinkProvider;
class AttachmentLinkProviderTest extends Base
{
public function testGetName()
{
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$this->assertEquals('Attachment', $attachmentLinkProvider->getName());
}
public function testGetType()
{
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$this->assertEquals('attachment', $attachmentLinkProvider->getType());
}
public function testGetDependencies()
{
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$this->assertEquals(array('related' => 'Related'), $attachmentLinkProvider->getDependencies());
}
public function testMatch()
{
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$attachmentLinkProvider->setUserTextInput('http://kanboard.net/FILE.DOC');
$this->assertTrue($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/document.PDF');
$this->assertTrue($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('http://kanboard.net/archive.zip');
$this->assertTrue($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput(' https://kanboard.net/folder/archive.tar ');
$this->assertTrue($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('http:// invalid url');
$this->assertFalse($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('');
$this->assertFalse($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/document.html');
$this->assertFalse($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/DOC.HTML');
$this->assertFalse($attachmentLinkProvider->match());
$attachmentLinkProvider->setUserTextInput('http://kanboard.net/folder/document.do');
$this->assertFalse($attachmentLinkProvider->match());
}
public function testGetLink()
{
$attachmentLinkProvider = new AttachmentLinkProvider($this->container);
$this->assertInstanceOf('\Kanboard\ExternalLink\AttachmentLink', $attachmentLinkProvider->getLink());
}
}

View File

@ -0,0 +1,18 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\ExternalLink\AttachmentLink;
class AttachmentLinkTest extends Base
{
public function testGetTitleFromUrl()
{
$url = 'https://kanboard.net/folder/document.pdf';
$link = new AttachmentLink($this->container);
$link->setUrl($url);
$this->assertEquals($url, $link->getUrl());
$this->assertEquals('document.pdf', $link->getTitle());
}
}

View File

@ -0,0 +1,52 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\ExternalLink\WebLinkProvider;
class WebLinkProviderTest extends Base
{
public function testGetName()
{
$webLinkProvider = new WebLinkProvider($this->container);
$this->assertEquals('Web Link', $webLinkProvider->getName());
}
public function testGetType()
{
$webLinkProvider = new WebLinkProvider($this->container);
$this->assertEquals('weblink', $webLinkProvider->getType());
}
public function testGetDependencies()
{
$webLinkProvider = new WebLinkProvider($this->container);
$this->assertEquals(array('related' => 'Related'), $webLinkProvider->getDependencies());
}
public function testMatch()
{
$webLinkProvider = new WebLinkProvider($this->container);
$webLinkProvider->setUserTextInput('http://kanboard.net/');
$this->assertTrue($webLinkProvider->match());
$webLinkProvider->setUserTextInput('http://kanboard.net/mypage');
$this->assertTrue($webLinkProvider->match());
$webLinkProvider->setUserTextInput(' https://kanboard.net/ ');
$this->assertTrue($webLinkProvider->match());
$webLinkProvider->setUserTextInput('http:// invalid url');
$this->assertFalse($webLinkProvider->match());
$webLinkProvider->setUserTextInput('');
$this->assertFalse($webLinkProvider->match());
}
public function testGetLink()
{
$webLinkProvider = new WebLinkProvider($this->container);
$this->assertInstanceOf('\Kanboard\ExternalLink\WebLink', $webLinkProvider->getLink());
}
}

View File

@ -0,0 +1,57 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\ExternalLink\WebLink;
class WebLinkTest extends Base
{
public function testGetTitleFromHtml()
{
$url = 'http://kanboard.net/something';
$title = 'My title';
$html = '<!DOCTYPE html><html><head><title> '.$title.' </title></head><body>Test</body></html>';
$this->container['httpClient'] = $this
->getMockBuilder('\Kanboard\Core\Http\Client')
->setConstructorArgs(array($this->container))
->setMethods(array('get'))
->getMock();
$webLink = new WebLink($this->container);
$webLink->setUrl($url);
$this->assertEquals($url, $webLink->getUrl());
$this->container['httpClient']
->expects($this->once())
->method('get')
->with($url)
->will($this->returnValue($html));
$this->assertEquals($title, $webLink->getTitle());
}
public function testGetTitleFromUrl()
{
$url = 'http://kanboard.net/something';
$html = '<!DOCTYPE html><html><head></head><body>Test</body></html>';
$this->container['httpClient'] = $this
->getMockBuilder('\Kanboard\Core\Http\Client')
->setConstructorArgs(array($this->container))
->setMethods(array('get'))
->getMock();
$webLink = new WebLink($this->container);
$webLink->setUrl($url);
$this->assertEquals($url, $webLink->getUrl());
$this->container['httpClient']
->expects($this->once())
->method('get')
->with($url)
->will($this->returnValue($html));
$this->assertEquals('kanboard.net/something', $webLink->getTitle());
}
}

View File

@ -0,0 +1,167 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\Model\TaskCreation;
use Kanboard\Model\Project;
use Kanboard\Model\TaskExternalLink;
use Kanboard\Core\ExternalLink\ExternalLinkManager;
use Kanboard\ExternalLink\WebLinkProvider;
class TaskExternalLinkTest extends Base
{
public function testCreate()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related')));
$link = $taskExternalLinkModel->getById(1);
$this->assertNotEmpty($link);
$this->assertEquals('My website', $link['title']);
$this->assertEquals('http://kanboard.net/', $link['url']);
$this->assertEquals('related', $link['dependency']);
$this->assertEquals('weblink', $link['link_type']);
$this->assertEquals(0, $link['creator_id']);
$this->assertEquals(time(), $link['date_modification'], '', 2);
$this->assertEquals(time(), $link['date_creation'], '', 2);
}
public function testCreateWithUserSession()
{
$this->container['sessionStorage']->user = array('id' => 1);
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related')));
$link = $taskExternalLinkModel->getById(1);
$this->assertNotEmpty($link);
$this->assertEquals('My website', $link['title']);
$this->assertEquals('http://kanboard.net/', $link['url']);
$this->assertEquals('related', $link['dependency']);
$this->assertEquals('weblink', $link['link_type']);
$this->assertEquals(1, $link['creator_id']);
$this->assertEquals(time(), $link['date_modification'], '', 2);
$this->assertEquals(time(), $link['date_creation'], '', 2);
}
public function testCreateWithNoType()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertFalse($taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'dependency' => 'related')));
}
public function testCreateWithNoDependency()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertFalse($taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'test')));
}
public function testCreateWithNoTitle()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertFalse($taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'link_type' => 'test', 'dependency' => 'test')));
}
public function testCreateWithNoUrl()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertFalse($taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'title' => 'test', 'link_type' => 'test', 'dependency' => 'test')));
}
public function testModification()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related')));
sleep(1);
$this->assertTrue($taskExternalLinkModel->update(array('id' => 1, 'url' => 'https://kanboard.net/')));
$link = $taskExternalLinkModel->getById(1);
$this->assertNotEmpty($link);
$this->assertEquals('https://kanboard.net/', $link['url']);
$this->assertEquals(time(), $link['date_modification'], '', 2);
}
public function testRemove()
{
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'id' => '', 'url' => 'http://kanboard.net/', 'title' => 'My website', 'link_type' => 'weblink', 'dependency' => 'related')));
$this->assertTrue($taskExternalLinkModel->remove(1));
$this->assertFalse($taskExternalLinkModel->remove(1));
$this->assertEmpty($taskExternalLinkModel->getById(1));
}
public function testGetAll()
{
$this->container['sessionStorage']->user = array('id' => 1);
$this->container['externalLinkManager'] = new ExternalLinkManager($this->container);
$projectModel = new Project($this->container);
$taskCreationModel = new TaskCreation($this->container);
$taskExternalLinkModel = new TaskExternalLink($this->container);
$webLinkProvider = new WebLinkProvider($this->container);
$this->container['externalLinkManager']->register($webLinkProvider);
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(1, $taskExternalLinkModel->create(array('task_id' => 1, 'url' => 'https://miniflux.net/', 'title' => 'MX', 'link_type' => 'weblink', 'dependency' => 'related')));
$this->assertEquals(2, $taskExternalLinkModel->create(array('task_id' => 1, 'url' => 'http://kanboard.net/', 'title' => 'KB', 'link_type' => 'weblink', 'dependency' => 'related')));
$links = $taskExternalLinkModel->getAll(1);
$this->assertCount(2, $links);
$this->assertEquals('KB', $links[0]['title']);
$this->assertEquals('MX', $links[1]['title']);
$this->assertEquals('Web Link', $links[0]['type']);
$this->assertEquals('Web Link', $links[1]['type']);
$this->assertEquals('Related', $links[0]['dependency_label']);
$this->assertEquals('Related', $links[1]['dependency_label']);
$this->assertEquals('admin', $links[0]['creator_username']);
$this->assertEquals('admin', $links[1]['creator_username']);
$this->assertEquals('', $links[0]['creator_name']);
$this->assertEquals('', $links[1]['creator_name']);
}
}

View File

@ -0,0 +1,63 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\Validator\ExternalLinkValidator;
class ExternalLinkValidatorTest extends Base
{
public function testValidateCreation()
{
$validator = new ExternalLinkValidator($this->container);
$result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertTrue($result[0]);
$result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 'abc', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink'));
$this->assertFalse($result[0]);
$result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateCreation(array('url' => 'http://somewhere', 'task_id' => 1, 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateCreation(array('url' => 'http://somewhere', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateCreation(array('task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
}
public function testValidateModification()
{
$validator = new ExternalLinkValidator($this->container);
$result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertTrue($result[0]);
$result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 'abc', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink'));
$this->assertFalse($result[0]);
$result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'task_id' => 1, 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateModification(array('id' => 1, 'url' => 'http://somewhere', 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateModification(array('id' => 1, 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
$result = $validator->validateModification(array('url' => 'http://somewhere', 'task_id' => 1, 'title' => 'Title', 'link_type' => 'weblink', 'dependency' => 'related'));
$this->assertFalse($result[0]);
}
}