source: extensions/BatchDownloader/include/BatchDownloader.class.php @ 16609

Last change on this file since 16609 was 16609, checked in by mistic100, 12 years ago

-add complete breadcrumb
-AJAX for remove photos (more faster)
-use try/catch for error handling

File size: 16.3 KB
RevLine 
[16379]1<?php
2defined('BATCH_DOWNLOAD_PATH') or die('Hacking attempt!');
3
4class BatchDownloader
5{
6  private $conf;
7  private $data;
8  private $images;
9 
10  /**
11   * __construct
12   * @param: mixed set id (##|'new')
13   * @param: array images
14   * @param: string set type ('album'|'tag'|'selection')
15   * @param: int set type id (for retrieving album infos for instance)
16   */
17  function __construct($set_id, $images=array(), $type=null, $type_id=null)
18  {
19    global $user, $conf;
20   
21    $this->conf = $conf['batch_download'];
22    $this->data = array(
[16400]23      'set_id' => 0,
[16379]24      'user_id' => $user['id'],
[16400]25      'date_creation' => '0000-00-00 00:00:00',
[16379]26      'type' => null,
27      'type_id' => null,
28      'nb_zip' => 0,
29      'last_zip' => 0,
30      'nb_images' => 0,
[16400]31      'total_size' => 0,
[16379]32      'status' => 'new',
33      );
34    $this->images = array();
35   
36    // load specific set
37    if (preg_match('#^[0-9]+$#', $set_id))
38    {
39      $query = '
40SELECT
[16592]41    user_id,
[16400]42    date_creation,
[16379]43    type,
44    type_id,
45    nb_zip,
46    last_zip,
47    nb_images,
[16400]48    total_size,
[16379]49    status
50  FROM '.BATCH_DOWNLOAD_TSETS.'
51  WHERE
[16400]52    id = '.$set_id.'
53    '.(!is_admin() ? 'AND user_id = '.$this->data['user_id'] : null).'
[16379]54;';
55      $result = pwg_query($query);
56     
57      if (pwg_db_num_rows($result))
58      {
59        $this->data['set_id'] = $set_id;
60        list(
[16592]61          $this->data['user_id'],
[16400]62          $this->data['date_creation'],
[16379]63          $this->data['type'], 
64          $this->data['type_id'], 
65          $this->data['nb_zip'], 
66          $this->data['last_zip'], 
67          $this->data['nb_images'], 
[16400]68          $this->data['total_size'],
[16379]69          $this->data['status']
70          ) = pwg_db_fetch_row($result);
71       
[16592]72        // make sur all pictures of the set exist
[16379]73        $query = '
74DELETE FROM '.BATCH_DOWNLOAD_TIMAGES.'
75  WHERE image_id NOT IN (
76    SELECT id FROM '.IMAGES_TABLE.'
77    )
78;';
79        pwg_query($query);
80     
81        $query = '
82SELECT
83    image_id,
84    zip
85  FROM '.BATCH_DOWNLOAD_TIMAGES.'
86  WHERE set_id = '.$this->data['set_id'].'
87;';
88        $this->images = simple_hash_from_query($query, 'image_id', 'zip');
89       
[16400]90        if ( $this->data['status'] != 'done' and count($this->images) != $this->data['nb_images'] )
[16379]91        {
92          $this->updateParam('nb_images', count($this->images));
93        }
94      }
95      else
96      {
[16609]97        throw new Exception(l10n('Invalid dowload set'));
[16379]98      }
99    }
100    // create a new set
101    else if ($set_id == 'new')
102    {
103      $this->data['type'] = $type;
104      $this->data['type_id'] = $type_id;
105     
106      $query = '
107INSERT INTO '.BATCH_DOWNLOAD_TSETS.'(
108    user_id,
109    date_creation,
110    type,
111    type_id,
112    nb_zip,
113    last_zip,
114    nb_images,
[16400]115    total_size,
[16379]116    status
117  )
118  VALUES(
119    '.$this->data['user_id'].',
120    NOW(),
121    "'.$this->data['type'].'",
122    "'.$this->data['type_id'].'",
123    0,
124    0,
125    0,
[16400]126    0,
[16379]127    "new"
128  )
129;';
130      pwg_query($query);
131      $this->data['set_id'] = pwg_db_insert_id();
132     
[16400]133      $date = pwg_query('SELECT FROM_UNIXTIME(NOW());');
134      list($this->data['date_creation']) = pwg_db_fetch_row($date);
135     
[16379]136      if (!empty($images))
137      {
138        $this->addImages($images);
139      }
140    }
141    else
142    {
143      trigger_error('BatchDownloader::__construct, invalid input parameter', E_USER_ERROR);
144    }
145  }
146 
147  /**
148   * updateParam
149   * @param: string param name
150   * @param: mixed param value
151   */
152  function updateParam($name, $value)
153  {
154    $this->data[$name] = $value;
155    pwg_query('UPDATE '.BATCH_DOWNLOAD_TSETS.' SET '.$name.' = "'.$value.'" WHERE id = '.$this->data['set_id'].';');
156  }
157 
158  /**
159   * getParam
160   * @param: string param name
161   * @return: mixed param value
162   */
163  function getParam($name)
164  {
165    return $this->data[$name];
166  }
167 
168  /**
169   * getImages
170   * @return: array
171   */
172  function getImages()
173  {
174    return $this->images;
175  }
176 
177  /**
178   * isInSet
179   * @param: int image id
180   * @return: bool
181   */
182  function isInSet($image_id)
183  {
184    return array_key_exists($image_id, $this->images);
185  }
186 
187  /**
188   * removeImages
189   * @param: array image ids
190   */
191  function removeImages($image_ids)
192  {
193    if (empty($image_ids) or !is_array($image_ids)) return;
194   
195    foreach ($image_ids as $image_id)
196    {
197      unset($this->images[ $image_id ]);
198    }
199   
200    $query = '
201DELETE FROM '.BATCH_DOWNLOAD_TIMAGES.'
202  WHERE
203    set_id = '.$this->data['set_id'].'
204    AND image_id IN('.implode(',', $image_ids).')
205;';
206    pwg_query($query);
207   
208    $this->updateParam('nb_images', count($this->images));
209  }
210 
211  /**
212   * addImages
213   * @param: array image ids
214   */
215  function addImages($image_ids)
216  {
217    if (empty($image_ids) or !is_array($image_ids)) return;
218   
219    $image_ids = array_unique($image_ids);
220    $inserts = array();
221   
222    foreach ($image_ids as $image_id)
223    {
224      if ($this->isInSet($image_id)) continue;
225     
226      $this->images[ $image_id ] = 0;
227      array_push($inserts, array('set_id'=>$this->data['set_id'], 'image_id'=>$image_id, 'zip'=>0));
228    }
229   
230    mass_inserts(
231      BATCH_DOWNLOAD_TIMAGES,
232      array('set_id', 'image_id', 'zip'),
233      $inserts
234      );
235     
236    $this->updateParam('nb_images', count($this->images));
237  }
238 
239  /**
[16400]240   * clearImages
[16379]241   */
[16400]242  function clearImages()
[16379]243  {
244    $this->images = array();
245   
246    $query = '
247DELETE FROM '.BATCH_DOWNLOAD_TIMAGES.'
248  WHERE set_id = '.$this->data['set_id'].'
249;';
250    pwg_query($query);
251  }
252 
253  /**
254   * deleteLastArchive
255   */
256  function deleteLastArchive()
257  {
258    $zip_path = $this->getArchivePath();
259    if (file_exists($zip_path))
260    {
261      unlink($zip_path);
262    }
263  }
264 
265  /**
266   * createNextArchive
[16400]267   * @param: bool force all elements in one archive
268   * @return: string zip path or false
[16379]269   */
[16392]270  function createNextArchive($force_one_archive=false)
[16379]271  {
[16609]272    // set already downloaded (we should never be there !)
[16379]273    if ( $this->data['status'] == 'done' or $this->data['nb_images'] == 0 )
274    {
275      trigger_error('BatchDownloader::createNextArchive, the set is empty', E_USER_ERROR);
276    }
277   
278    // first zip
279    if ($this->data['last_zip'] == 0)
280    {
281      $this->updateParam('status', 'download');
282     
283      // limit number of elements
284      if ($this->data['nb_images'] > $this->conf['max_elements'])
285      {
286        $images_ids = array_slice(array_keys($this->images), 0, $this->conf['max_elements']);
[16400]287        $this->clearImages();
[16379]288        $this->addImages($images_ids);
289      }
290     
291      $this->getEstimatedArchiveNumber();
292     
[16400]293      $this->updateParam('date_creation', date('Y-m-d H:i:s'));
[16379]294    }
295   
296    // get next images of the set
297    $images_to_add = array();
298    foreach ($this->images as $image_id => $zip_id)
299    {
300      if ($zip_id != 0) continue; // here are already added images
301      array_push($images_to_add, $image_id);
302    }
303   
304    if (count($images_to_add))
305    {
306      $query = '
307SELECT
308    id,
309    name,
310    file,
311    path,
312    filesize
313  FROM '.IMAGES_TABLE.'
314  WHERE id IN ('.implode(',', $images_to_add).')
315;';
316      $images_to_add = hash_from_query($query, 'id');
317     
318      // open zip
319      $this->updateParam('last_zip', $this->data['last_zip']+1);
320      $zip_path = $this->getArchivePath();
321     
322      $zip = new ZipArchive;
323      if ($zip->open($zip_path, ZipArchive::CREATE) !== true)
324      {
325        trigger_error('BatchDownloader::createNextArchive, unable to open ZIP archive', E_USER_ERROR);
326      }
327     
328      // add images until size limit is reach, or all images are added
329      $images_added = array();
330      $total_size = 0;
331      foreach ($images_to_add as $row)
332      {
333        $zip->addFile(PHPWG_ROOT_PATH . $row['path'], $row['id'].'_'.$row['name'].'.'.get_extension($row['file']));
334       
335        array_push($images_added, $row['id']);
336        $this->images[ $row['id'] ] = $this->data['last_zip'];
337       
338        $total_size+= $row['filesize'];
[16392]339        if ($total_size >= $this->conf['max_size']*1024 and !$force_one_archive) break;
[16379]340      }
341     
[16400]342      $this->updateParam('total_size', $this->data['total_size'] + $total_size);
343     
[16379]344      // archive comment
345      global $conf;
346      $comment = 'Generated on '.date('r').' with PHP ZipArchive '.PHP_VERSION.' by Piwigo Advanced Downloader.';
347      $comment.= "\n".$conf['gallery_title'].' - '.get_absolute_root_url();
348      if (!empty($this->conf['archive_comment']))
349      {
350        $comment.= "\n\n".$this->conf['archive_comment'];
351      }
352      $zip->setArchiveComment($comment);
353     
354      $zip->close();
355     
356      // update database
357      $query = '
358UPDATE '.BATCH_DOWNLOAD_TIMAGES.'
359  SET zip = '.$this->data['last_zip'].'
360  WHERE
361    set_id = '.$this->data['set_id'].'
362    AND image_id IN('.implode(',', $images_added).')
363;';
364      pwg_query($query);
365     
366      // all images added ?
367      if (count($images_to_add) == count($images_added))
368      {
369        $this->updateParam('status', 'done');
370      }
371     
372      // over estimed
373      if ( $this->data['status'] == 'done' and $this->data['last_zip'] < $this->data['nb_zip'] )
374      {
375        $this->updateParam('nb_zip', $this->data['last_zip']);
376      }
377      // under estimed
378      else if ( $this->data['last_zip'] == $this->data['nb_zip'] and $this->data['status'] != 'done' )
379      {
380        $this->updateParam('nb_zip', $this->data['last_zip']+1);
381      }
382     
383      return $zip_path;
384    }
385    else
386    {
387      return false;
388    }
389  }
390 
391  /**
392   * getEstimatedTotalSize
393   * @return: int
394   */
395  function getEstimatedTotalSize()
396  {
[16400]397    if ($this->data['status'] == 'done') return $this->data['total_size'];
[16379]398    if ($this->data['nb_images'] == 0) return 0;
399   
400    $image_ids = array_slice(array_keys($this->images), 0, $this->conf['max_elements']);
401   
402    $query = '
403SELECT SUM(filesize) AS total
404  FROM '.IMAGES_TABLE.'
405  WHERE id IN ('.implode(',', $image_ids).')
406;';
407    list($total) = pwg_db_fetch_row(pwg_query($query));
408    return $total;
409  }
410 
411  /**
412   * getEstimatedArchiveNumber
413   * @return: int
414   */
415  function getEstimatedArchiveNumber()
416  {
417    $nb_zip = ceil( $this->getEstimatedTotalSize() / ($this->conf['max_size']*1024) );
418    $this->updateParam('nb_zip', $nb_zip);
419    return $nb_zip;
420  }
421 
422  /**
423   * getDownloadList
424   * @return: string html
425   */
426  function getDownloadList($url='')
427  {
[16392]428    $out = '';
[16400]429    for ($i=1; $i<=$this->data['nb_zip']; $i++)
[16379]430    {
[16400]431      $out.= '<li id="zip-'.$i.'">';
432     
[16598]433      if ($this->data['status'] == 'done' or $i < $this->data['last_zip']+1)
[16379]434      {
[16400]435        $out.= '<img src="'.BATCH_DOWNLOAD_PATH.'template/drive.png"> Archive #'.$i.' (already downloaded)';
[16379]436      }
[16400]437      else if ($i == $this->data['last_zip']+1)
438      {
439          $out.= '<a href="'.add_url_params($url, array('set_id'=>$this->data['set_id'],'zip'=>$i)).'" rel="nofollow" style="font-weight:bold;"' 
440            .($i!=1 ? 'onClick="return confirm(\'Starting download Archive #'.$i.' will destroy Archive #'.($i-1).', be sure you finish the download. Continue ?\');"' : null).
441            '><img src="'.BATCH_DOWNLOAD_PATH.'template/drive_go.png"> Archive #'.$i.' (ready)</a>';
442      }
443      else
444      {
445        $out.= '<img src="'.BATCH_DOWNLOAD_PATH.'template/drive.png"> Archive #'.$i.' (pending)';
446      }
447     
448      $out.= '</li>';
[16379]449    }
450   
451    return $out;
452  }
453 
454  /**
455   * getArchivePath
456   * @param: int archive number
457   * @return: string
458   */
459  function getArchivePath($i=null)
460  {
461    if (!file_exists(BATCH_DOWNLOAD_LOCAL . 'u-' .$this->data['user_id']. '/'))
462    {
463      mkdir(BATCH_DOWNLOAD_LOCAL . 'u-' .$this->data['user_id']. '/', 0755, true);
464    }
465   
466    if ($i === null) $i = $this->data['last_zip'];
467   
468    include_once(PHPWG_ROOT_PATH . 'admin/include/functions.php');
469   
470    return BATCH_DOWNLOAD_LOCAL .'u-'. $this->data['user_id'] .'/'.
471          (!empty($this->conf['archive_prefix']) ? $this->conf['archive_prefix'] .'_' : null).
472          get_username($this->data['user_id']) .'_'. 
473          $this->data['type'] .'-'. $this->data['type_id'] .'_'.
[16400]474          $this->data['user_id'] . $this->data['set_id'] .
475          ($this->data['nb_zip']!=1 ? '_part'. $i : null).
[16392]476          '.zip';
[16379]477  }
478 
479  /**
480   * getSetInfo
481   * @return: array
482   */
483  function getSetInfo()
484  {
485    $set = array(
486      'NB_IMAGES' => $this->data['nb_images'],
487      'NB_ARCHIVES' => $this->data['nb_zip'],
488      'TOTAL_SIZE' => ceil($this->getEstimatedTotalSize()/1024),
489      'LINKS' => $this->getDownloadList(BATCH_DOWNLOAD_PUBLIC . 'init_zip'),
[16400]490      'DATE_CREATION' => format_date($this->data['date_creation'], true),
[16379]491      );
492       
493    if ($this->data['status'] == 'new')
494    {
495      $set['U_EDIT_SET'] = BATCH_DOWNLOAD_PUBLIC . 'view&amp;set_id='.$this->data['set_id'];
496    }
497   
498    switch ($this->data['type'])
499    {
[16400]500      // calendar
[16379]501      case 'calendar':
502      {
[16400]503        global $conf, $page;
504        $old_page = $page;
505       
506        $fields = array(
507          'created' => l10n('Creation date'),
508          'posted' => l10n('Post date'),
509          );
510       
511        $chronology = explode('-', $this->data['type_id']);
512        $page['chronology_field'] = $chronology[0];
513        $page['chronology_style'] = $chronology[1];
514        $page['chronology_view'] = $chronology[2];
515        $page['chronology_date'] = array_splice($chronology, 3);
516       
517        if (!class_exists('Calendar'))
518        {
519          include_once(PHPWG_ROOT_PATH.'include/calendar_'. $page['chronology_style'] .'.class.php');
520        }
521        $calendar = new Calendar();
522        $calendar->initialize('');
523        $display_name = strip_tags($calendar->get_display_name());
524       
525        $set['NAME'] = l10n('Calendar').': '.$fields[$page['chronology_field']].$display_name;
526        $set['sNAME'] = l10n('Calendar').': '.ltrim($display_name, $conf['level_separator']);
527       
528        $page = $old_page;
[16379]529        break;
530      }
531     
[16400]532      // category
[16379]533      case 'category':
534      {
535        $category = get_cat_info($this->data['type_id']);
[16392]536        $set['NAME'] = l10n('Album').': '.get_cat_display_name($category['upper_names']);
537        $set['sNAME'] = l10n('Album').': '.trigger_event('render_category_name', $category['name']);
538        $set['COMMENT'] = trigger_event('render_category_description', $category['comment']);
[16379]539        break;
540      }
541     
[16400]542      // flat
[16379]543      case 'flat':
544      {
545        $set['NAME'] = l10n('Whole gallery');
546        break;
547      }
548     
[16400]549      // tags
[16379]550      case 'tags':
551      {
552        $tags = find_tags(explode(',', $this->data['type_id']));
[16400]553        $set['NAME'] = l10n('Tags').': ';
[16379]554       
[16400]555        $first = true;
[16379]556        foreach ($tags as $tag)
557        {
558          if ($first) $first = false;
[16400]559          else $set['NAME'].= ', ';
560          $set['NAME'].=
[16379]561            '<a href="' . make_index_url(array('tags'=>array($tag))) . '">'
562            .trigger_event('render_tag_name', $tag['name'])
563            .'</a>';
564        }
565        break;
566      }
567     
[16400]568      // search
[16379]569      case 'search':
570      {
571        $set['NAME'] = '<a href="'.make_index_url(array('section'=>'search', 'search'=>$this->data['type_id'])).'">'.l10n('Search').'</a>';
572        break;
573      }
574     
[16400]575      // favorites
[16379]576      case 'favorites':
577      {
578        $set['NAME'] = '<a href="'.make_index_url(array('section'=>'favorites')).'">'.l10n('Your favorites').'</a>';
579        break;
580      }
581     
[16400]582      // most_visited
[16379]583      case 'most_visited':
584      {
585        $set['NAME'] = '<a href="'.make_index_url(array('section'=>'most_visited')).'">'.l10n('Most visited').'</a>';
586        break;
587      }
588     
[16400]589      // best_rated
[16379]590      case 'best_rated':
591      {
592        $set['NAME'] = '<a href="'.make_index_url(array('section'=>'best_rated')).'">'.l10n('Best rated').'</a>';
593        break;
594      }
595     
[16400]596      // list
[16379]597      case 'list':
598      {
599        $set['NAME'] = l10n('Random');
600        break;
601      }
602     
[16400]603      // recent_pics
[16379]604      case 'recent_pics':
605      {
606        $set['NAME'] = '<a href="'.make_index_url(array('section'=>'recent_pics')).'">'.l10n('Recent photos').'</a>';
607        break;
608      }
609     
[16592]610      // collection
611      case 'collection':
612      {
613        try
614        {
615          $UserCollection = new UserCollection($this->data['type_id']);
616          $set['NAME'] = l10n('Collection').': <a href="'.USER_COLLEC_PUBLIC.'view/'.$UserCollection->getParam('col_id').'">'.$UserCollection->getParam('name').'</a>';
617        }
618        catch (Exception $e)
619        {
620          $set['NAME'] = l10n('Collection').': #'.$this->data['type_id'].' (deleted)';
621        }
622        break;
623      }
[16379]624    }
625   
[16400]626    if (!isset($set['sNAME'])) $set['sNAME'] = strip_tags($set['NAME']);
[16379]627    if (!isset($set['COMMENT'])) $set['COMMENT'] = null;
628   
629    return $set;
630  }
631}
632
633?>
Note: See TracBrowser for help on using the repository browser.