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

Revision 1369, 13.5 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::Dashboard;
2
3use strict;
4use MT::Util qw( epoch2ts );
5
6sub dashboard {
7    my $app = shift;
8    my (%param) = @_;
9
10    if ( $app->request('fresh_login') ) {
11        if ( !$app->param('blog_id') ) {
12
13            # return to the last blog they visted, if any
14            my $fav_blogs = $app->user->favorite_blogs || [];
15            my $blog_id = $fav_blogs->[0] if @$fav_blogs;
16            $app->param( 'blog_id', $blog_id ) if $blog_id;
17            $app->delete_param('blog_id') unless $app->is_authorized;
18        }
19    }
20
21    my $param = \%param;
22
23    $param->{redirect}   ||= $app->param('redirect');
24    $param->{permission} ||= $app->param('permission');
25    $param->{saved}      ||= $app->param('saved');
26
27    $param->{system_overview_nav} = $app->param('blog_id') ? 0 : defined($app->param('blog_id')) ? 1 : 0;
28    $param->{quick_search}        = 0;
29    $param->{no_breadcrumbs}      = 1;
30    $param->{screen_class}        = "dashboard";
31    $param->{screen_id}           = "dashboard";
32
33    my $default_widgets = {
34        'blog_stats' =>
35          { param => { tab => 'entry' }, order => 1, set => 'main' },
36        'this_is_you-1' => { order => 1, set => 'sidebar' },
37        'mt_shortcuts'  => { order => 2, set => 'sidebar' },
38        'mt_news'       => { order => 3, set => 'sidebar' },
39    };
40
41    require MT::FileMgr;
42    my $fmgr = MT::FileMgr->new('Local');
43    $param->{support_path} =
44        File::Spec->catdir( $app->static_file_path, 'support', 'uploads' );
45    if ( $fmgr->exists( $param->{support_path} ) ) {
46        $param->{has_uploads_path} = 1;
47    } else {
48        $fmgr->mkpath( $param->{support_path} );
49        if ( $fmgr->exists( $param->{support_path} )
50             && $fmgr->can_write( $param->{support_path} ) )
51        {
52            $param->{has_uploads_path} = 1;
53        }
54    }
55
56    # We require that the determination of the 'single blog mode'
57    # state be done PRIOR to the generation of the widgets
58    $app->build_blog_selector($param);
59    $app->load_widget_list( 'dashboard', $param, $default_widgets );
60    $param = $app->load_widgets( 'dashboard', $param, $default_widgets );
61    return $app->load_tmpl( "dashboard.tmpl", $param );
62}
63
64sub new_version_widget {
65    my $app = shift;
66    my ( $tmpl, $param ) = @_;
67
68    push @{ $param->{feature_loop} ||= [] },
69      {
70        feature_label => MT->translate('Shared Template Modules'),
71        feature_url  => $app->help_url('designer/shared-templates.html'),
72        feature_description => MT->translate('Reuse elements of your site design or layout across all the blogs and sites managed within Movable Type.')
73      },
74      {
75        feature_label => MT->translate('Userpics'),
76        feature_url  => $app->help_url('author/userpics.html'),
77        feature_description => MT->translate('Allow authors and commenters to upload a photo of themselves to be displayed alongside their comments.')
78      },
79      {
80        feature_label => MT->translate('Template Sets'),
81        feature_url  => $app->help_url('designer/template-sets.html'),
82        feature_description => MT->translate('Template sets provide an easy way to bundle an entire design and install it into Movable Type.')
83      };
84}
85
86sub this_is_you_widget {
87    my $app = shift;
88    my ( $tmpl, $param ) = @_;
89
90    my $user = $app->user;
91
92    # User profile data
93    # Number of posts by this user
94    require MT::Entry;
95    $param->{publish_count} = MT::Entry->count( { author_id => $user->id, } );
96    $param->{draft_count} = MT::Entry->count(
97        {
98            author_id => $user->id,
99            status    => MT::Entry::HOLD(),
100        }
101    );
102    if ( $param->{publish_count} ) {
103        require MT::Comment;
104        $param->{comment_count} = MT::Comment->count(
105            { junk_status => [ 0, 1 ], },
106            {
107                join => MT::Entry->join_on(
108                    undef,
109                    {
110                        author_id => $user->id,
111                        'id'      => \'= comment_entry_id',
112                    },
113                    {},
114                ),
115            }
116        );
117    }
118
119    my $last_post = MT::Entry->load(
120        {
121            author_id => $user->id,
122            status    => MT::Entry::RELEASE(),
123        },
124        {
125            sort      => 'authored_on',
126            direction => 'descend',
127            limit     => 1,
128        }
129    );
130    if ($last_post) {
131        $param->{last_post_id}      = $last_post->id;
132        $param->{last_post_blog_id} = $last_post->blog_id;
133        $param->{last_post_blog_name} = $last_post->blog->name;
134        $param->{last_post_ts}      = $last_post->authored_on;
135    }
136
137    if (my ($url) = $user->userpic_url()) {
138        $param->{author_userpic_url}    = $url;
139    }
140    $param->{author_userpic_width}  = 50;
141    $param->{author_userpic_height} = 50;
142}
143
144sub mt_news_widget {
145    my $app = shift;
146    my ( $tmpl, $param ) = @_;
147
148    $param->{news_html} = get_newsbox_content($app) || '';
149    $param->{learning_mt_news_html} = get_lmt_content($app) || '';
150}
151
152sub get_newsbox_content {
153    my $app = shift;
154    my $newsbox_url = $app->config('NewsboxURL');
155    if ( $newsbox_url && $newsbox_url ne 'disable' ) {
156        return MT::Util::get_newsbox_html($newsbox_url, 'NW');
157    }
158    return q();
159}
160
161sub get_lmt_content {
162    my $app = shift;
163    my $newsbox_url = $app->config('LearningNewsURL');
164    if ( $newsbox_url && $newsbox_url ne 'disable' ) {
165        return MT::Util::get_newsbox_html($newsbox_url, 'LW');
166    }
167    return q();
168}
169
170sub mt_blog_stats_widget {
171    my $app = shift;
172    my ( $tmpl, $param ) = @_;
173
174    # For stats shown on this page
175    generate_dashboard_stats($app, $param) or return;
176
177    my $tabs = $app->registry('blog_stats_tabs') or return;
178    $tabs = $app->filter_conditional_list($tabs, 'dashboard', ($param->{widget_scope} || ''));
179
180    $param->{tab_html_head} = '';
181    {
182        local $param->{main};
183        local $param->{html_head};
184
185        my %cfgs;
186        my $stat_url = delete $param->{stat_url};
187        while (my ($tab_id, $url) = each %$stat_url) {
188            $param->{has_stat_urls} = 1;
189            $cfgs{$tab_id} = { param => { stat_url => $url } };
190        }
191        $app->build_widgets(
192            set            => 'blog_stats',
193            param          => $param,
194            widgets        => $tabs,
195            widget_cfgs    => \%cfgs,
196            passthru_param => [qw( html_head js_include tabs active_stats_panel_updates )],
197        ) or return;
198
199        $param->{blog_stats} = $param->{main};
200        $param->{tab_html_head} .= $param->{html_head};
201    }
202}
203
204sub mt_blog_stats_widget_entry_tab {
205    my ($app, $tmpl, $param) = @_;
206
207    my $user    = $app->user;
208    my $blog    = $app->blog;
209    my $blog_id = $blog->id if $blog;
210
211    $param->{editable} = $user->is_superuser;
212    if ( $blog && !$param->{editable} ) {
213        $param->{editable} = $user->permissions($blog_id)->can_edit_all_posts;
214    }
215
216    my $entries = sub {
217        my $args = {
218            limit     => 10,
219            sort      => 'authored_on',
220            direction => 'descend',
221        };
222        if ( !$user->is_superuser && !$blog_id ) {
223            $args->{join} = MT::Permission->join_on(
224                undef,
225                {
226                    blog_id   => \'= entry_blog_id',
227                    author_id => $user->id
228                },
229            );
230        }
231        my @e =
232          MT::Entry->load( { ( $blog_id ? ( blog_id => $blog_id ) : () ), },
233            $args );
234        \@e;
235    };
236
237    require MT::Promise;
238    my $ctx = $tmpl->context;
239    $ctx->stash( 'entries',  MT::Promise::delay($entries) );
240}
241
242sub generate_dashboard_stats {
243    my $app = shift;
244    my ($param) = @_;
245
246    my $cache_time = 60 * 15;    # cache for 15 minutes
247
248    my $blog_id = $app->blog ? $app->blog->id : 0;
249    my $user    = $app->user;
250    my $user_id = $user->id;
251
252    my $static_path      = $app->static_path;
253    my $static_file_path = $app->static_file_path;
254
255    if ( -f File::Spec->catfile( $static_file_path, "mt.js" ) ) {
256        $param->{static_file_path} = $static_file_path;
257    }
258    else {
259        return;
260    }
261
262    my $low_dir = sprintf("%03d", $user_id % 1000);
263    my $sub_dir = sprintf("%03d", $blog_id % 1000);
264    my $top_dir = $blog_id > $sub_dir ? $blog_id - $sub_dir : 0;
265    $param->{support_path} =
266      File::Spec->catdir( $static_file_path, 'support', 'dashboard', 'stats',
267        $top_dir, $sub_dir, $low_dir); 
268
269    require MT::FileMgr;
270    my $fmgr = MT::FileMgr->new('Local');
271    unless ( $fmgr->exists( $param->{support_path} ) ) {
272        $fmgr->mkpath( $param->{support_path} );
273        unless ( $fmgr->exists( $param->{support_path} ) ) {
274            return;
275        }
276    }
277
278    my $stats_static_path = $static_path . 'support/dashboard/stats/' .
279        $top_dir . '/' . $sub_dir . '/' . $low_dir;
280
281    my $tabs = $app->registry('blog_stats_tabs') or return;
282    while (my ($tab_id, $tab) = each %$tabs) {
283        my $file = "${tab_id}.xml";
284        $param->{stat_url}->{$tab_id} = $stats_static_path . '/' . $file;
285        my $path = File::Spec->catfile( $param->{support_path}, $file );
286
287        my $time = ( stat($path) )[9] if -f $path;
288
289        if ( !$time || ( time - $time > $cache_time ) ) {
290            my $gen_stats = $tab->{stats};
291            next if !$gen_stats;
292            $gen_stats = $app->handler_to_coderef($gen_stats);
293
294            my %counts = $gen_stats->($app, $tab);
295
296            unless ( create_dashboard_stats_file( $app, $path, \%counts ) ) {
297                delete $param->{stat_url}->{$tab_id};
298            }
299        }
300    }
301
302    1;
303}
304
305sub create_dashboard_stats_file {
306    my $app = shift;
307    my ( $file, $data ) = @_;
308
309    my $support_dir = File::Spec->catdir( $app->static_file_path, "support" );
310    if ( !-d $support_dir ) {
311        mkdir( $support_dir, 0777 );
312        if ($!) {
313            $app->log("Failed to create 'support' directory.");
314            return;
315        }
316    }
317
318    local *FOUT;
319    if ( !open( FOUT, ">$file" ) ) {
320        return;
321    }
322
323    print FOUT <<EOT;
324<?xml version="1.0"?>
325<rsp status_code="0" status_message="Success">
326  <daily_counts>
327EOT
328    my $now = time;
329    for ( my $i = 120 ; $i >= 1 ; $i-- ) {
330        my $ds =
331          substr( epoch2ts( $app->blog, $now - ( ( $i - 1 ) * 60 * 60 * 24 ) ),
332            0, 8 )
333          . 'T00:00:00';
334        my $count = $data->{$ds} || 0;
335        print FOUT qq{    <count date="$ds">$count</count>\n};
336    }
337    print FOUT <<EOT;
338  </daily_counts>
339</rsp>
340EOT
341    close FOUT;
342}
343
344sub generate_dashboard_stats_entry_tab {
345    my $app = shift;
346    my ($tab) = @_;
347   
348    my $blog_id = $app->blog ? $app->blog->id : 0;
349    my $user    = $app->user;
350    my $user_id = $user->id;
351
352    my $entry_class = $app->model('entry');
353    my $terms       = { status => MT::Entry::RELEASE() };
354    my $args        = {
355        group => [
356            "extract(year from authored_on)",
357            "extract(month from authored_on)",
358            "extract(day from authored_on)"
359        ],
360    };
361    $terms->{blog_id} = $blog_id if $blog_id;
362    if ( !$user->is_superuser && !$blog_id ) {
363        $args->{join} = MT::Permission->join_on(
364            undef,
365            {
366                blog_id   => \'= entry_blog_id',
367                author_id => $user_id
368            },
369        );
370    }
371
372    my $entry_iter = $entry_class->count_group_by( $terms, $args );
373    my %counts;
374    while ( my ( $count, $y, $m, $d ) = $entry_iter->() ) {
375        my $date = sprintf( "%04d%02d%02dT00:00:00", $y, $m, $d );
376        $counts{$date} = $count;
377    }
378
379    %counts;
380}
381
382sub mt_blog_stats_widget_comment_tab {
383    my ($app, $tmpl, $param) = @_;
384
385    my $user    = $app->user;
386    my $blog    = $app->blog;
387    my $blog_id = $blog->id if $blog;
388
389    $param->{editable} = $user->is_superuser;
390    if ( $blog && !$param->{editable} ) {
391        $param->{editable} = $user->permissions($blog_id)->can_edit_all_posts;
392        $param->{comment_editable} = $user->permissions($blog_id)->can_manage_feedback;
393    }
394
395    my $comments = sub {
396        my $args = {
397            limit     => 10,
398            sort      => 'created_on',
399            direction => 'descend',
400        };
401        if ( !$user->is_superuser && !$blog_id ) {
402            $args->{join} = MT::Permission->join_on(
403                undef,
404                {
405                    blog_id   => \'= comment_blog_id',
406                    author_id => $user->id
407                },
408            );
409        }
410        my @c = MT::Comment->load(
411            {
412                ( $blog_id ? ( blog_id => $blog_id ) : () ),
413                junk_status => [ 0, 1 ],
414            },
415            $args
416        );
417        \@c;
418    };
419
420    require MT::Promise;
421    my $ctx = $tmpl->context;
422    $ctx->stash( 'comments',  MT::Promise::delay($comments) );
423}
424
425sub generate_dashboard_stats_comment_tab {
426    my $app = shift;
427    my ($tab) = @_;
428   
429    my $blog_id = $app->blog ? $app->blog->id : 0;
430    my $user    = $app->user;
431    my $user_id = $user->id;
432
433    my $cmt_class = $app->model('comment');
434    my $terms = { visible => 1 };
435    $terms->{blog_id} = $blog_id if $blog_id;
436    my $args = {
437        group => [
438            "extract(year from created_on)",
439            "extract(month from created_on)",
440            "extract(day from created_on)"
441        ],
442    };
443    if ( !$user->is_superuser && !$blog_id ) {
444        $args->{join} = MT::Permission->join_on(
445            undef,
446            {
447                blog_id   => \'= comment_blog_id',
448                author_id => $user_id
449            },
450        );
451    }
452    my $cmt_iter = $cmt_class->count_group_by( $terms, $args );
453
454    my %counts;
455    while ( my ( $count, $y, $m, $d ) = $cmt_iter->() ) {
456        my $date = sprintf( "%04d%02d%02dT00:00:00", $y, $m, $d );
457        $counts{$date} = $count;
458    }
459
460    %counts;
461}
462
4631;
Note: See TracBrowser for help on using the browser.