root/branches/release-30/lib/MT/CMS/Log.pm @ 1369

Revision 1369, 12.7 kB (checked in by bchoate, 22 months ago)

Broke CMS into smaller parts to reduce memory footprint and group code into logical parts. BugId:58666

  • Property svn:keywords set to Id Revision
Line 
1package MT::CMS::Log;
2
3use strict;
4
5use MT::Util qw( format_ts epoch2ts ts2epoch relative_date offset_time );
6use MT::I18N qw( const break_up_text );
7
8sub view {
9    my $app     = shift;
10    my $user    = $app->user;
11    my $blog_id = $app->param('blog_id');
12    my $perms   = $app->permissions;
13    if ($blog_id) {
14        return $app->error( $app->translate("Permission denied.") )
15          unless ( $perms && $perms->can_view_blog_log ) || $user->can_view_log;
16    }
17    else {
18        return $app->error( $app->translate("Permission denied.") )
19          unless $user->can_view_log;
20    }
21    my $log_class  = $app->model('log');
22    my $blog_class = $app->model('blog');
23    my $list_pref  = $app->list_pref('log');
24    my $limit      = $list_pref->{rows};
25    my $offset     = $app->param('offset') || 0;
26    my $terms      = { $blog_id ? ( blog_id => $blog_id ) : () };
27    my $cfg        = $app->config;
28    my %param      = (%$list_pref);
29    my ( $filter_col, $val );
30    $param{filter_args} = "";
31
32    if (   ( $filter_col = $app->param('filter') )
33        && ( $val = $app->param('filter_val') ) )
34    {
35        $param{filter}     = $filter_col;
36        $param{filter_val} = $val;
37        my %filter_arg = %{ apply_log_filter( $app, \%param ) };
38        $terms->{$_} = $filter_arg{$_} foreach keys %filter_arg;
39        $param{filter_args} = "&filter=" . encode_url($filter_col) . "&filter_val=" . encode_url($val);
40    }
41
42    # all classes of log objects
43    unless ( exists $terms->{class} ) {
44        $terms->{class} = '*';
45    }
46
47    my $iter = $log_class->load_iter(
48        $terms,
49        {
50            'sort'      => 'id',
51            'direction' => 'descend',
52            'offset'    => $offset,
53            'limit'     => $limit
54        }
55    );
56
57    my @class_loop;
58    my $labels = MT::Log->class_labels;
59    foreach ( keys %$labels ) {
60        next if $_ eq 'log';
61        my $name = $_;
62        $name =~ s/log\.(\w)/$1/;
63        next unless $name;
64        push @class_loop,
65          {
66            class_name  => $name,
67            class_label => $labels->{$_},
68          };
69    }
70    push @class_loop,
71      {
72        class_name  => 'comment,ping',
73        class_label => $app->translate("All Feedback"),
74      },
75      {
76        class_name  => 'search',
77        class_label => $app->translate("Search"),
78      },
79      {
80        class_name  => 'publish',
81        class_label => $app->translate("Publishing"),
82      };
83    @class_loop = sort { $a->{class_label} cmp $b->{class_label} } @class_loop;
84    $param{class_loop} = \@class_loop;
85
86    my $log = build_log_table( $app, iter => $iter, param => \%param );
87    my $blog = $blog_class->load($blog_id) if $blog_id;
88    my ($so);
89    if ($blog) {
90        $so = $blog->server_offset;
91    }
92    else {
93        $so = $app->config('TimeOffset');
94    }
95    if ($so) {
96        my $partial_hour_offset = 60 * abs( $so - int($so) );
97        my $tz                  = sprintf( "%s%02d:%02d",
98            $so < 0 ? '-' : '+',
99            abs($so), $partial_hour_offset );
100        $param{time_offset} = $tz;
101    }
102    $param{object_type}     = 'log';
103    $param{search_label}    = $app->translate('Activity Log');
104    $param{list_start}      = $offset + 1;
105    $param{list_total}      = MT::Log->count($terms);
106    $param{list_end}        = $offset + ( scalar @$log );
107    $param{offset}          = $offset;
108    $param{next_offset_val} = $offset + ( scalar @$log );
109    $param{next_offset} = $param{next_offset_val} < $param{list_total} ? 1 : 0;
110    $param{next_max}    = $param{list_total} - $limit;
111    $param{next_max}    = 0 if ( $param{next_max} || 0 ) < $offset + 1;
112
113    if ( $offset > 0 ) {
114        $param{prev_offset}     = 1;
115        $param{prev_offset_val} = $offset - $limit;
116        $param{prev_offset_val} = 0 if $param{prev_offset_val} < 0;
117    }
118    $param{'reset'}      = $app->param('reset');
119    $param{nav_log}      = 1;
120    $param{feed_name}    = $app->translate("System Activity Feed");
121    $param{screen_class} = "list-log";
122    $param{screen_id} = "list-log";
123    $param{listing_screen} = 1;
124    $param{feed_url} =
125      $app->make_feed_link( 'system',
126        $blog_id ? { blog_id => $blog_id } : undef );
127    if ( $param{feed_url} && $param{filter_args} ) {
128        $param{feed_url} .= $param{filter_args};
129    }
130    $app->add_breadcrumb( $app->translate('Activity Log') );
131    unless ( $app->param('blog_id') ) {
132        $param{system_overview_nav} = 1;
133    }
134    $app->load_tmpl( 'view_log.tmpl', \%param );
135}
136
137sub build_log_table {
138    my $app = shift;
139    my (%args) = @_;
140
141    my $blog       = $app->blog;
142    my $blog_view  = $blog ? 1 : 0;
143    my $blog_class = $app->model('blog');
144    my $i          = 1;
145    my @log;
146    my $iter;
147    if ( $args{load_args} ) {
148        my $class = $app->model('log');
149        $iter = $class->load_iter( @{ $args{load_args} } );
150    }
151    elsif ( $args{iter} ) {
152        $iter = $args{iter};
153    }
154    elsif ( $args{items} ) {
155        $iter = sub { pop @{ $args{items} } };
156    }
157    return [] unless $iter;
158    my $param = $args{param};
159    my %blogs;
160    # reusing comment length constant for log view
161    my $break_len = const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_SHORT');
162    while ( my $log = $iter->() ) {
163        my $msg = $log->message;
164        $msg =
165          break_up_text( $msg, $break_len )
166          ;    # break up really long strings
167        my $row = {
168            log_message => $msg,
169            log_ip      => $log->ip,
170            id          => $log->id,
171            blog_id     => $log->blog_id
172        };
173        if ( my $ts = $log->created_on ) {
174            if ($blog_view) {
175                $row->{created_on_formatted} =
176                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(),
177                    epoch2ts( $blog, ts2epoch( undef, $ts ) ), $blog, $app->user ? $app->user->preferred_language : undef );
178            }
179            else {
180                $row->{created_on_formatted} =
181                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(),
182                    epoch2ts( undef, offset_time( ts2epoch( undef, $ts ) ) ), undef, $app->user ? $app->user->preferred_language : undef );
183                if ( $log->blog_id ) {
184                    $blog = $blogs{ $log->blog_id } ||=
185                      $blog_class->load( $log->blog_id, { cache_ok => 1 } );
186                    $row->{weblog_name} = $blog ? $blog->name : '';
187                }
188                else {
189                    $row->{weblog_name} = '';
190                }
191            }
192            $row->{created_on_relative} = relative_date( $ts, time );
193            $row->{log_detail} = $log->description;
194        }
195        if ( my $uid = $log->author_id ) {
196            my $user_class = $app->model('author');
197            my $user       = $user_class->load($uid);
198            $row->{username} = $user->name if defined $user;
199        }
200        $row->{object} = $log;
201        push @log, $row;
202    }
203    return [] unless @log;
204    $param->{object_loop} = $param->{log_table}[0]{object_loop} = \@log;
205    \@log;
206}
207
208sub reset {
209    my $app    = shift;
210    $app->validate_magic() or return;
211    my $author = $app->user;
212    my $log_class = $app->model('log');
213    if ( my $blog_id = $app->param('blog_id') ) {
214        my $perms = $app->permissions;
215        return $app->error( $app->translate("Permission denied.") )
216          unless $perms && $perms->can_view_log;
217        my $blog_class = $app->model('blog');
218        my $blog = $blog_class->load( $blog_id )
219            or return $app->errtrans("Invalid request.");
220        if ( $log_class->remove( { blog_id => $blog_id, class => '*' } ) ) {
221            $app->log(
222                {
223                    message => $app->translate(
224"Activity log for blog '[_1]' (ID:[_2]) reset by '[_3]'",
225                        $blog->name, $blog_id, $author->name
226                    ),
227                    level    => MT::Log::INFO(),
228                    class    => 'system',
229                    category => 'reset_log'
230                }
231            );
232        }
233    }
234    else {
235        return $app->error( $app->translate("Permission denied.") )
236          unless $author->can_view_log;
237        if ( $log_class->remove( { class => '*' } ) ) {
238            $app->log(
239                {
240                    message => $app->translate(
241                        "Activity log reset by '[_1]'",
242                        $author->name
243                    ),
244                    level    => MT::Log::INFO(),
245                    class    => 'system',
246                    category => 'reset_log'
247                }
248            );
249        }
250    }
251    $app->add_return_arg( 'reset' => 1 );
252    $app->call_return;
253}
254
255sub export {
256    my $app       = shift;
257    my $user      = $app->user;
258    my $perms     = $app->permissions;
259    my $blog      = $app->blog;
260    my $blog_view = $blog ? 1 : 0;
261    if ($blog_view) {
262        return $app->error( $app->translate("Permission denied.") )
263          unless $user->can_view_log || ( $perms && $perms->can_view_blog_log );
264    }
265    else {
266        return $app->error( $app->translate("Permission denied.") )
267          unless $user->can_view_log;
268    }
269    $app->validate_magic() or return;
270    $| = 1;
271    my $enc = $app->config('ExportEncoding');
272    $enc = $app->config('LogExportEncoding') if ( !$enc );
273    $enc = ( $app->charset || '' ) if ( !$enc );
274    my $blog_enc = $app->config('PublishCharset');
275
276    my $q           = $app->param;
277    my $filter_args = $q->param('filter_args');
278    my %terms;
279    if ($filter_args) {
280        $q->parse_params($filter_args) if $filter_args;
281        %terms = %{
282            apply_log_filter( $app,
283                {
284                    filter     => $q->param('filter'),
285                    filter_val => $q->param('filter_val')
286                }
287            )
288          };
289    } else {
290        %terms = ( class => '*' );
291    }
292    if ($blog) {
293        $terms{blog_id} = $blog->id;
294    }
295    my $log_class  = $app->model('log');
296    my $blog_class = $app->model('blog');
297    my $iter =
298      $log_class->load_iter( \%terms,
299        { 'sort' => 'created_on', 'direction' => 'ascend' } );
300    my %blogs;
301
302    my $file = '';
303    $file = dirify( $blog->name ) . '-' if $blog;
304    $file = "Blog-" . $blog->id . '-' if $file eq '-';
305    my @ts = gmtime(time);
306    my $ts = sprintf "%04d-%02d-%02d-%02d-%02d-%02d", $ts[5] + 1900, $ts[4] + 1,
307      @ts[ 3, 2, 1, 0 ];
308    $file .= "log_$ts.csv";
309    $app->{no_print_body} = 1;
310    $app->set_header( "Content-Disposition" => "attachment; filename=$file" );
311    $app->send_http_header(
312        $enc
313        ? "text/csv; charset=$enc"
314        : 'text/csv'
315    );
316
317    my $csv = "timestamp,ip,weblog,message\n";
318    while ( my $log = $iter->() ) {
319
320        # columns:
321        # date, ip address, weblog, log message
322        my @col;
323        my $ts = $log->created_on;
324        if ($blog_view) {
325            push @col,
326              format_ts( "%Y-%m-%d %H:%M:%S",
327                epoch2ts( $blog, ts2epoch( undef, $ts ) ), $blog, $app->user ? $app->user->preferred_language : undef );
328        }
329        else {
330            push @col, format_ts( "%Y-%m-%d %H:%M:%S", $log->created_on, undef, $app->user ? $app->user->preferred_language : undef );
331        }
332        push @col, $log->ip;
333        my $blog;
334        if ( $log->blog_id ) {
335            $blog = $blogs{ $log->blog_id } ||=
336              $blog_class->load( $log->blog_id );
337        }
338        if ( $blog ) {
339            my $name = $blog->name;
340            $name =~ s/"/\\"/gs;
341            $name =~ s/[\r\n]+/ /gs;
342            $name = encode_text( $name, undef, $enc ) if $enc;
343            push @col, '"' . $name . '"';
344        }
345        else {
346            push @col, '';
347        }
348        my $msg = $log->message;
349        $msg = encode_text( $msg, $blog_enc, $enc ) if $enc;
350        $msg =~ s/"/\\"/gs;
351        $msg =~ s/[\r\n]+/ /gs;
352        push @col, '"' . $msg . '"';
353        $csv .= ( join ',', @col ) . "\n";
354        $app->print($csv);
355        $csv = '';
356    }
357}
358
359sub apply_log_filter {
360    my $app = shift;
361    my ($param) = @_;
362    my %arg;
363    if ($param) {
364        my $filter_col = $param->{filter};
365        my $val        = $param->{filter_val};
366        if ( $filter_col && $val ) {
367            if ( $filter_col eq 'level' ) {
368                my @types;
369                for ( 1, 2, 4, 8, 16 ) {
370                    push @types, $_ if $val & $_;
371                }
372                if (@types) {
373                    $arg{'level'} = \@types;
374                }
375            }
376            elsif ( $filter_col eq 'class' ) {
377                if ($val eq 'publish') {
378                    $arg{category} = 'publish';
379                }
380                else {
381                    if ($val =~ m/,/) {
382                        $arg{class} = [ split /,/, $val ];
383                    } else {
384                        $arg{class} = $val;
385                    }
386                }
387            }
388        }
389        $arg{blog_id} = [ split /,/, $param->{blog_id} ]
390          if $param->{blog_id};
391    }
392    \%arg;
393}
394
3951;
Note: See TracBrowser for help on using the browser.