root/branches/StyleCatcher-2.1-dev/plugins/StyleCatcher/lib/StyleCatcher/CMS.pm @ 1040

Revision 1040, 24.5 kB (checked in by mpaschal, 15 months ago)

Use Byrne's strategy of using the blog's real template set, since it's going to be set while the themes may not declare tsets

Line 
1# Movable Type (r) Open Source (C) 2005-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id: CMS.pm 2576 2008-06-14 00:35:33Z bchoate $
6
7package StyleCatcher::CMS;
8
9use strict;
10use File::Basename qw(basename);
11
12use MT::Util qw( remove_html decode_html );
13
14our $DEFAULT_STYLE_LIBRARY;
15
16sub style_library {
17    return MT->registry("stylecatcher_libraries");
18}
19
20sub file_mgr {
21    my $app = MT->instance;
22    require MT::FileMgr;
23    my $filemgr = MT::FileMgr->new('Local')
24      or return $app->error( MT::FileMgr->errstr );
25    $filemgr;
26}
27
28sub listify {
29    my ($data) = @_;
30    my @list;
31    foreach my $k (keys %$data) {
32        my %entry = %{ $data->{$k} };
33        $entry{key} = $k;
34        delete $entry{plugin};
35        $entry{label} = $entry{label}->() if ref($entry{label});
36        $entry{description_label} = $entry{description_label}->() if ref($entry{description_label});
37        push @list, \%entry;
38    }
39    @list = sort { $a->{order} <=> $b->{order} } @list;
40    \@list;
41}
42
43sub view {
44    my $app     = shift;
45    my $blog_id = $app->param('blog_id');
46    $app->return_to_dashboard( redirect => 1 ) unless $blog_id;
47
48    my $blog = MT::Blog->load($blog_id);
49    return $app->errtrans("Invalid request") unless $blog;
50
51    my $static_path = $app->static_file_path;
52    if (! -d $static_path ) {
53        return $app->errtrans("Your mt-static directory could not be found. Please configure 'StaticFilePath' to continue.");
54    }
55
56    my $themeroot =
57      File::Spec->catdir( $app->static_file_path, 'support', 'themes' );
58    my $webthemeroot = $app->static_path . 'support/themes';
59    my $stylelibrary = listify(style_library());
60    my $theme_data   = make_themes();
61    my $styled_blogs = fetch_blogs();
62
63    my $config = plugin()->get_config_hash();
64
65    my @blog_loop;
66    my %current_themes;
67    my ($blog_theme, $blog_layout);
68    foreach my $blog (@$styled_blogs) {
69        my $curr_theme = $config->{"current_theme_" . $blog->id} || '';
70        my $curr_layout = $config->{"current_layout_" . $blog->id} || 'layout-wtt';
71        push @blog_loop,
72          {
73            blog_id   => $blog->id,
74            blog_name => $blog->name,
75            layout    => $curr_layout,
76            theme_id  => $curr_theme,
77            view_link => $blog->site_url,
78          };
79        if ($blog->id == $blog_id) {
80            $blog_theme = $curr_theme;
81            $blog_layout = $curr_layout;
82        }
83        if ( $theme_data->{themes} && $curr_theme ) {
84            foreach my $theme ( @{ $theme_data->{themes} } ) {
85                if ( ($theme->{prefix} || '') . ':' . $theme->{name} eq $curr_theme ) {
86                    push @{ $theme->{blogs} }, $blog->id;
87                    next if exists $current_themes{ $theme->{name} };
88                    $current_themes{ $theme->{name} } = 1;
89                    push @{ $theme->{tags} }, 'collection:current';
90                }
91            }
92        }
93    }
94
95    push @{ $theme_data->{categories} }, 'current'
96      if %current_themes;
97
98    require JSON;
99    my $url   = $app->param('url');
100    my %param = (
101        version     => plugin()->version,
102        # blog_loop   => \@blog_loop,
103        blog_id => $blog_id,
104        themes_json => JSON::objToJson(
105            $theme_data, { pretty => 1, indent => 2, delimiter => 1 }
106        ),
107        auto_fetch => $url ? 1 : 0,
108        style_library => $stylelibrary,
109        current_theme => $blog_theme || '',
110        current_layout => $blog_layout || 'layout-wtt',
111        dynamic_blog => (($blog->custom_dynamic_templates || '') eq 'all'),
112    );
113
114    if ( $blog_id && @$styled_blogs ) {
115        my $blog = $styled_blogs->[0];
116        $param{blog_name} = $blog->name;
117        $param{blog_url}  = $blog->site_url;
118    }
119
120    my $path = $app->static_path;
121    $path .= '/' unless $path =~ m!/$!;
122    $path .= plugin()->envelope . "/";
123    $path = $app->base . $path if $path =~ m!^/!;
124    $param{plugin_static_uri} = $path;
125
126    $app->build_page( 'view.tmpl', \%param );
127}
128
129# AJAX/JSON modes
130
131# returns a json structure of styles given a particular url
132sub js {
133    # ydnar's remixer uses javascript files for each collection of styles -
134    # we generate these js files from css metadata
135    # StyleCatcher will pick up any metadata in the theme css file in the
136    # format of 'key: value' in comment-space
137    # The remixer only uses name, author, description at the moment.
138    my $app = shift;
139    return $app->json_error( $app->errstr ) unless $app->validate_magic;
140
141    my $data = fetch_themes($app->param('url'))
142        or return $app->json_error( $app->errstr );
143    return $app->json_result( $data );
144}
145
146sub files_from_response {
147    my ($res, %param) = @_;
148
149    my $extensions = $param{css} ? qr{ (?:gif|jpe?g|png|css) }xms
150                   :               qr{ (?:gif|jpe?g|png)     }xms
151                   ;
152
153    my $stylesheet = $res->content;
154    $stylesheet =~ s!/\*.*?\*/!!gs;    # strip all comments first
155    my @images = $stylesheet =~ m{
156        \b url\( \s*                          # opening url() reference
157        ['"]?
158        ( [\w\.\-/]+\.$extensions )  # a filename ending in an image extension
159        ['"]?
160        \s* \)                                # close of url() reference
161    }xmsgi;
162
163    return @images;
164}
165
166sub download_theme {
167    my $app = shift;
168    my ($url) = @_;
169
170    my $static_path = $app->static_file_path;
171    my $themeroot   = File::Spec->catdir($static_path, 'support', 'themes');
172    my $ua          = $app->new_ua();
173    my $filemgr     = file_mgr()
174        or return;
175
176    my @url = split( /\//, $url );
177    my $stylesheet_filename = pop @url;
178    my $theme_url = join(q{/}, @url) . '/';
179
180    my ($basename, $extension) = split /\./, $stylesheet_filename;
181    if ($basename eq 'screen' || $basename eq 'style') {
182        $basename = $url[-1];
183    }
184
185    # Pick up the stylesheet
186    my $stylesheet_res = $ua->get($url);
187
188    my @images = files_from_response($stylesheet_res, css => 1);
189
190    my $theme_path = File::Spec->catdir($themeroot, $basename);
191    if (!$filemgr->mkpath($theme_path)) {
192        my $error = $app->translate("Could not create [_1] folder - Check that your 'themes' folder is webserver-writable.",
193            $basename);
194        return $app->json_error($error);
195    }
196
197    $filemgr->put_data( $stylesheet_res->content,
198        File::Spec->catfile($theme_path, $basename . '.css') );
199
200    # Pick up the images we parsed earlier and write them to the theme folder
201    my %got_files;
202    my @files = ('thumbnail.gif', 'thumbnail-large.gif', @images);
203    FILE: while (my $rel_url = shift @files) {
204        # Is this safe to get?
205        my $full_url = URI->new_abs($rel_url, $theme_url);
206        next FILE if !$full_url;
207        my $url = $full_url->as_string();
208        next FILE if $url !~ m{ \A \Q$theme_url\E }xms;
209
210        next FILE if $got_files{$url};
211        $got_files{$url} = 1;
212        my $res = $ua->get($url);
213
214        # Skip files that don't download; we were accidentally doing so already.
215        next FILE if !$res->is_success();
216
217        my $canon_rel_url = URI->new($rel_url)->rel($theme_url);
218        my @image_path = split /\//, $canon_rel_url->as_string();
219        my $image_filename = pop @image_path;
220
221        my $image_path = File::Spec->catdir($theme_path, @image_path);
222        if (!$filemgr->exists($image_path) && !$filemgr->mkpath($image_path)) {
223            my $error = $app->translate("Could not create [_1] folder - Check that your 'themes' folder is webserver-writable.",
224                $basename);
225            return $app->json_error($error);
226        }
227
228        my $image_full_path = File::Spec->catfile($image_path, $image_filename);
229        $filemgr->put_data($res->content, $image_full_path, 'upload')
230          or return $app->json_error( $filemgr->errstr );
231
232        if ($image_filename =~ m{ \.css \z }xmsi) {
233            my @new_files = files_from_response($res, css => 0);
234            # Schedule these as full URLs so relative references aren't
235            # misabsolved relative to the theme directory.
236            @new_files = map {
237                my $uri = URI->new_abs($_, $url);
238                $uri ? $uri->as_string() : ();
239            } @new_files;
240            push @files, @new_files;
241        }
242    }
243
244    return $basename;
245}
246
247# does the work after user selects a particular theme to apply to a blog
248sub apply {
249    my $app = shift;
250
251    my ($blog_id, $url, $layout, $name, $template_set)
252        = map { $app->param($_) || q{} } (qw( blog_id url layout name template_set ));
253
254    # Load the default stylesheet for this blog
255    my $tmpl = load_style_template($blog_id);
256
257    $app->validate_magic or return $app->json_error($app->translate("Invalid request"));
258    return $app->json_error($app->translate("Invalid request"))
259      unless $blog_id && $url && $tmpl;
260
261    my $static_path = $app->static_file_path;
262    if (! -d $static_path ) {
263        return $app->json_error($app->translate("Your mt-static directory could not be found. Please configure 'StaticFilePath' to continue."));
264    }
265
266    # if this isn't a local url, then we have to grab some files from
267    # yonder...
268    my $static_url = $app->static_path;
269    if ( $url !~ m{ \A \Q$static_url\E (?:support/)? themes/ }xms ) {
270        my $basename = download_theme($app, $url)
271            or return;
272        $url = "${static_url}support/themes/$basename/$basename.css";
273    }
274
275    my $blog = MT->model('blog')->load($blog_id)
276      or return $app->json_error( $app->translate('No such blog [_1]', $blog_id) );
277    my $blog_tset = $blog->template_set;
278    my $base_css_url = MT->registry('template_sets')->{$blog_tset}->{base_css};
279
280    my $base_css = q{};
281    if ($base_css_url) {
282        my $uri = URI->new_abs($base_css_url, $app->static_path);
283        $base_css = '@import url(' . $uri->as_string() . ');'
284            if $uri;
285    }
286
287    # Replacing the theme import or adding a new one at the beginning
288    my $template_text  = $tmpl->text();
289    my $replaced       = 0;
290    my $header = '/* This is the StyleCatcher theme addition. Do not remove this block. */';
291    my $footer = '/* end StyleCatcher imports */';
292    my $styles = <<"EOT";
293$header
294$base_css
295\@import url($url);
296$footer
297EOT
298    if ($template_text =~ s/\Q$header\E.*\Q$footer\E/$styles/s) {
299        $tmpl->text( $template_text );
300        $replaced = 1;
301    }
302    unless ($replaced) {
303
304        # we're dealing with a template that wasn't modified before now
305        # we will need to backup the existing one to make sure the new
306        # style is applied properly.
307        my @ts = MT::Util::offset_time_list( time, $blog_id );
308        my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d", $ts[5] + 1900,
309          $ts[4] + 1, @ts[ 3, 2, 1, 0 ];
310        my $backup = $tmpl->clone;
311        delete $backup->{column_values}
312          {id};    # make sure we don't overwrite original
313        delete $backup->{changed_cols}{id};
314        $backup->name( $backup->name . ' (Backup from ' . $ts . ')' );
315        $backup->outfile('');
316        $backup->linked_file( $tmpl->linked_file );
317        $backup->rebuild_me(0);
318        $backup->build_dynamic(0);
319        $backup->identifier(undef);
320        $backup->type('backup');
321        $backup->save;
322        $tmpl->linked_file('');    # make sure this one isn't linked now
323        $tmpl->identifier('styles');
324        $tmpl->text($styles);
325    }
326
327    # Putting the stylesheet back together again
328    $tmpl->save or return $app->json_error( $tmpl->errstr );
329
330    $blog->page_layout($layout);
331    $blog->touch();
332    $blog->save();
333
334    # rebuild only the stylesheet! forcibly. with prejudice.
335    $app->rebuild_indexes(
336        BlogID   => $tmpl->blog_id,
337        Template => $tmpl,
338        Force    => 1
339    );
340
341    my $p = plugin();
342    $name =~ s/^repo_\d+:/local:/;
343    $name =~ s/\.css$//;
344    $p->set_config_value('current_theme_' . $blog_id, $name);
345    if ($layout) {
346        $p->set_config_value('current_layout_' . $blog_id, $layout);
347    } else {
348        $p->set_config_value('current_layout_' . $blog_id, undef);
349    }
350
351    return $app->json_result(
352        {
353            message =>
354              $app->translate("Successfully applied new theme selection.")
355        }
356    );
357}
358
359# Utility methods
360
361sub fetch_blogs {
362    my $app     = MT->app;
363    my $user    = $app->user;
364    my $blog_id = $app->param('blog_id');
365
366    my @blogs;
367    if ($blog_id) {
368        @blogs = MT::Blog->load($blog_id);
369    } else {
370        if ( $user->is_superuser() ) {
371            if ($blog_id) {
372                @blogs = MT::Blog->load($blog_id);
373            }
374        }
375        else {
376            my $args = { author_id => $user->id };
377            $args->{blog_id} = $blog_id if $blog_id;
378            require MT::Permission;
379            my @perms = MT::Permission->load( { author_id => $user->id } );
380            foreach my $perm (@perms) {
381                next unless $perm->can_edit_templates;
382                push @blogs, MT::Blog->load( $perm->blog_id );
383            }
384        }
385    }
386    my @styled_blogs;
387    foreach my $blog (@blogs) {
388        my $tmpl = load_style_template( $blog->id );
389        if ($tmpl) {
390            push @styled_blogs, $blog;
391        }
392    }
393    @styled_blogs = sort { $a->name cmp $b->name } @styled_blogs;
394
395    \@styled_blogs;
396}
397
398sub load_style_template {
399    my ($blog_id) = @_;
400
401    require MT::Template;
402    my $tmpl;
403
404    $tmpl = MT::Template->load(
405        {
406            blog_id    => $blog_id,
407            identifier => 'styles'
408        }
409    );
410
411    $tmpl ||= MT::Template->load(
412        {
413            blog_id => $blog_id,
414            outfile => "styles.css"
415        }
416    );
417
418    # MT 3.x era stylesheet file
419    $tmpl ||= MT::Template->load(
420        {
421            blog_id => $blog_id,
422            outfile => "styles-site.css"
423        }
424    );
425
426    unless ($tmpl) {
427
428        # Create one since we didn't find a candidate
429        $tmpl = new MT::Template;
430        $tmpl->blog_id($blog_id);
431        $tmpl->name('Stylesheet');
432        $tmpl->type('index');
433        $tmpl->identifier('styles');
434        $tmpl->outfile("styles.css");
435        $tmpl->text(<<'EOT');
436@import url(<$MTStaticWebPath$>themes-base/blog.css);
437@import url(<$MTStaticWebPath$>themes/minimalist-red/styles.css);
438EOT
439        $tmpl->save();
440    }
441
442    $tmpl;
443}
444
445# pulls a list of themes available from a particular url
446sub fetch_themes {
447    my $app = MT->app;
448    my ($url) = @_;
449    return undef unless $url;
450
451    my $blog_id = $app->param('blog_id');
452    my $data    = {};
453
454  # If we have a url then we're specifying a specific theme (css) or repo (html)
455    # Pick up the file (html with <link>s or a css file with metadata)
456    my $user_agent = $app->new_ua;
457    my $request    = HTTP::Request->new( GET => $url );
458    my $response   = $user_agent->request($request);
459
460    # Make a repo if you've got a ton of links or an automagic entry if
461    # you're a css file
462    my $type = $response->headers->{'content-type'};
463    $type = shift @$type if ref $type eq 'ARRAY';
464    if ( $type =~ m!^text/css! ) {
465        $data->{auto}{url} = $url;
466        my $theme = fetch_theme(
467            url  => $url,
468            tags => ['collection:auto'],
469        );
470        $data->{themes} = [$theme];
471    }
472    elsif ( $type =~ m!^text/html! ) {
473        my @repo_themes;
474        for my $link (
475            ref( $response->headers->{'link'} ) eq 'ARRAY'
476            ? @{ $response->headers->{'link'} }
477            : $response->headers->{'link'}
478          )
479        {
480            my ( $css, @parsed_link ) = split( /;/, $link );
481            $css =~ s/[<>]//g;
482            my %attr;
483            foreach (@parsed_link) {
484                my ( $name, $val ) = split /=/, $_, 2;
485                $name =~ s/^ //;
486                $val  =~ s/^['"]|['"]$//g;
487                next if $name eq '/';
488                $attr{ lc($name) } = $val;
489            }
490            next unless lc $attr{rel}  eq 'theme';
491            next unless lc $attr{type} eq 'text/x-theme';
492
493            # Fix for relative theme locations
494            if ($css !~ m!^https?://!) {
495                my $new_css = $url;
496                $new_css =~ s!/[a-z0-9_-]+\.[a-z]+?$|/$!/!;
497                $new_css .= $css;
498                $css = $new_css;
499            }
500            push @repo_themes, $css;
501        }
502
503        my $themes = [];
504        for my $repo_theme (@repo_themes) {
505            my $theme = fetch_theme(
506                url => $repo_theme,
507            );
508            push @$themes, $theme if $theme;
509        }
510        $data->{themes} = $themes;
511        if ( $data->{repo}{display_name} = $response->headers->{'title'} ) {
512            $data->{repo}{name} =
513              MT::Util::dirify( $data->{repo}{display_name} );
514        }
515        else {
516            $data->{repo}{display_name} = $url;
517            $data->{repo}{name}         = MT::Util::dirify($url);
518        }
519        $data->{repo}{url} = $url;
520    }
521    else {
522        return $app->error( $app->translate('Invalid URL: [_1]', $url) );
523    }
524
525    $data;
526}
527
528# sets up the object structure we return through json to populate
529# the mixer.
530sub make_themes {
531    my $app = MT->instance;
532
533    # categories
534    #   current    (for active theme)
535    #   repo       (for themes found at repo link)
536    #   my-designs (for themes that are stored locally)
537    #   mt-designs (for themes that are local and installed by default)
538    #   auto       (for link to a single css file)
539
540    # structure of "data"
541    #   categories => [ one, two, three ]  ie: 'current', 'repo'
542    #   themes => [
543    #       { theme }
544    #   ]
545    #   repo => {
546    #       display_name => 'display name',
547    #       name => 'repo name',
548    #       url => 'url of repo',
549    #   }
550
551# structure of "theme"
552#   theme => {
553#       name => 'theme_dir',
554#       imageSmall => 'link_to/thumbnail.gif',
555#       imageBig => 'link_to/thumbnail-large.gif',
556#       title => 'Theme Title',
557#       description => 'Theme description.',
558#       url_css => 'link_to/theme.css',
559#       url_zip => 'link_to/theme.zip',
560#       author => 'Author Name',
561#       author_url => 'http://author.com/'
562#       author_affiliation => 'Author Co.',
563#       layouts => "comma,delimited,layout,list"
564#       sort => 'theme_sortable_name',
565#       tags => ['association:tag']  ie, 'color:blue', 'designer:author', 'collection:repo'
566#   }
567
568    my ( $categories, $themes );
569    my $sys_root = File::Spec->catdir( $app->static_file_path, 'themes' );
570
571    # Generate our list of themes within the themeroot directory
572    my @sys_list = glob( File::Spec->catfile( $sys_root, "*" ) );
573    $categories->{'mt-designs'} = 1 if @sys_list;
574    for my $theme (@sys_list) {
575        my $theme_dir = $theme;
576        my $theme_url = $app->static_path . 'themes';
577        next unless -d $theme;
578        $theme =~ s/.*[\\\/]//;
579        $themes->{$theme} = fetch_theme(
580            url      => $theme_dir,
581            tags     => ['collection:mt-designs'],
582            baseurl  => $theme_url,
583            basepath => $theme_dir,
584        );
585        $themes->{$theme}{name} = $themes->{$theme}{name};
586        $themes->{$theme}{prefix} = 'default';
587    }
588
589    my $themeroot =
590      File::Spec->catdir( $app->static_file_path, 'support', 'themes' );
591
592    # Generate our list of themes within the themeroot directory
593    my @themeroot_list = glob( File::Spec->catfile( $themeroot, "*" ) );
594    $categories->{'my-designs'} = 1 if @themeroot_list;
595    for my $theme (@themeroot_list) {
596        my $theme_dir = $theme;
597        next unless -d $theme;
598        $theme =~ s/.*[\\\/]//;
599        $themes->{$theme} = fetch_theme(
600            url  => $theme_dir,
601            tags => ['collection:my-designs'],
602        );
603        $themes->{$theme}{prefix} = 'local';
604    }
605
606    my $data = {
607        categories => [ keys %$categories ],
608        themes     => [ values %$themes ]
609    };
610
611    $data;
612}
613
614sub fetch_theme {
615    my $app = MT->app;
616    my %param = @_;
617    my ($url, $tags, $baseurl, $basepath, $default_metadata)
618        = @param{qw( url tags baseurl basepath metadata )};
619    $tags ||= [];
620
621    my $theme;
622    my $stylesheet;
623    my $new_url;
624    my $themeroot;
625    my $url_is_web = $url =~ m{ \A https?: }xms ? 1 : 0;
626    if ($url_is_web) {
627        # Pick up the css file
628        my $user_agent  = $app->new_ua;
629        my $css_request = HTTP::Request->new( GET => $url );
630        my $response    = $user_agent->request($css_request);
631        $stylesheet = $response->content if ($response->code >= 200) && ($response->code < 400);
632        return unless $stylesheet;
633
634# Break up the css url in to a couple useful pieces (generalize and break me out)
635        $theme = $url;
636        # discard any generic 'screen.css' filename
637        $theme =~ s{ / (?:screen|style) \.css \z }{}xms;
638        $theme =~ s/.*[\\\/]//;
639        my @url = split( /\//, $url );
640        for ( 0 .. ( scalar(@url) - 2 ) ) {
641            $new_url .= $url[$_] . '/';
642        }
643    }
644    else {
645        $themeroot = $basepath
646          || File::Spec->catdir( $app->static_file_path, 'support', 'themes' );
647        my $webthemeroot = $baseurl || $app->static_path . 'support/themes';
648
649        $theme = $url;
650        $theme =~ s/.*[\\\/]//;
651        my $file = File::Spec->catfile( $url, "$theme.css" );
652        $new_url = "$webthemeroot/$theme/";
653        if ( -e $file ) {
654            $stylesheet = file_mgr()->get_data($file);
655            $url        = $new_url . "$theme.css";
656        }
657        else {
658            $file = File::Spec->catfile( $url, $theme, "screen.css" );
659            if ( -e $file ) {
660                $stylesheet = file_mgr()->get_data($file);
661                $url        = $new_url . "screen.css";
662            }
663        }
664    }
665
666    # Pick up the metadata from the css
667    my @css_lines = split( /\r?\n/, $stylesheet || '' );
668    my $commented = 0;
669    my @comments;
670    for my $line (@css_lines) {
671        my $pos;
672        $pos = index( $line, "/*" );
673        unless ( $pos == -1 ) {
674            $line = substr( $line, $pos + 2 );
675            $commented = 1;
676        }
677        if ($commented) {
678            $pos = index( $line, "*/" );
679            unless ( $pos == -1 ) {
680                $line = substr( $line, 0, $pos );
681                $commented = 0;
682            }
683            push @comments, $line;
684        }
685    }
686
687    my $comment;
688    my %metadata;
689
690    # Trim me white space, yarr
691    for (@comments) {
692
693        # TBD: strip any "risky" content; we don't want any
694        # XSS in this content.
695        # Strip any null bytes
696        tr/\x00//d;
697        s/^\s+|\s+$//g;
698        my ( $key, $value ) = split( /:/, $_, 2 ) or next;
699        next unless defined $value;
700        $value =~ s/^\s+//;
701        $metadata{ lc $key } = $value;
702    }
703
704    my %thumbnails;
705    THUMB: for my $thumb (qw( thumbnail thumbnail_large )) {
706        $thumbnails{$thumb} = $metadata{$thumb};
707        next THUMB if $thumbnails{$thumb};
708
709        my $thumb_filename = $thumb;
710        $thumb_filename =~ tr/_/-/;
711        $thumb_filename .= '.gif';
712
713        if ($url_is_web) {
714            my $thumb_url = $new_url . $thumb_filename;
715
716            my $user_agent  = $app->new_ua;
717            my $css_request = HTTP::Request->new( HEAD => $thumb_url );
718            my $response    = $user_agent->request($css_request);
719            if ($response->is_success()) {
720                $thumbnails{$thumb} = $thumb_url;
721            }
722        }
723        else {
724            my $thumb_path = File::Spec->catfile($themeroot, $theme,
725                $thumb_filename);
726            if (-e $thumb_path) {
727                $thumbnails{$thumb} = $new_url . $thumb_filename;
728            }
729        }
730
731        $thumbnails{$thumb} ||= $app->static_path . 'plugins/StyleCatcher/'
732            . 'images/' . $thumb_filename;
733    }
734
735    my %field_map = (
736        title        => [ 'name',         'theme name' ],
737        author       => [ 'designer',     'author' ],
738        author_url   => [ 'designer_url', 'author_url', 'author uri' ],
739        template_set => [ 'template_set', 'template' ],
740        description  => [ 'description' ],
741    );
742    while (my ($best_name, $possible_names) = each %field_map) {
743        ($metadata{$best_name}) = grep { defined }
744            delete @metadata{ @$possible_names }, q{};
745        $metadata{$best_name} = decode_html(remove_html($metadata{$best_name}));
746    }
747
748    require MT::Util;
749    my $data = {
750        name         => $theme,
751        description  => $metadata{description} || q{},
752        title        => $metadata{title} || '(Untitled)',
753        url          => $url,
754        imageSmall   => $thumbnails{thumbnail},
755        imageBig     => $thumbnails{thumbnail_large},
756        layouts      => $metadata{layouts} || q{},
757        sort         => $metadata{name} || $theme || q{},
758        tags         => $tags,
759        blogs        => [],
760        author       => $metadata{author},
761        author_url   => $metadata{author_url},
762        template_set => $metadata{template_set},
763        author_affiliation => $metadata{author_affiliation} || q{},
764    };
765    $data;
766}
767
768sub plugin {
769    return MT->component('StyleCatcher');
770}
771
7721;
Note: See TracBrowser for help on using the browser.