From 57042cc475712736ccdb8707e47aea67aa217065 Mon Sep 17 00:00:00 2001 From: Linty Date: Tue, 26 Aug 2025 14:35:31 +0200 Subject: [PATCH] fixes #2364 redesign admin comments management Replaces legacy PHP comment management with a new interface for listing, filtering, selecting, validating, and deleting user comments. Updates templates and CSS for a modern, interactive experience, adds advanced filters, selection mode, and modal comment viewing. Removes obsolete server-side logic from comments.php and introduces new api methods for comment actions. --- admin/comments.php | 229 +------ admin/themes/clear/theme.css | 9 +- admin/themes/default/js/comments.js | 590 ++++++++++++++++ admin/themes/default/template/comments.tpl | 583 ++++++---------- admin/themes/default/theme.css | 738 ++++++++++++--------- admin/themes/roma/theme.css | 133 ++-- include/ws_functions/pwg.comments.php | 243 +++++++ language/en_UK/admin.lang.php | 3 + language/fr_FR/admin.lang.php | 3 + ws.php | 79 +++ 10 files changed, 1606 insertions(+), 1004 deletions(-) create mode 100644 admin/themes/default/js/comments.js create mode 100644 include/ws_functions/pwg.comments.php diff --git a/admin/comments.php b/admin/comments.php index cb2f4a7d2..133b545de 100644 --- a/admin/comments.php +++ b/admin/comments.php @@ -13,70 +13,12 @@ if (!defined('PHPWG_ROOT_PATH')) include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); -if (isset($_GET['start']) and is_numeric($_GET['start'])) -{ - $page['start'] = $_GET['start']; -} -else -{ - $page['start'] = 0; -} - // +-----------------------------------------------------------------------+ // | Check Access and exit when user status is not ok | // +-----------------------------------------------------------------------+ check_status(ACCESS_ADMINISTRATOR); -// +-----------------------------------------------------------------------+ -// | actions | -// +-----------------------------------------------------------------------+ - -if (!empty($_POST)) -{ - if (empty($_POST['comments'])) - { - $template->assign( - array( - 'save_warning' => l10n('Select at least one comment') - ) - ); - } - else - { - include_once( PHPWG_ROOT_PATH .'include/functions_comment.inc.php' ); - check_input_parameter('comments', $_POST, true, PATTERN_ID); - - if (isset($_POST['validate'])) - { - validate_user_comment($_POST['comments']); - - $template->assign( - array( - 'save_success' => l10n_dec( - '%d user comment validated', '%d user comments validated', - count($_POST['comments']) - ) - ) - ); - } - - if (isset($_POST['reject'])) - { - delete_user_comment($_POST['comments']); - - $template->assign( - array( - 'save_error' => l10n_dec( - '%d user comment rejected', '%d user comments rejected', - count($_POST['comments']) - ) - ) - ); - } - } -} - // +-----------------------------------------------------------------------+ // | template init | // +-----------------------------------------------------------------------+ @@ -85,7 +27,8 @@ $template->set_filenames(array('comments'=>'comments.tpl')); $template->assign( array( - 'F_ACTION' => get_root_url().'admin.php?page=comments' + 'F_ACTION' => get_root_url().'admin.php?page=comments', + 'PWG_TOKEN' => get_pwg_token(), ) ); @@ -102,174 +45,6 @@ $tabsheet->set_id('comments'); $tabsheet->select(''); $tabsheet->assign(); -// +-----------------------------------------------------------------------+ -// | comments display | -// +-----------------------------------------------------------------------+ - -$nb_total = 0; -$nb_pending = 0; -$nb_validated = 0; - -$query = ' -SELECT - COUNT(*) AS counter, - validated - FROM '.COMMENTS_TABLE.' - GROUP BY validated -;'; -$result = pwg_query($query); -while ($row = pwg_db_fetch_assoc($result)) -{ - $nb_total+= $row['counter']; - - if ('false' == $row['validated']) - { - $nb_pending = $row['counter']; - } -} - -$nb_validated = $nb_total - $nb_pending; - -if (!isset($_GET['filter']) and $nb_pending > 0) -{ - $page['filter'] = 'pending'; -} -else -{ - $page['filter'] = 'all'; -} - -if (isset($_GET['filter']) and ('pending' == $_GET['filter'] or 'validated' == $_GET['filter'])) -{ - $page['filter'] = $_GET['filter']; -} - -if (isset($_GET['status'])) -{ - $displayed_status = $_GET['status']; -} -else -{ - $displayed_status = 'all'; -} - -if (isset($_GET['author'])) -{ - $author = $_GET['author']; -} -else -{ - $author = 'all'; -} - -// by default, no filter by date is active -$start = $end = ""; - -if (isset($_GET['start_date'])){ - $start = $_GET['start_date']; -} - -if (isset($_GET['end_date'])){ - $end = $_GET['end_date']; -} - -$template->assign( - array( - 'nb_total' => $nb_total, - 'nb_pending' => $nb_pending, - 'nb_validated' => $nb_validated, - 'filter' => $page['filter'], - 'displayed_status' => $displayed_status, - 'displayed_author' => $author, - 'START' => $start, - 'END' => $end, - ) - ); - -$where_clauses = array('1=1'); - -if ('pending' == $page['filter']) -{ - $where_clauses[] = 'validated=\'false\''; -} -if ('validated' == $page['filter']) -{ - $where_clauses[] = 'validated=\'true\''; -} - -$query = ' -SELECT - c.id, - c.image_id, - c.date, - c.author, - '.$conf['user_fields']['username'].' AS username, - ui.status, - c.content, - i.path, - i.representative_ext, - validated, - c.anonymous_id - FROM '.COMMENTS_TABLE.' AS c - INNER JOIN '.IMAGES_TABLE.' AS i - ON i.id = c.image_id - LEFT JOIN '.USERS_TABLE.' AS u - ON u.'.$conf['user_fields']['id'].' = c.author_id - LEFT JOIN '.USER_INFOS_TABLE.' AS ui - ON ui.user_id = c.author_id - WHERE '.implode(' AND ', $where_clauses).' - ORDER BY c.date DESC - LIMIT '.$page['start'].', '.$conf['comments_page_nb_comments'].' -;'; -$result = pwg_query($query); -while ($row = pwg_db_fetch_assoc($result)) -{ - $thumb = DerivativeImage::thumb_url( - array( - 'id'=>$row['image_id'], - 'path'=>$row['path'], - 'representative_ext'=>$row['representative_ext'], - ) - ); - if (empty($row['author_id'])) - { - $author_name = $row['author']; - } - else - { - $author_name = stripslashes($row['username']); - } - $template->append( - 'comments', - array( - 'U_PICTURE' => get_root_url().'admin.php?page=photo-'.$row['image_id'], - 'ID' => $row['id'], - 'TN_SRC' => $thumb, - 'AUTHOR' => trigger_change('render_comment_author', $author_name), - 'AUTHOR_STATUS' => $row['status'], - 'DATE' => format_date($row['date'], array('day_name','day','month','year','time')), - 'CONTENT' => trigger_change('render_comment_content',$row['content']), - 'IS_PENDING' => ('false' == $row['validated']), - 'IP' => $row['anonymous_id'], - 'NUMERICAL_DATE' => $row['date'], - ) - ); - - $list[] = $row['id']; -} - -// +-----------------------------------------------------------------------+ -// | navigation bar | -// +-----------------------------------------------------------------------+ - -$navbar = create_navigation_bar( - get_root_url().'admin.php'.get_query_string_diff(array('start')), - ('pending' == $page['filter'] ? $nb_pending : $nb_total), - $page['start'], - $conf['comments_page_nb_comments'] - ); - -$template->assign('navbar', $navbar); $template->assign('ADMIN_PAGE_TITLE', l10n('User comments')); // +-----------------------------------------------------------------------+ diff --git a/admin/themes/clear/theme.css b/admin/themes/clear/theme.css index ed5b8b726..e7a4f494a 100644 --- a/admin/themes/clear/theme.css +++ b/admin/themes/clear/theme.css @@ -481,7 +481,8 @@ input:focus + .slider { box-shadow: 0 0 1px #ffa646; } -#selection-mode-block{ +#selection-mode-block, +.selection-mode { background-color: #FAFAFA; border-left:1px solid #e6e6e6; } @@ -505,12 +506,14 @@ input:focus + .slider { color:#a0a0a0; } -#selection-mode-block button{ +#selection-mode-block button, +#commentsSelection button{ border: 1px solid #e7e7e7; } -#selection-mode-block button:hover{ +#selection-mode-block button:hover, +#commentsSelection button:hover { background-color: #ffa744; border: 1px solid #ffa744; } diff --git a/admin/themes/default/js/comments.js b/admin/themes/default/js/comments.js new file mode 100644 index 000000000..f7254c1bb --- /dev/null +++ b/admin/themes/default/js/comments.js @@ -0,0 +1,590 @@ +const commentsContainer = $('#comments'); +const advancedFilters = $('#advancedFilters'); +const switchMode = $('#toggleSelectionMode'); +const commentContainer = $('#commentContainer'); +const commentsAll = $('#commentsAll'); +const commentsValidated = $('#commentsValidated'); +const commentsPending = $('#commentsPending'); +const commentsList = $('#commentsList'); +const commentsNb = $('#commentsNb a'); +const filterAuthor = $('#filter_author'); +const filterDateStart = $('#filter_date_start'); +const filterDateEnd = $('#filter_date_end'); +const commentsSelectController = $('#commentsSelectController'); +const tabFilters = $('#tabFilters'); +const commentsSelectedArea = $('#commentsSelected'); +const commentsSelectedOthers = $('#commentsSelectedOthers'); +const modalViewComment = $('#modalViewComment'); + +const commentsPaginElipsis = '...'; +const commentsPaginItems = '%d'; +const commentsPaginItemsCurrent = '%d'; +const commentsOptionsFiltersAuthor = ''; +const commentsSelectedList = '

#%d

'; + +let commentsState = {}; +let commentsParams = { + status: 'all', + page: 0, + per_page: 5, +} + +let updateAuthorId = true; +let searchTimeOut = null; +let selectionMode = false; +let commentsSelected = []; + +$(function() { + $('#commentFilters').on('click', function() { + $(this).toggleClass('advanced-filter-open'); + advancedFilters.toggle(); + }); + + switchMode.on('change', function() { + $('#contentSelectMode').toggle(); + $('#headerSelectMode, #contentSelectMode').toggleClass('selection-mode'); + commentContainer.toggleClass('active'); + + if (!commentContainer.hasClass('active')) { + selectionMode = false; + $('.comment-select-checkbox').hide(); + + $('.comment-buttons').show(); + commentsSelectController.removeClass('show'); + tabFilters.show(); + commentsUnselectAll(); + } else { + selectionMode = true; + $('.comment-select-checkbox').show(); + + $('.comment-buttons').hide(); + tabFilters.hide(); + commentsSelectController.addClass('show'); + } + }); + + $('#selectAll').on('click', function() { + commentsSelectAll(); + }); + + $('#selectNone').on('click', function() { + commentsUnselectAll(); + }); + + $('#selectInvert').on('click', function() { + commentsInvertSelect(); + }); + + $('.tab-filters input').on('change', function() { + commentsParams.status = $(this).attr('data-status'); + commentsParams.page = 0; + getComments(commentsParams); + }); + + commentsNb.on('click', function() { + const nb = $(this).text(); + updateNbComments(nb); + commentsParams.page = 0; + getComments(commentsParams); + }); + + $('#closeModalViewComment').on('click', function() { + closeModalViewComment(); + }); + + $('#commentSearchInput').on('input', function() { + clearTimeout(searchTimeOut); + searchTimeOut = setTimeout(() => { + const search = $(this).val(); + + delete commentsParams.author_id; + delete commentsParams.f_min_date; + delete commentsParams.f_max_date; + + commentsParams.search = search; + getComments(commentsParams); + }, 300); + }); + + $('#commentsResetFilters').on('click', function() { + commentsClearFilters(); + }); + + // get comments and set display + commentsParams.per_page = window.localStorage.getItem('adminCommentsNB') ?? 5 + updateNbComments(commentsParams.per_page); + getComments(commentsParams); +}); + + +function getComments(params) { + $.ajax({ + url: 'ws.php?format=json&method=pwg.userComments.getList', + type: 'GET', + dataType: 'json', + data: params, + success: (data) => { + if (data.stat === 'ok') { + // for debug + // console.log(data.result); + commentsState = {...data.result}; + commentsDisplaySummary(data.result.summary); + displayComments(data.result.comments); + commentsDiplayPagination(data.result.paging); + commentsDisplayFilters(data.result.filters); + + delete commentsParams.search; + } + }, + error: (e) => { + console.log(e); + $.alert({ title: str_an_error_has, content: "" , ...jConfirm_warning_options}); + } + }) +} + +function commentsDisplaySummary(summary) { + commentsAll.text(summary.all_comments); + commentsValidated.text(summary.validated); + commentsPending.text(summary.pending); +} + +function displayComments(comments) { + commentsList.empty(); + comments.forEach((comment) => { + const clone = $('.comment-template').clone(); + clone.removeClass('comment-template').addClass('comment'); + + clone.attr('id', comment.id); + clone.find('.comment-img').attr('src', comment.medium_url); + const raw_lenght = comment.raw_content.length; + const preview = raw_lenght > 50 ? comment.raw_content.substring(0, 50) + '...' : comment.raw_content; + clone.find('.comment-msg').text('"' + preview + '"'); + clone.find('.comment-author-name').text(comment.author); + clone.find('.comment-datetime').text(comment.date); + clone.find('.comment-delete').data('idx', comment.id); + clone.find('.comment-validate').data('idx', comment.id); + clone.find('.comment-content').data('idx', comment.id); + clone.find('.comment-hash').text(`#${comment.id}`); + clone.find('.comment-select-checkbox').val(comment.id); + clone.find('.comment-link').attr('href', comment.admin_link); + const authorIcons = clone.find('.comment-author-icon'); + + switch (comment.author_status) { + case "guest": + authorIcons.addClass('icon-user-secret icon-yellow'); + break; + + case "webmaster": + authorIcons.addClass('icon-user icon-purple'); + break; + + case "admin": + authorIcons.addClass('icon-user icon-green'); + break; + + case "main_user": + authorIcons.addClass('icon-king icon-blue'); + break; + + default: + authorIcons.addClass('icon-user icon-yellow'); + break; + } + + if (comment.is_pending) { + clone.find('.comment-validate').show(); + } else { + clone.find('.comment-container').addClass('comment-validated'); + } + + commentsList.append(clone); + }); + + $('.comment-delete').off('click').on('click', function(e) { + e.stopPropagation(); + const id = $(this).data('idx'); + deleteComment([id]); + }); + + $('.comment-validate').off('click').on('click', function(e) { + e.stopPropagation(); + const id = $(this).data('idx'); + validateComment([id]); + }); + + $('.comment-content').off('click').on('click', function() { + const id = $(this).data('idx'); + if (selectionMode) { + const checkbox = $(this).find('.comment-select-checkbox'); + + if (checkbox.hasClass('icon-circle-empty')) { + checkbox.removeClass('icon-circle-empty').addClass('icon-ok-circled'); + $(`#${id}`).addClass('comment-selected'); + commentsSelected.push(id); + + } else { + checkbox.removeClass('icon-ok-circled').addClass('icon-circle-empty'); + $(`#${id}`).removeClass('comment-selected'); + + commentsSelected = commentsSelected.filter((idx) => idx != id); + } + + commentsUpdateSelection(); + return; + } + + showModalViewComment(id); + }); +} + +function commentsDiplayPagination(paging) { + const container = $('.pagination-item-container'); + container.empty(); + + if (paging.total_pages == 0) { + const pageNumbers = paging.total_pages + 1; + const page = commentsPaginItems.replace(/%d/g, pageNumbers); + $(page).addClass('actual').appendTo(container); + + } else if (paging.total_pages <= 2) { + Array.from(Array(paging.total_pages + 1)).forEach((_, i) => { + const page = commentsPaginItems.replace(/%d/g, i + 1); + $(page).appendTo(container); + }); + $(`#comments_page_${paging.page + 1}`).addClass('actual'); + + } else { + const pageOne = commentsPaginItems.replace(/%d/g, 1); + const pageLast = commentsPaginItems.replace(/%d/g, paging.total_pages + 1); + const pageCurrent = commentsPaginItemsCurrent.replace(/%d/g, paging.page + 1); + + switch (paging.page) { + case 0: + container.append([ + pageCurrent, + commentsPaginElipsis, + pageLast + ]); + break; + + case paging.total_pages: + container.append([ + pageOne, + commentsPaginElipsis, + pageCurrent + ]); + break; + + default: + container.append([ + pageOne, + commentsPaginElipsis, + pageCurrent, + commentsPaginElipsis, + pageLast + ]); + break; + } + + $('.pagination-arrow').removeClass('unavailable') + .off('click').on('click', function() { + let newPage = commentsParams.page; + if ($(this).hasClass('left')) { + newPage = newPage - 1; + } else { + newPage = newPage + 1; + } + + if (newPage == -1 || newPage > commentsState.paging.total_pages) { + return; + } + commentsParams.page = newPage; + getComments(commentsParams); + }); + } + + $('.comments-paging').off('click').on('click', function() { + const newPage = $(this).attr('data-page') - 1; + commentsParams.page = newPage; + getComments(commentsParams); + }); + + +} + +function commentsDisplayFilters(filters) { + if (updateAuthorId) { + commentsDisplayAuthors(filters.nb_authors); + } + // reset here to let decide filterAuthor onChange + updateAuthorId = true; + + const minDate = filters.started_at.split(' ')[0] ?? ''; + const maxDate = filters.ended_at.split(' ')[0] ?? '' + filterDateStart.val(minDate).attr({ 'min': minDate, 'max': maxDate }); + filterDateEnd.val(maxDate).attr({ 'max': maxDate, 'min': minDate }); + + + filterDateStart.off('change').on('change', function() { + const min = $(this).val(); + + if (!min) { + delete commentsParams.f_min_date; + } else { + commentsParams.f_min_date = min; + } + + filterDateEnd.attr({ 'min': min }); + commentsParams.page = 0; + getComments(commentsParams); + }); + + filterDateEnd.off('change').on('change', function() { + const max = $(this).val(); + + if (!max) { + delete commentsParams.f_max_date; + } else { + commentsParams.f_max_date = max; + } + + filterDateStart.attr({ 'max': max }); + commentsParams.page = 0; + getComments(commentsParams); + }); +} + +function commentsDisplayAuthors(nb_authors) { + filterAuthor.empty(); + filterAuthor.append(commentsOptionsFiltersAuthor); + + nb_authors.forEach((a) => { + filterAuthor.append(` + + `); + }); + + filterAuthor.off('change').on('change', function() { + const authorId = $(this).val(); + + if (!authorId) { + delete commentsParams.author_id; + } else { + commentsParams.author_id = authorId; + } + + commentsParams.page = 0; + updateAuthorId = false; + getComments(commentsParams); + }); +} + +function updateNbComments(nb) { + commentsNb.removeClass('selected-pagination'); + $(`#pagination-per-page-${nb}`).addClass('selected-pagination'); + + commentsParams.per_page = nb; + window.localStorage.setItem('adminCommentsNB', nb); +} + +function showModalViewComment(id) { + const comment = commentsState.comments.filter((c) => c.id == id)[0] ?? null; + if (!comment) return; + + const item = $(`#${id}`); + modalViewComment.find('.comment-datetime').text(comment.date); + modalViewComment.find('.comment-author').remove(); + modalViewComment.find('.comments-modal-infos').prepend(item.find('.comment-author').clone()); + modalViewComment.find('.comments-modal-img').attr('src', comment.medium_url); + modalViewComment.find('.comments-modal-img-i').empty() + .append(` +

${comment.file}

+

${comment.image_date_available}

+ `); + modalViewComment.find('.comments-modal-body').html(comment.content) + + modalViewComment.fadeIn(); +} + +function closeModalViewComment() { + modalViewComment.fadeOut(); +} + +function validateComment(id) { + $.ajax({ + url: 'ws.php?format=json&method=pwg.userComments.validate', + type: 'POST', + dataType: 'json', + data: { + comment_id: id, + pwg_token: pwg_token + }, + success: function (res) { + if (res.stat === 'ok') { + $.alert({ + ...{ + title: str_comment_validated, + content: "", + }, + ...jConfirm_alert_options + }); + getComments(commentsParams); + return; + } + $.alert({ + ...{ + title: str_an_error_has, + content: "", + }, + ...jConfirm_warning_options + }); + }, + error: function (e) { + console.log(e) + $.alert({ + ...{ + title: str_an_error_has, + content: "", + }, + ...jConfirm_warning_options + }); + } + }); +} + +function deleteComment(id) { + $.confirm({ + title: str_delete.replace("%s", id), + draggable: false, + titleClass: "jconfirmDeleteConfirm", + theme: "modern", + content: "", + animation: "zoom", + boxWidth: '30%', + useBootstrap: false, + type: 'red', + animateFromElement: false, + backgroundDismiss: true, + typeAnimated: false, + buttons: { + confirm: { + text: str_yes_delete_confirmation, + btnClass: 'btn-red', + action: function () { + $.ajax({ + url: 'ws.php?format=json&method=pwg.userComments.delete', + type: 'POST', + dataType: 'json', + data: { + comment_id: id, + pwg_token + }, + success: function(res) { + if (res.stat === 'ok') { + getComments(commentsParams); + } + }, + error: function(e) { + console.log(e) + } + }) + } + }, + cancel: { + text: str_no_delete_confirmation + } + } + }); +} + +function commentsUnselectAll() { + $('.comment').removeClass('comment-selected'); + $('.comment-select-checkbox') + .removeClass('icon-ok-circled') + .addClass('icon-circle-empty'); + + commentsSelected = []; + commentsUpdateSelection(); +} + +function commentsSelectAll(){ + $('.comment').addClass('comment-selected'); + $('.comment-select-checkbox') + .removeClass('icon-circle-empty') + .addClass('icon-ok-circled'); + + commentsSelected = []; + $('.comment-selected').each((i, el) => { + const id = $(el).attr('id'); + commentsSelected.push(id); + }); + commentsUpdateSelection(); +} + +function commentsInvertSelect() { + $('.comment').toggleClass('comment-selected'); + $('.comment-select-checkbox') + .toggleClass('icon-ok-circled') + .toggleClass('icon-circle-empty'); + + commentsSelected = []; + $('.comment-selected').each((i, el) => { + const id = $(el).attr('id'); + commentsSelected.push(id); + }); + commentsUpdateSelection(); +} + +function commentsUpdateSelection() { + if (commentsSelected.length === 0) { + $('#commentsSelection').hide(); + $('#commentsNoSelection').show(); + $('.comments-selected-remove').off('click'); + $('#ValisateSelectionMode').off('click'); + $('#DeleteSelectionMode').off('click'); + + return; + } + + commentsSelectedArea.empty(); + let count = 0; + commentsSelected.forEach((id) => { + if (count === 5) { + commentsSelectedOthers.text(str_and_others.replace(/%s/g, commentsSelected.length - 5)); + return; + } + commentsSelectedOthers.text(''); + const item = commentsSelectedList.replace(/%d/g, id); + commentsSelectedArea.append(item); + count++ + }); + + $('.comments-selected-remove').off('click').on('click', function() { + const id = $(this).attr('id').split('_')[1]; + if (!id) return; + $(`#${id} .comment-content`).trigger('click'); + }); + + $('#ValisateSelectionMode').off('click').on('click', function() { + validateComment(commentsSelected); + commentsUnselectAll(); + }); + + $('#DeleteSelectionMode').off('click').on('click', function() { + deleteComment(commentsSelected); + commentsUnselectAll(); + }); + + $('#commentsNoSelection').hide(); + $('#commentsSelection').show(); +} + +function commentsClearFilters() { + delete commentsParams.author_id; + delete commentsParams.image_id; + delete commentsParams.search; + delete commentsParams.f_min_date; + delete commentsParams.f_max_date; + getComments(commentsParams); +} \ No newline at end of file diff --git a/admin/themes/default/template/comments.tpl b/admin/themes/default/template/comments.tpl index 8ff26389b..de680e204 100644 --- a/admin/themes/default/template/comments.tpl +++ b/admin/themes/default/template/comments.tpl @@ -1,422 +1,209 @@ -{combine_script id='jquery.ui.slider' require='jquery.ui' load='header' path='themes/default/js/ui/minified/jquery.ui.slider.min.js'} -{combine_css path="themes/default/js/ui/theme/jquery.ui.slider.css"} - +{combine_script id="comments" load="footer" path="admin/themes/default/js/comments.js"} +{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"} {footer_script} -jQuery(document).ready(function(){ - $("h1").append(""+{$nb_total}+""); - - function highlighComments() { - jQuery(".comment").each(function() { - var parent = jQuery(this).parent('tr'); - if (jQuery(this).children("input[type=checkbox]").is(':checked')) { - jQuery(parent).addClass('selectedComment'); - } - else { - jQuery(parent).removeClass('selectedComment'); - } - }); - } - - if ("{$filter}" == "pending"){ - $("#seeWaiting").prop('checked', true); - } - if ("{$filter}" == "validated"){ - $("#seeValidated").prop('checked', true); - } - - $("#seeAll").on("change", function(){ - if ($("#seeAll").prop('checked') == true){ - window.location.replace("{$F_ACTION}&filter=all&status={$displayed_status}&author={$displayed_author}&start_date={$START}&end_date={$END}"); - } - }); - - $("#seeWaiting").on("change", function(){ - if ($("#seeWaiting").prop('checked') == true){ - window.location.replace("{$F_ACTION}&filter=pending&status={$displayed_status}&author={$displayed_author}&start_date={$START}&end_date={$END}"); - } - }); - - $("#seeValidated").on("change", function(){ - if ($("#seeValidated").prop('checked') == true){ - window.location.replace("{$F_ACTION}&filter=validated&status={$displayed_status}&author={$displayed_author}&start_date={$START}&end_date={$END}"); - } - }); - - $("#status_filter").on("change", function(){ - let location = "{$F_ACTION}&filter={$filter}&status=" + $("#status_filter").find(":selected").val().toString() + "&author={$displayed_author}&start_date={$START}&end_date={$END}"; - window.location.replace(location); - }); - - $("#status_filter").val("{$displayed_status}"); - - $("#author_filter").on("change", function(){ - let location = "{$F_ACTION}&filter={$filter}&status={$displayed_status}&author=" + $("#author_filter").find(":selected").val().toString() + "&start_date={$START}&end_date={$END}"; - window.location.replace(location); - }); - - $("#author_filter").val("{$displayed_author}"); - - $("#start_unset").on("click", function(){ - $("#start_date").val(""); - let location = "{$F_ACTION}&filter={$filter}&status={$displayed_status}&author={$displayed_author}&start_date=&end_date={$END}"; - window.location.replace(location); - }); - - $("#start_date").on("focus", function(){ - $(this).data('previous', $(this).val()); - }); - - $("#start_date").val("{$START}".replaceAll("_", "-")); - - $("#start_date").on("change", function(){ - if ($("#end_date").val() != "") - { - var previous = $(this).data('previous'); - var current = new Date($(this).val()); - var max = new Date($("#end_date").val()); - if (current > max){ - $(this).val(previous); - $(this).data('previous', $(this).val()); - return - } - } - $(this).data('previous', $(this).val()); - let location = "{$F_ACTION}&filter={$filter}&status={$displayed_status}&author={$displayed_author}&start_date=" + $(this).val().replaceAll("-", "_") + "&end_date={$END}"; - window.location.replace(location); - }); - - $("#end_unset").on("click", function(){ - $("#end_date").val(""); - let location = "{$F_ACTION}&filter={$filter}&status={$displayed_status}&author={$displayed_author}&start_date={$START}&end_date="; - window.location.replace(location); - }); - - $("#end_date").on("focus", function(){ - $(this).data('previous', $(this).val()); - }); - - $("#end_date").val("{$END}".replaceAll("_", "-")); - - $("#end_date").on("change", function(){ - if ($("#start_date").val() != "") - { - var previous = $(this).data('previous'); - var current = new Date($(this).val()); - var min = new Date($("#start_date").val()); - if (current < min){ - $(this).val(previous); - $(this).data('previous', $(this).val()); - return - } - } - $(this).data('previous', $(this).val()); - let location = "{$F_ACTION}&filter={$filter}&status={$displayed_status}&author={$displayed_author}&start_date={$START}&end_date=" + $(this).val().replaceAll("-", "_"); - window.location.replace(location); - }); - - jQuery(".checkComment").click(function(event) { - var checkbox = jQuery(this).children("input[type=checkbox]"); - if (event.target.type !== 'checkbox') { - jQuery(checkbox).prop('checked', !jQuery(checkbox).prop('checked')); - } - highlighComments(); - }); - - jQuery("#commentSelectAll").click(function () { - $(".comment-select-checkbox").prop('checked', true); - $(".comment-select-checkbox").trigger("change"); - highlighComments(); - return false; - }); - - jQuery("#commentSelectNone").click(function () { - $(".comment-select-checkbox").prop('checked', false); - $(".comment-select-checkbox").trigger("change"); - highlighComments(); - return false; - }); - - jQuery("#commentSelectInvert").click(function () { - $(".comment-select-checkbox").each(function() { - jQuery(this).prop('checked', !$(this).prop('checked')); - }); - $(".comment-select-checkbox").trigger("change"); - highlighComments(); - return false; - }); - - $(".comment-select-checkbox").on("change", function(event) { - if ($(this).prop("checked")){ - $(this).removeClass("icon-circle-empty") - $(this).addClass("icon-ok-circled") - } - else { - $(this).removeClass("icon-ok-circled") - $(this).addClass("icon-circle-empty") - } - }); - - $("#toggleSelectionMode").on("click", function() { - if ($(".comment-select-checkbox").css("visibility") == "visible") { - $(".comment-buttons-container").css("visibility", "visible"); - $(".comment-select-checkbox").css("visibility", "hidden"); - $(".comment-selection-content").hide(); - $(".comment-container").css("margin-inline-end", "0em") - $("#advanced-filter-menu").css("margin-inline", "23px 10px") - - $(".comment-select-checkbox").prop('checked', false); - $(".comment-select-checkbox").trigger("change"); - highlighComments(); - } - else { - $(".comment-select-checkbox").css("visibility", "visible"); - $(".comment-buttons-container").css("visibility", "hidden"); - $(".comment-selection-content").css("display", "flex") - $(".comment-container").css("margin-inline-end", "5em") - $("#advanced-filter-menu").css("margin-inline", "23px 270px") - } - }) - - $(".advanced-filter-btn").on("click", function() { - if ($("#advanced-filter-menu").css("display") == "none") { - $("#advanced-filter-menu").css("display", "flex") - $("#advanced-filter-menu").css("margin-bottom", "1em") - $(".commentFilter").css("margin-bottom", "0.2em") - $(".commentFilter .advanced-filter-btn").css("height", "100%") - $(".commentFilter .advanced-filter-btn").css("height", "100%") - } - else { - $("#advanced-filter-menu").css("display", "none") - $("#advanced-filter-menu").css("margin-bottom", "0.2em") - $(".commentFilter").css("margin-bottom", "1em") - $(".commentFilter .advanced-filter-btn").css("height", "20px") - } - }) - - if ("{$displayed_status}" != "all" || "{$displayed_author}" != "all" || "{$START}" != "" || "{$END}" != ""){ - $(".advanced-filter-btn").trigger( "click" ); - } - - $(".delete-comment, #commentDeleteSelected").on("click", function() { - jQuery(this).parent().parent().children("input[type=checkbox]").prop('checked', true); - $("#pendingComments").trigger("submit") - }) - - $(".approve-comment, #commentValidateSelected").on("click", function() { - jQuery(this).parent().parent().children("input[type=checkbox]").prop('checked', true); - $("#pendingComments").trigger("submit") - }) - - $("#commentValidateSelected, #commentDeleteSelected").on("click", function() { - $("#pendingComments").trigger("submit") - }) - -}); +const str_yes_delete_confirmation = "{'Yes, delete'|@translate|@escape:'javascript'}" +const str_no_delete_confirmation = "{"No, I have changed my mind"|@translate|@escape:'javascript'}" +const str_delete = "{'Are you sure you want to delete comment "%s"?'|@translate|@escape:'javascript'}" +const str_no_comments_selected = "{'No comments selected, no actions possible.'|@translate|@escape:'javascript'}" +const pwg_token = "{$PWG_TOKEN}" +const str_an_error_has = "{"An error has occured"|@translate|@escape:'javascript'}" +const str_comment_validated = "{"The comment has been validated."|@translate|@escape:'javascript'}" +const str_and_others = "{"and %s others"|@translate}" {/footer_script} + +
+
+
+
+ + -
+ + -
- - - -
+ + +
- {if !empty($navbar) }{include file='navigation_bar.tpl'|@get_extent:'navbar'}{/if} +
+

{'Select'|@translate}

+

{'All'|@translate}

+

{'None'|@translate}

+

{'Invert'|@translate}

+
-
-
- {'Filters'|@translate} - -
- -
- - - -
- - -
- - Mode sélection -
- -
- -
- - - -{/if} +
+ +
+ +
+
+ +
+
+
+

+ +
+
+ +

+
+
+ +

+
+
+ + +
+
+
\ No newline at end of file diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css index a3ca37c07..3a554f448 100644 --- a/admin/themes/default/theme.css +++ b/admin/themes/default/theme.css @@ -11,22 +11,6 @@ ul.categoryActions { margin: 0 2px; width: auto; list-style-position:outside; padding: 0; text-indent: 0; list-style: none; text-align: center; } .content div.titrePage { padding: 0 0 3px; } -.content div.comment { margin: 0 0 0.5em 0; padding: 0; - overflow: hidden; width: 100%; /* don't ask why. It's a very usefull trick */ } - -.content DIV.comment A.illustration { - display: block; - float: left; - margin: 0.5em 30px 0 0.5em; -} - -.content div.comment p.commentHeader { - text-align: right; margin: 0.5em 0.5em 0 0; } -.content div.comment ul.actions { text-align: center; margin: 0.2em; } -.content div.comment blockquote { - margin-right: 0.5em; overflow: visible; /*avoid a very strange margin behaviour (all browsers) */ } - -.comment .pendingFlag {font-style:italic;color:red;margin-left: 0.5em;} /* not used but should be */ #thePopuphelpPage .content { margin: 1em; } @@ -546,265 +530,6 @@ LI.menuLi { transform: translate(calc(-50% - 4px), -50%); } -.comment-container{ - display: flex; - flex-flow: row wrap; - align-items: center; - - gap: 1.5em; -} - -.commentFilter{ - margin-bottom: 1em; - padding-inline: 1.5em; - display: flex; - flex-direction: row; -} - -.commentFilter .commentFilter { - margin-inline-start: auto; - gap: 1em; -} - -.commentFilter .advanced-filter-btn{ - height: 20px; - padding: 10px 10px 5px 10px; - margin: 4px; - position: relative; - left: 3.17em; -} - -.advanced-filter-date{ - display: flex; - flex-direction: column; -} - -.advanced-filter-date div { - flex: 1; -} - -.advanced-filter-date div input{ - height: 100%; - width: 74%; - border-color: #D4D4D4; - padding-inline: 1em; -} - -.commentFilter .userActions{ - align-self: center; - z-index: 99; -} - -.commentFilter .pluginTypeFilter{ - transform: none; - position: unset; - margin: 4px; -} - -.commentFilter #search-comment{ - padding-inline-end: 4em; -} - -#search-comment .search-icon{ - position: relative; - left: 37px; - top: 2px; -} - -.comment-selection-content{ - background-color: rgb(250, 250, 250); - border-left: 1px solid rgb(230, 230, 230); - position: absolute; - right: 0px; - width: 223px; - min-height: calc(87% - 171px); - top: 169.5px; - z-index: 10; - padding: 7em 1em 0em 1em; - display: none; - flex-direction: column; -} - -.selectButton { - text-align: center; - background-color: #f0f0f0; - padding: 10px; - color: #777; - font-weight: bold; - margin: 4px; - border-radius: 5px; -} - -.selectButton:hover { - background-color: #ddd; - color: #3A3A3A; - text-decoration: none !important; -} - -.selectButton2 { - text-align: center; - padding: 10px; - color: #3C3C3C; - font-weight: bold; - margin: 4px; - border: solid; - border-width: 1px; - border-color: #E7E7E7; -} - -.selectButton2:hover { - background-color: #ddd; - color: #000000; - text-decoration: none !important; -} - -.comment-form{ - display : flex; -} - -.comment-box-validated, .comment-box-validated:focus{ - border-color: #FFC17E !important; -} - -.comment-box, .comment-box:focus{ - background-color: #FAFAFA; - - display: flex; - flex-direction: row; - align-items: center; - - box-shadow: rgba(0, 0, 0, 0.14) 0px 2px 5px; - - border-radius: 4px; - border-left: solid; - border-color: #999999; - border-width: 6px; - - width: 29.8em; - height: 11.7em; - - padding: 0em 1em 0em 0em; -} - -.comment-box a img{ - object-fit: cover; - height: 11.7em; - width: 11.7em; - margin-top: 0.3em; - margin-right: 3%; -} - -.comment-box .comment{ - display: flex; - flex-direction: column; - height: 100%; - - color: #3C3C3C; - font-weight: 600; -} - -.comment-box .comment blockquote{ - align-self: flex-start; - font-style: italic; - margin : -1em 2% 0% 0%; - align-self: flex-start; - min-height: 0em; -} - -.comment-box .comment strong, .comment-box .comment p{ - align-self: flex-end; - text-align: right; - margin: 0% 2% 0% 0%; -} - -.badge-grey{ - padding-inline : 5px; - color: #898989; - background-color: #DBDBDB; - border-radius: 20px; -} -.badge-red{ - color: #FF5252; - background-color: #FFCFCF; - border-radius: 20px; -} -.badge-guest{ - color: #2883C3; - background-color: #CFEBFF; - border-radius: 20px; -} -.badge-user{ - color: #FFA646; - background-color: #FFE9CF; - border-radius: 20px; -} -.badge-admin{ - color: #896AF3; - background-color: #E0DAF4; - border-radius: 20px; -} -.badge-main-user{ - color: #896AF3; - background-color: #E0DAF4; - border-radius: 20px; -} - -.comment-select-checkbox, .comment-select-checkbox:focus{ - appearance: none; - visibility: hidden; - outline: none; - - position: relative; - top: 5px; - right: -85%; - width: 7%; - height: 10%; - color: #FFA646; - font-size: 2em; - background-color: #FAFAFA; -} - -.comment div { - text-align: end; - min-height: 18%; - min-width: 100%; - margin-top: auto; - margin-bottom: 0.8em; -} - -.comment-buttons-container{ - visibility: visible; -} - -.delete-comment{ - border-width: 0px; - border-radius: 6px; - background-color: #EEEEEE; - color: #3C3C3C; - height: 100%; - font-weight: bold; - font-size: 12px; -} -.delete-comment:hover{ - cursor: pointer; - text-decoration: none; -} - -.approve-comment{ - border-width: 0px; - border-radius: 6px; - background-color: #FFC17E; - color : #3C3C3C; - height: 100%; - font-size: 12px; - font-weight: bold; - margin-right: 3%; -} -.approve-comment:hover{ - cursor: pointer; - text-decoration: none; -} - .cat-modify-content { display: grid; grid-template-rows: 70px 380px; @@ -1070,7 +795,7 @@ LI.menuLi { /* Search bar */ .search-input{ - padding: 10px 10px 6px 10px; + padding: 10px; box-shadow: 0px 2px #00000024; border: none; background-color: #fafafa !important; @@ -2828,10 +2553,6 @@ div.jGrowl div.jGrowl-notification{ margin-left: 5px; } -.advanced-filter-item{ - text-align: left; -} - .advanced-filter-revision-date .advanced-filter-item-label { display: inline !important; @@ -3201,10 +2922,16 @@ div.jGrowl div.jGrowl-notification{ padding: 13px; } +.tab-filters .filter, .pluginTypeFilter .filter { display: none; } +.tab-filters { + display: flex; + align-items: center; +} + .pluginTypeFilter { display: flex; flex-direction: row; @@ -3217,6 +2944,7 @@ div.jGrowl div.jGrowl-notification{ transform: translateY(13px); } +.tab-filters label, .pluginTypeFilter label { display: flex; flex-direction: row; @@ -3233,6 +2961,7 @@ div.jGrowl div.jGrowl-notification{ font-weight: 600; } +.tab-filters label .filter-badge, .pluginTypeFilter label .filter-badge { font-weight: normal; border-radius: 20px; @@ -3241,17 +2970,20 @@ div.jGrowl div.jGrowl-notification{ margin-left: 10px; } +.tab-filters input:checked + label, .pluginTypeFilter input:checked + label{ background: #ffa500; color: white; box-shadow: 0 2px #d18800; } +.tab-filters input:checked + label .filter-badge, .pluginTypeFilter input:checked + label .filter-badge { background-color: #d18800; color: #ffe5b6; } +.tab-filters input + label .filter-badge, .pluginTypeFilter input + label .filter-badge { background-color: #dbdbdb; color: #8c8c8c; @@ -4448,7 +4180,8 @@ a#showPermissions:hover {text-decoration: none;} cursor: pointer; } -.tag-header .selection-controller { +.tag-header .selection-controller , +.comments-selection-controller { display: none; align-items: baseline; height: 38px; @@ -4456,7 +4189,8 @@ a#showPermissions:hover {text-decoration: none;} font-weight: bold; } -.tag-header .selection-controller.show { +.tag-header .selection-controller.show, +.comments-selection-controller.show { display: flex; } @@ -4465,7 +4199,8 @@ a#showPermissions:hover {text-decoration: none;} color: #555; } -.tag-header .selection-controller a { +.tag-header .selection-controller a, +.comments-selection-controller .comments-selection-btn { margin: 0; background-color: #f0f0f0; padding: 10px; @@ -4475,7 +4210,8 @@ a#showPermissions:hover {text-decoration: none;} border-radius: 5px; } -.tag-header .selection-controller a:hover { +.tag-header .selection-controller a:hover, +.comments-selection-controller .comments-selection-btn:hover { background-color: #ddd; color: #3A3A3A; text-decoration: none; @@ -4701,7 +4437,8 @@ a#showPermissions:hover {text-decoration: none;} text-align: start; } -.selection-mode-tag .selection-other-tags { +.selection-mode-tag .selection-other-tags, +.comments-and-others { color: #ffa646; font-weight: bold; font-size: 15px; @@ -5103,17 +4840,406 @@ a#showPermissions:hover {text-decoration: none;} background-color: #ffd7ad; } -/* Pending Comments */ -#pendingComments { - padding:0 5px 0 10px; +/* Comments */ +.comments { + display: grid; + grid-template-rows: auto 1fr; + min-height: calc(100svh - 169px); + padding-left: 20px; } -#pendingComments table { - width:100%; +.comments p { + margin: 0; } -#pendingComments .bottomButtons { - text-align:left; +.comments-selection, +.comment-validate, +.comments-selections, +.comment-template { + display: none; +} + +.comments-selection-mode, +.comments-selection { + border-left: 1px solid transparent; +} + +.comments-selection { + padding-top: 20px; +} + +.comments-container.active { + display: grid; + grid-template-columns: 1fr 250px; +} + +.comments-container.active .comment-content:hover { + background-color: #FFD9A7; +} + +.comment.comment-selected .comment-content { + background-color: #FFD9A7; +} + +.comments-container { + /* display: flex; */ + width: 100%; +} + +.comments-filters { + display: grid; + grid-template-columns: 1fr 250px; +} + +.comments-filters { + align-items: center; +} + +.comments-selection-mode { + height: 100%; + align-content: center; + text-align: start; + padding-left: 20px; +} + +.comments-selection-switch { + margin-top: 20px; +} + +.comments-search-cancel { + display: none; +} + +.comments-advanced-filter, +.comments-tabs-filters, +.comments-search { + display: flex; + align-items: center; +} + +.comments-advanced-filter { + gap: 10px; +} + +.comments-tabs-filters { + padding: 20px 20px 0 0; + justify-content: space-between; +} + +.comments-selection-btn { + cursor: pointer; +} + +.comments-search { + height: 36px; + padding: 0 5px; + box-shadow: 0px 2px #00000024; + border: none; + background-color: #fafafa !important; + width: 200px; +} + +.comments-search:hover { + background-color: #f0f0f0 !important +} + +.comments-search-icon, +.comments-search-cancel { + font-size: 18px; +} + +.comments-search-icon::before, +.comments-search-cancel::before { + margin-left: 0; +} + +.comments-search input { + all: unset; + background-color: transparent !important; + width: 100%; + height: 100%; + line-height: 18px; + text-align: start; +} + +.advanced-filter-dates-max .advanced-filter-select-container::before, +.advanced-filter-dates-min .advanced-filter-select-container::before { + content: ""; +} + +.advanced-filter-dates-max .advanced-filter-select, +.advanced-filter-dates-min .advanced-filter-select { + padding: 0 10px; + height: 32px; +} + +.comments-advanced-filters .advanced-filter-select { + min-width: 150px; + -webkit-appearance: none; + appearance: none; +} + +.comments-advanced-filters { + display: none; + background-color: #F3F3F3; + margin: 10px 20px 0 0; + position: relative; + z-index: 99; +} + +.comments-filters-container { + display: flex; + align-items: center; + padding: 10px; + gap: 20px; +} + +.comments-filters-date { + height: 33px !important; + padding: 0 5px !important; +} + +.comments-list { + display: flex; + flex-wrap: wrap; + gap: 20px; + padding-top: 20px; + transition: flex-wrap 0.5s ease; +} + +.comment-container { + background-color: #FAFAFA; + display: flex; + flex-direction: row; + align-items: stretch; + box-shadow: rgba(0, 0, 0, 0.14) 0px 2px 5px; + border-radius: 4px; + border-left: 6px solid #FFC17E; + /* width: 29.8em; + height: 11.7em; */ + height: 150px; + width: 390px; + overflow: hidden; +} + +.comment-validated { + border-left: 6px solid #999999 !important; +} + +.comment-content { + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px; + cursor: pointer; +} + +.comment-buttons { + display: flex; + gap: 8px; + justify-content: flex-end; +} + +.comment-footer { + display: flex; + align-items: center; + justify-content: space-between; +} + +.comment-hash { + color: #7a7a7a89; +} + +.comment-msgs { + display: flex; + justify-content: space-between; + gap: 5px; +} + +.comment-msg { + font-style: italic; + text-align: start; + padding-bottom: 5px; + font-size: 14px; +} + +.comment-select-checkbox, +.comment-select-checkbox:focus { + display: none; + appearance: none; + outline: none; + position: relative; + right: 10px; + bottom: 8px; + width: 7%; + height: 10%; + color: #FFA646; + font-size: 2em; + background-color: #FAFAFA; +} + +.comment-img { + height: 150px; + width: 150px; + object-fit: cover; +} + +.comment-author, +.comment-date { + display: flex; + align-items: center; + gap: 5px; + font-weight: 600; + color: #3C3C3C; + justify-content: end; + font-size: 12px; +} + +.comment-author-icon { + padding: 0px; + height: 16px; + width: 16px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + text-align: center; + font-size: 10px; +} + +.comment-author-icon::before { + width: 12px; + height: 16px; + place-content: center; +} + +.comment-buttons button { + border: 0; + border-radius: 6px; + padding: 5px 10px; + font-weight: bold; + font-size: 12px; + cursor: pointer; +} + +.comment-validate { + background-color: #FFC17E; + color: #3C3C3C; +} + +.comment-validate:hover { + background-color: #ff7700; + color: #493C21; +} + +.comment-delete { + background-color: #EEEEEE; + color: #3C3C3C; +} + +.comment-delete:hover { + background-color: #E3E3E3; +} + +.comments .user-pagination { + padding: 20px 20px 0 0; +} + +.comments-selected { + margin: 20px 30px; +} + +.comments-no-selection { + margin: 0 30px !important; +} + +.comments-selected-item { + display: flex; + align-items: center; + font-size: 16px; + gap: 5px; +} + +.comment-paging-current { + color: #ffa646; +} + +.comments-reset-filters { + position: absolute; + top: 10px; + right: 10px; + cursor: pointer; +} + +.comments-reset-filters:hover { + color: #232323; +} + +.comments-modal { + width: 700px !important; +} + +.comments-modal p { + margin: 0; +} + +.comments-modal-header { + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.comments-modal-infos { + display: flex; + flex-direction: column; + gap: 5px; + align-items: start; +} + +.comments-modal-img-info { + display: flex; + align-items: start; + gap: 5px; +} + +.comments-modal-img-i { + display: flex; + flex-direction: column; + gap: 5px; + align-items: self-end; +} + +.comments-modal .comment-author { + justify-content: start; + font-size: 18px; + flex-direction: row-reverse; + font-weight: bold; +} + +.comments-modal .comment-author-icon { + font-size: 16px; + height: 24px; + width: 24px; +} + +.comments-modal .comment-author-icon::before { + width: 16px; + height: 20px; +} + +.comments-modal-img { + width: 100px; + height: 100px; + object-fit: cover; + border-radius: 2px; +} + +.comments-modal-body { + width: 100%; + max-height: 200px; + overflow-y: auto; + margin: 15px 0; } .bottomButtons.syncBtn { @@ -5121,7 +5247,7 @@ a#showPermissions:hover {text-decoration: none;} margin-left: 25px; } - +/* Category Ordering */ FORM#categoryOrdering p.albumTitle {margin:0; margin-left: 5px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; margin-bottom: 4px;} FORM#categoryOrdering p.albumTitle a {font-size: 14px; font-weight: 600;} FORM#categoryOrdering p.albumTitle i {display: none;} @@ -5859,7 +5985,8 @@ input:checked + .slider.small:before, input:checked + .slider.small::after { white-space: nowrap; } -#selection-mode-block button{ +#selection-mode-block button, +#commentsSelection button { display:block; margin:20px auto; font-size: 12px; @@ -5869,7 +5996,8 @@ input:checked + .slider.small:before, input:checked + .slider.small::after { width: 180px; } -#selection-mode-block button:hover{ +#selection-mode-block button:hover, +#commentsSelection button:hover { cursor: pointer; } @@ -6836,23 +6964,6 @@ fieldset#environment legend i[class*="icon-"] { color:#493C21; } -.buttonSecondary{ - font-size:12px; - border:none; - padding:13px 20px; - margin-left:0; font-weight: bold; transition: all 125ms ease-out; - color:#3C3C3C; - background-color:#ECECEC; -} - -.buttonSecondary:hover{ - cursor: pointer; - background-color: #FFA646; - text-decoration: none; - color:#3C3C3C; -} - - #cboxLoadedContent input[type="submit"] {margin-bottom: 20px; float: none;} #permissions, #uploadForm fieldset {border: 0;} @@ -6875,7 +6986,7 @@ fieldset#environment legend i[class*="icon-"] { .icon-plus.cboxElement:hover {box-shadow: 0 2px 4px rgba(0, 0, 0, 0.16);} #albumSelection {margin-right: 20px;} .selectize-control.single .selectize-input {border-radius: 0; font-weight: bold;font-size: 12px;} -.buttonLike, .buttonSecondary {padding: 8px 10px; margin-left: 5px;} +.buttonLike {padding: 8px 10px; margin-left: 5px;} .changeUsername .buttonLike {padding: 1px 10px;} .changePassword .buttonLike {padding: 1px 10px;} .selectFilesButtonBlock {display: flex; margin-top: 10px;} @@ -8063,6 +8174,7 @@ color:#FF7B00; font-size: 30px; } +.modal-content, .new-album-modal-content { display: flex; flex-direction: column; diff --git a/admin/themes/roma/theme.css b/admin/themes/roma/theme.css index 6ddea9e9a..68d48d4df 100644 --- a/admin/themes/roma/theme.css +++ b/admin/themes/roma/theme.css @@ -727,7 +727,8 @@ input:focus + .slider { box-shadow: 0 0 1px #FFC27F; } -#selection-mode-block{ +#selection-mode-block, +.selection-mode { background-color: #232323; border-left:1px solid #1a1a1a; } @@ -762,13 +763,15 @@ input:focus + .slider { color:#c0c0c0; } -#selection-mode-block button{ +#selection-mode-block button, +#commentsSelection button { border: 1px solid #e7e7e7; color:#c0c0c0; } -#selection-mode-block button:hover{ +#selection-mode-block button:hover, +#commentsSelection button:hover { background-color: #ffa744; border: 1px solid #ffa744; color:#3c3c3c; @@ -1301,7 +1304,8 @@ background:#6C2D2D!important; /* Tag Manager */ .tag-box, -.tag-header .selection-controller a{ +.tag-header .selection-controller a, +.comments-selection-controller .comments-selection-btn { background-color: #333 !important; } @@ -1494,6 +1498,7 @@ background:#6C2D2D!important; transform: translateY(13px); } +.tab-filters label, .pluginTypeFilter label { display: flex; flex-direction: row; @@ -1509,25 +1514,30 @@ background:#6C2D2D!important; box-shadow: 0px 2px #00000066; } +.tab-filters label:hover, .pluginTypeFilter label:hover{ background: #1B1B1B; } +.tab-filters input:checked + label, .pluginTypeFilter input:checked + label{ background: #ffa646; color: #444444; box-shadow: 0 2px #925f00; } +.tab-filters input:checked:hover + label, .pluginTypeFilter input:checked:hover + label{ background: #ffc17e; } +.tab-filters input:checked + label .filter-badge, .pluginTypeFilter input:checked + label .filter-badge { background-color: #d18800; color: #444; } +.tab-filters input + label .filter-badge, .pluginTypeFilter input + label .filter-badge { background-color: #3f3f3f; color: #bababa; @@ -2275,7 +2285,46 @@ ul.jqtree-tree li.jqtree-ghost span.jqtree-line { opacity:60%!important; } +.comment-container { + background-color: #333; + border-color: #ffa646; +} + +.comment-validated { + border-color: #232323 !important; +} + +.comment-author, +.comment-date { + color: #aaa; +} + +.comment-validate { + background-color: #ffa646; +} + +.comment-delete { + background-color: #232323; +} + +.comment-delete:hover { + background-color: #111; +} + +.comments-advanced-filters { + background-color: #333; +} + +.comment.comment-selected .comment-content { + background-color: #1B1B1B; +} + +.comments-container.active .comment-content:hover { + background-color: #1B1B1B; +} + /* Cat modify description modal */ +.modal-content, .desc-modal-content, .new-album-modal-content { background-color: #444; @@ -2394,64 +2443,6 @@ ul.jqtree-tree li.jqtree-ghost span.jqtree-line { background-color:#444; } -.comment-box, .comment-box:focus{ - background-color: #333; - color: #777; - border-color: #777; -} - -.comment-box .comment{ - color : #777; -} - -.comment-selection-content{ - background-color: rgb(51, 51, 51); - border-left: 1px solid #3A3A3A; -} - -.badge-grey{ - background-color: rgb(63, 63, 63); - color: rgb(186, 186, 186); -} - -.selectButton{ - background-color: rgb(85, 85, 85); - color: rgb(193, 193, 193); -} - -.selectButton2{ - border-color: rgb(85, 85, 85); - color: #C1C1C1; -} - -.delete-comment{ - background-color: rgb(85, 85, 85); - color: rgb(193, 193, 193); -} - -.comment-select-checkbox, .comment-select-checkbox:focus{ - background-color: #333; -} - -.commentFilterSelected, .commentFilterSelected:hover { - color: #FFFFFF; - background-color: #FFA500; - border-bottom: solid #D18800; -} -.commentFilterUnselected, .commentFilterUnselected:hover { - color: #F3F3F3; - background-color: #898989; - border-bottom: solid #DBDBDB; -} - -.buttonSecondary { - background-color: #2E2E2E; - color: #777777; -} -.buttonSecondary:hover { - background-color: #1B1B1B; - color: #777777; - /* Filters options */ .select-views{ background-color: #444444; @@ -2480,4 +2471,20 @@ ul.jqtree-tree li.jqtree-ghost span.jqtree-line { label:has(> .filters-icon-check:disabled){ color:#555; -} \ No newline at end of file +} + +.comments-advanced-filter .advanced-filter-btn { + box-shadow: 0px 2px #333; +} + +.comments-search { + background-color: #333 !important; +} + +.comments-search:hover { + background-color: #222 !important +} + +.comments-search input:focus { + outline: none; +} diff --git a/include/ws_functions/pwg.comments.php b/include/ws_functions/pwg.comments.php new file mode 100644 index 000000000..11e7c4cb1 --- /dev/null +++ b/include/ws_functions/pwg.comments.php @@ -0,0 +1,243 @@ += \''. $min .'\''; + } + + if (!empty($params['f_max_date'])) + { + $max = date_format(date_create($params['f_max_date']), "Y-m-d 23:59:59"); + $where_clauses[] = 'date <= \''. $max .'\''; + } + + // reset all filters during search + if (!empty($params['search'])) + { + $where_clauses = array('1=1'); + $where_clauses[] = 'content LIKE "%'. pwg_db_real_escape_string($params['search']) .'%"'; + } + + // summary + $query = ' +SELECT + count(*) as all_comments, + sum(validated = \'true\') as validated, + sum(validated = \'false\') as pending +FROM '.COMMENTS_TABLE.' +WHERE '.implode(' AND ', $where_clauses).' +;'; + + $summary = pwg_db_fetch_assoc(pwg_query($query)); + $total_comments = $summary['all_comments']; + + switch($params['status']) + { + case 'pending': + $where_clauses[] = 'validated = \'false\''; + $total_comments = $summary['pending']; + break; + + case 'validated': + $where_clauses[] = 'validated = \'true\''; + $total_comments = $summary['validated']; + break; + } + + // comments + $query = ' +SELECT + c.id, + c.image_id, + c.date, + c.author, + c.author_id, + '.$conf['user_fields']['username'].' AS username, + ui.status, + c.content, + i.path, + i.representative_ext, + i.file, + i.date_available, + validated, + c.anonymous_id + FROM '.COMMENTS_TABLE.' AS c + INNER JOIN '.IMAGES_TABLE.' AS i + ON i.id = c.image_id + LEFT JOIN '.USERS_TABLE.' AS u + ON u.'.$conf['user_fields']['id'].' = c.author_id + LEFT JOIN '.USER_INFOS_TABLE.' AS ui + ON ui.user_id = c.author_id + WHERE '.implode(' AND ', $where_clauses).' + ORDER BY c.date DESC + LIMIT '.$params['per_page'] * $params['page'].', '.$params['per_page'].' +;'; + $result = pwg_query($query); + + $list = array(); + while ($row = pwg_db_fetch_assoc($result)) + { + + $medium = DerivativeImage::get_one( + IMG_MEDIUM, + array( + 'id' => $row['image_id'], + 'path' => $row['path'], + 'representative_ext' => $row['representative_ext'], + ) + )->get_url(); + + if (empty($row['author_id'])) + { + $author_name = $row['author']; + } + else + { + $author_name = stripslashes($row['username']); + } + + $list[] = array( + 'id' => $row['id'], + 'admin_link' => get_root_url().'admin.php?page=photo-'.$row['image_id'], + 'medium_url' => $medium, + 'file' => $row['file'], + 'image_date_available' => format_date($row['date_available'], array('day_name','day','month','year','time')), + 'author' => trigger_change('render_comment_author', $author_name), + 'author_status' => $conf['webmaster_id'] == $row['author_id'] ? 'main_user' : $row['status'], + 'date' => format_date($row['date'], array('day_name','day','month','year','time')), + 'content' => trigger_change('render_comment_content', $row['content']), + 'raw_content' => $row['content'], + 'is_pending' => ('false' == $row['validated']), + ); + } + + // filters + $query = ' +SELECT + MIN(date) AS started_at, + MAX(date) AS ended_at +FROM '.COMMENTS_TABLE.' +WHERE '.implode(' AND ', $where_clauses).' +;'; + + $dates = pwg_db_fetch_assoc(pwg_query($query)); + + unset($where_clauses['author_id']); + $query = ' +SELECT + author, + author_id, + count(*) as nb_authors +FROM '.COMMENTS_TABLE.' +WHERE '.implode(' AND ', $where_clauses).' +GROUP BY author_id +;'; + + $nb_authors_in = query2array($query); + + return array( + 'summary' => $summary, + 'comments' => $list, + 'filters' => array( + 'nb_authors' => $nb_authors_in, + 'started_at' => $dates['started_at'], + 'ended_at' => $dates['ended_at'] + ), + 'paging' => array( + 'page' => $params['page'], + 'per_page' => $params['per_page'], + 'total_pages' => max(0, ceil($total_comments / $params['per_page']) - 1), + ), + ); +} + +/** + * API method + * Delete comments + * @since 16 + * @param mixed[] $params + * + */ +function ws_userComments_delete($params, &$service) +{ + include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php'); + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, l10n('Invalid security token')); + } + + $params['comment_id'] = array_unique($params['comment_id']); + delete_user_comment($params['comment_id']); + return 'Comment successfully deleted'; +} + +/** + * API method + * Validate comments + * @since 16 + * @param mixed[] $params + * + */ +function ws_userComments_validate($params, &$service) +{ + include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php'); + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, l10n('Invalid security token')); + } + + $params['comment_id'] = array_unique($params['comment_id']); + validate_user_comment($params['comment_id']); + return 'Comment successfully validated'; +} \ No newline at end of file diff --git a/language/en_UK/admin.lang.php b/language/en_UK/admin.lang.php index 5ab96daf5..a64bccd4d 100644 --- a/language/en_UK/admin.lang.php +++ b/language/en_UK/admin.lang.php @@ -1411,5 +1411,8 @@ $lang['Save all photos'] = 'Save all photos'; $lang['Use standard Piwigo template for common pages.'] = 'Use standard Piwigo template for common pages.'; $lang['When enabled, a common template is used for the login, registration and forgotten password pages, regardless of the theme. Some themes might use these templates even if you uncheck this option'] = 'When enabled, a common template is used for the login, registration and forgotten password pages, regardless of the theme. Some themes might use these templates even if you uncheck this option'; $lang['We have found %d duplicate paths. Details provided by plugin Check Uploads'] = 'We have found %d duplicate paths. Details provided by plugin Check Uploads'; +$lang['Are you sure you want to delete comment "%s"?'] = 'Are you sure you want to delete comment "%s"?'; +$lang['No comments selected, no actions possible.'] = 'No comments selected, no actions possible.'; +$lang['The comment has been validated.'] = 'The comment has been validated.'; // Leave this line empty \ No newline at end of file diff --git a/language/fr_FR/admin.lang.php b/language/fr_FR/admin.lang.php index 2b0d5b94e..998029de6 100644 --- a/language/fr_FR/admin.lang.php +++ b/language/fr_FR/admin.lang.php @@ -1413,4 +1413,7 @@ $lang['Save all photos'] = 'Enregistrer toutes les photos'; $lang['Use standard Piwigo template for common pages.'] = 'Utiliser le modèle standard de Piwigo pour les pages courantes.'; $lang['When enabled, a common template is used for the login, registration and forgotten password pages, regardless of the theme. Some themes might use these templates even if you uncheck this option'] = 'Lorsque cette option est activée, un modèle commun est utilisé pour les pages de connexion, d\'inscription et de mot de passe oublié, quel que soit le thème. Certains thèmes peuvent utiliser ces modèles même si cette option est décochée.'; $lang['We have found %d duplicate paths. Details provided by plugin Check Uploads'] = 'Nous avons trouvé %d chemins anormalement dupliqués. À contrôler avec le plugin Check Uploads.'; +$lang['Are you sure you want to delete comment "%s"?'] = 'Êtes-vous sûr de vouloir supprimer le commentaire "%s" ?'; +$lang['No comments selected, no actions possible.'] = 'Aucun commentaire sélectionné, aucune action possible.'; +$lang['The comment has been validated.'] = 'Le commentaire a été validé.'; // Leave this line empty diff --git a/ws.php b/ws.php index 484e801c5..a8628418b 100644 --- a/ws.php +++ b/ws.php @@ -1623,6 +1623,85 @@ enabled_high, registration_date, registration_date_string, registration_date_sin $ws_functions_root . 'pwg.users.php', array('admin_only'=>false, 'post_only'=>true) ); + + $service->addMethod( + 'pwg.userComments.getList', + 'ws_userComments_getList', + array( + 'status' => array( + 'default' => 'all', + 'info' => 'must be: all, validated or pending' + ), + 'search' => array( + 'default' => null, + 'info' => 'All other parameters are not used during a search.' + ), + 'author_id' => array( + 'flags' => WS_PARAM_OPTIONAL, + 'type' => WS_TYPE_ID, + ), + 'image_id' => array( + 'flags' => WS_PARAM_OPTIONAL, + 'type' => WS_TYPE_ID, + ), + 'f_min_date' => array( + 'default' => null + ), + 'f_max_date' => array( + 'default' => null + ), + 'page' => array( + 'default' => 0, + 'type' => WS_TYPE_INT | WS_TYPE_POSITIVE + ), + 'per_page' => array( + 'default' => $conf['comments_page_nb_comments'], + 'type' => WS_TYPE_INT | WS_TYPE_POSITIVE + ) + ), + 'Get comments', + $ws_functions_root . 'pwg.comments.php', + array( + 'admin_only' => true, + 'post_only' => false + ) + ); + + $service->addMethod( + 'pwg.userComments.delete', + 'ws_userComments_delete', + array( + 'comment_id' => array( + 'flags' => WS_PARAM_FORCE_ARRAY, + 'type' => WS_TYPE_INT | WS_TYPE_POSITIVE, + ), + 'pwg_token' => array(), + ), + 'Delete comments', + $ws_functions_root . 'pwg.comments.php', + array( + 'admin_only'=>true, + 'post_only'=>true + ) + ); + + $service->addMethod( + 'pwg.userComments.validate', + 'ws_userComments_validate', + array( + 'comment_id' => array( + 'flags' => WS_PARAM_FORCE_ARRAY, + 'type' => WS_TYPE_INT | WS_TYPE_POSITIVE, + ), + 'pwg_token' => array(), + ), + 'Validate comments', + $ws_functions_root . 'pwg.comments.php', + array( + 'admin_only'=>true, + 'post_only'=>true + ) + ); } ?>