1 ###############################################################################
\r
2 # Chirpy! 0.3, a quote management system #
\r
3 # Copyright (C) 2005-2007 Tim De Pauw <ceetee@users.sourceforge.net> #
\r
4 ###############################################################################
\r
5 # This program is free software; you can redistribute it and/or modify it #
\r
6 # under the terms of the GNU General Public License as published by the Free #
\r
7 # Software Foundation; either version 2 of the License, or (at your option) #
\r
8 # any later version. #
\r
10 # This program is distributed in the hope that it will be useful, but WITHOUT #
\r
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
\r
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
\r
15 # You should have received a copy of the GNU General Public License along #
\r
16 # with this program; if not, write to the Free Software Foundation, Inc., 51 #
\r
17 # Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #
\r
18 ###############################################################################
\r
20 ###############################################################################
\r
21 # $Id:: Administration.pm 305 2007-02-09 01:06:56Z ceetee $ #
\r
22 ###############################################################################
\r
26 Chirpy::UI::WebApp::Administration - Administration-section related routines
\r
27 of L<Chirpy::UI::WebApp>
\r
31 Make this template-based and avoid inline calls to parents.
\r
35 Tim De Pauw E<lt>ceetee@users.sourceforge.netE<gt>
\r
39 L<Chirpy::UI::WebApp>, L<Chirpy::UI>, L<Chirpy>,
\r
40 L<http://chirpy.sourceforge.net/>
\r
44 Copyright 2005-2007 Tim De Pauw. All rights reserved.
\r
46 This program is free software; you can redistribute it and/or modify it under
\r
47 the terms of the GNU General Public License as published by the Free Software
\r
48 Foundation; either version 2 of the License, or (at your option) any later
\r
51 This program is distributed in the hope that it will be useful, but WITHOUT ANY
\r
52 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
\r
53 PARTICULAR PURPOSE. See the GNU General Public License for more details.
\r
57 package Chirpy::UI::WebApp::Administration;
\r
62 use vars qw($VERSION);
\r
67 use Chirpy::UI::WebApp 0.3;
\r
68 use Chirpy::Event 0.3;
\r
71 my ($class, $parent) = @_;
\r
72 return bless { 'parent' => $parent }, $class;
\r
77 return $self->{'parent'};
\r
81 my ($self, %params) = @_;
\r
82 my $event_log_allowed = $self->parent()->administration_allowed(
\r
83 Chirpy::UI::VIEW_EVENT_LOG);
\r
84 if ($event_log_allowed && $self->parent()->_wants_xml()) {
\r
85 $self->_serve_event_log_table_data();
\r
88 my $template = $self->parent()->_load_template('administration');
\r
89 my $locale = $self->parent()->locale();
\r
90 my ($upd_url, $upd_text);
\r
91 if (my $update = $self->parent()->{'available_update'}) {
\r
92 $template->param('UPDATE_AVAILABLE'
\r
93 => &_text_to_xhtml($locale->get_string('update_available')));
\r
94 $template->param('UPDATE_AVAILABLE_TEXT' => &_text_to_xhtml(
\r
95 $locale->get_string('update_available_text',
\r
96 $update->[0], $update->[1])));
\r
97 $template->param('UPDATE_LINK_TEXT'
\r
98 => &_text_to_xhtml($locale->get_string('update_link_text')));
\r
99 $template->param('UPDATE_URL' => &_text_to_xhtml($update->[2]));
\r
101 elsif (my $errmsg = $self->parent()->{'update_check_error'}) {
\r
102 $template->param('UPDATE_CHECK_FAILED'
\r
103 => &_text_to_xhtml($locale->get_string('update_check_failed')));
\r
104 $template->param('UPDATE_CHECK_FAILED_TEXT' => &_text_to_xhtml(
\r
105 $locale->get_string('update_check_failed_text')));
\r
106 $template->param('UPDATE_CHECK_ERROR_MESSAGE'
\r
107 => &_text_to_xhtml($errmsg));
\r
110 'PAGE_TITLE' => &_text_to_xhtml(
\r
111 $locale->get_string('administration')),
\r
112 'APPROVE_QUOTES' => &_text_to_xhtml(
\r
113 $locale->get_string('approve_quotes')),
\r
114 'APPROVE_QUOTES_HTML'
\r
115 => sub { return $self->get_approve_quotes_html(%params) },
\r
116 'APPROVE_QUOTES_ALLOWED'
\r
117 => $self->parent()->administration_allowed(Chirpy::UI::MANAGE_UNAPPROVED_QUOTES),
\r
118 'APPROVE_QUOTES_NOT_ALLOWED_HTML'
\r
119 => sub { return $self->get_access_disallowed_html() },
\r
120 'FLAGGED_QUOTES' => &_text_to_xhtml(
\r
121 $locale->get_string('flagged_quotes')),
\r
122 'FLAGGED_QUOTES_HTML'
\r
123 => sub { return $self->get_flagged_quotes_html(%params) },
\r
124 'FLAGGED_QUOTES_ALLOWED'
\r
125 => $self->parent()->administration_allowed(Chirpy::UI::MANAGE_FLAGGED_QUOTES),
\r
126 'FLAGGED_QUOTES_NOT_ALLOWED_HTML'
\r
127 => sub { return $self->get_access_disallowed_html() },
\r
128 'MANAGE_QUOTES' => &_text_to_xhtml(
\r
129 $locale->get_string('manage_quotes')),
\r
130 'MANAGE_QUOTES_HTML'
\r
131 => sub { return $self->get_manage_quotes_html(%params) },
\r
132 'MANAGE_QUOTES_ALLOWED'
\r
133 => $self->parent()->administration_allowed(Chirpy::UI::EDIT_QUOTE)
\r
134 && $self->parent()->administration_allowed(Chirpy::UI::REMOVE_QUOTE),
\r
135 'MANAGE_QUOTES_NOT_ALLOWED_HTML'
\r
136 => sub { return $self->get_access_disallowed_html() },
\r
137 'MANAGE_NEWS' => &_text_to_xhtml(
\r
138 $locale->get_string('manage_news')),
\r
140 => sub { return $self->get_manage_news_html(%params) },
\r
141 'MANAGE_NEWS_ALLOWED'
\r
142 => $self->parent()->administration_allowed(Chirpy::UI::ADD_NEWS)
\r
143 && $self->parent()->administration_allowed(Chirpy::UI::EDIT_NEWS)
\r
144 && $self->parent()->administration_allowed(Chirpy::UI::REMOVE_NEWS),
\r
145 'MANAGE_NEWS_NOT_ALLOWED_HTML'
\r
146 => sub { return $self->get_access_disallowed_html() },
\r
147 'MANAGE_ACCOUNTS' => &_text_to_xhtml(
\r
148 $locale->get_string('manage_accounts')),
\r
149 'MANAGE_ACCOUNTS_HTML'
\r
150 => sub { return $self->get_manage_accounts_html(%params) },
\r
151 'MANAGE_ACCOUNTS_ALLOWED'
\r
152 => $self->parent()->administration_allowed(Chirpy::UI::ADD_ACCOUNT)
\r
153 && $self->parent()->administration_allowed(Chirpy::UI::EDIT_ACCOUNT)
\r
154 && $self->parent()->administration_allowed(Chirpy::UI::REMOVE_ACCOUNT),
\r
155 'MANAGE_ACCOUNTS_NOT_ALLOWED_HTML'
\r
156 => sub { return $self->get_access_disallowed_html() },
\r
157 'VIEW_EVENT_LOG' => &_text_to_xhtml(
\r
158 $locale->get_string('view_event_log')),
\r
159 'VIEW_EVENT_LOG_HTML'
\r
160 => sub { return $self->get_event_log_html(%params) },
\r
161 'VIEW_EVENT_LOG_ALLOWED'
\r
162 => $event_log_allowed,
\r
163 'VIEW_EVENT_LOG_NOT_ALLOWED_HTML'
\r
164 => sub { return $self->get_access_disallowed_html() },
\r
165 'CHANGE_PASSWORD' => &_text_to_xhtml(
\r
166 $locale->get_string('change_password')),
\r
167 'CHANGE_PASSWORD_HTML'
\r
168 => sub { return $self->get_change_password_html(%params) },
\r
169 'ACTION_IS_' . $self->parent()->_admin_action() => 1
\r
171 $self->parent()->_output_template($template);
\r
174 sub get_approve_quotes_html {
\r
175 my ($self, %params) = @_;
\r
176 my $locale = $self->parent()->locale();
\r
177 my $quotes = $self->parent()->parent()->get_unapproved_quotes();
\r
178 if (defined $quotes) {
\r
179 my $html = '<script type="text/javascript" src="'
\r
180 . &_text_to_xhtml($self->parent()->_resources_url())
\r
181 . '/js/administration.js"></script>' . $/
\r
182 . '<form method="post" action="'
\r
183 . $self->parent()->_url(
\r
184 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'MANAGE_UNAPPROVED_QUOTES'},
\r
187 . '<ul id="unapproved-quotes-list" class="quote-list">' . $/;
\r
188 foreach my $quote (@$quotes) {
\r
189 my $id = $quote->get_id();
\r
190 my $notes = $quote->get_notes();
\r
191 my $tags = $quote->get_tags();
\r
192 $html .= '<li>' . $/
\r
193 . '<div class="quote-container">' . $/
\r
194 . '<h3 class="quote-header">'
\r
195 . '<span class="quote-id">#' . $quote->get_id() . '</span> '
\r
196 . ($self->parent()->moderation_queue_is_public()
\r
197 ? '<span class="quote-rating">'
\r
198 . Chirpy::Util::format_quote_rating($quote->get_rating())
\r
200 . '<span class="quote-vote-count">/<span>'
\r
201 . $quote->get_vote_count()
\r
202 . '</span></span> '
\r
204 . '<span class="quote-date">'
\r
205 . $self->parent()->format_date_time($quote->get_date_submitted())
\r
207 . '<a href="javascript:editQuote(' . $id . ');" '
\r
208 . 'class="quote-edit" id="quote-edit-' . $id . '">['
\r
209 . &_text_to_xhtml($locale->get_string('edit'))
\r
212 . '<div class="quote-data" id="quote-data-' . $id . '">' . $/
\r
213 . '<blockquote class="quote-body">' . $/
\r
214 . '<p id="quote-body-' . $id . '">' . &_text_to_xhtml($quote->get_body())
\r
216 . '</blockquote>' . $/
\r
217 . (defined $notes || @$tags ?
\r
218 '<div class="quote-footer">' . (defined $notes
\r
219 ? '<div class="quote-notes">' . $/
\r
220 . '<p><em class="quote-notes-title">Notes:</em>' . $/
\r
221 . '<span id="quote-notes-' . $id . '">'
\r
222 . &_text_to_xhtml($notes)
\r
223 . '</span></p>' . $/
\r
227 ? '<div class="quote-tags">' . $/
\r
228 . '<p><em class="quote-tags-title">'
\r
230 $locale->get_string('quote_tags_title'))
\r
232 . '<span id="quote-tags-' . $id . '">'
\r
233 . &_text_to_xhtml(join(' ', @$tags))
\r
234 . '</span></p>' . $/
\r
240 . '<div class="approval-options">' . $/
\r
241 . '<input type="radio" class="approve-do-nothing-rb" name="action_' . $id
\r
242 . '" value="0" id="a' . $id . '-0" '
\r
243 . 'checked="checked" /> <label '
\r
244 . 'for="a' . $id . '-0">'
\r
246 $locale->get_string('do_nothing'))
\r
248 . '<input type="radio" class="approve-rb" name="action_' . $id
\r
249 . '" value="1" id="a' . $id . '-1" /> <label '
\r
250 . 'for="a' . $id . '-1">'
\r
252 $locale->get_string('approve_unapproved_quote'))
\r
254 . '<input type="radio" class="discard-rb" name="action_' . $id
\r
255 . '" value="2" id="a' . $id . '-2" /> <label '
\r
256 . 'for="a' . $id . '-2">'
\r
258 $locale->get_string('discard_unapproved_quote'))
\r
263 $html .= '</ul>' . $/
\r
264 . '<div id="approve-submit-container">' . $/
\r
265 . '<div id="mass-approve-container">' . $/
\r
266 . '<input type="button" value="'
\r
268 $locale->get_string('approve_all_unapproved_quotes'))
\r
269 . '" id="approve-all-button" onclick="if (confirm(\''
\r
271 $locale->get_string('approve_all_unapproved_quotes_confirm'))
\r
272 . '\')) { var a = document.getElementsByTagName(\'input\'); '
\r
273 . 'for (var i = 0; i < a.length; i++) '
\r
274 . 'if (a[i].className == \'approve-rb\') a[i].checked = true; '
\r
275 . 'submit(); }" />' . $/
\r
276 . '<input type="button" value="'
\r
278 $locale->get_string('discard_all_unapproved_quotes'))
\r
279 . '" id="discard-all-button" onclick="if (confirm(\''
\r
281 $locale->get_string('discard_all_unapproved_quotes_confirm'))
\r
282 . '\')) { var a = document.getElementsByTagName(\'input\'); '
\r
283 . 'for (var i = 0; i < a.length; i++) '
\r
284 . 'if (a[i].className == \'discard-rb\') a[i].checked = true; '
\r
285 . 'submit(); }" />' . $/
\r
287 . '<input type="submit" value="'
\r
289 $locale->get_string('update_database'))
\r
290 . '" id="approve-submit-button" />' . $/
\r
291 . '<input type="reset" value="'
\r
293 $locale->get_string('reset_form'))
\r
294 . '" id="approve-reset-button" onclick="'
\r
295 . 'var a = document.getElementsByTagName(\'input\'); '
\r
296 . 'for (var i = 0; i < a.length; i++) '
\r
297 . 'if (a[i].className == \'approve-do-nothing-rb\') a[i].checked = true; '
\r
298 . 'return false;" />' . $/
\r
304 return '<p>' . &_text_to_xhtml(
\r
305 $locale->get_string('no_unapproved_quotes')) . '</p>';
\r
309 sub get_flagged_quotes_html {
\r
310 my ($self, %params) = @_;
\r
311 my $locale = $self->parent()->locale();
\r
312 my $quotes = $self->parent()->parent()->get_flagged_quotes();
\r
313 if (defined $quotes) {
\r
314 my $html = '<form method="post" action="'
\r
315 . $self->parent()->_url(
\r
316 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'MANAGE_FLAGGED_QUOTES'},
\r
319 . '<ul id="flagged-quotes-list" class="quote-list">' . $/;
\r
320 foreach my $quote (@$quotes) {
\r
321 my $id = $quote->get_id();
\r
322 my $notes = $quote->get_notes();
\r
323 my $tags = $quote->get_tags();
\r
324 $html .= '<li>' . $/
\r
325 . '<div class="quote-container">' . $/
\r
326 . '<h3 class="quote-header">'
\r
327 . '<span class="quote-id">#' . $quote->get_id() . '</span> '
\r
328 . '<span class="quote-rating">'
\r
329 . Chirpy::Util::format_quote_rating($quote->get_rating())
\r
331 . '<span class="quote-vote-count">/<span>'
\r
332 . $quote->get_vote_count()
\r
333 . '</span></span> '
\r
334 . '<span class="quote-date">'
\r
335 . $self->parent()->format_date_time($quote->get_date_submitted())
\r
338 . '<blockquote class="quote-body">' . $/
\r
339 . '<p>' . &_text_to_xhtml($quote->get_body())
\r
341 . '</blockquote>' . $/
\r
342 . (defined $notes || @$tags ?
\r
343 '<div class="quote-footer">' . (defined $notes
\r
344 ? '<div class="quote-notes">' . $/
\r
345 . '<p><em class="quote-notes-title">'
\r
347 $locale->get_string('quote_notes_title'))
\r
349 . &_text_to_xhtml($notes)
\r
354 ? '<div class="quote-tags">' . $/
\r
355 . '<p><em class="quote-tags-title">'
\r
357 $locale->get_string('quote_tags_title'))
\r
359 . &_text_to_xhtml(join(' ', @$tags))
\r
365 . '<div class="flag-options">' . $/
\r
366 . '<input type="radio" name="action_' . $id
\r
367 . '" value="0" id="a' . $id . '-0" '
\r
368 . 'checked="checked" /> <label '
\r
369 . 'for="a' . $id . '-0">'
\r
371 $locale->get_string('do_nothing'))
\r
373 . '<input type="radio" class="keep-rb" name="action_' . $id
\r
374 . '" value="1" id="a' . $id . '-1" /> <label '
\r
375 . 'for="a' . $id . '-1">'
\r
377 $locale->get_string('keep_flagged_quote'))
\r
379 . '<input type="radio" class="remove-rb" name="action_' . $id
\r
380 . '" value="2" id="a' . $id . '-2" /> <label '
\r
381 . 'for="a' . $id . '-2">'
\r
383 $locale->get_string('remove_flagged_quote'))
\r
388 $html .= '</ul>' . $/
\r
389 . '<div id="flag-submit-container">' . $/
\r
390 . '<div id="mass-unflag-container">' . $/
\r
391 . '<input type="button" value="'
\r
393 $locale->get_string('keep_all_flagged_quotes'))
\r
394 . '" id="keep-all-button" onclick="if (confirm(\''
\r
396 $locale->get_string('keep_all_flagged_quotes_confirm'))
\r
397 . '\')) { var a = document.getElementsByTagName(\'input\'); '
\r
398 . 'for (var i = 0; i < a.length; i++) '
\r
399 . 'if (a[i].className == \'keep-rb\') a[i].checked = true; '
\r
400 . 'submit(); }" />' . $/
\r
401 . '<input type="button" value="'
\r
403 $locale->get_string('remove_all_flagged_quotes'))
\r
404 . '" id="discard-all-button" onclick="if (confirm(\''
\r
406 $locale->get_string('remove_all_flagged_quotes_confirm'))
\r
407 . '\')) { var a = document.getElementsByTagName(\'input\'); '
\r
408 . 'for (var i = 0; i < a.length; i++) '
\r
409 . 'if (a[i].className == \'remove-rb\') a[i].checked = true; '
\r
410 . 'submit(); }" />' . $/
\r
412 . '<input type="submit" value="'
\r
414 $locale->get_string('update_database'))
\r
415 . '" id="flag-submit-button" />' . $/
\r
416 . '<input type="reset" value="'
\r
418 $locale->get_string('reset_form'))
\r
419 . '" id="flag-reset-button" />' . $/
\r
425 return '<p>' . &_text_to_xhtml(
\r
426 $locale->get_string('no_flagged_quotes')) . '</p>';
\r
430 sub get_manage_quotes_html {
\r
431 my ($self, %params) = @_;
\r
432 my $locale = $self->parent()->locale();
\r
433 if (my $quote = $params{'edit_quote'}) {
\r
434 my $notes = $quote->get_notes();
\r
435 my $tags = $quote->get_tags();
\r
436 return '<div id="edit-quote-form">' . $/
\r
437 . '<form method="post" action="'
\r
438 . $self->parent()->_url(
\r
439 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'EDIT_QUOTE'},
\r
441 'id' => $quote->get_id()
\r
442 ) . '"><div id="quote-container">' . $/
\r
443 . '<label for="quote-field">'
\r
445 $locale->get_string('submission_title'))
\r
447 . '<textarea name="quote" id="quote-field"' . $/
\r
448 . 'rows="8" cols="80">'
\r
449 . Chirpy::Util::encode_xml_entities($quote->get_body())
\r
450 . '</textarea></div>' . $/
\r
451 . '<div id="notes-container">' . $/
\r
452 . '<label for="notes-field">'
\r
454 $locale->get_string('notes_title'))
\r
456 . '<textarea name="notes" id="notes-field"' . $/
\r
457 . 'rows="3" cols="80">'
\r
458 . (defined $notes ? Chirpy::Util::encode_xml_entities($notes) : '')
\r
459 . '</textarea></div>' . $/
\r
460 . '<div id="tags-container">' . $/
\r
461 . '<label for="tags-field">'
\r
463 $locale->get_string('tags_title'))
\r
465 . '<input type="text" name="tags" value="'
\r
466 . (@$tags ? &_text_to_xhtml(join(' ', @$tags)) : '')
\r
467 . '" id="tags-field" /></div>' . $/
\r
468 . '<div id="edit-quote-submit-container">' . $/
\r
469 . '<input type="submit" value="'
\r
470 . &_text_to_xhtml($locale->get_string('save_quote'))
\r
471 . '" id="edit-quote-submit-button" />' . $/
\r
472 . '<input type="reset" value="'
\r
473 . &_text_to_xhtml($locale->get_string('reset_form'))
\r
474 . '" id="edit-quote-reset-button" />' . $/
\r
475 . '</div></form></div>';
\r
477 if (my $quote = $params{'confirm_quote_removal'}) {
\r
478 my ($body, $notes, $tags) = $self->parent()->_format_quote($quote);
\r
479 return '<div id="quote-removal-confirmation-form">' . $/
\r
480 . '<form method="post" action="'
\r
481 . $self->parent()->_url(
\r
482 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'REMOVE_QUOTE'},
\r
484 'id' => $quote->get_id()
\r
486 . '<p id="quote-removal-confirmation-request">'
\r
487 . &_text_to_xhtml($locale->get_string('quote_removal_confirmation'))
\r
489 . '<blockquote class="quote-body"><p>'
\r
490 . $body . '</p></blockquote>' . $/
\r
491 . '<div id="quote-removal-confirmation-submit-container">' . $/
\r
492 . '<input type="submit" name="confirm" value="'
\r
493 . &_text_to_xhtml($locale->get_string('remove_quote'))
\r
494 . '" id="quote-removal-confirmation-submit-button" />' . $/
\r
495 . '<input type="button" value="'
\r
496 . &_text_to_xhtml($locale->get_string('cancel'))
\r
497 . '" id="quote-removal-confirmation-cancel-button"'
\r
498 . ' onclick="history.go(-1);" />' . $/
\r
499 . '</div></form></div>';
\r
502 if ($params{'quote_removed'}) {
\r
503 $result = $locale->get_string('quote_removed');
\r
505 elsif ($params{'quote_to_edit_not_found'}) {
\r
506 $result = $locale->get_string('quote_to_edit_not_found');
\r
508 elsif ($params{'quote_to_remove_not_found'}) {
\r
509 $result = $locale->get_string('quote_to_remove_not_found');
\r
511 elsif ($params{'quote_modified'}) {
\r
512 $result = $locale->get_string('quote_modified');
\r
514 return (defined $result
\r
515 ? '<p id="manage-quotes-result">'
\r
516 . &_text_to_xhtml($result) . '</p>'
\r
518 . '<p id="manage-quote-instructions">'
\r
520 $locale->get_string('webapp.manage_quote_instructions'))
\r
522 . '<div id="quick-manage-quote-form">' . $/
\r
523 . '<form method="post" action="'
\r
524 . $self->parent()->_url(undef, 1)
\r
525 . '" onsubmit="return (!document.getElementById'
\r
526 . '(\'quote-remove\').checked || confirm("'
\r
527 . &_text_to_xhtml($locale->get_string(
\r
528 'webapp.remove_quote_without_viewing_confirmation'))
\r
529 . '"))">' . $/
\r
530 . '<div id="quick-manage-quote-id-container"><label' . $/
\r
531 . 'for="quick-manage-quote-id-field">'
\r
533 $locale->get_string('quote_id_title')) . '</label>' . $/
\r
534 . '<input name="id" id="quick-manage-quote-id-field" />' . $/
\r
536 . '<div id="quick-manage-quote-options">' . $/
\r
537 . '<input type="radio" name="admin_action" value="'
\r
538 . Chirpy::UI::WebApp::ADMIN_ACTIONS->{'EDIT_QUOTE'} . '"' . $/
\r
539 . 'id="quote-edit" checked="checked" /> <label for="quote-edit">'
\r
540 . &_text_to_xhtml($locale->get_string('edit'))
\r
542 . '<input type="radio" name="admin_action" value="'
\r
543 . Chirpy::UI::WebApp::ADMIN_ACTIONS->{'REMOVE_QUOTE'} . '"' . $/
\r
544 . 'id="quote-remove" /> <label for="quote-remove">'
\r
545 . &_text_to_xhtml($locale->get_string('remove'))
\r
546 . '</label></div>' . $/
\r
547 . '<div id="quick-manage-quote-submit">'
\r
548 . '<input type="submit" value="'
\r
549 . &_text_to_xhtml($locale->get_string('go'))
\r
550 . '→" /></div>' . $/
\r
554 sub get_manage_news_html {
\r
555 my ($self, %params) = @_;
\r
556 my $locale = $self->parent()->locale();
\r
557 if (my $item = $params{'edit_news_item'}) {
\r
558 my $html = '<div id="edit-news-item-form">' . $/
\r
559 . '<form method="post" action="'
\r
560 . $self->parent()->_url(
\r
561 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'EDIT_NEWS'},
\r
563 'id' => $item->get_id())
\r
565 . '<div id="news-item-container">' . $/
\r
566 . '<textarea name="body" id="news-item-field"' . $/
\r
567 . 'rows="8" cols="80">'
\r
568 . Chirpy::Util::encode_xml_entities($item->get_body())
\r
569 . '</textarea></div>' . $/
\r
570 . '<div id="news-item-poster-container">' . $/
\r
571 . '<label for="news-item-poster-select">'
\r
573 $locale->get_string('news_poster_title'))
\r
575 . '<select name="poster" id="news-item-poster-select">' . $/
\r
578 $locale->get_string('unknown'))
\r
579 . ')</option>' . $/;
\r
580 my $posters = $self->parent()->get_news_posters();
\r
581 if (defined $posters) {
\r
582 foreach my $account (@$posters) {
\r
583 my $p = $item->get_poster();
\r
584 my $level = $account->get_level();
\r
585 $html .= '<option value="' . $account->get_id() . '"'
\r
586 . (defined $p && $account->get_id() == $p->get_id()
\r
587 ? ' selected="selected"' : '')
\r
588 . ' class="user-level-' . $level . '">'
\r
589 . $account->get_username()
\r
590 . ' <' . $self->parent()->parent()->user_level_name($level)
\r
591 . '></option>' . $/;
\r
594 return $html . '</select>'
\r
596 . '<div id="edit-news-item-submit-container">' . $/
\r
597 . '<input type="submit" value="'
\r
599 $locale->get_string('save_news_item'))
\r
600 . '" id="edit-news-item-submit-button" />' . $/
\r
601 . '<input type="reset" value="'
\r
603 $locale->get_string('reset_form'))
\r
605 . 'id="edit-news-item-reset-button" />' . $/
\r
606 . '</div></form></div>';
\r
609 if ($params{'news_item_added'}) {
\r
610 $result = $locale->get_string('news_item_added');
\r
612 elsif ($params{'news_item_modified'}) {
\r
613 $result = $locale->get_string('news_item_modified');
\r
615 elsif ($params{'news_item_to_edit_not_found'}) {
\r
616 $result = $locale->get_string('news_item_to_edit_not_found');
\r
618 elsif ($params{'news_item_removed'}) {
\r
619 $result = $locale->get_string('news_item_removed');
\r
621 elsif ($params{'news_item_to_remove_not_found'}) {
\r
622 $result = $locale->get_string('news_item_to_remove_not_found');
\r
624 return (defined $result
\r
625 ? '<p id="manage-news-items-result">'
\r
626 . &_text_to_xhtml($result) . '</p>'
\r
628 . '<div id="post-news-form">' . $/
\r
629 . '<form method="post" action="'
\r
630 . $self->parent()->_url(
\r
631 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'ADD_NEWS'},
\r
634 . '<div id="news-container">' . $/
\r
635 . '<label for="news-field">'
\r
637 $locale->get_string('new_news_item_title'))
\r
639 . '<textarea name="news" id="news-field" rows="8" cols="80">'
\r
640 . '</textarea>' . $/
\r
642 . '<div id="submit-news-container">' . $/
\r
643 . '<input type="submit" value="'
\r
644 . &_text_to_xhtml($locale->get_string('add_news_item'))
\r
645 . '" id="submit-news-button" />' . $/
\r
646 . '<input type="reset" value="'
\r
647 . &_text_to_xhtml($locale->get_string('reset_form'))
\r
648 . '" id="reset-news-button" />' . $/
\r
652 . '<p id="news-instructions">'
\r
654 $locale->get_string('webapp.manage_news_instructions'))
\r
658 sub get_manage_accounts_html {
\r
659 my ($self, %params) = @_;
\r
660 my $locale = $self->parent()->locale();
\r
661 my $status_message;
\r
662 if ($params{'account_to_modify_not_found'}) {
\r
663 $status_message = $locale->get_string('account_to_modify_not_found');
\r
665 elsif ($params{'account_to_remove_not_found'}) {
\r
666 $status_message = $locale->get_string('account_to_remove_not_found');
\r
668 elsif ($params{'last_owner_account_removal'}) {
\r
669 $status_message = $locale->get_string(
\r
670 'last_owner_account_removal_error');
\r
672 elsif ($params{'modified_account_information_required'}) {
\r
673 $status_message = $locale->get_string(
\r
674 'modified_account_information_required');
\r
676 elsif ($params{'invalid_username'}) {
\r
677 $status_message = $locale->get_string('invalid_username');
\r
679 elsif ($params{'username_exists'}) {
\r
680 $status_message = $locale->get_string('username_exists');
\r
682 elsif ($params{'invalid_password'}) {
\r
683 $status_message = $locale->get_string('invalid_password');
\r
685 elsif ($params{'different_passwords'}) {
\r
686 $status_message = $locale->get_string('different_passwords');
\r
688 elsif ($params{'invalid_user_level'}) {
\r
689 $status_message = $locale->get_string('invalid_user_level');
\r
691 elsif ($params{'account_removed'}) {
\r
692 $status_message = $locale->get_string('account_removed');
\r
694 elsif ($params{'account_modified'}) {
\r
695 $status_message = $locale->get_string('account_modified');
\r
697 elsif ($params{'account_created'}) {
\r
698 $status_message = $locale->get_string('account_created');
\r
700 my $html = '<form method="post" action="'
\r
701 . $self->parent()->_url(
\r
702 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'ADD_ACCOUNT'},
\r
705 . '<div id="account-manager">' . $/
\r
706 . '<div id="username-select-container">' . $/
\r
707 . '<select name="id" id="username-select" size="16">' . $/
\r
708 . '<option value="-1" id="username-new-user" selected="selected">'
\r
710 . &_text_to_xhtml($locale->get_string('new_account'))
\r
711 . ' >></option>' . $/;
\r
712 my $users = $self->parent()->parent()->get_accounts();
\r
713 if (defined $users) {
\r
714 foreach my $user (@$users) {
\r
715 $html .= '<option value="' . $user->get_id()
\r
716 . '" class="user-level-' . $user->get_level()
\r
717 . '">' . $user->get_username() . ' <'
\r
719 $self->parent()->parent()->user_level_name($user->get_level()))
\r
720 . '></option>' . $/;
\r
723 $html .= '</select>' . $/
\r
725 . '<div id="username-container">' . $/
\r
726 . '<label for="username-field">'
\r
728 $locale->get_string('new_username_title'))
\r
730 . '<input name="new_username" id="username-field" />' . $/
\r
732 . '<div id="password-container">' . $/
\r
733 . '<label for="password-field">'
\r
735 $locale->get_string('new_password_title'))
\r
737 . '<input type="password" name="new_password" '
\r
738 . 'id="password-field" />' . $/
\r
740 . '<div id="repeat-password-container">' . $/
\r
741 . '<label for="repeat-password-field">'
\r
743 $locale->get_string('repeat_new_password_title'))
\r
745 . '<input type="password" name="new_password_repeat" '
\r
746 . 'id="repeat-password-field" />' . $/
\r
748 . '<div id="level-container">' . $/
\r
749 . '<label for="level-select">'
\r
751 $locale->get_string('new_user_level_title'))
\r
753 . '<select name="new_level" id="level-select" size="1">' . $/
\r
754 . '<option value="-1" selected="selected"><'
\r
756 $locale->get_string('no_change'))
\r
757 . '></option>' . $/;
\r
758 foreach my $level ($self->parent()->parent()->user_levels()) {
\r
759 $html .= '<option value="' . $level . '" class="user-level-'
\r
760 . $level . '">' . &_text_to_xhtml(
\r
761 $self->parent()->parent()->user_level_name($level))
\r
762 . ' <' . $level . '></option>' . $/;
\r
764 $html .= '</select>' . $/
\r
766 . '<div id="account-submit-container">' . $/
\r
767 . '<input type="submit" value="'
\r
768 . &_text_to_xhtml($locale->get_string('update_accounts'))
\r
769 . '" id="account-submit-button" />' . $/
\r
770 . '<input type="submit" name="account_remove" value="'
\r
772 $locale->get_string('remove_account'))
\r
773 . '" id="modify-account-remove-button" onclick="return confirm("'
\r
775 $locale->get_string('account_removal_confirmation'))
\r
776 . '")" />' . $/
\r
778 . (defined $status_message
\r
779 ? '<div id="account-manager-result">' . $/
\r
780 . &_text_to_xhtml($status_message)
\r
781 . $/ . '</div>' . $/
\r
784 . '<div style="clear: both;"></div>' . $/
\r
789 sub get_event_log_html {
\r
791 my $locale = $self->parent()->locale();
\r
792 my $resurl = $self->parent()->_resources_url();
\r
793 my $url = $self->parent()->_url(
\r
794 Chirpy::UI::WebApp::ADMIN_ACTIONS->{'VIEW_EVENT_LOG'},
\r
796 $url .= ($url =~ /\?/ ? '&' : '?');
\r
797 my $html = '<script type="text/javascript" src="'
\r
798 . $resurl . '/js/ajax.js"></script>' . $/
\r
799 . '<script type="text/javascript" src="'
\r
800 . $resurl . '/js/administration.js"></script>' . $/
\r
801 . '<script type="text/javascript">' . $/
\r
802 . 'var eventLogURL = "' . $url . '";' . $/
\r
803 . 'var eventLogLocale = new Array();' . $/;
\r
804 foreach my $col (qw(id date username event empty)) {
\r
805 $html .= 'eventLogLocale["' . $col . '"] = "'
\r
806 . &_text_to_xhtml($locale->get_string($col)) . '";' . $/;
\r
808 $html .= 'eventLogLocale["previous"] = "' . &_text_to_xhtml(
\r
809 $locale->get_string('webapp.previous_page_title')) . '";' . $/
\r
810 . 'eventLogLocale["next"] = "' . &_text_to_xhtml(
\r
811 $locale->get_string('webapp.next_page_title')) . '";' . $/
\r
812 . 'eventLogLocale["current"] = "' . &_text_to_xhtml(
\r
813 $locale->get_string('webapp.current_page_title')) . '";' . $/
\r
814 . 'eventLogLocale["loading"] = "' . &_text_to_xhtml(
\r
815 $locale->get_string('processing')) . '";' . $/
\r
816 . 'eventLogLocale["guest"] = "' . &_text_to_xhtml(
\r
817 $locale->get_string('guest')) . '";' . $/;
\r
818 foreach my $id (qw/code data user asc/) {
\r
819 my $val = $self->parent()->_cgi_param($id);
\r
820 next unless (defined $val);
\r
821 $html .= 'eventLogURLParam["' . $id . '"] = "'
\r
822 . &_text_to_xhtml($val) . '";' . $/;
\r
824 $html .= '</script>' . $/
\r
825 . '<div id="event-log-placeholder"></div>';
\r
829 sub _serve_event_log_table_data {
\r
831 my $locale = $self->parent()->locale();
\r
832 my $count = $self->parent()->_cgi_param('count');
\r
833 $count = (defined $count ? int $count : undef);
\r
834 my $start = $self->parent()->_cgi_param('start');
\r
835 $start = 0 unless (defined $start && $start >= 0);
\r
836 my $desc = ($self->parent()->_cgi_param('asc') ? 0 : 1);
\r
837 my $user = $self->parent()->_cgi_param('user');
\r
838 $user = undef if (defined $user && $user !~ /^\d+$/);
\r
839 my $event = $self->parent()->_cgi_param('code');
\r
840 $event = undef if (defined $event && $event !~ /^\d+$/);
\r
841 my $filter = $self->parent()->_cgi_param('data');
\r
842 if (defined $filter && $filter =~ /^([^=]+)=(.*)$/s) {
\r
843 $filter = { $1 => $2 };
\r
848 my ($events, $leading, $trailing) = $self->parent()->parent()->get_events(
\r
849 $start, $count, $desc, $event, $user, $filter);
\r
851 foreach my $event (@$events) {
\r
852 my $id = $event->get_id();
\r
853 my $date = $self->parent()->format_date_time($event->get_date());
\r
854 my $user = $event->get_user();
\r
856 if (defined $user) {
\r
857 my $acct = $self->parent()->parent()->get_account_by_id($user);
\r
858 if (defined $acct) {
\r
859 $username = $acct->get_username();
\r
862 my $description = &_text_to_xhtml($locale->get_string(
\r
863 'event_' . $event->get_code() . '_name'));
\r
864 my $data = $event->get_data();
\r
868 (defined $username ? ('username' => $username) : ()),
\r
869 'userid' => (defined $user ? $user : 0),
\r
870 'description' => $description,
\r
871 'code' => $event->get_code()
\r
874 foreach my $key (sort keys %$data) {
\r
875 my $value = $data->{$key};
\r
877 'name' => &_text_to_xhtml($key),
\r
878 'value' => &_text_to_xhtml($value)
\r
881 $result->{'data'} = \@data;
\r
882 push @events, $result;
\r
884 $self->parent()->_output_xml('result', {
\r
885 'event' => \@events,
\r
886 ($leading ? ('leading' => 'true') : ()),
\r
887 ($trailing ? ('trailing' => 'true') : ())
\r
891 sub get_access_disallowed_html {
\r
893 return '<p id="insufficient-administrator-privileges-notification">'
\r
894 . &_text_to_xhtml($self->parent()->locale()->get_string(
\r
895 'insufficient_administrative_privileges'))
\r
899 sub get_change_password_html {
\r
900 my ($self, %params) = @_;
\r
901 my $locale = $self->parent()->locale();
\r
902 if ($params{'password_changed'}) {
\r
903 return '<div id="change-password-result">' . $/
\r
904 . '<p>' . $locale->get_string('password_changed_text') . '</p>' . $/
\r
908 if (my $error = $params{'password_change_error'}) {
\r
909 $html = '<div id="change-password-error">' . $/ . '<p>'
\r
910 . &_text_to_xhtml($locale->get_string(
\r
911 $error == Chirpy::UI::NEW_PASSWORD_INVALID
\r
912 ? 'change_password_new_password_invalid_text'
\r
913 : ($error == Chirpy::UI::PASSWORDS_DIFFER
\r
914 ? 'change_password_passwords_differ_text'
\r
915 : ($error == Chirpy::UI::CURRENT_PASSWORD_INVALID
\r
916 ? 'change_password_current_password_invalid_text'
\r
918 . '</p>' . $/ . '</div>' . $/;
\r
920 return $html . '<div id="change-password-form">' . $/
\r
921 . '<form method="post" action="'
\r
922 . $self->parent()->_url(Chirpy::UI::WebApp::ADMIN_ACTIONS->{'CHANGE_PASSWORD'}, 1)
\r
924 . '<div id="current-password-container">' . $/
\r
925 . '<label for="current-password-field">'
\r
927 $locale->get_string('current_password_title'))
\r
929 . '<input type="password" name="current_password" '
\r
930 . 'id="current-password-field" />' . $/
\r
932 . '<div id="new-password-container">' . $/
\r
933 . '<label for="new-password-field">'
\r
935 $locale->get_string('new_password_title'))
\r
937 . '<input type="password" name="new_password" '
\r
938 . 'id="new-password-field" />' . $/
\r
940 . '<div id="repeat-new-password-container">' . $/
\r
941 . '<label for="repeat-new-password-field">'
\r
943 $locale->get_string('repeat_new_password_title'))
\r
945 . '<input type="password" name="repeat_new_password" '
\r
946 . 'id="repeat-new-password-field" />' . $/
\r
948 . '<div id="submit-container">' . $/
\r
949 . '<input type="submit" value="'
\r
951 $locale->get_string('change_password_button_label'))
\r
952 . '" id="change-password-button" />' . $/
\r
958 *_text_to_xhtml = \&Chirpy::UI::WebApp::_text_to_xhtml;
\r
962 ###############################################################################