To participate you must create an account on apostrophenow.org. If you have already done so, click Login.

Changeset 2134

Show
Ignore:
Timestamp:
09/03/10 16:43:34 (21 months ago)
Author:
tboutell
Message:

* A single Upload Media button. You can upload any mix of allowed file extensions. Also a single Embed Media button although for now it just directs you to the usual workflow for adding video.
* The set of file extensions, mime types and overall media types ("images," "video," "office") understood by Apostrophe is now configurable (in principle) via app.yml. In practice the main benefit is that I'm cleaning up how our own code figures out what is supported by a particular media item, and making it easier to add project specific stuff. There's no magic way to make a slideshow slot suddenly handle embeds or render BMPs etc. but the hooks are coming
* Every item is previewed, either with an icon for its format or (for images) with an actual preview.
* There is always a "Replace File" browse field, which is separate from the preview as that has generated confusion and doesn't make much sense when we can't really preview a particular format. A file is a file is a file, so let's not special case too much
* You can now remove items during the annotation pass. Previously if you uploaded three good images and one bad file (like a BMP or a bad JPEG), you were stuck uploading some bogus fourth JPEG just to make the form validate. Now you have an alternative. This needs styling
* The uploaded filename is humanized into a reasonable suggested title (TODO: get JPEG metadata in preference to this)
* When you are browsing for images for a slot, you can only upload images. When you are filtering by images, you can only upload images.
* There is just one edit form for uploaded media, however it prevents you from changing the major type so you don't break typical uses (you can't replace a GIF with a DOC).
* Our file upload widget is much smarter about detecting mime types and preferred file extensions correctly for a wide variety of file types including Office files
* Previews can be templated anywhere in the template containing the form, they don't have to be automatically inserted by render() which was very limiting in terms of where you presented them
* Removed the "active" field in favor of paying attention to the names of the item fields. This simplifies the multiple upload/multiple edit code a lot
* Multiple upload just forwards to multiple edit, we don't need the cheesy JS redirect anymore

Location:
plugins/apostrophePlugin/trunk
Files:
12 added
4 removed
21 modified
4 moved

Legend:

Unmodified
Added
Removed
  • plugins/apostrophePlugin/trunk

  • plugins/apostrophePlugin/trunk/README

    r457 r2134  
    11# Apostrophe 
    22 
    3 Welcome to the 1.0 stable release of Apostrophe! Although this is our first official stable release, our CMS is already in regular use on a number of production sites. This release reflects the fact that our code has been stable and ready for your professional use for quite some time. 
     3Welcome to Apostrophe!  
    44 
    55For complete and extensive documentation of Apostrophe please visit 
  • plugins/apostrophePlugin/trunk/lib/action/BaseaMediaActions.class.php

    r1957 r2134  
    5959    $tag = $request->getParameter('tag'); 
    6060    $type = $request->getParameter('type'); 
     61    $typeInfos = aMediaTools::getTypeInfos($type); 
     62    $this->embedAllowed = false; 
     63    $this->uploadAllowed = false; 
     64    foreach ($typeInfos as $typeInfo) 
     65    { 
     66      if ($typeInfo['embeddable']) 
     67      { 
     68        $this->embedAllowed = true; 
     69      } 
     70      if (count($typeInfo['extensions'])) 
     71      { 
     72        $this->uploadAllowed = true; 
     73      } 
     74    } 
    6175    $category = $request->getParameter('category'); 
    6276    if (aMediaTools::getType()) 
     
    409423  } 
    410424 
    411   public function executeEditImage(sfRequest $request) 
     425  public function executeEdit(sfRequest $request) 
    412426  { 
    413427    $this->forward404Unless(aMediaTools::userHasUploadPrivilege()); 
     
    424438    } 
    425439    $this->item = $item; 
    426     $this->form = new aMediaImageForm($item); 
     440    $this->form = new aMediaEditForm($item); 
    427441    if ($request->isMethod('post')) 
    428442    { 
    429       $this->firstPass = $request->getParameter('first_pass'); 
    430443      $parameters = $request->getParameter('a_media_item'); 
    431444      $files = $request->getFiles('a_media_item'); 
     
    444457          // Everything except the actual copy which can't succeed 
    445458          // until the slug is cast in stone 
    446           $object->preSaveImage($file->getTempName()); 
     459          $object->preSaveFile($file->getTempName()); 
    447460        } 
    448461        $this->form->save(); 
    449462        if ($file) 
    450463        { 
    451           $object->saveImage($file->getTempName()); 
     464          $object->saveFile($file->getTempName()); 
    452465        } 
    453466        return $this->redirect("aMedia/resumeWithPage"); 
    454467      } 
    455     } 
    456   } 
    457  
    458   public function executeEditPdf(sfRequest $request) 
    459   { 
    460     $this->forward404Unless(aMediaTools::userHasUploadPrivilege()); 
    461     $item = null; 
    462     $this->slug = false; 
    463     if ($request->hasParameter('slug')) 
    464     { 
    465       $item = $this->getItem(); 
    466       $this->slug = $item->getSlug(); 
    467     } 
    468     if ($item) 
    469     { 
    470       $this->forward404Unless($item->userHasPrivilege('edit')); 
    471     } 
    472     $this->item = $item; 
    473     $this->form = new aMediaPdfForm($item); 
    474     try 
    475     { 
    476       if ($request->isMethod('post')) 
    477       { 
    478         $this->firstPass = $request->getParameter('first_pass'); 
    479         $parameters = $request->getParameter('a_media_item'); 
    480         $files = $request->getFiles('a_media_item'); 
    481         $this->form->bind($parameters, $files); 
    482         if ($this->form->isValid()) 
    483         { 
    484           $file = $this->form->getValue('file'); 
    485           $object = $this->form->getObject(); 
    486           if ($file) 
    487           { 
    488             // This actually has to be shimmed in at a much lower level as an option if 
    489             // gs is not available. We can't just use a fake thumbnail as an 'original' as we 
    490             // do for foreign video because that would break 'download original' 
    491             // copy(sfConfig::get('sf_root_dir') . '/plugins/apostrophePlugin/web/images/a-media-pdf-btn-small.png', $previewFile); 
    492            
    493             // Everything except the actual copy which can't succeed 
    494             // until the slug is cast in stone 
    495             if (!$object->preSaveImage($file->getTempName())) 
    496             { 
    497               // Ideally this doesn't happen, in practice sometimes a PDF has 
    498               // a good signature but bad content or ghostscript hates it 
    499               throw new sfException('Defective PDF file'); 
    500             } 
    501           } 
    502           // The base implementation for saving files gets confused when  
    503           // $file is not set, a situation that our code tolerates as useful  
    504           // because if you're updating a record containing a PDF you  
    505           // often don't need to submit a new one. 
    506           unset($this->form['file']); 
    507           $this->form->save(); 
    508           if ($file) 
    509           { 
    510             $object->saveImage($file->getTempName()); 
    511           } 
    512           return $this->redirect("aMedia/resumeWithPage"); 
    513         } 
    514       } 
    515     } catch (Exception $e) 
    516     { 
    517       $this->serviceError = $e->getMessage(); 
    518       // TODO make this visible somehow 
    519468    } 
    520469  } 
     
    577526          if ($thumbnail) 
    578527          { 
    579             $object->preSaveImage($thumbnail->getTempName()); 
     528            $object->preSaveFile($thumbnail->getTempName()); 
    580529          } 
    581530          $this->form->save(); 
    582531          if ($thumbnail) 
    583532          { 
    584             $object->saveImage($thumbnail->getTempName());                      
     533            $object->saveFile($thumbnail->getTempName());                      
    585534          } 
    586535        } 
     
    644593          $object = $this->form->getObject(); 
    645594          $new = !$object->getId(); 
    646           $object->preSaveImage($thumbnailCopy); 
     595          $object->preSaveFile($thumbnailCopy); 
    647596          $object->setServiceUrl($url); 
    648597          $this->form->save(); 
    649           $object->saveImage($thumbnailCopy); 
     598          $object->saveFile($thumbnailCopy); 
    650599          unlink($thumbnailCopy); 
    651600        } 
     
    655604  } 
    656605 
    657   public function executeUploadImages(sfRequest $request) 
     606  public function executeUpload(sfRequest $request) 
    658607  { 
    659608    // Belongs at the beginning, not the end 
    660609    $this->forward404Unless(aMediaTools::userHasUploadPrivilege()); 
    661     $this->form = new aMediaUploadImagesForm(); 
     610    $this->form = new aMediaUploadMultipleForm(); 
    662611    if ($request->isMethod('post')) 
    663612    { 
     
    668617      { 
    669618        $request->setParameter('first_pass', true); 
    670         $active = array(); 
    671619        // Saving embedded forms is weird. We can get the form objects 
    672620        // via getEmbeddedForms(), but those objects were never really 
     
    676624        // See: 
    677625        // http://thatsquality.com/articles/can-the-symfony-forms-framework-be-domesticated-a-simple-todo-list 
     626        $items = $request->getParameter("a_media_items"); 
    678627        for ($i = 0; ($i < aMediaTools::getOption('batch_max')); $i++) 
    679628        { 
    680629          $values = $this->form->getValues(); 
    681           if ($values["item-$i"]['file']) 
    682           { 
    683             $active[] = $i; 
     630          $file = $values["item-$i"]['file']; 
     631          if ($file) 
     632          { 
     633            // Humanize the original filename 
     634            $title = $file->getOriginalName(); 
     635            $title = preg_replace('/\.\w+$/', '', $title); 
     636            $title = aTools::slugify($title, false, false, ' '); 
     637            $items["item-$i"]['title'] = $title; 
    684638          } 
    685639          else 
    686640          { 
    687641            // So the editImagesForm validator won't complain about these 
    688             $items = $request->getParameter("a_media_items"); 
    689642            unset($items["item-$i"]); 
    690             $request->setParameter("a_media_items", $items); 
    691643          } 
    692644        } 
    693         $request->setParameter('active', implode(",", $active)); 
    694         // We'd like to just do this... 
    695         // $this->forward('aMedia', 'editImages'); 
    696         // But we need to break out of the iframe, and  
    697         // modern browsers ignore Window-target: _top which 
    698         // would otherwise be perfect for this. 
    699         // Fortunately, the persistent file upload widget can tolerate 
    700         // a GET-method redirect very nicely as long as we pass the 
    701         // persistids. So we make the current parameters available 
    702         // to a template that breaks out of the iframe via 
    703         // JavaScript and passes the prameters on. 
    704         $this->parameters = $request->getParameterHolder('a_media_items')->getAll(); 
    705         // If I don't do this I just get redirected back to myself 
    706         unset($this->parameters['module']); 
    707         unset($this->parameters['action']); 
    708         return 'Redirect'; 
    709       } 
    710     } 
    711   } 
    712  
    713   public function executeEditImages(sfRequest $request) 
     645        $request->setParameter("a_media_items", $items); 
     646        // We're not doing stupid iframe tricks anymore, so we can just forward 
     647        $this->forward('aMedia', 'editMultiple'); 
     648        //  
     649        // // We'd like to just do this... 
     650        // // $this->forward('aMedia', 'edit'); 
     651        // // But we need to break out of the iframe, and  
     652        // // modern browsers ignore Window-target: _top which 
     653        // // would otherwise be perfect for this. 
     654        // // Fortunately, the persistent file upload widget can tolerate 
     655        // // a GET-method redirect very nicely as long as we pass the 
     656        // // persistids. So we make the current parameters available 
     657        // // to a template that breaks out of the iframe via 
     658        // // JavaScript and passes the prameters on. 
     659        // $this->parameters = $request->getParameterHolder('a_media_items')->getAll(); 
     660        // // If I don't do this I just get redirected back to myself 
     661        // unset($this->parameters['module']); 
     662        // unset($this->parameters['action']); 
     663        // return 'Redirect'; 
     664      } 
     665    } 
     666  } 
     667 
     668  public function executeEditMultiple(sfRequest $request) 
    714669  { 
    715670    $this->forward404Unless(aMediaTools::userHasUploadPrivilege()); 
     
    722677    // single HTML form element. 
    723678    $this->firstPass = $request->getParameter('first_pass'); 
    724     $active = $request->getParameter('active'); 
    725     $this->forward404Unless(preg_match("/^\d+[\d\,]*$/", $active)); 
    726     $this->active = explode(",", $request->getParameter('active')); 
    727  
    728     $this->form = new aMediaEditImagesForm($this->active); 
     679    $items = $request->getParameter('a_media_items'); 
     680    // The active parameter was redundant, just look at the items that are present. 
     681    // This allows successive passes to prune out some items if desired 
     682    $active = array(); 
     683    foreach ($items as $itemName => $item) 
     684    { 
     685      if (preg_match('/^item-(\d+)$/', $itemName, $matches)) 
     686      { 
     687        $active[] = $matches[1]; 
     688      } 
     689    } 
     690 
     691    $this->form = new aMediaEditMultipleForm($active); 
    729692    $this->form->bind( 
    730693      $request->getParameter('a_media_items'), 
    731694      $request->getFiles('a_media_items')); 
    732     if ($this->form->isValid()) 
     695    if ((!$this->firstPass) && $this->form->isValid()) 
    733696    { 
    734697      $values = $this->form->getValues(); 
     
    756719        // until the slug is cast in stone 
    757720        $file = $values[$key]['file']; 
    758         $object->preSaveImage($file->getTempName()); 
     721         
     722        $format = $file->getExtension(); 
     723        if (strlen($format)) 
     724        { 
     725          // Starts with a . 
     726          $format = substr($format, 1); 
     727        } 
     728        $object->format = $format; 
     729        $types = aMediaTools::getOption('types'); 
     730        foreach ($types as $type => $info) 
     731        { 
     732          $extensions = $info['extensions']; 
     733          if (in_array($format, $extensions)) 
     734          { 
     735            $object->type = $type; 
     736          } 
     737        } 
     738        $object->preSaveFile($file->getTempName()); 
    759739        $object->save(); 
    760         $object->saveImage($file->getTempName()); 
     740        $object->saveFile($file->getTempName()); 
    761741      } 
    762742      return $this->redirect('aMedia/resume'); 
     
    764744  } 
    765745 
     746  public function executeEmbed() 
     747  { 
     748    // TODO: rework this to be less video-oriented 
     749    return $this->redirect('aMedia/newVideo'); 
     750  } 
     751   
    766752  public function executeDelete() 
    767753  { 
  • plugins/apostrophePlugin/trunk/lib/action/BaseaMediaBackendActions.class.php

    r1917 r2134  
    1111    $item = $this->getItem(); 
    1212    $format = $request->getParameter('format'); 
    13     $this->forward404Unless( 
    14       in_array($format,  
    15       array_keys(aMediaItemTable::$mimeTypes))); 
     13    $mimeTypes = aMediaTools::getOption('mime_types'); 
     14    $this->forward404Unless(isset($mimeTypes[$format])); 
    1615    $path = $item->getOriginalPath($format); 
    1716    if (!file_exists($path)) 
     
    2120        $item->getOriginalPath($format)); 
    2221    } 
    23     header("Content-type: " . aMediaItemTable::$mimeTypes[$format]); 
     22    header("Content-type: " . $mimeTypes[$format]); 
    2423    readfile($item->getOriginalPath($format)); 
    2524    // Don't let the binary get decorated with crap 
     
    3534    $resizeType = $request->getParameter('resizeType'); 
    3635    $format = $request->getParameter('format'); 
    37     $this->forward404Unless( 
    38       in_array($format,  
    39       array_keys(aMediaItemTable::$mimeTypes))); 
     36    $mimeTypes = aMediaTools::getOption('mime_types'); 
     37    $this->forward404Unless(isset($mimeTypes[$format])); 
    4038    $this->forward404Unless(($resizeType !== 'c') || ($resizeType !== 's')); 
    4139    // EDITED FOR ARBITRARY CROPPING 
     
    105103    // PHP interpreter hit to consider, so use those directives! 
    106104    header("Content-length: " . filesize($output)); 
    107     header("Content-type: " . aMediaItemTable::$mimeTypes[$format]); 
     105    header("Content-type: " . $mimeTypes[$format]); 
    108106    readfile($output); 
    109107      // If I don't bail out manually here I get PHP warnings, 
  • plugins/apostrophePlugin/trunk/lib/form/BaseaMediaImageForm.class.php

    r1697 r2134  
    2323     
    2424    $this->setWidget('file', new aWidgetFormInputFilePersistent(array( 
    25       // Not yet 
    26       // "iframe" => true, 
    27       // "progress" => "Uploading...", 
    2825      'image-preview' => aMediaTools::getOption('gallery_constraints') 
    2926    ))); 
  • plugins/apostrophePlugin/trunk/lib/helper/aHelper.php

    r2076 r2134  
    447447{ 
    448448  $args = array_slice(func_get_args(), 1); 
    449    
    450449  a_js_call_array($callable, $args); 
    451450} 
  • plugins/apostrophePlugin/trunk/lib/model/doctrine/PluginaMediaItem.class.php

    r1935 r2134  
    108108  } 
    109109   
    110   public function preSaveImage($file) 
     110  public function preSaveFile($file) 
    111111  { 
    112112    // Refactored into aImageConverter for easier reuse of this should-be-in-PHP functionality 
     
    123123        $this->height = $info['height']; 
    124124      } 
    125       $this->format = $info['format']; 
     125      // Don't force this, but it's useful when we're not 
     126      // coming from a normal upload form 
     127      if (!isset($file->format)) 
     128      { 
     129        $this->format = $info['format']; 
     130      } 
    126131      $this->clearImageCache(true); 
    127       return true; 
    128     } 
    129     else 
    130     { 
    131       return false; 
    132     } 
    133   } 
    134  
    135   public function saveImage($file) 
     132    } 
     133  } 
     134 
     135  public function saveFile($file) 
    136136  { 
    137137    if (!$this->width) 
    138138    { 
    139       if (!$this->preSaveImage($file)) 
     139      if (!$this->preSaveFile($file)) 
    140140      { 
    141141        return false; 
     
    369369    return $this->getTable()->findOneBySlug($p[0]); 
    370370  } 
     371   
     372  public function getDownloadable() 
     373  { 
     374    // Right now videos are always embedded and nothing else is 
     375    return ($this->type !== 'video'); 
     376  } 
     377   
     378  public function getEmbeddable() 
     379  { 
     380    // Right now videos are always embedded and nothing else is 
     381    return ($this->type === 'video'); 
     382  } 
    371383} 
  • plugins/apostrophePlugin/trunk/lib/model/doctrine/PluginaMediaItemTable.class.php

    r1917 r2134  
    6767    return $q->execute(); 
    6868  } 
    69   static public $mimeTypes = array( 
    70     "gif" => "image/gif", 
    71     "png" => "image/png", 
    72     "jpg" => "image/jpeg", 
    73     "pdf" => "application/pdf" 
    74   ); 
    7569   
    7670  // Returns a query matching media items satisfying the specified parameters, all of which 
     
    7973  // tag 
    8074  // search 
    81   // type (video or image) 
     75  // type (video, image, etc) 
    8276  // user (a username, to determine access rights) 
    8377  // aspect-width and aspect-height (returns only images with the specified aspect ratio) 
  • plugins/apostrophePlugin/trunk/lib/toolkit/BaseaTools.class.php

    r2030 r2134  
    688688  // trailing -'s are removed 
    689689   
    690   static public function slugify($path, $allowSlashes = false) 
     690  // $betweenWords must not contain characters that have special meaning in a regexp. 
     691  // Usually it is - (the default) or ' ' 
     692   
     693  static public function slugify($path, $allowSlashes = false, $allowUnderscores = true, $betweenWords = '-') 
    691694  { 
    692695    // This is the inverse of the method above 
     
    694697    { 
    695698      // UTF-8 capable replacement for \W. Works fine for English and also for Greek, etc. 
    696       $alnum = '\p{L}\p{N}_'; 
     699      $alnum = '\p{L}\p{N}' . ($allowUnderscores ? '_' : ''); 
    697700      $modifier = 'u'; 
    698701    } 
    699702    else 
    700703    { 
    701       $alnum = '\w'; 
     704      $alnum = $allowUnderscores ? '\w' : '[A-Za-z0-9]'; 
    702705      $modifier = ''; 
    703706    } 
     
    706709      $alnum .= '\/'; 
    707710    } 
    708     $regexp = "/[^$alnum\-]+/$modifier"; 
    709     $path = aTools::strtolower(preg_replace("/[^$alnum\-]+/$modifier", '-', $path));   
     711    // Removing - here expands flexibility and shouldn't hurt because it's the replacement anyway 
     712    $regexp = "/[^$alnum]+/$modifier"; 
     713    $path = aTools::strtolower(preg_replace($regexp, $betweenWords, $path));   
    710714    if ($allowSlashes) 
    711715    { 
     
    716720    } 
    717721    // No consecutive dashes 
    718     $path = preg_replace("/-+/$modifier", '-', $path); 
     722    $path = preg_replace("/$betweenWords+/$modifier", $betweenWords, $path); 
    719723    // Leading and trailing dashes are silly. This has the effect of trim() 
    720724    // among other sensible things 
  • plugins/apostrophePlugin/trunk/lib/toolkit/aImageConverter.class.php

    r1917 r2134  
    258258  static private function scaleGd($fileIn, $fileOut, $scaleParameters = array(), $cropParameters = array(), $quality = 75) 
    259259  { 
     260     
    260261    // gd version for those who can't install netpbm, poor buggers 
    261262    // "handles" PDF by rendering a blank white image. We already superimpose a PDF icon, 
     
    295296    { 
    296297      $in = self::imagecreatefromany($fileIn); 
     298    } 
     299     
     300    if (!$in) 
     301    { 
     302      return false; 
    297303    } 
    298304     
     
    453459  // If the file does not have a valid header identifying it as one of these types, false is returned. 
    454460   
    455   static public function getInfo($file) 
    456   { 
     461  // If the 'format-only' option is true, only the format field is returned. This is much faster if the 
     462  // file is a PDF. 
     463   
     464  static public function getInfo($file, $options = array()) 
     465  { 
     466    $formatOnly = (isset($options['format-only']) && $options['format-only']); 
    457467    $result = array(); 
    458468    $in = fopen($file, "rb"); 
    459469    $data = fread($in, 4); 
    460470    fclose($in); 
     471     
     472     
    461473    if ($data === '%PDF') 
    462474    { 
    463       if (!aImageConverter::supportsInput('pdf')) 
     475      // format-only  
     476      if ((!aImageConverter::supportsInput('pdf')) || $formatOnly) 
    464477      { 
    465478        // All we can do is confirm the format and allow 
     
    528541      } 
    529542      $format = $formats[$data[2]]; 
     543      $result['format'] = $format; 
     544      if ($formatOnly) 
     545      { 
     546        return $result; 
     547      } 
    530548      $result['width'] = $data[0]; 
    531549      $result['height'] = $data[1]; 
    532       $result['format'] = $format; 
    533550      return $result; 
    534551    } 
  • plugins/apostrophePlugin/trunk/lib/toolkit/aMediaRouting.php

    r1917 r2134  
    102102      ))); 
    103103       
    104       $r->prependRoute('a_media_upload_images', new aRoute('/uploadImages', array( 
     104      $r->prependRoute('a_media_upload', new aRoute('/upload', array( 
    105105        'module' => 'aMedia', 
    106         'action' => 'uploadImages' 
     106        'action' => 'upload' 
    107107      ))); 
    108108       
    109       $r->prependRoute('a_media_edit_images', new aRoute('/editImages', array( 
     109      $r->prependRoute('a_media_edit_multiple', new aRoute('/editMultiple', array( 
    110110        'module' => 'aMedia', 
    111         'action' => 'editImages' 
     111        'action' => 'editMultiple' 
     112      ))); 
     113 
     114      $r->prependRoute('a_media_edit', new aRoute('/edit', array( 
     115        'module' => 'aMedia', 
     116        'action' => 'edit' 
    112117      ))); 
    113118       
  • plugins/apostrophePlugin/trunk/lib/toolkit/aMediaTools.php

    r2022 r2134  
    106106    return self::getAttribute('type'); 
    107107  } 
     108   
     109  static public function getBestTypeLabel() 
     110  { 
     111    $type = aMediaTools::getType(); 
     112    if ($type) 
     113    { 
     114      $typeInfo = aMediaTools::getTypeInfo($type); 
     115      return $typeInfo['label']; 
     116    } 
     117    else 
     118    { 
     119      return 'Media'; 
     120    } 
     121  } 
    108122 
    109123  static public function userHasUploadPrivilege() 
     
    129143    return sfContext::getInstance()->getUser(); 
    130144  } 
    131   // Symfony 1.2 has no namespaces for attributes for some reason 
     145 
    132146  static public function getAttribute($attribute, $default = null) 
    133147  { 
     148    // If you are logged out, you should have no attributes, as 
     149    // all attributes used in the media engine relate to selection 
     150    if (!self::getUser()->isAuthenticated()) 
     151    { 
     152      return $default; 
     153    } 
    134154    $attribute = "aMedia-$attribute"; 
    135155    return self::getUser()->getAttribute($attribute, $default, 'apostrophe_media'); 
    136156  } 
     157   
    137158  static public function setAttribute($attribute, $value = null) 
    138159  { 
     
    140161    self::getUser()->setAttribute($attribute, $value, 'apostrophe_media'); 
    141162  } 
     163   
    142164  static public function removeAttributes() 
    143165  { 
     
    145167    $user->getAttributeHolder()->removeNamespace('apostrophe_media'); 
    146168  } 
     169   
    147170  // This is a good convention for plugin options IMHO 
    148171  static private $options = array( 
     
    174197    'apipublic' => false, 
    175198    'embed_codes' => false, 
    176     'apikeys' => array() 
    177   ); 
     199    'apikeys' => array(), 
     200 
     201    // All mime types that are acceptable for upload to the media repository, 
     202    // keyed by the file extensions we save them under (regardless of the original name) 
     203     
     204    'mime_types' => array( 
     205      "gif" => "image/gif", 
     206      "png" => "image/png", 
     207      "jpg" => "image/jpeg", 
     208      "pdf" => "application/pdf", 
     209      "mp3" => "mpeg/mp3", 
     210      'xls' => 'application/vnd.ms-excel', 
     211      'ppt' => 'application/vnd.ms-powerpoint', 
     212      'doc' => 'application/msword', 
     213      'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 
     214      'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 
     215      'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 
     216      'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 
     217      'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 
     218      'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 
     219      'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 
     220      'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 
     221      'txt' => 'text/plain', 
     222      'rtf' => 'text/rtf' 
     223      ), 
     224       
     225    // You can override these to add more types to the system. These are the  
     226    // major types one can filter by in the media repository. Adding something here 
     227    // doesn't necessarily mean browsers can display it or our slots are designed 
     228    // to render it, in particular don't add new audio formats to 'audio' without 
     229    // overriding our audio slots to play them (and keep in mind the browser probably 
     230    // knows nothing about them) 
     231     
     232    // Video has no extensions because we don't provide processing of video uploads, 
     233    // which are in a dizzying array of formats most browsers won't play. That's why 
     234    // YouTube exists. Videos are brought into the system via "Embed Media," not "Upload Media" 
     235 
     236    // Typically the only section you'll override here is 'file'. You can add more 
     237    // accepted extensions and/or break it up into 'Office' and 'Other' etc 
     238 
     239    // Also see 'getDownloadable' and 'getEmbeddable' in aMediaItem 
     240 
     241    'types' => array( 
     242      'image' => array('label' => 'Image', 'extensions' => array('gif', 'png', 'jpg'), 'embeddable' => false), 
     243      'pdf' => array('label' => 'PDF', 'extensions' => array('pdf'), 'embeddable' => false), 
     244      'audio' => array('label' => 'Audio', 'extensions' => array('mp3'), 'embeddable' => false), 
     245      'video' => array('label' => 'Video', 'extensions' => array(), 'embeddable' => true), 
     246       
     247      // A long whitelist of file formats that are usually benign and useful. 
     248      // No .exe, no .zip. You can add them via app.yml if you really want them. 
     249      // We list only the non-macro-enabled Microsoft extensions in an effort to 
     250      // honor their good-faith attempt to label more dangerous files 
     251       
     252      'office' => array('label' => 'Office', 'extensions' => array('txt', 'rtf', 'csv', 'doc', 'docx', 'xls', 'xlsx', 'xlsb', 'ppt', 'pptx', 'ppsx'), 'embeddable' => false))); 
     253   
    178254  static public function getOption($name) 
    179255  { 
     
    188264      throw new Exception("Unknown option in apostrophePlugin: $name"); 
    189265    } 
     266  } 
     267   
     268  static public function getTypeInfo($name) 
     269  { 
     270    $types = aMediaTools::getOption('types'); 
     271    return $types[$name]; 
     272  } 
     273 
     274  // Returns an array of type infos, just the one if you specify a type, all if you don't. 
     275  // Handy when filtering 
     276  static public function getTypeInfos($type = null) 
     277  { 
     278    $types = aMediaTools::getOption('types'); 
     279    if (is_null($type)) 
     280    { 
     281      return $types; 
     282    } 
     283    return array($types[$type]); 
    190284  } 
    191285   
  • plugins/apostrophePlugin/trunk/lib/validator/aValidatorFilePersistent.class.php

    r1957 r2134  
    1717class aValidatorFilePersistent extends sfValidatorFile 
    1818{ 
     19  // Make the original name available to guessers. It's a nice thought to 
     20  // avoid this but with Microsoft Office formats there are no reliable 
     21  // magic numbers, and those that do exist can be misleading because 
     22  // Word can contain Excel and vice versa 
     23  protected $originalName; 
    1924   
    2025  protected function configure($options = array(), $messages = array()) 
     
    3136      array_unshift($mimeTypeGuessers, array($this, 'guessFromImageconverter')); 
    3237      array_unshift($mimeTypeGuessers, array($this, 'guessFromID3')); 
     38      array_unshift($mimeTypeGuessers, array($this, 'guessRTF')); 
    3339      $this->setOption('mime_type_guessers', $mimeTypeGuessers); 
    3440    } 
     
    123129      $cvalue = $value['newfile']; 
    124130    } 
    125     // This will throw an exception if there is a validation error. 
    126     // That's a good thing: we don't want to save it for reuse 
    127     // in that situation. 
     131    $this->originalName = $cvalue['name']; 
    128132    try 
    129133    { 
     
    155159        $data['newfile'] = true; 
    156160        $data['tmp_name'] = $filePath; 
     161         
     162        // It's useful to know the mime type and true extension for  
     163        // supplying previews and icons 
     164        $extensionsByMimeType = array_flip(aMediaTools::getOption('mime_types')); 
     165        $data['mime_type'] = $this->getMimeType($filePath, $cvalue['type']); 
     166        if (isset($extensionsByMimeType[$data['mime_type']])) 
     167        { 
     168          $data['extension'] = $extensionsByMimeType[$data['mime_type']]; 
     169        } 
     170         
    157171        self::putFileInfo($persistid, $data); 
    158172      } 
     
    197211      $persistid = $value['persistid']; 
    198212      $info = self::getFileInfo($persistid); 
     213      // Only web images are reasonable for preview. We could do 
     214      // PDFs but in practice it's very slow, slower than you 
     215      // want to wait for when annotating; it's worth it later 
     216      // for display in the media repository 
     217      return $info['tmp_name'] && getimagesize($info['tmp_name']); 
     218    } 
     219    return false; 
     220  } 
     221 
     222  static public function alreadyPersisting($value) 
     223  { 
     224    if (isset($value['persistid'])) 
     225    { 
     226      $persistid = $value['persistid']; 
     227      $info = self::getFileInfo($persistid); 
     228      // Only web images are reasonable for preview. We could do 
     229      // PDFs but in practice it's very slow, slower than you 
     230      // want to wait for when annotating; it's worth it later 
     231      // for display in the media repository 
    199232      return !!$info['tmp_name']; 
    200233    } 
    201234    return false; 
    202235  } 
     236 
    203237   
    204238  static public function getFileInfo($persistid) 
     
    217251    else 
    218252    { 
    219       return false; 
     253      return false;  
    220254    } 
    221255  } 
     
    273307    return 'audio/mpeg'; 
    274308  } 
     309 
     310  protected function guessRTF($file) 
     311  { 
     312    $in = fopen($file, 'rb'); 
     313    $magic = fread($in, 5); 
     314    fclose($in); 
     315    if ($magic !== '{\\rtf') 
     316    { 
     317      return null; 
     318    } 
     319    return 'text/rtf'; 
     320  } 
     321   
     322  protected function guessMicrosoft($file) 
     323  { 
     324    // We look at the original name to get the rest. 
     325    // Sorry, but there are no reliable magic numbers 
     326    // that don't sometimes mislead for Microsoft Office files. 
     327    $in = fopen($file, "rb"); 
     328    $data = fread($in, 3); 
     329    fclose($in); 
     330    $maybeMicrosoft = false; 
     331    // Magic numbers: old Microsoft container and new zip-based Microsoft container 
     332    if (($data === sprintf("%c%c%c", 0xD0, 0xCF, 0x11)) || ($data === sprintf("%c%c%c", 0x50, 0x4B, 0x03))) 
     333    { 
     334      $maybeMicrosoft = true; 
     335    } 
     336    if (!$maybeMicrosoft) 
     337    { 
     338      return null; 
     339    } 
     340    $ms = array( 
     341      'xls' => 'application/vnd.ms-excel', 
     342      'ppt' => 'application/vnd.ms-powerpoint', 
     343      'doc' => 'application/msword', 
     344      'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 
     345      'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 
     346      'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 
     347      'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 
     348      'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 
     349      'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 
     350      'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 
     351      'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' 
     352    ); 
     353    if (preg_match('/\.(\w+)$/', $this->originalName, $matches)) 
     354    { 
     355      $extension = $matches[1]; 
     356      if (isset($ms[$extension])) 
     357      { 
     358        return $ms[$extension]; 
     359      } 
     360    } 
     361    return null; 
     362  } 
     363   
     364  protected function getMimeType($file, $fallback) 
     365  { 
     366    // The microsoft guesser needs access to the original filename. 
     367    // For reasons I'm not sure of, it doesn't work as a dynamic method 
     368    // with call_user_func. 
     369    $match = $this->guessMicrosoft($file); 
     370    if (!is_null($match)) 
     371    { 
     372      return $match; 
     373    } 
     374 
     375    return parent::getMimeType($file, $fallback); 
     376  } 
    275377} 
  • plugins/apostrophePlugin/trunk/lib/widget/aWidgetFormInputFilePersistent.class.php

    r431 r2134  
    1414class aWidgetFormInputFilePersistent extends sfWidgetForm 
    1515{ 
     16   
    1617  /** 
    1718   * @param array $options     An array of options 
     
    5152  public function render($name, $value = null, $attributes = array(), $errors = array()) 
    5253  { 
    53     $exists = false; 
    54     if (isset($value['persistid']) && strlen($value['persistid'])) 
    55     { 
    56       $persistid = $value['persistid']; 
    57       $info = aValidatorFilePersistent::getFileInfo($persistid); 
    58       if ($info) 
    59       { 
    60         $exists = true; 
    61       } 
    62     } 
    63     else 
    64     { 
    65       // One implementation, not two (to inevitably drift apart) 
    66       $persistid = aGuid::generate(); 
    67     } 
     54    list($exists, $persistid, $extension) = $this->getExistsPersistidAndExtension($value); 
    6855    $result = ''; 
    69     // hasOption just verifies that the option is valid, it doesn't check what, 
    70     // if anything, was passed. Thanks to Lucjan Wilczewski  
    7156    $preview = $this->hasOption('image-preview') ? $this->getOption('image-preview') : false; 
    72     $defaultPreview = $this->hasOption('default-preview') ? $this->getOption('default-preview') : false; 
     57     
    7358    if ($exists) 
    7459    { 
    75       $defaultPreview = false; 
    76     } 
    77     if ($exists || $defaultPreview) 
    78     { 
    79       if ($preview) 
    80       { 
    81         // Note change of key 
    82         $urlStem = sfConfig::get('app_aPersistentFileUpload_preview_url', '/uploads/uploaded_image_preview'); 
    83         // This is the corresponding directory path. You have to override one 
    84         // if you override the other. You override this one by setting 
    85         // app_aToolkit_upload_uploaded_image_preview_dir 
    86         $dir = aFiles::getUploadFolder("uploaded_image_preview"); 
    87         // While we're here age off stale previews 
    88         aValidatorFilePersistent::removeOldFiles($dir); 
    89         $imagePreview = $this->getOption('image-preview'); 
    90         if ($exists) 
    91         { 
    92           $source = $info['tmp_name']; 
    93         } 
    94         else 
    95         { 
    96           $source = $defaultPreview; 
    97         } 
    98         $info = aImageConverter::getInfo($source); 
    99         if ($info) 
    100         { 
    101           $iwidth = $info['width']; 
    102           $height = $info['height']; 
    103           // This is safe - based on sniffed file contents and not a user supplied extension 
    104           $format = $info['format']; 
    105           list($iwidth, $iheight) = getimagesize($source); 
    106           $dimensions = aDimensions::constrain($iwidth, $iheight, $format, $imagePreview); 
    107           // A simple filename reveals less 
    108           $imagename = "$persistid.$format"; 
    109           $url = "$urlStem/$imagename"; 
    110           $output = "$dir/$imagename"; 
    111           if ((isset($info['newfile']) && $info['newfile']) || (!file_exists($output))) 
    112           { 
    113             if ($imagePreview['resizeType'] === 'c') 
    114             { 
    115               $method = 'cropOriginal'; 
    116             } 
    117             else 
    118             { 
    119               $method = 'scaleToFit'; 
    120             } 
    121             sfContext::getInstance()->getLogger()->info("YY calling converter method $method width " . $dimensions['width'] . ' height ' . $dimensions['height']); 
    122             aImageConverter::$method( 
    123               $source, 
    124               $output, 
    125               $dimensions['width'], 
    126               $dimensions['height']); 
    127             sfContext::getInstance()->getLogger()->info("YY after converter"); 
    128           } 
    129         } 
    130         if (isset($imagePreview['markup'])) 
    131         { 
    132           $markup = $imagePreview['markup']; 
    133         } 
    134         else 
    135         { 
    136           $markup = '<img src="%s" />'; 
    137         } 
    138         $result .= sprintf($markup, $url); 
    139       } 
    14060      $result .= $this->getOption('existing-html'); 
    14161    } 
     62     
     63    if ($preview) 
     64    { 
     65      if (isset($imagePreview['markup'])) 
     66      { 
     67        $markup = $imagePreview['markup']; 
     68      } 
     69      else 
     70      { 
     71        $markup = '<img src="%s" />'; 
     72      } 
     73      $previewUrl = $this->getPreviewUrl($value, $preview); 
     74      if ($previewUrl !== false) 
     75      { 
     76        $result .= sprintf($markup, $previewUrl); 
     77      } 
     78    } 
     79 
    14280    return $result . 
    14381      $this->renderTag('input', 
     
    15391          'value' => $persistid)); 
    15492  } 
    155  
     93   
     94  public function getPreviewUrl($value, $imagePreview = array()) 
     95  { 
     96    list($exists, $persistid, $extension) = $this->getExistsPersistidAndExtension($value); 
     97     
     98    // hasOption just verifies that the option is valid, it doesn't check what, 
     99    // if anything, was passed. Thanks to Lucjan Wilczewski  
     100    $defaultPreview = $this->hasOption('default-preview') ? $this->getOption('default-preview') : false; 
     101    if ($exists) 
     102    { 
     103      $defaultPreview = false; 
     104    } 
     105    if ($exists || $defaultPreview) 
     106    { 
     107      // Note change of key 
     108      $urlStem = sfConfig::get('app_aPersistentFileUpload_preview_url', '/uploads/uploaded_image_preview'); 
     109      // This is the corresponding directory path. You have to override one 
     110      // if you override the other. You override this one by setting 
     111      // app_aToolkit_upload_uploaded_image_preview_dir 
     112      $dir = aFiles::getUploadFolder("uploaded_image_preview"); 
     113      // While we're here age off stale previews 
     114      aValidatorFilePersistent::removeOldFiles($dir); 
     115      if ($exists) 
     116      { 
     117        $info = aValidatorFilePersistent::getFileInfo($persistid); 
     118        $source = $info['tmp_name']; 
     119      } 
     120      else 
     121      { 
     122        $source = $defaultPreview; 
     123      } 
     124      $info = aImageConverter::getInfo($source, array('format-only' => true)); 
     125      $previewable = false; 
     126      if ($info && in_array($info['format'], array('jpg', 'png', 'gif'))) 
     127      { 
     128        $previewable = true; 
     129        $info = aImageConverter::getInfo($source); 
     130      } 
     131      if ($previewable) 
     132      { 
     133        $iwidth = $info['width']; 
     134        $iheight = $info['height']; 
     135        // This is safe - based on sniffed file contents and not a user supplied extension 
     136        $format = $info['format']; 
     137        $dimensions = aDimensions::constrain($iwidth, $iheight, $format, $imagePreview); 
     138        // A simple filename reveals less 
     139        $imagename = "$persistid.$format"; 
     140        $url = "$urlStem/$imagename"; 
     141        $output = "$dir/$imagename"; 
     142        if ((isset($info['newfile']) && $info['newfile']) || (!file_exists($output))) 
     143        { 
     144          if ($imagePreview['resizeType'] === 'c') 
     145          { 
     146            $method = 'cropOriginal'; 
     147          } 
     148          else 
     149          { 
     150            $method = 'scaleToFit'; 
     151          } 
     152          sfContext::getInstance()->getLogger()->info("YY calling converter method $method width " . $dimensions['width'] . ' height ' . $dimensions['height']); 
     153          aImageConverter::$method( 
     154            $source, 
     155            $output, 
     156            $dimensions['width'], 
     157            $dimensions['height']); 
     158          sfContext::getInstance()->getLogger()->info("YY after converter"); 
     159        } 
     160      } 
     161      else 
     162      { 
     163        if ($extension !== false) 
     164        { 
     165          $url = '/apostrophePlugin/images/a-icons-format-' . $extension . '.png'; 
     166        } 
     167      } 
     168      return $url; 
     169    } 
     170    return false; 
     171  } 
     172   
     173  protected function getExistsPersistidAndExtension($value) 
     174  { 
     175    // TODO: should cache this 
     176    $exists = false; 
     177    $extension = false; 
     178    if (isset($value['persistid']) && strlen($value['persistid'])) 
     179    { 
     180      $persistid = $value['persistid']; 
     181      $info = aValidatorFilePersistent::getFileInfo($persistid); 
     182      if ($info) 
     183      { 
     184        $exists = true; 
     185        if (isset($info['extension'])) 
     186        { 
     187          $extension = $info['extension']; 
     188        } 
     189      } 
     190    } 
     191    else 
     192    { 
     193      // One implementation, not two (to inevitably drift apart) 
     194      $persistid = aGuid::generate(); 
     195    } 
     196     
     197    if (!$exists) 
     198    { 
     199      $defaultPreview = $this->hasOption('default-preview') ? $this->getOption('default-preview') : false; 
     200      if ($defaultPreview) 
     201      { 
     202        $extension = pathinfo($defaultPreview, PATHINFO_EXTENSION); 
     203      } 
     204    } 
     205     
     206    return array($exists, $persistid, $extension); 
     207  } 
    156208} 
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/_browser.php

    r1957 r2134  
    4444                  <div class="a-filter-options type"> 
    4545                                <?php $type = isset($type) ? $type : '' ?> 
    46                                 <div class="a-filter-option"> 
    47                                         <?php echo link_to(__('Image', null, 'apostrophe'), aUrl::addParams($current, array('type' => ($type == 'image') ? '' : 'image')), array('class' => ($type=='image') ? 'selected' : '', )) ?> 
    48                                 </div> 
    49                                 <div class="a-filter-option"> 
    50                                         <?php echo link_to(__('Video', null, 'apostrophe'), aUrl::addParams($current, array('type' => ($type == 'video') ? '' : 'video')), array('class' => ($type=='video') ? 'selected' : '', )) ?>                            
    51                                 </div> 
    52                                 <div class="a-filter-option"> 
    53                                         <?php echo link_to(__('PDF', null, 'apostrophe'), aUrl::addParams($current, array('type' => ($type == 'pdf') ? '' : 'pdf')), array('class' => ($type=='pdf') ? 'selected' : '', )) ?> 
    54                                 </div> 
     46                    <?php $typesInfo = aMediaTools::getOption('types') ?> 
     47                                <?php foreach ($typesInfo as $typeName => $typeInfo): ?> 
     48                                <div class="a-filter-option"> 
     49                                        <?php echo link_to(a_($typeInfo['label']), aUrl::addParams($current, array('type' => ($typeName == $type) ? '' : $typeName)), array('class' => ($typeName == $type) ? 'selected' : '', )) ?> 
     50                                </div> 
     51                        <?php endforeach ?> 
    5552                  </div> 
    5653                </div> 
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/_edit.php

    r2058 r2134  
    88  $n = isset($n) ? $sf_data->getRaw('n') : null; 
    99?> 
    10 <?php use_helper('I18N') ?> 
     10<?php use_helper('a') ?> 
    1111   
    1212<?php if (!isset($n)): ?> <?php $n = 0 ?> <?php endif ?> 
    1313 
     14<?php // if there is an $item then we're editing one exiting media item, if not ?> 
     15<?php // we're part of an annotation of potentially several new items in a bigger form ?> 
     16 
    1417<?php if (!$item): ?>    
    15 <li class="a-media-item <?php echo ($n%2) ? "odd" : "even" ?>"> 
     18<li class="a-media-item <?php echo ($n%2) ? "odd" : "even" ?>" id="a-media-item-<?php echo $i ?>"> 
    1619        <div class="a-media-item-edit-form"> 
    1720<?php endif ?> 
     
    1922<?php if ($item): ?> 
    2023<form method="POST" id="a-media-edit-form" enctype="multipart/form-data"  
    21   action="<?php echo url_for(aUrl::addParams("aMedia/editImage", 
     24  action="<?php echo url_for(aUrl::addParams("aMedia/edit", 
    2225    array("slug" => $item->getSlug())))?>"> 
    2326<?php endif ?> 
    24  
    25                 <?php $previewAvailable = aValidatorFilePersistent::previewAvailable($form['file']->getValue()) ?> 
    26                 <?php if ($previewAvailable || $item): ?> 
    27  
    28                 <div class="a-form-row image"> 
    29                 <?php if (0): ?> 
    30                   <?php // Maybe Rick doesn't want this... ?> 
    31                   <?php echo $form['file']->renderLabel() ?> 
    32                 <?php endif ?> 
    33                 <?php // But we must have this ?> 
    34                 <?php echo $form['file']->renderError() ?> 
    35                 <?php echo $form['file']->render() ?> 
    36                 <?php else: ?> 
    37                 <div class="a-form-row newfile"> 
    38                 <?php echo $form['file']->renderRow() ?> 
    39                 </div> 
    40                 <?php endif ?> 
    41                 </div> 
     27     
     28    <?php // This is how we get the preview (which might be a rendering or a large icon, depending on the type) ?> 
     29    <?php // outside of the widget. Jamming it into the widget made templating weird ?> 
     30    <div class="a-form-row preview"> 
     31      <?php echo image_tag($form['file']->getWidget()->getPreviewUrl($form['file']->getValue(), aMediaTools::getOption('gallery_constraints'))) ?> 
     32      <?php if (!$item): ?> 
     33        <a class="a-media-remove-file" href="#">x</a> 
     34        <?php a_js_call('apostrophe.mediaEnableRemoveButton(?)', $i) ?> 
     35      <?php endif ?> 
     36    </div> 
     37     
     38    <?php // The label says 'Replace File' now, see BaseaMediaEditForm ?> 
     39                <div class="a-form-row file"> 
     40      <?php echo $form['file']->renderLabel() ?> 
     41      <?php echo $form['file']->renderError() ?> 
     42      <?php echo $form['file']->render() ?> 
     43    </div> 
    4244 
    4345                <div class="a-form-row title"> 
     
    4951                </div> 
    5052 
    51                 <?php echo $form['id']->render() ?> 
    5253                <div class="a-form-row description"> 
    5354                        <?php echo $form['description']->renderLabel() ?> 
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/_editLinks.php

    r1957 r2134  
    77        <ul class="a-ui a-controls"> 
    88 
    9     <?php if ($mediaItem->getType() === 'video'): ?> 
    10       <li><?php echo link_to(__("Edit", null, 'apostrophe'), "aMedia/editVideo", array("query_string" => http_build_query(array("slug" => $mediaItem->getSlug())), "class" => "a-btn icon a-edit")) ?></li> 
    11     <?php elseif ($mediaItem->getType() === 'pdf'): ?> 
    12       <li><?php echo link_to(__("Edit", null, 'apostrophe'), "aMedia/editPdf", array("query_string" => http_build_query(array("slug" => $mediaItem->getSlug())), "class" => "a-btn icon a-edit")) ?></li> 
    13     <?php else: ?> 
    14       <li><?php echo link_to(__("Edit", null, 'apostrophe'), "aMedia/editImage", array("query_string" => http_build_query(array("slug" => $mediaItem->getSlug())), "class" => "a-btn icon a-edit")) ?></li> 
    15     <?php endif ?> 
    16          
    17                 <?php if ($mediaItem->getType() !== 'video' && $sf_params->get('action') == 'show'): ?> 
     9    <li><?php echo link_to(a_("Edit"), "aMedia/edit", array("query_string" => http_build_query(array("slug" => $mediaItem->getSlug())), "class" => "a-btn icon a-edit")) ?></li> 
     10 
     11                <?php if ($mediaItem->getDownloadable() && $sf_params->get('action') == 'show'): ?> 
    1812          <li class="a-media-download-original"> 
    1913             <?php // download link ?> 
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/_mediaItem.php

    r1917 r2134  
    4444</li> 
    4545 
    46 <?php // Stored as HTML ?> 
    4746<li class="a-media-item-title"> 
    4847        <h3> 
     
    5251</li> 
    5352 
     53<?php // John: you could use $mediaItem->format to choose an icon here. Make sure ?> 
     54<?php // you have a default icon if it's not on your list of awesome icons ?> 
    5455<li class="a-media-item-description"><?php echo $mediaItem->getDescription() ?></li> 
    5556<?php if ($mediaItem->getWidth()): ?> 
     
    6162<li class="a-media-item-tags a-media-item-meta"><?php echo __('<span>Tags:</span> %tags%', array('%tags%' => get_partial('aMedia/showTags', array('tags' => $mediaItem->getTags()))), 'apostrophe') ?></li> 
    6263 
    63 <?php if ($mediaItem->getType() === 'pdf'): ?> 
     64<?php if ($mediaItem->getDownloadable()): ?> 
    6465  <li class="a-media-item-link a-media-item-meta"> 
    6566                <?php echo __('<span>URL:</span> %urlfield%', array('%urlfield%' =>  
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/editMultipleSuccess.php

    r2058 r2134  
    55  $form = isset($form) ? $sf_data->getRaw('form') : null; 
    66?> 
    7 <?php use_helper('I18N', 'jQuery', 'a') ?> 
     7<?php use_helper('a') ?> 
    88<?php slot('body_class') ?>a-media<?php end_slot() ?> 
    99 
     
    1313 
    1414        <div class="a-media-toolbar"> 
    15                 <h3><?php echo __('Annotate Images', null, 'apostrophe') ?></h3> 
     15                <h3><?php echo __('Annotate ' . aMediaTools::getBestTypeLabel(), null, 'apostrophe') ?></h3> 
    1616        </div> 
    1717 
    1818        <div class="a-media-library">                            
    19                 <form method="POST" action="<?php echo url_for("aMedia/editImages") ?>" enctype="multipart/form-data" id="a-media-edit-form"> 
     19                <form method="POST" action="<?php echo url_for("aMedia/editMultiple") ?>" enctype="multipart/form-data" id="a-media-edit-form"> 
    2020                <?php echo $form->renderHiddenFields() ?> 
    2121   
    22                 <input type="hidden" name="active" value="<?php echo implode(",", $active) ?>" /> 
    23  
    2422                <?php $n = 0 ?> 
    2523 
     
    3028                            <?php // (they get nested when embedded forms are present), but ?> 
    3129                            <?php // it supports the same methods as a form for rendering purposes ?> 
    32                             <?php include_partial('aMedia/editImage',  
    33                                                                 array( 
    34                                                                         "item" => false,  
    35                                                 "firstPass" => $firstPass,  
    36                                                                         "form" => $form["item-$i"],  
    37                                                                         "n" => $n,  
    38                                                                         'i' => $i, 
    39                                                                         'itemFormScripts' => 'false', 
    40                                                                         )) ?> 
     30                             
     31                            <?php // Please do not remove this div, I need it to implement the remove button ?> 
     32                            <div id="a-media-editor-<?php echo $i ?>"> 
     33                            <?php include_partial('aMedia/edit',  
     34                                                                array( 
     35                                                                        "item" => false,  
     36                                                "firstPass" => $firstPass,  
     37                                                                        "form" => $form["item-$i"],  
     38                                                                        "n" => $n,  
     39                                                                        'i' => $i, 
     40                                                                        'itemFormScripts' => 'false', 
     41                                                                        )) ?> 
     42                                        </div> 
    4143                                        <?php $n++ ?> 
    4244                          <?php endif ?> 
     
    4850                <?php //We should wrap this with logic to say 'photo' if only one object has been uploaded ?> 
    4951                <ul class="a-ui a-controls"> 
    50                         <li><input type="submit" name="submit" value="<?php echo __('Save Images', null, 'apostrophe') ?>" class="a-btn a-submit" /></li> 
    51                         <li><?php echo link_to(__("Cancel", null, 'apostrophe'), "aMedia/resume", array("class"=>"a-btn icon a-cancel")) ?></li> 
     52                        <li><input type="submit" name="submit" value="<?php echo a_('Save ' . aMediaTools::getBestTypeLabel()) ?>" class="a-btn a-submit" /></li> 
     53                        <?php // I use a-cancel and a-media-edit-multiple to find and trigger this cancel button in JS ?> 
     54                        <li><?php echo link_to(a_("Cancel"), "aMedia/resume", array("class"=>"a-btn icon a-media-edit-multiple-cancel")) ?></li> 
    5255                </ul> 
    5356                </form> 
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/editSuccess.php

    r1957 r2134  
    1717 
    1818        <div class="a-media-library">                    
    19         <?php include_partial('aMedia/editImage', array('item' => $item, 'firstPass' => false, 'form' => $form)) ?>              
     19        <?php include_partial('aMedia/edit', array('item' => $item, 'form' => $form)) ?>                 
    2020        </div> 
    21  
    2221</div> 
  • plugins/apostrophePlugin/trunk/modules/aMedia/templates/indexSuccess.php

    r1957 r2134  
    3838         
    3939        <?php if (aMediaTools::userHasUploadPrivilege()): ?> 
    40    <ul class="a-ui a-controls"> 
     40    <ul class="a-ui a-controls"> 
    4141 
    42      <?php if (!($selecting && $type && ($type !== 'image'))): ?> 
    43      <li><a href="<?php echo url_for("aMedia/uploadImages") ?>" class="a-btn icon big a-add"><?php echo __('Add Images', null, 'apostrophe') ?></a></li> 
    44      <?php endif ?> 
    45  
    46      <?php if (!($selecting && $type && ($type !== 'video'))): ?> 
    47      <li><a href="<?php echo url_for("aMedia/newVideo") ?>" class="a-btn icon big a-add"><?php echo __('Add Video', null, 'apostrophe') ?></a></li> 
    48      <?php endif ?> 
    49  
    50      <?php if (!($selecting && $type && ($type !== 'pdf'))): ?> 
    51      <li><a href="<?php echo url_for("aMedia/editPdf") ?>" class="a-btn icon big a-add"><?php echo __('Add PDF', null, 'apostrophe') ?></a></li> 
    52      <?php endif ?> 
     42      <?php $typeLabel = aMediaTools::getBestTypeLabel() ?> 
     43      <?php if ($uploadAllowed): ?> 
     44        <li><a href="<?php echo url_for("aMedia/upload") ?>" class="a-btn icon big a-add"><?php echo a_('Upload ' . $typeLabel) ?></a></li> 
     45      <?php endif ?> 
     46      <?php if ($embedAllowed): ?> 
     47        <li><a href="<?php echo url_for("aMedia/embed") ?>" class="a-btn icon big a-add"><?php echo a_('Embed ' . $typeLabel) ?></a></li> 
     48      <?php endif ?> 
    5349   </ul> 
    5450  <?php endif ?> 
  • plugins/apostrophePlugin/trunk/web/css/a.css

    • Property svn:mergeinfo changed (with no actual effect on merging)
  • plugins/apostrophePlugin/trunk/web/images

    • Property svn:mergeinfo changed (with no actual effect on merging)
  • plugins/apostrophePlugin/trunk/web/js/a.js

    r2124 r2134  
    367367        } 
    368368         
     369        this.mediaEnableRemoveButton = function(i) 
     370        { 
     371                var editor = $('#a-media-item-' + i); 
     372                editor.find('.a-media-remove-file').click(function() 
     373                { 
     374                        editor.remove(); 
     375                        if ($('.a-media-item').length == 0) 
     376                        { 
     377                                // This is a bit hacky 
     378                                document.location = $('.a-media-edit-multiple-cancel').attr('href'); 
     379                        } 
     380                        return false; 
     381                }); 
     382        } 
     383         
    369384        // console.log wrapper prevents JS errors if we leave an apostrophe.log call hanging out in our code someplace 
    370385        this.log = function(output)