unique css ids

and bug fix test: editing comment: button label "save" instead of "edit comment"
This commit is contained in:
Katja Suess 2020-01-16 15:32:46 +01:00
parent 6794d95f85
commit f7ae6e49a5
4 changed files with 258 additions and 240 deletions

View File

@ -1,3 +1,3 @@
Extended existing review workflow by stati ``rejected`` and ``spam`` Extended existing review workflow by stati ``rejected`` and ``spam``
Moderation view extended to handle four workflow states. Moderation view extended to handle four workflow states.
[ksuess] [ksuess and precious input of agitator]

View File

@ -32,6 +32,7 @@
<div class="comment" <div class="comment"
tal:define="reply reply_dict/comment; tal:define="reply reply_dict/comment;
comment_id reply/getId;
depth reply_dict/depth|python:0; depth reply_dict/depth|python:0;
depth python: depth > 10 and '10' or depth; depth python: depth > 10 and '10' or depth;
author_home_url python:view.get_commenter_home_url(username=reply.author_username); author_home_url python:view.get_commenter_home_url(username=reply.author_username);
@ -42,7 +43,7 @@
canDelete python:view.can_delete(reply); canDelete python:view.can_delete(reply);
colorclass python:lambda x: 'state-private' if x=='rejected' else ('state-internal' if x=='spam' else 'state-'+x);" colorclass python:lambda x: 'state-private' if x=='rejected' else ('state-internal' if x=='spam' else 'state-'+x);"
tal:attributes="class python:'comment replyTreeLevel{depth} {state}'.format(depth= depth, state=colorclass(review_state)); tal:attributes="class python:'comment replyTreeLevel{depth} {state}'.format(depth= depth, state=colorclass(review_state));
id string:${reply/getId}" id comment_id"
tal:condition="python:canReview or review_state == 'published'"> tal:condition="python:canReview or review_state == 'published'">
<div class="commentImage" tal:condition="showCommenterImage"> <div class="commentImage" tal:condition="showCommenterImage">
@ -95,7 +96,8 @@
class="commentactionsform" class="commentactionsform"
tal:condition="python:not canDelete and isDeleteOwnCommentAllowed and view.could_delete_own(reply)" tal:condition="python:not canDelete and isDeleteOwnCommentAllowed and view.could_delete_own(reply)"
tal:attributes="action string:${reply/absolute_url}/@@delete-own-comment; tal:attributes="action string:${reply/absolute_url}/@@delete-own-comment;
style python:view.can_delete_own(reply) and 'display: inline' or 'display: none'"> style python:view.can_delete_own(reply) and 'display: inline' or 'display: none';
id string:delete-${comment_id}">
<input name="form.button.DeleteComment" <input name="form.button.DeleteComment"
class="destructive" class="destructive"
type="submit" type="submit"
@ -108,7 +110,8 @@
method="post" method="post"
class="commentactionsform" class="commentactionsform"
tal:condition="python:canDelete" tal:condition="python:canDelete"
tal:attributes="action string:${reply/absolute_url}/@@moderate-delete-comment"> tal:attributes="action string:${reply/absolute_url}/@@moderate-delete-comment;
id string:delete-${comment_id}">
<input name="form.button.DeleteComment" <input name="form.button.DeleteComment"
class="destructive" class="destructive"
type="submit" type="submit"
@ -120,7 +123,7 @@
<tal:edit tal:condition="python:isEditCommentAllowed and canEdit"> <tal:edit tal:condition="python:isEditCommentAllowed and canEdit">
<!-- plone 5 will have auth_token available <!-- plone 5 will have auth_token available
so we'll use modal pattern --> so we'll use modal pattern -->
<a class="pat-plone-modal context" <a class="pat-plone-modal context commentactionsform"
tal:condition="auth_token" tal:condition="auth_token"
tal:attributes="href string:${reply/absolute_url}/@@edit-comment?_authenticator=${auth_token}" tal:attributes="href string:${reply/absolute_url}/@@edit-comment?_authenticator=${auth_token}"
i18n:translate="Edit">Edit</a> i18n:translate="Edit">Edit</a>
@ -129,7 +132,8 @@
method="get" method="get"
class="commentactionsform" class="commentactionsform"
tal:condition="not: auth_token" tal:condition="not: auth_token"
tal:attributes="action string:${reply/absolute_url}/@@edit-comment"> tal:attributes="action string:${reply/absolute_url}/@@edit-comment;
id string:edit-${comment_id}">
<input name="form.button.EditComment" <input name="form.button.EditComment"
class="context" class="context"
type="submit" type="submit"
@ -148,7 +152,8 @@
tal:condition="canReview" tal:condition="canReview"
tal:repeat="action reply_dict/actions|nothing" tal:repeat="action reply_dict/actions|nothing"
tal:attributes="action string:${reply/absolute_url}/@@transmit-comment; tal:attributes="action string:${reply/absolute_url}/@@transmit-comment;
name action/id"> name action/id;
id string:${action/id}-${comment_id}">
<input type="hidden" name="workflow_action" tal:attributes="value action/id" /> <input type="hidden" name="workflow_action" tal:attributes="value action/id" />
<input name="form.button.TransmitComment" <input name="form.button.TransmitComment"
class="context" class="context"

View File

@ -5,17 +5,19 @@
******************************************************************************/ ******************************************************************************/
/* global require */ /* global require */
if(require === undefined){ if (require === undefined) {
require = function(reqs, torun){ // jshint ignore:line require = function(reqs, torun) {
'use strict'; // jshint ignore:line
"use strict";
return torun(window.jQuery); return torun(window.jQuery);
}; };
} }
require([ // jshint ignore:line require([
'jquery' // jshint ignore:line
], function ($) { "jquery"
'use strict'; ], function($) {
"use strict";
// This unnamed function allows us to use $ inside of a block of code // This unnamed function allows us to use $ inside of a block of code
// without permanently overwriting $. // without permanently overwriting $.
@ -25,80 +27,82 @@ require([ // jshint ignore:line
* the function. We do this by copying the regular comment form and * the function. We do this by copying the regular comment form and
* adding a hidden in_reply_to field to the form. * adding a hidden in_reply_to field to the form.
**************************************************************************/ **************************************************************************/
$.createReplyForm = function (comment_div) { $.createReplyForm = function(comment_div) {
var comment_id = comment_div.attr("id");
var comment_id = comment_div.attr('id'); var reply_button = comment_div.find(".reply-to-comment-button");
var reply_button = comment_div.find('.reply-to-comment-button');
/* Clone the reply div at the end of the page template that contains /* Clone the reply div at the end of the page template that contains
* the regular comment form. * the regular comment form.
*/ */
var reply_div = $('#commenting').clone(true); var reply_div = $("#commenting").clone(true);
/* Remove the ReCaptcha JS code before appending the form. If not /* Remove the ReCaptcha JS code before appending the form. If not
* removed, this causes problems * removed, this causes problems
*/ */
reply_div.find('#formfield-form-widgets-captcha') reply_div
.find('script') .find("#formfield-form-widgets-captcha")
.find("script")
.remove(); .remove();
/* Insert the cloned comment form right after the reply button of the /* Insert the cloned comment form right after the reply button of the
* current comment. * current comment.
*/ */
reply_div.appendTo(comment_div).css('display', 'none'); reply_div.appendTo(comment_div).css("display", "none");
/* Remove id='commenting' attribute, since we use it to uniquely define /* Remove id='commenting' attribute, since we use it to uniquely define
the main reply form. */ the main reply form. */
// Still belongs to class='reply' // Still belongs to class='reply'
reply_div.removeAttr('id'); reply_div.removeAttr("id");
/* Hide the reply button (only hide, because we may want to show it /* Hide the reply button (only hide, because we may want to show it
* again if the user hits the cancel button). * again if the user hits the cancel button).
*/ */
$(reply_button).css('display', 'none'); $(reply_button).css("display", "none");
/* Fetch the reply form inside the reply div */ /* Fetch the reply form inside the reply div */
var reply_form = reply_div.find('form'); var reply_form = reply_div.find("form");
/* Change the id of the textarea of the reply form /* Change the id of the textarea of the reply form
* To avoid conflict later between textareas with same id 'form-widgets-comment-text' while implementing a seperate instance of TinyMCE * To avoid conflict later between textareas with same id 'form-widgets-comment-text' while implementing a seperate instance of TinyMCE
* */ * */
reply_form.find('#formfield-form-widgets-comment-text').attr('id', 'formfield-form-widgets-new-textarea'+comment_id ); reply_form
reply_form.find('#form-widgets-comment-text').attr('id', 'form-widgets-new-textarea'+comment_id ); .find("#formfield-form-widgets-comment-text")
.attr("id", "formfield-form-widgets-new-textarea" + comment_id);
reply_form
.find("#form-widgets-comment-text")
.attr("id", "form-widgets-new-textarea" + comment_id);
/* Populate the hidden 'in_reply_to' field with the correct comment /* Populate the hidden 'in_reply_to' field with the correct comment
id */ id */
reply_form.find('input[name="form.widgets.in_reply_to"]') reply_form.find('input[name="form.widgets.in_reply_to"]').val(comment_id);
.val(comment_id);
/* Add a remove-reply-to-comment Javascript function to remove the /* Add a remove-reply-to-comment Javascript function to remove the
form */ form */
var cancel_reply_button = reply_div.find('.cancelreplytocomment'); var cancel_reply_button = reply_div.find(".cancelreplytocomment");
cancel_reply_button.attr('id', comment_id); cancel_reply_button.attr("id", comment_id);
/* Show the cancel buttons. */ /* Show the cancel buttons. */
reply_form.find('input[name="form.buttons.cancel"]') reply_form
.css('display', 'inline'); .find('input[name="form.buttons.cancel"]')
.css("display", "inline");
/* Show the reply layer with a slide down effect */ /* Show the reply layer with a slide down effect */
reply_div.slideDown('slow'); reply_div.slideDown("slow");
/* Show the cancel button in the reply-to-comment form */ /* Show the cancel button in the reply-to-comment form */
cancel_reply_button.css('display', 'inline'); cancel_reply_button.css("display", "inline");
}; };
/************************************************************************** /**************************************************************************
* Remove all error messages and field values from the form that is passed * Remove all error messages and field values from the form that is passed
* to the function. * to the function.
**************************************************************************/ **************************************************************************/
$.clearForm = function (form_div) { $.clearForm = function(form_div) {
form_div.find('.error').removeClass('error'); form_div.find(".error").removeClass("error");
form_div.find('.fieldErrorBox').remove(); form_div.find(".fieldErrorBox").remove();
form_div.find('input[type="text"]').attr('value', ''); form_div.find('input[type="text"]').attr("value", "");
form_div.find('textarea').attr('value', ''); form_div.find("textarea").attr("value", "");
/* XXX: Clean all additional form extender fields. */ /* XXX: Clean all additional form extender fields. */
}; };
@ -108,78 +112,83 @@ require([ // jshint ignore:line
* Window Load Function: Executes when complete page is fully loaded, * Window Load Function: Executes when complete page is fully loaded,
* including all frames, * including all frames,
**************************************************************************/ **************************************************************************/
$(window).load(function () { $(window).load(function() {
/********************************************************************** /**********************************************************************
* If the user has hit the reply button of a reply-to-comment form * If the user has hit the reply button of a reply-to-comment form
* (form was submitted with a value for the 'in_reply_to' field in the * (form was submitted with a value for the 'in_reply_to' field in the
* request), create a reply-to-comment form right under this comment. * request), create a reply-to-comment form right under this comment.
**********************************************************************/ **********************************************************************/
var post_comment_div = $('#commenting'); var post_comment_div = $("#commenting");
var in_reply_to_field = var in_reply_to_field = post_comment_div.find(
post_comment_div.find('input[name="form.widgets.in_reply_to"]'); 'input[name="form.widgets.in_reply_to"]'
if (in_reply_to_field.length !== 0 && in_reply_to_field.val() !== '') { );
var current_reply_id = '#' + in_reply_to_field.val(); if (in_reply_to_field.length !== 0 && in_reply_to_field.val() !== "") {
var current_reply_to_div = $('.discussion').find(current_reply_id); var current_reply_id = "#" + in_reply_to_field.val();
var current_reply_to_div = $(".discussion").find(current_reply_id);
$.createReplyForm(current_reply_to_div); $.createReplyForm(current_reply_to_div);
$.clearForm(post_comment_div); $.clearForm(post_comment_div);
} }
/********************************************************************** /**********************************************************************
* If the user hits the 'reply' button of an existing comment, create a * If the user hits the 'reply' button of an existing comment, create a
* reply form right beneath this comment. * reply form right beneath this comment.
**********************************************************************/ **********************************************************************/
$('.reply-to-comment-button').bind('click', function (e) { // jshint ignore:line $(".reply-to-comment-button").bind("click", function(e) {
var comment_div = $(this).parents().filter('.comment'); // jshint ignore:line
var comment_div = $(this)
.parents()
.filter(".comment");
$.createReplyForm(comment_div); $.createReplyForm(comment_div);
$.clearForm(comment_div); $.clearForm(comment_div);
}); });
/********************************************************************** /**********************************************************************
* If the user hits the 'clear' button of an open reply-to-comment form, * If the user hits the 'clear' button of an open reply-to-comment form,
* remove the form and show the 'reply' button again. * remove the form and show the 'reply' button again.
**********************************************************************/ **********************************************************************/
$('#commenting #form-buttons-cancel').bind('click', function (e) { $("#commenting #form-buttons-cancel").bind("click", function(e) {
e.preventDefault(); e.preventDefault();
var reply_to_comment_button = $(this). var reply_to_comment_button = $(this)
parents(). .parents()
filter('.comment'). .filter(".comment")
find('.reply-to-comment-button'); .find(".reply-to-comment-button");
/* Find the reply-to-comment form and hide and remove it again. */ /* Find the reply-to-comment form and hide and remove it again. */
$.reply_to_comment_form = $(this).parents().filter('.reply'); $.reply_to_comment_form = $(this)
$.reply_to_comment_form.slideUp('slow', function () { .parents()
.filter(".reply");
$.reply_to_comment_form.slideUp("slow", function() {
$(this).remove(); $(this).remove();
}); });
/* Show the reply-to-comment button again. */ /* Show the reply-to-comment button again. */
reply_to_comment_button.css('display', 'inline'); reply_to_comment_button.css("display", "inline");
}); });
/********************************************************************** /**********************************************************************
* Transmit a single comment. * Transmit a single comment.
**********************************************************************/ **********************************************************************/
$('input[name="form.button.TransmitComment"]').on('click', function () { $('input[name="form.button.TransmitComment"]').on("click", function() {
var trigger = this; var trigger = this;
var form = $(this).parents('form'); var form = $(this).parents("form");
var data = $(form).serialize(); var data = $(form).serialize();
var form_url = $(form).attr('action'); var form_url = $(form).attr("action");
$.ajax({ $.ajax({
type: 'GET', type: "GET",
url: form_url, url: form_url,
data: data, data: data,
context: trigger, context: trigger,
success: function (msg) { // jshint ignore:line success: function(msg) {
// jshint ignore:line
// remove button (trigger object can't be directly removed) // remove button (trigger object can't be directly removed)
form.find('input[name="form.button.TransmitComment"]').remove(); form.find('input[name="form.button.TransmitComment"]').remove();
form.parents('.state-pending').toggleClass('state-pending').toggleClass('state-published'); form
.parents(".state-pending")
.toggleClass("state-pending")
.toggleClass("state-published");
}, },
error: function (msg) { // jshint ignore:line error: function(msg) {
// jshint ignore:line
return true; return true;
} }
}); });
@ -189,79 +198,83 @@ require([ // jshint ignore:line
/********************************************************************** /**********************************************************************
* Edit a comment * Edit a comment
**********************************************************************/ **********************************************************************/
if($.fn.prepOverlay){ if ($.fn.prepOverlay) {
$('form[name="edit"]').prepOverlay({ $('form[name="edit"]').prepOverlay({
cssclass: 'overlay-edit-comment', cssclass: "overlay-edit-comment",
width: '60%', width: "60%",
subtype: 'ajax', subtype: "ajax",
filter: '#content>*' filter: "#content>*"
}); });
} }
/********************************************************************** /**********************************************************************
* Delete a comment and its answers. * Delete a comment and its answers.
**********************************************************************/ **********************************************************************/
$('input[name="form.button.DeleteCommentComment"]').on('click', function () { $('input[name="form.button.DeleteComment"]').on("click", function() {
var trigger = this; var trigger = this;
var form = $(this).parents('form'); var form = $(this).parents("form");
var data = $(form).serialize(); var data = $(form).serialize();
var form_url = $(form).attr('action'); var form_url = $(form).attr("action");
$.ajax({ $.ajax({
type: 'POST', type: "POST",
url: form_url, url: form_url,
data: data, data: data,
context: $(trigger).parents('.comment'), context: $(trigger).parents(".comment"),
success: function (data) { // jshint ignore:line success: function(data) {
// jshint ignore:line
var comment = $(this); var comment = $(this);
var clss = comment.attr('class'); var clss = comment.attr("class");
// remove replies // remove replies
var treelevel = parseInt(clss[clss.indexOf('replyTreeLevel') + 'replyTreeLevel'.length], 10); var treelevel = parseInt(
clss[clss.indexOf("replyTreeLevel") + "replyTreeLevel".length],
10
);
// selector for all the following elements of lower level // selector for all the following elements of lower level
var selector = '.replyTreeLevel' + treelevel; var selector = ".replyTreeLevel" + treelevel;
for (var i = 0; i < treelevel; i++) { for (var i = 0; i < treelevel; i++) {
selector += ', .replyTreeLevel' + i; selector += ", .replyTreeLevel" + i;
} }
comment.nextUntil(selector).each(function () { comment.nextUntil(selector).each(function() {
$(this).fadeOut('fast', function () { $(this).fadeOut("fast", function() {
$(this).remove(); $(this).remove();
}); });
}); });
// Add delete button to the parent // Add delete button to the parent
var parent = comment.prev('[class*="replyTreeLevel' + (treelevel - 1) + '"]'); var parent = comment.prev(
parent.find('form[name="delete"]').css('display', 'inline'); '[class*="replyTreeLevel' + (treelevel - 1) + '"]'
);
parent.find('form[name="delete"]').css("display", "inline");
// remove comment // remove comment
$(this).fadeOut('fast', function () { $(this).fadeOut("fast", function() {
$(this).remove(); $(this).remove();
}); });
}, },
error: function (req, error) { // jshint ignore:line error: function(req, error) {
// jshint ignore:line
return true; return true;
} }
}); });
return false; return false;
}); });
/********************************************************************** /**********************************************************************
* By default, hide the reply and the cancel button for the regular add * By default, hide the reply and the cancel button for the regular add
* comment form. * comment form.
**********************************************************************/ **********************************************************************/
$('.reply').find('input[name="form.buttons.reply"]') $(".reply")
.css('display', 'none'); .find('input[name="form.buttons.reply"]')
$('.reply').find('input[name="form.buttons.cancel"]') .css("display", "none");
.css('display', 'none'); $(".reply")
.find('input[name="form.buttons.cancel"]')
.css("display", "none");
/********************************************************************** /**********************************************************************
* By default, show the reply button only when Javascript is enabled. * By default, show the reply button only when Javascript is enabled.
* Otherwise hide it, since the reply functions only work with JS * Otherwise hide it, since the reply functions only work with JS
* enabled. * enabled.
**********************************************************************/ **********************************************************************/
$('.reply-to-comment-button').removeClass('hide'); $(".reply-to-comment-button").removeClass("hide");
}); });
//#JSCOVERAGE_ENDIF //#JSCOVERAGE_ENDIF
}); });

View File

@ -285,7 +285,7 @@ Open the edit comment view
Change and save the comment Change and save the comment
>>> ctrl.value = 'Comment from admin / was edited' >>> ctrl.value = 'Comment from admin / was edited'
>>> browser.getControl('Edit comment').click() >>> browser.getControl('Save').click()
This used to trigger permissions problems in some portlet configurations. This used to trigger permissions problems in some portlet configurations.
Check it ain't so. Check it ain't so.