diff --git a/MenuItem.php b/MenuItem.php new file mode 100644 index 0000000..ee27fa1 --- /dev/null +++ b/MenuItem.php @@ -0,0 +1,79 @@ +params['rev'] = $REV; + } + + /** + * Get label from plugin language file + * + * @return string + */ + public function getLabel() { + $hlp = plugin_load('helper', 'tagging'); + return $hlp->getLang('edit_tags_button'); + } + + /** + * Return the link this item links to + * + * @return string + */ + public function getLink() { + return 'javascript:void(0);'; + } + + /** + * Convenience method to get the attributes for constructing an element + * + * @see buildAttributes() + * @return array + */ + public function getLinkAttributes($classprefix = 'menuitemtagging ') { + global $INFO; + global $lang; + + $attr = array( + 'href' => $this->getLink(), + 'title' => $this->getTitle(), + ); + $attr['rel'] = 'nofollow'; + + /** @var helper_plugin_tagging $hlp */ + $hlp = plugin_load('helper', 'tagging'); + + $filter = array('pid' => $INFO['id']); + if ($hlp->getConf('singleusermode')) { + $filter['tagger'] = 'auto'; + } + + $tags = $hlp->findItems($filter, 'tag'); + $attr['data-tags'] = implode(', ', array_keys($tags)); + + return $attr; + } +} diff --git a/action.php b/action.php index ec7c26e..3a53692 100644 --- a/action.php +++ b/action.php @@ -69,6 +69,10 @@ function register(Doku_Event_Handler $controller) { 'PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 'update_moved_page' ); + + $controller->register_hook( + 'MENU_ITEMS_ASSEMBLY', 'AFTER', $this, + 'add_menu', array()); } /** @@ -103,6 +107,12 @@ function handle_ajax_call_unknown(Doku_Event &$event, $param) { $this->deleteTag(); } elseif ($event->data === 'plugin_tagging_rename') { $this->renameTag(); + } elseif ($event->data === 'plugin_tagging_get') { + $this->getTags(); + } elseif ($event->data === 'plugin_tagging_add_tag') { + $this->addTag(); + } elseif ($event->data === 'plugin_tagging_remove_tag') { + $this->removeTag(); } else { $handled = false; } @@ -602,4 +612,154 @@ protected function restoreSearchQuery() global $QUERY; $QUERY = $this->originalQuery; } + + /** + * Add tagging button to page tools menu + * + * @param Doku_Event $event + */ + public function add_menu(Doku_Event $event) { + if($event->data['view'] != 'page') return; + // ToDo: only insert button if configured and if logged in + // user has got the required permissions + if($this->getConf('showedittagsbutton')) { + array_splice($event->data['items'], -1, 0, [new \dokuwiki\plugin\tagging\MenuItem()]); + } + } + + protected function getTags() { + global $INFO; + + /** @var helper_plugin_tagging $hlp */ + $hlp = plugin_load('helper', 'tagging'); + + $filter = array('pid' => $INFO['id']); + if ($hlp->getConf('singleusermode')) { + $filter['tagger'] = 'auto'; + } + + $tags = $hlp->findItems($filter, 'tag'); + $tags = implode(', ', array_keys($tags)); + $tags = array('tags' => $tags); + + header('Content-Type: application/json'); + echo json_encode($tags); + } + + /** + * Add single tag, return current tags + */ + function addTag() { + global $INPUT; + global $INFO; + + $error = false; + + /** @var helper_plugin_tagging $hlp */ + $hlp = plugin_load('helper', 'tagging'); + + $data = $INPUT->arr('tagging'); + $id = $INPUT->str('id'); + $tag = $INPUT->str('tag'); + $user = $hlp->getUser(); + $INFO['writable'] = auth_quickaclcheck($id) >= AUTH_EDIT; // we also need this in findItems + + if (empty($id) || empty($tag) || empty($user)) { + $error = true; + $msg = 'missing parameters'; + } else if ($INFO['writable'] && $hlp->getUser()) { + // Get saved tags + $filter = array('pid' => $INFO['id']); + if ($hlp->getConf('singleusermode')) { + $filter['tagger'] = 'auto'; + } + + $tags = $hlp->findItems($filter, 'tag'); + $tags = array_keys($tags); + + // Add new tag, if not yet existing + if (array_search($tag, $tags) === false) { + array_push($tags, $tag); + $hlp->replaceTags($id, $user, $tags); + $msg = 'added \''.$tag.'\' to page \''.$id.'\''; + } else { + $error = true; + $msg = 'tag already exists'; + } + } else { + $error = true; + $msg = 'permission denied'; + } + + // Return JSON encoded list of current tags + $tags = $hlp->findItems($filter, 'tag'); + $tags = implode(', ', array_keys($tags)); + if ($error) { + $result = array('tags' => $tags, 'error' => $error, 'message' => $msg); + } else { + $result = array('tags' => $tags, 'error' => $error, 'message' => $msg); + } + + header('Content-Type: application/json'); + echo json_encode($result); + } + + /** + * Remove single tag, return current tags + */ + function removeTag() { + global $INPUT; + global $INFO; + + $error = false; + + /** @var helper_plugin_tagging $hlp */ + $hlp = plugin_load('helper', 'tagging'); + + $data = $INPUT->arr('tagging'); + $id = $INPUT->str('id'); + $tag = $INPUT->str('tag'); + $user = $hlp->getUser(); + $INFO['writable'] = auth_quickaclcheck($id) >= AUTH_EDIT; // we also need this in findItems + + if (empty($id) || empty($tag) || empty($user)) { + $error = true; + $msg = 'missing parameters'; + } else if ($INFO['writable'] && $hlp->getUser()) { + // Get saved tags + $filter = array('pid' => $INFO['id']); + if ($hlp->getConf('singleusermode')) { + $filter['tagger'] = 'auto'; + } + + $tags = $hlp->findItems($filter, 'tag'); + $tags = array_keys($tags); + + // Remove tag, if existing + $key = array_search($tag, $tags); + if ($key !== false) { + unset($tags[$key]); + $hlp->replaceTags($id, $user, $tags); + $msg = 'removed \''.$tag.'\' from page \''.$id.'\''; + } else { + $error = true; + $msg = 'tag does not exist'; + } + } else { + $error = true; + $msg = 'permission denied'; + } + + // Return JSON encoded list of current tags + $tags = $hlp->findItems($filter, 'tag'); + $tags = implode(', ', array_keys($tags)); + if ($error) { + $result = array('tags' => $tags, 'error' => $error, 'message' => $msg); + } else { + $result = array('tags' => $tags, 'error' => $error, 'message' => $msg); + } + + header('Content-Type: application/json'); + echo json_encode($result); + } } diff --git a/conf/default.php b/conf/default.php index c36c685..8cb57ec 100644 --- a/conf/default.php +++ b/conf/default.php @@ -1,5 +1,6 @@ '; + if (tags && tags.length > 0) { + tags.forEach(function (tag) { + var button = '' + + LANG.plugins.tagging.edit_dialog_button_delete + ''; + table += ''; + }); + } else { + table += ''; + } + table += '
' + tag + '' + button + '
' + LANG.plugins.tagging.notags + '
'; + + return table; + } + + /** + * Create and show the edit tags dialog. + * Tags can be added and removed from the current page. + * Closing the dialog refreshes the browser page. + * + * @param {array} tags Array of tags + * @returns {string} HTML code + */ + function plugin_tagging_show_edit_dialog(tags) { + var content = '
'; + + dialog = jQuery(content).dialog({ + resizable: false, + width: 480, + height: 'auto', + modal: true, + buttons: { + Close: function() { + jQuery(this).dialog('close'); + } + }, + close: function( event, ui ) { + jQuery(this).dialog('destroy'); + location.reload(); + } + }); + jQuery(dialog).append('

' + LANG.plugins.tagging.edit_dialog_text_list + '

'); + + table = edit_dialog_build_tag_list(tags); + jQuery(dialog).append(table); + + jQuery('.tagging_delete_button').button({ + icon: "ui-icon-trash" + }); + + var button = '' + + LANG.plugins.tagging.edit_dialog_button_add + ''; + var input = '
' + + '' + button; + jQuery(dialog).append(input); + + jQuery('.tagging_add_button').button({ + icon: "ui-icon-plus" + }); + + jQuery('.tagging_add_button').click(edit_dialog_add_tag); + jQuery('.tagging_delete_button').click(edit_dialog_delete_tag); + + jQuery('#new_tag_name').keyup(function (event) { + if (event.which === 13) { + edit_dialog_add_tag(); + } else { + setTimeout(edit_dialog_validate_input, 250); + } + }); + + return dialog; + } + + /** + * Callback function for validation of the input field '#new_tag_name'. + * + * @returns {boolean} true if valid, false otherwise + */ + function edit_dialog_validate_input() { + var tag = jQuery('#new_tag_name').val(), + valid = true; + + if (tag.length > 0) { + var $cells = jQuery('#tag_list td:first-child'); + for (var cell of $cells) { + if (tag === cell.textContent) { + // Ignore duplicates. + valid = false; + break; + } + } + } else { + valid = false; + } + + var input = jQuery('#new_tag_name'); + if (valid) { + input.addClass('valid_input'); + input.removeClass('invalid_input'); + } else { + input.removeClass('valid_input'); + input.addClass('invalid_input'); + } + + return valid; + } + + /** + * The function updates the tag list in the edit dialog. + * + * @param {array} tags Array of tags + */ + function edit_dialog_update_tags(tags) { + table = edit_dialog_build_tag_list(tags); + jQuery('#tag_list').replaceWith(table); + jQuery('.tagging_add_button').click(edit_dialog_add_tag); + jQuery('.tagging_delete_button').click(edit_dialog_delete_tag); + } + + /** + * Reads tags from the given Jquery ajax response and returns + * them as an array (might be empty). + * + * @param {object} response Ajax response object + * @returns {array} Array of tags + */ + function get_tags_from_response(response) { + if (response.tags && response.tags.length > 0) { + tags = response.tags.split(/,\s*/); + } else { + tags = []; + } + return tags; + } + + /** + * Callback function for the add button. + * Adds a new tag. + */ + function edit_dialog_add_tag() { + var tag = jQuery('#new_tag_name').val(); + + if (edit_dialog_validate_input()) { + // Clear input field + jQuery('#new_tag_name').val(''); + + result = callBackend({call: 'plugin_tagging_add_tag', tag: tag}, + function (response) { + tags = get_tags_from_response(response); + edit_dialog_update_tags(tags); + }, + function (error) { + }); + } + } + + /** + * Callback function for the delete button. + * Removes the clicked tag. + */ + function edit_dialog_delete_tag() { + var tag = jQuery(this).closest("td").prev().html(); + + result = callBackend({call: 'plugin_tagging_remove_tag', tag: tag}, + function (response) { + tags = get_tags_from_response(response); + edit_dialog_update_tags(tags); + }, + function (error) { + }); + } }); diff --git a/style.less b/style.less index 4dcca6a..ba0fe88 100644 --- a/style.less +++ b/style.less @@ -135,3 +135,43 @@ table.plugin_tagging { width: 300px; } } + +#tagging__edit_dialog { + p, table { + margin-top: 1em; + margin-bottm: 1em; + } + + table { + width: 100%; + margin-left: auto; + margin-right: auto; + border-collapse: collapse; + border: none; + } + + tr { + background-color: gray; + } + + td { + background-color: white; + margin: 5px; + text-align: center; + vertical-align: middle; + border: solid #ddd; + border-width: 2px 0px; + } + + input { + margin: 5px; + } + + .invalid_input { + background-color: LightPink; + } + + .valid_input { + background-color: LightGreen; + } +}