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
Line 
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(
23      'set_id' => 0,
24      'user_id' => $user['id'],
25      'date_creation' => '0000-00-00 00:00:00',
26      'type' => null,
27      'type_id' => null,
28      'nb_zip' => 0,
29      'last_zip' => 0,
30      'nb_images' => 0,
31      'total_size' => 0,
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
41    user_id,
42    date_creation,
43    type,
44    type_id,
45    nb_zip,
46    last_zip,
47    nb_images,
48    total_size,
49    status
50  FROM '.BATCH_DOWNLOAD_TSETS.'
51  WHERE
52    id = '.$set_id.'
53    '.(!is_admin() ? 'AND user_id = '.$this->data['user_id'] : null).'
54;';
55      $result = pwg_query($query);
56     
57      if (pwg_db_num_rows($result))
58      {
59        $this->data['set_id'] = $set_id;
60        list(
61          $this->data['user_id'],
62          $this->data['date_creation'],
63          $this->data['type'], 
64          $this->data['type_id'], 
65          $this->data['nb_zip'], 
66          $this->data['last_zip'], 
67          $this->data['nb_images'], 
68          $this->data['total_size'],
69          $this->data['status']
70          ) = pwg_db_fetch_row($result);
71       
72        // make sur all pictures of the set exist
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       
90        if ( $this->data['status'] != 'done' and count($this->images) != $this->data['nb_images'] )
91        {
92          $this->updateParam('nb_images', count($this->images));
93        }
94      }
95      else
96      {
97        throw new Exception(l10n('Invalid dowload set'));
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,
115    total_size,
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,
126    0,
127    "new"
128  )
129;';
130      pwg_query($query);
131      $this->data['set_id'] = pwg_db_insert_id();
132     
133      $date = pwg_query('SELECT FROM_UNIXTIME(NOW());');
134      list($this->data['date_creation']) = pwg_db_fetch_row($date);
135     
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  /**
240   * clearImages
241   */
242  function clearImages()
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
267   * @param: bool force all elements in one archive
268   * @return: string zip path or false
269   */
270  function createNextArchive($force_one_archive=false)
271  {
272    // set already downloaded (we should never be there !)
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']);
287        $this->clearImages();
288        $this->addImages($images_ids);
289      }
290     
291      $this->getEstimatedArchiveNumber();
292     
293      $this->updateParam('date_creation', date('Y-m-d H:i:s'));
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'];
339        if ($total_size >= $this->conf['max_size']*1024 and !$force_one_archive) break;
340      }
341     
342      $this->updateParam('total_size', $this->data['total_size'] + $total_size);
343     
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  {
397    if ($this->data['status'] == 'done') return $this->data['total_size'];
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  {
428    $out = '';
429    for ($i=1; $i<=$this->data['nb_zip']; $i++)
430    {
431      $out.= '<li id="zip-'.$i.'">';
432     
433      if ($this->data['status'] == 'done' or $i < $this->data['last_zip']+1)
434      {
435        $out.= '<img src="'.BATCH_DOWNLOAD_PATH.'template/drive.png"> Archive #'.$i.' (already downloaded)';
436      }
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>';
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'] .'_'.
474          $this->data['user_id'] . $this->data['set_id'] .
475          ($this->data['nb_zip']!=1 ? '_part'. $i : null).
476          '.zip';
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'),
490      'DATE_CREATION' => format_date($this->data['date_creation'], true),
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    {
500      // calendar
501      case 'calendar':
502      {
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;
529        break;
530      }
531     
532      // category
533      case 'category':
534      {
535        $category = get_cat_info($this->data['type_id']);
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']);
539        break;
540      }
541     
542      // flat
543      case 'flat':
544      {
545        $set['NAME'] = l10n('Whole gallery');
546        break;
547      }
548     
549      // tags
550      case 'tags':
551      {
552        $tags = find_tags(explode(',', $this->data['type_id']));
553        $set['NAME'] = l10n('Tags').': ';
554       
555        $first = true;
556        foreach ($tags as $tag)
557        {
558          if ($first) $first = false;
559          else $set['NAME'].= ', ';
560          $set['NAME'].=
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     
568      // search
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     
575      // favorites
576      case 'favorites':
577      {
578        $set['NAME'] = '<a href="'.make_index_url(array('section'=>'favorites')).'">'.l10n('Your favorites').'</a>';
579        break;
580      }
581     
582      // most_visited
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     
589      // best_rated
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     
596      // list
597      case 'list':
598      {
599        $set['NAME'] = l10n('Random');
600        break;
601      }
602     
603      // recent_pics
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     
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      }
624    }
625   
626    if (!isset($set['sNAME'])) $set['sNAME'] = strip_tags($set['NAME']);
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.