diff --git a/plugins/tasklist/drivers/database/SQL/mysql.sql b/plugins/tasklist/drivers/database/SQL/mysql.sql index eb9b4549..18f130da 100644 --- a/plugins/tasklist/drivers/database/SQL/mysql.sql +++ b/plugins/tasklist/drivers/database/SQL/mysql.sql @@ -29,6 +29,7 @@ CREATE TABLE `tasks` ( `del` tinyint(1) unsigned NOT NULL DEFAULT '0', `title` varchar(255) NOT NULL, `description` text, + `tags` text, `date` varchar(10) DEFAULT NULL, `time` varchar(5) DEFAULT NULL, `flagged` tinyint(4) NOT NULL DEFAULT '0', diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php index 5b1e99bb..b9aeba09 100644 --- a/plugins/tasklist/drivers/database/tasklist_database_driver.php +++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php @@ -344,6 +344,7 @@ class tasklist_database_driver extends tasklist_driver $rec['id'] = $rec['task_id']; $rec['list'] = $rec['tasklist_id']; $rec['changed'] = strtotime($rec['changed']); + $rec['tags'] = array_filter(explode(',', $rec['tags'])); if (!$rec['parent_id']) unset($rec['parent_id']); @@ -373,8 +374,8 @@ class tasklist_database_driver extends tasklist_driver $result = $this->rc->db->query(sprintf( "INSERT INTO " . $this->db_tasks . " - (tasklist_id, uid, parent_id, created, changed, title, date, time) - VALUES (?, ?, ?, %s, %s, ?, ?, ?)", + (tasklist_id, uid, parent_id, created, changed, title, date, time, description, tags) + VALUES (?, ?, ?, %s, %s, ?, ?, ?, ?, ?)", $this->rc->db->now(), $this->rc->db->now() ), @@ -383,7 +384,9 @@ class tasklist_database_driver extends tasklist_driver $prop['parent_id'], $prop['title'], $prop['date'], - $prop['time'] + $prop['time'], + strval($prop['description']), + join(',', (array)$prop['tags']) ); if ($result) @@ -410,6 +413,8 @@ class tasklist_database_driver extends tasklist_driver if (isset($prop[$col])) $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . (empty($prop[$col]) ? 'NULL' : $this->rc->db->quote($prop[$col])); } + if (isset($prop['tags'])) + $sql_set[] = $this->rc->db->quote_identifier('tags') . '=' . $this->rc->db->quote(join(',', (array)$prop['tags'])); // moved from another list if ($prop['_fromlist'] && ($newlist = $prop['list'])) { diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php index b1d62eb3..897ce185 100644 --- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php +++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php @@ -326,6 +326,7 @@ class tasklist_kolab_driver extends tasklist_driver 'title' => $record['title'], # 'location' => $record['location'], 'description' => $record['description'], + 'tags' => (array)$record['categories'], 'flagged' => $record['priority'] == 1, 'complete' => $record['status'] == 'COMPLETED' ? 1 : floatval($record['complete'] / 100), 'parent_id' => $record['parent_id'], @@ -350,6 +351,7 @@ class tasklist_kolab_driver extends tasklist_driver private function _from_rcube_task($task, $old = array()) { $object = $task; + $object['categories'] = (array)$task['tags']; if (!empty($task['date'])) { $object['due'] = new DateTime($task['date'].' '.$task['time'], $this->plugin->timezone); diff --git a/plugins/tasklist/drivers/tasklist_driver.php b/plugins/tasklist/drivers/tasklist_driver.php index c1d694dc..6b93dc15 100644 --- a/plugins/tasklist/drivers/tasklist_driver.php +++ b/plugins/tasklist/drivers/tasklist_driver.php @@ -33,6 +33,7 @@ * 'changed' => , // Last modification date of record * 'title' => 'Event title/summary', * 'description' => 'Event description', + * 'tags' => array(), // List of tags for this task * 'date' => 'Due date', // as string of format YYYY-MM-DD or null if no date is set * 'time' => 'Due time', // as string of format hh::ii or null if no due time is set * 'categories' => 'Task category', diff --git a/plugins/tasklist/jquery.tagedit.js b/plugins/tasklist/jquery.tagedit.js new file mode 100755 index 00000000..a9ffd77c --- /dev/null +++ b/plugins/tasklist/jquery.tagedit.js @@ -0,0 +1,535 @@ +/* +* Tagedit - jQuery Plugin +* The Plugin can be used to edit tags from a database the easy way +* +* Examples and documentation at: tagedit.webwork-albrecht.de +* +* Copyright (c) 2010 Oliver Albrecht +* +* License: +* This work is licensed under a MIT License +* http://www.opensource.org/licenses/mit-license.php +* +* @author Oliver Albrecht Mial: info@webwork-albrecht.de Twitter: @webworka +* @version 1.2.1 (11/2011) +* Requires: jQuery v1.4+, jQueryUI v1.8+, jQuerry.autoGrowInput +* +* Example of usage: +* +* $( "input.tag" ).tagedit(); +* +* Possible options: +* +* autocompleteURL: '', // url for a autocompletion +* deleteEmptyItems: true, // Deletes items with empty value +* deletedPostfix: '-d', // will be put to the Items that are marked as delete +* addedPostfix: '-a', // will be put to the Items that are choosem from the database +* additionalListClass: '', // put a classname here if the wrapper ul shoud receive a special class +* allowEdit: true, // Switch on/off edit entries +* allowDelete: true, // Switch on/off deletion of entries. Will be ignored if allowEdit = false +* allowAdd: true, // switch on/off the creation of new entries +* direction: 'ltr' // Sets the writing direction for Outputs and Inputs +* animSpeed: 500 // Sets the animation speed for effects +* autocompleteOptions: {}, // Setting Options for the jquery UI Autocomplete (http://jqueryui.com/demos/autocomplete/) +* breakKeyCodes: [ 13, 44 ], // Sets the characters to break on to parse the tags (defaults: return, comma) +* checkNewEntriesCaseSensitive: false, // If there is a new Entry, it is checked against the autocompletion list. This Flag controlls if the check is (in-)casesensitive +* texts: { // some texts +* removeLinkTitle: 'Remove from list.', +* saveEditLinkTitle: 'Save changes.', +* deleteLinkTitle: 'Delete this tag from database.', +* deleteConfirmation: 'Are you sure to delete this entry?', +* deletedElementTitle: 'This Element will be deleted.', +* breakEditLinkTitle: 'Cancel' +* } +*/ + +(function($) { + + $.fn.tagedit = function(options) { + /** + * Merge Options with defaults + */ + options = $.extend(true, { + // default options here + autocompleteURL: null, + deletedPostfix: '-d', + addedPostfix: '-a', + additionalListClass: '', + allowEdit: true, + allowDelete: true, + allowAdd: true, + direction: 'ltr', + animSpeed: 500, + autocompleteOptions: { + select: function( event, ui ) { + $(this).val(ui.item.value).trigger('transformToTag', [ui.item.id]); + return false; + } + }, + breakKeyCodes: [ 13, 44 ], + checkNewEntriesCaseSensitive: false, + texts: { + removeLinkTitle: 'Remove from list.', + saveEditLinkTitle: 'Save changes.', + deleteLinkTitle: 'Delete this tag from database.', + deleteConfirmation: 'Are you sure to delete this entry?', + deletedElementTitle: 'This Element will be deleted.', + breakEditLinkTitle: 'Cancel' + }, + tabindex: false + }, options || {}); + + // no action if there are no elements + if(this.length == 0) { + return; + } + + // set the autocompleteOptions source + if(options.autocompleteURL) { + options.autocompleteOptions.source = options.autocompleteURL; + } + + // Set the direction of the inputs + var direction= this.attr('dir'); + if(direction && direction.length > 0) { + options.direction = this.attr('dir'); + } + + var elements = this; + + var baseNameRegexp = new RegExp("^(.*)\\[([0-9]*?("+options.deletedPostfix+"|"+options.addedPostfix+")?)?\]$", "i"); + + var baseName = elements.eq(0).attr('name').match(baseNameRegexp); + if(baseName && baseName.length == 4) { + baseName = baseName[1]; + } + else { + // Elementname does not match the expected format, exit + alert('elementname dows not match the expected format (regexp: '+baseNameRegexp+')') + return; + } + + // read tabindex from source element + var ti; + if (!options.tabindex && (ti = elements.eq(0).attr('tabindex'))) + options.tabindex = ti; + + // init elements + inputsToList(); + + /** + * Creates the tageditinput from a list of textinputs + * + */ + function inputsToList() { + var html = ''; + + elements + .append(html) + .attr('tabindex', options.tabindex) // set tabindex to