| 1 | package MT::CMS::Tools; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use Symbol; |
|---|
| 5 | |
|---|
| 6 | use MT::I18N qw( encode_text wrap_text ); |
|---|
| 7 | use MT::Util qw( encode_url encode_html decode_html encode_js ); |
|---|
| 8 | |
|---|
| 9 | sub system_check { |
|---|
| 10 | my $app = shift; |
|---|
| 11 | |
|---|
| 12 | if ( my $blog_id = $app->param('blog_id') ) { |
|---|
| 13 | return $app->redirect( |
|---|
| 14 | $app->uri( |
|---|
| 15 | 'mode' => 'view_log', |
|---|
| 16 | args => { blog_id => $blog_id } |
|---|
| 17 | ) |
|---|
| 18 | ); |
|---|
| 19 | } |
|---|
| 20 | |
|---|
| 21 | my %param; |
|---|
| 22 | # licensed user count: someone who has logged in within 90 days |
|---|
| 23 | my $sess_class = $app->model('session'); |
|---|
| 24 | my $from = time - ( 60 * 60 * 24 * 90 + 60 * 60 * 24 ); |
|---|
| 25 | $sess_class->remove( |
|---|
| 26 | { kind => 'UA', start => [ undef, $from ] }, |
|---|
| 27 | { range => { start => 1 } } |
|---|
| 28 | ); |
|---|
| 29 | $param{licensed_user_count} = $sess_class->count( { kind => 'UA' } ); |
|---|
| 30 | |
|---|
| 31 | my $author_class = $app->model('author'); |
|---|
| 32 | $param{user_count} = $author_class->count( |
|---|
| 33 | { type => MT::Author::AUTHOR() } ); |
|---|
| 34 | |
|---|
| 35 | # commeters: users with only comment permission and MT::Author::COMMENTER |
|---|
| 36 | my $cmntrs = $author_class->count( |
|---|
| 37 | { type => MT::Author::COMMENTER() } ); |
|---|
| 38 | |
|---|
| 39 | my @perms = $app->model('permission')->load( |
|---|
| 40 | { |
|---|
| 41 | permissions => "%'comment'%", |
|---|
| 42 | blog_id => '0', |
|---|
| 43 | }, |
|---|
| 44 | { |
|---|
| 45 | 'like' => { 'permissions' => 1 }, |
|---|
| 46 | 'not' => { 'blog_id' => 1 }, |
|---|
| 47 | } |
|---|
| 48 | ); |
|---|
| 49 | @perms = grep { $_->permissions =~ m/'comment'/ } @perms; |
|---|
| 50 | $param{commenter_count} = scalar(@perms) + $cmntrs; |
|---|
| 51 | $param{screen_id} = "system-check"; |
|---|
| 52 | $param{syscheck_html} = get_syscheck_content($app) || ''; |
|---|
| 53 | |
|---|
| 54 | $app->load_tmpl( 'system_check.tmpl', \%param ); |
|---|
| 55 | } |
|---|
| 56 | |
|---|
| 57 | sub get_syscheck_content { |
|---|
| 58 | my $app = shift; |
|---|
| 59 | |
|---|
| 60 | my $syscheck_url = $app->base . $app->mt_path . $app->config('CheckScript') . |
|---|
| 61 | '?view=tools&version=' . MT->version_id; |
|---|
| 62 | if ( $syscheck_url && $syscheck_url ne 'disable' ) { |
|---|
| 63 | my $SYSCHECKCACHE_TIMEOUT = 60 * 60 * 24; |
|---|
| 64 | my $sess_class = $app->model('session'); |
|---|
| 65 | my ($syscheck_object) = (""); |
|---|
| 66 | my $retries = 0; |
|---|
| 67 | $syscheck_object = $sess_class->load( { id => 'SC' } ); |
|---|
| 68 | if ( $syscheck_object |
|---|
| 69 | && ( $syscheck_object->start() < ( time - $SYSCHECKCACHE_TIMEOUT ) ) ) |
|---|
| 70 | { |
|---|
| 71 | $syscheck_object->remove; |
|---|
| 72 | $syscheck_object = undef; |
|---|
| 73 | } |
|---|
| 74 | return encode_text( $syscheck_object->data(), 'utf-8', undef ) |
|---|
| 75 | if ($syscheck_object); |
|---|
| 76 | |
|---|
| 77 | my $ua = $app->new_ua({ timeout => 20 }); |
|---|
| 78 | return unless $ua; |
|---|
| 79 | $ua->max_size(undef) if $ua->can('max_size'); |
|---|
| 80 | |
|---|
| 81 | my $req = new HTTP::Request( GET => $syscheck_url ); |
|---|
| 82 | my $resp = $ua->request($req); |
|---|
| 83 | return unless $resp->is_success(); |
|---|
| 84 | my $result = $resp->content(); |
|---|
| 85 | if ($result) { |
|---|
| 86 | require MT::Sanitize; |
|---|
| 87 | |
|---|
| 88 | # allowed html |
|---|
| 89 | my $spec = '* style class id,ul,li,div,span,br,h2,h3,strong,code,blockquote,p'; |
|---|
| 90 | $result = MT::Sanitize->sanitize( $result, $spec ); |
|---|
| 91 | $syscheck_object = MT::Session->new(); |
|---|
| 92 | $syscheck_object->set_values( |
|---|
| 93 | { |
|---|
| 94 | id => 'SC', |
|---|
| 95 | kind => 'SC', |
|---|
| 96 | start => time(), |
|---|
| 97 | data => $result |
|---|
| 98 | } |
|---|
| 99 | ); |
|---|
| 100 | $syscheck_object->save(); |
|---|
| 101 | $result = encode_text( $result, 'utf-8', undef ); |
|---|
| 102 | } |
|---|
| 103 | return $result; |
|---|
| 104 | } |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | sub start_recover { |
|---|
| 108 | my $app = shift; |
|---|
| 109 | $app->add_breadcrumb( $app->translate('Password Recovery') ); |
|---|
| 110 | $app->load_tmpl('dialog/recover.tmpl'); |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | sub recover_password { |
|---|
| 114 | my $app = shift; |
|---|
| 115 | my $q = $app->param; |
|---|
| 116 | my $name = $q->param('name'); |
|---|
| 117 | my $class = ref $app eq 'MT::App::Upgrader' ? 'MT::BasicAuthor' : $app->model('author'); |
|---|
| 118 | eval "use $class;"; |
|---|
| 119 | my @author = $class->load( { name => $name } ); |
|---|
| 120 | my $author; |
|---|
| 121 | foreach (@author) { |
|---|
| 122 | next unless $_->password && ( $_->password ne '(none)' ); |
|---|
| 123 | $author = $_; |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | my ( $rc, $res ) = |
|---|
| 127 | reset_password( $app, $author, $q->param('hint'), $name ); |
|---|
| 128 | |
|---|
| 129 | if ($rc) { |
|---|
| 130 | $app->add_breadcrumb( $app->translate('Password Recovery') ); |
|---|
| 131 | $app->load_tmpl( |
|---|
| 132 | 'dialog/recover.tmpl', |
|---|
| 133 | { |
|---|
| 134 | recovered => 1, |
|---|
| 135 | email => $author->email |
|---|
| 136 | } |
|---|
| 137 | ); |
|---|
| 138 | } |
|---|
| 139 | else { |
|---|
| 140 | $app->error($res); |
|---|
| 141 | } |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | sub do_list_action { |
|---|
| 145 | my $app = shift; |
|---|
| 146 | $app->validate_magic or return; |
|---|
| 147 | |
|---|
| 148 | # plugin_action_selector should always (?) be in the query; use it? |
|---|
| 149 | my $action_name = $app->param('action_name'); |
|---|
| 150 | my $type = $app->param('_type'); |
|---|
| 151 | my ($the_action) = |
|---|
| 152 | ( grep { $_->{key} eq $action_name } @{ $app->list_actions($type) } ); |
|---|
| 153 | return $app->errtrans( "That action ([_1]) is apparently not implemented!", |
|---|
| 154 | $action_name ) |
|---|
| 155 | unless $the_action; |
|---|
| 156 | |
|---|
| 157 | unless ( ref( $the_action->{code} ) ) { |
|---|
| 158 | if ( my $plugin = $the_action->{plugin} ) { |
|---|
| 159 | $the_action->{code} = |
|---|
| 160 | $app->handler_to_coderef( $the_action->{handler} |
|---|
| 161 | || $the_action->{code} ); |
|---|
| 162 | } |
|---|
| 163 | } |
|---|
| 164 | $the_action->{code}->($app); |
|---|
| 165 | } |
|---|
| 166 | |
|---|
| 167 | sub do_page_action { |
|---|
| 168 | my $app = shift; |
|---|
| 169 | |
|---|
| 170 | # plugin_action_selector should always (?) be in the query; use it? |
|---|
| 171 | my $action_name = $app->param('action_name'); |
|---|
| 172 | my $type = $app->param('_type'); |
|---|
| 173 | my ($the_action) = |
|---|
| 174 | ( grep { $_->{key} eq $action_name } @{ $app->page_actions($type) } ); |
|---|
| 175 | return $app->errtrans( "That action ([_1]) is apparently not implemented!", |
|---|
| 176 | $action_name ) |
|---|
| 177 | unless $the_action; |
|---|
| 178 | |
|---|
| 179 | unless ( ref( $the_action->{code} ) ) { |
|---|
| 180 | if ( my $plugin = $the_action->{plugin} ) { |
|---|
| 181 | $the_action->{code} = |
|---|
| 182 | $app->handler_to_coderef( $the_action->{handler} |
|---|
| 183 | || $the_action->{code} ); |
|---|
| 184 | } |
|---|
| 185 | } |
|---|
| 186 | $the_action->{code}->($app); |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | sub cfg_system_general { |
|---|
| 190 | my $app = shift; |
|---|
| 191 | my %param; |
|---|
| 192 | if ( $app->param('blog_id') ) { |
|---|
| 193 | return $app->return_to_dashboard( redirect => 1 ); |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | return $app->errtrans("Permission denied.") |
|---|
| 197 | unless $app->user->is_superuser(); |
|---|
| 198 | my $cfg = $app->config; |
|---|
| 199 | $app->add_breadcrumb( $app->translate('General Settings') ); |
|---|
| 200 | $param{nav_config} = 1; |
|---|
| 201 | $param{nav_settings} = 1; |
|---|
| 202 | $param{languages} = |
|---|
| 203 | $app->languages_list( $app->config('DefaultUserLanguage') ); |
|---|
| 204 | my $tag_delim = $app->config('DefaultUserTagDelimiter') || 'comma'; |
|---|
| 205 | $param{"tag_delim_$tag_delim"} = 1; |
|---|
| 206 | |
|---|
| 207 | ( my $tz = $app->config('DefaultTimezone') ) =~ s![-\.]!_!g; |
|---|
| 208 | $tz =~ s!_00$!!; |
|---|
| 209 | $param{ 'server_offset_' . $tz } = 1; |
|---|
| 210 | |
|---|
| 211 | $param{default_site_root} = $app->config('DefaultSiteRoot'); |
|---|
| 212 | $param{default_site_url} = $app->config('DefaultSiteURL'); |
|---|
| 213 | $param{personal_weblog_readonly} = |
|---|
| 214 | $app->config->is_readonly('NewUserAutoProvisioning'); |
|---|
| 215 | $param{personal_weblog} = $app->config->NewUserAutoProvisioning ? 1 : 0; |
|---|
| 216 | if ( my $id = $param{new_user_template_blog_id} = |
|---|
| 217 | $app->config('NewUserTemplateBlogId') || '' ) |
|---|
| 218 | { |
|---|
| 219 | my $blog = MT::Blog->load($id); |
|---|
| 220 | if ($blog) { |
|---|
| 221 | $param{new_user_template_blog_name} = $blog->name; |
|---|
| 222 | } |
|---|
| 223 | else { |
|---|
| 224 | $app->config( 'NewUserTemplateBlogId', undef, 1 ); |
|---|
| 225 | $cfg->save_config(); |
|---|
| 226 | delete $param{new_user_template_blog_id}; |
|---|
| 227 | } |
|---|
| 228 | } |
|---|
| 229 | $param{system_email_address} = $cfg->EmailAddressMain; |
|---|
| 230 | $param{saved} = $app->param('saved'); |
|---|
| 231 | $param{error} = $app->param('error'); |
|---|
| 232 | $param{screen_class} = "settings-screen system-general-settings"; |
|---|
| 233 | $app->load_tmpl( 'cfg_system_general.tmpl', \%param ); |
|---|
| 234 | } |
|---|
| 235 | |
|---|
| 236 | sub save_cfg_system_general { |
|---|
| 237 | my $app = shift; |
|---|
| 238 | $app->validate_magic or return; |
|---|
| 239 | return $app->errtrans("Permission denied.") |
|---|
| 240 | unless $app->user->is_superuser(); |
|---|
| 241 | |
|---|
| 242 | my $cfg = $app->config; |
|---|
| 243 | $app->config( 'EmailAddressMain', |
|---|
| 244 | $app->param('system_email_address') || undef, 1 ); |
|---|
| 245 | |
|---|
| 246 | $cfg->save_config(); |
|---|
| 247 | |
|---|
| 248 | my $args = (); |
|---|
| 249 | $args->{saved} = 1; |
|---|
| 250 | $app->redirect( |
|---|
| 251 | $app->uri( |
|---|
| 252 | 'mode' => 'cfg_system', |
|---|
| 253 | args => $args |
|---|
| 254 | ) |
|---|
| 255 | ); |
|---|
| 256 | } |
|---|
| 257 | |
|---|
| 258 | sub upgrade { |
|---|
| 259 | my $app = shift; |
|---|
| 260 | |
|---|
| 261 | # check for an empty database... no author table would do it... |
|---|
| 262 | my $driver = MT::Object->driver; |
|---|
| 263 | my $upgrade_script = $app->config('UpgradeScript'); |
|---|
| 264 | my $user_class = MT->model('author'); |
|---|
| 265 | if ( !$driver || !$driver->table_exists($user_class) ) { |
|---|
| 266 | return $app->redirect( $app->path |
|---|
| 267 | . $upgrade_script |
|---|
| 268 | . $app->uri_params( mode => 'install' ) ); |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | return $app->redirect( $app->path . $upgrade_script ); |
|---|
| 272 | } |
|---|
| 273 | |
|---|
| 274 | sub recover_profile_password { |
|---|
| 275 | my $app = shift; |
|---|
| 276 | $app->validate_magic or return; |
|---|
| 277 | return $app->errtrans("Permission denied.") |
|---|
| 278 | unless $app->user->is_superuser(); |
|---|
| 279 | |
|---|
| 280 | my $q = $app->param; |
|---|
| 281 | |
|---|
| 282 | require MT::Auth; |
|---|
| 283 | require MT::Log; |
|---|
| 284 | if ( !MT::Auth->can_recover_password ) { |
|---|
| 285 | $app->log( |
|---|
| 286 | { |
|---|
| 287 | message => $app->translate( |
|---|
| 288 | "Invalid password recovery attempt; can't recover password in this configuration" |
|---|
| 289 | ), |
|---|
| 290 | level => MT::Log::SECURITY(), |
|---|
| 291 | class => 'system', |
|---|
| 292 | category => 'recover_profile_password', |
|---|
| 293 | } |
|---|
| 294 | ); |
|---|
| 295 | return $app->error("Can't recover password in this configuration"); |
|---|
| 296 | } |
|---|
| 297 | |
|---|
| 298 | my $author_id = $q->param('author_id'); |
|---|
| 299 | my $author = MT::Author->load($author_id); |
|---|
| 300 | |
|---|
| 301 | return $app->error( $app->translate("Invalid author_id") ) |
|---|
| 302 | if !$author || $author->type != MT::Author::AUTHOR() || !$author_id; |
|---|
| 303 | |
|---|
| 304 | my ( $rc, $res ) = |
|---|
| 305 | reset_password( $app, $author, $author->hint, $author->name ); |
|---|
| 306 | |
|---|
| 307 | if ($rc) { |
|---|
| 308 | my $url = $app->uri( |
|---|
| 309 | 'mode' => 'view', |
|---|
| 310 | args => { _type => 'author', recovered => 1, id => $author_id } |
|---|
| 311 | ); |
|---|
| 312 | $app->redirect($url); |
|---|
| 313 | } |
|---|
| 314 | else { |
|---|
| 315 | $app->error($res); |
|---|
| 316 | } |
|---|
| 317 | } |
|---|
| 318 | |
|---|
| 319 | sub start_backup { |
|---|
| 320 | my $app = shift; |
|---|
| 321 | my $user = $app->user; |
|---|
| 322 | my $blog_id = $app->param('blog_id'); |
|---|
| 323 | my $perms = $app->permissions; |
|---|
| 324 | |
|---|
| 325 | unless ( $user->is_superuser ) { |
|---|
| 326 | return $app->errtrans("Permission denied.") |
|---|
| 327 | unless defined($blog_id) && $perms->can_administer_blog; |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | my %param = (); |
|---|
| 331 | if ( defined($blog_id) ) { |
|---|
| 332 | $param{blog_id} = $blog_id; |
|---|
| 333 | $app->add_breadcrumb( $app->translate('Backup') ); |
|---|
| 334 | } |
|---|
| 335 | else { |
|---|
| 336 | $app->add_breadcrumb( $app->translate('Backup & Restore') ); |
|---|
| 337 | } |
|---|
| 338 | $param{system_overview_nav} = 1 unless $blog_id; |
|---|
| 339 | $param{nav_backup} = 1; |
|---|
| 340 | require MT::Util::Archive; |
|---|
| 341 | my @formats = MT::Util::Archive->available_formats(); |
|---|
| 342 | $param{archive_formats} = \@formats; |
|---|
| 343 | |
|---|
| 344 | my $limit = $app->config('CGIMaxUpload') || 2048; |
|---|
| 345 | $param{over_300} = 1 if $limit >= 300 * 1024; |
|---|
| 346 | $param{over_500} = 1 if $limit >= 500 * 1024; |
|---|
| 347 | $param{over_1024} = 1 if $limit >= 1024 * 1024; |
|---|
| 348 | $param{over_2048} = 1 if $limit >= 2048 * 1024; |
|---|
| 349 | |
|---|
| 350 | my $tmp = $app->config('TempDir'); |
|---|
| 351 | unless ( ( -d $tmp ) && ( -w $tmp ) ) { |
|---|
| 352 | $param{error} = |
|---|
| 353 | $app->translate( |
|---|
| 354 | 'Temporary directory needs to be writable for backup to work correctly. Please check TempDir configuration directive.' |
|---|
| 355 | ); |
|---|
| 356 | } |
|---|
| 357 | $app->load_tmpl( 'backup.tmpl', \%param ); |
|---|
| 358 | } |
|---|
| 359 | |
|---|
| 360 | sub start_restore { |
|---|
| 361 | my $app = shift; |
|---|
| 362 | my $user = $app->user; |
|---|
| 363 | my $blog_id = $app->param('blog_id'); |
|---|
| 364 | my $perms = $app->permissions; |
|---|
| 365 | |
|---|
| 366 | unless ( $user->is_superuser ) { |
|---|
| 367 | return $app->errtrans("Permission denied.") |
|---|
| 368 | unless defined($blog_id) && $perms->can_administer_blog; |
|---|
| 369 | } |
|---|
| 370 | |
|---|
| 371 | my %param = (); |
|---|
| 372 | if ( defined($blog_id) ) { |
|---|
| 373 | $param{blog_id} = $blog_id; |
|---|
| 374 | $app->add_breadcrumb( $app->translate('Backup') ); |
|---|
| 375 | } |
|---|
| 376 | else { |
|---|
| 377 | $app->add_breadcrumb( $app->translate('Backup & Restore') ); |
|---|
| 378 | } |
|---|
| 379 | $param{system_overview_nav} = 1 unless $blog_id; |
|---|
| 380 | $param{nav_backup} = 1; |
|---|
| 381 | |
|---|
| 382 | eval "require XML::SAX"; |
|---|
| 383 | $param{missing_sax} = 1 if $@; |
|---|
| 384 | |
|---|
| 385 | my $tmp = $app->config('TempDir'); |
|---|
| 386 | unless ( ( -d $tmp ) && ( -w $tmp ) ) { |
|---|
| 387 | $param{error} = |
|---|
| 388 | $app->translate( |
|---|
| 389 | 'Temporary directory needs to be writable for restore to work correctly. Please check TempDir configuration directive.' |
|---|
| 390 | ); |
|---|
| 391 | } |
|---|
| 392 | $app->load_tmpl( 'restore.tmpl', \%param ); |
|---|
| 393 | } |
|---|
| 394 | |
|---|
| 395 | sub backup { |
|---|
| 396 | my $app = shift; |
|---|
| 397 | my $user = $app->user; |
|---|
| 398 | my $q = $app->param; |
|---|
| 399 | my $blog_id = $q->param('blog_id'); |
|---|
| 400 | my $perms = $app->permissions; |
|---|
| 401 | unless ( $user->is_superuser ) { |
|---|
| 402 | return $app->errtrans("Permission denied.") |
|---|
| 403 | unless defined($blog_id) && $perms->can_administer_blog; |
|---|
| 404 | } |
|---|
| 405 | $app->validate_magic() or return; |
|---|
| 406 | |
|---|
| 407 | my $blog_ids = $q->param('backup_what'); |
|---|
| 408 | |
|---|
| 409 | my $size = $q->param('size_limit') || 0; |
|---|
| 410 | return $app->errtrans( '[_1] is not a number.', |
|---|
| 411 | encode_html($size) ) |
|---|
| 412 | if $size !~ /^\d+$/; |
|---|
| 413 | |
|---|
| 414 | my @blog_ids = split ',', $blog_ids; |
|---|
| 415 | |
|---|
| 416 | my $archive = $q->param('backup_archive_format'); |
|---|
| 417 | my $enc = $app->charset || 'utf-8'; |
|---|
| 418 | my @ts = gmtime(time); |
|---|
| 419 | my $ts = sprintf "%04d-%02d-%02d-%02d-%02d-%02d", $ts[5] + 1900, $ts[4] + 1, |
|---|
| 420 | @ts[ 3, 2, 1, 0 ]; |
|---|
| 421 | my $file = "Movable_Type-$ts" . '-Backup'; |
|---|
| 422 | |
|---|
| 423 | my $param = { return_args => '__mode=start_backup' }; |
|---|
| 424 | $app->{no_print_body} = 1; |
|---|
| 425 | $app->add_breadcrumb( |
|---|
| 426 | $app->translate('Backup & Restore'), |
|---|
| 427 | $app->uri( mode => 'start_backup' ) |
|---|
| 428 | ); |
|---|
| 429 | $app->add_breadcrumb( $app->translate('Backup') ); |
|---|
| 430 | $param->{system_overview_nav} = 1 if defined($blog_ids) && $blog_ids; |
|---|
| 431 | $param->{blog_id} = $blog_id if $blog_id; |
|---|
| 432 | $param->{blog_ids} = $blog_ids if $blog_ids; |
|---|
| 433 | $param->{nav_backup} = 1; |
|---|
| 434 | |
|---|
| 435 | local $| = 1; |
|---|
| 436 | $app->send_http_header('text/html'); |
|---|
| 437 | $app->print( $app->build_page( 'include/backup_start.tmpl', $param ) ); |
|---|
| 438 | require File::Temp; |
|---|
| 439 | require File::Spec; |
|---|
| 440 | use File::Copy; |
|---|
| 441 | my $temp_dir = $app->config('TempDir'); |
|---|
| 442 | |
|---|
| 443 | require MT::BackupRestore; |
|---|
| 444 | my $count_term = |
|---|
| 445 | $blog_id |
|---|
| 446 | ? { class => '*', blog_id => $blog_id } |
|---|
| 447 | : { class => '*' }; |
|---|
| 448 | my $num_assets = $app->model('asset')->count($count_term); |
|---|
| 449 | my $printer; |
|---|
| 450 | my $splitter; |
|---|
| 451 | my $finisher; |
|---|
| 452 | my $progress = sub { _progress($app, @_); }; |
|---|
| 453 | my $fh; |
|---|
| 454 | my $fname; |
|---|
| 455 | my $arc_buf; |
|---|
| 456 | |
|---|
| 457 | if ( !( $size || $num_assets ) ) { |
|---|
| 458 | $splitter = sub { }; |
|---|
| 459 | |
|---|
| 460 | if ( '0' eq $archive ) { |
|---|
| 461 | ( $fh, my $filepath ) = |
|---|
| 462 | File::Temp::tempfile( 'xml.XXXXXXXX', DIR => $temp_dir ); |
|---|
| 463 | ( my $vol, my $dir, $fname ) = File::Spec->splitpath($filepath); |
|---|
| 464 | $printer = |
|---|
| 465 | sub { my ($data) = @_; print $fh $data; return length($data); }; |
|---|
| 466 | $finisher = sub { |
|---|
| 467 | my ($asset_files) = @_; |
|---|
| 468 | close $fh; |
|---|
| 469 | _backup_finisher( $app, $fname, $param ); |
|---|
| 470 | }; |
|---|
| 471 | } |
|---|
| 472 | else { # archive/compress files |
|---|
| 473 | $printer = |
|---|
| 474 | sub { my ($data) = @_; $arc_buf .= $data; return length($data); }; |
|---|
| 475 | $finisher = sub { |
|---|
| 476 | require MT::Util::Archive; |
|---|
| 477 | my ($asset_files) = @_; |
|---|
| 478 | ( my $fh, my $filepath ) = |
|---|
| 479 | File::Temp::tempfile( $archive . '.XXXXXXXX', DIR => $temp_dir ); |
|---|
| 480 | ( my $vol, my $dir, $fname ) = File::Spec->splitpath($filepath); |
|---|
| 481 | close $fh; |
|---|
| 482 | unlink $filepath; |
|---|
| 483 | my $arc = MT::Util::Archive->new($archive, $filepath); |
|---|
| 484 | $arc->add_string( $arc_buf, "$file.xml" ); |
|---|
| 485 | $arc->add_string( |
|---|
| 486 | "<manifest xmlns='" |
|---|
| 487 | . MT::BackupRestore::NS_MOVABLETYPE() |
|---|
| 488 | . "'><file type='backup' name='$file.xml' /></manifest>", |
|---|
| 489 | "$file.manifest"); |
|---|
| 490 | $arc->close; |
|---|
| 491 | _backup_finisher( $app, $fname, $param ); |
|---|
| 492 | }; |
|---|
| 493 | } |
|---|
| 494 | } |
|---|
| 495 | else { |
|---|
| 496 | my @files; |
|---|
| 497 | my $filename = File::Spec->catfile( $temp_dir, $file . "-1.xml" ); |
|---|
| 498 | $fh = gensym(); |
|---|
| 499 | open $fh, ">$filename"; |
|---|
| 500 | my $url = |
|---|
| 501 | $app->uri |
|---|
| 502 | . "?__mode=backup_download&name=$file-1.xml&magic_token=" |
|---|
| 503 | . $app->current_magic; |
|---|
| 504 | $url .= "&blog_id=$blog_id" if defined($blog_id); |
|---|
| 505 | push @files, |
|---|
| 506 | { |
|---|
| 507 | url => $url, |
|---|
| 508 | filename => $file . "-1.xml" |
|---|
| 509 | }; |
|---|
| 510 | $printer = |
|---|
| 511 | sub { my ($data) = @_; print $fh $data; return length($data); }; |
|---|
| 512 | $splitter = sub { |
|---|
| 513 | my ($findex) = @_; |
|---|
| 514 | print $fh '</movabletype>'; |
|---|
| 515 | close $fh; |
|---|
| 516 | my $filename = |
|---|
| 517 | File::Spec->catfile( $temp_dir, $file . "-$findex.xml" ); |
|---|
| 518 | $fh = gensym(); |
|---|
| 519 | open $fh, ">$filename"; |
|---|
| 520 | my $url = |
|---|
| 521 | $app->uri |
|---|
| 522 | . "?__mode=backup_download&name=$file-$findex.xml&magic_token=" |
|---|
| 523 | . $app->current_magic; |
|---|
| 524 | $url .= "&blog_id=$blog_id" if defined($blog_id); |
|---|
| 525 | push @files, |
|---|
| 526 | { |
|---|
| 527 | url => $url, |
|---|
| 528 | filename => $file . "-$findex.xml" |
|---|
| 529 | }; |
|---|
| 530 | my $header .= |
|---|
| 531 | "<movabletype xmlns='" |
|---|
| 532 | . MT::BackupRestore::NS_MOVABLETYPE() . "'>\n"; |
|---|
| 533 | $header = "<?xml version='1.0' encoding='$enc'?>\n$header" |
|---|
| 534 | if $enc !~ m/utf-?8/i; |
|---|
| 535 | print $fh $header; |
|---|
| 536 | }; |
|---|
| 537 | $finisher = sub { |
|---|
| 538 | my ($asset_files) = @_; |
|---|
| 539 | close $fh; |
|---|
| 540 | my $filename = File::Spec->catfile( $temp_dir, "$file.manifest" ); |
|---|
| 541 | $fh = gensym(); |
|---|
| 542 | open $fh, ">$filename"; |
|---|
| 543 | print $fh "<manifest xmlns='" |
|---|
| 544 | . MT::BackupRestore::NS_MOVABLETYPE() . "'>\n"; |
|---|
| 545 | for my $file (@files) { |
|---|
| 546 | my $name = $file->{filename}; |
|---|
| 547 | print $fh "<file type='backup' name='$name' />\n"; |
|---|
| 548 | } |
|---|
| 549 | for my $id ( keys %$asset_files ) { |
|---|
| 550 | my $name = $id . '-' . $asset_files->{$id}->[2]; |
|---|
| 551 | my $tmp = File::Spec->catfile( $temp_dir, $name ); |
|---|
| 552 | unless ( copy( $asset_files->{$id}->[1], $tmp ) ) { |
|---|
| 553 | $app->log( |
|---|
| 554 | { |
|---|
| 555 | message => $app->translate( |
|---|
| 556 | 'Copying file [_1] to [_2] failed: [_3]', |
|---|
| 557 | $asset_files->{$id}->[1], |
|---|
| 558 | $tmp, $! |
|---|
| 559 | ), |
|---|
| 560 | level => MT::Log::INFO(), |
|---|
| 561 | class => 'system', |
|---|
| 562 | category => 'backup' |
|---|
| 563 | } |
|---|
| 564 | ); |
|---|
| 565 | next; |
|---|
| 566 | } |
|---|
| 567 | print $fh "<file type='asset' name='" |
|---|
| 568 | . $asset_files->{$id}->[2] |
|---|
| 569 | . "' asset_id='" |
|---|
| 570 | . $id |
|---|
| 571 | . "' />\n"; |
|---|
| 572 | my $url = |
|---|
| 573 | $app->uri |
|---|
| 574 | . "?__mode=backup_download&assetname=$name&magic_token=" |
|---|
| 575 | . $app->current_magic; |
|---|
| 576 | $url .= "&blog_id=$blog_id" if defined($blog_id); |
|---|
| 577 | push @files, |
|---|
| 578 | { |
|---|
| 579 | url => $url, |
|---|
| 580 | filename => $name, |
|---|
| 581 | }; |
|---|
| 582 | } |
|---|
| 583 | print $fh "</manifest>\n"; |
|---|
| 584 | close $fh; |
|---|
| 585 | my $url = |
|---|
| 586 | $app->uri |
|---|
| 587 | . "?__mode=backup_download&name=$file.manifest&magic_token=" |
|---|
| 588 | . $app->current_magic; |
|---|
| 589 | $url .= "&blog_id=$blog_id" if defined($blog_id); |
|---|
| 590 | push @files, |
|---|
| 591 | { |
|---|
| 592 | url => $url, |
|---|
| 593 | filename => "$file.manifest" |
|---|
| 594 | }; |
|---|
| 595 | if ( '0' eq $archive ) { |
|---|
| 596 | $param->{files_loop} = \@files; |
|---|
| 597 | $param->{tempdir} = $temp_dir; |
|---|
| 598 | my @fnames = map { $_->{filename} } @files; |
|---|
| 599 | _backup_finisher( $app, \@fnames, $param ); |
|---|
| 600 | } |
|---|
| 601 | else { |
|---|
| 602 | my ( $fh_arc, $filepath ) = |
|---|
| 603 | File::Temp::tempfile( $archive . '.XXXXXXXX', DIR => $temp_dir ); |
|---|
| 604 | ( my $vol, my $dir, $fname ) = File::Spec->splitpath($filepath); |
|---|
| 605 | require MT::Util::Archive; |
|---|
| 606 | close $fh_arc; |
|---|
| 607 | unlink $filepath; |
|---|
| 608 | my $arc = MT::Util::Archive->new($archive, $filepath); |
|---|
| 609 | for my $f (@files) { |
|---|
| 610 | $arc->add_file( $temp_dir, $f->{filename} ); |
|---|
| 611 | } |
|---|
| 612 | $arc->close; |
|---|
| 613 | # for safery, don't unlink before closing $arc here. |
|---|
| 614 | for my $f (@files) { |
|---|
| 615 | unlink File::Spec->catfile( $temp_dir, $f->{filename} ); |
|---|
| 616 | } |
|---|
| 617 | _backup_finisher( $app, $fname, $param ); |
|---|
| 618 | } |
|---|
| 619 | }; |
|---|
| 620 | } |
|---|
| 621 | |
|---|
| 622 | my @tsnow = gmtime(time); |
|---|
| 623 | my $metadata = { |
|---|
| 624 | backup_by => $app->user->name . '(ID: ' . $app->user->id . ')', |
|---|
| 625 | backup_on => sprintf( |
|---|
| 626 | "%04d-%02d-%02dT%02d:%02d:%02d", |
|---|
| 627 | $tsnow[5] + 1900, |
|---|
| 628 | $tsnow[4] + 1, |
|---|
| 629 | @tsnow[ 3, 2, 1, 0 ] |
|---|
| 630 | ), |
|---|
| 631 | backup_what => join( ',', @blog_ids ), |
|---|
| 632 | schema_version => $app->config('SchemaVersion'), |
|---|
| 633 | }; |
|---|
| 634 | MT::BackupRestore->backup( \@blog_ids, $printer, $splitter, $finisher, |
|---|
| 635 | $progress, $size * 1024, |
|---|
| 636 | $enc, $metadata ); |
|---|
| 637 | } |
|---|
| 638 | |
|---|
| 639 | sub backup_download { |
|---|
| 640 | my $app = shift; |
|---|
| 641 | my $user = $app->user; |
|---|
| 642 | my $blog_id = $app->param('blog_id'); |
|---|
| 643 | unless ( $user->is_superuser ) { |
|---|
| 644 | my $perms = $app->permissions; |
|---|
| 645 | return $app->errtrans("Permission denied.") |
|---|
| 646 | unless defined($blog_id) && $perms->can_administer_blog; |
|---|
| 647 | } |
|---|
| 648 | $app->validate_magic() or return; |
|---|
| 649 | my $filename = $app->param('filename'); |
|---|
| 650 | my $assetname = $app->param('assetname'); |
|---|
| 651 | my $temp_dir = $app->config('TempDir'); |
|---|
| 652 | my $newfilename; |
|---|
| 653 | if ( defined($assetname) ) { |
|---|
| 654 | my $sess = MT::Session->load( { kind => 'BU', name => $assetname } ); |
|---|
| 655 | if ( !defined($sess) || !$sess ) { |
|---|
| 656 | return $app->errtrans("Specified file was not found."); |
|---|
| 657 | } |
|---|
| 658 | $newfilename = $filename = $assetname; |
|---|
| 659 | $sess->remove; |
|---|
| 660 | } |
|---|
| 661 | elsif ( defined($filename) ) { |
|---|
| 662 | my $sess = MT::Session->load( { kind => 'BU', name => $filename } ); |
|---|
| 663 | if ( !defined($sess) || !$sess ) { |
|---|
| 664 | return $app->errtrans("Specified file was not found."); |
|---|
| 665 | } |
|---|
| 666 | my @ts = gmtime( $sess->start ); |
|---|
| 667 | my $ts = sprintf "%04d-%02d-%02d-%02d-%02d-%02d", $ts[5] + 1900, |
|---|
| 668 | $ts[4] + 1, @ts[ 3, 2, 1, 0 ]; |
|---|
| 669 | $newfilename = "Movable_Type-$ts" . '-Backup'; |
|---|
| 670 | $sess->remove; |
|---|
| 671 | } |
|---|
| 672 | else { |
|---|
| 673 | $newfilename = $app->param('name'); |
|---|
| 674 | return |
|---|
| 675 | if $newfilename !~ |
|---|
| 676 | /Movable_Type-\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-Backup(?:-\d+)?\.\w+/; |
|---|
| 677 | $filename = $newfilename; |
|---|
| 678 | } |
|---|
| 679 | |
|---|
| 680 | require File::Spec; |
|---|
| 681 | my $fname = File::Spec->catfile( $temp_dir, $filename ); |
|---|
| 682 | |
|---|
| 683 | my $contenttype; |
|---|
| 684 | if ( !defined($assetname) && ( $filename =~ m/^xml\..+$/i ) ) { |
|---|
| 685 | my $enc = $app->charset || 'utf-8'; |
|---|
| 686 | $contenttype = "text/xml; charset=$enc"; |
|---|
| 687 | $newfilename .= '.xml'; |
|---|
| 688 | } |
|---|
| 689 | elsif ( $filename =~ m/^tgz\..+$/i ) { |
|---|
| 690 | $contenttype = 'application/x-tar-gz'; |
|---|
| 691 | $newfilename .= '.tar.gz'; |
|---|
| 692 | } |
|---|
| 693 | elsif ( $filename =~ m/^zip\..+$/i ) { |
|---|
| 694 | $contenttype = 'application/zip'; |
|---|
| 695 | $newfilename .= '.zip'; |
|---|
| 696 | } |
|---|
| 697 | else { |
|---|
| 698 | $contenttype = 'application/octet-stream'; |
|---|
| 699 | } |
|---|
| 700 | |
|---|
| 701 | if ( open( my $fh, "<", $fname ) ) { |
|---|
| 702 | binmode $fh; |
|---|
| 703 | $app->{no_print_body} = 1; |
|---|
| 704 | $app->set_header( |
|---|
| 705 | "Content-Disposition" => "attachment; filename=$newfilename" ); |
|---|
| 706 | $app->send_http_header($contenttype); |
|---|
| 707 | my $data; |
|---|
| 708 | while ( read $fh, my ($chunk), 8192 ) { |
|---|
| 709 | $data .= $chunk; |
|---|
| 710 | } |
|---|
| 711 | close $fh; |
|---|
| 712 | $app->print($data); |
|---|
| 713 | $app->log( |
|---|
| 714 | { |
|---|
| 715 | message => $app->translate( |
|---|
| 716 | '[_1] successfully downloaded backup file ([_2])', |
|---|
| 717 | $app->user->name, $fname |
|---|
| 718 | ), |
|---|
| 719 | level => MT::Log::INFO(), |
|---|
| 720 | class => 'system', |
|---|
| 721 | category => 'restore' |
|---|
| 722 | } |
|---|
| 723 | ); |
|---|
| 724 | unlink $fname; |
|---|
| 725 | } |
|---|
| 726 | else { |
|---|
| 727 | $app->errtrans('Specified file was not found.'); |
|---|
| 728 | } |
|---|
| 729 | } |
|---|
| 730 | |
|---|
| 731 | sub restore { |
|---|
| 732 | my $app = shift; |
|---|
| 733 | my $user = $app->user; |
|---|
| 734 | return $app->errtrans("Permission denied.") if !$user->is_superuser; |
|---|
| 735 | $app->validate_magic() or return; |
|---|
| 736 | |
|---|
| 737 | my $q = $app->param; |
|---|
| 738 | |
|---|
| 739 | my ($fh) = $app->upload_info('file'); |
|---|
| 740 | my $uploaded = $q->param('file'); |
|---|
| 741 | my ( $volume, $directories, $uploaded_filename ) = |
|---|
| 742 | File::Spec->splitpath($uploaded) |
|---|
| 743 | if defined($uploaded); |
|---|
| 744 | if ( defined($uploaded_filename) |
|---|
| 745 | && ( $uploaded_filename =~ /^.+\.manifest$/i ) ) |
|---|
| 746 | { |
|---|
| 747 | return restore_upload_manifest( $app, $fh ); |
|---|
| 748 | } |
|---|
| 749 | |
|---|
| 750 | my $param = { return_args => '__mode=dashboard' }; |
|---|
| 751 | |
|---|
| 752 | $app->add_breadcrumb( |
|---|
| 753 | $app->translate('Backup & Restore'), |
|---|
| 754 | $app->uri( mode => 'start_restore' ) |
|---|
| 755 | ); |
|---|
| 756 | $app->add_breadcrumb( $app->translate('Restore') ); |
|---|
| 757 | $param->{system_overview_nav} = 1; |
|---|
| 758 | $param->{nav_backup} = 1; |
|---|
| 759 | |
|---|
| 760 | $app->{no_print_body} = 1; |
|---|
| 761 | |
|---|
| 762 | local $| = 1; |
|---|
| 763 | $app->send_http_header('text/html'); |
|---|
| 764 | |
|---|
| 765 | $app->print( $app->build_page( 'restore_start.tmpl', $param ) ); |
|---|
| 766 | |
|---|
| 767 | require File::Path; |
|---|
| 768 | |
|---|
| 769 | my $error = ''; |
|---|
| 770 | my $result; |
|---|
| 771 | if (!$fh) { |
|---|
| 772 | $param->{restore_upload} = 0; |
|---|
| 773 | my $dir = $app->config('ImportPath'); |
|---|
| 774 | my ( $blog_ids, $asset_ids ) = restore_directory( $app, $dir, \$error ); |
|---|
| 775 | if ( defined $blog_ids ) { |
|---|
| 776 | $param->{open_dialog} = 1; |
|---|
| 777 | $param->{blog_ids} = join( ',', @$blog_ids ); |
|---|
| 778 | $param->{asset_ids} = join( ',', @$asset_ids ) |
|---|
| 779 | if defined $asset_ids; |
|---|
| 780 | $param->{tmp_dir} = $dir; |
|---|
| 781 | } |
|---|
| 782 | elsif ( defined $asset_ids ) { |
|---|
| 783 | my %asset_ids = @$asset_ids; |
|---|
| 784 | my %error_assets; |
|---|
| 785 | _restore_non_blog_asset( $app, $dir, $asset_ids, \%error_assets ); |
|---|
| 786 | if (%error_assets) { |
|---|
| 787 | my $data; |
|---|
| 788 | while ( my ( $key, $value ) = each %error_assets ) { |
|---|
| 789 | $data .= |
|---|
| 790 | $app->translate( 'MT::Asset#[_1]: ', $key ) . $value . "\n"; |
|---|
| 791 | } |
|---|
| 792 | my $message = $app->translate( |
|---|
| 793 | 'Some of the actual files for assets could not be restored.'); |
|---|
| 794 | $app->log( |
|---|
| 795 | { |
|---|
| 796 | message => $message, |
|---|
| 797 | level => MT::Log::WARNING(), |
|---|
| 798 | class => 'system', |
|---|
| 799 | category => 'restore', |
|---|
| 800 | metadata => $data, |
|---|
| 801 | } |
|---|
| 802 | ); |
|---|
| 803 | $error .= $message; |
|---|
| 804 | } |
|---|
| 805 | } |
|---|
| 806 | } |
|---|
| 807 | else { |
|---|
| 808 | $param->{restore_upload} = 1; |
|---|
| 809 | if ( $uploaded_filename =~ /^.+\.xml$/i ) { |
|---|
| 810 | my $blog_ids = restore_file( $app, $fh, \$error ); |
|---|
| 811 | if ( defined $blog_ids ) { |
|---|
| 812 | $param->{open_dialog} = 1; |
|---|
| 813 | $param->{blog_ids} = join( ',', @$blog_ids ); |
|---|
| 814 | } |
|---|
| 815 | } |
|---|
| 816 | else { |
|---|
| 817 | require MT::Util::Archive; |
|---|
| 818 | my $arc; |
|---|
| 819 | my $error; |
|---|
| 820 | if ( $uploaded_filename =~ /^.+\.tar(\.gz)?$/i ) { |
|---|
| 821 | $arc = MT::Util::Archive->new('tgz', $fh); |
|---|
| 822 | } |
|---|
| 823 | elsif ( $uploaded_filename =~ /^.+\.zip$/i ) { |
|---|
| 824 | $arc = MT::Util::Archive->new('zip', $fh); |
|---|
| 825 | } |
|---|
| 826 | else { |
|---|
| 827 | $error = |
|---|
| 828 | $app->translate( |
|---|
| 829 | 'Please use xml, tar.gz, zip, or manifest as a file extension.' |
|---|
| 830 | ); |
|---|
| 831 | } |
|---|
| 832 | unless ($arc) { |
|---|
| 833 | $result = 0; |
|---|
| 834 | $param->{restore_success} = 0; |
|---|
| 835 | if ($error) { |
|---|
| 836 | $param->{error} = $error; |
|---|
| 837 | } |
|---|
| 838 | else { |
|---|
| 839 | $error = MT->translate('Unknown file format'); |
|---|
| 840 | $app->log( |
|---|
| 841 | { |
|---|
| 842 | message => $error . ":$uploaded_filename", |
|---|
| 843 | level => MT::Log::WARNING(), |
|---|
| 844 | class => 'system', |
|---|
| 845 | category => 'restore', |
|---|
| 846 | metadata => MT::Util::Archive->errstr, |
|---|
| 847 | } |
|---|
| 848 | ); |
|---|
| 849 | } |
|---|
| 850 | $app->print( $error ); |
|---|
| 851 | $app->print( |
|---|
| 852 | $app->build_page( "restore_end.tmpl", $param ) ); |
|---|
| 853 | close $fh if $fh; |
|---|
| 854 | return 1; |
|---|
| 855 | } |
|---|
| 856 | my $temp_dir = $app->config('TempDir'); |
|---|
| 857 | require File::Temp; |
|---|
| 858 | my $tmp = File::Temp::tempdir( $uploaded_filename . 'XXXX', |
|---|
| 859 | DIR => $temp_dir ); |
|---|
| 860 | $arc->extract($tmp); |
|---|
| 861 | $arc->close; |
|---|
| 862 | my ( $blog_ids, $asset_ids ) = |
|---|
| 863 | restore_directory( $app, $tmp, \$error ); |
|---|
| 864 | if (defined $blog_ids) { |
|---|
| 865 | $param->{open_dialog} = 1; |
|---|
| 866 | $param->{blog_ids} = join( ',', @$blog_ids ) |
|---|
| 867 | if defined $blog_ids; |
|---|
| 868 | $param->{asset_ids} = join( ',', @$asset_ids ) |
|---|
| 869 | if defined $asset_ids; |
|---|
| 870 | $param->{tmp_dir} = $tmp; |
|---|
| 871 | } |
|---|
| 872 | elsif ( defined $asset_ids ) { |
|---|
| 873 | my %asset_ids = @$asset_ids; |
|---|
| 874 | my %error_assets; |
|---|
| 875 | _restore_non_blog_asset( $app, $tmp, \%asset_ids, \%error_assets ); |
|---|
| 876 | if (%error_assets) { |
|---|
| 877 | my $data; |
|---|
| 878 | while ( my ( $key, $value ) = each %error_assets ) { |
|---|
| 879 | $data .= |
|---|
| 880 | $app->translate( 'MT::Asset#[_1]: ', $key ) . $value . "\n"; |
|---|
| 881 | } |
|---|
| 882 | my $message = $app->translate( |
|---|
| 883 | 'Some of the actual files for assets could not be restored.'); |
|---|
| 884 | $app->log( |
|---|
| 885 | { |
|---|
| 886 | message => $message, |
|---|
| 887 | level => MT::Log::WARNING(), |
|---|
| 888 | class => 'system', |
|---|
| 889 | category => 'restore', |
|---|
| 890 | metadata => $data, |
|---|
| 891 | } |
|---|
| 892 | ); |
|---|
| 893 | $error .= $message; |
|---|
| 894 | } |
|---|
| 895 | } |
|---|
| 896 | } |
|---|
| 897 | } |
|---|
| 898 | $param->{restore_success} = !$error; |
|---|
| 899 | $param->{error} = $error if $error; |
|---|
| 900 | if ( ( exists $param->{open_dialog} ) && ( $param->{open_dialog} ) ) { |
|---|
| 901 | $param->{dialog_mode} = 'dialog_adjust_sitepath'; |
|---|
| 902 | $param->{dialog_params} = |
|---|
| 903 | 'magic_token=' |
|---|
| 904 | . $app->current_magic |
|---|
| 905 | . '&blog_ids=' |
|---|
| 906 | . $param->{blog_ids} |
|---|
| 907 | . '&asset_ids=' |
|---|
| 908 | . $param->{asset_ids} |
|---|
| 909 | . '&tmp_dir=' |
|---|
| 910 | . encode_url( $param->{tmp_dir} ); |
|---|
| 911 | if ( ( $param->{restore_upload} ) && ( $param->{restore_upload} ) ) { |
|---|
| 912 | $param->{dialog_params} .= '&restore_upload=1'; |
|---|
| 913 | } |
|---|
| 914 | if ( ( $param->{error} ) && ( $param->{error} ) ) { |
|---|
| 915 | $param->{dialog_params} .= |
|---|
| 916 | '&error=' . encode_url( $param->{error} ); |
|---|
| 917 | } |
|---|
| 918 | } |
|---|
| 919 | |
|---|
| 920 | $app->print( $app->build_page( "restore_end.tmpl", $param ) ); |
|---|
| 921 | close $fh if $fh; |
|---|
| 922 | 1; |
|---|
| 923 | } |
|---|
| 924 | |
|---|
| 925 | sub restore_premature_cancel { |
|---|
| 926 | my $app = shift; |
|---|
| 927 | my $user = $app->user; |
|---|
| 928 | return $app->errtrans("Permission denied.") if !$user->is_superuser; |
|---|
| 929 | $app->validate_magic() or return; |
|---|
| 930 | |
|---|
| 931 | require JSON; |
|---|
| 932 | my $deferred = JSON::jsonToObj( $app->param('deferred_json') ) |
|---|
| 933 | if $app->param('deferred_json'); |
|---|
| 934 | my $param = { restore_success => 1 }; |
|---|
| 935 | if ( defined $deferred && ( scalar( keys %$deferred ) ) ) { |
|---|
| 936 | _log_dirty_restore( $app, $deferred ); |
|---|
| 937 | my $log_url = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 938 | $param->{restore_success} = 0; |
|---|
| 939 | my $message = |
|---|
| 940 | $app->translate( |
|---|
| 941 | 'Some objects were not restored because their parent objects were not restored.' |
|---|
| 942 | ); |
|---|
| 943 | $param->{error} = $message . ' ' |
|---|
| 944 | . $app->translate( |
|---|
| 945 | "Detailed information is in the <a href='javascript:void(0)' onclick='closeDialog(\"[_1]\")'>activity log</a>.", |
|---|
| 946 | $log_url |
|---|
| 947 | ); |
|---|
| 948 | } |
|---|
| 949 | else { |
|---|
| 950 | $app->log( |
|---|
| 951 | { |
|---|
| 952 | message => $app->translate( |
|---|
| 953 | '[_1] has canceled the multiple files restore operation prematurely.', |
|---|
| 954 | $app->user->name |
|---|
| 955 | ), |
|---|
| 956 | level => MT::Log::WARNING(), |
|---|
| 957 | class => 'system', |
|---|
| 958 | category => 'restore', |
|---|
| 959 | } |
|---|
| 960 | ); |
|---|
| 961 | } |
|---|
| 962 | $app->redirect( $app->uri( mode => 'view_log', args => {} ) ); |
|---|
| 963 | } |
|---|
| 964 | |
|---|
| 965 | sub _restore_non_blog_asset { |
|---|
| 966 | my ( $app, $tmp_dir, $asset_ids, $error_assets ) = @_; |
|---|
| 967 | require MT::FileMgr; |
|---|
| 968 | my $fmgr = MT::FileMgr->new('Local'); |
|---|
| 969 | foreach my $new_id ( keys %$asset_ids ) { |
|---|
| 970 | my $asset = $app->model('asset')->load($new_id); |
|---|
| 971 | next unless $asset; |
|---|
| 972 | my $old_id = $asset_ids->{$new_id}; |
|---|
| 973 | my $filename = $old_id . '-' . $asset->file_name; |
|---|
| 974 | my $file = File::Spec->catfile( $tmp_dir, $filename ); |
|---|
| 975 | MT::BackupRestore->restore_asset( $file, $asset, $old_id, $fmgr, |
|---|
| 976 | $error_assets, sub { $app->print(@_); } ); |
|---|
| 977 | } |
|---|
| 978 | } |
|---|
| 979 | |
|---|
| 980 | sub adjust_sitepath { |
|---|
| 981 | my $app = shift; |
|---|
| 982 | my $user = $app->user; |
|---|
| 983 | return $app->errtrans("Permission denied.") if !$user->is_superuser; |
|---|
| 984 | $app->validate_magic() or return; |
|---|
| 985 | |
|---|
| 986 | require MT::BackupRestore; |
|---|
| 987 | |
|---|
| 988 | my $q = $app->param; |
|---|
| 989 | my $tmp_dir = $q->param('tmp_dir'); |
|---|
| 990 | my $error = $q->param('error') || q(); |
|---|
| 991 | my %asset_ids = split ',', $q->param('asset_ids'); |
|---|
| 992 | |
|---|
| 993 | $app->{no_print_body} = 1; |
|---|
| 994 | |
|---|
| 995 | local $| = 1; |
|---|
| 996 | $app->send_http_header('text/html'); |
|---|
| 997 | |
|---|
| 998 | $app->print( $app->build_page( 'dialog/restore_start.tmpl', {} ) ); |
|---|
| 999 | |
|---|
| 1000 | my $asset_class = $app->model('asset'); |
|---|
| 1001 | my %error_assets; |
|---|
| 1002 | my %blogs_meta; |
|---|
| 1003 | my @p = $q->param; |
|---|
| 1004 | foreach my $p (@p) { |
|---|
| 1005 | next unless $p =~ /^site_path_(\d+)/; |
|---|
| 1006 | my $id = $1; |
|---|
| 1007 | my $blog = $app->model('blog')->load($id) |
|---|
| 1008 | or return $app->error($app->translate('Can\'t load blog #[_1].', $id)); |
|---|
| 1009 | my $old_site_path = scalar $q->param("old_site_path_$id"); |
|---|
| 1010 | my $old_site_url = scalar $q->param("old_site_url_$id"); |
|---|
| 1011 | my $site_path = scalar $q->param("site_path_$id") || q(); |
|---|
| 1012 | my $site_url = scalar $q->param("site_url_$id") || q(); |
|---|
| 1013 | $blog->site_path($site_path); |
|---|
| 1014 | $blog->site_url($site_url); |
|---|
| 1015 | |
|---|
| 1016 | if ( $site_url || $site_path ) { |
|---|
| 1017 | $app->print( |
|---|
| 1018 | $app->translate( |
|---|
| 1019 | "Changing Site Path for the blog '[_1]' (ID:[_2])...", |
|---|
| 1020 | $blog->name, $blog->id |
|---|
| 1021 | ) |
|---|
| 1022 | ); |
|---|
| 1023 | } |
|---|
| 1024 | else { |
|---|
| 1025 | $app->print( |
|---|
| 1026 | $app->translate( |
|---|
| 1027 | "Removing Site Path for the blog '[_1]' (ID:[_2])...", |
|---|
| 1028 | $blog->name, $blog->id |
|---|
| 1029 | ) |
|---|
| 1030 | ); |
|---|
| 1031 | } |
|---|
| 1032 | my $old_archive_path = scalar $q->param("old_archive_path_$id"); |
|---|
| 1033 | my $old_archive_url = scalar $q->param("old_archive_url_$id"); |
|---|
| 1034 | my $archive_path = scalar $q->param("archive_path_$id") || q(); |
|---|
| 1035 | my $archive_url = scalar $q->param("archive_url_$id") || q(); |
|---|
| 1036 | $blog->archive_path($archive_path); |
|---|
| 1037 | $blog->archive_url($archive_url); |
|---|
| 1038 | if ( ( $old_archive_url && $archive_url ) |
|---|
| 1039 | || ( $old_archive_path && $archive_path ) ) |
|---|
| 1040 | { |
|---|
| 1041 | $app->print( |
|---|
| 1042 | "\n" |
|---|
| 1043 | . $app->translate( |
|---|
| 1044 | "Changing Archive Path for the blog '[_1]' (ID:[_2])...", |
|---|
| 1045 | $blog->name, $blog->id |
|---|
| 1046 | ) |
|---|
| 1047 | ); |
|---|
| 1048 | } |
|---|
| 1049 | elsif ( $old_archive_url || $old_archive_path ) { |
|---|
| 1050 | $app->print( |
|---|
| 1051 | "\n" |
|---|
| 1052 | . $app->translate( |
|---|
| 1053 | "Removing Archive Path for the blog '[_1]' (ID:[_2])...", |
|---|
| 1054 | $blog->name, $blog->id |
|---|
| 1055 | ) |
|---|
| 1056 | ); |
|---|
| 1057 | } |
|---|
| 1058 | $blog->save or $app->print( $app->translate("failed") . "\n" ), next; |
|---|
| 1059 | $app->print( $app->translate("ok") . "\n" ); |
|---|
| 1060 | |
|---|
| 1061 | $blogs_meta{$id} = { |
|---|
| 1062 | 'old_archive_path' => $old_archive_path, |
|---|
| 1063 | 'old_archive_url' => $old_archive_url, |
|---|
| 1064 | 'archive_path' => $archive_path, |
|---|
| 1065 | 'archive_url' => $archive_url, |
|---|
| 1066 | 'old_site_path' => $old_site_path, |
|---|
| 1067 | 'old_site_url' => $old_site_url, |
|---|
| 1068 | 'site_path' => $site_path, |
|---|
| 1069 | 'site_url' => $site_url, |
|---|
| 1070 | }; |
|---|
| 1071 | next unless %asset_ids; |
|---|
| 1072 | |
|---|
| 1073 | my $fmgr = ( $site_path || $archive_path ) ? $blog->file_mgr : undef; |
|---|
| 1074 | next unless defined $fmgr; |
|---|
| 1075 | |
|---|
| 1076 | my @assets = |
|---|
| 1077 | $asset_class->load( { blog_id => $id, class => '*' } ); |
|---|
| 1078 | foreach my $asset (@assets) { |
|---|
| 1079 | my $path = $asset->column('file_path'); |
|---|
| 1080 | my $url = $asset->column('url'); |
|---|
| 1081 | if ($archive_path) { |
|---|
| 1082 | $path =~ s/^\Q$old_archive_path\E/$archive_path/i; |
|---|
| 1083 | $asset->file_path($path); |
|---|
| 1084 | } |
|---|
| 1085 | if ($archive_url) { |
|---|
| 1086 | $url =~ s/^\Q$old_archive_url\E/$archive_url/i; |
|---|
| 1087 | $asset->url($url); |
|---|
| 1088 | } |
|---|
| 1089 | if ($site_path) { |
|---|
| 1090 | $path =~ s/^\Q$old_site_path\E/$site_path/i; |
|---|
| 1091 | $asset->file_path($path); |
|---|
| 1092 | } |
|---|
| 1093 | if ($site_url) { |
|---|
| 1094 | $url =~ s/^\Q$old_site_url\E/$site_url/i; |
|---|
| 1095 | $asset->url($url); |
|---|
| 1096 | } |
|---|
| 1097 | $app->print( |
|---|
| 1098 | $app->translate( |
|---|
| 1099 | "Changing file path for the asset '[_1]' (ID:[_2])...", |
|---|
| 1100 | $asset->label, $asset->id |
|---|
| 1101 | ) |
|---|
| 1102 | ); |
|---|
| 1103 | $asset->save |
|---|
| 1104 | or $app->print( $app->translate("failed") . "\n" ), next; |
|---|
| 1105 | $app->print( $app->translate("ok") . "\n" ); |
|---|
| 1106 | unless ( $q->param('redirect') ) { |
|---|
| 1107 | my $old_id = delete $asset_ids{ $asset->id }; |
|---|
| 1108 | my $filename = "$old_id-" . $asset->file_name; |
|---|
| 1109 | my $file = File::Spec->catfile( $tmp_dir, $filename ); |
|---|
| 1110 | MT::BackupRestore->restore_asset( $file, $asset, $old_id, $fmgr, |
|---|
| 1111 | \%error_assets, sub { $app->print(@_); } ); |
|---|
| 1112 | } |
|---|
| 1113 | } |
|---|
| 1114 | } |
|---|
| 1115 | unless ( $q->param('redirect') ) { |
|---|
| 1116 | _restore_non_blog_asset( $app, $tmp_dir, \%asset_ids, \%error_assets ); |
|---|
| 1117 | } |
|---|
| 1118 | if (%error_assets) { |
|---|
| 1119 | my $data; |
|---|
| 1120 | while ( my ( $key, $value ) = each %error_assets ) { |
|---|
| 1121 | $data .= |
|---|
| 1122 | $app->translate( 'MT::Asset#[_1]: ', $key ) . $value . "\n"; |
|---|
| 1123 | } |
|---|
| 1124 | my $message = $app->translate( |
|---|
| 1125 | 'Some of the actual files for assets could not be restored.'); |
|---|
| 1126 | $app->log( |
|---|
| 1127 | { |
|---|
| 1128 | message => $message, |
|---|
| 1129 | level => MT::Log::WARNING(), |
|---|
| 1130 | class => 'system', |
|---|
| 1131 | category => 'restore', |
|---|
| 1132 | metadata => $data, |
|---|
| 1133 | } |
|---|
| 1134 | ); |
|---|
| 1135 | $error .= $message; |
|---|
| 1136 | } |
|---|
| 1137 | |
|---|
| 1138 | if ($tmp_dir) { |
|---|
| 1139 | require File::Path; |
|---|
| 1140 | File::Path::rmtree($tmp_dir); |
|---|
| 1141 | } |
|---|
| 1142 | |
|---|
| 1143 | my $param = {}; |
|---|
| 1144 | if ( scalar $q->param('redirect') ) { |
|---|
| 1145 | $param->{restore_end} = 0; # redirect=1 means we are from multi-uploads |
|---|
| 1146 | require JSON; |
|---|
| 1147 | $param->{blogs_meta} = JSON::objToJson( \%blogs_meta ); |
|---|
| 1148 | $param->{next_mode} = 'dialog_restore_upload'; |
|---|
| 1149 | } |
|---|
| 1150 | else { |
|---|
| 1151 | $param->{restore_end} = 1; |
|---|
| 1152 | } |
|---|
| 1153 | if ($error) { |
|---|
| 1154 | $param->{error} = $error; |
|---|
| 1155 | $param->{error_url} = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 1156 | } |
|---|
| 1157 | for my $key ( |
|---|
| 1158 | qw(files last redirect is_dirty is_asset objects_json deferred_json)) |
|---|
| 1159 | { |
|---|
| 1160 | $param->{$key} = scalar $q->param($key); |
|---|
| 1161 | } |
|---|
| 1162 | $param->{name} = $q->param('current_file'); |
|---|
| 1163 | $param->{assets} = encode_html( $q->param('assets') ); |
|---|
| 1164 | $app->print( $app->build_page( 'dialog/restore_end.tmpl', $param ) ); |
|---|
| 1165 | } |
|---|
| 1166 | |
|---|
| 1167 | sub dialog_restore_upload { |
|---|
| 1168 | my $app = shift; |
|---|
| 1169 | my $user = $app->user; |
|---|
| 1170 | return $app->errtrans("Permission denied.") if !$user->is_superuser; |
|---|
| 1171 | $app->validate_magic() or return; |
|---|
| 1172 | |
|---|
| 1173 | my $q = $app->param; |
|---|
| 1174 | |
|---|
| 1175 | my $current = $q->param('current_file'); |
|---|
| 1176 | my $last = $q->param('last'); |
|---|
| 1177 | my $files = $q->param('files'); |
|---|
| 1178 | my $assets_json = $q->param('assets'); |
|---|
| 1179 | my $is_asset = $q->param('is_asset') || 0; |
|---|
| 1180 | my $schema_version = $q->param('schema_version') |
|---|
| 1181 | || $app->config('SchemaVersion'); |
|---|
| 1182 | my $overwrite_template = $q->param('overwrite_templates') ? 1 : 0; |
|---|
| 1183 | |
|---|
| 1184 | my $objects = {}; |
|---|
| 1185 | my $deferred = {}; |
|---|
| 1186 | require JSON; |
|---|
| 1187 | my $objects_json = $q->param('objects_json') if $q->param('objects_json'); |
|---|
| 1188 | $deferred = JSON::jsonToObj( $q->param('deferred_json') ) |
|---|
| 1189 | if $q->param('deferred_json'); |
|---|
| 1190 | |
|---|
| 1191 | my ($fh) = $app->upload_info('file'); |
|---|
| 1192 | |
|---|
| 1193 | my $param = {}; |
|---|
| 1194 | $param->{start} = $q->param('start') || 0; |
|---|
| 1195 | $param->{is_asset} = $is_asset; |
|---|
| 1196 | $param->{name} = $current; |
|---|
| 1197 | $param->{files} = $files; |
|---|
| 1198 | $param->{assets} = $assets_json; |
|---|
| 1199 | $param->{last} = $last; |
|---|
| 1200 | $param->{redirect} = 1; |
|---|
| 1201 | $param->{is_dirty} = $q->param('is_dirty'); |
|---|
| 1202 | $param->{objects_json} = $objects_json if defined($objects_json); |
|---|
| 1203 | $param->{deferred_json} = JSON::objToJson($deferred) if defined($deferred); |
|---|
| 1204 | $param->{blogs_meta} = $q->param('blogs_meta'); |
|---|
| 1205 | $param->{schema_version} = $schema_version; |
|---|
| 1206 | $param->{overwrite_templates} = $overwrite_template; |
|---|
| 1207 | |
|---|
| 1208 | my $uploaded = $q->param('file'); |
|---|
| 1209 | if ( defined($uploaded) ) { |
|---|
| 1210 | $uploaded =~ s!\\!/!g; ## Change backslashes to forward slashes |
|---|
| 1211 | my ( $volume, $directories, $uploaded_filename ) = |
|---|
| 1212 | File::Spec->splitpath($uploaded); |
|---|
| 1213 | if ( $current ne $uploaded_filename ) { |
|---|
| 1214 | close $fh if $uploaded_filename; |
|---|
| 1215 | $param->{error} = |
|---|
| 1216 | $app->translate( 'Please upload [_1] in this page.', $current ); |
|---|
| 1217 | return $app->load_tmpl( 'dialog/restore_upload.tmpl', $param ); |
|---|
| 1218 | } |
|---|
| 1219 | } |
|---|
| 1220 | |
|---|
| 1221 | if (!$fh) { |
|---|
| 1222 | $param->{error} = $app->translate('File was not uploaded.') |
|---|
| 1223 | if !( $q->param('redirect') ); |
|---|
| 1224 | return $app->load_tmpl( 'dialog/restore_upload.tmpl', $param ); |
|---|
| 1225 | } |
|---|
| 1226 | |
|---|
| 1227 | $app->{no_print_body} = 1; |
|---|
| 1228 | |
|---|
| 1229 | local $| = 1; |
|---|
| 1230 | $app->send_http_header('text/html'); |
|---|
| 1231 | |
|---|
| 1232 | $app->print( $app->build_page( 'dialog/restore_start.tmpl', $param ) ); |
|---|
| 1233 | |
|---|
| 1234 | if ( defined $objects_json ) { |
|---|
| 1235 | my $objects_tmp = JSON::jsonToObj($objects_json); |
|---|
| 1236 | my %class2ids; |
|---|
| 1237 | |
|---|
| 1238 | # { MT::CLASS#OLD_ID => NEW_ID } |
|---|
| 1239 | for my $key ( keys %$objects_tmp ) { |
|---|
| 1240 | my ( $class, $old_id ) = split '#', $key; |
|---|
| 1241 | if ( exists $class2ids{$class} ) { |
|---|
| 1242 | my $newids = $class2ids{$class}->{newids}; |
|---|
| 1243 | push @$newids, $objects_tmp->{$key}; |
|---|
| 1244 | my $keymaps = $class2ids{$class}->{keymaps}; |
|---|
| 1245 | push @$keymaps, |
|---|
| 1246 | { newid => $objects_tmp->{$key}, oldid => $old_id }; |
|---|
| 1247 | } |
|---|
| 1248 | else { |
|---|
| 1249 | $class2ids{$class} = { |
|---|
| 1250 | newids => [ $objects_tmp->{$key} ], |
|---|
| 1251 | keymaps => |
|---|
| 1252 | [ { newid => $objects_tmp->{$key}, oldid => $old_id } ] |
|---|
| 1253 | }; |
|---|
| 1254 | } |
|---|
| 1255 | } |
|---|
| 1256 | for my $class ( keys %class2ids ) { |
|---|
| 1257 | eval "require $class;"; |
|---|
| 1258 | next if $@; |
|---|
| 1259 | my $newids = $class2ids{$class}->{newids}; |
|---|
| 1260 | my $keymaps = $class2ids{$class}->{keymaps}; |
|---|
| 1261 | my @objs = $class->load( { id => $newids } ); |
|---|
| 1262 | for my $obj (@objs) { |
|---|
| 1263 | my @old_ids = grep { $_->{newid} eq $obj->id } @$keymaps; |
|---|
| 1264 | my $old_id = $old_ids[0]->{oldid}; |
|---|
| 1265 | $objects->{"$class#$old_id"} = $obj; |
|---|
| 1266 | } |
|---|
| 1267 | } |
|---|
| 1268 | } |
|---|
| 1269 | |
|---|
| 1270 | my $assets = JSON::jsonToObj( decode_html($assets_json) ) |
|---|
| 1271 | if ( defined($assets_json) && $assets_json ); |
|---|
| 1272 | $assets = [] if !defined($assets); |
|---|
| 1273 | my $asset; |
|---|
| 1274 | my @errors; |
|---|
| 1275 | my $error_assets = {}; |
|---|
| 1276 | require MT::BackupRestore; |
|---|
| 1277 | my $blog_ids; |
|---|
| 1278 | my $asset_ids; |
|---|
| 1279 | |
|---|
| 1280 | if ($is_asset) { |
|---|
| 1281 | $asset = shift @$assets; |
|---|
| 1282 | $asset->{fh} = $fh; |
|---|
| 1283 | my $blogs_meta = JSON::jsonToObj( $q->param('blogs_meta') || '{}' ); |
|---|
| 1284 | MT::BackupRestore->_restore_asset_multi( $asset, $objects, |
|---|
| 1285 | $error_assets, sub { $app->print(@_); }, $blogs_meta ); |
|---|
| 1286 | if ( defined( $error_assets->{ $asset->{asset_id} } ) ) { |
|---|
| 1287 | $app->log( |
|---|
| 1288 | { |
|---|
| 1289 | message => $app->translate('Restoring a file failed: ') |
|---|
| 1290 | . $error_assets->{ $asset->{asset_id} }, |
|---|
| 1291 | level => MT::Log::WARNING(), |
|---|
| 1292 | class => 'system', |
|---|
| 1293 | category => 'restore', |
|---|
| 1294 | } |
|---|
| 1295 | ); |
|---|
| 1296 | } |
|---|
| 1297 | } |
|---|
| 1298 | else { |
|---|
| 1299 | ( $blog_ids, $asset_ids ) = eval { |
|---|
| 1300 | MT::BackupRestore->restore_process_single_file( $fh, $objects, |
|---|
| 1301 | $deferred, \@errors, $schema_version, $overwrite_template, |
|---|
| 1302 | sub { _progress($app, @_) } ); |
|---|
| 1303 | }; |
|---|
| 1304 | if ($@) { |
|---|
| 1305 | $last = 1; |
|---|
| 1306 | } |
|---|
| 1307 | } |
|---|
| 1308 | |
|---|
| 1309 | my @files = split( ',', $files ); |
|---|
| 1310 | my $file_next = shift @files if scalar(@files); |
|---|
| 1311 | if ( !defined($file_next) ) { |
|---|
| 1312 | if ( scalar(@$assets) ) { |
|---|
| 1313 | $asset = $assets->[0]; |
|---|
| 1314 | $file_next = $asset->{asset_id} . '-' . $asset->{name}; |
|---|
| 1315 | $param->{is_asset} = 1; |
|---|
| 1316 | } |
|---|
| 1317 | } |
|---|
| 1318 | $param->{files} = join( ',', @files ); |
|---|
| 1319 | $param->{assets} = encode_html( JSON::objToJson($assets) ); |
|---|
| 1320 | $param->{name} = $file_next; |
|---|
| 1321 | if ( 0 < scalar(@files) ) { |
|---|
| 1322 | $param->{last} = 0; |
|---|
| 1323 | } |
|---|
| 1324 | elsif ( 0 >= scalar(@$assets) - 1 ) { |
|---|
| 1325 | $param->{last} = 1; |
|---|
| 1326 | } |
|---|
| 1327 | else { |
|---|
| 1328 | $param->{last} = 0; |
|---|
| 1329 | } |
|---|
| 1330 | $param->{is_dirty} = scalar( keys %$deferred ); |
|---|
| 1331 | if ($last) { |
|---|
| 1332 | $param->{restore_end} = 1; |
|---|
| 1333 | if ( $param->{is_dirty} ) { |
|---|
| 1334 | _log_dirty_restore( $app, $deferred ); |
|---|
| 1335 | my $log_url = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 1336 | $param->{error} = |
|---|
| 1337 | $app->translate( |
|---|
| 1338 | 'Some objects were not restored because their parent objects were not restored.' |
|---|
| 1339 | ); |
|---|
| 1340 | $param->{error_url} = $log_url; |
|---|
| 1341 | } |
|---|
| 1342 | elsif ( scalar( keys %$error_assets ) ) { |
|---|
| 1343 | $param->{error} = |
|---|
| 1344 | $app->translate('Some of the files were not restored correctly.'); |
|---|
| 1345 | my $log_url = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 1346 | $param->{error_url} = $log_url; |
|---|
| 1347 | } |
|---|
| 1348 | elsif ( scalar @errors ) { |
|---|
| 1349 | $param->{error} = join '; ', @errors; |
|---|
| 1350 | my $log_url = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 1351 | $param->{error_url} = $log_url; |
|---|
| 1352 | } |
|---|
| 1353 | else { |
|---|
| 1354 | $app->log( |
|---|
| 1355 | { |
|---|
| 1356 | message => $app->translate( |
|---|
| 1357 | "Successfully restored objects to Movable Type system by user '[_1]'", |
|---|
| 1358 | $app->user->name |
|---|
| 1359 | ), |
|---|
| 1360 | level => MT::Log::INFO(), |
|---|
| 1361 | class => 'system', |
|---|
| 1362 | category => 'restore' |
|---|
| 1363 | } |
|---|
| 1364 | ); |
|---|
| 1365 | $param->{ok_url} = $app->uri( mode => 'start_restore', args => {} ); |
|---|
| 1366 | } |
|---|
| 1367 | } |
|---|
| 1368 | else { |
|---|
| 1369 | my %objects_json; |
|---|
| 1370 | %objects_json = map { $_ => $objects->{$_}->id } keys %$objects; |
|---|
| 1371 | $param->{objects_json} = JSON::objToJson( \%objects_json ); |
|---|
| 1372 | $param->{deferred_json} = JSON::objToJson($deferred); |
|---|
| 1373 | |
|---|
| 1374 | $param->{error} = join( '; ', @errors ); |
|---|
| 1375 | if ( defined($blog_ids) && scalar(@$blog_ids) ) { |
|---|
| 1376 | $param->{next_mode} = 'dialog_adjust_sitepath'; |
|---|
| 1377 | $param->{blog_ids} = join( ',', @$blog_ids ); |
|---|
| 1378 | $param->{asset_ids} = join( ',', @$asset_ids ) |
|---|
| 1379 | if defined($asset_ids); |
|---|
| 1380 | } |
|---|
| 1381 | else { |
|---|
| 1382 | $param->{next_mode} = 'dialog_restore_upload'; |
|---|
| 1383 | } |
|---|
| 1384 | } |
|---|
| 1385 | MT->run_callbacks('restore', $objects, $deferred, \@errors, sub { _progress( $app, @_ ) }); |
|---|
| 1386 | |
|---|
| 1387 | $app->print( $app->build_page( 'dialog/restore_end.tmpl', $param ) ); |
|---|
| 1388 | close $fh if $fh; |
|---|
| 1389 | } |
|---|
| 1390 | |
|---|
| 1391 | sub dialog_adjust_sitepath { |
|---|
| 1392 | my $app = shift; |
|---|
| 1393 | my $user = $app->user; |
|---|
| 1394 | return $app->errtrans("Permission denied.") if !$user->is_superuser; |
|---|
| 1395 | $app->validate_magic() or return; |
|---|
| 1396 | |
|---|
| 1397 | my $q = $app->param; |
|---|
| 1398 | my $tmp_dir = $q->param('tmp_dir'); |
|---|
| 1399 | my $error = $q->param('error') || q(); |
|---|
| 1400 | my $uploaded = $q->param('restore_upload') || 0; |
|---|
| 1401 | my @blog_ids = split ',', $q->param('blog_ids'); |
|---|
| 1402 | my $asset_ids = $q->param('asset_ids'); |
|---|
| 1403 | my @blogs = $app->model('blog')->load( { id => \@blog_ids } ); |
|---|
| 1404 | my @blogs_loop; |
|---|
| 1405 | |
|---|
| 1406 | foreach my $blog (@blogs) { |
|---|
| 1407 | push @blogs_loop, |
|---|
| 1408 | { |
|---|
| 1409 | name => $blog->name, |
|---|
| 1410 | id => $blog->id, |
|---|
| 1411 | old_site_path => $blog->site_path, |
|---|
| 1412 | old_site_url => $blog->site_url, |
|---|
| 1413 | $blog->column('archive_path') |
|---|
| 1414 | ? ( old_archive_path => $blog->archive_path ) |
|---|
| 1415 | : (), |
|---|
| 1416 | $blog->column('archive_url') |
|---|
| 1417 | ? ( old_archive_url => $blog->archive_url ) |
|---|
| 1418 | : (), |
|---|
| 1419 | }; |
|---|
| 1420 | } |
|---|
| 1421 | my $param = { blogs_loop => \@blogs_loop, tmp_dir => $tmp_dir }; |
|---|
| 1422 | $param->{error} = $error if $error; |
|---|
| 1423 | $param->{restore_upload} = $uploaded if $uploaded; |
|---|
| 1424 | $param->{asset_ids} = $asset_ids if $asset_ids; |
|---|
| 1425 | for my $key ( |
|---|
| 1426 | qw(files assets last redirect is_dirty is_asset objects_json deferred_json) |
|---|
| 1427 | ) |
|---|
| 1428 | { |
|---|
| 1429 | $param->{$key} = $q->param($key) if $q->param($key); |
|---|
| 1430 | } |
|---|
| 1431 | $param->{name} = $q->param('current_file'); |
|---|
| 1432 | $param->{screen_id} = "adjust-sitepath"; |
|---|
| 1433 | $app->load_tmpl( 'dialog/adjust_sitepath.tmpl', $param ); |
|---|
| 1434 | } |
|---|
| 1435 | |
|---|
| 1436 | sub convert_to_html { |
|---|
| 1437 | my $app = shift; |
|---|
| 1438 | my $format = $app->param('format'); |
|---|
| 1439 | my $text = $app->param('text'); |
|---|
| 1440 | # XMLHttpRequest always send text in UTF-8... right? |
|---|
| 1441 | if ( defined $text ) { |
|---|
| 1442 | $text = encode_text($text, 'utf-8', $app->config->PublishCharset); |
|---|
| 1443 | } |
|---|
| 1444 | else { |
|---|
| 1445 | $text = '' ; |
|---|
| 1446 | } |
|---|
| 1447 | my $text_more = $app->param('text_more'); |
|---|
| 1448 | if ( defined $text_more ) { |
|---|
| 1449 | $text_more = encode_text($text_more, 'utf-8', $app->config->PublishCharset); |
|---|
| 1450 | } |
|---|
| 1451 | else { |
|---|
| 1452 | $text_more = '' ; |
|---|
| 1453 | } |
|---|
| 1454 | my $result = { |
|---|
| 1455 | text => $app->apply_text_filters( $text, [$format] ), |
|---|
| 1456 | text_more => $app->apply_text_filters( $text_more, [$format] ), |
|---|
| 1457 | format => $format, |
|---|
| 1458 | }; |
|---|
| 1459 | return $app->json_result($result); |
|---|
| 1460 | } |
|---|
| 1461 | |
|---|
| 1462 | sub update_list_prefs { |
|---|
| 1463 | my $app = shift; |
|---|
| 1464 | my $prefs = $app->list_pref( $app->param('_type') ); |
|---|
| 1465 | $app->call_return; |
|---|
| 1466 | } |
|---|
| 1467 | |
|---|
| 1468 | sub recover_passwords { |
|---|
| 1469 | my $app = shift; |
|---|
| 1470 | my @id = $app->param('id'); |
|---|
| 1471 | |
|---|
| 1472 | return $app->errtrans("Permission denied.") |
|---|
| 1473 | unless $app->user->is_superuser(); |
|---|
| 1474 | |
|---|
| 1475 | my $class = ref $app eq 'MT::App::Upgrader' ? 'MT::BasicAuthor' : $app->model('author'); |
|---|
| 1476 | eval "use $class;"; |
|---|
| 1477 | |
|---|
| 1478 | my @msg_loop; |
|---|
| 1479 | foreach (@id) { |
|---|
| 1480 | my $author = $class->load($_) |
|---|
| 1481 | or next; |
|---|
| 1482 | my ( $rc, $res ) = reset_password( $app, $author, $author->hint ); |
|---|
| 1483 | push @msg_loop, { message => $res }; |
|---|
| 1484 | } |
|---|
| 1485 | |
|---|
| 1486 | $app->load_tmpl( 'recover_password_result.tmpl', |
|---|
| 1487 | { message_loop => \@msg_loop, return_url => $app->return_uri } ); |
|---|
| 1488 | } |
|---|
| 1489 | |
|---|
| 1490 | sub reset_password { |
|---|
| 1491 | my $app = shift; |
|---|
| 1492 | my ($author) = $_[0]; |
|---|
| 1493 | my $hint = $_[1]; |
|---|
| 1494 | my $name = $_[2]; |
|---|
| 1495 | |
|---|
| 1496 | require MT::Auth; |
|---|
| 1497 | require MT::Log; |
|---|
| 1498 | if ( !MT::Auth->can_recover_password ) { |
|---|
| 1499 | $app->log( |
|---|
| 1500 | { |
|---|
| 1501 | message => $app->translate( |
|---|
| 1502 | "Invalid password recovery attempt; can't recover password in this configuration" |
|---|
| 1503 | ), |
|---|
| 1504 | level => MT::Log::SECURITY(), |
|---|
| 1505 | class => 'system', |
|---|
| 1506 | category => 'recover_password', |
|---|
| 1507 | } |
|---|
| 1508 | ); |
|---|
| 1509 | return ( 0, |
|---|
| 1510 | $app->translate("Can't recover password in this configuration") ); |
|---|
| 1511 | } |
|---|
| 1512 | |
|---|
| 1513 | $app->log( |
|---|
| 1514 | { |
|---|
| 1515 | message => $app->translate( |
|---|
| 1516 | "Invalid user name '[_1]' in password recovery attempt", $name |
|---|
| 1517 | ), |
|---|
| 1518 | level => MT::Log::SECURITY(), |
|---|
| 1519 | class => 'system', |
|---|
| 1520 | category => 'recover_password', |
|---|
| 1521 | } |
|---|
| 1522 | ), |
|---|
| 1523 | return ( 0, $app->translate("User name or password hint is incorrect.") ) |
|---|
| 1524 | unless $author; |
|---|
| 1525 | return ( 0, |
|---|
| 1526 | $app->translate("User has not set pasword hint; cannot recover password") |
|---|
| 1527 | ) if ( $hint && !$author->hint ); |
|---|
| 1528 | |
|---|
| 1529 | $app->log( |
|---|
| 1530 | { |
|---|
| 1531 | message => $app->translate( |
|---|
| 1532 | "Invalid attempt to recover password (used hint '[_1]')", |
|---|
| 1533 | $hint |
|---|
| 1534 | ), |
|---|
| 1535 | level => MT::Log::SECURITY(), |
|---|
| 1536 | class => 'system', |
|---|
| 1537 | category => 'recover_password' |
|---|
| 1538 | } |
|---|
| 1539 | ), |
|---|
| 1540 | return ( 0, $app->translate("User name or password hint is incorrect.") ) |
|---|
| 1541 | unless $author->hint eq $hint; |
|---|
| 1542 | |
|---|
| 1543 | return ( 0, $app->translate("User does not have email address") ) |
|---|
| 1544 | unless $author->email; |
|---|
| 1545 | |
|---|
| 1546 | my @pool = ( 'a' .. 'z', 0 .. 9 ); |
|---|
| 1547 | my $pass = ''; |
|---|
| 1548 | for ( 1 .. 8 ) { $pass .= $pool[ rand @pool ] } |
|---|
| 1549 | $author->set_password($pass); |
|---|
| 1550 | $author->save; |
|---|
| 1551 | my $message = |
|---|
| 1552 | $app->translate( |
|---|
| 1553 | "Password was reset for user '[_1]' (user #[_2]). Password was sent to the following address: [_3]", |
|---|
| 1554 | $author->name, $author->id, $author->email ); |
|---|
| 1555 | $app->log( |
|---|
| 1556 | { |
|---|
| 1557 | message => $message, |
|---|
| 1558 | level => MT::Log::SECURITY(), |
|---|
| 1559 | class => 'system', |
|---|
| 1560 | category => 'recover_password' |
|---|
| 1561 | } |
|---|
| 1562 | ); |
|---|
| 1563 | |
|---|
| 1564 | my $address = |
|---|
| 1565 | defined $author->nickname |
|---|
| 1566 | ? $author->nickname . ' <' . $author->email . '>' |
|---|
| 1567 | : $author->email; |
|---|
| 1568 | my %head = ( |
|---|
| 1569 | id => 'recover_password', |
|---|
| 1570 | To => $address, |
|---|
| 1571 | From => $app->config('EmailAddressMain') || $address, |
|---|
| 1572 | Subject => $app->translate("Password Recovery") |
|---|
| 1573 | ); |
|---|
| 1574 | my $charset = $app->charset; |
|---|
| 1575 | my $mail_enc = uc( $app->config('MailEncoding') || $charset ); |
|---|
| 1576 | $head{'Content-Type'} = qq(text/plain; charset="$mail_enc"); |
|---|
| 1577 | |
|---|
| 1578 | my $body = $app->build_email( 'recover-password.tmpl', |
|---|
| 1579 | { user_password => $pass, link_to_login => $app->base . $app->mt_uri } |
|---|
| 1580 | ); |
|---|
| 1581 | $body = wrap_text( $body, 72 ); |
|---|
| 1582 | require MT::Mail; |
|---|
| 1583 | MT::Mail->send( \%head, $body ) |
|---|
| 1584 | or return ( |
|---|
| 1585 | 0, |
|---|
| 1586 | $app->translate( |
|---|
| 1587 | "Error sending mail ([_1]); please fix the problem, then " |
|---|
| 1588 | . "try again to recover your password.", |
|---|
| 1589 | MT::Mail->errstr |
|---|
| 1590 | ) |
|---|
| 1591 | ); |
|---|
| 1592 | ( 1, $message ); |
|---|
| 1593 | } |
|---|
| 1594 | |
|---|
| 1595 | sub restore_file { |
|---|
| 1596 | my $app = shift; |
|---|
| 1597 | my ( $fh, $errormsg ) = @_; |
|---|
| 1598 | my $q = $app->param; |
|---|
| 1599 | my $schema_version = |
|---|
| 1600 | $q->param('ignore_schema_conflict') |
|---|
| 1601 | ? 'ignore' |
|---|
| 1602 | : $app->config('SchemaVersion'); |
|---|
| 1603 | my $overwrite_template = $q->param('overwrite_global_templates') ? 1 : 0; |
|---|
| 1604 | |
|---|
| 1605 | require MT::BackupRestore; |
|---|
| 1606 | my ( $deferred, $blogs ) = |
|---|
| 1607 | MT::BackupRestore->restore_file( $fh, $errormsg, $schema_version, $overwrite_template, |
|---|
| 1608 | sub { _progress( $app, @_ ); } ); |
|---|
| 1609 | |
|---|
| 1610 | if ( !defined($deferred) || scalar( keys %$deferred ) ) { |
|---|
| 1611 | _log_dirty_restore( $app, $deferred ); |
|---|
| 1612 | my $log_url = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 1613 | $$errormsg .= '; ' if $$errormsg; |
|---|
| 1614 | $$errormsg .= $app->translate( |
|---|
| 1615 | 'Some objects were not restored because their parent objects were not restored. Detailed information is in the <a href="javascript:void(0);" onclick="closeDialog(\'[_1]\');">activity log</a>.', |
|---|
| 1616 | $log_url |
|---|
| 1617 | ); |
|---|
| 1618 | return $blogs; |
|---|
| 1619 | } |
|---|
| 1620 | if ($$errormsg) { |
|---|
| 1621 | $app->log( |
|---|
| 1622 | { |
|---|
| 1623 | message => $$errormsg, |
|---|
| 1624 | level => MT::Log::ERROR(), |
|---|
| 1625 | class => 'system', |
|---|
| 1626 | category => 'restore', |
|---|
| 1627 | } |
|---|
| 1628 | ); |
|---|
| 1629 | return $blogs; |
|---|
| 1630 | } |
|---|
| 1631 | |
|---|
| 1632 | $app->log( |
|---|
| 1633 | { |
|---|
| 1634 | message => $app->translate( |
|---|
| 1635 | "Successfully restored objects to Movable Type system by user '[_1]'", |
|---|
| 1636 | $app->user->name |
|---|
| 1637 | ), |
|---|
| 1638 | level => MT::Log::INFO(), |
|---|
| 1639 | class => 'system', |
|---|
| 1640 | category => 'restore' |
|---|
| 1641 | } |
|---|
| 1642 | ); |
|---|
| 1643 | |
|---|
| 1644 | $blogs; |
|---|
| 1645 | } |
|---|
| 1646 | |
|---|
| 1647 | sub restore_directory { |
|---|
| 1648 | my $app = shift; |
|---|
| 1649 | my ( $dir, $error ) = @_; |
|---|
| 1650 | |
|---|
| 1651 | if ( !-d $dir ) { |
|---|
| 1652 | $$error = $app->translate( '[_1] is not a directory.', $dir ); |
|---|
| 1653 | return ( undef, undef ); |
|---|
| 1654 | } |
|---|
| 1655 | |
|---|
| 1656 | my $q = $app->param; |
|---|
| 1657 | my $schema_version = |
|---|
| 1658 | $q->param('ignore_schema_conflict') |
|---|
| 1659 | ? 'ignore' |
|---|
| 1660 | : $app->config('SchemaVersion'); |
|---|
| 1661 | |
|---|
| 1662 | my $overwrite_template = $q->param('overwrite_global_templates') ? 1 : 0; |
|---|
| 1663 | |
|---|
| 1664 | my @errors; |
|---|
| 1665 | my %error_assets; |
|---|
| 1666 | require MT::BackupRestore; |
|---|
| 1667 | my ( $deferred, $blogs, $assets ) = |
|---|
| 1668 | MT::BackupRestore->restore_directory( $dir, \@errors, \%error_assets, |
|---|
| 1669 | $schema_version, $overwrite_template, sub { _progress( $app, @_ ); } ); |
|---|
| 1670 | |
|---|
| 1671 | if ( scalar @errors ) { |
|---|
| 1672 | $$error = $app->translate('Error occured during restore process.'); |
|---|
| 1673 | $app->log( |
|---|
| 1674 | { |
|---|
| 1675 | message => $$error, |
|---|
| 1676 | level => MT::Log::WARNING(), |
|---|
| 1677 | class => 'system', |
|---|
| 1678 | category => 'restore', |
|---|
| 1679 | metadata => join( '; ', @errors ), |
|---|
| 1680 | } |
|---|
| 1681 | ); |
|---|
| 1682 | } |
|---|
| 1683 | return ( $blogs, $assets ) unless ( defined($deferred) && %$deferred ); |
|---|
| 1684 | |
|---|
| 1685 | if ( scalar( keys %error_assets ) ) { |
|---|
| 1686 | my $data; |
|---|
| 1687 | while ( my ( $key, $value ) = each %error_assets ) { |
|---|
| 1688 | $data .= |
|---|
| 1689 | $app->translate( 'MT::Asset#[_1]: ', $key ) . $value . "\n"; |
|---|
| 1690 | } |
|---|
| 1691 | my $message = $app->translate('Some of files could not be restored.'); |
|---|
| 1692 | $app->log( |
|---|
| 1693 | { |
|---|
| 1694 | message => $message, |
|---|
| 1695 | level => MT::Log::WARNING(), |
|---|
| 1696 | class => 'system', |
|---|
| 1697 | category => 'restore', |
|---|
| 1698 | metadata => $data, |
|---|
| 1699 | } |
|---|
| 1700 | ); |
|---|
| 1701 | $$error .= $message; |
|---|
| 1702 | } |
|---|
| 1703 | |
|---|
| 1704 | if ( scalar( keys %$deferred ) ) { |
|---|
| 1705 | _log_dirty_restore( $app, $deferred ); |
|---|
| 1706 | my $log_url = $app->uri( mode => 'view_log', args => {} ); |
|---|
| 1707 | $$error = $app->translate( |
|---|
| 1708 | 'Some objects were not restored because their parent objects were not restored. Detailed information is in the <a href="javascript:void(0);" onclick="closeDialog(\'[_1]\');">activity log</a>.', |
|---|
| 1709 | $log_url |
|---|
| 1710 | ); |
|---|
| 1711 | return ( $blogs, $assets ); |
|---|
| 1712 | } |
|---|
| 1713 | |
|---|
| 1714 | return ( $blogs, $assets ) if $$error; |
|---|
| 1715 | |
|---|
| 1716 | $app->log( |
|---|
| 1717 | { |
|---|
| 1718 | message => $app->translate( |
|---|
| 1719 | "Successfully restored objects to Movable Type system by user '[_1]'", |
|---|
| 1720 | $app->user->name |
|---|
| 1721 | ), |
|---|
| 1722 | level => MT::Log::INFO(), |
|---|
| 1723 | class => 'system', |
|---|
| 1724 | category => 'restore' |
|---|
| 1725 | } |
|---|
| 1726 | ); |
|---|
| 1727 | return ( $blogs, $assets ); |
|---|
| 1728 | } |
|---|
| 1729 | |
|---|
| 1730 | sub restore_upload_manifest { |
|---|
| 1731 | my $app = shift; |
|---|
| 1732 | my ($fh) = @_; |
|---|
| 1733 | my $user = $app->user; |
|---|
| 1734 | return $app->errtrans("Permission denied.") if !$user->is_superuser; |
|---|
| 1735 | $app->validate_magic() or return; |
|---|
| 1736 | |
|---|
| 1737 | my $q = $app->param; |
|---|
| 1738 | |
|---|
| 1739 | require MT::BackupRestore; |
|---|
| 1740 | my $backups = MT::BackupRestore->process_manifest($fh); |
|---|
| 1741 | return $app->errtrans( |
|---|
| 1742 | "Uploaded file was not a valid Movable Type backup manifest file.") |
|---|
| 1743 | if !defined($backups); |
|---|
| 1744 | |
|---|
| 1745 | my $files = $backups->{files}; |
|---|
| 1746 | my $assets = $backups->{assets}; |
|---|
| 1747 | my $file_next = shift @$files if defined($files) && scalar(@$files); |
|---|
| 1748 | my $assets_json; |
|---|
| 1749 | my $param = {}; |
|---|
| 1750 | |
|---|
| 1751 | if ( !defined($file_next) ) { |
|---|
| 1752 | if ( scalar(@$assets) > 0 ) { |
|---|
| 1753 | my $asset = shift @$assets; |
|---|
| 1754 | $file_next = $asset->{name}; |
|---|
| 1755 | $param->{is_asset} = 1; |
|---|
| 1756 | } |
|---|
| 1757 | } |
|---|
| 1758 | require JSON; |
|---|
| 1759 | $assets_json = encode_url( JSON::objToJson($assets) ) |
|---|
| 1760 | if scalar(@$assets) > 0; |
|---|
| 1761 | $param->{files} = join( ',', @$files ); |
|---|
| 1762 | $param->{assets} = $assets_json; |
|---|
| 1763 | $param->{filename} = $file_next; |
|---|
| 1764 | $param->{last} = scalar(@$files) ? 0 : ( scalar(@$assets) ? 0 : 1 ); |
|---|
| 1765 | $param->{open_dialog} = 1; |
|---|
| 1766 | $param->{schema_version} = |
|---|
| 1767 | $q->param('ignore_schema_conflict') |
|---|
| 1768 | ? 'ignore' |
|---|
| 1769 | : $app->config('SchemaVersion'); |
|---|
| 1770 | $param->{overwrite_templates} = $q->param('overwrite_global_templates') ? 1 : 0; |
|---|
| 1771 | |
|---|
| 1772 | $param->{dialog_mode} = 'dialog_restore_upload'; |
|---|
| 1773 | $param->{dialog_params} = |
|---|
| 1774 | 'start=1' |
|---|
| 1775 | . '&magic_token=' |
|---|
| 1776 | . $app->current_magic |
|---|
| 1777 | . '&files=' |
|---|
| 1778 | . $param->{files} |
|---|
| 1779 | . '&assets=' |
|---|
| 1780 | . $param->{assets} |
|---|
| 1781 | . '&current_file=' |
|---|
| 1782 | . $param->{filename} |
|---|
| 1783 | . '&last=' |
|---|
| 1784 | . $param->{'last'} |
|---|
| 1785 | . '&schema_version=' |
|---|
| 1786 | . $param->{schema_version} |
|---|
| 1787 | . '&overwrite_templates=' |
|---|
| 1788 | . $param->{overwrite_templates} |
|---|
| 1789 | . '&redirect=1'; |
|---|
| 1790 | $app->load_tmpl( 'restore.tmpl', $param ); |
|---|
| 1791 | |
|---|
| 1792 | #close $fh if $fh; |
|---|
| 1793 | } |
|---|
| 1794 | |
|---|
| 1795 | sub _backup_finisher { |
|---|
| 1796 | my $app = shift; |
|---|
| 1797 | my ( $fnames, $param ) = @_; |
|---|
| 1798 | unless ( ref $fnames ) { |
|---|
| 1799 | $fnames = [$fnames]; |
|---|
| 1800 | } |
|---|
| 1801 | $param->{filename} = $fnames->[0]; |
|---|
| 1802 | $param->{backup_success} = 1; |
|---|
| 1803 | require MT::Session; |
|---|
| 1804 | MT::Session->remove( { kind => 'BU' } ); |
|---|
| 1805 | foreach my $fname (@$fnames) { |
|---|
| 1806 | my $sess = MT::Session->new; |
|---|
| 1807 | $sess->id( $app->make_magic_token() ); |
|---|
| 1808 | $sess->kind('BU'); # BU == Backup |
|---|
| 1809 | $sess->name($fname); |
|---|
| 1810 | $sess->start(time); |
|---|
| 1811 | $sess->save; |
|---|
| 1812 | } |
|---|
| 1813 | my $message; |
|---|
| 1814 | if ( my $blog_id = $param->{blog_id} || $param->{blog_ids} ) { |
|---|
| 1815 | $message = $app->translate( |
|---|
| 1816 | "Blog(s) (ID:[_1]) was/were successfully backed up by user '[_2]'", |
|---|
| 1817 | $blog_id, $app->user->name |
|---|
| 1818 | ); |
|---|
| 1819 | } |
|---|
| 1820 | else { |
|---|
| 1821 | $message = |
|---|
| 1822 | $app->translate( |
|---|
| 1823 | "Movable Type system was successfully backed up by user '[_1]'", |
|---|
| 1824 | $app->user->name ); |
|---|
| 1825 | } |
|---|
| 1826 | $app->log( |
|---|
| 1827 | { |
|---|
| 1828 | message => $message, |
|---|
| 1829 | level => MT::Log::INFO(), |
|---|
| 1830 | class => 'system', |
|---|
| 1831 | category => 'restore' |
|---|
| 1832 | } |
|---|
| 1833 | ); |
|---|
| 1834 | $app->print( $app->build_page( 'include/backup_end.tmpl', $param ) ); |
|---|
| 1835 | } |
|---|
| 1836 | |
|---|
| 1837 | sub _progress { |
|---|
| 1838 | my $app = shift; |
|---|
| 1839 | my $ids = $app->request('progress_ids') || {}; |
|---|
| 1840 | |
|---|
| 1841 | my ( $str, $id ) = @_; |
|---|
| 1842 | if ( $id && $ids->{$id} ) { |
|---|
| 1843 | my $str_js = encode_js($str); |
|---|
| 1844 | $app->print( |
|---|
| 1845 | qq{<script type="text/javascript">progress('$str_js', '$id');</script>} |
|---|
| 1846 | ); |
|---|
| 1847 | } |
|---|
| 1848 | elsif ($id) { |
|---|
| 1849 | $ids->{$id} = 1; |
|---|
| 1850 | $app->print(qq{\n<span id="$id">$str</span>}); |
|---|
| 1851 | } |
|---|
| 1852 | else { |
|---|
| 1853 | $app->print("<span>$str</span>"); |
|---|
| 1854 | } |
|---|
| 1855 | |
|---|
| 1856 | $app->request( 'progress_ids', $ids ); |
|---|
| 1857 | } |
|---|
| 1858 | |
|---|
| 1859 | sub _log_dirty_restore { |
|---|
| 1860 | my $app = shift; |
|---|
| 1861 | my ($deferred) = @_; |
|---|
| 1862 | my %deferred_by_class; |
|---|
| 1863 | for my $key ( keys %$deferred ) { |
|---|
| 1864 | my ( $class, $id ) = split( '#', $key ); |
|---|
| 1865 | if ( exists $deferred_by_class{$class} ) { |
|---|
| 1866 | push @{ $deferred_by_class{$class} }, $id; |
|---|
| 1867 | } |
|---|
| 1868 | else { |
|---|
| 1869 | $deferred_by_class{$class} = [$id]; |
|---|
| 1870 | |
|---|
| 1871 | } |
|---|
| 1872 | } |
|---|
| 1873 | while ( my ( $class_name, $ids ) = each %deferred_by_class ) { |
|---|
| 1874 | my $message = $app->translate( |
|---|
| 1875 | 'Some [_1] were not restored because their parent objects were not restored.', |
|---|
| 1876 | $class_name |
|---|
| 1877 | ); |
|---|
| 1878 | $app->log( |
|---|
| 1879 | { |
|---|
| 1880 | message => $message, |
|---|
| 1881 | level => MT::Log::WARNING(), |
|---|
| 1882 | class => 'system', |
|---|
| 1883 | category => 'restore', |
|---|
| 1884 | metadata => join( ', ', @$ids ), |
|---|
| 1885 | } |
|---|
| 1886 | ); |
|---|
| 1887 | } |
|---|
| 1888 | 1; |
|---|
| 1889 | } |
|---|
| 1890 | |
|---|
| 1891 | 1; |
|---|