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 = $('

'+names[id]+'

'); + $('.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}

+ +
+ + + {'Edit'|@translate} + {'Delete'|@translate} + {'Duplicate'|@translate} +
+ + + +
+
+ + +
+ + +
+{/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}

+
- + -{* plugins *} -{if !empty($tag_manage_plugin_actions)} - {foreach from=$element_set_groupe_plugins_actions item=action} -
- {if !empty($action.CONTENT)}{$action.CONTENT}{/if} -
+
+
+ + +
+ + + + + + {if $warning_tags != ""} +

{$warning_tags}

+ {/if} +

{$message_tags}

+

+
+ +
+ {foreach from=$all_tags item=tag} +
+ {tagContent + tag_name=$tag.name + tag_U_VIEW=$tag.U_VIEW + tag_U_EDIT=$tag.U_EDIT + has_image=($tag.counter > 0) + } +
{/foreach} -{/if} - +
+
{'No tag found'|@translate}
- - {* #permitAction *} - -{/if} - - + \ 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',