diff --git a/admin/batch_manager_unit.php b/admin/batch_manager_unit.php index 352f8d2f0..d69577d01 100644 --- a/admin/batch_manager_unit.php +++ b/admin/batch_manager_unit.php @@ -96,6 +96,40 @@ SELECT id, date_creation invalidate_user_cache(); } +//collection +$collection = array(); +if (isset($_POST['nb_photos_deleted'])) +{ + check_input_parameter('nb_photos_deleted', $_POST, false, '/^\d+$/'); + + // let's fake a collection (we don't know the image_ids so we use "null", we only + // care about the number of items here) + $collection = array_fill(0, $_POST['nb_photos_deleted'], null); +} +else if (isset($_POST['setSelected'])) +{ + // Here we don't use check_input_parameter because preg_match has a limit in + // the repetitive pattern. Found a limit to 3276 but may depend on memory. + // + // check_input_parameter('whole_set', $_POST, false, '/^\d+(,\d+)*$/'); + // + // Instead, let's break the input parameter into pieces and check pieces one by one. + $collection = explode(',', $_POST['whole_set']); + + foreach ($collection as $id) + { + if (!preg_match('/^\d+$/', $id)) + { + fatal_error('[Hacking attempt] the input parameter "whole_set" is not valid'); + } + } +} +else if (isset($_POST['selection'])) +{ + $collection = $_POST['selection']; +} + + // +-----------------------------------------------------------------------+ // | template init | // +-----------------------------------------------------------------------+ @@ -105,15 +139,132 @@ $template->set_filenames( $base_url = PHPWG_ROOT_PATH.'admin.php'; + $template->assign( array( + 'U_ELEMENTS_PAGE' => $base_url.get_query_string_diff(array('display','start')), - 'F_ACTION' => $base_url.get_query_string_diff(array()), 'level_options' => get_privacy_level_options(), 'ADMIN_PAGE_TITLE' => l10n('Batch Manager'), 'PWG_TOKEN' => get_pwg_token(), ) ); + //prefilter + $prefilters = array( + array('ID' => 'caddie', 'NAME' => l10n('Caddie')), + array('ID' => 'favorites', 'NAME' => l10n('Your favorites')), + array('ID' => 'last_import', 'NAME' => l10n('Last import')), + array('ID' => 'no_album', 'NAME' => l10n('With no album').' ('.l10n('Orphans').')'), + array('ID' => 'no_tag', 'NAME' => l10n('With no tag')), + array('ID' => 'duplicates', 'NAME' => l10n('Duplicates')), + array('ID' => 'all_photos', 'NAME' => l10n('All')) + ); + + if ($conf['enable_synchronization']) + { + $prefilters[] = array('ID' => 'no_virtual_album', 'NAME' => l10n('With no virtual album')); + $prefilters[] = array('ID' => 'no_sync_md5sum', 'NAME' => l10n('With no checksum')); + } + + function UC_name_compare($a, $b) + { + return strcmp(strtolower($a['NAME']), strtolower($b['NAME'])); + } + + $prefilters = trigger_change('get_batch_manager_prefilters', $prefilters); + + // Sort prefilters by localized name. + usort($prefilters, function ($a, $b) { + return strcmp(strtolower($a['NAME']), strtolower($b['NAME'])); + }); + + $template->assign( + array( + 'conf_checksum_compute_blocksize' => $conf['checksum_compute_blocksize'], + 'prefilters' => $prefilters, + 'filter' => $_SESSION['bulk_manager_filter'], + 'selection' => $collection, + 'all_elements' => $page['cat_elements_id'], + 'START' => $page['start'], + 'U_DISPLAY'=>$base_url.get_query_string_diff(array('display')), + 'F_ACTION'=>$base_url.get_query_string_diff(array('cat','start','tag','filter')), + ) + ); + + if (isset($page['no_md5sum_number'])) + { + $template->assign( + array( + 'NB_NO_MD5SUM' => $page['no_md5sum_number'], + ) + ); + } else { + $template->assign('NB_NO_MD5SUM', ''); + } + + + // privacy level + foreach ($conf['available_permission_levels'] as $level) + { + $level_options[$level] = l10n(sprintf('Level %d', $level)); + + if (0 == $level) + { + $level_options[$level] = l10n('Everybody'); + } + } + $template->assign( + array( + 'filter_level_options'=> $level_options, + 'filter_level_options_selected' => isset($_SESSION['bulk_manager_filter']['level']) + ? $_SESSION['bulk_manager_filter']['level'] + : 0, + ) + ); + + // tags + $filter_tags = array(); + + if (!empty($_SESSION['bulk_manager_filter']['tags'])) + { + $query = ' + SELECT + id, + name + FROM '.TAGS_TABLE.' + WHERE id IN ('.implode(',', $_SESSION['bulk_manager_filter']['tags']).') + ;'; + + $filter_tags = get_taglist($query); + } + + $template->assign('filter_tags', $filter_tags); + + // in the filter box, which category to select by default + $selected_category = array(); + + if (isset($_SESSION['bulk_manager_filter']['category'])) + { + $selected_category = array($_SESSION['bulk_manager_filter']['category']); + } + else + { + // we need to know the category in which the last photo was added + $query = ' + SELECT category_id + FROM '.IMAGE_CATEGORY_TABLE.' + ORDER BY image_id DESC + LIMIT 1 + ;'; + $result = pwg_query($query); + if (pwg_db_num_rows($result) > 0) + { + $row = pwg_db_fetch_assoc($result); + $selected_category[] = $row['category_id']; + } + } + + $template->assign('filter_category_selected', $selected_category); // +-----------------------------------------------------------------------+ // | global mode thumbnails | @@ -122,6 +273,8 @@ $template->assign( // how many items to display on this page if (!empty($_GET['display'])) { + // conf_update_param('batch_manager_images_per_page_unit' , intval($_GET['display'])); + // $page['nb_images'] = $conf['batch_manager_images_per_page_unit']; $page['nb_images'] = intval($_GET['display']); } elseif (in_array($conf['batch_manager_images_per_page_unit'], array(5, 10, 50))) @@ -132,7 +285,7 @@ else { $page['nb_images'] = 5; } - +$template->assign('per_page', $conf['batch_manager_images_per_page_unit']); if (count($page['cat_elements_id']) > 0) @@ -193,6 +346,20 @@ SELECT * LIMIT '.$page['nb_images'].' OFFSET '.$page['start'].' ;'; $result = pwg_query($query); + + $storage_category_id = null; + if (!empty($row['storage_category_id'])) + { + $storage_category_id = $row['storage_category_id']; + } + + $level_convert = [ + "0" => "4", + "1" => "3", + "2" => "2", + "4" => "1", + "8" => "0", +]; while ($row = pwg_db_fetch_assoc($result)) { @@ -200,6 +367,10 @@ SELECT * $src_image = new SrcImage($row); + $image_file = $row['file']; + + + $query = ' SELECT id, @@ -208,6 +379,7 @@ SELECT JOIN '.TAGS_TABLE.' AS t ON t.id = it.tag_id WHERE image_id = '.$row['id'].' ;'; + $tag_selection = get_taglist($query); $legend = render_element_name($row); @@ -216,12 +388,95 @@ SELECT $legend.= ' ('.$row['file'].')'; } $extTab = explode('.',$row['path']); + + + +// represent + + // categories + + $query = ' + SELECT category_id, uppercats, dir + FROM '.IMAGE_CATEGORY_TABLE.' AS ic + INNER JOIN '.CATEGORIES_TABLE.' AS c + ON c.id = ic.category_id + WHERE image_id = '.$row['id'].' + ;'; + + $sub_result = pwg_query($query); + $related_categories = array(); + $related_category_ids = array(); + $media['image'] = get_image_infos($row['id'], true); + while ($item = pwg_db_fetch_assoc($sub_result)) + { + $name = + get_cat_display_name_cache( + $item['uppercats'], + get_root_url().'admin.php?page=album-' + ); + + if ($item['category_id'] == $storage_category_id) + { + $template->assign('STORAGE_CATEGORY', $name); + } + + $related_categories[$item['category_id']] = array('name' => $name, 'unlinkable' => $item['category_id'] != $storage_category_id); + $related_category_ids[] = $item['category_id']; + } + + // jump to link + $image_file = $row['file']; + + $query = ' + SELECT category_id + FROM '.IMAGE_CATEGORY_TABLE.' + WHERE image_id = '.$row['id'].' + ;'; + $authorizeds = array_diff( + array_from_query($query, 'category_id'), + explode( + ',', + calculate_permissions($user['id'], $user['status']) + ) + ); + + if (isset($row['cat_id']) + and in_array($row['cat_id'], $authorizeds)) + { + $url_img = make_picture_url( + array( + 'image_id' => $row['id'], + 'image_file' => $image_file, + 'category' => $cache['cat_names'][ $row['cat_id'] ], + ) + ); + } + else + { + foreach ($authorizeds as $category) + { + $url_img = make_picture_url( + array( + 'image_id' => $row['id'], //utile ? + 'image_file' => $image_file, + 'category' => $cache['cat_names'][ $category ], + ) + ); + break; + } + } + $admin_photo_base_url = get_root_url().'admin.php?page=photo-'.$row['id']; + $admin_url_start = $admin_photo_base_url.'-properties'; + $admin_url_start.= isset($row['cat_id']) ? '&cat_id='.$row['cat_id'] : ''; + $selected_level = isset($row['level']) ? $row['level'] : $row['level']; + + $template->append( 'elements', array_merge($row, array( 'ID' => $row['id'], - 'TN_SRC' => DerivativeImage::url(IMG_THUMB, $src_image), + 'TN_SRC' => DerivativeImage::url(IMG_MEDIUM, $src_image), 'FILE_SRC' => DerivativeImage::url(IMG_LARGE, $src_image), 'LEGEND' => $legend, 'U_EDIT' => get_root_url().'admin.php?page=photo-'.$row['id'], @@ -232,14 +487,42 @@ SELECT 'DATE_CREATION' => $row['date_creation'], 'TAGS' => $tag_selection, 'is_svg' => (strtoupper(end($extTab)) == 'SVG'), + 'TITLE' => render_element_name($row), + 'DIMENSIONS' => @$row['width'].'x'.@$row['height'].' px', + 'FORMAT' => ($row['width'] >= $row['height'])? 1:0,//0:horizontal, 1:vertical + 'FILESIZE' => l10n('%.2f MB',$row['filesize']/1024), + 'REGISTRATION_DATE' => format_date($row['date_available']), + 'EXT' => l10n('%s file type',end($extTab)), + 'POST_DATE' => l10n('Posted the %s', format_date($row['date_available'], array('day', 'month', 'year'))), + 'AGE' => l10n(ucfirst(time_since($row['date_available'], 'year'))), + 'ADDED_BY' => l10n('Added by %s', $row['added_by']), + 'STATS' => l10n('Visited %d times', $row['hit']), + 'FILE' => l10n('%s', $row['file']), + 'related_categories' => $related_categories, + 'related_category_ids' => json_encode($related_category_ids,JSON_NUMERIC_CHECK), + 'U_JUMPTO' => (isset($url_img) and $user['level'] >= $media['image']['level']) ? $url_img : null, + 'tag_selection' => $tag_selection, + 'U_DOWNLOAD' => 'action.php?id='.$row['id'].'&part=e&pwg_token='.get_pwg_token().'&download', + 'U_HISTORY' => get_root_url().'admin.php?page=history&filter_image_id='.$row['id'], + 'U_DELETE' => $admin_url_start.'&delete=1&pwg_token='.get_pwg_token(), + 'U_SYNC' => $admin_url_start.'&sync_metadata=1', + 'PATH'=>$row['path'], + 'LEVEL_CONVERT' => $level_convert[!empty($row['level'])?$row['level']:'0'], + 'level_options_selected' => array($selected_level) + + ) )); } + + $template->assign('ACTIVE_PLUGINS', array_keys($pwg_loaded_plugins)); $template->assign(array( 'ELEMENT_IDS' => implode(',', $element_ids), 'CACHE_KEYS' => get_admin_client_cache_keys(array('tags')), + )); + } trigger_notify('loc_end_element_set_unit'); diff --git a/admin/themes/default/js/album_selector.js b/admin/themes/default/js/album_selector.js index 16cf5d308..1d2fbb01c 100644 --- a/admin/themes/default/js/album_selector.js +++ b/admin/themes/default/js/album_selector.js @@ -8,15 +8,20 @@ function linked_albums_close() { $("#addLinkedAlbum").fadeOut(); } -function linked_albums_open() { +function linked_albums_open(pictureId) { $("#addLinkedAlbum").fadeIn(); + if(null != pictureId) + { + console.log(pictureId); + $("#addLinkedAlbum .linkedAlbumPopInContainer").attr("id",pictureId); + } $(".search-input").val(""); $(".search-input").focus(); $("#searchResult").empty(); $(".limitReached").html(str_no_search_in_progress); } -function linked_albums_search(searchText) { +function linked_albums_search(searchText, pictureId) { if (api_method == 'pwg.categories.getList') { api_params = { @@ -32,8 +37,6 @@ function linked_albums_search(searchText) { } } - // console.log(api_method); - $(".linkedAlbumPopInContainer .searching").show(); $.ajax({ url: "ws.php?format=json&method=" + api_method, @@ -47,7 +50,11 @@ function linked_albums_search(searchText) { $(".linkedAlbumPopInContainer .searching").hide(); categories = raw_data.result.categories; - fill_results(categories); + if (typeof pictureId !== 'undefined') { + fill_results(categories, pictureId); + } else { + fill_results(categories); + } if (raw_data.result.limit_reached) { $(".limitReached").html(str_result_limit.replace("%d", categories.length)); diff --git a/admin/themes/default/js/batchManagerFilter.js b/admin/themes/default/js/batchManagerFilter.js new file mode 100644 index 000000000..bcef0ed99 --- /dev/null +++ b/admin/themes/default/js/batchManagerFilter.js @@ -0,0 +1,69 @@ +/* ********** Filters*/ +function filter_enable(filter) { + /* show the filter*/ + $("#"+filter).show(); + + /* check the checkbox to declare we use this filter */ + $("input[type=checkbox][name="+filter+"_use]").prop("checked", true); + + /* forbid to select this filter in the addFilter list */ + $("#addFilter").find("a[data-value="+filter+"]").addClass("disabled", "disabled"); + + /* hide the no filter message */ + $('.noFilter').hide(); + $('.addFilter-button').removeClass('highlight'); +} + +function filter_disable(filter) { + /* hide the filter line */ + $("#"+filter).hide(); + + /* uncheck the checkbox to declare we do not use this filter */ + $("input[name="+filter+"_use]").prop("checked", false); + + /* give the possibility to show it again */ + $("#addFilter").find("a[data-value="+filter+"]").removeClass("disabled"); + + /* show the no filter message if no filter selected */ + if ($('#filterList li:visible').length == 0) { + $('.noFilter').show(); + $('.addFilter-button').addClass('highlight'); + } + +} +$(document).ready(function () { + + $(".removeFilter").addClass("icon-cancel-circled"); + + $(".removeFilter").click(function () { + var filter = $(this).parent('li').attr("id"); + filter_disable(filter); + + return false; + }); + + $("#addFilter a").on('click', function () { + var filter = $(this).attr("data-value"); + filter_enable(filter); + }); + + $("#removeFilters").click(function() { + $("#filterList li").each(function() { + var filter = $(this).attr("id"); + filter_disable(filter); + }); + return false; + }); + + $('[data-slider=widths]').pwgDoubleSlider(sliders.widths); + $('[data-slider=heights]').pwgDoubleSlider(sliders.heights); + $('[data-slider=ratios]').pwgDoubleSlider(sliders.ratios); + $('[data-slider=filesizes]').pwgDoubleSlider(sliders.filesizes); + + $(document).mouseup(function (e) { + e.stopPropagation(); + if (!$(event.target).hasClass('addFilter-button')) { + $('.addFilter-dropdown').slideUp(); + } + }); +}) \ No newline at end of file diff --git a/admin/themes/default/js/batchManagerGlobal.js b/admin/themes/default/js/batchManagerGlobal.js index c1ef1ae98..302a8e800 100644 --- a/admin/themes/default/js/batchManagerGlobal.js +++ b/admin/themes/default/js/batchManagerGlobal.js @@ -1,73 +1,3 @@ - -/* ********** Filters*/ -function filter_enable(filter) { - /* show the filter*/ - $("#"+filter).show(); - - /* check the checkbox to declare we use this filter */ - $("input[type=checkbox][name="+filter+"_use]").prop("checked", true); - - /* forbid to select this filter in the addFilter list */ - $("#addFilter").find("a[data-value="+filter+"]").addClass("disabled", "disabled"); - - /* hide the no filter message */ - $('.noFilter').hide(); - $('.addFilter-button').removeClass('highlight'); -} - -function filter_disable(filter) { - /* hide the filter line */ - $("#"+filter).hide(); - - /* uncheck the checkbox to declare we do not use this filter */ - $("input[name="+filter+"_use]").prop("checked", false); - - /* give the possibility to show it again */ - $("#addFilter").find("a[data-value="+filter+"]").removeClass("disabled"); - - /* show the no filter message if no filter selected */ - if ($('#filterList li:visible').length == 0) { - $('.noFilter').show(); - $('.addFilter-button').addClass('highlight'); - } - -} - -$(".removeFilter").addClass("icon-cancel-circled"); - -$(".removeFilter").click(function () { - var filter = $(this).parent('li').attr("id"); - filter_disable(filter); - - return false; -}); - -$("#addFilter a").on('click', function () { - var filter = $(this).attr("data-value"); - filter_enable(filter); -}); - -$("#removeFilters").click(function() { - $("#filterList li").each(function() { - var filter = $(this).attr("id"); - filter_disable(filter); - }); - return false; -}); - -$('[data-slider=widths]').pwgDoubleSlider(sliders.widths); -$('[data-slider=heights]').pwgDoubleSlider(sliders.heights); -$('[data-slider=ratios]').pwgDoubleSlider(sliders.ratios); -$('[data-slider=filesizes]').pwgDoubleSlider(sliders.filesizes); - - -$(document).mouseup(function (e) { - e.stopPropagation(); - if (!$(event.target).hasClass('addFilter-button')) { - $('.addFilter-dropdown').slideUp(); - } -}); - /* ********** Thumbs */ /* Shift-click: select all photos between the click and the shift+click */ diff --git a/admin/themes/default/js/batchManagerUnit.js b/admin/themes/default/js/batchManagerUnit.js new file mode 100644 index 000000000..2ab2bec7c --- /dev/null +++ b/admin/themes/default/js/batchManagerUnit.js @@ -0,0 +1,556 @@ +$(document).ready(function() { + // Detect unsaved changes on any inputs + var user_interacted = false; + + $('input, textarea, select').on('focus', function() { + user_interacted = true; + }); + + $('input, textarea').on('input', function() { + var pictureId = $(this).parents("fieldset").data("image_id"); + if (user_interacted == true) { + showUnsavedLocalBadge(pictureId); + } + }); + + // Specific handler for datepicker inputs + $('input[data-datepicker]').on('change', function() { + var pictureId = $(this).parents("fieldset").data("image_id"); + if (user_interacted == true) { + showUnsavedLocalBadge(pictureId); + } + }); + + $('select').on('change', function() { + var pictureId = $(this).parents("fieldset").data("image_id"); + if (user_interacted == true) { + showUnsavedLocalBadge(pictureId); + } + }); + + $('.related-categories-container .remove-item, .datepickerDelete').on('click', function() { + user_interacted = true; + var pictureId = $(this).parents("fieldset").data("image_id"); + showUnsavedLocalBadge(pictureId); + }); + + // METADATA SYNC + $('.action-sync-metadata').on('click', function(event) { + var pictureId = $(this).parents("fieldset").data("image_id"); + $.confirm({ + title: str_meta_warning, + draggable: false, + titleClass: "metadataSyncConfirm", + theme: "modern", + content: "", + animation: "zoom", + boxWidth: '30%', + useBootstrap: false, + type: 'red', + animateFromElement: false, + backgroundDismiss: true, + typeAnimated: false, + buttons: { + confirm: { + text: str_meta_yes, + btnClass: 'btn-red', + action: function() { + disableLocalButton(pictureId); + $.ajax({ + type: 'POST', + url: 'ws.php?format=json', + data: { + method: "pwg.images.syncMetadata", + pwg_token: jQuery("input[name=pwg_token]").val(), + image_id: pictureId + }, + dataType: 'json', + success: function(data) { + var isOk = data.stat && data.stat === "ok"; + if (isOk) { + updateBlock(pictureId); + } else { + showErrorLocalBadge(pictureId); + enableLocalButton(pictureId); + } + }, + error: function(data) { + console.error("Error occurred"); + enableLocalButton(pictureId); + } + }); + } + }, + cancel: { + text: str_meta_no + } + } + }); + }); + // DELETE + $('.action-delete-picture').on('click', function(event) { + var $fieldset = $(this).parents("fieldset"); + var pictureId = $fieldset.data("image_id"); + $.confirm({ + title: str_are_you_sure, + draggable: false, + titleClass: "groupDeleteConfirm", + theme: "modern", + content: "", + animation: "zoom", + boxWidth: '30%', + useBootstrap: false, + type: 'red', + animateFromElement: false, + backgroundDismiss: true, + typeAnimated: false, + buttons: { + confirm: { + text: str_yes, + btnClass: 'btn-red', + action: function() { + var image_ids = [pictureId]; + (function(ids) { + $.ajax({ + type: 'POST', + url: 'ws.php?format=json', + data: { + method: "pwg.images.delete", + pwg_token: jQuery("input[name=pwg_token]").val(), + image_id: ids.join(',') + }, + dataType: 'json', + success: function(data) { + var isOk = data.stat && data.stat === "ok"; + if (isOk) { + $fieldset.remove(); + $('.pagination-container').css({ + 'pointer-events': 'none', + 'opacity': '0.5' + }); + $('.button-reload').css('display', 'block'); + $('div[data-image_id="' + pictureId + '"]').css('display', 'flex'); + } else { + showErrorLocalBadge(pictureId); + } + }, + error: function(data) { + console.error("Error occurred"); + } + }); + })(image_ids); + image_ids = []; + } + }, + cancel: { + text: str_no + } + } + }); + }); + // VALIDATION + //Unit Save + $('.action-save-picture').on('click', function(event) { + var $fieldset = $(this).parents("fieldset"); + var pictureId = $fieldset.data("image_id"); + saveChanges(pictureId); + }); + //Global Save + $('.action-save-global').on('click', function(event) { + saveAllChanges(); + }); + //Categories + $(".linked-albums.add-item").on("click", function() { + var pictureId = $(this).parents("fieldset").data("image_id") + linked_albums_open(pictureId); + set_up_popin(); + }); + $(".limitReached").html(str_no_search_in_progress); + $(".search-cancel-linked-album").hide(); + $(".linkedAlbumPopInContainer .searching").hide(); + $("#linkedAlbumSearch .search-input").on('input', function() { + var pictureId = $("#linkedAlbumSearch .search-input").parents(".linkedAlbumPopInContainer").attr("id"); + if ($(this).val() != 0) { + $("#linkedAlbumSearch .search-cancel-linked-album").show() + } else { + $("#linkedAlbumSearch .search-cancel-linked-album").hide(); + } + // Search input value length required to start searching + if ($(this).val().length > 0) { + linked_albums_search($(this).val(), pictureId); + } else { + $(".limitReached").html(str_no_search_in_progress); + $("#searchResult").empty(); + } + }) + $(".search-cancel-linked-album").on("click", function() { + $("#linkedAlbumSearch .search-input").val(""); + $("#linkedAlbumSearch .search-input").trigger("input"); + }) + $(".related-categories-container .breadcrumb-item .remove-item").on("click", function() { + var pictureId = $(this).parents("fieldset").data("image_id") + remove_related_category($(this).attr("id"), pictureId); + }) + pluginFunctionMapInit(activePlugins); +}) + +function fill_results(cats, pictureId) { + $("#searchResult").empty(); + cats.forEach(cat => { + $("#searchResult").append( + "