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

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

-button on admin page to purge download list
-confirmation is properly updated on UserCollection page

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