Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F256735
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
37 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php
new file mode 100644
index 00000000..3c369583
--- /dev/null
+++ b/plugins/kolab_notes/kolab_notes.php
@@ -0,0 +1,357 @@
+<?php
+
+/**
+ * Kolab notes module
+ *
+ * Adds simple notes management features to the web client
+ *
+ * @version @package_version@
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+class kolab_notes extends rcube_plugin
+{
+ public $task = '?(?!login|logout).*';
+ public $rc;
+
+ private $ui;
+ private $lists;
+ private $folders;
+
+ /**
+ * Required startup method of a Roundcube plugin
+ */
+ public function init()
+ {
+ $this->require_plugin('libkolab');
+
+ $this->rc = rcube::get_instance();
+
+ $this->register_task('notes');
+
+ // load plugin configuration
+ $this->load_config();
+
+ // proceed initialization in startup hook
+ $this->add_hook('startup', array($this, 'startup'));
+ }
+
+ /**
+ * Startup hook
+ */
+ public function startup($args)
+ {
+ // the notes module can be enabled/disabled by the kolab_auth plugin
+ if ($this->rc->config->get('notes_disabled', false) || !$this->rc->config->get('notes_enabled', true)) {
+ return;
+ }
+
+ // load localizations
+ $this->add_texts('localization/', $args['task'] == 'notes' && !$args['action']);
+
+ if ($args['task'] == 'notes') {
+ // register task actions
+ $this->register_action('index', array($this, 'notes_view'));
+ $this->register_action('fetch', array($this, 'notes_fetch'));
+ $this->register_action('get', array($this, 'note_record'));
+ $this->register_action('action', array($this, 'note_action'));
+ }
+
+ if (!$this->rc->output->ajax_call && !$this->rc->output->env['framed']) {
+ require_once($this->home . '/kolab_notes_ui.php');
+ $this->ui = new kolab_notes_ui($this);
+ $this->ui->init();
+ }
+
+ }
+
+ /**
+ * Read available calendars for the current user and store them internally
+ */
+ private function _read_lists($force = false)
+ {
+ // already read sources
+ if (isset($this->lists) && !$force)
+ return $this->lists;
+
+ // get all folders that have type "task"
+ $folders = kolab_storage::sort_folders(kolab_storage::get_folders('note'));
+ $this->lists = $this->folders = array();
+
+ // find default folder
+ $default_index = 0;
+ foreach ($folders as $i => $folder) {
+ if ($folder->default)
+ $default_index = $i;
+ }
+
+ // put default folder on top of the list
+ if ($default_index > 0) {
+ $default_folder = $folders[$default_index];
+ unset($folders[$default_index]);
+ array_unshift($folders, $default_folder);
+ }
+
+ $delim = $this->rc->get_storage()->get_hierarchy_delimiter();
+ $listnames = array();
+
+ // include virtual folders for a full folder tree
+ if (!$this->rc->output->ajax_call && in_array($this->rc->action, array('index','')))
+ $folders = kolab_storage::folder_hierarchy($folders);
+
+ foreach ($folders as $folder) {
+ $utf7name = $folder->name;
+
+ $path_imap = explode($delim, $utf7name);
+ $editname = rcube_charset::convert(array_pop($path_imap), 'UTF7-IMAP'); // pop off raw name part
+ $path_imap = join($delim, $path_imap);
+
+ $fullname = $folder->get_name();
+ $listname = kolab_storage::folder_displayname($fullname, $listnames);
+
+ // special handling for virtual folders
+ if ($folder->virtual) {
+ $list_id = kolab_storage::folder_id($utf7name);
+ $this->lists[$list_id] = array(
+ 'id' => $list_id,
+ 'name' => $fullname,
+ 'listname' => $listname,
+ 'virtual' => true,
+ 'editable' => false,
+ );
+ continue;
+ }
+
+ if ($folder->get_namespace() == 'personal') {
+ $norename = false;
+ $readonly = false;
+ $alarms = true;
+ }
+ else {
+ $alarms = false;
+ $readonly = true;
+ if (($rights = $folder->get_myrights()) && !PEAR::isError($rights)) {
+ if (strpos($rights, 'i') !== false)
+ $readonly = false;
+ }
+ $info = $folder->get_folder_info();
+ $norename = $readonly || $info['norename'] || $info['protected'];
+ }
+
+ $list_id = kolab_storage::folder_id($utf7name);
+ $item = array(
+ 'id' => $list_id,
+ 'name' => $fullname,
+ 'listname' => $listname,
+ 'editname' => $editname,
+ 'editable' => !$readionly,
+ 'norename' => $norename,
+ 'parentfolder' => $path_imap,
+ 'default' => $folder->default,
+ 'class_name' => trim($folder->get_namespace() . ($folder->default ? ' default' : '')),
+ );
+ $this->lists[$item['id']] = $item;
+ $this->folders[$item['id']] = $folder;
+ $this->folders[$folder->name] = $folder;
+ }
+ }
+
+ /**
+ * Get a list of available folders from this source
+ */
+ public function get_lists()
+ {
+ $this->_read_lists();
+
+ // attempt to create a default folder for this user
+ if (empty($this->lists)) {
+ #if ($this->create_list(array('name' => 'Tasks', 'color' => '0000CC', 'default' => true)))
+ # $this->_read_lists(true);
+ }
+
+ return $this->lists;
+ }
+
+
+ /******* UI functions ********/
+
+ /**
+ * Render main view of the tasklist task
+ */
+ public function notes_view()
+ {
+ $this->ui->init();
+ $this->ui->init_templates();
+ $this->rc->output->set_pagetitle($this->gettext('navtitle'));
+ $this->rc->output->send('kolab_notes.notes');
+ }
+
+ /**
+ *
+ */
+ public function notes_fetch()
+ {
+ $search = rcube_utils::get_input_value('_q', RCUBE_INPUT_GPC);
+ $list = rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC);
+
+ $data = $this->notes_data($this->list_notes($list, $search), $tags);
+ $this->rc->output->command('plugin.data_ready', array('list' => $list, 'search' => $search, 'data' => $data, 'tags' => array_values(array_unique($tags))));
+ }
+
+ /**
+ *
+ */
+ protected function notes_data($records, &$tags)
+ {
+ $tags = array();
+
+ foreach ($records as $i => $rec) {
+ $this->_client_encode($records[$i]);
+ unset($records[$i]['description']);
+
+ foreach ((array)$reg['categories'] as $tag) {
+ $tags[] = $tag;
+ }
+ }
+
+ $tags = array_unique($tags);
+ return $records;
+ }
+
+ /**
+ *
+ */
+ protected function list_notes($list_id, $search = null)
+ {
+ $results = array();
+
+ // query Kolab storage
+ $query = array();
+
+ // full text search (only works with cache enabled)
+ if (strlen($search)) {
+ foreach (rcube_utils::normalize_string(mb_strtolower($search), true) as $word) {
+ $query[] = array('words', '~', $word);
+ }
+ }
+
+ $this->_read_lists();
+ if ($folder = $this->folders[$list_id]) {
+ foreach ($folder->select($query) as $record) {
+ $record['list'] = $list_id;
+ $results[] = $record;
+ }
+ }
+
+ return $results;
+ }
+
+ public function note_record()
+ {
+ $data = $this->get_note(array(
+ 'uid' => rcube_utils::get_input_value('_id', RCUBE_INPUT_GPC),
+ 'list' => rcube_utils::get_input_value('_list', RCUBE_INPUT_GPC),
+ ));
+
+ // encode for client use
+ if (is_array($data)) {
+ $this->_client_encode($data);
+ }
+
+ $this->rc->output->command('plugin.render_note', $data);
+ }
+
+ public function get_note($note)
+ {
+ if (is_array($note)) {
+ $uid = $note['id'] ?: $note['uid'];
+ $list_id = $note['list'];
+ }
+ else {
+ $uid = $note;
+ }
+
+ $this->_read_lists();
+ if ($list_id) {
+ if ($folder = $this->folders[$list_id]) {
+ return $folder->get_object($uid);
+ }
+ }
+ // iterate over all calendar folders and search for the event ID
+ else {
+ foreach ($this->folders as $list_id => $folder) {
+ if ($result = $folder->get_object($uid)) {
+ $result['list'] = $list_id;
+ return $result;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ */
+ private function _client_encode(&$note)
+ {
+ foreach ($note as $key => $prop) {
+ if ($key[0] == '_') {
+ unset($note[$key]);
+ }
+ }
+
+ foreach (array('created','changed') as $key) {
+ if (is_object($note[$key]) && $note[$key] instanceof DateTime) {
+ $note[$key.'_'] = $note[$key]->format('U');
+ $note[$key] = $this->rc->format_date($note[$key]);
+ }
+ }
+
+ return $note;
+ }
+
+ public function note_action()
+ {
+ $action = rcube_utils::get_input_value('_do', RCUBE_INPUT_POST);
+ $note = rcube_utils::get_input_value('_data', RCUBE_INPUT_POST, true);
+
+ $success =false;
+ switch ($action) {
+ case 'save':
+ console($action, $note);
+ sleep(3);
+ $success = true;
+ break;
+ }
+
+ // show confirmation/error message
+ if ($success) {
+ $this->rc->output->show_message('successfullysaved', 'confirmation');
+ }
+ else {
+ $this->rc->output->show_message('kolab_notes.errorsaving', 'error');
+ }
+
+ // unlock client
+ $this->rc->output->command('plugin.unlock_saving');
+ // $this->rc->output->command('plugin.update_note', $note);
+ }
+
+}
+
diff --git a/plugins/kolab_notes/kolab_notes_ui.php b/plugins/kolab_notes/kolab_notes_ui.php
new file mode 100644
index 00000000..cc22a550
--- /dev/null
+++ b/plugins/kolab_notes/kolab_notes_ui.php
@@ -0,0 +1,139 @@
+<?php
+
+class kolab_notes_ui
+{
+ private $rc;
+ private $plugin;
+ private $ready = false;
+
+ function __construct($plugin)
+ {
+ $this->plugin = $plugin;
+ $this->rc = $plugin->rc;
+ }
+
+ /**
+ * Calendar UI initialization and requests handlers
+ */
+ public function init()
+ {
+ if ($this->ready) // already done
+ return;
+
+ // add taskbar button
+ $this->plugin->add_button(array(
+ 'command' => 'notes',
+ 'class' => 'button-notes',
+ 'classsel' => 'button-notes button-selected',
+ 'innerclass' => 'button-inner',
+ 'label' => 'kolab_notes.navtitle',
+ ), 'taskbar');
+
+ $this->plugin->include_stylesheet($this->plugin->local_skin_path() . '/notes.css');
+
+ $this->ready = true;
+ }
+
+ /**
+ * Register handler methods for the template engine
+ */
+ public function init_templates()
+ {
+ $this->plugin->register_handler('plugin.tagslist', array($this, 'tagslist'));
+ $this->plugin->register_handler('plugin.notebooks', array($this, 'folders'));
+ #$this->plugin->register_handler('plugin.folders_select', array($this, 'folders_select'));
+ $this->plugin->register_handler('plugin.searchform', array($this->rc->output, 'search_form'));
+ $this->plugin->register_handler('plugin.listing', array($this, 'listing'));
+ $this->plugin->register_handler('plugin.editform', array($this, 'editform'));
+ $this->plugin->register_handler('plugin.notetitle', array($this, 'notetitle'));
+ #$this->plugin->register_handler('plugin.detailview', array($this, 'detailview'));
+
+ $this->rc->output->include_script('list.js');
+ $this->plugin->include_script('notes.js');
+ $this->plugin->include_script('jquery.tagedit.js');
+
+ $this->plugin->include_stylesheet($this->plugin->local_skin_path() . '/tagedit.css');
+
+ // TODO: load config options and user prefs relevant for the UI
+ $settings = array();
+ $this->rc->output->set_env('kolab_notes_settings', $settings);
+ }
+
+ public function folders($attrib)
+ {
+ $attrib += array('id' => 'rcmkolabnotebooks');
+
+ $jsenv = array();
+ $items = '';
+ foreach ($this->plugin->get_lists() as $prop) {
+ unset($prop['user_id']);
+ $id = $prop['id'];
+
+ if (!$prop['virtual'])
+ $jsenv[$id] = $prop;
+
+ $html_id = rcube_utils::html_identifier($id);
+ $title = $prop['name'] != $prop['listname'] ? html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET) : '';
+
+ if ($prop['virtual'])
+ $class .= ' virtual';
+ else if (!$prop['editable'])
+ $class .= ' readonly';
+ if ($prop['class_name'])
+ $class .= ' '.$prop['class_name'];
+
+ $items .= html::tag('li', array('id' => 'rcmliknb' . $html_id, 'class' => trim($class)),
+ html::span(array('class' => 'listname', 'title' => $title), Q($prop['listname'])) .
+ html::span(array('class' => 'count'), '')
+ );
+ }
+
+ $this->rc->output->set_env('kolab_notebooks', $jsenv);
+ $this->rc->output->add_gui_object('notebooks', $attrib['id']);
+
+ return html::tag('ul', $attrib, $items, html::$common_attrib);
+ }
+
+ public function listing($attrib)
+ {
+ $attrib += array('id' => 'rcmkolabnoteslist');
+ $this->rc->output->add_gui_object('noteslist', $attrib['id']);
+ return html::tag('ul', $attrib, '', html::$common_attrib);
+ }
+
+ public function tagslist($attrib)
+ {
+ $attrib += array('id' => 'rcmkolabnotestagslist');
+ $this->rc->output->add_gui_object('notestagslist', $attrib['id']);
+ return html::tag('ul', $attrib, '', html::$common_attrib);
+ }
+
+ public function editform($attrib)
+ {
+ $attrib += array('action' => '#', 'id' => 'rcmkolabnoteseditform');
+ $this->rc->output->add_gui_object('noteseditform', $attrib['id']);
+
+ $textarea = new html_textarea(array('name' => 'content', 'id' => 'notecontent', 'cols' => 60, 'rows' => 20, 'tabindex' => 3));
+ return html::tag('form', $attrib, $textarea->show(), array_merge(html::$common_attrib, array('action')));
+ }
+
+ public function notetitle($attrib)
+ {
+ $attrib += array('id' => 'rcmkolabnotestitle');
+ $this->rc->output->add_gui_object('noteviewtitle', $attrib['id']);
+
+ $summary = new html_inputfield(array('name' => 'summary', 'class' => 'notetitle inline-edit', 'size' => 60, 'tabindex' => 1));
+
+ $html = $summary->show();
+ $html .= html::div(array('class' => 'tagline tagedit', 'style' => 'display:none'), ' ');
+ $html .= html::div(array('class' => 'dates', 'style' => 'display:none'),
+ html::label(array(), $this->plugin->gettext('created')) .
+ html::span('notecreated', '') .
+ html::label(array(), $this->plugin->gettext('changed')) .
+ html::span('notechanged', '')
+ );
+
+ return html::div($attrib, $html);
+ }
+}
+
diff --git a/plugins/kolab_notes/localization/en_US.inc b/plugins/kolab_notes/localization/en_US.inc
new file mode 100644
index 00000000..131c6c60
--- /dev/null
+++ b/plugins/kolab_notes/localization/en_US.inc
@@ -0,0 +1,17 @@
+<?php
+
+$labels = array();
+$labels['navtitle'] = 'Notes';
+$labels['tags'] = 'Tags';
+$labels['lists'] = 'Notebooks';
+$labels['notes'] = 'Notes';
+$labels['create'] = 'Create';
+$labels['newnote'] = 'New Note';
+$labels['notags'] = 'No tags';
+$labels['removetag'] = 'Remove tag';
+$labels['created'] = 'Created';
+$labels['changed'] = 'Last Modified';
+
+$labels['savingdata'] = 'Saving data...';
+$labels['recordnotfound'] = 'Record not found';
+$labels['entertitle'] = 'Please enter a title for this note!';
diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js
new file mode 100644
index 00000000..873c3941
--- /dev/null
+++ b/plugins/kolab_notes/notes.js
@@ -0,0 +1,339 @@
+/**
+ * Client scripts for the Kolab Notes plugin
+ *
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+function rcube_kolab_notes_ui(settings)
+{
+ /* private vars */
+ var ui_loading = false;
+ var saving_lock;
+ var search_query;
+ var noteslist;
+ var notesdata = {};
+ var tags = [];
+ var me = this;
+
+ /* public members */
+ this.selected_list;
+ this.selected_note;
+ this.notebooks = rcmail.env.kolab_notebooks || {};
+
+ /**
+ * initialize the notes UI
+ */
+ function init()
+ {
+ // register button commands
+ rcmail.register_command('createnote', function(){ edit_note(null, 'new'); }, false);
+ rcmail.register_command('list-create', function(){ list_edit_dialog(null); }, true);
+ rcmail.register_command('list-edit', function(){ list_edit_dialog(me.selected_list); }, false);
+ rcmail.register_command('list-remove', function(){ list_remove(me.selected_list); }, false);
+ rcmail.register_command('save', save_note, true);
+ rcmail.register_command('search', quicksearch, true);
+ rcmail.register_command('reset-search', reset_search, true);
+
+ // register server callbacks
+ rcmail.addEventListener('plugin.data_ready', data_ready);
+ rcmail.addEventListener('plugin.render_note', render_note);
+ rcmail.addEventListener('plugin.unlock_saving', function(){
+ if (saving_lock) {
+ rcmail.set_busy(false, null, saving_lock);
+ }
+ if (rcmail.gui_objects.noteseditform) {
+ rcmail.lock_form(rcmail.gui_objects.noteseditform, false);
+ }
+ });
+
+ // initialize folder selectors
+ var li, id;
+ for (id in me.notebooks) {
+ init_folder_li(id);
+
+ if (me.notebooks[id].editable && (!me.selected_list || (me.notebooks[id].active && !me.notebooks[me.selected_list].active))) {
+ me.selected_list = id;
+ }
+ }
+
+ // initialize notes list widget
+ if (rcmail.gui_objects.noteslist) {
+ noteslist = new rcube_list_widget(rcmail.gui_objects.noteslist,
+ { multiselect:true, draggable:false, keyboard:false });
+ noteslist.addEventListener('select', function(list) {
+ var note;
+ if (list.selection.length == 1 && (note = notesdata[list.selection[0]])) {
+ edit_note(note.uid, 'edit');
+ }
+ else {
+ reset_view();
+ }
+ })
+ .init();
+ }
+
+ if (me.selected_list) {
+ rcmail.enable_command('createnote', true);
+ $('#rcmliknb'+me.selected_list).click();
+ }
+ }
+ this.init = init;
+
+ /**
+ * Quote HTML entities
+ */
+ function Q(html)
+ {
+ return String(html).replace(/&/g, '"').replace(/</g, '<').replace(/>/g, '>');
+ }
+
+ /**
+ * Trim whitespace off the given string
+ */
+ function trim(str)
+ {
+ return String(str).replace(/\s+$/, '').replace(/^\s+/, '');
+ }
+
+ /**
+ *
+ */
+ function init_folder_li(id)
+ {
+ $('#rcmliknb'+id).click(function(e){
+ var id = $(this).data('id');
+ rcmail.enable_command('list-edit', 'list-remove', me.notebooks[id].editable);
+ fetch_notes(id);
+ me.selected_list = id;
+ })
+ .dblclick(function(e){
+ // list_edit_dialog($(this).data('id'));
+ })
+ .data('id', id);
+ }
+
+ /**
+ *
+ */
+ function edit_note(uid, action)
+ {
+ if (!uid) {
+ me.selected_note = { list:me.selected_list, uid:null, title:rcmail.gettext('newnote','kolab_notes'), description:'', categories:[] }
+ render_note(me.selected_note);
+ }
+ else {
+ ui_loading = rcmail.set_busy(true, 'loading');
+ rcmail.http_request('get', { _list:me.selected_list, _id:uid }, true);
+ }
+ }
+
+ /**
+ *
+ */
+ function list_edit_dialog(id)
+ {
+
+ }
+
+ /**
+ *
+ */
+ function list_remove(id)
+ {
+
+ }
+
+ /**
+ *
+ */
+ function quicksearch()
+ {
+
+ }
+
+ /**
+ *
+ */
+ function reset_search()
+ {
+
+ }
+
+ /**
+ *
+ */
+ function fetch_notes(id)
+ {
+ if (rcmail.busy)
+ return;
+
+ if (id) {
+ me.selected_list = id;
+ $('li.selected', rcmail.gui_objects.notebooks).removeClass('selected')
+ $('#rcmliknb'+id).addClass('selected');
+ }
+
+ ui_loading = rcmail.set_busy(true, 'loading');
+ rcmail.http_request('fetch', { _list:me.selected_list, _q:search_query }, true);
+
+ reset_view();
+ noteslist.clear();
+ notesdata = {};
+ }
+
+ /**
+ *
+ */
+ function data_ready(data)
+ {
+ data.data.sort(function(a,b){
+ return b.changed_ - a.changed_;
+ });
+
+ var i, id, rec;
+ for (i=0; data.data && i < data.data.length; i++) {
+ rec = data.data[i];
+ rec.id = rcmail.html_identifier_encode(rec.uid);
+ noteslist.insert_row({
+ id: 'rcmrow' + rec.id,
+ cols: [
+ { className:'title', innerHTML:Q(rec.title) },
+ { className:'date', innerHTML:Q(rec.changed || '') }
+ ]
+ });
+
+ notesdata[rec.id] = rec;
+ }
+
+ tags = data.tags || [];
+ rcmail.set_busy(false, 'loading', ui_loading);
+ }
+
+ /**
+ *
+ */
+ function render_note(data)
+ {
+ rcmail.set_busy(false, 'loading', ui_loading);
+
+ if (!data) {
+ rcmail.display_message(rcmail.get_label('recordnotfound', 'kolab_notes'), 'error');
+ return;
+ }
+
+ var list = me.notebooks[data.list] || me.notebooks[me.selected_list]
+ var title = $('.notetitle', rcmail.gui_objects.noteviewtitle).val(data.title);
+ var content = $('#notecontent').val(data.description);
+ $('.dates .notecreated', rcmail.gui_objects.noteviewtitle).html(Q(data.created || ''));
+ $('.dates .notechanged', rcmail.gui_objects.noteviewtitle).html(Q(data.changed || ''));
+ if (data.created || data.changed)
+ $('.dates', rcmail.gui_objects.noteviewtitle).show();
+
+ $(rcmail.gui_objects.noteseditform).show();
+
+ // tag-edit line
+ var tagline = $('.tagline', rcmail.gui_objects.noteviewtitle).empty().show();
+ $.each(typeof data.categories == 'object' && data.categories.length ? data.categories : [''], function(i,val){
+ $('<input>')
+ .attr('name', 'tags[]')
+ .attr('tabindex', '2')
+ .addClass('tag')
+ .val(val)
+ .appendTo(tagline);
+ });
+
+ if (!data.categories || !data.categories.length) {
+ $('<span>').addClass('placeholder').html(rcmail.gettext('notags', 'kolab_notes')).appendTo(tagline);
+ }
+
+ $('.tagline input.tag', rcmail.gui_objects.noteviewtitle).tagedit({
+ animSpeed: 100,
+ allowEdit: false,
+ checkNewEntriesCaseSensitive: false,
+ autocompleteOptions: { source: tags, minLength: 0 },
+ texts: { removeLinkTitle: rcmail.gettext('removetag', 'kolab_notes') }
+ })
+
+ $('.tagedit-list', rcmail.gui_objects.noteviewtitle)
+ .on('click', function(){ $('.tagline .placeholder').hide(); });
+
+ me.selected_note = data;
+ rcmail.enable_command('save', list.editable && !data.readonly);
+ content.select();
+ }
+
+ /**
+ *
+ */
+ function reset_view()
+ {
+ me.selected_note = null;
+ $('.notetitle', rcmail.gui_objects.noteviewtitle).val('');
+ $('.tagline, .dates', rcmail.gui_objects.noteviewtitle).hide();
+ $(rcmail.gui_objects.noteseditform).hide();
+ rcmail.enable_command('save', false);
+ }
+
+ /**
+ * Collect data from the edit form and submit it to the server
+ */
+ function save_note()
+ {
+ if (!me.selected_note) {
+ return false;
+ }
+
+ var savedata = {
+ title: trim($('.notetitle', rcmail.gui_objects.noteviewtitle).val()),
+ description: $('#notecontent').val(),
+ list: me.selected_note.list || me.selected_list,
+ uid: me.selected_note.uid,
+ categories: []
+ };
+
+ // collect tags
+ $('.tagedit-list input[type="hidden"]', rcmail.gui_objects.noteviewtitle).each(function(i, elem){
+ if (elem.value)
+ savedata.categories.push(elem.value);
+ });
+ // including the "pending" one in the text box
+ var newtag = $('#tagedit-input').val();
+ if (newtag != '') {
+ savedata.categories.push(newtag);
+ }
+
+ // do some input validation
+ if (savedata.title == '') {
+ alert(rcmail.gettext('entertitle', 'kolab_notes'))
+ return false;
+ }
+
+ rcmail.lock_form(rcmail.gui_objects.noteseditform, true);
+ saving_lock = rcmail.set_busy(true, 'kolab_notes.savingdata');
+ rcmail.http_post('action', { _data: savedata, _do:'save' }, true);
+ }
+}
+
+
+/* notes plugin UI initialization */
+var kolabnotes;
+window.rcmail && rcmail.addEventListener('init', function(evt) {
+ kolabnotes = new rcube_kolab_notes_ui(rcmail.env.kolab_notes_settings);
+ kolabnotes.init();
+});
+
diff --git a/plugins/kolab_notes/skins/larry/notes.css b/plugins/kolab_notes/skins/larry/notes.css
new file mode 100644
index 00000000..1f86084b
--- /dev/null
+++ b/plugins/kolab_notes/skins/larry/notes.css
@@ -0,0 +1,227 @@
+/**
+ * Kolab Notes plugin styles for skin "Larry"
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
+ * Screendesign by FLINT / Büro für Gestaltung, bueroflint.com
+ *
+ * The contents are subject to the Creative Commons Attribution-ShareAlike
+ * License. It is allowed to copy, distribute, transmit and to adapt the work
+ * by keeping credits to the original autors in the README file.
+ * See http://creativecommons.org/licenses/by-sa/3.0/ for details.
+ */
+
+.notesview #sidebar {
+ position: absolute;
+ top: 42px;
+ left: 0;
+ bottom: 0;
+ width: 240px;
+}
+
+.notesview #notestoolbar {
+ position: absolute;
+ top: -6px;
+ left: 0;
+ width: 100%;
+ height: 40px;
+ white-space: nowrap;
+}
+
+.notesview #notestoolbar a.button.createnote {
+
+}
+
+.notesview #taskbar a.button-notes span.button-inner {
+
+}
+
+.notesview #taskbar a.button-notes.button-selected span.button-inner {
+
+}
+
+.notesview #quicksearchbar {
+ top: 8px;
+}
+
+.notesview #searchmenulink {
+ width: 15px;
+}
+
+.notesview #mainview-right {
+ top: 42px;
+}
+
+.notesview #tagsbox {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 242px;
+}
+
+.notesview #notebooksbox {
+ position: absolute;
+ top: 300px;
+ left: 0;
+ width: 100%;
+ bottom: 0px;
+}
+
+.notesview #noteslistbox {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 240px;
+ bottom: 0px;
+}
+
+.notesview #kolabnoteslist .title {
+ display: block;
+ padding: 4px 8px;
+}
+
+.notesview #kolabnoteslist .date {
+ display: block;
+ padding: 0px 8px 4px 8px;
+ color: #777;
+ font-weight: normal;
+}
+
+.notesview #notedetailsbox {
+ position: absolute;
+ top: 0;
+ left: 256px;
+ right: 0;
+ bottom: 0px;
+}
+
+.notesview #notedetailsbox .formbuttons {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 8px 12px;
+ background: #f9f9f9;
+}
+
+.notesview #notecontent {
+ position: absolute;
+ top: 82px;
+ left: 0;
+ bottom: 41px;
+ width: 100%;
+ border: 0;
+ border-radius: 0;
+ padding: 8px 0 8px 8px;
+ resize: none;
+ font-family: monospace;
+ font-size: 9pt;
+ outline: none;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.2);
+ -moz-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.2);
+ -o-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.2);
+ box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.2);
+}
+
+.notesview #notecontent:active,
+.notesview #notecontent:focus {
+ -webkit-box-shadow: inset 0 0 3px 2px rgba(71,135,177, 0.9);
+ -moz-box-shadow: inset 0 0 3px 2px rgba(71,135,177, 0.9);
+ -o-box-shadow: inset 0 0 3px 2px rgba(71,135,177, 0.9);
+ box-shadow: inset 0 0 3px 2px rgba(71,135,177, 0.9);
+}
+
+.notesview #notedetailstitle {
+ height: 68px;
+}
+
+.notesview #notedetailstitle .tagedit-list,
+.notesview #notedetailstitle input.inline-edit,
+.notesview #notedetailstitle input.inline-edit:focus {
+ outline: none;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ background: rgba(255,255,255,0.01);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ -o-box-shadow: none;
+ box-shadow: none;
+}
+
+.notesview #notedetailstitle input.notetitle,
+.notesview #notedetailstitle input.notetitle:focus {
+ width: 100%;
+ font-size: 14px;
+ font-weight: bold;
+ color: #777;
+}
+
+.notesview #notedetailstitle .dates,
+.notesview #notedetailstitle .tagline {
+ color: #999;
+ font-weight: normal;
+ font-size: 0.9em;
+ margin-top: 6px;
+}
+
+.notesview #notedetailstitle .dates {
+ margin-top: 0;
+}
+
+.notesview #notedetailstitle .tagline {
+ position: relative;
+ cursor: text;
+}
+
+.notesview #notedetailstitle .tagline .placeholder {
+ position: absolute;
+ top: 4px;
+ left: 0;
+ z-index: 1;
+}
+
+.notesview #notedetailstitle .tagedit-list {
+ position: relative;
+ z-index: 2;
+}
+
+.notesview #notedetailstitle #tagedit-input {
+ background: none;
+}
+
+.notesview #notedetailstitle .notecreated,
+.notesview #notedetailstitle .notechanged {
+ display: inline-block;
+ padding-left: 0.4em;
+ padding-right: 2em;
+ color: #777;
+}
+
+.notesview #notebooks li {
+ margin: 0;
+ height: 20px;
+ padding: 6px 8px 2px 6px;
+ display: block;
+ position: relative;
+ white-space: nowrap;
+}
+
+.notesview #notebooks li span.listname {
+ display: block;
+ position: absolute;
+ top: 7px;
+ left: 32px;
+ right: 26px;
+ cursor: default;
+ padding-bottom: 2px;
+ padding-right: 30px;
+ color: #004458;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
diff --git a/plugins/kolab_notes/skins/larry/tagedit.css b/plugins/kolab_notes/skins/larry/tagedit.css
new file mode 120000
index 00000000..f25378ff
--- /dev/null
+++ b/plugins/kolab_notes/skins/larry/tagedit.css
@@ -0,0 +1 @@
+../../../tasklist/skins/larry/tagedit.css
\ No newline at end of file
diff --git a/plugins/kolab_notes/skins/larry/templates/notes.html b/plugins/kolab_notes/skins/larry/templates/notes.html
new file mode 100644
index 00000000..567941bc
--- /dev/null
+++ b/plugins/kolab_notes/skins/larry/templates/notes.html
@@ -0,0 +1,97 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+</head>
+<body class="notesview noscroll">
+
+<roundcube:include file="/includes/header.html" />
+
+<div id="mainscreen">
+ <div id="notestoolbar" class="toolbar">
+ <roundcube:button command="createnote" type="link" class="button createnote disabled" classAct="button createnote" classSel="button createnote pressed" label="kolab_notes.create" title="kolab_notes.createnote" />
+ <roundcube:container name="toolbar" id="notestoolbar" />
+
+ <div id="quicksearchbar">
+ <roundcube:object name="plugin.searchform" id="quicksearchbox" />
+ <a id="searchmenulink" class="iconbutton searchoptions" > </a>
+ <roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " />
+ </div>
+ </div>
+
+ <div id="sidebar">
+ <div id="tagsbox" class="uibox listbox">
+ <h2 class="boxtitle"><roundcube:label name="kolab_notes.tags" id="taglist" /></h2>
+ <div class="scroller">
+ <roundcube:object name="plugin.tagslist" id="tagslist" />
+ </div>
+ </div>
+
+ <div id="notebooksbox" class="uibox listbox">
+ <h2 class="boxtitle"><roundcube:label name="kolab_notes.lists" /></h2>
+ <div class="scroller withfooter">
+ <roundcube:object name="plugin.notebooks" id="notebooks" class="listing" />
+ </div>
+ <div class="boxfooter">
+ <roundcube:button command="list-create" type="link" title="kolab_notes.createlist" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="notesoptionslink" id="notesoptionsmenulink" type="link" title="kolab_notes.listactions" class="listbutton groupactions" onclick="UI.show_popup('notesoptionsmenu', undefined, { above:true });return false" innerClass="inner" content="⚙" />
+ </div>
+ </div>
+ </div>
+
+ <div id="mainview-right">
+ <div id="noteslistbox" class="uibox listbox">
+ <h2 class="boxtitle"><roundcube:label name="kolab_notes.notes" /></h2>
+ <div class="scroller withfooter">
+ <roundcube:object name="plugin.listing" id="kolabnoteslist" class="listing" />
+ </div>
+ <div class="boxfooter">
+ <roundcube:button command="delete" type="link" title="delete" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" content="-" />
+ <roundcube:object name="plugin.recordsCountDisplay" class="countdisplay" label="fromtoshort" />
+ </div>
+ </div>
+
+ <div id="notedetailsbox" class="uibox contentbox">
+ <roundcube:object name="plugin.notetitle" id="notedetailstitle" class="boxtitle" />
+ <roundcube:object name="plugin.editform" id="noteform" />
+ <roundcube:object name="plugin.detailview" id="notedetails" class="scroller" />
+ <div class="footerleft formbuttons">
+ <roundcube:button command="save" type="input" class="button mainaction" label="save" />
+ </div>
+ </div>
+
+ </div>
+
+</div>
+
+<roundcube:object name="message" id="messagestack" />
+
+<div id="notesoptionsmenu" class="popupmenu">
+ <ul class="toolbarmenu">
+ <li><roundcube:button command="list-edit" label="edit" classAct="active" /></li>
+ <li><roundcube:button command="list-remove" label="delete" classAct="active" /></li>
+ <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
+ </ul>
+</div>
+
+
+<script type="text/javascript">
+
+// UI startup
+var UI = new rcube_mail_ui();
+
+$(document).ready(function(e){
+ UI.init();
+
+ new rcube_splitter({ id:'notesviewsplitter', p1:'#sidebar', p2:'#mainview-right',
+ orientation:'v', relative:true, start:240, min:180, size:16, offset:2 }).init();
+ new rcube_splitter({ id:'noteslistsplitter2', p1:'#noteslistbox', p2:'#notedetailsbox',
+ orientation:'v', relative:true, start:242, min:180, size:16, offset:2 }).init();
+ new rcube_splitter({ id:'notesviewsplitterv', p1:'#tagsbox', p2:'#notebooksbox',
+ orientation:'h', relative:true, start:242, min:120, size:16, offset:6 }).init();
+});
+
+</script>
+
+</body>
+</html>
\ No newline at end of file
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Jun 9, 11:31 AM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196800
Default Alt Text
(37 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment