diff --git a/admin/tags.php b/admin/tags.php
index a957c889d..52fcd8270 100644
--- a/admin/tags.php
+++ b/admin/tags.php
@@ -14,16 +14,6 @@ if( !defined("PHPWG_ROOT_PATH") )
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
check_status(ACCESS_ADMINISTRATOR);
-if (!empty($_POST))
-{
- check_pwg_token();
- check_input_parameter('tags', $_POST, true, PATTERN_ID);
- check_input_parameter('selectAction', $_POST, false, '/^[a-zA-Z0-9_-]+$/');
- check_input_parameter('edit_list', $_POST, false, '/^\d+(,\d+)*$/');
- check_input_parameter('merge_list', $_POST, false, '/^\d+(,\d+)*$/');
- check_input_parameter('destination_tag', $_POST, false, PATTERN_ID);
-}
-
// +-----------------------------------------------------------------------+
// | tabs |
// +-----------------------------------------------------------------------+
@@ -37,64 +27,6 @@ $tabsheet->set_id('tags');
$tabsheet->select('');
$tabsheet->assign();
-// +-----------------------------------------------------------------------+
-// | edit tags |
-// +-----------------------------------------------------------------------+
-
-if (isset($_POST['edit_submit']))
-{
- $query = '
-SELECT name
- FROM '.TAGS_TABLE.'
-;';
- $existing_names = array_from_query($query, 'name');
-
-
- $current_name_of = array();
- $query = '
-SELECT id, name
- FROM '.TAGS_TABLE.'
- WHERE id IN ('.$_POST['edit_list'].')
-;';
- $result = pwg_query($query);
- while ($row = pwg_db_fetch_assoc($result))
- {
- $current_name_of[ $row['id'] ] = $row['name'];
- }
-
- $updates = array();
- // we must not rename tag with an already existing name
- foreach (explode(',', $_POST['edit_list']) as $tag_id)
- {
- $tag_name = stripslashes($_POST['tag_name-'.$tag_id]);
-
- if ($tag_name != $current_name_of[$tag_id])
- {
- if (in_array($tag_name, $existing_names))
- {
- $page['errors'][] = l10n('Tag "%s" already exists', $tag_name);
- }
- else if (!empty($tag_name))
- {
- $updates[] = array(
- 'id' => $tag_id,
- 'name' => addslashes($tag_name),
- 'url_name' => trigger_change('render_tag_url', $tag_name),
- );
- }
- }
- }
- mass_updates(
- TAGS_TABLE,
- array(
- 'primary' => array('id'),
- 'update' => array('name', 'url_name'),
- ),
- $updates
- );
-
- pwg_activity('tag', explode(',', $_POST['edit_list']), 'edit');
-}
// +-----------------------------------------------------------------------+
// | dulicate tags |
// +-----------------------------------------------------------------------+
@@ -282,67 +214,21 @@ SELECT
}
}
-
-// +-----------------------------------------------------------------------+
-// | delete tags |
-// +-----------------------------------------------------------------------+
-
-if (isset($_POST['delete']) and isset($_POST['tags']))
-{
- if (!isset($_POST['confirm_deletion']))
- {
- $page['errors'][] = l10n('You need to confirm deletion');
- }
- else
- {
- $query = '
-SELECT name
- FROM '.TAGS_TABLE.'
- WHERE id IN ('.implode(',', $_POST['tags']).')
-;';
- $tag_names = array_from_query($query, 'name');
-
- delete_tags($_POST['tags']);
-
- $page['infos'][] = l10n_dec(
- 'The following tag was deleted', 'The %d following tags were deleted',
- count($tag_names)
- )
- .' : '.implode(', ', $tag_names);
- }
-}
-
// +-----------------------------------------------------------------------+
// | delete orphan tags |
// +-----------------------------------------------------------------------+
+$message_tags = "";
+
if (isset($_GET['action']) and 'delete_orphans' == $_GET['action'])
{
check_pwg_token();
delete_orphan_tags();
- $_SESSION['page_infos'] = array(l10n('Orphan tags deleted'));
+ $message_tags = array(l10n('Orphan tags deleted'));
redirect(get_root_url().'admin.php?page=tags');
}
-// +-----------------------------------------------------------------------+
-// | add a tag |
-// +-----------------------------------------------------------------------+
-
-if (isset($_POST['add']) and !empty($_POST['add_tag']))
-{
- $ret = create_tag($_POST['add_tag']);
-
- if (isset($ret['error']))
- {
- $page['errors'][] = $ret['error'];
- }
- else
- {
- $page['infos'][] = $ret['info'];
- }
-}
-
// +-----------------------------------------------------------------------+
// | template init |
// +-----------------------------------------------------------------------+
@@ -360,6 +246,8 @@ $template->assign(
// | orphan tags |
// +-----------------------------------------------------------------------+
+$warning_tags = "";
+
$orphan_tags = get_orphan_tags();
$orphan_tag_names = array();
@@ -370,14 +258,23 @@ foreach ($orphan_tags as $tag)
if (count($orphan_tag_names) > 0)
{
- $page['warnings'][] = sprintf(
- l10n('You have %d orphan tags: %s.').' '.l10n('Delete orphan tags').'',
+ $warning_tags = sprintf(
+ l10n('You have %d orphan tags: %s.'),
count($orphan_tag_names),
- implode(', ', $orphan_tag_names),
- get_root_url().'admin.php?page=tags&action=delete_orphans&pwg_token='.get_pwg_token()
+ ''
+ .l10n('See details').''
);
}
+$template->assign(
+ array(
+ 'warning_tags' => $warning_tags,
+ 'message_tags' => $message_tags
+ )
+ );
+
// +-----------------------------------------------------------------------+
// | form creation |
// +-----------------------------------------------------------------------+
@@ -423,38 +320,6 @@ $template->assign(
)
);
-if ((isset($_POST['edit']) or isset($_POST['duplicate']) or isset($_POST['merge'])) and isset($_POST['tags']))
-{
- $list_name = 'EDIT_TAGS_LIST';
- if (isset($_POST['duplicate']))
- {
- $list_name = 'DUPLIC_TAGS_LIST';
- }
- elseif (isset($_POST['merge']))
- {
- $list_name = 'MERGE_TAGS_LIST';
- }
-
- $template->assign($list_name, implode(',', $_POST['tags']));
-
- $query = '
-SELECT id, name
- FROM '.TAGS_TABLE.'
- WHERE id IN ('.implode(',', $_POST['tags']).')
-;';
- $result = pwg_query($query);
- while ($row = pwg_db_fetch_assoc($result))
- {
- $template->append(
- 'tags',
- array(
- 'ID' => $row['id'],
- 'NAME' => $row['name'],
- )
- );
- }
-}
-
// +-----------------------------------------------------------------------+
// | sending html code |
// +-----------------------------------------------------------------------+
diff --git a/admin/themes/clear/theme.css b/admin/themes/clear/theme.css
index 08552f570..b442af2c4 100644
--- a/admin/themes/clear/theme.css
+++ b/admin/themes/clear/theme.css
@@ -513,12 +513,12 @@ input:focus + .slider {
color:#a0a0a0;
}
-.SelectionModeGroup button{
+#selection-mode-block button{
border: 1px solid #e7e7e7;
}
-.SelectionModeGroup button:hover{
+#selection-mode-block button:hover{
background-color: #ffa744;
border: 1px solid #ffa744;
}
diff --git a/admin/themes/default/js/common.js b/admin/themes/default/js/common.js
index f6f03e147..d1cb1f01b 100644
--- a/admin/themes/default/js/common.js
+++ b/admin/themes/default/js/common.js
@@ -125,4 +125,137 @@ function sprintf() {
}
return o.join('');
+}
+
+// Class to implement a temporary state and reverse it
+class TemporaryState {
+ //Arrays to reverse changes
+ attrChanges = []; //Attribute changes : {object(s), attribute, value}
+ classChanges = []; //Class changes : {object(s), state(add:true/remove:false), class}
+ htmlChanges = []; //Html changes : {object(s), html}
+
+ /**
+ * Change temporaly an attribute of an object
+ * @param {Jquery Object(s)} obj HTML Object(s)
+ * @param {String} attr Attribute
+ * @param {String} tempVal Temporary value of the attribute
+ */
+ changeAttribute(obj, attr, tempVal) {
+ for (let i = 0; i < obj.length; i++) {
+ this.attrChanges.push({
+ object: $(obj[i]),
+ attribute: attr,
+ value: $(obj[i]).attr(attr)
+ })
+ }
+ obj.attr(attr, tempVal)
+ }
+
+ /**
+ * Add/remove a class temporarily
+ * @param {Jquery Object(s)} obj HTML Object
+ * @param {Boolean} st Add (true) or Remove (false) the class
+ * @param {String} loadclass Class Name
+ */
+ changeClass(obj, st, tempclass) {
+ for (let i = 0; i < obj.length; i++) {
+ if (!($(obj[i]).hasClass(tempclass) && st)) {
+ this.classChanges.push({
+ object: $(obj[i]),
+ state: !st,
+ class: tempclass
+ })
+ if (st)
+ $(obj[i]).addClass(tempclass)
+ else
+ $(obj[i]).removeClass(tempclass)
+ }
+ }
+ }
+
+ /**
+ * Add temporarily a class to the object
+ * @param {Jquery Object(s)} obj
+ * @param {string} tempclass
+ */
+ addClass(obj, tempclass) {
+ this.changeClass(obj, true, tempclass);
+ }
+
+ /**
+ * Remove temporarily a class to the object
+ * @param {Jquery Object(s)} obj
+ * @param {string} tempclass
+ */
+ removeClass(obj, tempclass) {
+ this.changeClass(obj, false, tempclass);
+ }
+
+ /**
+ * Change temporaly the html of objects (remove event handlers on the actual content)
+ * @param {Jquery Object(s)} obj
+ * @param {string} temphtml
+ */
+ changeHTML(obj, temphtml) {
+ for (let i = 0; i < obj.length; i++) {
+ this.htmlChanges.push({
+ object:$(obj[i]),
+ html:$(obj[i]).html()
+ })
+ }
+ obj.html(temphtml);
+ }
+
+ /**
+ * Reverse all the changes and clear the history
+ */
+ reverse() {
+ this.attrChanges.forEach(function(change) {
+ if (change.value == undefined) {
+ change.object.removeAttr(change.attribute);
+ } else {
+ change.object.attr(change.attribute, change.value)
+ }
+ })
+ this.classChanges.forEach(function(change) {
+ if (change.state)
+ change.object.addClass(change.class)
+ else
+ change.object.removeClass(change.class)
+ })
+ this.htmlChanges.forEach(function(change) {
+ change.object.html(change.html);
+ })
+ this.attrChanges = [];
+ this.classChanges = [];
+ this.htmlChanges = [];
+ }
+}
+
+const jConfirm_alert_options = {
+ icon: 'icon-ok',
+ titleClass: "jconfirmAlert",
+ theme:"modern",
+ closeIcon: true,
+ draggable: false,
+ animation: "zoom",
+ boxWidth: '20%',
+ useBootstrap: false,
+ backgroundDismiss: true,
+ animateFromElement: false,
+ typeAnimated: false,
+}
+
+const jConfirm_confirm_options = {
+ draggable: false,
+ titleClass: "jconfirmDeleteConfirm",
+ theme: "modern",
+ content: "",
+ animation: "zoom",
+ boxWidth: '30%',
+ useBootstrap: false,
+ type: 'red',
+ animateFromElement: false,
+ backgroundDismiss: true,
+ typeAnimated: false,
}
\ No newline at end of file
diff --git a/admin/themes/default/js/group_list.js b/admin/themes/default/js/group_list.js
index eba4f6fd9..d4021e51d 100644
--- a/admin/themes/default/js/group_list.js
+++ b/admin/themes/default/js/group_list.js
@@ -1,17 +1,4 @@
const DELAY_FEEDBACK = 3000;
-const jConfirm_alert_options = {
- icon: 'icon-ok',
- titleClass: "groupAlert",
- theme:"modern",
- closeIcon: true,
- draggable: false,
- animation: "zoom",
- boxWidth: '20%',
- useBootstrap: false,
- backgroundDismiss: true,
- animateFromElement: false,
- typeAnimated: false,
-}
/*-------
Group Popin
-------*/
@@ -265,7 +252,7 @@ var deleteGroup = function (id) {
$.confirm({
title: str_delete.replace("%s",$("#group-"+id+" #group_name").html()),
draggable: false,
- titleClass: "groupDeleteConfirm",
+ titleClass: "jconfirmDeleteConfirm",
theme: "modern",
content: "",
animation: "zoom",
@@ -939,109 +926,4 @@ $(".input-user-name").on("input", function() {
while ($(".UsersInGroupList").height() > maxOffsetUserCont) {
$(".UsernameBlock").last().remove();
}
-})
-
-// Class to implement a temporary state and reverse it
-class TemporaryState {
- //Arrays to reverse changes
- attrChanges = []; //Attribute changes : {object(s), attribute, (old) value}
- classChanges = []; //Class changes : {object(s), state(add:true/remove:false), class}
- htmlChanges = []; //Html changes : {object(s), (old) html}
-
- /**
- * Change temporaly an attribute of an object
- * @param {Jquery Object(s)} obj HTML Object(s)
- * @param {String} attr Attribute
- * @param {String} tempVal Temporary value of the attribute
- */
- changeAttribute(obj, attr, tempVal) {
- for (let i = 0; i < obj.length; i++) {
- this.attrChanges.push({
- object: $(obj[i]),
- attribute: attr,
- value: $(obj[i]).attr(attr)
- })
- }
- obj.attr(attr, tempVal)
- }
-
- /**
- * Add/remove a class temporarily
- * @param {Jquery Object(s)} obj HTML Object
- * @param {Boolean} st Add (true) or Remove (false) the class
- * @param {String} loadclass Class Name
- */
- changeClass(obj, st, tempclass) {
- for (let i = 0; i < obj.length; i++) {
- if (!($(obj[i]).hasClass(tempclass) && st)) {
- this.classChanges.push({
- object: $(obj[i]),
- state: !st,
- class: tempclass
- })
- if (st)
- $(obj[i]).addClass(tempclass)
- else
- $(obj[i]).removeClass(tempclass)
- }
- }
- }
-
- /**
- * Add temporarily a class to the object
- * @param {Jquery Object(s)} obj
- * @param {string} tempclass
- */
- addClass(obj, tempclass) {
- this.changeClass(obj, true, tempclass);
- }
-
- /**
- * Remove temporarily a class to the object
- * @param {Jquery Object(s)} obj
- * @param {string} tempclass
- */
- removeClass(obj, tempclass) {
- this.changeClass(obj, false, tempclass);
- }
-
- /**
- * Change temporaly the html of objects (remove event handlers on the actual content)
- * @param {Jquery Object(s)} obj
- * @param {string} temphtml
- */
- changeHTML(obj, temphtml) {
- for (let i = 0; i < obj.length; i++) {
- this.htmlChanges.push({
- object:$(obj[i]),
- html:$(obj[i]).html()
- })
- }
- obj.html(temphtml);
- }
-
- /**
- * Reverse all the changes and clear the history
- */
- reverse() {
- this.attrChanges.forEach(function(change) {
- if (change.value == undefined) {
- change.object.removeAttr(change.attribute);
- } else {
- change.object.attr(change.attribute, change.value)
- }
- })
- this.classChanges.forEach(function(change) {
- if (change.state)
- change.object.addClass(change.class)
- else
- change.object.removeClass(change.class)
- })
- this.htmlChanges.forEach(function(change) {
- change.object.html(change.html);
- })
- this.attrChanges = [];
- this.classChanges = [];
- this.htmlChanges = [];
- }
-}
\ No newline at end of file
+})
\ No newline at end of file
diff --git a/admin/themes/default/js/tags.js b/admin/themes/default/js/tags.js
new file mode 100644
index 000000000..856ec352c
--- /dev/null
+++ b/admin/themes/default/js/tags.js
@@ -0,0 +1,568 @@
+//Orphan tags
+$('.tag-warning p a').on('click', () => {
+ let url = $('.tag-warning p a').data('url');
+ let tags = $('.tag-warning p a').data('tags');
+ let str_orphans = str_orphan_tags.replace('%s1', tags.length).replace('%s2', tags.join(', '));
+ $.confirm({
+ content : str_orphans,
+ title : str_delete_orphan_tags,
+ draggable: false,
+ theme: "modern",
+ animation: "zoom",
+ boxWidth: '30%',
+ useBootstrap: false,
+ type: 'red',
+ animateFromElement: false,
+ backgroundDismiss: true,
+ typeAnimated: false,
+ buttons: {
+ delete : {
+ text:str_delete_them,
+ btnClass: 'btn-red',
+ action: function() {
+ window.location.href = url.replaceAll('amp;', '');
+ }
+ },
+ keep : {
+ text:str_keep_them,
+ }
+ }
+ })
+})
+
+//Add a tag
+$('.add-tag-container').on('click', function() {
+ $('#add-tag').addClass('input-mode');
+})
+
+$('#add-tag .icon-cancel').on('click', function() {
+ $('#add-tag').removeClass('input-mode');
+})
+
+//Display/Hide tag option
+$('.tag-box').each(function() {
+ setupTagbox($(this))
+})
+
+/*-------
+ Add a tag
+-------*/
+
+$('#add-tag').submit(function (e) {
+ e.preventDefault();
+ if ($('#add-tag-input').val() != "") {
+ loadState = new TemporaryState();
+ loadState.removeClass($('#add-tag .icon-validate'),'icon-plus-circled');
+ loadState.changeHTML($('#add-tag .icon-validate') , " ")
+ loadState.changeAttribute($('#add-tag .icon-validate'), 'style','pointer-event:none')
+ addTag($('#add-tag-input').val()).then(function () {
+ showMessage(str_tag_created.replace('%s', $('#add-tag-input').val()))
+ loadState.reverse();
+ $('#add-tag-input').val("");
+ $('#add-tag').removeClass('input-mode');
+ }).catch(message => {
+ loadState.reverse();
+ showError(message)
+ })
+ }
+});
+
+$('#add-tag .icon-validate').on('click', function () {
+ if ($('#add-tag').hasClass('input-mode')) {
+ $('#add-tag').submit();
+ }
+})
+
+function addTag(name) {
+ return new Promise((resolve, reject) => {
+ jQuery.ajax({
+ url: "ws.php?format=json&method=pwg.tags.add",
+ type: "POST",
+ data: "name=" + name + "&pwg_token=" + pwg_token,
+ success: function (raw_data) {
+ data = jQuery.parseJSON(raw_data);
+ if (data.stat === "ok") {
+ newTag = createTagBox(data.result.id, name);
+ $('.tag-container').prepend(newTag);
+ setupTagbox(newTag);
+ resolve();
+ } else {
+ reject(str_already_exist.replace('%s', name));
+ }
+ },
+ error : function (err) {
+ reject(err);
+ }
+ })
+ })
+}
+
+function createTagBox(id, name) {
+ let u_edit = 'admin.php?page=batch_manager&filter=tag-'+id;
+ let u_view = 'index.php?/tags/'+id+'-'+name.toLowerCase().replace(' ', '_');
+ let html = $('.tag-template').html()
+ .replaceAll('%name%', unescape(name))
+ .replace('%U_VIEW%', u_view)
+ .replace('%U_EDIT%', u_edit);
+ newTag = $('
'+html+'
');
+ if ($("#toggleSelectionMode").is(":checked")) {
+ newTag.addClass('selection');
+ newTag.find(".in-selection-mode").show();
+ newTag.find(".not-in-selection-mode").hide();
+ }
+ return newTag;
+}
+
+/*-------
+ Setup Tag Box
+-------*/
+
+function setupTagbox(tagBox) {
+
+ let id = tagBox.data('id');
+ let name = tagBox.find('.tag-name').html();
+
+ //Dropdown options
+ tagBox.find('.showOptions').on('click', function () {
+ tagBox.find(".tag-dropdown-block").css('display', 'grid');
+ })
+
+ $(document).mouseup(function (e) {
+ e.stopPropagation();
+ let option_is_clicked = false
+ tagBox.find('.tag-dropdown-action').each(function () {
+ if (!($(this).has(e.target).length === 0)) {
+ option_is_clicked = true;
+ }
+ })
+ if (!option_is_clicked) {
+ tagBox.find(".tag-dropdown-block").hide();
+ }
+ });
+
+ tagBox.on('click', function() {
+ if (tagBox.hasClass('selection')) {
+ if (tagBox.attr('data-selected') == '1') {
+ tagBox.attr('data-selected', '0');
+ } else {
+ tagBox.attr('data-selected', '1');
+ }
+ updateListItem();
+ }
+ })
+
+ //Edit Name
+ tagBox.find('.tag-dropdown-action.edit').on('click', function() {
+ tagBox.addClass('edit-name');
+ })
+
+ tagBox.find('.tag-rename .icon-cancel').on('click', function() {
+ tagBox.removeClass('edit-name');
+ })
+
+ tagBox.find('.tag-rename .validate').on('click', function() {
+ tagBox.find('.tag-rename form').submit();
+ })
+
+ tagBox.find('.tag-rename form').submit(function (e) {
+ e.preventDefault();
+ new_name = tagBox.find('.tag-rename .tag-name-editable').val();
+ if (new_name != "") {
+ let loadState = new TemporaryState();
+ loadState.removeClass(tagBox.find('.tag-rename .validate'), 'icon-ok');
+ loadState.changeHTML(tagBox.find('.tag-rename .validate'), " ");
+ renameTag(id, new_name).then(() => {
+ showMessage(str_tag_renamed.replace('%s1', name).replace('%s2', new_name));
+ loadState.reverse();
+ tagBox.removeClass('edit-name');
+ name = new_name;
+ }).catch((message) => {
+ loadState.reverse();
+ showError(message);
+ })
+ }
+ })
+
+ //Delete Tag
+ tagBox.find('.tag-dropdown-action.delete').on('click', function () {
+ $.confirm({
+ title: str_delete.replace("%s",name),
+ buttons: {
+ confirm: {
+ text: str_yes_delete_confirmation,
+ btnClass: 'btn-red',
+ action: function () {
+ removeTag(id, name);
+ },
+ },
+ cancel: {
+ text: str_no_delete_confirmation
+ }
+ },
+ ...jConfirm_confirm_options
+ })
+ })
+
+ //Duplicate Tag
+ tagBox.find('.tag-dropdown-action.duplicate').on('click', function () {
+ duplicateTag(id, name).then((data) => {
+ showMessage(str_tag_created.replace('%s',data.result.name))
+ })
+ })
+
+}
+
+function removeTag(id, name) {
+ $.alert({
+ title : str_tag_deleted.replace("%s",name),
+ content: function() {
+ return jQuery.ajax({
+ url: "ws.php?format=json&method=pwg.tags.delete",
+ type: "POST",
+ data: "tag_id=" + id + "&pwg_token=" + pwg_token,
+ success: function (raw_data) {
+ data = jQuery.parseJSON(raw_data);
+ showMessage(str_tag_deleted.replace('%s', name));
+ if (data.stat === "ok") {
+ $('.tag-box[data-id='+id+']').remove();
+ }
+ }
+ })
+ },
+ ...jConfirm_alert_options
+ });
+}
+
+function renameTag(id, new_name) {
+ return new Promise((resolve, reject) => {
+ jQuery.ajax({
+ url: "ws.php?format=json&method=pwg.tags.rename",
+ type: "POST",
+ data: "tag_id=" + id + "&new_name=" + new_name + "&pwg_token=" + pwg_token,
+ success: function (raw_data) {
+ data = jQuery.parseJSON(raw_data);
+ if (data.stat === "ok") {
+ $('.tag-box[data-id='+id+'] p').html(data.result.name);
+ $('.tag-box[data-id='+id+'] .tag-name-editable').attr('placeholder', data.result.name);
+ resolve(data);
+ } else {
+ reject(str_already_exist.replace('%s', new_name))
+ }
+ },
+ error:function(XMLHttpRequest) {
+ reject(XMLHttpRequest.statusText);
+ }
+ })
+ })
+}
+
+function duplicateTag(id, name) {
+ return new Promise((resolve, reject) => {
+ copy_name = name + str_copy;
+
+ let name_exist = function(name) {
+ exist = false;
+ $(".tag-box .tag-name").each(function () {
+ if ($(this).html() === name)
+ exist = true
+ })
+ return exist;
+ }
+
+ let i = 1;
+ while (name_exist(copy_name))
+ {
+ copy_name = name + str_other_copy.replace("%s", i++)
+ }
+
+ jQuery.ajax({
+ url: "ws.php?format=json&method=pwg.tags.duplicate",
+ type: "POST",
+ data: "tag_id=" + id + "©_name=" + copy_name + "&pwg_token=" + pwg_token,
+ success: function (raw_data) {
+ data = jQuery.parseJSON(raw_data);
+ if (data.stat === "ok") {
+ newTag = createTagBox(data.result.id, data.result.name);
+ newTag.insertAfter($('.tag-box[data-id='+id+']'));
+ if ($('.tag-box[data-id='+id+'] .tag-dropdown-action.view').css('display') == 'inline') {
+ newTag.find('.tag-dropdown-action.view').show();
+ newTag.find('.tag-dropdown-action.manage').show();
+ }
+ setupTagbox(newTag);
+ resolve(data);
+ }
+ },
+ error:function(XMLHttpRequest) {
+ reject(XMLHttpRequest.statusText);
+ }
+ })
+ })
+}
+
+/*-------
+ Selection mode
+-------*/
+$("#toggleSelectionMode").attr("checked", false)
+$("#toggleSelectionMode").click(function () {
+ if ($(this).is(":checked")) {
+ $(".in-selection-mode").show();
+ $(".not-in-selection-mode").removeAttr('style');
+ $(".tag-box").addClass("selection");
+ $(".tag-box").removeClass('edit-name');
+ } else {
+ $(".in-selection-mode").removeAttr('style');
+ $(".not-in-selection-mode").show();
+ $(".tag-box").removeClass("selection");
+ $(".tag-box").attr("data-selected", '0');
+ updateListItem();
+ }
+});
+
+function updateListItem() {
+
+ let nowSelected = [];
+ let selected = [];
+ let shouldBeItem = [];
+ let shouldNotBeItem = [];
+ let names = {};
+ $('.tag-box[data-selected="1"]').each(function () {
+ let id = $(this).attr('data-id');
+ nowSelected.push(id);
+ names[id] = $(this).find('.tag-name').html();
+ });
+
+ $('.selection-mode-tag .tag-list div').each(function () {
+ let id = $(this).attr('data-id');
+ selected.push(id);
+ });
+
+ shouldNotBeItem = [...selected];
+ shouldNotBeItem = shouldNotBeItem.filter(x => !nowSelected.includes(x));
+ shouldBeItem = [...nowSelected];
+ shouldBeItem = shouldBeItem.filter(x => !selected.includes(x));
+ selected = nowSelected;
+
+ shouldBeItem.forEach(function(id) {
+ let newItemStructure = $('');
+ $('.selection-mode-tag .tag-list').prepend(newItemStructure);
+ $('.selection-mode-tag .tag-list div[data-id='+id+'] a').on('click', function () {
+ $('.tag-box[data-id='+id+']').attr('data-selected', '0');
+ updateListItem();
+ })
+ })
+
+ shouldNotBeItem.forEach(function(id) {
+ $('.selection-mode-tag .tag-list div[data-id='+id+']').remove();
+ })
+
+ $('#MergeOptionsChoices').html('');
+ nowSelected.forEach(id => {
+ $('#MergeOptionsChoices').append(
+ $('')
+ )
+ })
+
+ updateSelectionContent()
+}
+
+mergeOption = false;
+
+function updateSelectionContent() {
+ number = $('.tag-box[data-selected="1"]').length;
+ if (number == 0) {
+ $('#nothing-selected').show();
+ $('.selection-mode-tag').hide();
+ $('#MergeOptionsBlock').hide();
+ } else if (number == 1) {
+ mergeOption = false;
+ $('#nothing-selected').hide();
+ $('.selection-mode-tag').show();
+ $('#MergeOptionsBlock').hide();
+ $('#MergeSelectionMode').addClass('unavailable');
+ } else if (number > 1) {
+ $('#MergeSelectionMode').removeClass('unavailable');
+ if (mergeOption) {
+ $('#MergeOptionsBlock').show();
+ $('.selection-mode-tag').hide();
+ } else {
+ $('#MergeOptionsBlock').hide();
+ $('.selection-mode-tag').show();
+ }
+ }
+
+}
+
+$('#MergeSelectionMode').on('click', function() {
+ mergeOption = true;
+ updateSelectionContent()
+});
+
+$('#CancelMerge').on('click', function() {
+ mergeOption = false;
+ updateSelectionContent()
+});
+
+/*-------
+ Actions in selection mode
+-------*/
+
+//Remove tags
+$('#DeleteSelectionMode').on('click', function() {
+ names = [];
+
+ $('.tag-box[data-selected=1]').each(function() {
+ names.push($(this).find('.tag-name').html());
+ })
+
+ $.confirm({
+ title: str_delete_tags.replace("%s",names.join(', ')),
+ buttons: {
+ confirm: {
+ text: str_yes_delete_confirmation,
+ btnClass: 'btn-red',
+ action: function () {
+ removeSelectedTags();
+ }
+ },
+ cancel: {
+ text: str_no_delete_confirmation
+ }
+ },
+ ...jConfirm_confirm_options
+ });
+})
+
+function removeSelectedTags() {
+ str_id = "";
+ names = [];
+ ids = [];
+
+ $('.tag-box[data-selected=1]').each(function() {
+ id = $(this).data('id');
+ ids.push(id);
+ names.push($(this).find('.tag-name').html());
+ str_id += "tag_id[]=" + id + "&";
+ })
+
+ console.log(names);
+
+ $.alert({
+ title : str_tags_deleted.replace("%s",names.join(', ')),
+ content: function() {
+ return jQuery.ajax({
+ url: "ws.php?format=json&method=pwg.tags.delete",
+ type: "POST",
+ data: str_id + "pwg_token=" + pwg_token,
+ success: function (raw_data) {
+ data = jQuery.parseJSON(raw_data);
+ if (data.stat === "ok") {
+ ids.forEach(function(id) {
+ $('.tag-box[data-id='+id+']').remove();
+ })
+ updateListItem();
+ showMessage(str_tags_deleted.replace('%s', names.join(', ')));
+ }
+ }
+ })
+ },
+ ...jConfirm_alert_options
+ });
+}
+
+//Merge Tags
+$('.ConfirmMergeButton').on('click',() => {
+ merge_ids = [];
+ $('.tag-box[data-selected=1]').each(function() {
+ merge_ids.push($(this).data('id'))
+ })
+ dest_id = $('#MergeOptionsChoices').val();
+ mergeGroups(dest_id, merge_ids)
+})
+
+function mergeGroups(destination_id, merge_ids) {
+
+ destination_name = $('.tag-box[data-id='+destination_id+'] .tag-name').html();
+ merge_name = [];
+
+ merge_ids.forEach((id) =>{
+ merge_name.push($('.tag-box[data-id='+id+'] .tag-name').html());
+ })
+
+ str_message = str_merged_into
+ .replace('%s1', merge_name.join(', '))
+ .replace('%s2', destination_name)
+
+ $.alert({
+ title : str_message,
+ content: function() {
+ return jQuery.ajax({
+ url: "ws.php?format=json&method=pwg.tags.merge",
+ type: "POST",
+ data: "destination_tag_id=" + destination_id
+ + "&merge_tag_id[]=" + merge_ids.join('&merge_tag_id[]=')
+ + "&pwg_token=" + pwg_token,
+ success: function (raw_data) {
+ data = jQuery.parseJSON(raw_data);
+ if (data.stat === "ok") {
+ console.log()
+ data.result.deleted_tag.forEach((id) => {
+ if (data.result.destination_tag != id)
+ $('.tag-box[data-id='+id+']').remove();
+ })
+ if (data.result.images_in_merged_tag.length > 0) {
+ tagBox = $('.tag-box[data-id='+data.result.destination_tag+']')
+ tagBox.find('.tag-dropdown-action.view, .tag-dropdown-action.manage').show();
+ }
+ showMessage(str_message);
+ $(".tag-box").attr("data-selected", '0');
+ updateListItem();
+ }
+ }
+ })
+ },
+ ...jConfirm_alert_options
+ });
+}
+
+/*-------
+ Filter research
+-------*/
+
+$("#search-tag .search-input").on("input", function() {
+ let text = $(this).val().toLowerCase();
+ var searchNumber = 0;
+ $('.tag-box').each(function () {
+ if (text == "") {
+ $(this).fadeIn()
+ searchNumber++;
+ } else {
+ let name = $(this).find("p").text().toLowerCase();
+ if (name.search(text) != -1){
+ $(this).delay(300).fadeIn()
+ searchNumber++;
+ } else {
+ $(this).fadeOut()
+ }
+ }
+ })
+ if (searchNumber == 0) {
+ $('.emptyResearch').delay(300).fadeIn();
+ } else {
+ $('.emptyResearch').fadeOut();
+ }
+})
+
+/*-------
+ Show Info
+-------*/
+function showError(message) {
+ $('.tag-error p').html(message);
+ $('.tag-info').hide()
+ $('.tag-error').css('display', 'flex');
+}
+
+function showMessage(message) {
+ $('.tag-message p').html(message);
+ $('.tag-info').hide()
+ $('.tag-message').css('display', 'flex');
+}
diff --git a/admin/themes/default/template/group_list.tpl b/admin/themes/default/template/group_list.tpl
index 55e0281e4..b9bfb375f 100644
--- a/admin/themes/default/template/group_list.tpl
+++ b/admin/themes/default/template/group_list.tpl
@@ -24,6 +24,9 @@ var serverId = '{$CACHE_KEYS._hash}'
var rootUrl = '{$ROOT_URL}'
{/footer_script}
+{combine_script id='common' load='footer' path='admin/themes/default/js/common.js'}
+{combine_script id='group_list' load='footer' path='admin/themes/default/js/group_list.js'}
+
{combine_script id='jquery.selectize' load='footer' path='themes/default/js/plugins/selectize.min.js'}
{combine_css path="themes/default/js/plugins/selectize.{$themeconf.colorscheme}.css"}
@@ -33,8 +36,6 @@ var rootUrl = '{$ROOT_URL}'
{combine_css path="themes/default/js/plugins/jquery-confirm.min.css"}
{combine_css path="admin/themes/default/fontello/css/animation.css"}
-{combine_script id='common' load='footer' path='admin/themes/default/js/group_list.js'}
-
{* Define template function for the content of Groups*}
{function name=groupContent}
{function groupContent}
diff --git a/admin/themes/default/template/include/tag_selection.inc.tpl b/admin/themes/default/template/include/tag_selection.inc.tpl
deleted file mode 100644
index e84ec2431..000000000
--- a/admin/themes/default/template/include/tag_selection.inc.tpl
+++ /dev/null
@@ -1,15 +0,0 @@
-{footer_script require='jquery'}{literal}
-jQuery(document).ready(function(){
- jQuery(".tagSelection").on("click", "label", function () {
- var parent = jQuery(this).parent('li');
- var checkbox = jQuery(this).children("input[type=checkbox]");
-
- if (jQuery(checkbox).is(':checked')) {
- parent.addClass("tagSelected");
- }
- else {
- parent.removeClass('tagSelected');
- }
- });
-});
-{/literal}{/footer_script}
diff --git a/admin/themes/default/template/tags.tpl b/admin/themes/default/template/tags.tpl
index 8d22e72f0..81ca766bb 100644
--- a/admin/themes/default/template/tags.tpl
+++ b/admin/themes/default/template/tags.tpl
@@ -1,336 +1,135 @@
-{combine_script id='common' load='footer' path='admin/themes/default/js/common.js'}
-{include file='include/tag_selection.inc.tpl'}
-
-{html_style}
-.showInfo { text-indent:5px; }
-form fieldset p { margin-left:0; }
-{/html_style}
-
-{footer_script require='jquery'}
-/**
- * Add tag
- */
-jQuery("#addTag").click(function() {
- jQuery("#addTagForm").toggle();
- jQuery("input[name=add_tag]").focus();
- return false;
-});
-
-jQuery("#addTagClose").click(function() {
- jQuery("#addTagForm").hide();
- return false;
-});
-
-jQuery("#selectionMode").click(function() {
- if (jQuery(this).hasClass("icon-check-empty")) {
- jQuery("#selectionMode").removeClass("icon-check-empty").addClass("icon-check");
- jQuery('label.font-checkbox span').show();
- jQuery('ul.tagSelection a.showInfo').hide();
- jQuery('fieldset#action').show();
- jQuery('fieldset#selectTags legend').html("{'Tag selection'|translate|escape:javascript}");
- }
- else {
- jQuery("#selectionMode").removeClass("icon-check").addClass("icon-check-empty");
- jQuery('label.font-checkbox span').hide();
- jQuery('ul.tagSelection a.showInfo').show();
- jQuery('fieldset#action').hide();
- jQuery('fieldset#selectTags legend').html("{'Tags'|translate|escape:javascript}");
- }
- return false;
-});
-
-
-jQuery('.showInfo').tipTip({
- 'delay' : 0,
- 'fadeIn' : 200,
- 'fadeOut' : 200,
- 'maxWidth':'300px',
- 'keepAlive':true,
- 'activation':'click'
-});
-
-function displayDeletionWarnings() {
- jQuery(".warningDeletion").show();
- jQuery("input[name=destination_tag]:checked").parent("label").children(".warningDeletion").hide();
-}
-
-displayDeletionWarnings();
-
-jQuery("#mergeTags label").click(function() {
- displayDeletionWarnings();
-});
-
-$("#searchInput").on("keydown", function(e) {
- var $this = $(this),
- timer = $this.data("timer");
-
- if (timer) {
- clearTimeout(timer);
- }
-
- $this.data("timer", setTimeout(function() {
- var val = $this.val();
- if (!val) {
- $(".tagSelection>li").show();
- $("#filterIcon").css("visibility","hidden");
- }
- else {
- $("#filterIcon").css("visibility","visible");
- var regex = new RegExp( val.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"), "i" );
- $(".tagSelection>li").each(function() {
- var $li = $(this),
- text = $.trim( $("label", $li).text() );
- $li.toggle(regex.test(text));
- });
- }
-
- }, 300) );
-
- if (e.keyCode == 13) { // Enter
- e.preventDefault();
- }
-});
-
-jQuery('input[name="tags[]"]').click(function() {
- var nbSelected = 0;
- nbSelected = jQuery('input[name="tags[]"]').filter(':checked').length;
-
- if (nbSelected == 0) {
- jQuery("#permitAction").hide();
- jQuery("#forbidAction").show();
- }
- else {
- jQuery("#permitAction").show();
- jQuery("#forbidAction").hide();
- }
-});
-
-jQuery("[id^=action_]").hide();
-
-jQuery("select[name=selectAction]").change(function () {
- jQuery("[id^=action_]").hide();
-
- jQuery("#action_"+jQuery(this).prop("value")).show();
-
- jQuery("#displayFormBlock").hide();
- jQuery("#applyActionBlock").hide();
-
- if (jQuery(this).val() != -1 ) {
- if (jQuery(this).val() == 'delete') {
- jQuery("#applyActionBlock").show();
- jQuery("#applyAction").attr("name", jQuery(this).val());
- }
- else {
- jQuery("#displayForm").attr("name", jQuery(this).val());
- jQuery("#displayFormBlock").show();
- }
- }
- else {
- }
-});
-
-jQuery("form").submit(function() {
- if (jQuery("select[name=selectAction]").val() == "delete") {
- if (!jQuery("input[name=confirm_deletion]").is(":checked")) {
- jQuery("#action_delete .errors").show();
- return false;
- }
- }
-
- if (jQuery("select[name=selectAction]").val() == "merge") {
- if (jQuery("ul.tagSelection input[type=checkbox]:checked").length < 2) {
- alert("{'Select at least two tags for merging'|@translate}");
- return false;
- }
- }
-});
-
-jQuery("input[name=confirm_deletion]").change(function() {
- jQuery("#action_delete .errors").hide();
-});
+{footer_script}
+var pwg_token = "{$PWG_TOKEN}";
+var str_delete = '{'Delete tag "%s"?'|@translate}'
+var str_delete_tags = '{'Delete tags \{%s\}?'|@translate}'
+var str_yes_delete_confirmation = "{'Yes, delete'|@translate}"
+var str_no_delete_confirmation = "{"No, I have changed my mind"|@translate}"
+var str_tag_deleted = '{'Tag "%s" succesfully deleted'|@translate}'
+var str_tags_deleted = '{'Tags \{%s\} succesfully deleted'|@translate}'
+var str_already_exist = '{'Tag "%s" already exists'|@translate}'
+var str_tag_created = '{'Tag "%s" created'|@translate}'
+var str_tag_renamed = '{'Tag "%s1" renamed in "%s2"'|@translate}'
+var str_delete_orphan_tags = '{'Delete orphan tags ?'|@translate}'
+var str_orphan_tags = '{'You have %s1 orphan : %s2'|@translate}';
+var str_delete_them = '{'Delete them'|@translate}';
+var str_keep_them = '{'Keep them'|@translate}';
+var str_copy = '{' (copy)'|@translate}'
+var str_other_copy = '{' (copy %s)'|@translate}'
+var str_merged_into = '{'Tag(s) \{%s1\} succesfully merged into "%s2"'|@translate}'
{/footer_script}
+{combine_script id='common' load='footer' path='admin/themes/default/js/common.js'}
+{combine_script id='jquery.confirm' load='footer' require='jquery' path='themes/default/js/plugins/jquery-confirm.min.js'}
+{combine_css path="themes/default/js/plugins/jquery-confirm.min.css"}
+{combine_css path="admin/themes/default/fontello/css/animation.css"}
+{combine_script id='tiptip' load='header' path='themes/default/js/plugins/jquery.tipTip.minified.js'}
+{combine_script id='tags' load='footer' path='admin/themes/default/js/tags.js'}
+
+{function name=tagContent}
+{function tagContent}
+ {$tag_name}
+
+
+
+
+
+
+
+
+
+
+{/function}
+{/function}
{'Manage tags'|@translate}
-{if !isset($EDIT_TAGS_LIST) and !isset($DUPLIC_TAGS_LIST) and !isset($MERGE_TAGS_LIST)}
-
- {'Add a tag'|translate}
- {'Select tags'|translate}
-
+
+
+
{'Selection mode'|@translate}
+
-
+
+ {tagContent
+ tag_name='%name%'
+ tag_U_VIEW='%U_VIEW%'
+ tag_U_EDIT='%U_EDIT%'
+ has_image=false
+ }
+
\ No newline at end of file
diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css
index af1f1d71d..21a370626 100644
--- a/admin/themes/default/theme.css
+++ b/admin/themes/default/theme.css
@@ -2194,7 +2194,7 @@ input[type="text"].dError {border-color:#ff7070; background-color:#FFe5e5;}
.selection-mode-group-manager{
position:absolute;
right:15px;
- z-index:1;
+ z-index:11;
}
.switch {
@@ -2257,9 +2257,9 @@ input:checked + .slider:before {
position: absolute;
right: 0;
width: 223px;
- min-height: 584px;
- height:calc(100% - 171px);
+ min-height:calc(100% - 171px);
top: 169.5px;
+ z-index: 10;
}
.Selection-mode-content{
@@ -2304,7 +2304,7 @@ input:checked + .slider:before {
white-space: nowrap;
}
-.SelectionModeGroup button{
+#selection-mode-block button{
display:block;
margin:20px auto;
font-size: 12px;
@@ -2314,11 +2314,11 @@ input:checked + .slider:before {
width: 180px;
}
-.SelectionModeGroup button:hover{
+#selection-mode-block button:hover{
cursor: pointer;
}
-.SelectionModeGroup button.unavailable{
+#selection-mode-block button.unavailable{
opacity: 0.3;
background: none !important;
border: none !important;
@@ -2503,26 +2503,26 @@ input:checked + .slider:before {
font-size: 13px;
}
-.groupDeleteConfirm, .groupAlert {
+.jconfirmDeleteConfirm, .jconfirmAlert {
padding-bottom: 0 !important;
color: #3c3c3c !important;
line-height: 28px !important;
}
-.groupAlert {
+.jconfirmAlert {
margin-bottom: -2px !important;
}
-.groupDeleteConfirm ~ .jconfirm-content-pane, .groupAlert ~ .jconfirm-content-pane {
+.jconfirmDeleteConfirm ~ .jconfirm-content-pane, .jconfirmAlert ~ .jconfirm-content-pane {
height: 0px !important;
margin: 0px !important;
}
-.groupDeleteConfirm ~ .jconfirm-buttons button {
+.jconfirmDeleteConfirm ~ .jconfirm-buttons button {
text-transform: none !important;
}
-.groupAlert .jconfirm-icon-c i {
+.jconfirmAlert .jconfirm-icon-c i {
color: #0a0 !important;
background-color:#c2f5c2 !important;
border-radius: 20px;
@@ -2532,11 +2532,11 @@ input:checked + .slider:before {
font-size: 45px;
}
-.groupAlert .jconfirm-icon-c {
+.jconfirmAlert .jconfirm-icon-c {
margin-bottom: 25px !important;
}
-.groupAlert .jconfirm-title {
+.jconfirmAlert .jconfirm-title {
font-size: 20px !important;
}
@@ -3223,4 +3223,344 @@ li.plupload_delete a:hover {background: url("images/cancelhover.svg")!important;
}
.selectedAlbum.cat-list-album-path span {
background-color: transparent;
+}
+
+.tag-header {
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: center;
+ margin-left: 10px;
+}
+
+.tag-header #search-tag{
+ position: relative;
+ margin-left: 10px;
+}
+
+.tag-header #search-tag .search-input{
+ padding: 10px;
+ box-shadow: 0px 2px #00000024;
+ border: none;
+ background-color: #fafafa;
+ padding-left: 30px;
+ width: 300px;
+}
+
+.tag-header #search-tag span {
+ position: absolute;
+ top: 50%;
+ transform: translate(4px, -50%);
+ font-size: 18px;
+}
+
+.tag-header #add-tag {
+ position: relative;
+}
+
+.tag-header #add-tag .add-tag-container {
+ position: relative;
+ padding: 10px;
+ background-color: #fafafa;
+ padding-left: 30px;
+ box-shadow: 0px 2px #00000024;
+ border-radius: 5px;
+ font-weight: bold;
+ display: inline-flex;
+ transition: ease 0.2s;
+ cursor: pointer;
+}
+
+.tag-header #add-tag .add-tag-container:hover {
+ background-color: #f0f0f0;
+}
+
+.tag-header #add-tag.input-mode .add-tag-container {
+ width: 200px;
+ cursor: auto;
+}
+
+.tag-header #add-tag input[type=text] {
+ position: absolute;
+ left: 5%;
+ background: none;
+ width: calc(90% - 40px);
+ border: none;
+ opacity: 0;
+ display: none;
+ transition: ease 0.2s;
+}
+
+.tag-header #add-tag span{
+ z-index: 100;
+ cursor: pointer;
+}
+
+.tag-header #add-tag.input-mode input[type=text] {
+ opacity: 1;
+ display: block;
+}
+
+.tag-header #add-tag .icon-validate {
+ position: absolute;
+ top: 50%;
+ transform: translate(5px, -50%);
+ font-size: 18px;
+ position: absolute;
+ left: 0;
+ right: auto;
+ transition: transform ease 0.8s, color ease 0.2s;
+}
+
+.tag-header #add-tag.input-mode .icon-validate{
+ transform: translate(180px, -50%) rotate(360deg);
+ color: #FFA646;
+ font-size: 22px;
+ cursor: pointer;
+}
+
+.tag-header #add-tag.input-mode span:hover{
+ color: #ff7700;
+}
+
+.tag-header #add-tag p {
+ margin: 0;
+ white-space: nowrap;
+}
+
+.tag-header #add-tag.input-mode p {
+ opacity: 0;
+}
+
+.tag-header #add-tag .icon-cancel {
+ position: absolute;
+ top: 50%;
+ transform: translate(209px, -50%);
+ font-size: 18px;
+ position: absolute;
+ left: 0;
+ transition: ease 0.2s;
+ display: none;
+ opacity: 0;
+}
+
+.tag-header #add-tag.input-mode .icon-cancel {
+ display: block;
+ opacity: 1;
+}
+
+.tag-info {
+ height: 35px;
+ overflow: hidden;
+ border-radius: 20px;
+ display: flex;
+ padding: 0px 10px;
+ font-weight: bold;
+}
+
+.tag-info p {
+ margin: auto;
+ white-space: nowrap;
+}
+
+.tag-info::before {
+ line-height: 35px;
+ margin: 0px 10px;
+ font-size: 16px;
+}
+
+.tag-info.tag-warning {
+ color: #ee8800;
+ background-color:#ffdd99;
+}
+
+.tag-info.tag-message {
+ color: #0a0;
+ background-color:#c2f5c2;
+ display: none;
+}
+
+.tag-info.tag-error {
+ color: #f22;
+ background-color: #ffd5dc;
+ display: none;
+ animation-name: tag-error-appear ;
+ animation-duration: 0.4s;
+ animation-timing-function: ease;
+}
+
+@keyframes tag-error-appear {
+ 25% { transform: translateX(-10px)}
+ 50% { transform: translateX(10px)}
+ 75% { transform: translateX(-10px)}
+ 100% { transform: translateX(0px)}
+}
+
+.tag-container {
+ display: flex;
+ padding: 25px;
+ flex-flow: wrap;
+ padding-right: 223px;
+}
+
+.tag-container .tag-box{
+ display: inline-flex;
+ align-items: center;
+ padding: 10px;
+ background-color: #fafafa;
+ margin: 5px;
+ box-shadow: 0px 2px 1px #00000024;
+ border-radius: 18px;
+ position: relative;
+}
+
+.tag-container .tag-box.selection {
+ opacity: 0.6;
+ cursor: pointer;
+}
+
+.tag-container .tag-box[data-selected='1'] {
+ opacity: 1;
+}
+
+.tag-container .tag-box p {
+ white-space: nowrap;
+ margin: 0;
+}
+
+.tag-container .tag-box.edit-name p {
+ display: none;
+}
+
+.tag-container .tag-box.edit-name .showOptions {
+ display: none;
+}
+
+.tag-container .tag-box .tag-dropdown-block {
+ display:none;
+ position:absolute;
+ right: 0;
+ top: 30px;
+ background-color:white;
+ z-index:2;
+ padding:5px 0px;
+ box-shadow: 0px 0px 5px #d7d7d7;
+ border-radius: 10px;
+ transform: translateX(85%);
+}
+
+.tag-container .tag-box .tag-dropdown-block .tag-dropdown-action {
+ white-space: nowrap;
+ text-align: initial;
+ padding: 5px 10px;
+ font-size: 13px;
+}
+
+.tag-container .tag-box .tag-dropdown-block .tag-dropdown-action:hover {
+ color: #3A3A3A;
+ text-decoration: none;
+ background-color: #e7e7e7;
+}
+
+.tag-container .tag-box .select-checkbox {
+ display: none;
+ width: 18.4px;
+ height: 19px;
+ background-color: #e7e7e7;
+ border-radius: 100%;
+ margin: 2px 0px;
+ margin-left: 0px;
+ margin-left: 4px;
+}
+
+.tag-container .tag-box[data-selected='1'] .select-checkbox {
+ background-color: #ddd;
+}
+
+.tag-container .tag-box .select-checkbox i {
+ display: none;
+ font-size: 20px;
+ transform: translate(-12px , -6px);
+ position: absolute;
+ animation-name: icon-check-animation;
+ animation-duration: 0.4s;
+ animation-timing-function: ease-out;
+}
+
+.tag-container .tag-box[data-selected='1'] .select-checkbox i {
+ display: inline;
+}
+
+@keyframes icon-check-animation {
+ 0% {
+ transform: translate(-12px , -6px) scale(0);
+ }
+ 75% {
+ transform: translate(-12px , -6px) scale(1.3);
+ }
+ 100% {
+ transform: translate(-12px , -6px);
+ }
+}
+
+
+.tag-container .tag-box .tag-rename {
+ display: none;
+}
+
+.tag-container .tag-box.edit-name .tag-rename {
+ display: flex;
+}
+
+.tag-container .tag-box .tag-rename .tag-name-editable {
+ text-align: left;
+ width: 100px;
+ color: #3c3c3c;
+ font-family: "Open Sans", "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
+ margin: 0px;
+ background-color: white;
+ border: 1px solid #d5d5d5;
+}
+
+.tag-container .tag-box .tag-rename span:hover {
+ color: #ffa744;
+}
+
+
+.tag-container .tag-box .tag-rename span {
+ padding: 2px 3px;
+ margin: auto;
+ cursor: pointer;
+ color: #3A3A3A;
+}
+
+.tag-selection .tag-selection-content {
+ margin-top: 90px;
+ padding: 5px;
+}
+
+.tag-selection .tag-selection-content .selection-mode-tag{
+ display: none;
+}
+
+
+.selection-mode-tag .tag-list {
+ margin: 10px;
+ text-align: start;
+ font-weight: 700;
+ font-size: 15px;
+}
+
+.selection-mode-tag .tag-list p {
+ width: 85%;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ color: #a0a0a0;
+ margin: 0;
+}
+
+.selection-mode-tag .tag-list div {
+ display: flex;
+ margin: 10px;
+ text-align: start;
}
\ No newline at end of file
diff --git a/admin/themes/roma/theme.css b/admin/themes/roma/theme.css
index e74680716..162f81e9a 100644
--- a/admin/themes/roma/theme.css
+++ b/admin/themes/roma/theme.css
@@ -558,13 +558,13 @@ input:focus + .slider {
color:#c0c0c0;
}
-.SelectionModeGroup button{
+#selection-mode-block button{
border: 1px solid #e7e7e7;
color:#c0c0c0;
}
-.SelectionModeGroup button:hover{
+#selection-mode-block button:hover{
background-color: #ffa744;
border: 1px solid #ffa744;
color:#3c3c3c;
@@ -862,7 +862,7 @@ li.plupload_delete a:hover {background: url("images/cancelhover.svg")!important;
color: #777 !important;
}
-.groupAlert .jconfirm-icon-c i {
+.jconfirmAlert .jconfirm-icon-c i {
color:#c2f5c2 !important;
background-color:#0a0 !important;
}
diff --git a/include/ws_functions/pwg.tags.php b/include/ws_functions/pwg.tags.php
index 1f0b620bf..46328150a 100644
--- a/include/ws_functions/pwg.tags.php
+++ b/include/ws_functions/pwg.tags.php
@@ -220,14 +220,281 @@ function ws_tags_add($params, &$service)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+ if (get_pwg_token() != $params['pwg_token'])
+ {
+ return new PwgError(403, 'Invalid security token');
+ }
+
$creation_output = create_tag($params['name']);
if (isset($creation_output['error']))
{
- return new PwgError(500, $creation_output['error']);
+ return new PwgError(WS_ERR_INVALID_PARAM, $creation_output['error']);
}
+ pwg_activity('tag', $creation_output['id'], 'add');
+
return $creation_output;
}
+function ws_tags_delete($params, &$service)
+{
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+ if (get_pwg_token() != $params['pwg_token'])
+ {
+ return new PwgError(403, 'Invalid security token');
+ }
+
+$query = '
+SELECT COUNT(*)
+ FROM `'. TAGS_TABLE .'`
+ WHERE id in ('.implode(',', $params['tag_id']) .')
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count != count($params['tag_id']))
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, 'All tags does not exist.');
+ }
+
+
+ $tag_ids = $params['tag_id'];
+
+ if (count($tag_ids) > 0)
+ {
+ delete_tags($params['tag_id']);
+ return array('id' => $tag_ids);
+ foreach ($tag_ids as $ids) {
+ pwg_activity('tag', $creation_output['id'], 'delete');
+ }
+ } else {
+ return array('id' => array());
+ }
+}
+
+function ws_tags_rename($params, &$service)
+{
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+ if (get_pwg_token() != $params['pwg_token'])
+ {
+ return new PwgError(403, 'Invalid security token');
+ }
+
+ $tag_id = $params['tag_id'];
+ $tag_name = $params['new_name'];
+
+ // does the tag exist ?
+ $query = '
+SELECT COUNT(*)
+ FROM `'. TAGS_TABLE .'`
+ WHERE id = '. $tag_id .'
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count == 0)
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, 'This tag does not exist.');
+ }
+
+ $query = '
+SELECT name
+ FROM '.TAGS_TABLE.'
+ WHERE id != '.$tag_id.'
+;';
+ $existing_names = array_from_query($query, 'name');
+
+ $update = array();
+
+ if (in_array($tag_name, $existing_names))
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, 'This name is already token');
+ }
+ else if (!empty($tag_name))
+ {
+ $update = array(
+ 'name' => addslashes($tag_name),
+ 'url_name' => trigger_change('render_tag_url', $tag_name),
+ );
+
+ }
+
+ pwg_activity('tag', $tag_id, 'edit');
+
+ single_update(
+ TAGS_TABLE,
+ $update,
+ array('id' => $tag_id)
+ );
+
+ return array(
+ 'id' => $tag_id,
+ 'name' => addslashes($tag_name),
+ 'url_name' => trigger_change('render_tag_url', $tag_name)
+ );
+}
+
+
+function ws_tags_duplicate($params, &$service) {
+
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+ if (get_pwg_token() != $params['pwg_token'])
+ {
+ return new PwgError(403, 'Invalid security token');
+ }
+
+ $tag_id = $params['tag_id'];
+ $copy_name = $params['copy_name'];
+
+ // does the tag exist ?
+ $query = '
+SELECT COUNT(*)
+ FROM `'. TAGS_TABLE .'`
+ WHERE id = '. $tag_id .'
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count == 0)
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, 'This tag does not exist.');
+ }
+
+ $query = '
+SELECT COUNT(*)
+ FROM `'. TAGS_TABLE .'`
+ WHERE name = "'. $copy_name .'"
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count != 0)
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, 'This name is already taken.');
+ }
+
+
+ single_insert(
+ TAGS_TABLE,
+ array(
+ 'name' => $copy_name,
+ 'url_name' => trigger_change('render_tag_url', $copy_name),
+ )
+ );
+ $destination_tag_id = pwg_db_insert_id(TAGS_TABLE);
+
+ pwg_activity('tag', $destination_tag_id, 'add', array('action'=>'duplicate', 'source_tag'=>$tag_id));
+
+ $query = '
+SELECT image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id = '.$tag_id.'
+;';
+ $destination_tag_image_ids = array_from_query($query, 'image_id');
+
+ $inserts = array();
+
+ foreach ($destination_tag_image_ids as $image_id)
+ {
+ $inserts[] = array(
+ 'tag_id' => $destination_tag_id,
+ 'image_id' => $image_id
+ );
+ pwg_activity('image', $image_id, 'edit', array("add-tag" => $destination_tag_id));
+ }
+
+ if (count($inserts) > 0)
+ {
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array_keys($inserts[0]),
+ $inserts
+ );
+ }
+
+ return array(
+ 'id' => $destination_tag_id,
+ 'name' => $copy_name,
+ 'url_name' => trigger_change('render_tag_url', $copy_name),
+ );
+}
+
+function ws_tags_merge($params, &$service) {
+
+ if (get_pwg_token() != $params['pwg_token'])
+ {
+ return new PwgError(403, 'Invalid security token');
+ }
+
+ $all_tags = $params['merge_tag_id'];
+ array_push($all_tags, $params['destination_tag_id']);
+
+ $all_tags = array_unique($all_tags);
+ $merge_tag = array_diff($params['merge_tag_id'], array($params['destination_tag_id']));
+
+ $query = '
+SELECT COUNT(*)
+ FROM `'. TAGS_TABLE .'`
+ WHERE id in ('.implode(',', $all_tags) .')
+;';
+ list($count) = pwg_db_fetch_row(pwg_query($query));
+ if ($count != count($all_tags))
+ {
+ return new PwgError(WS_ERR_INVALID_PARAM, 'All tags does not exist.');
+ }
+
+ $image_in_merge_tags = array();
+ $image_in_dest = array();
+ $image_to_add = array();
+
+ $query = '
+SELECT DISTINCT(image_id)
+ FROM `'. IMAGE_TAG_TABLE .'`
+ WHERE
+ tag_id IN ('.implode(',', $merge_tag) .')
+;';
+ $image_in_merge_tags = query2array($query, null, 'image_id');
+
+ $query = '
+SELECT image_id
+ FROM `'. IMAGE_TAG_TABLE .'`
+ WHERE tag_id = '.$params['destination_tag_id'].'
+;';
+
+ $image_in_dest = query2array($query, null, 'image_id');;
+
+
+ $image_to_add = array_diff($image_in_merge_tags, $image_in_dest);
+
+ $inserts = array();
+ foreach ($image_to_add as $image)
+ {
+ $inserts[] = array(
+ 'tag_id' => $params['destination_tag_id'],
+ 'image_id' => $image,
+ );
+ }
+
+ mass_inserts(
+ IMAGE_TAG_TABLE,
+ array('tag_id', 'image_id'),
+ $inserts,
+ array('ignore'=>true)
+ );
+
+ pwg_activity('tag', $params['destination_tag_id'], 'edit');
+ foreach ($image_to_add as $image_id)
+ {
+ pwg_activity('image', $image_id, 'edit', array("tag-add" => $params['destination_tag_id']));
+ }
+
+ include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+ delete_tags($merge_tag);
+
+ $image_in_merged = array_merge($image_in_dest, $image_to_add);
+
+ return array(
+ "destination_tag" => $params['destination_tag_id'],
+ "deleted_tag" => $params['merge_tag_id'],
+ "images_in_merged_tag" => $image_in_merged
+ );
+}
+
?>
diff --git a/ws.php b/ws.php
index c3c9dddd7..1e35c0d23 100644
--- a/ws.php
+++ b/ws.php
@@ -640,12 +640,69 @@ function ws_addDefaultMethods( $arr )
$service->addMethod( // TODO: create multiple tags
'pwg.tags.add',
'ws_tags_add',
- array('name'),
+ array(
+ 'name' => array(),
+ 'pwg_token' => array(),
+ ),
'Adds a new tag.',
$ws_functions_root . 'pwg.tags.php',
array('admin_only'=>true)
);
+ $service->addMethod(
+ 'pwg.tags.delete',
+ 'ws_tags_delete',
+ array(
+ 'tag_id' => array('type'=>WS_TYPE_ID,
+ 'flags'=>WS_PARAM_FORCE_ARRAY),
+ 'pwg_token' => array(),
+ ),
+ 'Delete tag(s) by ID.',
+ $ws_functions_root . 'pwg.tags.php',
+ array('admin_only'=>true)
+ );
+
+ $service->addMethod(
+ 'pwg.tags.rename',
+ 'ws_tags_rename',
+ array(
+ 'tag_id' => array('type'=>WS_TYPE_ID),
+ 'new_name' => array(),
+ 'pwg_token' => array(),
+ ),
+ 'Rename tag',
+ $ws_functions_root . 'pwg.tags.php',
+ array('admin_only'=>true)
+ );
+
+ $service->addMethod(
+ 'pwg.tags.duplicate',
+ 'ws_tags_duplicate',
+ array(
+ 'tag_id' => array('type'=>WS_TYPE_ID),
+ 'copy_name' => array(),
+ 'pwg_token' => array(),
+ ),
+ 'Create a copy of a tag',
+ $ws_functions_root . 'pwg.tags.php',
+ array('admin_only'=>true, 'post_only'=>true)
+ );
+
+ $service->addMethod(
+ 'pwg.tags.merge',
+ 'ws_tags_merge',
+ array(
+ 'destination_tag_id' => array('type'=>WS_TYPE_ID,
+ 'info'=>'Is not necessarily part of groups to merge'),
+ 'merge_tag_id' => array('flags'=>WS_PARAM_FORCE_ARRAY,
+ 'type'=>WS_TYPE_ID),
+ 'pwg_token' => array(),
+ ),
+ 'Merge tags in one other group',
+ $ws_functions_root . 'pwg.tags.php',
+ array('admin_only'=>true, 'post_only'=>true)
+ );
+
$service->addMethod(
'pwg.images.exist',
'ws_images_exist',