1 2 3 4 5 6 7 8 9 |
define('DB_DRIVER', 'mysql'); // Mysql/Postgres username define('DB_USERNAME', 'root'); // Mysql/Postgres password define('DB_PASSWORD', 'password'); // Mysql/Postgres hostname define('DB_HOSTNAME', 'IP Add'); // Mysql/Postgres database name define('DB_NAME', 'kanboard'); |
1 |
vim /etc/nginx/conf.d/kb.conf |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
server { listen 80; server_name kb.mekau.com; root /home/dev/php/kanboard; location / { root /home/dev/php/kanboard; index index.php index.html index.htm; if (-f $request_filename/index.html){ rewrite (.*) $1/index.html break; } if (-f $request_filename/index.php){ rewrite (.*) $1/index.php; } if (!-f $request_filename){ rewrite (.*) /index.php; } } location ~ \.php$ { root /home/dev/php/kanboard; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; #include fastcgi.conf; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } |
1 |
service nginx restart |
1 2 |
$ php artisan config:cache $ php artisan route:cache |
1 2 3 |
username: admin or admin@admin.com password: admin INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$0WR8YPAwOCrDQTRFjji6u.krMgA4PcVsmw3ypmXAkqFKFLwnFOpAG', 'app-admin'); |
1 2 |
chown apache:apache -R data chmod 755 -R data |
$acl->add(‘ProjectUserOverviewController’, ‘*’, Role::PROJECT_MANAGER);
问题修改源码记录:
1 2 |
ALTER TABLE `users` ADD UNIQUE INDEX `email_idx` (`email`) USING BTREE ; |
D:\xampp\htdocs\kanboard\app\Controller\UserModificationController.php 修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public function save() { $user = $this->getUser(); $values = $this->request->getValues(); if (! $this->userSession->isAdmin()) { $values = array( 'id' => $this->userSession->getId(), 'username' => isset($values['username']) ? $values['username'] : '', 'name' => isset($values['name']) ? $values['name'] : '', 'email' => isset($values['email']) ? $values['email'] : '', 'timezone' => isset($values['timezone']) ? $values['timezone'] : '', 'language' => isset($values['language']) ? $values['language'] : '', ); } list($valid, $errors) = $this->userValidator->validateModification($values); if ($valid) { if ($this->userModel->update($values)) { $this->flash->success(t('User updated successfully.')); $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true); return; } else { $this->flash->failure(t('Unable to update this user.')); $this->show($values, array(t('Unable to update this user.'))); return; } } $this->show($values, $errors); } |
D:\xampp\htdocs\kanboard\app\Template\user_modification\show.php
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="page-header"> <h2><?= t('Edit user') ?></h2> </div> <?php if (!empty($errors)): ?> <p class="alert alert-error"> <?php foreach ($errors as $idx=>$msg){ print $msg; } ?> </p> <?php endif ?> |
修改logo:
增加国际化:
kanboard/app/Locale/es_ES/translations.php
1 2 |
'LogoTxt1' => 'K', 'LogoTxt2' => 'B', |
kanboard/app/Locale/zh_CN/translations.php
1 2 |
'LogoTxt1' => '看板', 'LogoTxt2' => '管理系统', |
kanboard/app/Template/header/title.php
1 |
<?= $this->url->link('K<span>B</span>', 'DashboardController', 'show', array(), false, '', t('Dashboard')) ?> |
修改成:
1 |
<?= $this->url->link(t('LogoTxt1').'<span>'.t('LogoTxt2').'</span>', 'DashboardController', 'show', array(), false, '', t('Dashboard')) ?> |
修改logo图片: 备份kanboard/assets/img,然后增加相应的图片就好
删除多余的国际化信息
权限说明: kanboard\app\Core\Security\Role.php
1 2 3 4 5 6 7 8 |
const APP_ADMIN = 'app-admin'; 超级管理员 const APP_MANAGER = 'app-manager'; 管理员 const APP_USER = 'app-user'; 用户 const APP_PUBLIC = 'app-public'; const PROJECT_MANAGER = 'project-manager'; 项目管理员 const PROJECT_MEMBER = 'project-member'; 项目成员 const PROJECT_VIEWER = 'project-viewer'; 项目观察员 |
初始化数据:kanboard\app\ServiceProvider\AuthenticationProvider.php; getProjectAccessMap()
验证数据: kanboard\app\Core\Security\Authorization.php; isAllowed($controller, $method, $role)
发送邮件设定
发送邮件:发送邮件箱 xxxxxxxx@163.com
1. 系统设定邮箱设置: xxxxxxxx@163.com
2. config.php
1 2 3 4 5 6 7 |
define('MAIL_FROM', 'xxxxxxxx@163.com'); define('MAIL_TRANSPORT', 'smtp'); define('MAIL_SMTP_HOSTNAME', 'smtp.163.com'); define('MAIL_SMTP_PORT', 465); define('MAIL_SMTP_USERNAME', 'xxxxxxxx'); define('MAIL_SMTP_PASSWORD', 'xxxxxxxx pwd'); define('MAIL_SMTP_ENCRYPTION', "ssl"); |
3. 发送邮件代码:
1 |
$this->emailClient->send("收件人@163.com", "潘永郑", "测试邮件", "这是一份Kanboard的测试邮件"); |
D:\xampp\htdocs\kanboard\app\Core\Mail\Transport\Mail.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public function sendEmail($recipientEmail, $recipientName, $subject, $html, $authorName, $authorEmail = '') { try { $message = Swift_Message::newInstance() ->setSubject($subject) ->setFrom($this->helper->mail->getMailSenderAddress(), $authorName) //这里获得系统设定的发送邮箱 ->setTo(array($recipientEmail => $recipientName)); if (! empty($authorEmail)) { $message->setReplyTo($authorEmail); //可见,发送是用系统发送,回复邮件地址被改变 } $message->setBody($html, 'text/html'); Swift_Mailer::newInstance($this->getTransport())->send($message); } catch (Swift_TransportException $e) { $this->logger->error($e->getMessage()); } } |
修改源码
修改源码1: 我一个日任务是2017-07-17到2017-08-14, 但是日历无法从2017-07-17开始,而是直接从2017-08-01开始.
当任务的日期超过1个月多10天到15天左右(开始出问题) 那么上个月会先显示一下,然后又被清除掉了。
问题找到了:CalendarController.php–>CalendarHelper.php–>TaskDueDateRangeFilter.php 这里的apply()方法找的是:到期日期大于日历第一日,小于日历最后一日。可能源代码想找的是当月要完成的日期。
但是按道理,应该是找当月包含的日期,那么应该找开始日期小于当月最后一天,或者到期日期大于当月第一天,注释掉第二个sql就好。
解决办法:
1.复制TaskDueDateRangeFilter.php一份,命名为:kanboard/app/Filter/TaskDateRangeFilter.php,修改apply()方法的第二句SQL
1 |
$this->query->lte(TaskModel::TABLE.'.date_due', is_numeric($this->value[1]) ? $this->value[1] : strtotime($this->value[1])); |
修改成:
1 |
$this->query->lte(TaskModel::TABLE.'.date_started', is_numeric($this->value[1]) ? $this->value[1] : strtotime($this->value[1])); |
2.修改kanboard/plugins/Calendar/Helper/CalendarHelper.php的getTaskDateDueEvents():
1 2 3 |
return $queryBuilder ->withFilter(new TaskDueDateRangeFilter(array($start, $end))) ->format($formatter); |
修改成:
1 2 3 |
return $queryBuilder ->withFilter(new TaskDateRangeFilter(array($start, $end))) ->format($formatter); |
修改源码2: 创建任务对话框,到期日期和开始日期位置对换
kanboard/app/Template/task_creation/show.php
kanboard/app/Template/task_modification/show.php
1 2 |
<?= $this->task->renderDueDateField($values, $errors) ?> <?= $this->task->renderStartDateField($values, $errors) ?> |
修改成
1 2 |
<?= $this->task->renderStartDateField($values, $errors) ?> <?= $this->task->renderDueDateField($values, $errors) ?> |
修改源码3: 任务里表的到期日期和开始日期位置对换:
kanboard/app/Template/task/details.php,对换两块代码位置
修改源码4: 修改日期汉化不彻底,参考: http://keenwon.com/752.html
代码位置:kanboard/plugins/Calendar/Assets/calendar.js, 初始化配置增加
1 2 3 4 5 6 7 |
buttonText: { today: '今天', month: '月视图', week: '周视图', day: '日视图' }, allDayText: "全天", |
修改源码5: 修改甘特图的标题: kanboard/plugins/Gantt/Locale/zh_CN/translations.php
在数组前面追加:
1 |
'Gantt' => '甘特图', |
修改源码6: 甘特图百分比问题:
首先,Kanbord的源码里面kanboard/app/Model/TaskModel.php,定义了getProgress(array $task, array $columns)方法,这个是从看板的类型数量来判断的,
当移动到完成的时候,一直是75%,是计算有问题,应该改为:$position = 1;
但是,如果想自定义进度,需要数据库增加字段和源码
1.
1 |
ALTER TABLE `tasks` ADD COLUMN `progress` int(255) NULL AFTER `score`; |
2. kanboard/app/Export/TaskExport.php 增加定义
TaskModel::TABLE . '.progress'
3. kanboard/app/Model/TaskFinderModel.php 的 getProjectUserOverviewQuery(),getExtendedQuery()方法增加:
TaskModel::TABLE.'.progress',
4. 修改: kanboard/app/Model/TaskModel.php 的 getProgress(array $task, array $columns)方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public function getProgress(array $task, array $columns) { return $task['progress']; /*if ($task['is_active'] == self::STATUS_CLOSED) { return 100; } $position = 0; foreach ($columns as $column_id => $column_title) { if ($column_id == $task['column_id']) { break; } $position++; } return round(($position * 100) / count($columns), 1);*/ } |
5. kanboard/app/Locale/zh_CN/translations.php 增加一个翻译:
'Progress' => '进度',
6. kanboard/app/Helper/TaskHelper.php 增加:
1 2 3 4 5 6 7 |
public function renderProgressField(array $values, array $errors = array(), array $attributes = array()) { $attributes = array_merge(array('tabindex="15"'), $attributes); $html = $this->helper->form->label(t('Progress'), 'progress'); $html .= $this->helper->form->number('progress', $values, $errors, $attributes); return $html; } |
7. 修改:
kanboard/app/Template/task_creation/show.php
kanboard/app/Template/task_modification/show.php
增加:
<?= $this->task->renderProgressField($values, $errors) ?>
8. 看板增加进度显示:
/mnt/D/work_documents/htdocs/kanboard/app/Template/board/task_private.php, 在<div class=”task-board-expanded”>增加:
1 2 3 4 5 6 7 8 9 |
<?php if (! empty($task['progress'])): ?> <span class="task-board-progress"> <?= $this->text->e($task['progress'] ?: $task['progress']) ?>% </span> <?php else: ?> <span class="task-board-progress"> 0% </span> <?php endif ?> |
9. 列表增加进度显示:/mnt/D/work_documents/htdocs/kanboard/app/Template/task_list/task_title.php 增加:
1 2 3 4 5 6 7 8 9 |
<?php if (! empty($task['progress'])): ?> <span class="task-board-progress"> <?= $this->text->e($task['progress'] ?: $task['progress']) ?>% </span> <?php else: ?> <span class="task-board-progress"> 0% </span> <?php endif ?> |
10.任务里表显示进度:kanboard/app/Template/task/details.php,增加:
1 2 3 4 5 6 7 8 9 10 11 |
<?php if ($task['progress']): ?> <li> <strong><?= t('Progress') ?>:</strong> <span><?= $task['progress'] ?>%</span> </li> <?php else: ?> <li> <strong><?= t('Progress') ?>:</strong> <span>0%</span> </li> <?php endif ?> |
11. 保存问题: kanboard/app/Model/TaskModificationModel.php 的 prepare(array &$values)方法增加进度转换
1 |
$this->helper->model->convertIntegerFields($values, array('priority', 'is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate')); |
修改成:
1 |
$this->helper->model->convertIntegerFields($values, array('progress','priority', 'is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate')); |
kanboard/app/Model/TaskCreationModel.php的prepare()方法增加:
1 |
$this->helper->model->convertIntegerFields($values, array('progress')); |
修改源码7: TaskBoardDate插件修改:
1 2 3 4 5 6 7 8 |
<?php return array( 'PluginName' => '预计启动', 'PluginDescription' => '设定预计启动日期,仪表板出现预计启动任务。其实就是一个自定义字段的例子。', 'My future tasks' => '预计启动任务', 'Board Date' => '预计启动日期', ); |
kanboard/plugins/TaskBoardDate/Plugin.php
1 2 3 4 5 6 7 8 9 10 11 |
public function getPluginName() { //return 'TaskBoardDate'; return T("PluginName"); } public function getPluginDescription() { //return t('Add a new date field for tasks to define the visibility on the board and dashboard'); return T('PluginDescription'); } |
修改源码8: 给项目配置颜色方便查看:
存储项目颜色:
1 |
ALTER TABLE `projects` ADD COLUMN `color_id` VARCHAR(50) NULL AFTER `name`; |
kanboard/app/Template/project_creation/create.php增加:
1 |
<?= $this->task->renderColorField($values) ?> |
kanboard/app/Model/ProjectModel.php的create()增加:
1 2 3 |
if (empty($values['color_id'])) { $values['color_id'] = $this->colorModel->getDefaultColor(); } |
kanboard/app/Controller/ProjectCreationController.php 的 createNewProject(array $values) 增加:
1 2 3 4 5 6 7 8 9 10 |
private function createNewProject(array $values) { $project = array( 'name' => $values['name'], 'color_id' => $values['color_id'], 'is_private' => $values['is_private'], ); return $this->projectModel->create($project, $this->userSession->getId(), true); } |
修改项目:
kanboard/app/Template/project_edit/show.php加入:
1 |
<?= $this->task->renderColorField($values) ?> |
显示项目颜色:
kanboard/app/Template/project_list/listing.php
<div class=”table-list-row table-border-left”>里面的div增加样式:
1 |
style="border-left: 30px solid <?= $this->modal->colorModel->getBackgroundColor($project['color_id']) ?>" |
kanboard/app/Template/dashboard/overview.php
<div class=”table-list-row table-border-left”>增加样式:
1 |
style="border-left: 30px solid <?= $this->modal->colorModel->getBackgroundColor($project['color_id']) ?>" |
kanboard/app/Template/dashboard/projects.php
<div class=”table-list-row table-border-left”>加入样式:
1 |
style="border-left: 30px solid <?= $this->modal->colorModel->getBackgroundColor($project['color_id']) ?>"> |
项目修改的时候的颜色:
kanboard/app/Helper/FormHelper.php 增加方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public function selectColor($name, array $options, array $values = array(), array $errors = array(), array $attributes = array(), $class = '') { $html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '.implode(' ', $attributes).'>'; foreach ($options as $id => $value) { $html .= '<option value="'.$this->helper->text->e($id).'"'; if (isset($values->$name) && $id == $values->$name) { $html .= ' selected="selected"'; } if (isset($values[$name]) && $id == $values[$name]) { $html .= ' selected="selected"'; } $html .= ' style="background-color:'.$this->colorModel->getBackgroundColor($id).'"'; $html .= '>'.$this->helper->text->e($value).'</option>'; } $html .= '</select>'; $html .= $this->errorList($errors, $name); return $html; } |
kanboard/app/Helper/TaskHelper.php 增加方法:
1 2 3 4 5 6 7 |
public function renderColorField2(array $values) { $colors = $this->colorModel->getList(); $html = $this->helper->form->label(t('Color'), 'color_id'); $html .= $this->helper->form->selectColor('color_id', $colors, $values, array(), array(), 'color-picker'); return $html; } |
kanboard/app/Template/project_edit/show.php 增加或者修改:
1 |
<?= $this->task->renderColorField($values) ?> |
修改成:
1 |
<?= $this->task->renderColorField2($values) ?> |
修改:D:\xampp\htdocs\kanboard\app\Pagination\DashboardPagination.php
1 2 3 4 5 6 7 8 |
if ($paginator->getTotal() > 0) { $paginators[] = array( 'project_id' => $projectId, 'project_name' => $projectName, 'project' => $this->projectModel->getById($projectId), 'paginator' => $paginator, ); } |
修改:D:\xampp\htdocs\kanboard\app\Template\dashboard\overview.php
1 2 3 4 5 |
<div class="page-header"> <h2 style="background: <?= $this->modal->colorModel->getBackgroundColor($result['project']['color_id']) ?>;"> <?= $this->url->link($this->text->e($result['project_name']), 'BoardViewController', 'show', array('project_id' => $result['project_id'])) ?> </h2> </div> |
修改源码9:修改日历今天的单元格的样式:
找到:.fc-unthemed td.fc-today #fcf8e3 修改 #FCF8B1
修改源码10:一个TaskBoardDate插件引起的问题:抛弃这个插件
当录入bord_date的时候,如果大于当日,那么在看板就看不到这个任务,这个任务在仪表盘上(TaskBoardDate过滤得到的信息位置,下方)才能看到:
kanboard/plugins/TaskBoardDate/Plugin.php
注释掉:applyDateFilter(Table $query)方法里面的过来操作,这样两个地方都能看到。
但是,容易引起误会,比如一些在预备,但是又不能在仪表盘上(TaskBoardDate过滤得到的信息位置,下方)看到。
所以,干脆抛弃这个插件!!!!
修改源码11: 甘特图功能修改:
1.甘特图跳转功能:
kanboard/plugins/Gantt/Plugin.php 加入这个js
1 |
$this->hook->on('template:layout:js', array('template' => 'plugins/Gantt/Assets/gantt-ext.js')); |
chart.js修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
//适当延长两边的宽度,使得更好看 Gantt.prototype.getDateRange = function(minDays) { var minStart = new Date(); var maxEnd = new Date(); for (var i = 0; i < this.data.length; i++) { var start = new Date(); start.setTime(Date.parse(this.data[i].start)); var end = new Date(); end.setTime(Date.parse(this.data[i].end)); if (i == 0) { minStart = start; maxEnd = end; } if (this.compareDate(minStart, start) == 1) { minStart = start; } if (this.compareDate(maxEnd, end) == -1) { maxEnd = end; } } // Insure that the width of the chart is at least the slide width to avoid empty // whitespace to the right of the grid if (this.daysBetween(minStart, maxEnd) < minDays) { maxEnd = this.addDays(this.cloneDate(minStart), minDays); }else{ maxEnd.setDate(maxEnd.getDate() + 50); } // Always start one day before the minStart minStart.setDate(minStart.getDate() - 10); return [minStart, maxEnd]; }; Gantt.prototype.renderVerticalHeader = function() { var headerDiv = jQuery("<div>", { "class": "ganttview-vtheader" }); var itemDiv = jQuery("<div>", { "class": "ganttview-vtheader-item" }); var seriesDiv = jQuery("<div>", { "class": "ganttview-vtheader-series" }); for (var i = 0; i < this.data.length; i++) { var content = jQuery("<span>") .append(jQuery("<i>", {"class": "fa fa-info-circle tooltip", "title": this.getVerticalHeaderTooltip(this.data[i])})) .append(" "); if (this.data[i].type == "task") { content.append(jQuery("<a>", {"href": this.data[i].link, "title": this.data[i].title}).text($.datepicker.formatDate('yy-mm-dd', this.data[i].start)+" "+this.data[i].title)); } else { content .append(jQuery("<a>", {"href": this.data[i].board_link, "title": $(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')) .append(" ") .append(jQuery("<a>", {"href": this.data[i].gantt_link, "title": $(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')) .append(" ") .append(jQuery("<a>", {"href": this.data[i].link}).text($.datepicker.formatDate('yy-mm-dd', this.data[i].start)+" "+this.data[i].title)); } seriesDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-name", "data-task-id":this.data[i].id, "data-task-start":this.data[i].start.getTime(), "data-task-end":this.data[i].end.getTime() }).append(content)); if (this.data[i].not_defined) { //修改没有定义启动日期的任务颜色 content.find("i").css({"color":"#A4A4A4"}); content.find("a").css({"color":"#A4A4A4"}); }else{ //自定义颜色方便标记 if(parseFloat(this.data[i].progress,10)>=30){ content.find("i").css({"color":"#FF5722"}); content.find("a").css({"color":"#FF5722"}); } if(parseFloat(this.data[i].progress,10)>=50){ content.find("i").css({"color":"#FFB800"}); content.find("a").css({"color":"#FFB800"}); } if(parseFloat(this.data[i].progress,10)>=80){ content.find("i").css({"color":"#5FB878"}); content.find("a").css({"color":"#5FB878"}); } if(parseFloat(this.data[i].progress,10)>=100){ content.find("i").css({"color":"#1E9FFF"}); content.find("a").css({"color":"#1E9FFF"}); } } } itemDiv.append(seriesDiv); headerDiv.append(itemDiv); return headerDiv; }; Gantt.prototype.addBlocks = function(slider, start) { var rows = jQuery("div.ganttview-blocks div.ganttview-block-container", slider); var rowIdx = 0; for (var i = 0; i < this.data.length; i++) { var series = this.data[i]; var size = this.daysBetween(series.start, series.end) + 1; var offset = this.daysBetween(start, series.start); var text = jQuery("<div>", {"class": "ganttview-block-text"}); var block = jQuery("<div>", { "class": "ganttview-block tooltip" + (this.options.allowMoves ? " ganttview-block-movable" : ""), "title": this.getBarTooltip(series), "css": { "width": ((size * this.options.cellWidth) - 9) + "px", "margin-left": (offset * this.options.cellWidth) + "px" }, "data-task-id":this.data[i].id, "data-task-start":this.data[i].start.getTime(), "data-task-end":this.data[i].end.getTime(), "data-margin-left": (offset * this.options.cellWidth), "data-not-defined":(this.data[i].not_defined?"Y":"N") }).append(text); if (size >= 2) { text.append(series.progress); } block.data("record", series); this.setBarColor(block, series); jQuery(rows[rowIdx]).append(block); rowIdx = rowIdx + 1; } }; // Get tooltip for vertical header Gantt.prototype.getVerticalHeaderTooltip = function(record) { var tooltip = ""; if (record.type == "task") { if (record.not_defined) { tooltip = jQuery("<span>") .append(jQuery("<strong>").text(record.column_title)) .append(document.createTextNode(' (' + record.progress + ')')) .append(jQuery("<br>")) .append(document.createTextNode(record.title)) .append(jQuery("<br>")) .append(document.createTextNode($(this.options.container).data("label-not-defined"))) .prop('outerHTML'); }else{ tooltip = jQuery("<span>") .append(jQuery("<strong>").text(record.column_title)) .append(document.createTextNode(' (' + record.progress + ')')) .append(jQuery("<br>")) .append(document.createTextNode(record.title)) .append(jQuery("<br>")) .append(document.createTextNode($(this.options.container).data("label-start-date") + " " + $.datepicker.formatDate('yy-mm-dd', record.start))) .append(jQuery("<br>")) .append(document.createTextNode($(this.options.container).data("label-end-date") + " " + $.datepicker.formatDate('yy-mm-dd', record.end))).prop('outerHTML'); } } else { var types = ["project-manager", "project-member"]; for (var index in types) { var type = types[index]; if (! jQuery.isEmptyObject(record.users[type])) { var list = jQuery("<ul>"); for (var user_id in record.users[type]) { if (user_id) { list.append(jQuery("<li>").text(record.users[type][user_id])); } } tooltip += "<p><strong>" + $(this.options.container).data("label-" + type) + "</strong></p>" + list.prop('outerHTML'); } } } return tooltip; }; // Get tooltip for bars Gantt.prototype.getBarTooltip = function(record) { var tooltip = ""; if (record.not_defined) { tooltip = $(this.options.container).data("label-not-defined"); } else { if (record.type == "task") { var assigneeLabel = $(this.options.container).data("label-assignee"); tooltip += jQuery("<strong>").text(record.title+"("+record.progress+")").prop('outerHTML'); tooltip += "<br>"; tooltip += jQuery('<span>').append(document.createTextNode(assigneeLabel + " " + (record.assignee ? record.assignee : ''))).prop('outerHTML'); tooltip += "<br>"; } tooltip += $(this.options.container).data("label-start-date") + " " + $.datepicker.formatDate('yy-mm-dd', record.start) + "<br/>"; tooltip += $(this.options.container).data("label-end-date") + " " + $.datepicker.formatDate('yy-mm-dd', record.end); } return tooltip; }; |
gantt.js
1 2 3 4 5 6 7 8 9 10 |
if (KB.exists('#gantt-chart')) { var chart = new Gantt(); chart.show(); var tooltip = new Kanboard.Tooltip(); tooltip.execute(); chart.prepareVerticalHeaderClick(); chart.doTwinkle(); } |
gantt-ext.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
var GanttExtInterval = null; var GanttExtCurrMarginLeft = 0; Gantt.prototype.prepareVerticalHeaderClick = function (startDate) { var self = this; var cellWidth = this.options.cellWidth; jQuery("div.ganttview-vtheader-series-name").css({"cursor": "pointer"}).click(function () { var taskId = jQuery(this).data("task-id"); var block = jQuery("div.ganttview-block[data-task-id=" + taskId + "]"); var marginLeft = jQuery(block).data("margin-left"); var maxMarginLeft = parseInt(marginLeft, 10) - cellWidth; if (GanttExtInterval != null) { clearInterval(GanttExtInterval); } var currMarginLeft = GanttExtCurrMarginLeft; var padCellWidth = cellWidth * 2; if (maxMarginLeft >= GanttExtCurrMarginLeft) { GanttExtInterval = setInterval(function () { currMarginLeft = currMarginLeft + padCellWidth; if (currMarginLeft >= maxMarginLeft) { currMarginLeft = maxMarginLeft; GanttExtCurrMarginLeft = currMarginLeft; clearInterval(GanttExtInterval); jQuery(".ganttview-slide-container").scrollLeft(currMarginLeft); } jQuery(".ganttview-slide-container").scrollLeft(currMarginLeft); }, 25) } else { GanttExtInterval = setInterval(function () { currMarginLeft = currMarginLeft - padCellWidth; if (currMarginLeft <= maxMarginLeft) { currMarginLeft = maxMarginLeft; GanttExtCurrMarginLeft = currMarginLeft; clearInterval(GanttExtInterval); jQuery(".ganttview-slide-container").scrollLeft(currMarginLeft); } jQuery(".ganttview-slide-container").scrollLeft(currMarginLeft); }, 10) } }); }; Gantt.prototype.doTwinkle = function () { setInterval(function () { //没有开始日期的任务,闪烁显示 var opacity = jQuery("div.ganttview-block[data-not-defined=Y]").css("opacity"); if (typeof(opacity) == "undefined" || opacity == 1) { jQuery("div.ganttview-block[data-not-defined=Y]").css({"opacity": "0.3"}); } else { jQuery("div.ganttview-block[data-not-defined=Y]").css({"opacity": "1"}); } }, 1000); setInterval(function () { //今天要处理的任务闪烁 jQuery("div.ganttview-block").each(function () { var t=jQuery(this); var notDefined=t.data("not-defined"); if("Y"!=notDefined){ var taskStart = t.data("task-start"); var date = new Date(); date.setTime(taskStart); var startDateInt = parseInt($.datepicker.formatDate('yymmdd', date), 10); var taskEnd = t.data("task-end"); date = new Date(); date.setTime(taskEnd); var endDateInt = parseInt($.datepicker.formatDate('yymmdd', date), 10); date = new Date(); var todayDateInt = parseInt($.datepicker.formatDate('yymmdd', date), 10); if(startDateInt<=todayDateInt && endDateInt>=todayDateInt){ console.log("startDateInt=" + startDateInt+", endDateInt=" + endDateInt+",todayDateInt=" + todayDateInt); //没有开始日期的任务,闪烁显示 var opacity = t.css("opacity"); if (typeof(opacity) == "undefined" || opacity == 1) { t.css({"opacity": "0.9"}); } else { t.css({"opacity": "1"}); } } } }); }, 1150); }; |
2. 默认排序:
kanboard/plugins/Gantt/Controller/TaskGanttController.php 的show();
$sorting = $this->request->getStringParam(‘sorting’, ‘board’);
修改成:
1 |
$sorting = $this->request->getStringParam('sorting', 'date'); |
修改源码12: 修改甘特图到当前日期
gantt.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
div.ganttview-hzheader-month { width: 60px; height: 20px; border-right: 1px solid #d0d0d0; line-height: 20px; overflow: hidden; font-weight: bold; } div.ganttview-grid-row-cell.ganttview-weekend { /*background-color: #fafafa;*/ background-color: #EAEAEA; } div.ganttview-grid-row-cell.ganttview-today { /*background-color: #fcf8e3;*/ background-color: #F1B8B8; } |
gantt-ext.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Gantt.prototype.showTodayRang = function() { var minStart = new Date(); for (var i = 0; i < this.data.length; i++) { var start = new Date(); start.setTime(Date.parse(this.data[i].start)); var end = new Date(); end.setTime(Date.parse(this.data[i].end)); if (i == 0) { minStart = start; } if (this.compareDate(minStart, start) == 1) { minStart = start; } } //减去10,是因为修改源码变成多距左边10天 var offset= this.daysBetween(minStart, new Date())-10; var marginLeft=(offset * this.options.cellWidth); jQuery(".ganttview-slide-container").scrollLeft(marginLeft); }; |
在kanboard\plugins\Gantt\Assets\gantt.js创建甘特图后面调用:
1 |
chart.showTodayRang(); |
修改Gantt.prototype.renderHorizontalHeader()方法,顶部的显示格式:
1 2 |
//}).append($.datepicker.regional[$("body").data('js-lang')].monthNames[m] + " " + y)); }).append(y+ " " + $.datepicker.regional[$("body").data('js-lang')].monthNames[m])); |
修改源码13:管理员无法看到甘特图,权限怎么修改D:\xampp\htdocs\kanboard\plugins\Gantt\Plugin.php的initialize();
1 2 3 4 5 6 7 |
$this->projectAccessMap->add('TaskGanttController', '*', Role::PROJECT_MANAGER); $this->projectAccessMap->add('TaskGanttController', '*', Role::PROJECT_MEMBER); $this->projectAccessMap->add('TaskGanttController', '*', Role::PROJECT_VIEWER); $this->applicationAccessMap->add('ProjectGanttController', '*', Role::APP_ADMIN); $this->applicationAccessMap->add('ProjectGanttController', '*', Role::APP_MANAGER); $this->applicationAccessMap->add('ProjectGanttController', '*', Role::APP_USER); |
修改14: 甘特图显示的时候,如果没有开始日期,应该排到最后面去,修 kanboard\plugins\Gantt\Controller\TaskGanttController.php的show()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public function show() { $project = $this->getProject(); $search = $this->helper->projectHeader->getSearchQuery($project); $sorting = $this->request->getStringParam('sorting', 'date'); $filter = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project['id'])); if ($sorting === 'date') { //系统默认 $filter->getQuery()->asc(TaskModel::TABLE.'.date_started')->asc(TaskModel::TABLE.'.date_creation'); //空开始日期排到最后 //$filter->getQuery()->asc('(CASE '.TaskModel::TABLE.'.date_started WHEN 0 THEN 9999999999 ELSE '.TaskModel::TABLE.'.date_started END)')->asc(TaskModel::TABLE.'.date_creation'); } else { $filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position'); } $this->response->html($this->helper->layout->app('Gantt:task_gantt/show', array( 'project' => $project, 'title' => $project['name'], 'description' => $this->helper->projectHeader->getDescription($project), 'sorting' => $sorting, 'tasks' => $filter->format($this->taskGanttFormatter), ))); } |
修改15
1 |
ALTER TABLE `projects` ADD COLUMN `order_index` int(255) NULL AFTER `color_id`; |
kanboard/app/Template/project_creation/create.php
1 |
<?= $this->task->renderOrderIndexField($values) ?> |
kanboard/app/Template/project_edit/show.php加入:
1 |
<?= $this->task->renderOrderIndexField2($values) ?> |
kanboard/app/Model/ProjectModel.php的create()增加:
1 2 3 |
if (empty($values['order_index'])) { $values['order_index'] = 1000; } |
kanboard/app/Controller/ProjectCreationController.php 的 createNewProject(array $values) 增加:
1 2 3 4 5 6 |
$project = array( 'name' => $values['name'], 'color_id' => $values['color_id'], 'order_index' => $values['order_index'], 'is_private' => $values['is_private'], ); |
kanboard/app/Locale/zh_CN/translations.php 增加一个翻译: ‘OrderIndex’ => ‘序号’,
kanboard\app\Helper\TaskHelper.php
1 2 3 4 5 6 7 |
public function renderOrderIndexField(array $values, array $errors = array(), array $attributes = array()) { $attributes = array_merge(array('tabindex="16"'), $attributes); $html = $this->helper->form->label(t('OrderIndex'), 'order_index'); $html .= $this->helper->form->number('order_index', $values, $errors, $attributes); return $html; } |
1 2 3 4 5 6 7 |
public function renderOrderIndexField2(array $values, array $errors = array(), array $attributes = array()) { $attributes = array_merge(array('tabindex="16"'), $attributes); $html = $this->helper->form->label(t('OrderIndex'), 'order_index'); $html .= $this->helper->form->number('order_index', $values, $errors, $attributes); return $html; } |
D:\xampp\htdocs\kanboard\app\Template\dashboard\projects.php 我的项目
加入:
1 |
<?= t('OrderIndex') ?>[<?= $project['order_index'] ?>] |
上面这段代码也加入到: D:\xampp\htdocs\kanboard\app\Template\project_list\project_title.php
可以看到插叙方式:D:\xampp\htdocs\kanboard\app\Controller\DashboardController.php 的 projects() –> $this->projectPagination->getDashboardPaginator()
D:\xampp\htdocs\kanboard\app\Template\dashboard\overview.php 首页项目列表
可以看到插叙方式:D:\xampp\htdocs\kanboard\app\Controller\DashboardController.php 的 show() –> $this->projectPagination->getDashboardPaginator()
===> D:\xampp\htdocs\kanboard\app\Pagination\ProjectPagination.php
1 2 3 4 5 6 7 8 9 |
public function getDashboardPaginator($user_id, $method, $max) { return $this->paginator ->setUrl('DashboardController', $method, array('pagination' => 'projects', 'user_id' => $user_id)) ->setMax($max) ->setOrder(ProjectModel::TABLE.'.order_index') ->setQuery($this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id))) ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects'); } |
D:\xampp\htdocs\kanboard\app\Template\project_list\sort_menu.php
1 2 3 |
<li> <?= $paginator->order(t('OrderIndex'), \Kanboard\Model\ProjectModel::TABLE.'.order_index') ?> </li> |
D:\xampp\htdocs\kanboard\app\Controller\ProjectListController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public function show() { if ($this->userSession->isAdmin()) { $projectIds = $this->projectModel->getAllIds(); } else { $projectIds = $this->projectPermissionModel->getProjectIds($this->userSession->getId()); } $paginator = $this->paginator ->setUrl('ProjectListController', 'show') ->setMax(20) ->setOrder('order_index') ->setQuery($this->projectModel->getQueryByProjectIds($projectIds)) ->calculate(); $this->response->html($this->helper->layout->app('project_list/listing', array( 'paginator' => $paginator, 'title' => t('Projects') . ' (' . $paginator->getTotal() . ')', ))); } |
D:\xampp\htdocs\kanboard\app\Controller\ProjectUserOverviewController.php 前面加入
1 |
use Kanboard\Model\ProjectModel; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private function role($role, $action, $title, $title_user) { list($user_id, $project_ids, $users) = $this->common(); $query = $this->projectPermissionModel->getQueryByRole($project_ids, $role)->callback(array($this->projectModel, 'applyColumnStats')); if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) { $query->eq(UserModel::TABLE.'.id', $user_id); $title = t($title_user, $users[$user_id]); } $paginator = $this->paginator ->setUrl('ProjectUserOverviewController', $action, array('user_id' => $user_id)) ->setMax(30) ->setOrder(ProjectModel::TABLE.'.order_index') ->setQuery($query) ->calculate(); $this->response->html($this->helper->layout->projectUser('project_user_overview/roles', array( 'paginator' => $paginator, 'title' => $title, 'user_id' => $user_id, 'users' => $users, ))); } |
修改:D:\xampp\htdocs\kanboard\app\Model\ProjectUserRoleModel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public function getProjectsByUser($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) { $userProjects = $this->db ->hashtable(ProjectModel::TABLE) ->beginOr() ->eq(self::TABLE.'.user_id', $user_id) ->eq(ProjectModel::TABLE.'.is_everybody_allowed', 1) ->closeOr() ->in(ProjectModel::TABLE.'.is_active', $status) ->join(self::TABLE, 'project_id', 'id') ->orderBy(ProjectModel::TABLE.'.order_index') ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); $groupProjects = $this->projectGroupRoleModel->getProjectsByUser($user_id, $status); $projects = $userProjects + $groupProjects; //asort($projects); return $projects; } |
修改:D:\xampp\htdocs\kanboard\app\Model\ProjectGroupRoleModel.php
1 2 3 4 5 6 7 8 9 10 11 |
public function getProjectsByUser($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) { return $this->db ->hashtable(ProjectModel::TABLE) ->join(self::TABLE, 'project_id', 'id') ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) ->orderBy(ProjectModel::TABLE.'.order_index') ->eq(GroupMemberModel::TABLE.'.user_id', $user_id) ->in(ProjectModel::TABLE.'.is_active', $status) ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); } |
修改17
tooltip工具在403错误下面无法正常提示错误信息
修改:D:\xampp\htdocs\kanboard\assets\js\app.min.js修改$(“.tooltip”).tooltip的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
// 找到下面方法修改 KB.modal = { open: function (e, t, o) { /*KB.trigger("modal.open"), _KB.get("Dropdown").close(), s(), void 0 === o && (o = !0), KB.http.get(e).success(function (e) { c = !0, d(e, l(t), o) })*/ KB.trigger("modal.open"), _KB.get("Dropdown").close(), s(), void 0 === o && (o = !0); /*KB.http.get(e).success(function (e) { c = !0, d(e, l(t), o) });*/ $.ajax({ url: e, type: 'GET', success: function(e){ c = !0, d(e, l(t), o) }, error: function(data) { alert(data.responseJSON.message); } }) }, close: function () { s() }, isOpen: function () { return c }, replace: function (e) { KB.http.get(e).success(function (e) { r(e) }) }, getForm: n, submitForm: a } ..........// 然后找到下面方法修改 content: function () { var e = this, t = $(this).attr("data-href"); var s=null; /*s= t ? ($.get(t, function (t) { var o = $(".ui-tooltip:visible"); $(".ui-tooltip-content:visible").html(t), o.css({ top: "", left: "" }), o.children(".tooltip-arrow").remove(); var n = $(e).tooltip("option", "position"); n.of = $(e), o.position(n) }), '<i class="fa fa-spinner fa-spin"></i>') : '<div class="markdown">' + $(this).attr("title") + "</div>";*/ s= t ? ( $.ajax({ url: t, type: 'GET', success: function(t){ var o = $(".ui-tooltip:visible"); $(".ui-tooltip-content:visible").html(t), o.css({ top: "", left: "" }), o.children(".tooltip-arrow").remove(); var n = $(e).tooltip("option", "position"); n.of = $(e), o.position(n) }, error: function(data) { var t='<span style="color: red; font-weight: bold;">'+data.responseJSON.message+'</span>'; var o = $(".ui-tooltip:visible"); $(".ui-tooltip-content:visible").html(t), o.css({ top: "", left: "" }), o.children(".tooltip-arrow").remove(); var n = $(e).tooltip("option", "position"); n.of = $(e), o.position(n) } }), '<i class="fa fa-spinner fa-spin"></i>') : '<div class="markdown">' + $(this).attr("title") + "</div>"; return s; } |
修改18
实现项目和用户的多对多功能(其实在项目权限那里已经可以配置):
系统设置追加样式:
1 2 3 4 |
/*实现项目和用户的多对多功能*/ .show-user-list a .fa{ color: #888888; } |
复制: D:\xampp\htdocs\kanboard\app\Template\project_list\project_details.php 成 D:\xampp\htdocs\kanboard\app\Template\project_list\project_user.php
1 2 3 4 5 |
<div class="table-list-details"> <?php if ($this->user->hasAccess('ProjectUserOverviewController', 'managers')): ?> <?= $this->modal->projectModel->getUserInfo($project['id']) ?> <?php endif ?> </div> |
修改: D:\xampp\htdocs\kanboard\app\Template\project_list\listing.php
显示项目列表的div增加样式:
1 |
<div class="table-list project-list"> |
然后下面显示详细信息代码修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div class="table-list-row table-border-left" style="border-left: 30px solid <?= $this->modal->colorModel->getBackgroundColor($project['color_id']) ?>" > <?= $this->render('project_list/project_title', array( 'project' => $project, )) ?> <?= $this->render('project_list/project_details', array( 'project' => $project, )) ?> <?= $this->render('project_list/project_icons', array( 'project' => $project, )) ?> <?= $this->render('project_list/project_user', array( 'project' => $project, )) ?> </div> |
修改(这个应该放到controller,但现在还不知道怎么处理):D:\xampp\htdocs\kanboard\app\Model\ProjectModel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public function getUserInfo($project_id) { //return "project_id=".$project_id; $this->response->html($this->template->render('project_list/show_users', array( 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project_id), 'roles' => $this->projectRoleModel->getList($project_id), 'project_id' => $project_id, ))); } public function getUserInfo2($project_id) { //return "project_id=".$project_id; $this->response->html($this->template->render('dashboard/show_users', array( 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project_id), 'roles' => $this->projectRoleModel->getList($project_id), 'project_id' => $project_id, ))); } |
增加: D:\xampp\htdocs\kanboard\app\Template\project_list\show_users.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php if (empty($users)): ?> <?= t('There is no project member.') ?> <?php foreach ($roles as $role => $role_name): ?> <span class="show-user-list" style="color:#999; font-weight: bold;"> <?= $this->text->e($role_name) ?> <?= $this->modal->medium('plus', '', 'UserStatusController', 'confirmAddProjectUser', array('project_id' => $project_id, 'role' => $role)) ?> :</span> <?php endforeach ?> <?php else: ?> <?php foreach ($roles as $role => $role_name): ?> <span class="show-user-list" style="color:#999; font-weight: bold;"> <?= $this->text->e($role_name) ?> <?= $this->modal->medium('plus', '', 'UserStatusController', 'confirmAddProjectUser', array('project_id' => $project_id, 'role' => $role)) ?> :</span> <?php if (isset($users[$role])): ?> <?php foreach ($users[$role] as $user_id => $user): ?> <span class="show-user-list"> <?= $this->text->e($user) ?> <?= $this->modal->confirm('times', '', 'UserStatusController', 'confirmRemoveProjectUser', array('project_id' => $project_id, 'user_id' => $user_id)) ?>; </span> <?php endforeach ?> <?php else: ?> <span class="show-user-list"><?= t('None')?></span> <?php endif ?> <?php endforeach ?> <?php endif ?> |
增加:D:\xampp\htdocs\kanboard\app\Template\project_list\remove_user.php
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div class="page-header"> <h2><?= t('Remove user') ?></h2> </div> <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to remove this user: "%s"?', $user['name'] ?: $user['username']) ?></p> <?= $this->modal->confirmButtons( 'UserStatusController', 'removeProjectUser', array('user_id' => $user['id'],'project_id' => $project_id) ) ?> </div> |
修改:D:\xampp\htdocs\kanboard\app\Controller\UserStatusController.php 增加方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
public function confirmAddProjectUser() { $user = $this->getUser(); $project_id = $this->request->getIntegerParam("project_id",-1); $roles = $this->projectRoleModel->getList($project_id); $role = $this->request->getStringParam("role",''); $users = $this->userModel->getAll(); $existUser=$this->projectUserRoleModel->getAllUsers($project_id); $showUsers=array(); foreach ($users as $idx => $values){ $isFound=false; foreach ($existUser as $userId=>$name){ if($values["id"]==$userId){ $isFound=true; break; } } if(!$isFound) $showUsers[$values["id"]]=$values["name"]; } $this->response->html($this->helper->layout->user('project_list/add_user', array( 'users' => $showUsers, 'roles' => $this->projectRoleModel->getList($project_id), 'role' => $role, 'project_id' => $project_id, ))); } public function addProjectUser() { $user = $this->getUser(); //$this->checkCSRFParam(); $values = $this->request->getValues(); $project_id = $values["project_id"]; $role = $values["role"]; $user_id = $values["user_id"]; if ($this->projectUserRoleModel->addUser($project_id, $user_id,$role)) { $this->flash->success(t('User updated successfully.')); } else { $this->flash->failure(t('Unable to updated successfully.')); } $this->response->redirect($this->helper->url->to('ProjectListController', 'show')); } public function confirmRemoveProjectUser() { $user = $this->getUser(); $project_id = $this->request->getIntegerParam("project_id",-1); $this->response->html($this->helper->layout->user('project_list/remove_user', array( 'user' => $user, 'project_id' => $project_id, ))); } public function removeProjectUser() { $user = $this->getUser(); $this->checkCSRFParam(); $project_id = $this->request->getIntegerParam("project_id",-1); $user_id = $this->request->getIntegerParam("user_id",-1); if ($this->projectUserRoleModel->removeUser($project_id, $user_id)) { $this->flash->success(t('User removed successfully.')); } else { $this->flash->failure(t('Unable to remove this user.')); } $this->response->redirect($this->helper->url->to('ProjectListController', 'show')); } |
增加:D:\xampp\htdocs\kanboard\app\Template\project_list\add_user.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<div class="page-header"> <h2><?= t('Users overview') ?></h2> </div> <form method="post" action="<?= $this->url->href('UserStatusController', 'addProjectUser') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <div class="form-columns"> <div class="form-column"> <input name="project_id" type="hidden" value="<?= $project_id ?>"> <fieldset> <legend><?= t('Role') ?></legend> <?= $this->form->select('role', $roles, array("role"=>$role)) ?> </fieldset> <fieldset> <legend><?= t('All users') ?></legend> <?= $this->form->select('user_id', $users, array("name"=>'')) ?> </fieldset> </div> </div> <?= $this->modal->submitButtons() ?> </form> |
修改:D:\xampp\htdocs\kanboard\app\Template\dashboard\overview.php 和 D:\xampp\htdocs\kanboard\app\Template\dashboard\projects.php
项目列表div增加class:
1 |
<div class="table-list project-list"> |
然后制定样式追加:
1 |
.project-list .table-list-title a{ color : #d40000 ; font-weight: bold; } |
修改显示任务代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<div class="table-list-row table-border-left" style="border-left: 30px solid <?= $this->modal->colorModel->getBackgroundColor($project['color_id']) ?>" > <div> <?php if ($this->user->hasProjectAccess('ProjectViewController', 'show', $project['id'])): ?> <?= $this->render('project/dropdown', array('project' => $project)) ?> <?php else: ?> <strong><?= '#'.$project['id'] ?></strong> <?php endif ?> <span class="table-list-title <?= $project['is_active'] == 0 ? 'status-closed' : '' ?>"> <?= t('OrderIndex') ?>[<?= $project['order_index'] ?>] <?= $this->url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> </span> <?php if ($project['is_private']): ?> <i class="fa fa-lock fa-fw" title="<?= t('Private project') ?>"></i> <?php endif ?> </div> <div class="table-list-details"> <?php foreach ($project['columns'] as $column): ?> <strong title="<?= t('Task count') ?>"><?= $column['nb_open_tasks'] ?></strong> <small><?= $this->text->e($column['title']) ?></small> <?php endforeach ?> </div> <?= $this->render('dashboard/project_user', array( 'project' => $project, )) ?> </div> |
增加:D:\xampp\htdocs\kanboard\app\Template\dashboard\project_user.php
1 2 3 4 5 |
<div class="table-list-details"> <?php if ($this->user->hasAccess('ProjectUserOverviewController', 'managers')): ?> <?= $this->modal->projectModel->getUserInfo2($project['id']) ?> <?php endif ?> </div> |
增加:D:\xampp\htdocs\kanboard\app\Template\dashboard\show_users.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php if (empty($users)): ?> <?= t('There is no project member.') ?> <?php foreach ($roles as $role => $role_name): ?> <span class="show-user-list" style="color:#999; font-weight: bold;"> <?= $this->text->e($role_name) ?> <?= $this->modal->medium('plus', '', 'UserStatusController', 'confirmAddProjectUser', array('project_id' => $project_id, 'role' => $role)) ?> :</span> <?php endforeach ?> <?php else: ?> <?php foreach ($roles as $role => $role_name): ?> <span class="show-user-list" style="color:#999; font-weight: bold;"> <?= $this->text->e($role_name) ?> :</span> <?php if (isset($users[$role])): ?> <?php foreach ($users[$role] as $user_id => $user): ?> <span class="show-user-list"> <?= $this->text->e($user) ?>; </span> <?php endforeach ?> <?php else: ?> <span class="show-user-list"><?= t('None')?></span> <?php endif ?> <?php endforeach ?> <?php endif ?> |
修改:D:\xampp\htdocs\kanboard\app\Locale\zh_CN\translations.php
1 |
'Unable to updated successfully.' => '无法更新用户。', |
修改: D:\xampp\htdocs\kanboard\app\ServiceProvider\AuthenticationProvider.php
getProjectAccessMap()增加:
1 2 3 4 |
$acl->add('UserStatusController', 'confirmAddProjectUser', Role::PROJECT_MANAGER); $acl->add('UserStatusController', 'addProjectUser', Role::PROJECT_MANAGER); $acl->add('UserStatusController', 'confirmRemoveProjectUser', Role::PROJECT_MANAGER); $acl->add('UserStatusController', 'removeProjectUser', Role::PROJECT_MANAGER); |
getApplicationAccessMap()增加:
1 2 3 4 |
$acl->add('UserStatusController', 'confirmAddProjectUser', Role::APP_MANAGER); $acl->add('UserStatusController', 'addProjectUser', Role::APP_MANAGER); $acl->add('UserStatusController', 'confirmRemoveProjectUser', Role::APP_MANAGER); $acl->add('UserStatusController', 'removeProjectUser', Role::APP_MANAGER); |
修改19
任务默认排序(看板不处理)
D:\xampp\htdocs\kanboard\app\Controller\ProjectUserOverviewController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private function tasks($is_active, $action, $title, $title_user) { list($user_id, $project_ids, $users) = $this->common(); $query = $this->taskFinderModel->getProjectUserOverviewQuery($project_ids, $is_active); if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) { $query->eq(TaskModel::TABLE.'.owner_id', $user_id); $title = t($title_user, $users[$user_id]); } $paginator = $this->paginator ->setUrl('ProjectUserOverviewController', $action, array('user_id' => $user_id)) ->setMax(50) ->setOrder(TaskModel::TABLE.'.date_due') ->setDirection('desc') ->setQuery($query) ->calculate(); $this->response->html($this->helper->layout->projectUser('project_user_overview/tasks', array( 'paginator' => $paginator, 'title' => $title, 'user_id' => $user_id, 'users' => $users, ))); } |
D:\xampp\htdocs\kanboard\app\Pagination\DashboardPagination.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public function getOverview($userId) { $paginators = array(); $projects = $this->projectUserRoleModel->getActiveProjectsByUser($userId); foreach ($projects as $projectId => $projectName) { $paginator = $this->paginator ->setUrl('DashboardController', 'show', array('user_id' => $userId)) ->setMax(50) //->setOrder(TaskModel::TABLE.'.priority') ->setOrder(TaskModel::TABLE.'.date_due') ->setDirection('DESC') ->setFormatter($this->taskListSubtaskAssigneeFormatter->withUserId($userId)) ->setQuery($this->taskFinderModel->getUserQuery($userId)->eq(ProjectModel::TABLE.'.id', $projectId)) ->calculate(); if ($paginator->getTotal() > 0) { $paginators[] = array( 'project_id' => $projectId, 'project_name' => $projectName, 'project' => $this->projectModel->getById($projectId), 'paginator' => $paginator, ); } } return $paginators; } |
D:\xampp\htdocs\kanboard\app\Controller\TaskListController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$paginator = $this->paginator ->setUrl('TaskListController', 'show', array('project_id' => $project['id'])) ->setMax(30) //->setOrder(TaskModel::TABLE.'.id') ->setOrder(TaskModel::TABLE.'.date_due') ->setDirection('DESC') ->setFormatter($formatter) ->setQuery($this->taskLexer ->build($search) ->withFilter(new TaskProjectFilter($project['id'])) ->getQuery() ) ->calculate(); |
D:\xampp\htdocs\kanboard\app\Pagination\TaskPagination.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public function getDashboardPaginator($userId, $method, $max) { $query = $this->taskFinderModel->getUserQuery($userId); $this->hook->reference('pagination:dashboard:task:query', $query); return $this->paginator ->setUrl('DashboardController', $method, array('pagination' => 'tasks', 'user_id' => $userId)) ->setMax($max) //->setOrder(TaskModel::TABLE.'.id') ->setOrder(TaskModel::TABLE.'.date_due') ->setQuery($query) ->setFormatter($this->taskListFormatter) ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks'); } |
修改20
取消顶部项目下拉框的自动排序功能,
D:\xampp\htdocs\kanboard\app\Helper\LayoutHelper.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public function app($template, array $params = array()) { $isAjax = $this->request->isAjax(); $params['is_ajax'] = $isAjax; if ($isAjax) { return $this->template->render($template, $params); } if (! isset($params['no_layout']) && ! isset($params['board_selector'])) { //$params['board_selector'] = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); $params['board_selector'] = $this->projectUserRoleModel->getActiveProjectsByUser2($this->userSession->getId()); if (isset($params['project']['id'])) { //unset($params['board_selector'][$params['project']['id']]); foreach ($params['board_selector'] as $idx=>$val){ if($params['project']['id']==$val['id']){ array_splice($params['board_selector'],$idx,1); } } } } return $this->pageLayout($template, $params); } |
D:\xampp\htdocs\kanboard\app\Model\ProjectUserRoleModel.php 增加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public function getActiveProjectsByUser2($user_id) { return $this->getProjectsByUser2($user_id, array(ProjectModel::ACTIVE)); } public function getProjectsByUser2($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) { $userProjects = $this->db ->hashtable(ProjectModel::TABLE) ->beginOr() ->eq(self::TABLE.'.user_id', $user_id) ->eq(ProjectModel::TABLE.'.is_everybody_allowed', 1) ->closeOr() ->in(ProjectModel::TABLE.'.is_active', $status) ->join(self::TABLE, 'project_id', 'id') ->orderBy(ProjectModel::TABLE.'.order_index') ->findAll(); //->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); $groupProjects = $this->projectGroupRoleModel->getProjectsByUser2($user_id, $status); $projects = $userProjects + $groupProjects; //asort($projects); return $projects; } |
D:\xampp\htdocs\kanboard\app\Template\header\board_selector.php 修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?= $this->app->component('select-dropdown-autocomplete', array( 'name' => 'boardId', 'placeholder' => t('Display another project'), 'items' => $board_selector, 'value'=>'id', 'text'=>'name', 'redirect' => array( 'regex' => 'PROJECT_ID', 'url' => $this->url->to('BoardViewController', 'show', array('project_id' => 'PROJECT_ID')), ), 'onFocus' => array( 'board.selector.open', ) )) ?> |
D:\xampp\htdocs\kanboard\app\Model\ProjectGroupRoleModel.php
1 2 3 4 5 6 7 8 9 10 11 12 |
public function getProjectsByUser2($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) { return $this->db ->hashtable(ProjectModel::TABLE) ->join(self::TABLE, 'project_id', 'id') ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) ->orderBy(ProjectModel::TABLE.'.order_index') ->eq(GroupMemberModel::TABLE.'.user_id', $user_id) ->in(ProjectModel::TABLE.'.is_active', $status) ->findAll(); //->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); } |
找到app.min.js,找到select-dropdown-menu-item字符串,并且前面是class:”select-dropdown-menu-item”,然后替换掉这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function h(e) { var o = []; for (var n in e) e.hasOwnProperty(n) && o.push({ class:"select-dropdown-menu-item", /*text: e[n], "data-label": e[n], "data-value": n*/ text: (t.text?e[n][t.text]:e[n]), "data-label": (t.text?e[n][t.text]:e[n]), "data-value": (t.value?e[n][t.value]:n) }); /*如果不设定,这不需要排序*/ return t.sortByKeys ? o.sort(function (e, t) { var o = e["data-value"].toLowerCase(), n = t["data-value"].toLowerCase(); return o < n ? -1 : o > n ? 1 : 0 }) : (t.sortByLabels ? o.sort(function (e, t) { var o = e["data-label"].toLowerCase(), n = t["data-label"].toLowerCase(); return o < n ? -1 : o > n ? 1 : 0 }):null), o } |
找到”.select-dropdown-menu-item.active”字符串,替换相应的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function u() { var e = KB.find(".select-dropdown-menu-item.active"), n = e.data("value"); if(t.text && t.items && t.items.length>0){ for (var i=0; i<t.items.length; i++){ var it=t.items[i]; if(n==it[t.value]){ y.value = it[t.text]; break; } } C.value = n, /*y.value = t.items[n],*/ b(), t.redirect ? window.location = t.redirect.url.replace(new RegExp(t.redirect.regex, "g"), n) : t.replace && (o(), KB.modal.replace(t.replace.url.replace(new RegExp(t.replace.regex, "g"), n))); }else{ C.value = n, y.value = t.items[n], b(), t.redirect ? window.location = t.redirect.url.replace(new RegExp(t.redirect.regex, "g"), n) : t.replace && (o(), KB.modal.replace(t.replace.url.replace(new RegExp(t.replace.regex, "g"), n))) } } |
修改21
D:\xampp\htdocs\kanboard\app\Core\DateParser.php
1 2 3 4 5 6 7 8 9 |
$userFormats = array( 'm/d/Y', 'd/m/Y', 'Y/m/d', 'd.m.Y', 'm-d-Y', 'd-m-Y', 'Y-m-d', ); |
自定义样式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/*设定样式输入框*/ #form-application_stylesheet{ width: 1000px;height: 400px; } /*表格里面的标题*/ .project-list .table-list-title a{ color : #d40000 ; font-weight: bold; } /*移动任务到其他项目的时候,项目下拉框显示不正确*/ #select-dropdown-menu{ position: fixed; overflow-x: auto;} /*下拉框高度*/ select{ height: 30px; } /*实现项目和用户的多对多功能*/ .show-user-list a .fa{ color: #888888; } /*表格的图标*/ .table-list-row i.fa-fw{ color: #FFB200; } /*标题颜色*/ .title-container .title{color: #d40000; font-weight: bold;} /*显示对话框的关闭按钮*/ #modal-overlay #modal-box{height: calc(100% - 30px);overflow: hidden;} #modal-overlay #modal-box #modal-content{height: 90%; overflow: auto;} |