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 @@ + + * + * Copyright (C) 2014, Kolab Systems AG + * + * 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 . + */ + +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 @@ +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 @@ + + * + * Copyright (C) 2014, Kolab Systems AG + * + * 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 . + */ + +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, '>'); + } + + /** + * 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){ + $('') + .attr('name', 'tags[]') + .attr('tabindex', '2') + .addClass('tag') + .val(val) + .appendTo(tagline); + }); + + if (!data.categories || !data.categories.length) { + $('').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 + * 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="pagetitle" /> + + + + + + +
+
+ + + +
+ + + +
+
+ + + +
+
+

+
+ +
+
+ + +
+
+ +
+ + + +
+ +
+
+ +
+ +
+ + + +
+
    +
  • +
  • +
  • +
+
+ + + + + + \ No newline at end of file