diff options
11 files changed, 766 insertions, 0 deletions
diff --git a/extensions/EditComments/Config.pm b/extensions/EditComments/Config.pm new file mode 100644 index 000000000..dae675001 --- /dev/null +++ b/extensions/EditComments/Config.pm @@ -0,0 +1,19 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Extension::EditComments; + +use 5.10.1; +use strict; + +use constant NAME => 'EditComments'; + +use constant REQUIRED_MODULES => []; + +use constant OPTIONAL_MODULES => []; + +__PACKAGE__->NAME; diff --git a/extensions/EditComments/Extension.pm b/extensions/EditComments/Extension.pm new file mode 100644 index 000000000..7396c6f28 --- /dev/null +++ b/extensions/EditComments/Extension.pm @@ -0,0 +1,262 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Extension::EditComments; + +use 5.10.1; +use strict; + +use base qw(Bugzilla::Extension); + +use Bugzilla::Bug; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Config::Common; +use Bugzilla::Config::GroupSecurity; +use Bugzilla::WebService::Bug; +use Bugzilla::WebService::Util qw(filter_wants); + +our $VERSION = '0.01'; + +################ +# Installation # +################ + +sub db_schema_abstract_schema { + my ($self, $args) = @_; + my $schema = $args->{schema}; + + $schema->{'longdescs_activity'} = { + FIELDS => [ + comment_id => {TYPE => 'INT', NOTNULL => 1, + REFERENCES => {TABLE => 'longdescs', + COLUMN => 'comment_id', + DELETE => 'CASCADE'}}, + who => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, + change_when => {TYPE => 'DATETIME', NOTNULL => 1}, + old_comment => {TYPE => 'LONGTEXT', NOTNULL => 1}, + ], + INDEXES => [ + longdescs_activity_comment_id_idx => ['comment_id'], + longdescs_activity_change_when_idx => ['change_when'], + longdescs_activity_comment_id_change_when_idx => [qw(comment_id change_when)], + ], + }; +} + +sub install_update_db { + my $dbh = Bugzilla->dbh; + $dbh->bz_add_column('longdescs', 'edit_count', { TYPE => 'INT3', DEFAULT => 0 }); +} + +#################### +# Template Methods # +#################### + +sub page_before_template { + my ($self, $args) = @_; + + return if $args->{'page_id'} ne 'editcomments.html'; + + my $vars = $args->{'vars'}; + my $user = Bugzilla->user; + my $params = Bugzilla->input_params; + + # validate group membership + my $edit_comments_group = Bugzilla->params->{edit_comments_group}; + if (!$edit_comments_group || !$user->in_group($edit_comments_group)) { + ThrowUserError('auth_failure', { group => $edit_comments_group, + action => 'view', + object => 'editcomments' }); + } + + my $bug_id = $params->{bug_id}; + my $bug = Bugzilla::Bug->check($bug_id); + + my $comment_id = $params->{comment_id}; + + my ($comment) = grep($_->id == $comment_id, @{ $bug->comments }); + if (!$comment + || ($comment->is_private && !$user->is_insider)) + { + ThrowUserError("edit_comment_invalid_comment_id", { comment_id => $comment_id }); + } + + $vars->{'bug'} = $bug; + $vars->{'comment'} = $comment; +} + +################## +# Object Methods # +################## + +BEGIN { + no warnings 'redefine'; + *Bugzilla::Comment::activity = \&_get_activity; + *Bugzilla::Comment::edit_count = \&_edit_count; + *Bugzilla::WebService::Bug::_super_translate_comment = \&Bugzilla::WebService::Bug::_translate_comment; + *Bugzilla::WebService::Bug::_translate_comment = \&_new_translate_comment; +} + +sub _new_translate_comment { + my ($self, $comment, $filters) = @_; + + my $comment_hash = $self->_super_translate_comment($comment, $filters); + + if (filter_wants $filters, 'raw_text') { + $comment_hash->{raw_text} = $self->type('string', $comment->body); + } + + return $comment_hash; +} + +sub _edit_count { return $_[0]->{'edit_count'}; } + +sub _get_activity { + my ($self, $activity_sort_order) = @_; + + return $self->{'activity'} if $self->{'activity'}; + + my $dbh = Bugzilla->dbh; + my $query = 'SELECT longdescs_activity.comment_id AS id, profiles.userid, ' . + $dbh->sql_date_format('longdescs_activity.change_when', '%Y.%m.%d %H:%i:%s') . ' + AS time, longdescs_activity.old_comment AS old + FROM longdescs_activity + INNER JOIN profiles + ON profiles.userid = longdescs_activity.who + WHERE longdescs_activity.comment_id = ?'; + $query .= " ORDER BY longdescs_activity.change_when DESC"; + my $sth = $dbh->prepare($query); + $sth->execute($self->id); + + # We are shifting each comment activity body 1 back. The reason this + # has to be done is that the longdescs_activity table stores the comment + # body that the comment was before the edit, not the actual new version + # of the comment. + my @activity; + my $new_comment; + my $last_old_comment; + my $count = 0; + while (my $change_ref = $sth->fetchrow_hashref()) { + my %change = %$change_ref; + $change{'author'} = Bugzilla::User->new({ id => $change{'userid'}, cache => 1 }); + if ($count == 0) { + $change{new} = $self->body; + } + else { + $change{new} = $new_comment; + } + $new_comment = $change{old}; + $last_old_comment = $change{old}; + push (@activity, \%change); + $count++; + } + + return [] if !@activity; + + # Store the original comment as the first or last entry + # depending on sort order + push(@activity, { + author => $self->author, + body => $last_old_comment, + time => $self->creation_ts, + original => 1 + }); + + $activity_sort_order + ||= Bugzilla->user->settings->{'comment_sort_order'}->{'value'}; + + if ($activity_sort_order eq "oldest_to_newest") { + @activity = reverse @activity; + } + + $self->{'activity'} = \@activity; + + return $self->{'activity'}; +} + +######### +# Hooks # +######### + +sub object_columns { + my ($self, $args) = @_; + my ($class, $columns) = @$args{qw(class columns)}; + if ($class->isa('Bugzilla::Comment')) { + push(@$columns, 'edit_count'); + } +} + +sub bug_end_of_update { + my ($self, $args) = @_; + + # Silently return if not in the proper group + # or if editing comments is disabled + my $user = Bugzilla->user; + my $edit_comments_group = Bugzilla->params->{edit_comments_group}; + return if (!$edit_comments_group || !$user->in_group($edit_comments_group)); + + my $bug = $args->{bug}; + my $timestamp = $args->{timestamp}; + my $params = Bugzilla->input_params; + my $dbh = Bugzilla->dbh; + + my $updated = 0; + foreach my $param (grep(/^edit_comment_textarea_/, keys %$params)) { + my ($comment_id) = $param =~ /edit_comment_textarea_(\d+)$/; + next if !detaint_natural($comment_id); + + # The comment ID must belong to this bug. + my ($comment_obj) = grep($_->id == $comment_id, @{ $bug->comments}); + next if (!$comment_obj || ($comment_obj->is_private && !$user->is_insider)); + + my $new_comment = $comment_obj->_check_thetext($params->{$param}); + + my $old_comment = $comment_obj->body; + next if $old_comment eq $new_comment; + + trick_taint($new_comment); + $dbh->do("UPDATE longdescs SET thetext = ?, edit_count = edit_count + 1 + WHERE comment_id = ?", + undef, $new_comment, $comment_id); + + # Log old comment to the longdescs activity table + $timestamp ||= $dbh->selectrow_array("SELECT NOW()"); + $dbh->do("INSERT INTO longdescs_activity " . + "(comment_id, who, change_when, old_comment) " . + "VALUES (?, ?, ?, ?)", + undef, ($comment_id, $user->id, $timestamp, $old_comment)); + + $comment_obj->{thetext} = $new_comment; + + $updated = 1; + } + + $bug->_sync_fulltext( update_comments => 1 ) if $updated; +} + +sub config_modify_panels { + my ($self, $args) = @_; + push @{ $args->{panels}->{groupsecurity}->{params} }, { + name => 'edit_comments_group', + type => 's', + choices => \&Bugzilla::Config::GroupSecurity::_get_all_group_names, + default => 'admin', + checker => \&check_group + }; +} + +sub webservice { + my ($self, $args) = @_; + my $dispatch = $args->{dispatch}; + $dispatch->{EditComments} = "Bugzilla::Extension::EditComments::WebService"; +} + +__PACKAGE__->NAME; diff --git a/extensions/EditComments/lib/WebService.pm b/extensions/EditComments/lib/WebService.pm new file mode 100644 index 000000000..9213f0407 --- /dev/null +++ b/extensions/EditComments/lib/WebService.pm @@ -0,0 +1,170 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Extension::EditComments::WebService; + +use strict; +use warnings; + +use base qw(Bugzilla::WebService); + +use Bugzilla::Error; +use Bugzilla::Util qw(trim); +use Bugzilla::WebService::Util qw(validate); + +sub comments { + my ($self, $params) = validate(@_, 'comment_ids'); + my $dbh = Bugzilla->switch_to_shadow_db(); + my $user = Bugzilla->user; + + if (!defined $params->{comment_ids}) { + ThrowCodeError('param_required', + { function => 'Bug.comments', + param => 'comment_ids' }); + } + + my @ids = map { trim($_) } @{ $params->{comment_ids} || [] }; + my $comment_data = Bugzilla::Comment->new_from_list(\@ids); + + # See if we were passed any invalid comment ids. + my %got_ids = map { $_->id => 1 } @$comment_data; + foreach my $comment_id (@ids) { + if (!$got_ids{$comment_id}) { + ThrowUserError('comment_id_invalid', { id => $comment_id }); + } + } + + # Now make sure that we can see all the associated bugs. + my %got_bug_ids = map { $_->bug_id => 1 } @$comment_data; + $user->visible_bugs([ keys %got_bug_ids ]); # preload cache for visibility check + Bugzilla::Bug->check($_) foreach (keys %got_bug_ids); + + my %comments; + foreach my $comment (@$comment_data) { + if ($comment->is_private && !$user->is_insider) { + ThrowUserError('comment_is_private', { id => $comment->id }); + } + $comments{$comment->id} = $comment->body; + } + + return { comments => \%comments }; +} + +sub rest_resources { + return [ + qr{^/editcomments/comment/(\d+)$}, { + GET => { + method => 'comments', + params => sub { + return { comment_ids => $_[0] }; + }, + }, + }, + qr{^/editcomments/comment$}, { + GET => { + method => 'comments', + }, + }, + ]; +}; + + +1; + +__END__ + +=head1 NAME + +Bugzilla::Extension::EditComments::Webservice - The EditComments WebServices API + +=head1 DESCRIPTION + +This module contains API methods that are useful to user's of bugzilla.mozilla.org. + +=head1 METHODS + +=head2 comments + +B<EXPERIMENTAL> + +=over + +=item B<Description> + +This allows you to get the raw comment text about comments, given a list of comment ids. + +=item B<REST> + +To get all comment text for a list of comment ids: + +GET /bug/editcomments/comment?comment_ids=1234&comment_ids=5678... + +To get comment text for a specific comment based on the comment ID: + +GET /bug/editcomments/comment/<comment_id> + +The returned data format is the same as below. + +=item B<Params> + +=over + +=item C<comment_ids> (required) + +C<array> An array of integer comment_ids. These comments will be +returned individually, separate from any other comments in their +respective bugs. + +=item B<Returns> + +1 item is returned: + +=over + +=item C<comments> + +Each individual comment requested in C<comment_ids> is returned here, +in a hash where the numeric comment id is the key, and the value +is the comment's raw text. + +=back + +=item B<Errors> + +In addition to standard Bug.get type errors, this method can throw the +following additional errors: + +=over + +=item 110 (Comment Is Private) + +You specified the id of a private comment in the C<comment_ids> +argument, and you are not in the "insider group" that can see +private comments. + +=item 111 (Invalid Comment ID) + +You specified an id in the C<comment_ids> argument that is invalid--either +you specified something that wasn't a number, or there is no comment with +that id. + +=back + +=item B<History> + +=over + +=item Added in BMO Bugzilla B<4.2>. + +=back + +=back + +=back + +See L<Bugzilla::WebService> for a description of how parameters are passed, +and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean. diff --git a/extensions/EditComments/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl b/extensions/EditComments/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl new file mode 100644 index 000000000..01ca7bbb7 --- /dev/null +++ b/extensions/EditComments/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl @@ -0,0 +1,13 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% IF panel.name == "groupsecurity" %] + [% panel.param_descs.edit_comments_group = + 'The name of the group of users who can edit comments. Leave blank to disable comment editing.' + %] +[% END -%] diff --git a/extensions/EditComments/template/en/default/hook/bug/comments-a_comment-end.html.tmpl b/extensions/EditComments/template/en/default/hook/bug/comments-a_comment-end.html.tmpl new file mode 100644 index 000000000..28482c6c3 --- /dev/null +++ b/extensions/EditComments/template/en/default/hook/bug/comments-a_comment-end.html.tmpl @@ -0,0 +1,45 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% IF Param('edit_comments_group') && user.in_group(Param('edit_comments_group')) %] + <span id="edit_comment_link_[% comment.count FILTER html %]"> + [<a href="javascript:void(0);" id="edit_comment_edit_link_[% comment.count FILTER html %]" + onclick="editComment('[% comment.count FILTER js %]','[% comment.id FILTER js %]');">edit</a> + [% IF comment.edit_count %] + | <a href="page.cgi?id=editcomments.html&bug_id=[% bug.id FILTER uri %]&comment_id=[% comment.id FILTER uri %]">history</a> + ([% comment.edit_count FILTER html %]) + [% END %]] + </span> + <div id="edit_comment_[% comment.count FILTER html %]"> + <div class="bz_comment_text bz_default_hidden" id="edit_comment_loading_[% comment.count FILTER html %]">Loading...</div> + [% INCLUDE global/textarea.html.tmpl + name = "edit_comment_textarea_${comment.id}" + id = "edit_comment_textarea_${comment.count}" + minrows = 10 + maxrows = 25 + classes = "edit_comment_textarea bz_default_hidden" + cols = constants.COMMENT_COLS + disabled = 1 + %] + </div> + <script> + YAHOO.util.Event.onDOMReady(function() { + // Insert edit links near other comment actions such as reply + var comment_div = YAHOO.util.Dom.get('c[% comment.count FILTER js %]'); + var bz_comment_actions = YAHOO.util.Dom.getElementsByClassName('bz_comment_actions', 'span', comment_div)[0]; + var edit_comment_link = YAHOO.util.Dom.get('edit_comment_link_[% comment.count FILTER js %]'); + bz_comment_actions.insertBefore(edit_comment_link, bz_comment_actions.firstChild); + + // Insert blank textarea right below formatted comment + var comment_div = YAHOO.util.Dom.get('c[% comment.count FILTER js %]'); + var comment_pre = YAHOO.util.Dom.get('comment_text_[% comment.count FILTER js %]'); + var edit_comment_div = YAHOO.util.Dom.get('edit_comment_[% comment.count FILTER js %]'); + comment_div.insertBefore(edit_comment_div, comment_pre); + }); + </script> +[% END %] diff --git a/extensions/EditComments/template/en/default/hook/bug/show-header-end.html.tmpl b/extensions/EditComments/template/en/default/hook/bug/show-header-end.html.tmpl new file mode 100644 index 000000000..331d7e6df --- /dev/null +++ b/extensions/EditComments/template/en/default/hook/bug/show-header-end.html.tmpl @@ -0,0 +1,12 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% IF Param('edit_comments_group') && user.in_group(Param('edit_comments_group')) %] + [% style_urls.push('extensions/EditComments/web/styles/editcomments.css') %] + [% javascript_urls.push('extensions/EditComments/web/js/editcomments.js') %] +[% END %] diff --git a/extensions/EditComments/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl b/extensions/EditComments/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl new file mode 100644 index 000000000..4325aab30 --- /dev/null +++ b/extensions/EditComments/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl @@ -0,0 +1,11 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% IF object == 'editcomments' %] + edit comments +[% END %] diff --git a/extensions/EditComments/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/EditComments/template/en/default/hook/global/user-error-errors.html.tmpl new file mode 100644 index 000000000..bc02b52f0 --- /dev/null +++ b/extensions/EditComments/template/en/default/hook/global/user-error-errors.html.tmpl @@ -0,0 +1,12 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% IF error == "edit_comment_invalid_comment_id" %] + [% title = "Invalid Comment ID" %] + The comment id '[% comment_id FILTER html %]' is invalid. +[% END %] diff --git a/extensions/EditComments/template/en/default/pages/editcomments.html.tmpl b/extensions/EditComments/template/en/default/pages/editcomments.html.tmpl new file mode 100644 index 000000000..8b3b90c9e --- /dev/null +++ b/extensions/EditComments/template/en/default/pages/editcomments.html.tmpl @@ -0,0 +1,122 @@ +[%# This Source Code Form is subject to the terms of the Mozilla Public + # License, v. 2.0. If a copy of the MPL was not distributed with this + # file, You can obtain one at http://mozilla.org/MPL/2.0/. + # + # This Source Code Form is "Incompatible With Secondary Licenses", as + # defined by the Mozilla Public License, v. 2.0. + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% PROCESS global/header.html.tmpl + title = "Comment changes made to $terms.bug $bug.id, comment $comment.id" + header = "Activity log for $terms.bug $bug.id, comment $comment.id" + %] + +<script type="text/javascript"> +/* The functions below expand and collapse comments */ +function toggle_comment_display(link, comment_id) { + if (YAHOO.util.Dom.hasClass('comment_text_' + comment_id, 'collapsed')) { + expand_comment(link, comment); + } + else { + collapse_comment(link, comment); + } +} + +function toggle_all_comments(action) { + var num_comments = [% comment.activity.size FILTER html %]; + + // If for some given ID the comment doesn't exist, this doesn't mean + // there are no more comments, but that the comment is private and + // the user is not allowed to view it. + + for (var id = 0; id < num_comments; id++) { + var comment = document.getElementById('comment_text_' + id); + if (!comment) { + continue; + } + + var link = document.getElementById('comment_link_' + id); + if (action == 'collapse') { + collapse_comment(link, comment); + } + else { + expand_comment(link, comment); + } + } +} + +function collapse_comment(link, comment) { + link.innerHTML = "[+]"; + link.title = "Expand the comment."; + YAHOO.util.Dom.addClass(comment, 'collapsed'); +} + +function expand_comment(link, comment) { + link.innerHTML = "[-]"; + link.title = "Collapse the comment"; + YAHOO.util.Dom.removeClass(comment, 'collapsed'); +} +</script> + +<p> + [% "Back to $terms.bug $bug.id" FILTER bug_link(bug.id) FILTER none %] +</p> + +<p> + <strong>Note</strong>: The actual edited comment in the [% terms.bug %] view page will always show the original commentor's name and original timestamp. +</p> + +<p> + <a href="#" onclick="toggle_all_comments('collapse'); return false;">Collapse All Changes</a> - + <a href="#" onclick="toggle_all_comments('expand'); return false;">Expand All Changes</a> +</p> + +[% count = 0 %] +[% FOREACH a = comment.activity %] + <div class="bz_comment"> + <div class="bz_comment_head"> + <i> + [% IF a.original %] + Original comment by [% (a.author.name || "Need Real Name") FILTER html %] + <span class="vcard"> + (<a class="fn email" href="mailto:[% a.author.email FILTER html %]"> + [%- a.author.email FILTER html -%]</a>) + </span> + on [%+ a.time FILTER time %] + [% ELSE %] + Revision by [% (a.author.name || "Need Real Name") FILTER html %] + <span class="vcard"> + (<a class="fn email" href="mailto:[% a.author.email FILTER html %]"> + [%- a.author.email FILTER html -%]</a>) + </span> + on [%+ a.time FILTER time %] + [% END %] + </i> + <a href="#" id="comment_link_[% count FILTER html %]" + onclick="toggle_comment_display(this, '[% count FILTER html FILTER js %]'); return false;" + title="Collapse the comment.">[-]</a> + </div> + [% IF a.original %] + [% wrapped_comment = a.body FILTER wrap_comment %] + [% ELSE %] + [% wrapped_comment = a.new FILTER wrap_comment %] + [% END %] +[%# Don't indent the <pre> block, since then the spaces are displayed in the + # generated HTML %] +<pre class="bz_comment_text" id="comment_text_[% count FILTER html %]"> + [%- wrapped_comment FILTER quoteUrls(bug) -%] +</pre> + </div> + [% count = count + 1 %] +[% END %] + +[% IF comment.activity.size > 0 %] + <p> + [% "Back to $terms.bug $bug.id" FILTER bug_link(bug.id) FILTER none %] + </p> +[% END %] + +[% PROCESS global/footer.html.tmpl %] + diff --git a/extensions/EditComments/web/js/editcomments.js b/extensions/EditComments/web/js/editcomments.js new file mode 100644 index 000000000..91763fa62 --- /dev/null +++ b/extensions/EditComments/web/js/editcomments.js @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. + */ + +function editComment(comment_count, comment_id) { + if (!comment_count || !comment_id) return; + + var edit_comment_textarea = YAHOO.util.Dom.get('edit_comment_textarea_' + comment_count); + if (!YAHOO.util.Dom.hasClass(edit_comment_textarea, 'bz_default_hidden')) { + hideEditCommentField(comment_count); + return; + } + + // Show the loading indicator + toggleCommentLoading(comment_count); + + YAHOO.util.Connect.setDefaultPostHeader('application/json', true); + YAHOO.util.Connect.asyncRequest( + 'POST', + 'jsonrpc.cgi', + { + success: function(res) { + // Hide the loading indicator + toggleCommentLoading(comment_count); + data = YAHOO.lang.JSON.parse(res.responseText); + if (data.error) { + alert("Get [% comment failed: " + data.error.message); + } + else if (data.result.comments[comment_id]) { + var comment_text = data.result.comments[comment_id]; + showEditCommentField(comment_count, comment_text); + } + }, + failure: function(res) { + // Hide the loading indicator + toggleCommentLoading(comment_count); + if (res.responseText) { + alert("Get comment failed: " + res.responseText); + } + } + }, + YAHOO.lang.JSON.stringify({ + version: "1.1", + method: "EditComments.comments", + id: comment_id, + params: { comment_ids: [ comment_id ] } + }) + ); +} + +function hideEditCommentField(comment_count) { + var comment_text_pre = YAHOO.util.Dom.get('comment_text_' + comment_count); + YAHOO.util.Dom.removeClass(comment_text_pre, 'bz_default_hidden'); + + var edit_comment_textarea = YAHOO.util.Dom.get('edit_comment_textarea_' + comment_count); + YAHOO.util.Dom.addClass(edit_comment_textarea, 'bz_default_hidden'); + edit_comment_textarea.disabled = true; + + YAHOO.util.Dom.get("edit_comment_edit_link_" + comment_count).innerHTML = "edit"; +} + +function showEditCommentField(comment_count, comment_text) { + var comment_text_pre = YAHOO.util.Dom.get('comment_text_' + comment_count); + YAHOO.util.Dom.addClass(comment_text_pre, 'bz_default_hidden'); + + var edit_comment_textarea = YAHOO.util.Dom.get('edit_comment_textarea_' + comment_count); + YAHOO.util.Dom.removeClass(edit_comment_textarea, 'bz_default_hidden'); + edit_comment_textarea.disabled = false; + edit_comment_textarea.value = comment_text; + + YAHOO.util.Dom.get("edit_comment_edit_link_" + comment_count).innerHTML = "unedit"; +} + +function toggleCommentLoading(comment_count, hide) { + var comment_div = 'comment_text_' + comment_count; + var loading_div = 'edit_comment_loading_' + comment_count; + if (YAHOO.util.Dom.hasClass(loading_div, 'bz_default_hidden')) { + YAHOO.util.Dom.addClass(comment_div, 'bz_default_hidden'); + YAHOO.util.Dom.removeClass(loading_div, 'bz_default_hidden'); + } + else { + YAHOO.util.Dom.removeClass(comment_div, 'bz_default_hidden'); + YAHOO.util.Dom.addClass(loading_div, 'bz_default_hidden'); + } +} + diff --git a/extensions/EditComments/web/styles/editcomments.css b/extensions/EditComments/web/styles/editcomments.css new file mode 100644 index 000000000..911896ac8 --- /dev/null +++ b/extensions/EditComments/web/styles/editcomments.css @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. */ + +.edit_comment_textarea { + width: 845px; +} |