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 @@
';
+
+ 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;
+ }
+}