1<?php 2/* 3 * Gallery - a web based photo album viewer and editor 4 * Copyright (C) 2000-2008 Bharat Mediratta 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or (at 9 * your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. 19 */ 20 21/** 22 * This controller will handle the editing of an item 23 * @package GalleryCore 24 * @subpackage UserInterface 25 * @author Bharat Mediratta <bharat@menalto.com> 26 * @version $Revision: 17679 $ 27 */ 28class ItemEditController extends GalleryController { 29 30 /** 31 * @see GalleryController::handleRequest 32 */ 33 function handleRequest($form) { 34 global $gallery; 35 36 $editPlugin = GalleryUtilities::getRequestVariables('editPlugin'); 37 38 list ($ret, $item) = $this->getItem(); 39 if ($ret) { 40 return array($ret, null); 41 } 42 $itemId = $item->getId(); 43 44 /* Make sure we have permission to edit this item */ 45 $ret = GalleryCoreApi::assertHasItemPermission($item->getId(), 'core.edit'); 46 if ($ret) { 47 return array($ret, null); 48 } 49 50 /* Check to see if we have a preferred source */ 51 list ($ret, $preferredTable) = 52 GalleryCoreApi::fetchPreferredsByItemIds(array($item->getId())); 53 if ($ret) { 54 return array($ret, null); 55 } 56 $preferred = empty($preferredTable) ? null : array_shift($preferredTable); 57 58 /* Load the thumbnail */ 59 list ($ret, $thumbnails) = GalleryCoreApi::fetchThumbnailsByItemIds(array($item->getId())); 60 if ($ret) { 61 return array($ret, null); 62 } 63 $thumbnail = empty($thumbnails) ? null : array_shift($thumbnails); 64 65 /* Get all the edit options */ 66 list ($ret, $optionInstances) = 67 ItemEditOption::getAllOptions($editPlugin, $item, $thumbnail); 68 if ($ret) { 69 return array($ret, null); 70 } 71 72 /* Load the correct edit plugin */ 73 list ($ret, $plugin) = 74 GalleryCoreApi::newFactoryInstanceById('ItemEditPlugin', $editPlugin); 75 if ($ret) { 76 return array($ret, null); 77 } 78 if (!isset($plugin)) { 79 return array(GalleryCoreApi::error(ERROR_BAD_PARAMETER), null); 80 } 81 82 $status = array(); 83 list ($ret, $error, $status['editMessage'], $requiresProgressBar) = 84 $plugin->handleRequest($form, $item, $preferred); 85 if ($ret) { 86 return array($ret, null); 87 } 88 if (!empty($requiresProgressBar)) { 89 $results['delegate']['view'] = 'core.ProgressBar'; 90 } 91 92 /* Now let each option process its data */ 93 if (!isset($status['warning'])) { 94 $status['warning'] = array(); 95 } 96 if (isset($form['action']['save'])) { 97 $progressBarOptions = array(); 98 foreach ($optionInstances as $option) { 99 if ($requiresProgressBar || $option->requiresProgressBar($form)) { 100 $progressBarOptions[] = $option; 101 } else { 102 list ($ret, $optionErrors, $optionWarnings) = 103 $option->handleRequestAfterEdit($form, $item, $preferred); 104 if ($ret) { 105 return array($ret, null); 106 } 107 108 $error = array_merge($error, $optionErrors); 109 $status['warning'] = array_merge($status['warning'], $optionWarnings); 110 } 111 } 112 113 if (empty($error) && $progressBarOptions) { 114 $templateAdapter =& $gallery->getTemplateAdapter(); 115 $templateAdapter->registerTrailerCallback( 116 array($this, 'runProgressBarOptions'), 117 array($progressBarOptions, $form, $item, $preferred, $status, $editPlugin)); 118 $results['delegate']['view'] = 'core.ProgressBar'; 119 } 120 } 121 122 if (empty($results['delegate'])) { 123 /* It's not a progress bar view */ 124 if (empty($error)) { 125 $results['redirect'] = array('view' => 'core.ItemAdmin', 126 'subView' => 'core.ItemEdit', 127 'editPlugin' => $editPlugin, 128 'itemId' => $item->getId()); 129 } else { 130 $results['delegate']['view'] = 'core.ItemAdmin'; 131 $results['delegate']['subView'] = 'core.ItemEdit'; 132 $results['delegate']['editPlugin'] = $editPlugin; 133 } 134 } 135 136 $results['status'] = $status; 137 $results['error'] = $error; 138 139 return array(null, $results); 140 } 141 142 function runProgressBarOptions($options, $form, $item, $preferred, $status, $editPlugin) { 143 global $gallery; 144 $templateAdapter =& $gallery->getTemplateAdapter(); 145 146 $error = array(); 147 foreach ($options as $option) { 148 $gallery->guaranteeTimeLimit(60); 149 150 list ($ret, $optionErrors, $optionWarnings) = 151 $option->handleRequestAfterEdit($form, $item, $preferred); 152 if ($ret) { 153 return $ret; 154 } 155 156 $error = array_merge($error, $optionErrors); 157 $status['warning'] = array_merge($status['warning'], $optionWarnings); 158 } 159 $session =& $gallery->getSession(); 160 $session->putStatus($status); 161 162 $redirect = array(); 163 $redirect['view'] = 'core.ItemAdmin'; 164 $redirect['subView'] = 'core.ItemEdit'; 165 $redirect['itemId'] = $item->getId(); 166 $redirect['editPlugin'] = $editPlugin; 167 168 $urlGenerator =& $gallery->getUrlGenerator(); 169 $templateAdapter->completeProgressBar($urlGenerator->generateUrl($redirect)); 170 171 return null; 172 } 173} 174 175/** 176 * This view will show options to edit an item 177 */ 178class ItemEditView extends GalleryView { 179 180 /** 181 * Returns sizes for "maxlength" in forms 182 * @todo This function is duplicated in ItemEditCaptions.inc, so it needs to be consolidated 183 * somehow. 184 * @param integer $value is the value from 185 * @return size for "maxlength" in form 186 * @access private 187 */ 188 function _getSizesForMaxlength($value) { 189 switch ($value) { 190 case STORAGE_SIZE_SMALL: 191 $size = 32; 192 break; 193 194 case STORAGE_SIZE_MEDIUM: 195 $size = 128; 196 break; 197 198 case STORAGE_SIZE_LARGE: 199 $size = 255; 200 break; 201 } 202 203 return $size; 204 } 205 206 /** 207 * @see GalleryView::loadTemplate 208 */ 209 function loadTemplate(&$template, &$form) { 210 global $gallery; 211 212 $editPlugin = GalleryUtilities::getRequestVariables('editPlugin'); 213 214 list ($ret, $item, $wasSpecified) = $this->getItem(); 215 if ($ret) { 216 return array($ret, null); 217 } 218 219 /* Make sure we have permission to edit this item */ 220 $ret = GalleryCoreApi::assertHasItemPermission($item->getId(), 'core.edit'); 221 if ($ret) { 222 return array($ret, null); 223 } 224 225 /* Load the thumbnail */ 226 list ($ret, $thumbnails) = GalleryCoreApi::fetchThumbnailsByItemIds(array($item->getId())); 227 if ($ret) { 228 return array($ret, null); 229 } 230 if (!empty($thumbnails)) { 231 list ($ret, $thumbnail) = GalleryCoreApi::rebuildDerivativeCacheIfNotCurrent( 232 $thumbnails[$item->getId()]->getId()); 233 if ($ret) { 234 /* Ignore thumbnail errors so we can edit items with broken thumbnail */ 235 } 236 } else { 237 $thumbnail = null; 238 } 239 240 /* Get the edit plugins that support this item type */ 241 list ($ret, $allPluginIds) = 242 GalleryCoreApi::getAllFactoryImplementationIds('ItemEditPlugin'); 243 if ($ret) { 244 return array($ret, null); 245 } 246 247 $pluginInstances = array(); 248 foreach (array_keys($allPluginIds) as $pluginId) { 249 list ($ret, $plugin) = 250 GalleryCoreApi::newFactoryInstanceById('ItemEditPlugin', $pluginId); 251 if ($ret) { 252 return array($ret, null); 253 } 254 255 if ($plugin->isSupported($item, $thumbnail)) { 256 $pluginInstances[$pluginId] = $plugin; 257 } 258 } 259 260 /* Define the first edit plugin as the default in case we need to fall back. */ 261 $editPluginIds = array_keys($pluginInstances); 262 $defaultEditPlugin = $editPluginIds[0]; 263 264 /* 265 * If the plugin is empty get it from the session. If it's empty there, default to the 266 * first plugin we find. Either way, save the user's preference in the session. 267 */ 268 $session =& $gallery->getSession(); 269 $editPluginSessionKey = 'core.view.ItemEdit.editPlugin.' . get_class($item); 270 if (empty($editPlugin)) { 271 $editPlugin = $session->get($editPluginSessionKey); 272 if (empty($editPlugin) || !in_array($editPlugin, array_keys($pluginInstances))) { 273 $editPlugin = $defaultEditPlugin; 274 } 275 } 276 /* Input validation of the given editPlugin id. */ 277 if (!isset($pluginInstances[$editPlugin])) { 278 $editPlugin = $defaultEditPlugin; 279 } 280 $session->put($editPluginSessionKey, $editPlugin); 281 282 /* Get display data for all plugins */ 283 $plugins = array(); 284 foreach ($pluginInstances as $pluginId => $plugin) { 285 list ($ret, $title) = $plugin->getTitle(); 286 if ($ret) { 287 return array($ret, null); 288 } 289 $plugins[] = array('title' => $title, 290 'id' => $pluginId, 291 'isSelected' => ($pluginId == $editPlugin)); 292 } 293 294 /* Record our item serial number in the form so that all plugins can use it */ 295 $form['serialNumber'] = $item->getSerialNumber(); 296 297 $ItemEdit = array(); 298 $ItemEdit['editPlugin'] = $editPlugin; 299 $ItemEdit['plugins'] = $plugins; 300 $ItemEdit['itemTypeNames'] = $item->itemTypeName(); 301 $ItemEdit['showEditThumbnail'] = $thumbnail != null; 302 303 list ($ret, $ItemEdit['isAdmin']) = GalleryCoreApi::isUserInSiteAdminGroup(); 304 if ($ret) { 305 return array($ret, null); 306 } 307 308 /* Let the plugin load its template data */ 309 list ($ret, $ItemEdit['pluginFile'], $ItemEdit['pluginL10Domain']) = 310 $pluginInstances[$editPlugin]->loadTemplate($template, $form, $item, $thumbnail); 311 if ($ret) { 312 return array($ret, null); 313 } 314 315 /* Get all the edit options */ 316 list ($ret, $optionInstances) = 317 ItemEditOption::getAllOptions($editPlugin, $item, $thumbnail); 318 if ($ret) { 319 return array($ret, null); 320 } 321 322 /* Now let all options load their template data */ 323 $ItemEdit['options'] = array(); 324 foreach ($optionInstances as $option) { 325 list ($ret, $entry['file'], $entry['l10Domain']) = 326 $option->loadTemplate($template, $form, $item, $thumbnail); 327 if ($ret) { 328 return array($ret, null); 329 } 330 if (!empty($entry['file'])) { 331 $ItemEdit['options'][] = $entry; 332 } 333 } 334 335 list ($ret, $entityInfo) = GalleryCoreApi::describeEntity('GalleryItem'); 336 if ($ret) { 337 return array($ret, null); 338 } 339 340 $ItemEdit['fieldLengths'] = array(); 341 $ItemEdit['fieldLengths']['title'] = 342 $this->_getSizesForMaxLength($entityInfo['GalleryItem']['members']['title']['size']); 343 344 $template->setVariable('ItemEdit', $ItemEdit); 345 $template->setVariable('controller', 'core.ItemEdit'); 346 return array(null, array('body' => 'modules/core/templates/ItemEdit.tpl')); 347 } 348 349 /** 350 * @see GalleryView::getViewDescription 351 */ 352 function getViewDescription() { 353 global $gallery; 354 355 list ($ret, $item) = $this->getItem(); 356 if ($ret) { 357 return array($ret, null); 358 } 359 360 list ($ret, $core) = GalleryCoreApi::loadPlugin('module', 'core'); 361 if ($ret) { 362 return array($ret, null); 363 } 364 365 $itemTypeNames = $item->itemTypeName(true); 366 367 return array(null, 368 $core->translate(array('text' => 'edit %s', 'arg1' => $itemTypeNames[1]))); 369 } 370} 371 372/** 373 * Interface for plugins to the ItemEdit view and controller 374 * @abstract 375 */ 376class ItemEditPlugin { 377 /** 378 * Does this plugin support the given item type? 379 * 380 * @param GalleryItem $item 381 * @param GalleryDerivative $thumbnail item's thumbnail 382 * @return boolean true if it's supported 383 */ 384 function isSupported($item, $thumbnail) { 385 return false; 386 } 387 388 /** 389 * Load the template with data from this plugin 390 * @see GalleryView::loadTemplate 391 * 392 * @param GalleryTemplate $template 393 * @param array $form the form values 394 * @param GalleryItem $item 395 * @param GalleryDerivative $thumbnail item's thumbnail 396 * @return array GalleryStatus a status code 397 * string the path to a template file to include 398 * string localization domain for the template file 399 */ 400 function loadTemplate(&$template, &$form, $item, $thumbnail) { 401 return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), null, null); 402 } 403 404 /** 405 * Let the plugin handle the incoming request 406 * @see GalleryController::handleRequest 407 * 408 * @param array $form the form values 409 * @param GalleryItem $item 410 * @param GalleryDerivative $preferred item's preferred derivative, if there is one 411 * @return array GalleryStatus a status code 412 * array error messages 413 * string localized status message 414 * boolean true if progress bar is needed 415 */ 416 function handleRequest($form, &$item, &$preferred) { 417 return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), 418 null, null, null); 419 } 420 421 /** 422 * Return a localized title for this plugin, suitable for display to the user 423 * 424 * @return array GalleryStatus a status code 425 * string localized title 426 */ 427 function getTitle() { 428 return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), null); 429 } 430 431 /** 432 * Check to see if a given operation is available for any of a set of mime types 433 * 434 * @param string $operation the operation (eg. 'rotate' or 'scale') 435 * @param array $mimeTypes 436 * @return array GalleryStatus a status code 437 * bool true if any of the mime types are supported 438 * @access protected 439 */ 440 function _checkForOperation($operation, $mimeTypes) { 441 foreach (array_unique($mimeTypes) as $mimeType) { 442 list ($ret, $toolkit) = GalleryCoreApi::getToolkitByOperation($mimeType, $operation); 443 if ($ret) { 444 return array($ret, null); 445 } 446 447 if (isset($toolkit)) { 448 break; 449 } 450 } 451 452 return array(null, isset($toolkit)); 453 } 454} 455 456/** 457 * Interface for options to the ItemEdit view and controller. 458 * Options allow us to provide extra UI in the views and extra processing in the controller so 459 * that we can add new functionality to various ItemEditPlugins 460 * @abstract 461 */ 462class ItemEditOption { 463 464 /** 465 * Return all the available option plugins 466 * 467 * @param string $editPlugin name of ItemEditPlugin 468 * @param GalleryItem $item 469 * @param GalleryDerivative $thumbnail 470 * @return array GalleryStatus a status code 471 * array ItemEditOption instances 472 * @static 473 */ 474 function getAllOptions($editPlugin, $item, $thumbnail) { 475 list ($ret, $allOptionIds) = 476 GalleryCoreApi::getAllFactoryImplementationIdsWithHint('ItemEditOption', $editPlugin); 477 if ($ret) { 478 return array($ret, null); 479 } 480 481 $optionInstances = array(); 482 foreach (array_keys($allOptionIds) as $optionId) { 483 list ($ret, $option) = 484 GalleryCoreApi::newFactoryInstanceById('ItemEditOption', $optionId); 485 if ($ret) { 486 return array($ret, null); 487 } 488 489 list ($ret, $isAppropriate) = $option->isAppropriate($item, $thumbnail); 490 if ($ret) { 491 return array($ret, null); 492 } 493 494 if ($isAppropriate) { 495 $optionInstances[$optionId] = $option; 496 } 497 } 498 499 return array(null, $optionInstances); 500 } 501 502 /** 503 * Load the template with data from this plugin 504 * @see GalleryView::loadTemplate 505 * 506 * @param GalleryTemplate $template 507 * @param array $form the form values 508 * @param GalleryItem $item 509 * @return array GalleryStatus a status code 510 * string the path to a template file to include 511 * string localization domain for the template file 512 */ 513 function loadTemplate(&$template, &$form, $item, $thumbnail) { 514 return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), null, null); 515 } 516 517 /** 518 * Let the plugin handle the incoming request. We expect the $item to be locked. 519 * @see GalleryController::handleRequest 520 * 521 * @param array $form the form values 522 * @param GalleryItem $item reference to the item 523 * @param GalleryDerivative $preferred reference to preferred derivative 524 * @return array GalleryStatus a status code 525 * array error messages 526 * array localized warning messages 527 */ 528 function handleRequestAfterEdit($form, &$item, &$preferred) { 529 return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), null, null); 530 } 531 532 /** 533 * Is this option appropriate at this time? 534 * 535 * @param GalleryItem $item 536 * @param GalleryDerivative $thumbnail 537 * @return array GalleryStatus a status code 538 * boolean true or false 539 */ 540 function isAppropriate($item, $thumbnail) { 541 return array(null, false); 542 } 543 544 /** 545 * Will this task run so long that it requires a progress bar? 546 * 547 * @param array $form the state of the current form 548 * @return boolean 549 */ 550 function requiresProgressBar($form) { 551 return false; 552 } 553} 554?> 555