Skip to content

Commit

Permalink
MBS-8974: Add repeating of cards
Browse files Browse the repository at this point in the history
  • Loading branch information
sh-csg committed Mar 26, 2024
1 parent 3ea864d commit 81b9325
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 14 deletions.
15 changes: 14 additions & 1 deletion backup/moodle2/backup_kanban_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@ protected function define_structure(): backup_nested_element {
$kanban = new backup_nested_element(
'kanban',
['id'],
['course', 'name', 'intro', 'introformat', 'userboards', 'history', 'completioncreate', 'completioncomplete']
[
'course',
'name',
'intro',
'introformat',
'userboards',
'history',
'completioncreate',
'completioncomplete',
'repeat_enable',
'repeat_interval',
'repeat_interval_type',
'repeat_newduedate',
]
);
$kanban->set_source_table('kanban', ['id' => backup::VAR_ACTIVITYID]);
$kanban->annotate_files('mod_kanban', 'intro', null);
Expand Down
59 changes: 51 additions & 8 deletions classes/boardmanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* Class to handle updating the board
*
* @package mod_kanban
* @copyright 2023-2024 ISB Bayern
* @copyright 2023-2024 ISB Bayern
* @author Stefan Hanauska
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
Expand All @@ -34,7 +34,7 @@
* Class to handle updating the board. It also sends notifications, but does not check permissions.
*
* @package mod_kanban
* @copyright 2023-2024 ISB Bayern
* @copyright 2023-2024 ISB Bayern
* @author Stefan Hanauska
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
Expand Down Expand Up @@ -477,7 +477,6 @@ public function add_card(int $columnid, int $aftercard = 0, array $data = []): i

// Users can always edit cards they created.
$data['canedit'] = $this->can_user_manage_specific_card($data['id']);
;
$data['columnname'] = clean_param($column->title, PARAM_TEXT);

$this->formatter->put('cards', $data);
Expand Down Expand Up @@ -581,11 +580,7 @@ public function move_card(int $cardid, int $aftercard, int $columnid = 0): void
$assignees = $this->get_card_assignees($cardid);
helper::send_notification($this->cminfo, 'moved', $assignees, (object) $data);
if (!empty($options->autoclose) && $card->completed == 0) {
$data['title'] = clean_param($card->title, PARAM_TEXT);
helper::send_notification($this->cminfo, 'closed', $assignees, (object) $data);
helper::remove_calendar_event($this->kanban, $card);
$this->write_history('completed', constants::MOD_KANBAN_CARD, [], $columnid, $cardid);
$this->update_completion($assignees);
self::set_card_complete($cardid, 1);
}
$this->write_history(
'moved',
Expand Down Expand Up @@ -688,6 +683,30 @@ public function set_card_complete(int $cardid, int $state): void {
$assignees = $this->get_card_assignees($cardid);
if ($state) {
helper::remove_calendar_event($this->kanban, $card, $assignees);
if ($card->repeat_enable) {
$newcard = clone $card;
if ($card->repeat_newduedate == constants::MOD_KANBAN_REPEAT_NONEWDUEDATE) {
$newcard->duedate = 0;
$newcard->reminder = 0;
} else {
$timedifference = $newcard->duedate - $newcard->reminder;
$timebase = (
$card->repeat_newduedate == constants::MOD_KANBAN_REPEAT_NEWDUEDATE_AFTERDUE && !empty($newcard->duedate) ?
$newcard->duedate :
time()
);
$newcard->duedate = strtotime(
'+' .
$card->repeat_interval .
' ' .
constants::MOD_KANBAN_REPEAT_INTERVAL_TYPE[$card->repeat_interval_type],
$timebase
);
$newcard->reminder = $newcard->duedate - $timedifference;
}
$newcard->isrepeated = 1;
$this->add_card($this->get_leftmost_column($card->kanban_board), 0, (array)$newcard);
}
} else {
helper::add_or_update_calendar_event($this->kanban, $card, $assignees);
}
Expand Down Expand Up @@ -813,6 +832,10 @@ public function update_card(int $cardid, array $data): void {
'kanban_column',
'kanban_board',
'completed',
'repeat_enable',
'repeat_interval',
'repeat_interval_type',
'repeat_newduedate',
];
// Do some extra sanitizing.
if (isset($data['title'])) {
Expand Down Expand Up @@ -1230,4 +1253,24 @@ public function can_user_manage_specific_card(int $cardid, int $userid = 0): boo

return false;
}

/**
* Returns the leftmost column of a board, 0 if none is found.
*
* @param int $boardid Id of the board, defaults to 0 (current board)
* @return int
*/
public function get_leftmost_column(int $boardid = 0): int {
global $DB;
if (empty($boardid) || $this->board->id == $boardid) {
$sequence = $this->board->sequence;
} else {
$sequence = $DB->get_field('kanban_board', 'sequence', ['id' => $boardid]);
}
if (empty($sequence)) {
return 0;
}
$columnids = explode(',', $sequence, 2);
return $columnids[0];
}
}
42 changes: 42 additions & 0 deletions classes/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,46 @@ class constants {
self::MOD_KANBAN_DISCUSSION => 'discussion',
self::MOD_KANBAN_HISTORY => 'history',
];
/**
* Repeat interval type: hours
*/
public const MOD_KANBAN_REPEAT_HOURS = 2;
/**
* Repeat interval type: days
*/
public const MOD_KANBAN_REPEAT_DAYS = 3;
/**
* Repeat interval type: weeks
*/
public const MOD_KANBAN_REPEAT_WEEKS = 4;
/**
* Repeat interval type: months
*/
public const MOD_KANBAN_REPEAT_MONTHS = 5;
/**
* Repeat interval type: years
*/
public const MOD_KANBAN_REPEAT_YEARS = 6;
/**
* Mapping of repeat interval types to strings
*/
public const MOD_KANBAN_REPEAT_INTERVAL_TYPE = [
self::MOD_KANBAN_REPEAT_HOURS => 'hour',
self::MOD_KANBAN_REPEAT_DAYS => 'day',
self::MOD_KANBAN_REPEAT_WEEKS => 'week',
self::MOD_KANBAN_REPEAT_MONTHS => 'month',
self::MOD_KANBAN_REPEAT_YEARS => 'year',
];
/**
* Repeat new due date: no new due date
*/
public const MOD_KANBAN_REPEAT_NONEWDUEDATE = 0;
/**
* Repeat new due date: after due date
*/
public const MOD_KANBAN_REPEAT_NEWDUEDATE_AFTERDUE = 1;
/**
* Repeat new due date: after completion
*/
public const MOD_KANBAN_REPEAT_NEWDUEDATE_AFTERCOMPLETION = 2;
}
29 changes: 28 additions & 1 deletion classes/form/edit_card_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@
use core_form\dynamic_form;
use mod_kanban\boardmanager;
use mod_kanban\helper;
use mod_kanban\constants;
use moodle_url;

/**
* From for editing a card.
*
* @package mod_kanban
* @copyright 2023-2024 ISB Bayern
* @copyright 2023-2024 ISB Bayern
* @author Stefan Hanauska
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
Expand Down Expand Up @@ -80,6 +81,32 @@ public function definition() {

$mform->addElement('date_time_selector', 'reminderdate', get_string('reminderdate', 'kanban'), ['optional' => true]);

$repeatgroup = [];
$repeatgroup[] = $mform->createElement('advcheckbox', 'repeat_enable', get_string('enable'));
$repeatgroup[] = $mform->createElement('text', 'repeat_interval', get_string('repeat_interval', 'kanban'), ['size' => 3]);
$repeatgroup[] = $mform->createElement('select', 'repeat_interval_type', get_string('repeat_interval_type', 'kanban'), [
constants::MOD_KANBAN_REPEAT_HOURS => get_string('hours'),
constants::MOD_KANBAN_REPEAT_DAYS => get_string('days'),
constants::MOD_KANBAN_REPEAT_WEEKS => get_string('weeks'),
constants::MOD_KANBAN_REPEAT_MONTHS => get_string('months'),
constants::MOD_KANBAN_REPEAT_YEARS => get_string('years'),
]);
$repeatgroup[] = $mform->createElement('select', 'repeat_newduedate', get_string('repeat_newduedate', 'kanban'), [
constants::MOD_KANBAN_REPEAT_NONEWDUEDATE => get_string('nonewduedate', 'kanban'),
constants::MOD_KANBAN_REPEAT_NEWDUEDATE_AFTERDUE => get_string('afterdue', 'kanban'),
constants::MOD_KANBAN_REPEAT_NEWDUEDATE_AFTERCOMPLETION => get_string('aftercompletion', 'kanban'),
]);

$mform->addElement('group', 'repeatgroup', get_string('repeat', 'kanban'), $repeatgroup, ' ', false);

$mform->setType('repeat_interval', PARAM_INT);
$mform->setType('repeat_interval_type', PARAM_INT);
$mform->setDefault('repeat_interval', 1);
$mform->disabledIf('repeatgroup', 'repeat_enable', 'notchecked');
$mform->disabledIf('repeat_interval', 'repeat_newduedate', 'eq', constants::MOD_KANBAN_REPEAT_NONEWDUEDATE);
$mform->disabledIf('repeat_interval_type', 'repeat_newduedate', 'eq', constants::MOD_KANBAN_REPEAT_NONEWDUEDATE);
$mform->addHelpButton('repeatgroup', 'repeat', 'kanban');

$mform->addElement('filemanager', 'attachments', get_string('attachments', 'kanban'));

$mform->addElement('color', 'color', get_string('color', 'mod_kanban'), ['size' => 5]);
Expand Down
4 changes: 4 additions & 0 deletions db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
<FIELD NAME="createdby" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="repeat_enable" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="repeat_interval" TYPE="int" LENGTH="11" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="repeat_interval_type" TYPE="int" LENGTH="11" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="repeat_newduedate" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
Expand Down
58 changes: 56 additions & 2 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* mod_kanban db upgrades.
*
* @package mod_kanban
* @copyright 2023-2024 ISB Bayern
* @copyright 2023-2024 ISB Bayern
* @author Stefan Hanauska
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
Expand All @@ -29,6 +29,60 @@
* @param int $oldversion Version number the plugin is being upgraded from.
*/
function xmldb_kanban_upgrade($oldversion) {
// No upgrade steps until now.
global $DB;
$dbman = $DB->get_manager();

if ($oldversion < 2024032601) {
// Define field repeat_enable to be added to kanban_card.
$table = new xmldb_table('kanban_card');
$field = new xmldb_field('repeat_enable', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'timemodified');

// Conditionally launch add field repeat_enable.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

$field = new xmldb_field('repeat_interval', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, '0', 'repeat_enable');

// Conditionally launch add field repeat_interval.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

$field = new xmldb_field(
'repeat_interval_type',
XMLDB_TYPE_INTEGER,
'11',
null,
XMLDB_NOTNULL,
null,
'0',
'repeat_interval'
);

// Conditionally launch add field repeat_interval_type.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

$field = new xmldb_field(
'repeat_newduedate',
XMLDB_TYPE_INTEGER,
'5',
null,
XMLDB_NOTNULL,
null,
'0',
'repeat_interval_type'
);

// Conditionally launch add field repeat_newduedate.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Kanban savepoint reached.
upgrade_mod_savepoint(true, 2024032601, 'kanban');
}
return true;
}
9 changes: 9 additions & 0 deletions lang/en/kanban.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

$string['addcard'] = 'Add a card to this column';
$string['addcolumn'] = 'Add a column to this board';
$string['aftercompletion'] = 'after card is closed';
$string['afterdue'] = 'after card is due';
$string['assignee'] = 'Assignee';
$string['assignees'] = 'Assignees';
$string['assignme'] = 'Assign me';
Expand Down Expand Up @@ -70,6 +72,7 @@
$string['enablehistory'] = 'Enable history';
$string['enablehistory_help'] = 'Enable recording history of cards in this board (e.g. when card was moved / renamed / completed)';
$string['enablehistorydescription'] = 'Enabling this option will make history of changes available to the boards.';
$string['repeat_interval_type'] = 'Frequency';
$string['groupboard'] = 'Group board for group "{$a}"';
$string['hidehidden'] = 'Hide hidden cards';
$string['history'] = 'History';
Expand All @@ -83,6 +86,7 @@
$string['history_card_unassigned'] = '{$a->username} unassigned card from user {$a->affectedusername}';
$string['history_discussion_added'] = '{$a->username} added discussion message';
$string['history_discussion_deleted'] = '{$a->username} deleted discussion message';
$string['repeat_interval'] = 'Interval';
$string['kanban:addcard'] = 'Add a card to a Kanban board';
$string['kanban:addinstance'] = 'Add a Kanban board';
$string['kanban:assignothers'] = 'Assign others to a card';
Expand Down Expand Up @@ -132,7 +136,9 @@
$string['name_help'] = 'This name will be visible in course overview and as a title of the board';
$string['newcard'] = 'New card';
$string['newcolumn'] = 'New column';
$string['repeat_newduedate'] = 'New due date';
$string['nogroupavailable'] = 'No group available';
$string['nonewduedate'] = 'No new due date';
$string['nouser'] = 'No user';
$string['nouserboards'] = 'No personal boards';
$string['pluginadministration'] = 'Kanban administration';
Expand All @@ -157,6 +163,9 @@
$string['pushcardconfirm'] = 'This will send a copy of this card to all boards inside this kanban activity including templates. Existing copies will be replaced.';
$string['reminderdate'] = 'Reminder date';
$string['remindertask'] = 'Send reminder notifications';
$string['repeat'] = 'Repeat card';
$string['repeat_help'] = "If selected, a new copy of this card will be created in the leftmost column as soon as this instance is completed. Discussion, history and assignees are not copied.
You can choose how to calculate the new due date, if needed. This will also be applied to the new reminder date.";
$string['reset_kanban'] = 'Reset shared boards';
$string['reset_group'] = 'Reset group boards';
$string['reset_personal'] = 'Reset personal boards';
Expand Down
4 changes: 2 additions & 2 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
defined('MOODLE_INTERNAL') || die();

$plugin->component = 'mod_kanban';
$plugin->release = '0.2.5';
$plugin->version = 2024030701;
$plugin->release = '0.2.6';
$plugin->version = 2024032601;
$plugin->requires = 2022112800;
$plugin->supported = [401, 404];
$plugin->maturity = MATURITY_BETA;

0 comments on commit 81b9325

Please sign in to comment.