| 1 | # EstCrawler |
|---|
| 2 | # |
|---|
| 3 | # $Id$ |
|---|
| 4 | # |
|---|
| 5 | # This software is provided as-is. You may use it for commercial or |
|---|
| 6 | # personal use. If you distribute it, please keep this notice intact. |
|---|
| 7 | # |
|---|
| 8 | # Copyright (c) 2007 Hirotaka Ogawa |
|---|
| 9 | |
|---|
| 10 | package MT::Plugin::EstCrawler; |
|---|
| 11 | use strict; |
|---|
| 12 | use base qw(MT::Plugin); |
|---|
| 13 | |
|---|
| 14 | use MT; |
|---|
| 15 | use File::Basename qw(dirname); |
|---|
| 16 | use File::Spec; |
|---|
| 17 | |
|---|
| 18 | our $VERSION = '0.01'; |
|---|
| 19 | our $_OPTIMIZE = 0; |
|---|
| 20 | |
|---|
| 21 | my $plugin = __PACKAGE__->new({ |
|---|
| 22 | id => 'est_crawler', |
|---|
| 23 | name => 'EstCralwer', |
|---|
| 24 | description => q(<MT_TRANS phrase="EstCralwer automatically updates HyperEstraier database when posting and deleting entries.">), |
|---|
| 25 | doc_link => 'http://code.as-is.net/public/wiki/EstCrawler', |
|---|
| 26 | author_name => 'Hirotaka Ogawa', |
|---|
| 27 | author_link => 'http://as-is.net/blog/', |
|---|
| 28 | version => $VERSION, |
|---|
| 29 | system_config_template => 'system_config.tmpl', |
|---|
| 30 | settings => new MT::PluginSettings([ |
|---|
| 31 | ['est_db_path', { |
|---|
| 32 | Default => File::Spec->catdir(dirname(__FILE__), 'db'), |
|---|
| 33 | Scope => 'system' |
|---|
| 34 | }], |
|---|
| 35 | ]), |
|---|
| 36 | l10n_class => 'EstCrawler::L10N', |
|---|
| 37 | }); |
|---|
| 38 | MT->add_plugin($plugin); |
|---|
| 39 | |
|---|
| 40 | sub init_registry { |
|---|
| 41 | my $plugin = shift; |
|---|
| 42 | $plugin->registry({ |
|---|
| 43 | callbacks => { |
|---|
| 44 | 'MT::Entry::post_save' => \&post_save_entry, |
|---|
| 45 | 'MT::Page::post_save' => \&post_save_entry, |
|---|
| 46 | 'MT::Entry::post_remove' => \&post_remove_entry, |
|---|
| 47 | 'MT::Page::post_remove' => \&post_remove_entry, |
|---|
| 48 | }, |
|---|
| 49 | applications => { |
|---|
| 50 | cms => { |
|---|
| 51 | list_actions => { |
|---|
| 52 | entry => { |
|---|
| 53 | estraier_entry => { |
|---|
| 54 | label => $plugin->translate('Add to Estraier DB'), |
|---|
| 55 | code => \&entry_list_action, |
|---|
| 56 | permission => 'administer', |
|---|
| 57 | }, |
|---|
| 58 | }, |
|---|
| 59 | page => { |
|---|
| 60 | estraier_page => { |
|---|
| 61 | label => $plugin->translate('Add to Estraier DB'), |
|---|
| 62 | code => \&entry_list_action, |
|---|
| 63 | permission => 'administer', |
|---|
| 64 | }, |
|---|
| 65 | }, |
|---|
| 66 | }, |
|---|
| 67 | menus => { |
|---|
| 68 | 'estraier' => { |
|---|
| 69 | label => 'Estraier', |
|---|
| 70 | order => 900, |
|---|
| 71 | permission => 'administer', |
|---|
| 72 | }, |
|---|
| 73 | 'estraier:scanall' => { |
|---|
| 74 | label => $plugin->translate('Scan All'), |
|---|
| 75 | dialog => 'estraier_scanall', |
|---|
| 76 | order => 100, |
|---|
| 77 | permission => 'administer', |
|---|
| 78 | }, |
|---|
| 79 | 'estraier:cleanup' => { |
|---|
| 80 | label => $plugin->translate('Clean Up'), |
|---|
| 81 | dialog => 'estraier_cleanup', |
|---|
| 82 | order => 200, |
|---|
| 83 | permission => 'administer', |
|---|
| 84 | }, |
|---|
| 85 | }, |
|---|
| 86 | methods => { |
|---|
| 87 | 'estraier_scanall' => \&estraier_scanall, |
|---|
| 88 | 'estraier_cleanup' => \&estraier_cleanup, |
|---|
| 89 | }, |
|---|
| 90 | }, |
|---|
| 91 | }, |
|---|
| 92 | }); |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | sub load_tmpl { |
|---|
| 96 | my $plugin = shift; |
|---|
| 97 | my $tmpl = $plugin->SUPER::load_tmpl(@_); |
|---|
| 98 | $tmpl->text($plugin->translate_templatized($tmpl->text)); |
|---|
| 99 | $tmpl; |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | sub post_save_entry { |
|---|
| 103 | my $class = shift; |
|---|
| 104 | my ($app, $entry) = @_; |
|---|
| 105 | return unless $entry->isa('MT::Entry'); |
|---|
| 106 | |
|---|
| 107 | if ($entry->status == MT::Entry::RELEASE()) { |
|---|
| 108 | MT::Util::start_background_task(sub { add_entry($entry) }); |
|---|
| 109 | } else { |
|---|
| 110 | MT::Util::start_background_task(sub { delete_entry($entry) }); |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | sub post_remove_entry { |
|---|
| 115 | my $class = shift; |
|---|
| 116 | my ($app, $entry) = @_; |
|---|
| 117 | return unless $entry->isa('MT::Entry'); |
|---|
| 118 | MT::Util::start_background_task(sub { delete_entry($entry) }); |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | sub entry_list_action { |
|---|
| 122 | my $app = shift; |
|---|
| 123 | return $app->trans_error("Permission denied.") |
|---|
| 124 | unless $app->user->is_superuser; |
|---|
| 125 | MT::Util::start_background_task(sub { add_multiple_entries(@_) }); |
|---|
| 126 | $app->call_return; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | use Estraier; |
|---|
| 130 | |
|---|
| 131 | sub add_entry { |
|---|
| 132 | my $entry = shift; |
|---|
| 133 | my $db = new Database(); |
|---|
| 134 | my $dbpath = $plugin->get_config_value('est_db_path'); |
|---|
| 135 | $db->open($dbpath, Database::DBWRITER | Database::DBCREAT) |
|---|
| 136 | or $plugin->trans_error("Cannot open Estraier DB."); |
|---|
| 137 | $db->put_doc(entry_to_doc($entry), Database::PDCLEAN) |
|---|
| 138 | or $plugin->trans_error("Cannot add an entry to Estraier DB."); |
|---|
| 139 | $db->optimize() if $_OPTIMIZE; |
|---|
| 140 | $db->close() |
|---|
| 141 | or $plugin->trans_error("Cannot close Estraier DB."); |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | sub add_multiple_entries { |
|---|
| 145 | my @ids = @_; |
|---|
| 146 | my $db = new Database(); |
|---|
| 147 | my $dbpath = $plugin->get_config_value('est_db_path'); |
|---|
| 148 | $db->open($dbpath, Database::DBWRITER | Database::DBCREAT) |
|---|
| 149 | or $plugin->trans_error("Cannot open Estraier DB."); |
|---|
| 150 | my $iter = MT::Entry->load_iter({ |
|---|
| 151 | id => \@ids, |
|---|
| 152 | status => MT::Entry::RELEASE(), |
|---|
| 153 | }); |
|---|
| 154 | while (my $entry = $iter->()) { |
|---|
| 155 | $db->put_doc(entry_to_doc($entry), Database::PDCLEAN); |
|---|
| 156 | } |
|---|
| 157 | $db->optimize() if $_OPTIMIZE; |
|---|
| 158 | $db->close() |
|---|
| 159 | or $plugin->trans_error("Cannot close Estraier DB."); |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | sub delete_entry { |
|---|
| 163 | my $entry = shift; |
|---|
| 164 | my $id = uri_to_id($entry->permalink) |
|---|
| 165 | or $plugin->error($plugin->errstr || $plugin->translate('Cannot find an Estraier entry for entry [ID:_1]', $entry->id)); |
|---|
| 166 | if ($id > 0) { |
|---|
| 167 | my $db = new Database(); |
|---|
| 168 | my $dbpath = $plugin->get_config_value('est_db_path'); |
|---|
| 169 | $db->open($dbpath, Database::DBWRITER | Database::DBCREAT) |
|---|
| 170 | or $plugin->trans_error("Cannot open Estraier DB."); |
|---|
| 171 | $db->out_doc($id, Database::ODCLEAN) |
|---|
| 172 | or $plugin->trans_error("Cannot remove an entry from Estraier DB."); |
|---|
| 173 | $db->optimize() if $_OPTIMIZE; |
|---|
| 174 | $db->close() |
|---|
| 175 | or $plugin->trans_error("Cannot close Estraier DB."); |
|---|
| 176 | } |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | sub estraier_scanall { |
|---|
| 180 | my $app = shift; |
|---|
| 181 | return $app->trans_error("Permission denied.") |
|---|
| 182 | unless $app->user->is_superuser; |
|---|
| 183 | #my $blog_id = $app->param('blog_id'); |
|---|
| 184 | |
|---|
| 185 | MT::Util::start_background_task( |
|---|
| 186 | sub { |
|---|
| 187 | my $db = new Database(); |
|---|
| 188 | my $dbpath = $plugin->get_config_value('est_db_path'); |
|---|
| 189 | $db->open($dbpath, Database::DBWRITER | Database::DBCREAT) |
|---|
| 190 | or $plugin->trans_error("Cannot open Estraier DB."); |
|---|
| 191 | my $iter = MT::Entry->load_iter({ |
|---|
| 192 | status => MT::Entry::RELEASE(), |
|---|
| 193 | class => '*', |
|---|
| 194 | #$blog_id ? (blog_id => $blog_id) : (), |
|---|
| 195 | }); |
|---|
| 196 | while (my $entry = $iter->()) { |
|---|
| 197 | $db->put_doc(entry_to_doc($entry), Database::PDCLEAN); |
|---|
| 198 | } |
|---|
| 199 | $db->optimize() if $_OPTIMIZE; |
|---|
| 200 | $db->close() |
|---|
| 201 | or $plugin->trans_error("Cannot close Estraier DB."); |
|---|
| 202 | } |
|---|
| 203 | ); |
|---|
| 204 | |
|---|
| 205 | my $tmpl = $plugin->load_tmpl('dialog_scanall.tmpl'); |
|---|
| 206 | return $app->build_page($tmpl); |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | sub estraier_cleanup { |
|---|
| 210 | my $app = shift; |
|---|
| 211 | my $tmpl = $plugin->load_tmpl('dialog_cleanup.tmpl'); |
|---|
| 212 | return $app->build_page($tmpl); |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | sub estraier_cleanup_ { |
|---|
| 216 | my $app = shift; |
|---|
| 217 | return $app->trans_error("Permission denied.") |
|---|
| 218 | unless $app->user->is_superuser; |
|---|
| 219 | my $blog_id = $app->param('blog_id'); |
|---|
| 220 | |
|---|
| 221 | # not yet implemented |
|---|
| 222 | |
|---|
| 223 | $app->call_return; |
|---|
| 224 | } |
|---|
| 225 | |
|---|
| 226 | sub uri_to_id { |
|---|
| 227 | my $uri = shift; |
|---|
| 228 | my $db = new Database(); |
|---|
| 229 | my $dbpath = $plugin->get_config_value('est_db_path'); |
|---|
| 230 | $db->open($dbpath, Database::DBREADER) |
|---|
| 231 | or $plugin->trans_error("Cannot open Estraier DB."); |
|---|
| 232 | my $id = $db->uri_to_id($uri); |
|---|
| 233 | $db->close() |
|---|
| 234 | or $plugin->trans_error("Cannot close Estraier DB."); |
|---|
| 235 | $id; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | use MT::Util qw(remove_html ts2iso); |
|---|
| 239 | sub entry_to_doc { |
|---|
| 240 | my ($entry) = @_; |
|---|
| 241 | my $doc = new Document(); |
|---|
| 242 | |
|---|
| 243 | my $title = remove_html($entry->title) || ''; |
|---|
| 244 | my $author = remove_html($entry->author->nickname || $entry->author->name); |
|---|
| 245 | my $cdate = ts2iso($entry->blog, $entry->authored_on); |
|---|
| 246 | my $mdate = ts2iso($entry->blog, $entry->modified_on); |
|---|
| 247 | my $categories = join('', map { '[' . $_->label . ']' } @{$entry->categories}) |
|---|
| 248 | if $entry->categories; |
|---|
| 249 | my $tags = join('', map { '[' . $_ . ']' } $entry->tags) |
|---|
| 250 | if $entry->tags; |
|---|
| 251 | |
|---|
| 252 | # metainfo (attribute, not searchable) |
|---|
| 253 | $doc->add_attr('@uri', $entry->permalink); |
|---|
| 254 | $doc->add_attr('@title', $title); |
|---|
| 255 | $doc->add_attr('@author', $author); |
|---|
| 256 | $doc->add_attr('@cdate', $cdate); |
|---|
| 257 | $doc->add_attr('@mdate', $mdate); |
|---|
| 258 | $doc->add_attr('entry_id', $entry->id); |
|---|
| 259 | $doc->add_attr('blog_id', $entry->blog_id); |
|---|
| 260 | $doc->add_attr('categories', $categories) if $categories; |
|---|
| 261 | $doc->add_attr('tags', $tags) if $tags; |
|---|
| 262 | |
|---|
| 263 | # document body (searchable) |
|---|
| 264 | my $filters = $entry->text_filters; |
|---|
| 265 | push @$filters, '__default__' unless @$filters; |
|---|
| 266 | $doc->add_text(remove_html(MT->apply_text_filters($entry->text, $filters)) || ''); |
|---|
| 267 | $doc->add_text(remove_html(MT->apply_text_filters($entry->text_more, $filters)) || ''); |
|---|
| 268 | |
|---|
| 269 | # metainfo (hidden, searchable) |
|---|
| 270 | $doc->add_hidden_text($title); |
|---|
| 271 | $doc->add_hidden_text($author); |
|---|
| 272 | $doc->add_hidden_text($categories || ''); |
|---|
| 273 | $doc->add_hidden_text($tags || ''); |
|---|
| 274 | $doc->add_hidden_text(remove_html($entry->keywords) || ''); |
|---|
| 275 | $doc; |
|---|
| 276 | } |
|---|
| 277 | |
|---|
| 278 | 1; |
|---|