source: trunk/include/ws_functions.inc.php @ 19703

Last change on this file since 19703 was 19703, checked in by plg, 11 years ago

update Piwigo headers to 2013 (the end of the world didn't occur as expected on r12922)

  • Property svn:eol-style set to LF
File size: 84.1 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2013 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24/**** IMPLEMENTATION OF WEB SERVICE METHODS ***********************************/
25
26/**
27 * Event handler for method invocation security check. Should return a PwgError
28 * if the preconditions are not satifsied for method invocation.
29 */
30function ws_isInvokeAllowed($res, $methodName, $params)
31{
32  global $conf;
33
34  if ( strpos($methodName,'reflection.')===0 )
35  { // OK for reflection
36    return $res;
37  }
38
39  if ( !is_autorize_status(ACCESS_GUEST) and
40      strpos($methodName,'pwg.session.')!==0 )
41  {
42    return new PwgError(401, 'Access denied');
43  }
44
45  return $res;
46}
47
48/**
49 * returns a "standard" (for our web service) array of sql where clauses that
50 * filters the images (images table only)
51 */
52function ws_std_image_sql_filter( $params, $tbl_name='' )
53{
54  $clauses = array();
55  if ( is_numeric($params['f_min_rate']) )
56  {
57    $clauses[] = $tbl_name.'rating_score>'.$params['f_min_rate'];
58  }
59  if ( is_numeric($params['f_max_rate']) )
60  {
61    $clauses[] = $tbl_name.'rating_score<='.$params['f_max_rate'];
62  }
63  if ( is_numeric($params['f_min_hit']) )
64  {
65    $clauses[] = $tbl_name.'hit>'.$params['f_min_hit'];
66  }
67  if ( is_numeric($params['f_max_hit']) )
68  {
69    $clauses[] = $tbl_name.'hit<='.$params['f_max_hit'];
70  }
71  if ( isset($params['f_min_date_available']) )
72  {
73    $clauses[] = $tbl_name."date_available>='".$params['f_min_date_available']."'";
74  }
75  if ( isset($params['f_max_date_available']) )
76  {
77    $clauses[] = $tbl_name."date_available<'".$params['f_max_date_available']."'";
78  }
79  if ( isset($params['f_min_date_created']) )
80  {
81    $clauses[] = $tbl_name."date_creation>='".$params['f_min_date_created']."'";
82  }
83  if ( isset($params['f_max_date_created']) )
84  {
85    $clauses[] = $tbl_name."date_creation<'".$params['f_max_date_created']."'";
86  }
87  if ( is_numeric($params['f_min_ratio']) )
88  {
89    $clauses[] = $tbl_name.'width/'.$tbl_name.'height>'.$params['f_min_ratio'];
90  }
91  if ( is_numeric($params['f_max_ratio']) )
92  {
93    $clauses[] = $tbl_name.'width/'.$tbl_name.'height<='.$params['f_max_ratio'];
94  }
95  if (is_numeric($params['f_max_level']) )
96  {
97    $clauses[] = $tbl_name.'level <= '.$params['f_max_level'];
98  }
99  return $clauses;
100}
101
102/**
103 * returns a "standard" (for our web service) ORDER BY sql clause for images
104 */
105function ws_std_image_sql_order( $params, $tbl_name='' )
106{
107  $ret = '';
108  if ( empty($params['order']) )
109  {
110    return $ret;
111  }
112  $matches = array();
113  preg_match_all('/([a-z_]+) *(?:(asc|desc)(?:ending)?)? *(?:, *|$)/i',
114    $params['order'], $matches);
115  for ($i=0; $i<count($matches[1]); $i++)
116  {
117    switch ($matches[1][$i])
118    {
119      case 'date_created':
120        $matches[1][$i] = 'date_creation'; break;
121      case 'date_posted':
122        $matches[1][$i] = 'date_available'; break;
123      case 'rand': case 'random':
124        $matches[1][$i] = DB_RANDOM_FUNCTION.'()'; break;
125    }
126    $sortable_fields = array('id', 'file', 'name', 'hit', 'rating_score',
127      'date_creation', 'date_available', DB_RANDOM_FUNCTION.'()' );
128    if ( in_array($matches[1][$i], $sortable_fields) )
129    {
130      if (!empty($ret))
131        $ret .= ', ';
132      if ($matches[1][$i] != DB_RANDOM_FUNCTION.'()' )
133      {
134        $ret .= $tbl_name;
135      }
136      $ret .= $matches[1][$i];
137      $ret .= ' '.$matches[2][$i];
138    }
139  }
140  return $ret;
141}
142
143/**
144 * returns an array map of urls (thumb/element) for image_row - to be returned
145 * in a standard way by different web service methods
146 */
147function ws_std_get_urls($image_row)
148{
149  $ret = array();
150
151  $ret['page_url'] = make_picture_url( array(
152            'image_id' => $image_row['id'],
153            'image_file' => $image_row['file'],
154          )
155        );
156
157  $src_image = new SrcImage($image_row);
158
159  if ( $src_image->is_original() )
160  {// we have a photo
161    global $user;
162    if ($user['enabled_high'])
163    {
164      $ret['element_url'] = $src_image->get_url();
165    }
166  }
167  else
168  {
169    $ret['element_url'] = get_element_url($image_row);
170  }
171
172  $derivatives = DerivativeImage::get_all($src_image);
173  $derivatives_arr = array();
174  foreach($derivatives as $type=>$derivative)
175  {
176    $size = $derivative->get_size();
177    $size != null or $size=array(null,null);
178    $derivatives_arr[$type] = array('url' => $derivative->get_url(), 'width'=>$size[0], 'height'=>$size[1] );
179  }
180  $ret['derivatives'] = $derivatives_arr;;
181  return $ret;
182}
183
184/**
185 * returns an array of image attributes that are to be encoded as xml attributes
186 * instead of xml elements
187 */
188function ws_std_get_image_xml_attributes()
189{
190  return array(
191    'id','element_url', 'page_url', 'file','width','height','hit','date_available','date_creation'
192    );
193}
194
195function ws_getMissingDerivatives($params, &$service)
196{
197  if (!is_admin())
198  {
199    return new PwgError(403, 'Forbidden');
200  }
201
202  if ( empty($params['types']) )
203  {
204    $types = array_keys(ImageStdParams::get_defined_type_map());
205  }
206  else
207  {
208    $types = array_intersect(array_keys(ImageStdParams::get_defined_type_map()), $params['types']);
209    if (count($types)==0)
210    {
211      return new PwgError(WS_ERR_INVALID_PARAM, "Invalid types");
212    }
213  }
214
215  if ( ($max_urls = intval($params['max_urls'])) <= 0)
216  {
217    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid max_urls");
218  }
219
220  list($max_id, $image_count) = pwg_db_fetch_row( pwg_query('SELECT MAX(id)+1, COUNT(*) FROM '.IMAGES_TABLE) );
221
222  if (0 == $image_count)
223  {
224    return array();
225  }
226
227  $start_id = intval($params['prev_page']);
228  if ($start_id<=0)
229  {
230    $start_id = $max_id;
231  }
232
233  $uid = '&b='.time();
234  global $conf;
235  $conf['question_mark_in_urls'] = $conf['php_extension_in_urls'] = true;
236  $conf['derivative_url_style']=2; //script
237
238  $qlimit = min(5000, ceil(max($image_count/500, $max_urls/count($types))));
239  $where_clauses = ws_std_image_sql_filter( $params, '' );
240  $where_clauses[] = 'id<start_id';
241  if ( !empty($params['ids']) )
242  {
243    $where_clauses[] = 'id IN ('.implode(',',$params['ids']).')';
244  }
245
246  $query_model = 'SELECT id, path, representative_ext, width,height,rotation
247  FROM '.IMAGES_TABLE.'
248  WHERE '.implode(' AND ', $where_clauses).'
249  ORDER BY id DESC
250  LIMIT '.$qlimit;
251
252  $urls=array();
253  do
254  {
255    $result = pwg_query( str_replace('start_id', $start_id, $query_model));
256    $is_last = pwg_db_num_rows($result) < $qlimit;
257    while ($row=pwg_db_fetch_assoc($result))
258    {
259      $start_id = $row['id'];
260      $src_image = new SrcImage($row);
261      if ($src_image->is_mimetype())
262        continue;
263      foreach($types as $type)
264      {
265        $derivative = new DerivativeImage($type, $src_image);
266        if ($type != $derivative->get_type())
267          continue;
268        if (@filemtime($derivative->get_path())===false)
269        {
270          $urls[] = $derivative->get_url().$uid;
271        }
272      }
273      if (count($urls)>=$max_urls && !$is_last)
274        break;
275    }
276    if ($is_last)
277    {
278      $start_id = 0;
279    }
280  }while (count($urls)<$max_urls && $start_id);
281
282  $ret = array();
283  if ($start_id)
284  {
285    $ret['next_page']=$start_id;
286  }
287  $ret['urls']=$urls;
288  return $ret;
289}
290
291/**
292 * returns PWG version (web service method)
293 */
294function ws_getVersion($params, &$service)
295{
296  global $conf;
297  if ($conf['show_version'] or is_admin() )
298    return PHPWG_VERSION;
299  else
300    return new PwgError(403, 'Forbidden');
301}
302
303/**
304 * returns general informations (web service method)
305 */
306function ws_getInfos($params, &$service)
307{
308  if (!is_admin())
309  {
310    return new PwgError(403, 'Forbidden');
311  }
312
313  $infos['version'] = PHPWG_VERSION;
314
315  $query = 'SELECT COUNT(*) FROM '.IMAGES_TABLE.';';
316  list($infos['nb_elements']) = pwg_db_fetch_row(pwg_query($query));
317
318  $query = 'SELECT COUNT(*) FROM '.CATEGORIES_TABLE.';';
319  list($infos['nb_categories']) = pwg_db_fetch_row(pwg_query($query));
320
321  $query = 'SELECT COUNT(*) FROM '.CATEGORIES_TABLE.' WHERE dir IS NULL;';
322  list($infos['nb_virtual']) = pwg_db_fetch_row(pwg_query($query));
323
324  $query = 'SELECT COUNT(*) FROM '.CATEGORIES_TABLE.' WHERE dir IS NOT NULL;';
325  list($infos['nb_physical']) = pwg_db_fetch_row(pwg_query($query));
326
327  $query = 'SELECT COUNT(*) FROM '.IMAGE_CATEGORY_TABLE.';';
328  list($infos['nb_image_category']) = pwg_db_fetch_row(pwg_query($query));
329
330  $query = 'SELECT COUNT(*) FROM '.TAGS_TABLE.';';
331  list($infos['nb_tags']) = pwg_db_fetch_row(pwg_query($query));
332
333  $query = 'SELECT COUNT(*) FROM '.IMAGE_TAG_TABLE.';';
334  list($infos['nb_image_tag']) = pwg_db_fetch_row(pwg_query($query));
335
336  $query = 'SELECT COUNT(*) FROM '.USERS_TABLE.';';
337  list($infos['nb_users']) = pwg_db_fetch_row(pwg_query($query));
338
339  $query = 'SELECT COUNT(*) FROM '.GROUPS_TABLE.';';
340  list($infos['nb_groups']) = pwg_db_fetch_row(pwg_query($query));
341
342  $query = 'SELECT COUNT(*) FROM '.COMMENTS_TABLE.';';
343  list($infos['nb_comments']) = pwg_db_fetch_row(pwg_query($query));
344
345  // first element
346  if ($infos['nb_elements'] > 0)
347  {
348    $query = 'SELECT MIN(date_available) FROM '.IMAGES_TABLE.';';
349    list($infos['first_date']) = pwg_db_fetch_row(pwg_query($query));
350  }
351
352  // unvalidated comments
353  if ($infos['nb_comments'] > 0)
354  {
355    $query = 'SELECT COUNT(*) FROM '.COMMENTS_TABLE.' WHERE validated=\'false\';';
356    list($infos['nb_unvalidated_comments']) = pwg_db_fetch_row(pwg_query($query));
357  }
358
359  foreach ($infos as $name => $value)
360  {
361    $output[] = array(
362      'name' => $name,
363      'value' => $value,
364    );
365  }
366
367  return array('infos' => new PwgNamedArray($output, 'item'));
368}
369
370function ws_caddie_add($params, &$service)
371{
372  if (!is_admin())
373  {
374    return new PwgError(401, 'Access denied');
375  }
376  $params['image_id'] = array_map( 'intval',$params['image_id'] );
377  if ( empty($params['image_id']) )
378  {
379    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
380  }
381  global $user;
382  $query = '
383SELECT id
384  FROM '.IMAGES_TABLE.' LEFT JOIN '.CADDIE_TABLE.' ON id=element_id AND user_id='.$user['id'].'
385  WHERE id IN ('.implode(',',$params['image_id']).')
386    AND element_id IS NULL';
387  $datas = array();
388  foreach ( array_from_query($query, 'id') as $id )
389  {
390    array_push($datas, array('element_id'=>$id, 'user_id'=>$user['id']) );
391  }
392  if (count($datas))
393  {
394    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
395    mass_inserts(CADDIE_TABLE, array('element_id','user_id'), $datas);
396  }
397  return count($datas);
398}
399
400/**
401 * returns images per category (web service method)
402 */
403function ws_categories_getImages($params, &$service)
404{
405  global $user, $conf;
406
407  $images = array();
408
409  //------------------------------------------------- get the related categories
410  $where_clauses = array();
411  foreach($params['cat_id'] as $cat_id)
412  {
413    $cat_id = (int)$cat_id;
414    if ($cat_id<=0)
415      continue;
416    if ($params['recursive'])
417    {
418      $where_clauses[] = 'uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.$cat_id.'(,|$)\'';
419    }
420    else
421    {
422      $where_clauses[] = 'id='.$cat_id;
423    }
424  }
425  if (!empty($where_clauses))
426  {
427    $where_clauses = array( '('.
428    implode('
429    OR ', $where_clauses) . ')'
430      );
431  }
432  $where_clauses[] = get_sql_condition_FandF(
433        array('forbidden_categories' => 'id'),
434        NULL, true
435      );
436
437  $query = '
438SELECT id, name, permalink, image_order
439  FROM '.CATEGORIES_TABLE.'
440  WHERE '. implode('
441    AND ', $where_clauses);
442  $result = pwg_query($query);
443  $cats = array();
444  while ($row = pwg_db_fetch_assoc($result))
445  {
446    $row['id'] = (int)$row['id'];
447    $cats[ $row['id'] ] = $row;
448  }
449
450  //-------------------------------------------------------- get the images
451  if ( !empty($cats) )
452  {
453    $where_clauses = ws_std_image_sql_filter( $params, 'i.' );
454    $where_clauses[] = 'category_id IN ('
455      .implode(',', array_keys($cats) )
456      .')';
457    $where_clauses[] = get_sql_condition_FandF( array(
458          'visible_images' => 'i.id'
459        ), null, true
460      );
461
462    $order_by = ws_std_image_sql_order($params, 'i.');
463    if ( empty($order_by)
464          and count($params['cat_id'])==1
465          and isset($cats[ $params['cat_id'][0] ]['image_order'])
466        )
467    {
468      $order_by = $cats[ $params['cat_id'][0] ]['image_order'];
469    }
470    $order_by = empty($order_by) ? $conf['order_by'] : 'ORDER BY '.$order_by;
471
472    $query = '
473SELECT i.*, GROUP_CONCAT(category_id) AS cat_ids
474  FROM '.IMAGES_TABLE.' i
475    INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON i.id=image_id
476  WHERE '. implode('
477    AND ', $where_clauses).'
478GROUP BY i.id
479'.$order_by.'
480LIMIT '.(int)$params['per_page'].' OFFSET '.(int)($params['per_page']*$params['page']);
481
482    $result = pwg_query($query);
483    while ($row = pwg_db_fetch_assoc($result))
484    {
485      $image = array();
486      foreach ( array('id', 'width', 'height', 'hit') as $k )
487      {
488        if (isset($row[$k]))
489        {
490          $image[$k] = (int)$row[$k];
491        }
492      }
493      foreach ( array('file', 'name', 'comment', 'date_creation', 'date_available') as $k )
494      {
495        $image[$k] = $row[$k];
496      }
497      $image = array_merge( $image, ws_std_get_urls($row) );
498
499      $image_cats = array();
500      foreach ( explode(',', $row['cat_ids']) as $cat_id )
501      {
502        $url = make_index_url(
503                array(
504                  'category' => $cats[$cat_id],
505                  )
506                );
507        $page_url = make_picture_url(
508                array(
509                  'category' => $cats[$cat_id],
510                  'image_id' => $row['id'],
511                  'image_file' => $row['file'],
512                  )
513                );
514        array_push( $image_cats,  array(
515              WS_XML_ATTRIBUTES => array (
516                  'id' => (int)$cat_id,
517                  'url' => $url,
518                  'page_url' => $page_url,
519                )
520            )
521          );
522      }
523
524      $image['categories'] = new PwgNamedArray(
525            $image_cats,'category', array('id','url','page_url')
526          );
527      array_push($images, $image);
528    }
529  }
530
531  return array( 'images' =>
532    array (
533      WS_XML_ATTRIBUTES =>
534        array(
535            'page' => $params['page'],
536            'per_page' => $params['per_page'],
537            'count' => count($images)
538          ),
539       WS_XML_CONTENT => new PwgNamedArray($images, 'image',
540          ws_std_get_image_xml_attributes() )
541      )
542    );
543}
544
545
546/**
547 * create a tree from a flat list of categories, no recursivity for high speed
548 */
549function categories_flatlist_to_tree($categories)
550{
551  $tree = array();
552  $key_of_cat = array();
553
554  foreach ($categories as $key => &$node)
555  {
556    $key_of_cat[$node['id']] = $key;
557
558    if (!isset($node['id_uppercat']))
559    {
560      $tree[$key] = &$node;
561    }
562    else
563    {
564      if (!isset($categories[ $key_of_cat[ $node['id_uppercat'] ] ]['sub_categories']))
565      {
566        $categories[ $key_of_cat[ $node['id_uppercat'] ] ]['sub_categories'] = array();
567      }
568
569      $categories[ $key_of_cat[ $node['id_uppercat'] ] ]['sub_categories'][$key] = &$node;
570    }
571  }
572
573  return $tree;
574}
575
576/**
577 * returns a list of categories (web service method)
578 */
579function ws_categories_getList($params, &$service)
580{
581  global $user,$conf;
582
583  if ($params['tree_output'])
584  {
585    if (!isset($_GET['format']) or !in_array($_GET['format'], array('php', 'json')))
586    {
587      // the algorithm used to build a tree from a flat list of categories
588      // keeps original array keys, which is not compatible with
589      // PwgNamedArray.
590      //
591      // PwgNamedArray is useful to define which data is an attribute and
592      // which is an element in the XML output. The "hierarchy" output is
593      // only compatible with json/php output.
594
595      return new PwgError(405, "The tree_output option is only compatible with json/php output formats");
596    }
597  }
598
599  $where = array('1=1');
600  $join_type = 'INNER';
601  $join_user = $user['id'];
602
603  if (!$params['recursive'])
604  {
605    if ($params['cat_id']>0)
606      $where[] = '(id_uppercat='.(int)($params['cat_id']).'
607    OR id='.(int)($params['cat_id']).')';
608    else
609      $where[] = 'id_uppercat IS NULL';
610  }
611  else if ($params['cat_id']>0)
612  {
613    $where[] = 'uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.
614      (int)($params['cat_id'])
615      .'(,|$)\'';
616  }
617
618  if ($params['public'])
619  {
620    $where[] = 'status = "public"';
621    $where[] = 'visible = "true"';
622
623    $join_user = $conf['guest_id'];
624  }
625  elseif (is_admin())
626  {
627    // in this very specific case, we don't want to hide empty
628    // categories. Function calculate_permissions will only return
629    // categories that are either locked or private and not permitted
630    //
631    // calculate_permissions does not consider empty categories as forbidden
632    $forbidden_categories = calculate_permissions($user['id'], $user['status']);
633    $where[]= 'id NOT IN ('.$forbidden_categories.')';
634    $join_type = 'LEFT';
635  }
636
637  $query = '
638SELECT id, name, permalink, uppercats, global_rank, id_uppercat,
639    comment,
640    nb_images, count_images AS total_nb_images,
641    representative_picture_id, user_representative_picture_id, count_images, count_categories,
642    date_last, max_date_last, count_categories AS nb_categories
643  FROM '.CATEGORIES_TABLE.'
644   '.$join_type.' JOIN '.USER_CACHE_CATEGORIES_TABLE.' ON id=cat_id AND user_id='.$join_user.'
645  WHERE '. implode('
646    AND ', $where);
647
648  $result = pwg_query($query);
649
650  // management of the album thumbnail -- starts here
651  $image_ids = array();
652  $categories = array();
653  $user_representative_updates_for = array();
654  // management of the album thumbnail -- stops here
655
656  $cats = array();
657  while ($row = pwg_db_fetch_assoc($result))
658  {
659    $row['url'] = make_index_url(
660        array(
661          'category' => $row
662          )
663      );
664    foreach( array('id','nb_images','total_nb_images','nb_categories') as $key)
665    {
666      $row[$key] = (int)$row[$key];
667    }
668
669    if ($params['fullname'])
670    {
671      $row['name'] = strip_tags(get_cat_display_name_cache($row['uppercats'], null, false));
672    }
673    else
674    {
675      $row['name'] = strip_tags(
676        trigger_event(
677          'render_category_name',
678          $row['name'],
679          'ws_categories_getList'
680          )
681        );
682    }
683
684    $row['comment'] = strip_tags(
685      trigger_event(
686        'render_category_description',
687        $row['comment'],
688        'ws_categories_getList'
689        )
690      );
691
692    // management of the album thumbnail -- starts here
693    //
694    // on branch 2.3, the algorithm is duplicated from
695    // include/category_cats, but we should use a common code for Piwigo 2.4
696    //
697    // warning : if the API method is called with $params['public'], the
698    // album thumbnail may be not accurate. The thumbnail can be viewed by
699    // the connected user, but maybe not by the guest. Changing the
700    // filtering method would be too complicated for now. We will simply
701    // avoid to persist the user_representative_picture_id in the database
702    // if $params['public']
703    if (!empty($row['user_representative_picture_id']))
704    {
705      $image_id = $row['user_representative_picture_id'];
706    }
707    else if (!empty($row['representative_picture_id']))
708    { // if a representative picture is set, it has priority
709      $image_id = $row['representative_picture_id'];
710    }
711    else if ($conf['allow_random_representative'])
712    {
713      // searching a random representant among elements in sub-categories
714      $image_id = get_random_image_in_category($row);
715    }
716    else
717    { // searching a random representant among representant of sub-categories
718      if ($row['count_categories']>0 and $row['count_images']>0)
719      {
720        $query = '
721  SELECT representative_picture_id
722    FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
723    ON id = cat_id and user_id = '.$user['id'].'
724    WHERE uppercats LIKE \''.$row['uppercats'].',%\'
725      AND representative_picture_id IS NOT NULL'
726          .get_sql_condition_FandF
727          (
728            array
729            (
730              'visible_categories' => 'id',
731              ),
732            "\n  AND"
733            ).'
734    ORDER BY '.DB_RANDOM_FUNCTION.'()
735    LIMIT 1
736  ;';
737        $subresult = pwg_query($query);
738        if (pwg_db_num_rows($subresult) > 0)
739        {
740          list($image_id) = pwg_db_fetch_row($subresult);
741        }
742      }
743    }
744
745    if (isset($image_id))
746    {
747      if ($conf['representative_cache_on_subcats'] and $row['user_representative_picture_id'] != $image_id)
748      {
749        $user_representative_updates_for[ $user['id'].'#'.$row['id'] ] = $image_id;
750      }
751
752      $row['representative_picture_id'] = $image_id;
753      array_push($image_ids, $image_id);
754      array_push($categories, $row);
755    }
756    unset($image_id);
757    // management of the album thumbnail -- stops here
758
759
760    array_push($cats, $row);
761  }
762  usort($cats, 'global_rank_compare');
763
764  // management of the album thumbnail -- starts here
765  if (count($categories) > 0)
766  {
767    $thumbnail_src_of = array();
768    $new_image_ids = array();
769
770    $query = '
771SELECT id, path, representative_ext, level
772  FROM '.IMAGES_TABLE.'
773  WHERE id IN ('.implode(',', $image_ids).')
774;';
775    $result = pwg_query($query);
776    while ($row = pwg_db_fetch_assoc($result))
777    {
778      if ($row['level'] <= $user['level'])
779      {
780        $thumbnail_src_of[$row['id']] = DerivativeImage::thumb_url($row);
781      }
782      else
783      {
784        // problem: we must not display the thumbnail of a photo which has a
785        // higher privacy level than user privacy level
786        //
787        // * what is the represented category?
788        // * find a random photo matching user permissions
789        // * register it at user_representative_picture_id
790        // * set it as the representative_picture_id for the category
791
792        foreach ($categories as &$category)
793        {
794          if ($row['id'] == $category['representative_picture_id'])
795          {
796            // searching a random representant among elements in sub-categories
797            $image_id = get_random_image_in_category($category);
798
799            if (isset($image_id) and !in_array($image_id, $image_ids))
800            {
801              array_push($new_image_ids, $image_id);
802            }
803
804            if ($conf['representative_cache_on_level'])
805            {
806              $user_representative_updates_for[ $user['id'].'#'.$category['id'] ] = $image_id;
807            }
808
809            $category['representative_picture_id'] = $image_id;
810          }
811        }
812        unset($category);
813      }
814    }
815
816    if (count($new_image_ids) > 0)
817    {
818      $query = '
819SELECT id, path, representative_ext
820  FROM '.IMAGES_TABLE.'
821  WHERE id IN ('.implode(',', $new_image_ids).')
822;';
823      $result = pwg_query($query);
824      while ($row = pwg_db_fetch_assoc($result))
825      {
826        $thumbnail_src_of[$row['id']] = DerivativeImage::thumb_url($row);
827      }
828    }
829  }
830
831  // compared to code in include/category_cats, we only persist the new
832  // user_representative if we have used $user['id'] and not the guest id,
833  // or else the real guest may see thumbnail that he should not
834  if (!$params['public'] and count($user_representative_updates_for))
835  {
836    $updates = array();
837
838    foreach ($user_representative_updates_for as $user_cat => $image_id)
839    {
840      list($user_id, $cat_id) = explode('#', $user_cat);
841
842      array_push(
843        $updates,
844        array(
845          'user_id' => $user_id,
846          'cat_id' => $cat_id,
847          'user_representative_picture_id' => $image_id,
848          )
849        );
850    }
851
852    mass_updates(
853      USER_CACHE_CATEGORIES_TABLE,
854      array(
855        'primary' => array('user_id', 'cat_id'),
856        'update'  => array('user_representative_picture_id')
857        ),
858      $updates
859      );
860  }
861
862  foreach ($cats as &$cat)
863  {
864    foreach ($categories as $category)
865    {
866      if ($category['id'] == $cat['id'] and isset($category['representative_picture_id']))
867      {
868        $cat['tn_url'] = $thumbnail_src_of[$category['representative_picture_id']];
869      }
870    }
871    // we don't want them in the output
872    unset($cat['user_representative_picture_id']);
873    unset($cat['count_images']);
874    unset($cat['count_categories']);
875  }
876  unset($cat);
877  // management of the album thumbnail -- stops here
878
879  if ($params['tree_output'])
880  {
881    return categories_flatlist_to_tree($cats);
882  }
883  else
884  {
885    return array(
886      'categories' => new PwgNamedArray(
887        $cats,
888        'category',
889        array(
890          'id',
891          'url',
892          'nb_images',
893          'total_nb_images',
894          'nb_categories',
895          'date_last',
896          'max_date_last',
897          )
898        )
899      );
900  }
901}
902
903/**
904 * returns the list of categories as you can see them in administration (web
905 * service method).
906 *
907 * Only admin can run this method and permissions are not taken into
908 * account.
909 */
910function ws_categories_getAdminList($params, &$service)
911{
912  if (!is_admin())
913  {
914    return new PwgError(401, 'Access denied');
915  }
916
917  $query = '
918SELECT
919    category_id,
920    COUNT(*) AS counter
921  FROM '.IMAGE_CATEGORY_TABLE.'
922  GROUP BY category_id
923;';
924  $nb_images_of = simple_hash_from_query($query, 'category_id', 'counter');
925
926  $query = '
927SELECT
928    id,
929    name,
930    comment,
931    uppercats,
932    global_rank
933  FROM '.CATEGORIES_TABLE.'
934;';
935  $result = pwg_query($query);
936  $cats = array();
937
938  while ($row = pwg_db_fetch_assoc($result))
939  {
940    $id = $row['id'];
941    $row['nb_images'] = isset($nb_images_of[$id]) ? $nb_images_of[$id] : 0;
942    $row['name'] = strip_tags(
943      trigger_event(
944        'render_category_name',
945        $row['name'],
946        'ws_categories_getAdminList'
947        )
948      );
949    $row['comment'] = strip_tags(
950      trigger_event(
951        'render_category_description',
952        $row['comment'],
953        'ws_categories_getAdminList'
954        )
955      );
956    array_push($cats, $row);
957  }
958
959  usort($cats, 'global_rank_compare');
960  return array(
961    'categories' => new PwgNamedArray(
962      $cats,
963      'category',
964      array(
965        'id',
966        'nb_images',
967        'name',
968        'uppercats',
969        'global_rank',
970        )
971      )
972    );
973}
974
975/**
976 * returns detailed information for an element (web service method)
977 */
978function ws_images_addComment($params, &$service)
979{
980  if (!$service->isPost())
981  {
982    return new PwgError(405, "This method requires HTTP POST");
983  }
984  $params['image_id'] = (int)$params['image_id'];
985  $query = '
986SELECT DISTINCT image_id
987  FROM '.IMAGE_CATEGORY_TABLE.' INNER JOIN '.CATEGORIES_TABLE.' ON category_id=id
988  WHERE commentable="true"
989    AND image_id='.$params['image_id'].
990    get_sql_condition_FandF(
991      array(
992        'forbidden_categories' => 'id',
993        'visible_categories' => 'id',
994        'visible_images' => 'image_id'
995      ),
996      ' AND'
997    );
998  if ( !pwg_db_num_rows( pwg_query( $query ) ) )
999  {
1000    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1001  }
1002
1003  $comm = array(
1004    'author' => trim( $params['author'] ),
1005    'content' => trim( $params['content'] ),
1006    'image_id' => $params['image_id'],
1007   );
1008
1009  include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
1010
1011  $comment_action = insert_user_comment(
1012      $comm, $params['key'], $infos
1013    );
1014
1015  switch ($comment_action)
1016  {
1017    case 'reject':
1018      array_push($infos, l10n('Your comment has NOT been registered because it did not pass the validation rules') );
1019      return new PwgError(403, implode("; ", $infos) );
1020    case 'validate':
1021    case 'moderate':
1022      $ret = array(
1023          'id' => $comm['id'],
1024          'validation' => $comment_action=='validate',
1025        );
1026      return new PwgNamedStruct(
1027          'comment',
1028          $ret,
1029          null, array()
1030        );
1031    default:
1032      return new PwgError(500, "Unknown comment action ".$comment_action );
1033  }
1034}
1035
1036/**
1037 * returns detailed information for an element (web service method)
1038 */
1039function ws_images_getInfo($params, &$service)
1040{
1041  global $user, $conf;
1042  $params['image_id'] = (int)$params['image_id'];
1043  if ( $params['image_id']<=0 )
1044  {
1045    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1046  }
1047
1048  $query='
1049SELECT * FROM '.IMAGES_TABLE.'
1050  WHERE id='.$params['image_id'].
1051    get_sql_condition_FandF(
1052      array('visible_images' => 'id'),
1053      ' AND'
1054    ).'
1055LIMIT 1';
1056
1057  $image_row = pwg_db_fetch_assoc(pwg_query($query));
1058  if ($image_row==null)
1059  {
1060    return new PwgError(404, "image_id not found");
1061  }
1062  $image_row = array_merge( $image_row, ws_std_get_urls($image_row) );
1063
1064  //-------------------------------------------------------- related categories
1065  $query = '
1066SELECT id, name, permalink, uppercats, global_rank, commentable
1067  FROM '.IMAGE_CATEGORY_TABLE.'
1068    INNER JOIN '.CATEGORIES_TABLE.' ON category_id = id
1069  WHERE image_id = '.$image_row['id'].
1070  get_sql_condition_FandF(
1071      array( 'forbidden_categories' => 'category_id' ),
1072      ' AND'
1073    ).'
1074;';
1075  $result = pwg_query($query);
1076  $is_commentable = false;
1077  $related_categories = array();
1078  while ($row = pwg_db_fetch_assoc($result))
1079  {
1080    if ($row['commentable']=='true')
1081    {
1082      $is_commentable = true;
1083    }
1084    unset($row['commentable']);
1085    $row['url'] = make_index_url(
1086        array(
1087          'category' => $row
1088          )
1089      );
1090
1091    $row['page_url'] = make_picture_url(
1092        array(
1093          'image_id' => $image_row['id'],
1094          'image_file' => $image_row['file'],
1095          'category' => $row
1096          )
1097      );
1098    $row['id']=(int)$row['id'];
1099    array_push($related_categories, $row);
1100  }
1101  usort($related_categories, 'global_rank_compare');
1102  if ( empty($related_categories) )
1103  {
1104    return new PwgError(401, 'Access denied');
1105  }
1106
1107  //-------------------------------------------------------------- related tags
1108  $related_tags = get_common_tags( array($image_row['id']), -1 );
1109  foreach( $related_tags as $i=>$tag)
1110  {
1111    $tag['url'] = make_index_url(
1112        array(
1113          'tags' => array($tag)
1114          )
1115      );
1116    $tag['page_url'] = make_picture_url(
1117        array(
1118          'image_id' => $image_row['id'],
1119          'image_file' => $image_row['file'],
1120          'tags' => array($tag),
1121          )
1122      );
1123    unset($tag['counter']);
1124    $tag['id']=(int)$tag['id'];
1125    $related_tags[$i]=$tag;
1126  }
1127  //------------------------------------------------------------- related rates
1128        $rating = array('score'=>$image_row['rating_score'], 'count'=>0, 'average'=>null);
1129        if (isset($rating['score']))
1130        {
1131                $query = '
1132SELECT COUNT(rate) AS count
1133     , ROUND(AVG(rate),2) AS average
1134  FROM '.RATE_TABLE.'
1135  WHERE element_id = '.$image_row['id'].'
1136;';
1137                $row = pwg_db_fetch_assoc(pwg_query($query));
1138                $rating['score'] = (float)$rating['score'];
1139                $rating['average'] = (float)$row['average'];
1140                $rating['count'] = (int)$row['count'];
1141        }
1142
1143  //---------------------------------------------------------- related comments
1144  $related_comments = array();
1145
1146  $where_comments = 'image_id = '.$image_row['id'];
1147  if ( !is_admin() )
1148  {
1149    $where_comments .= '
1150    AND validated="true"';
1151  }
1152
1153  $query = '
1154SELECT COUNT(id) AS nb_comments
1155  FROM '.COMMENTS_TABLE.'
1156  WHERE '.$where_comments;
1157  list($nb_comments) = array_from_query($query, 'nb_comments');
1158  $nb_comments = (int)$nb_comments;
1159
1160  if ( $nb_comments>0 and $params['comments_per_page']>0 )
1161  {
1162    $query = '
1163SELECT id, date, author, content
1164  FROM '.COMMENTS_TABLE.'
1165  WHERE '.$where_comments.'
1166  ORDER BY date
1167  LIMIT '.(int)$params['comments_per_page'].
1168    ' OFFSET '.(int)($params['comments_per_page']*$params['comments_page']);
1169
1170    $result = pwg_query($query);
1171    while ($row = pwg_db_fetch_assoc($result))
1172    {
1173      $row['id']=(int)$row['id'];
1174      array_push($related_comments, $row);
1175    }
1176  }
1177
1178  $comment_post_data = null;
1179  if ($is_commentable and
1180      (!is_a_guest()
1181        or (is_a_guest() and $conf['comments_forall'] )
1182      )
1183      )
1184  {
1185    $comment_post_data['author'] = stripslashes($user['username']);
1186    $comment_post_data['key'] = get_ephemeral_key(2, $params['image_id']);
1187  }
1188
1189  $ret = $image_row;
1190  foreach ( array('id','width','height','hit','filesize') as $k )
1191  {
1192    if (isset($ret[$k]))
1193    {
1194      $ret[$k] = (int)$ret[$k];
1195    }
1196  }
1197  foreach ( array('path', 'storage_category_id') as $k )
1198  {
1199    unset($ret[$k]);
1200  }
1201
1202  $ret['rates'] = array( WS_XML_ATTRIBUTES => $rating );
1203  $ret['categories'] = new PwgNamedArray($related_categories, 'category', array('id','url', 'page_url') );
1204  $ret['tags'] = new PwgNamedArray($related_tags, 'tag', array('id','url_name','url','name','page_url') );
1205  if ( isset($comment_post_data) )
1206  {
1207    $ret['comment_post'] = array( WS_XML_ATTRIBUTES => $comment_post_data );
1208  }
1209  $ret['comments'] = array(
1210     WS_XML_ATTRIBUTES =>
1211        array(
1212          'page' => $params['comments_page'],
1213          'per_page' => $params['comments_per_page'],
1214          'count' => count($related_comments),
1215          'nb_comments' => $nb_comments,
1216        ),
1217     WS_XML_CONTENT => new PwgNamedArray($related_comments, 'comment', array('id','date') )
1218      );
1219
1220  return new PwgNamedStruct('image',$ret, null, array('name','comment') );
1221}
1222
1223
1224/**
1225 * rates the image_id in the parameter
1226 */
1227function ws_images_Rate($params, &$service)
1228{
1229  $image_id = (int)$params['image_id'];
1230  $query = '
1231SELECT DISTINCT id FROM '.IMAGES_TABLE.'
1232  INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id=image_id
1233  WHERE id='.$image_id
1234  .get_sql_condition_FandF(
1235    array(
1236        'forbidden_categories' => 'category_id',
1237        'forbidden_images' => 'id',
1238      ),
1239    '    AND'
1240    ).'
1241    LIMIT 1';
1242  if ( pwg_db_num_rows( pwg_query($query) )==0 )
1243  {
1244    return new PwgError(404, "Invalid image_id or access denied" );
1245  }
1246  $rate = (int)$params['rate'];
1247  include_once(PHPWG_ROOT_PATH.'include/functions_rate.inc.php');
1248  $res = rate_picture( $image_id, $rate );
1249  if ($res==false)
1250  {
1251    global $conf;
1252    return new PwgError( 403, "Forbidden or rate not in ". implode(',',$conf['rate_items']));
1253  }
1254  return $res;
1255}
1256
1257
1258/**
1259 * returns a list of elements corresponding to a query search
1260 */
1261function ws_images_search($params, &$service)
1262{
1263  global $page;
1264  $images = array();
1265  include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' );
1266
1267  $where_clauses = ws_std_image_sql_filter( $params, 'i.' );
1268  $order_by = ws_std_image_sql_order($params, 'i.');
1269
1270  $super_order_by = false;
1271  if ( !empty($order_by) )
1272  {
1273    global $conf;
1274    $conf['order_by'] = 'ORDER BY '.$order_by;
1275    $super_order_by=true; // quick_search_result might be faster
1276  }
1277
1278  $search_result = get_quick_search_results($params['query'],
1279      $super_order_by,
1280      implode(',', $where_clauses)
1281    );
1282
1283  $image_ids = array_slice(
1284      $search_result['items'],
1285      $params['page']*$params['per_page'],
1286      $params['per_page']
1287    );
1288
1289  if ( count($image_ids) )
1290  {
1291    $query = '
1292SELECT * FROM '.IMAGES_TABLE.'
1293  WHERE id IN ('.implode(',', $image_ids).')';
1294
1295    $image_ids = array_flip($image_ids);
1296    $result = pwg_query($query);
1297    while ($row = pwg_db_fetch_assoc($result))
1298    {
1299      $image = array();
1300      foreach ( array('id', 'width', 'height', 'hit') as $k )
1301      {
1302        if (isset($row[$k]))
1303        {
1304          $image[$k] = (int)$row[$k];
1305        }
1306      }
1307      foreach ( array('file', 'name', 'comment', 'date_creation', 'date_available') as $k )
1308      {
1309        $image[$k] = $row[$k];
1310      }
1311      $image = array_merge( $image, ws_std_get_urls($row) );
1312      $images[$image_ids[$image['id']]] = $image;
1313    }
1314    ksort($images, SORT_NUMERIC);
1315    $images = array_values($images);
1316  }
1317
1318
1319  return array( 'images' =>
1320    array (
1321      WS_XML_ATTRIBUTES =>
1322        array(
1323            'page' => $params['page'],
1324            'per_page' => $params['per_page'],
1325            'count' => count($images)
1326          ),
1327       WS_XML_CONTENT => new PwgNamedArray($images, 'image',
1328          ws_std_get_image_xml_attributes() )
1329      )
1330    );
1331}
1332
1333function ws_images_setPrivacyLevel($params, &$service)
1334{
1335  if (!is_admin())
1336  {
1337    return new PwgError(401, 'Access denied');
1338  }
1339  if (!$service->isPost())
1340  {
1341    return new PwgError(405, "This method requires HTTP POST");
1342  }
1343  $params['image_id'] = array_map( 'intval',$params['image_id'] );
1344  if ( empty($params['image_id']) )
1345  {
1346    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1347  }
1348  global $conf;
1349  if ( !in_array( (int)$params['level'], $conf['available_permission_levels']) )
1350  {
1351    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid level");
1352  }
1353
1354  $query = '
1355UPDATE '.IMAGES_TABLE.'
1356  SET level='.(int)$params['level'].'
1357  WHERE id IN ('.implode(',',$params['image_id']).')';
1358  $result = pwg_query($query);
1359  $affected_rows = pwg_db_changes($result);
1360  if ($affected_rows)
1361  {
1362    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
1363    invalidate_user_cache();
1364  }
1365  return $affected_rows;
1366}
1367
1368function ws_images_setRank($params, &$service)
1369{
1370  if (!is_admin())
1371  {
1372    return new PwgError(401, 'Access denied');
1373  }
1374
1375  if (!$service->isPost())
1376  {
1377    return new PwgError(405, "This method requires HTTP POST");
1378  }
1379
1380  // is the image_id valid?
1381  $params['image_id'] = (int)$params['image_id'];
1382  if ($params['image_id'] <= 0)
1383  {
1384    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1385  }
1386
1387  // is the category valid?
1388  $params['category_id'] = (int)$params['category_id'];
1389  if ($params['category_id'] <= 0)
1390  {
1391    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
1392  }
1393
1394  // is the rank valid?
1395  $params['rank'] = (int)$params['rank'];
1396  if ($params['rank'] <= 0)
1397  {
1398    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid rank");
1399  }
1400
1401  // does the image really exist?
1402  $query='
1403SELECT
1404    *
1405  FROM '.IMAGES_TABLE.'
1406  WHERE id = '.$params['image_id'].'
1407;';
1408
1409  $image_row = pwg_db_fetch_assoc(pwg_query($query));
1410  if ($image_row == null)
1411  {
1412    return new PwgError(404, "image_id not found");
1413  }
1414
1415  // is the image associated to this category?
1416  $query = '
1417SELECT
1418    image_id,
1419    category_id,
1420    rank
1421  FROM '.IMAGE_CATEGORY_TABLE.'
1422  WHERE image_id = '.$params['image_id'].'
1423    AND category_id = '.$params['category_id'].'
1424;';
1425  $category_row = pwg_db_fetch_assoc(pwg_query($query));
1426  if ($category_row == null)
1427  {
1428    return new PwgError(404, "This image is not associated to this category");
1429  }
1430
1431  // what is the current higher rank for this category?
1432  $query = '
1433SELECT
1434    MAX(rank) AS max_rank
1435  FROM '.IMAGE_CATEGORY_TABLE.'
1436  WHERE category_id = '.$params['category_id'].'
1437;';
1438  $result = pwg_query($query);
1439  $row = pwg_db_fetch_assoc($result);
1440
1441  if (is_numeric($row['max_rank']))
1442  {
1443    if ($params['rank'] > $row['max_rank'])
1444    {
1445      $params['rank'] = $row['max_rank'] + 1;
1446    }
1447  }
1448  else
1449  {
1450    $params['rank'] = 1;
1451  }
1452
1453  // update rank for all other photos in the same category
1454  $query = '
1455UPDATE '.IMAGE_CATEGORY_TABLE.'
1456  SET rank = rank + 1
1457  WHERE category_id = '.$params['category_id'].'
1458    AND rank IS NOT NULL
1459    AND rank >= '.$params['rank'].'
1460;';
1461  pwg_query($query);
1462
1463  // set the new rank for the photo
1464  $query = '
1465UPDATE '.IMAGE_CATEGORY_TABLE.'
1466  SET rank = '.$params['rank'].'
1467  WHERE image_id = '.$params['image_id'].'
1468    AND category_id = '.$params['category_id'].'
1469;';
1470  pwg_query($query);
1471
1472  // return data for client
1473  return array(
1474    'image_id' => $params['image_id'],
1475    'category_id' => $params['category_id'],
1476    'rank' => $params['rank'],
1477    );
1478}
1479
1480function ws_images_add_chunk($params, &$service)
1481{
1482  global $conf;
1483
1484  // data
1485  // original_sum
1486  // type {thumb, file, high}
1487  // position
1488
1489  if (!is_admin())
1490  {
1491    return new PwgError(401, 'Access denied');
1492  }
1493
1494  if (!$service->isPost())
1495  {
1496    return new PwgError(405, "This method requires HTTP POST");
1497  }
1498
1499  foreach ($params as $param_key => $param_value) {
1500    if ('data' == $param_key) {
1501      continue;
1502    }
1503
1504    ws_logfile(
1505      sprintf(
1506        '[ws_images_add_chunk] input param "%s" : "%s"',
1507        $param_key,
1508        is_null($param_value) ? 'NULL' : $param_value
1509        )
1510      );
1511  }
1512
1513  $upload_dir = $conf['upload_dir'].'/buffer';
1514
1515  // create the upload directory tree if not exists
1516  if (!mkgetdir($upload_dir, MKGETDIR_DEFAULT&~MKGETDIR_DIE_ON_ERROR))
1517  {
1518    return new PwgError(500, 'error during buffer directory creation');
1519  }
1520
1521  $filename = sprintf(
1522    '%s-%s-%05u.block',
1523    $params['original_sum'],
1524    $params['type'],
1525    $params['position']
1526    );
1527
1528  ws_logfile('[ws_images_add_chunk] data length : '.strlen($params['data']));
1529
1530  $bytes_written = file_put_contents(
1531    $upload_dir.'/'.$filename,
1532    base64_decode($params['data'])
1533    );
1534
1535  if (false === $bytes_written) {
1536    return new PwgError(
1537      500,
1538      'an error has occured while writting chunk '.$params['position'].' for '.$params['type']
1539      );
1540  }
1541}
1542
1543function merge_chunks($output_filepath, $original_sum, $type)
1544{
1545  global $conf;
1546
1547  ws_logfile('[merge_chunks] input parameter $output_filepath : '.$output_filepath);
1548
1549  if (is_file($output_filepath))
1550  {
1551    unlink($output_filepath);
1552
1553    if (is_file($output_filepath))
1554    {
1555      return new PwgError(500, '[merge_chunks] error while trying to remove existing '.$output_filepath);
1556    }
1557  }
1558
1559  $upload_dir = $conf['upload_dir'].'/buffer';
1560  $pattern = '/'.$original_sum.'-'.$type.'/';
1561  $chunks = array();
1562
1563  if ($handle = opendir($upload_dir))
1564  {
1565    while (false !== ($file = readdir($handle)))
1566    {
1567      if (preg_match($pattern, $file))
1568      {
1569        ws_logfile($file);
1570        array_push($chunks, $upload_dir.'/'.$file);
1571      }
1572    }
1573    closedir($handle);
1574  }
1575
1576  sort($chunks);
1577
1578  if (function_exists('memory_get_usage')) {
1579    ws_logfile('[merge_chunks] memory_get_usage before loading chunks: '.memory_get_usage());
1580  }
1581
1582  $i = 0;
1583
1584  foreach ($chunks as $chunk)
1585  {
1586    $string = file_get_contents($chunk);
1587
1588    if (function_exists('memory_get_usage')) {
1589      ws_logfile('[merge_chunks] memory_get_usage on chunk '.++$i.': '.memory_get_usage());
1590    }
1591
1592    if (!file_put_contents($output_filepath, $string, FILE_APPEND))
1593    {
1594      return new PwgError(500, '[merge_chunks] error while writting chunks for '.$output_filepath);
1595    }
1596
1597    unlink($chunk);
1598  }
1599
1600  if (function_exists('memory_get_usage')) {
1601    ws_logfile('[merge_chunks] memory_get_usage after loading chunks: '.memory_get_usage());
1602  }
1603}
1604
1605/**
1606 * Function introduced for Piwigo 2.4 and the new "multiple size"
1607 * (derivatives) feature. As we only need the biggest sent photo as
1608 * "original", we remove chunks for smaller sizes. We can't make it earlier
1609 * in ws_images_add_chunk because at this moment we don't know which $type
1610 * will be the biggest (we could remove the thumb, but let's use the same
1611 * algorithm)
1612 */
1613function remove_chunks($original_sum, $type)
1614{
1615  global $conf;
1616
1617  $upload_dir = $conf['upload_dir'].'/buffer';
1618  $pattern = '/'.$original_sum.'-'.$type.'/';
1619  $chunks = array();
1620
1621  if ($handle = opendir($upload_dir))
1622  {
1623    while (false !== ($file = readdir($handle)))
1624    {
1625      if (preg_match($pattern, $file))
1626      {
1627        array_push($chunks, $upload_dir.'/'.$file);
1628      }
1629    }
1630    closedir($handle);
1631  }
1632
1633  foreach ($chunks as $chunk)
1634  {
1635    unlink($chunk);
1636  }
1637}
1638
1639function ws_images_addFile($params, &$service)
1640{
1641  ws_logfile(__FUNCTION__.', input :  '.var_export($params, true));
1642  // image_id
1643  // type {thumb, file, high}
1644  // sum -> not used currently (Piwigo 2.4)
1645
1646  global $conf;
1647  if (!is_admin())
1648  {
1649    return new PwgError(401, 'Access denied');
1650  }
1651
1652  $params['image_id'] = (int)$params['image_id'];
1653  if ($params['image_id'] <= 0)
1654  {
1655    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1656  }
1657
1658  //
1659  // what is the path and other infos about the photo?
1660  //
1661  $query = '
1662SELECT
1663    path,
1664    file,
1665    md5sum,
1666    width,
1667    height,
1668    filesize
1669  FROM '.IMAGES_TABLE.'
1670  WHERE id = '.$params['image_id'].'
1671;';
1672  $image = pwg_db_fetch_assoc(pwg_query($query));
1673
1674  if ($image == null)
1675  {
1676    return new PwgError(404, "image_id not found");
1677  }
1678
1679  // since Piwigo 2.4 and derivatives, we do not take the imported "thumb"
1680  // into account
1681  if ('thumb' == $params['type'])
1682  {
1683    remove_chunks($image['md5sum'], $type);
1684    return true;
1685  }
1686
1687  // since Piwigo 2.4 and derivatives, we only care about the "original"
1688  $original_type = 'file';
1689  if ('high' == $params['type'])
1690  {
1691    $original_type = 'high';
1692  }
1693
1694  $file_path = $conf['upload_dir'].'/buffer/'.$image['md5sum'].'-original';
1695
1696  merge_chunks($file_path, $image['md5sum'], $original_type);
1697  chmod($file_path, 0644);
1698
1699  include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
1700
1701  // if we receive the "file", we only update the original if the "file" is
1702  // bigger than current original
1703  if ('file' == $params['type'])
1704  {
1705    $do_update = false;
1706
1707    $infos = pwg_image_infos($file_path);
1708
1709    foreach (array('width', 'height', 'filesize') as $image_info)
1710    {
1711      if ($infos[$image_info] > $image[$image_info])
1712      {
1713        $do_update = true;
1714      }
1715    }
1716
1717    if (!$do_update)
1718    {
1719      unlink($file_path);
1720      return true;
1721    }
1722  }
1723
1724  $image_id = add_uploaded_file(
1725    $file_path,
1726    $image['file'],
1727    null,
1728    null,
1729    $params['image_id'],
1730    $image['md5sum'] // we force the md5sum to remain the same
1731    );
1732}
1733
1734function ws_images_add($params, &$service)
1735{
1736  global $conf, $user;
1737  if (!is_admin())
1738  {
1739    return new PwgError(401, 'Access denied');
1740  }
1741
1742  foreach ($params as $param_key => $param_value) {
1743    ws_logfile(
1744      sprintf(
1745        '[pwg.images.add] input param "%s" : "%s"',
1746        $param_key,
1747        is_null($param_value) ? 'NULL' : $param_value
1748        )
1749      );
1750  }
1751
1752  $params['image_id'] = (int)$params['image_id'];
1753  if ($params['image_id'] > 0)
1754  {
1755    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
1756
1757    $query='
1758SELECT *
1759  FROM '.IMAGES_TABLE.'
1760  WHERE id = '.$params['image_id'].'
1761;';
1762
1763    $image_row = pwg_db_fetch_assoc(pwg_query($query));
1764    if ($image_row == null)
1765    {
1766      return new PwgError(404, "image_id not found");
1767    }
1768  }
1769
1770  // does the image already exists ?
1771  if ($params['check_uniqueness'])
1772  {
1773    if ('md5sum' == $conf['uniqueness_mode'])
1774    {
1775      $where_clause = "md5sum = '".$params['original_sum']."'";
1776    }
1777    if ('filename' == $conf['uniqueness_mode'])
1778    {
1779      $where_clause = "file = '".$params['original_filename']."'";
1780    }
1781
1782    $query = '
1783SELECT
1784    COUNT(*) AS counter
1785  FROM '.IMAGES_TABLE.'
1786  WHERE '.$where_clause.'
1787;';
1788    list($counter) = pwg_db_fetch_row(pwg_query($query));
1789    if ($counter != 0) {
1790      return new PwgError(500, 'file already exists');
1791    }
1792  }
1793
1794  // due to the new feature "derivatives" (multiple sizes) introduced for
1795  // Piwigo 2.4, we only take the biggest photos sent on
1796  // pwg.images.addChunk. If "high" is available we use it as "original"
1797  // else we use "file".
1798  remove_chunks($params['original_sum'], 'thumb');
1799
1800  if (isset($params['high_sum']))
1801  {
1802    $original_type = 'high';
1803    remove_chunks($params['original_sum'], 'file');
1804  }
1805  else
1806  {
1807    $original_type = 'file';
1808  }
1809
1810  $file_path = $conf['upload_dir'].'/buffer/'.$params['original_sum'].'-original';
1811
1812  merge_chunks($file_path, $params['original_sum'], $original_type);
1813  chmod($file_path, 0644);
1814
1815  include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
1816
1817  $image_id = add_uploaded_file(
1818    $file_path,
1819    $params['original_filename'],
1820    null, // categories
1821    isset($params['level']) ? $params['level'] : null,
1822    $params['image_id'] > 0 ? $params['image_id'] : null,
1823    $params['original_sum']
1824    );
1825
1826  $info_columns = array(
1827    'name',
1828    'author',
1829    'comment',
1830    'date_creation',
1831    );
1832
1833  $update = array();
1834
1835  foreach ($info_columns as $key)
1836  {
1837    if (isset($params[$key]))
1838    {
1839      $update[$key] = $params[$key];
1840    }
1841  }
1842
1843  if (count(array_keys($update)) > 0)
1844  {
1845    single_update(
1846      IMAGES_TABLE,
1847      $update,
1848      array('id' => $image_id)
1849      );
1850  }
1851
1852  $url_params = array('image_id' => $image_id);
1853
1854  // let's add links between the image and the categories
1855  if (isset($params['categories']))
1856  {
1857    ws_add_image_category_relations($image_id, $params['categories']);
1858
1859    if (preg_match('/^\d+/', $params['categories'], $matches)) {
1860      $category_id = $matches[0];
1861
1862      $query = '
1863SELECT id, name, permalink
1864  FROM '.CATEGORIES_TABLE.'
1865  WHERE id = '.$category_id.'
1866;';
1867      $result = pwg_query($query);
1868      $category = pwg_db_fetch_assoc($result);
1869
1870      $url_params['section'] = 'categories';
1871      $url_params['category'] = $category;
1872    }
1873  }
1874
1875  // and now, let's create tag associations
1876  if (isset($params['tag_ids']) and !empty($params['tag_ids']))
1877  {
1878    set_tags(
1879      explode(',', $params['tag_ids']),
1880      $image_id
1881      );
1882  }
1883
1884  invalidate_user_cache();
1885
1886  return array(
1887    'image_id' => $image_id,
1888    'url' => make_picture_url($url_params),
1889    );
1890}
1891
1892function ws_images_addSimple($params, &$service)
1893{
1894  global $conf;
1895  if (!is_admin())
1896  {
1897    return new PwgError(401, 'Access denied');
1898  }
1899
1900  if (!$service->isPost())
1901  {
1902    return new PwgError(405, "This method requires HTTP POST");
1903  }
1904
1905  if (!isset($_FILES['image']))
1906  {
1907    return new PwgError(405, "The image (file) parameter is missing");
1908  }
1909
1910  $params['image_id'] = (int)$params['image_id'];
1911  if ($params['image_id'] > 0)
1912  {
1913    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
1914
1915    $query='
1916SELECT *
1917  FROM '.IMAGES_TABLE.'
1918  WHERE id = '.$params['image_id'].'
1919;';
1920
1921    $image_row = pwg_db_fetch_assoc(pwg_query($query));
1922    if ($image_row == null)
1923    {
1924      return new PwgError(404, "image_id not found");
1925    }
1926  }
1927
1928  // category
1929  $params['category'] = (int)$params['category'];
1930  if ($params['category'] <= 0 and $params['image_id'] <= 0)
1931  {
1932    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
1933  }
1934
1935  include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
1936
1937  $image_id = add_uploaded_file(
1938    $_FILES['image']['tmp_name'],
1939    $_FILES['image']['name'],
1940    $params['category'] > 0 ? array($params['category']) : null,
1941    8,
1942    $params['image_id'] > 0 ? $params['image_id'] : null
1943    );
1944
1945  $info_columns = array(
1946    'name',
1947    'author',
1948    'comment',
1949    'level',
1950    'date_creation',
1951    );
1952
1953  foreach ($info_columns as $key)
1954  {
1955    if (isset($params[$key]))
1956    {
1957      $update[$key] = $params[$key];
1958    }
1959  }
1960
1961  if (count(array_keys($update)) > 0)
1962  {
1963    $update['id'] = $image_id;
1964
1965    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
1966    mass_updates(
1967      IMAGES_TABLE,
1968      array(
1969        'primary' => array('id'),
1970        'update'  => array_diff(array_keys($update), array('id'))
1971        ),
1972      array($update)
1973      );
1974  }
1975
1976
1977  if (isset($params['tags']) and !empty($params['tags']))
1978  {
1979    $tag_ids = array();
1980    $tag_names = explode(',', $params['tags']);
1981    foreach ($tag_names as $tag_name)
1982    {
1983      $tag_id = tag_id_from_tag_name($tag_name);
1984      array_push($tag_ids, $tag_id);
1985    }
1986
1987    add_tags($tag_ids, array($image_id));
1988  }
1989
1990  $url_params = array('image_id' => $image_id);
1991
1992  if ($params['category'] > 0)
1993  {
1994    $query = '
1995SELECT id, name, permalink
1996  FROM '.CATEGORIES_TABLE.'
1997  WHERE id = '.$params['category'].'
1998;';
1999    $result = pwg_query($query);
2000    $category = pwg_db_fetch_assoc($result);
2001
2002    $url_params['section'] = 'categories';
2003    $url_params['category'] = $category;
2004  }
2005
2006  // update metadata from the uploaded file (exif/iptc), even if the sync
2007  // was already performed by add_uploaded_file().
2008
2009  require_once(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
2010  sync_metadata(array($image_id));
2011
2012  return array(
2013    'image_id' => $image_id,
2014    'url' => make_picture_url($url_params),
2015    );
2016}
2017
2018function ws_rates_delete($params, &$service)
2019{
2020  global $conf;
2021
2022  if (!$service->isPost())
2023  {
2024    return new PwgError(405, 'This method requires HTTP POST');
2025  }
2026
2027  if (!is_admin())
2028  {
2029    return new PwgError(401, 'Access denied');
2030  }
2031
2032  $user_id = (int)$params['user_id'];
2033  if ($user_id<=0)
2034  {
2035    return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid user_id');
2036  }
2037
2038  $query = '
2039DELETE FROM '.RATE_TABLE.'
2040  WHERE user_id='.$user_id;
2041
2042  if (!empty($params['anonymous_id']))
2043  {
2044    $query .= ' AND anonymous_id=\''.$params['anonymous_id'].'\'';
2045  }
2046
2047  $changes = pwg_db_changes(pwg_query($query));
2048  if ($changes)
2049  {
2050    include_once(PHPWG_ROOT_PATH.'include/functions_rate.inc.php');
2051    update_rating_score();
2052  }
2053  return $changes;
2054}
2055
2056
2057/**
2058 * perform a login (web service method)
2059 */
2060function ws_session_login($params, &$service)
2061{
2062  global $conf;
2063
2064  if (!$service->isPost())
2065  {
2066    return new PwgError(405, "This method requires HTTP POST");
2067  }
2068  if (try_log_user($params['username'], $params['password'],false))
2069  {
2070    return true;
2071  }
2072  return new PwgError(999, 'Invalid username/password');
2073}
2074
2075
2076/**
2077 * performs a logout (web service method)
2078 */
2079function ws_session_logout($params, &$service)
2080{
2081  if (!is_a_guest())
2082  {
2083    logout_user();
2084  }
2085  return true;
2086}
2087
2088function ws_session_getStatus($params, &$service)
2089{
2090  global $user;
2091  $res = array();
2092  $res['username'] = is_a_guest() ? 'guest' : stripslashes($user['username']);
2093  foreach ( array('status', 'theme', 'language') as $k )
2094  {
2095    $res[$k] = $user[$k];
2096  }
2097  $res['pwg_token'] = get_pwg_token();
2098  $res['charset'] = get_pwg_charset();
2099
2100  list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
2101  $res['current_datetime'] = $dbnow;
2102
2103  return $res;
2104}
2105
2106
2107/**
2108 * returns a list of tags (web service method)
2109 */
2110function ws_tags_getList($params, &$service)
2111{
2112  $tags = get_available_tags();
2113  if ($params['sort_by_counter'])
2114  {
2115    usort($tags, create_function('$a,$b', 'return -$a["counter"]+$b["counter"];') );
2116  }
2117  else
2118  {
2119    usort($tags, 'tag_alpha_compare');
2120  }
2121  for ($i=0; $i<count($tags); $i++)
2122  {
2123    $tags[$i]['id'] = (int)$tags[$i]['id'];
2124    $tags[$i]['counter'] = (int)$tags[$i]['counter'];
2125    $tags[$i]['url'] = make_index_url(
2126        array(
2127          'section'=>'tags',
2128          'tags'=>array($tags[$i])
2129        )
2130      );
2131  }
2132  return array('tags' => new PwgNamedArray($tags, 'tag', array('id','url_name','url', 'name', 'counter' )) );
2133}
2134
2135/**
2136 * returns the list of tags as you can see them in administration (web
2137 * service method).
2138 *
2139 * Only admin can run this method and permissions are not taken into
2140 * account.
2141 */
2142function ws_tags_getAdminList($params, &$service)
2143{
2144  if (!is_admin())
2145  {
2146    return new PwgError(401, 'Access denied');
2147  }
2148
2149  $tags = get_all_tags();
2150  return array(
2151    'tags' => new PwgNamedArray(
2152      $tags,
2153      'tag',
2154      array(
2155        'name',
2156        'id',
2157        'url_name',
2158        )
2159      )
2160    );
2161}
2162
2163/**
2164 * returns a list of images for tags (web service method)
2165 */
2166function ws_tags_getImages($params, &$service)
2167{
2168  global $conf;
2169
2170  // first build all the tag_ids we are interested in
2171  $params['tag_id'] = array_map( 'intval',$params['tag_id'] );
2172  $tags = find_tags($params['tag_id'], $params['tag_url_name'], $params['tag_name']);
2173  $tags_by_id = array();
2174  foreach( $tags as $tag )
2175  {
2176    $tags['id'] = (int)$tag['id'];
2177    $tags_by_id[ $tag['id'] ] = $tag;
2178  }
2179  unset($tags);
2180  $tag_ids = array_keys($tags_by_id);
2181
2182
2183  $where_clauses = ws_std_image_sql_filter($params);
2184  if (!empty($where_clauses))
2185  {
2186    $where_clauses = implode( ' AND ', $where_clauses);
2187  }
2188  $image_ids = get_image_ids_for_tags(
2189    $tag_ids,
2190    $params['tag_mode_and'] ? 'AND' : 'OR',
2191    $where_clauses,
2192    ws_std_image_sql_order($params) );
2193
2194
2195  $image_ids = array_slice($image_ids, (int)($params['per_page']*$params['page']), (int)$params['per_page'] );
2196
2197  $image_tag_map = array();
2198  if ( !empty($image_ids) and !$params['tag_mode_and'] )
2199  { // build list of image ids with associated tags per image
2200    $query = '
2201SELECT image_id, GROUP_CONCAT(tag_id) AS tag_ids
2202  FROM '.IMAGE_TAG_TABLE.'
2203  WHERE tag_id IN ('.implode(',',$tag_ids).') AND image_id IN ('.implode(',',$image_ids).')
2204  GROUP BY image_id';
2205    $result = pwg_query($query);
2206    while ( $row=pwg_db_fetch_assoc($result) )
2207    {
2208      $row['image_id'] = (int)$row['image_id'];
2209      array_push( $image_ids, $row['image_id'] );
2210      $image_tag_map[ $row['image_id'] ] = explode(',', $row['tag_ids']);
2211    }
2212  }
2213
2214  $images = array();
2215  if (!empty($image_ids))
2216  {
2217    $rank_of = array_flip($image_ids);
2218    $result = pwg_query('
2219SELECT * FROM '.IMAGES_TABLE.'
2220  WHERE id IN ('.implode(',',$image_ids).')');
2221    while ($row = pwg_db_fetch_assoc($result))
2222    {
2223      $image = array();
2224      $image['rank'] = $rank_of[ $row['id'] ];
2225      foreach ( array('id', 'width', 'height', 'hit') as $k )
2226      {
2227        if (isset($row[$k]))
2228        {
2229          $image[$k] = (int)$row[$k];
2230        }
2231      }
2232      foreach ( array('file', 'name', 'comment', 'date_creation', 'date_available') as $k )
2233      {
2234        $image[$k] = $row[$k];
2235      }
2236      $image = array_merge( $image, ws_std_get_urls($row) );
2237
2238      $image_tag_ids = ($params['tag_mode_and']) ? $tag_ids : $image_tag_map[$image['id']];
2239      $image_tags = array();
2240      foreach ($image_tag_ids as $tag_id)
2241      {
2242        $url = make_index_url(
2243                 array(
2244                  'section'=>'tags',
2245                  'tags'=> array($tags_by_id[$tag_id])
2246                )
2247              );
2248        $page_url = make_picture_url(
2249                 array(
2250                  'section'=>'tags',
2251                  'tags'=> array($tags_by_id[$tag_id]),
2252                  'image_id' => $row['id'],
2253                  'image_file' => $row['file'],
2254                )
2255              );
2256        array_push($image_tags, array(
2257                'id' => (int)$tag_id,
2258                'url' => $url,
2259                'page_url' => $page_url,
2260              )
2261            );
2262      }
2263      $image['tags'] = new PwgNamedArray($image_tags, 'tag',
2264              array('id','url_name','url','page_url')
2265            );
2266      array_push($images, $image);
2267    }
2268    usort($images, 'rank_compare');
2269    unset($rank_of);
2270  }
2271
2272  return array( 'images' =>
2273    array (
2274      WS_XML_ATTRIBUTES =>
2275        array(
2276            'page' => $params['page'],
2277            'per_page' => $params['per_page'],
2278            'count' => count($images)
2279          ),
2280       WS_XML_CONTENT => new PwgNamedArray($images, 'image',
2281          ws_std_get_image_xml_attributes() )
2282      )
2283    );
2284}
2285
2286function ws_categories_add($params, &$service)
2287{
2288  if (!is_admin())
2289  {
2290    return new PwgError(401, 'Access denied');
2291  }
2292
2293  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2294
2295  $options = array();
2296  if (!empty($params['status']) and in_array($params['status'], array('private','public')))
2297  {
2298    $options['status'] = $params['status'];
2299  }
2300 
2301  if (!empty($params['visible']) and in_array($params['visible'], array('true','false')))
2302  {
2303    $options['visible'] = get_boolean($params['visible']);
2304  }
2305 
2306  if (!empty($params['commentable']) and in_array($params['commentable'], array('true','false')) )
2307  {
2308    $options['commentable'] = get_boolean($params['commentable']);
2309  }
2310 
2311  if (!empty($params['comment']))
2312  {
2313    $options['comment'] = $params['comment'];
2314  }
2315 
2316
2317  $creation_output = create_virtual_category(
2318    $params['name'],
2319    $params['parent'],
2320    $options
2321    );
2322
2323  if (isset($creation_output['error']))
2324  {
2325    return new PwgError(500, $creation_output['error']);
2326  }
2327
2328  invalidate_user_cache();
2329
2330  return $creation_output;
2331}
2332
2333function ws_tags_add($params, &$service)
2334{
2335  if (!is_admin())
2336  {
2337    return new PwgError(401, 'Access denied');
2338  }
2339
2340  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2341
2342  $creation_output = create_tag($params['name']);
2343
2344  if (isset($creation_output['error']))
2345  {
2346    return new PwgError(500, $creation_output['error']);
2347  }
2348
2349  return $creation_output;
2350}
2351
2352function ws_images_exist($params, &$service)
2353{
2354  ws_logfile(__FUNCTION__.' '.var_export($params, true));
2355
2356  global $conf;
2357
2358  if (!is_admin())
2359  {
2360    return new PwgError(401, 'Access denied');
2361  }
2362
2363  $split_pattern = '/[\s,;\|]/';
2364
2365  if ('md5sum' == $conf['uniqueness_mode'])
2366  {
2367    // search among photos the list of photos already added, based on md5sum
2368    // list
2369    $md5sums = preg_split(
2370      $split_pattern,
2371      $params['md5sum_list'],
2372      -1,
2373      PREG_SPLIT_NO_EMPTY
2374    );
2375
2376    $query = '
2377SELECT
2378    id,
2379    md5sum
2380  FROM '.IMAGES_TABLE.'
2381  WHERE md5sum IN (\''.implode("','", $md5sums).'\')
2382;';
2383    $id_of_md5 = simple_hash_from_query($query, 'md5sum', 'id');
2384
2385    $result = array();
2386
2387    foreach ($md5sums as $md5sum)
2388    {
2389      $result[$md5sum] = null;
2390      if (isset($id_of_md5[$md5sum]))
2391      {
2392        $result[$md5sum] = $id_of_md5[$md5sum];
2393      }
2394    }
2395  }
2396
2397  if ('filename' == $conf['uniqueness_mode'])
2398  {
2399    // search among photos the list of photos already added, based on
2400    // filename list
2401    $filenames = preg_split(
2402      $split_pattern,
2403      $params['filename_list'],
2404      -1,
2405      PREG_SPLIT_NO_EMPTY
2406    );
2407
2408    $query = '
2409SELECT
2410    id,
2411    file
2412  FROM '.IMAGES_TABLE.'
2413  WHERE file IN (\''.implode("','", $filenames).'\')
2414;';
2415    $id_of_filename = simple_hash_from_query($query, 'file', 'id');
2416
2417    $result = array();
2418
2419    foreach ($filenames as $filename)
2420    {
2421      $result[$filename] = null;
2422      if (isset($id_of_filename[$filename]))
2423      {
2424        $result[$filename] = $id_of_filename[$filename];
2425      }
2426    }
2427  }
2428
2429  return $result;
2430}
2431
2432function ws_images_checkFiles($params, &$service)
2433{
2434  ws_logfile(__FUNCTION__.', input :  '.var_export($params, true));
2435
2436  if (!is_admin())
2437  {
2438    return new PwgError(401, 'Access denied');
2439  }
2440
2441  // input parameters
2442  //
2443  // image_id
2444  // thumbnail_sum
2445  // file_sum
2446  // high_sum
2447
2448  $params['image_id'] = (int)$params['image_id'];
2449  if ($params['image_id'] <= 0)
2450  {
2451    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
2452  }
2453
2454  $query = '
2455SELECT
2456    path
2457  FROM '.IMAGES_TABLE.'
2458  WHERE id = '.$params['image_id'].'
2459;';
2460  $result = pwg_query($query);
2461  if (pwg_db_num_rows($result) == 0)
2462  {
2463    return new PwgError(404, "image_id not found");
2464  }
2465  list($path) = pwg_db_fetch_row($result);
2466
2467  $ret = array();
2468
2469  if (isset($params['thumbnail_sum']))
2470  {
2471    // We always say the thumbnail is equal to create no reaction on the
2472    // other side. Since Piwigo 2.4 and derivatives, the thumbnails and web
2473    // sizes are always generated by Piwigo
2474    $ret['thumbnail'] = 'equals';
2475  }
2476
2477  if (isset($params['high_sum']))
2478  {
2479    $ret['file'] = 'equals';
2480    $compare_type = 'high';
2481  }
2482  elseif (isset($params['file_sum']))
2483  {
2484    $compare_type = 'file';
2485  }
2486
2487  if (isset($compare_type))
2488  {
2489    ws_logfile(__FUNCTION__.', md5_file($path) = '.md5_file($path));
2490    if (md5_file($path) != $params[$compare_type.'_sum'])
2491    {
2492      $ret[$compare_type] = 'differs';
2493    }
2494    else
2495    {
2496      $ret[$compare_type] = 'equals';
2497    }
2498  }
2499
2500  ws_logfile(__FUNCTION__.', output :  '.var_export($ret, true));
2501
2502  return $ret;
2503}
2504
2505function ws_images_setInfo($params, &$service)
2506{
2507  global $conf;
2508  if (!is_admin())
2509  {
2510    return new PwgError(401, 'Access denied');
2511  }
2512
2513  if (!$service->isPost())
2514  {
2515    return new PwgError(405, "This method requires HTTP POST");
2516  }
2517
2518  $params['image_id'] = (int)$params['image_id'];
2519  if ($params['image_id'] <= 0)
2520  {
2521    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
2522  }
2523
2524  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2525
2526  $query='
2527SELECT *
2528  FROM '.IMAGES_TABLE.'
2529  WHERE id = '.$params['image_id'].'
2530;';
2531
2532  $image_row = pwg_db_fetch_assoc(pwg_query($query));
2533  if ($image_row == null)
2534  {
2535    return new PwgError(404, "image_id not found");
2536  }
2537
2538  // database registration
2539  $update = array();
2540
2541  $info_columns = array(
2542    'name',
2543    'author',
2544    'comment',
2545    'level',
2546    'date_creation',
2547    );
2548
2549  foreach ($info_columns as $key)
2550  {
2551    if (isset($params[$key]))
2552    {
2553      if ('fill_if_empty' == $params['single_value_mode'])
2554      {
2555        if (empty($image_row[$key]))
2556        {
2557          $update[$key] = $params[$key];
2558        }
2559      }
2560      elseif ('replace' == $params['single_value_mode'])
2561      {
2562        $update[$key] = $params[$key];
2563      }
2564      else
2565      {
2566        return new PwgError(
2567          500,
2568          '[ws_images_setInfo]'
2569          .' invalid parameter single_value_mode "'.$params['single_value_mode'].'"'
2570          .', possible values are {fill_if_empty, replace}.'
2571          );
2572      }
2573    }
2574  }
2575
2576  if (isset($params['file']))
2577  {
2578    if (!empty($image_row['storage_category_id']))
2579    {
2580      return new PwgError(500, '[ws_images_setInfo] updating "file" is forbidden on photos added by synchronization');
2581    }
2582
2583    $update['file'] = $params['file'];
2584  }
2585
2586  if (count(array_keys($update)) > 0)
2587  {
2588    $update['id'] = $params['image_id'];
2589
2590    mass_updates(
2591      IMAGES_TABLE,
2592      array(
2593        'primary' => array('id'),
2594        'update'  => array_diff(array_keys($update), array('id'))
2595        ),
2596      array($update)
2597      );
2598  }
2599
2600  if (isset($params['categories']))
2601  {
2602    ws_add_image_category_relations(
2603      $params['image_id'],
2604      $params['categories'],
2605      ('replace' == $params['multiple_value_mode'] ? true : false)
2606      );
2607  }
2608
2609  // and now, let's create tag associations
2610  if (isset($params['tag_ids']))
2611  {
2612    $tag_ids = explode(',', $params['tag_ids']);
2613
2614    if ('replace' == $params['multiple_value_mode'])
2615    {
2616      set_tags(
2617        $tag_ids,
2618        $params['image_id']
2619        );
2620    }
2621    elseif ('append' == $params['multiple_value_mode'])
2622    {
2623      add_tags(
2624        $tag_ids,
2625        array($params['image_id'])
2626        );
2627    }
2628    else
2629    {
2630      return new PwgError(
2631        500,
2632        '[ws_images_setInfo]'
2633        .' invalid parameter multiple_value_mode "'.$params['multiple_value_mode'].'"'
2634        .', possible values are {replace, append}.'
2635        );
2636    }
2637  }
2638
2639  invalidate_user_cache();
2640}
2641
2642function ws_images_delete($params, &$service)
2643{
2644  global $conf;
2645  if (!is_admin())
2646  {
2647    return new PwgError(401, 'Access denied');
2648  }
2649
2650  if (!$service->isPost())
2651  {
2652    return new PwgError(405, "This method requires HTTP POST");
2653  }
2654
2655  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
2656  {
2657    return new PwgError(403, 'Invalid security token');
2658  }
2659
2660  $params['image_id'] = preg_split(
2661    '/[\s,;\|]/',
2662    $params['image_id'],
2663    -1,
2664    PREG_SPLIT_NO_EMPTY
2665    );
2666  $params['image_id'] = array_map('intval', $params['image_id']);
2667
2668  $image_ids = array();
2669  foreach ($params['image_id'] as $image_id)
2670  {
2671    if ($image_id > 0)
2672    {
2673      array_push($image_ids, $image_id);
2674    }
2675  }
2676
2677  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2678  delete_elements($image_ids, true);
2679}
2680
2681function ws_add_image_category_relations($image_id, $categories_string, $replace_mode=false)
2682{
2683  // let's add links between the image and the categories
2684  //
2685  // $params['categories'] should look like 123,12;456,auto;789 which means:
2686  //
2687  // 1. associate with category 123 on rank 12
2688  // 2. associate with category 456 on automatic rank
2689  // 3. associate with category 789 on automatic rank
2690  $cat_ids = array();
2691  $rank_on_category = array();
2692  $search_current_ranks = false;
2693
2694  $tokens = explode(';', $categories_string);
2695  foreach ($tokens as $token)
2696  {
2697    @list($cat_id, $rank) = explode(',', $token);
2698
2699    if (!preg_match('/^\d+$/', $cat_id))
2700    {
2701      continue;
2702    }
2703
2704    array_push($cat_ids, $cat_id);
2705
2706    if (!isset($rank))
2707    {
2708      $rank = 'auto';
2709    }
2710    $rank_on_category[$cat_id] = $rank;
2711
2712    if ($rank == 'auto')
2713    {
2714      $search_current_ranks = true;
2715    }
2716  }
2717
2718  $cat_ids = array_unique($cat_ids);
2719
2720  if (count($cat_ids) == 0)
2721  {
2722    return new PwgError(
2723      500,
2724      '[ws_add_image_category_relations] there is no category defined in "'.$categories_string.'"'
2725      );
2726  }
2727
2728  $query = '
2729SELECT
2730    id
2731  FROM '.CATEGORIES_TABLE.'
2732  WHERE id IN ('.implode(',', $cat_ids).')
2733;';
2734  $db_cat_ids = array_from_query($query, 'id');
2735
2736  $unknown_cat_ids = array_diff($cat_ids, $db_cat_ids);
2737  if (count($unknown_cat_ids) != 0)
2738  {
2739    return new PwgError(
2740      500,
2741      '[ws_add_image_category_relations] the following categories are unknown: '.implode(', ', $unknown_cat_ids)
2742      );
2743  }
2744
2745  $to_update_cat_ids = array();
2746
2747  // in case of replace mode, we first check the existing associations
2748  $query = '
2749SELECT
2750    category_id
2751  FROM '.IMAGE_CATEGORY_TABLE.'
2752  WHERE image_id = '.$image_id.'
2753;';
2754  $existing_cat_ids = array_from_query($query, 'category_id');
2755
2756  if ($replace_mode)
2757  {
2758    $to_remove_cat_ids = array_diff($existing_cat_ids, $cat_ids);
2759    if (count($to_remove_cat_ids) > 0)
2760    {
2761      $query = '
2762DELETE
2763  FROM '.IMAGE_CATEGORY_TABLE.'
2764  WHERE image_id = '.$image_id.'
2765    AND category_id IN ('.implode(', ', $to_remove_cat_ids).')
2766;';
2767      pwg_query($query);
2768      update_category($to_remove_cat_ids);
2769    }
2770  }
2771
2772  $new_cat_ids = array_diff($cat_ids, $existing_cat_ids);
2773  if (count($new_cat_ids) == 0)
2774  {
2775    return true;
2776  }
2777
2778  if ($search_current_ranks)
2779  {
2780    $query = '
2781SELECT
2782    category_id,
2783    MAX(rank) AS max_rank
2784  FROM '.IMAGE_CATEGORY_TABLE.'
2785  WHERE rank IS NOT NULL
2786    AND category_id IN ('.implode(',', $new_cat_ids).')
2787  GROUP BY category_id
2788;';
2789    $current_rank_of = simple_hash_from_query(
2790      $query,
2791      'category_id',
2792      'max_rank'
2793      );
2794
2795    foreach ($new_cat_ids as $cat_id)
2796    {
2797      if (!isset($current_rank_of[$cat_id]))
2798      {
2799        $current_rank_of[$cat_id] = 0;
2800      }
2801
2802      if ('auto' == $rank_on_category[$cat_id])
2803      {
2804        $rank_on_category[$cat_id] = $current_rank_of[$cat_id] + 1;
2805      }
2806    }
2807  }
2808
2809  $inserts = array();
2810
2811  foreach ($new_cat_ids as $cat_id)
2812  {
2813    array_push(
2814      $inserts,
2815      array(
2816        'image_id' => $image_id,
2817        'category_id' => $cat_id,
2818        'rank' => $rank_on_category[$cat_id],
2819        )
2820      );
2821  }
2822
2823  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2824  mass_inserts(
2825    IMAGE_CATEGORY_TABLE,
2826    array_keys($inserts[0]),
2827    $inserts
2828    );
2829
2830  update_category($new_cat_ids);
2831}
2832
2833function ws_categories_setInfo($params, &$service)
2834{
2835  global $conf;
2836  if (!is_admin())
2837  {
2838    return new PwgError(401, 'Access denied');
2839  }
2840
2841  if (!$service->isPost())
2842  {
2843    return new PwgError(405, "This method requires HTTP POST");
2844  }
2845
2846  // category_id
2847  // name
2848  // comment
2849
2850  $params['category_id'] = (int)$params['category_id'];
2851  if ($params['category_id'] <= 0)
2852  {
2853    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
2854  }
2855
2856  // database registration
2857  $update = array(
2858    'id' => $params['category_id'],
2859    );
2860
2861  $info_columns = array(
2862    'name',
2863    'comment',
2864    );
2865
2866  $perform_update = false;
2867  foreach ($info_columns as $key)
2868  {
2869    if (isset($params[$key]))
2870    {
2871      $perform_update = true;
2872      $update[$key] = $params[$key];
2873    }
2874  }
2875
2876  if ($perform_update)
2877  {
2878    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2879    mass_updates(
2880      CATEGORIES_TABLE,
2881      array(
2882        'primary' => array('id'),
2883        'update'  => array_diff(array_keys($update), array('id'))
2884        ),
2885      array($update)
2886      );
2887  }
2888
2889}
2890
2891function ws_categories_setRepresentative($params, &$service)
2892{
2893  global $conf;
2894
2895  if (!is_admin())
2896  {
2897    return new PwgError(401, 'Access denied');
2898  }
2899
2900  if (!$service->isPost())
2901  {
2902    return new PwgError(405, "This method requires HTTP POST");
2903  }
2904
2905  // category_id
2906  // image_id
2907
2908  $params['category_id'] = (int)$params['category_id'];
2909  if ($params['category_id'] <= 0)
2910  {
2911    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
2912  }
2913
2914  // does the category really exist?
2915  $query='
2916SELECT
2917    *
2918  FROM '.CATEGORIES_TABLE.'
2919  WHERE id = '.$params['category_id'].'
2920;';
2921  $row = pwg_db_fetch_assoc(pwg_query($query));
2922  if ($row == null)
2923  {
2924    return new PwgError(404, "category_id not found");
2925  }
2926
2927  $params['image_id'] = (int)$params['image_id'];
2928  if ($params['image_id'] <= 0)
2929  {
2930    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
2931  }
2932
2933  // does the image really exist?
2934  $query='
2935SELECT
2936    *
2937  FROM '.IMAGES_TABLE.'
2938  WHERE id = '.$params['image_id'].'
2939;';
2940
2941  $row = pwg_db_fetch_assoc(pwg_query($query));
2942  if ($row == null)
2943  {
2944    return new PwgError(404, "image_id not found");
2945  }
2946
2947  // apply change
2948  $query = '
2949UPDATE '.CATEGORIES_TABLE.'
2950  SET representative_picture_id = '.$params['image_id'].'
2951  WHERE id = '.$params['category_id'].'
2952;';
2953  pwg_query($query);
2954
2955  $query = '
2956UPDATE '.USER_CACHE_CATEGORIES_TABLE.'
2957  SET user_representative_picture_id = NULL
2958  WHERE cat_id = '.$params['category_id'].'
2959;';
2960  pwg_query($query);
2961}
2962
2963function ws_categories_delete($params, &$service)
2964{
2965  global $conf;
2966  if (!is_admin())
2967  {
2968    return new PwgError(401, 'Access denied');
2969  }
2970
2971  if (!$service->isPost())
2972  {
2973    return new PwgError(405, "This method requires HTTP POST");
2974  }
2975
2976  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
2977  {
2978    return new PwgError(403, 'Invalid security token');
2979  }
2980
2981  $modes = array('no_delete', 'delete_orphans', 'force_delete');
2982  if (!in_array($params['photo_deletion_mode'], $modes))
2983  {
2984    return new PwgError(
2985      500,
2986      '[ws_categories_delete]'
2987      .' invalid parameter photo_deletion_mode "'.$params['photo_deletion_mode'].'"'
2988      .', possible values are {'.implode(', ', $modes).'}.'
2989      );
2990  }
2991
2992  $params['category_id'] = preg_split(
2993    '/[\s,;\|]/',
2994    $params['category_id'],
2995    -1,
2996    PREG_SPLIT_NO_EMPTY
2997    );
2998  $params['category_id'] = array_map('intval', $params['category_id']);
2999
3000  $category_ids = array();
3001  foreach ($params['category_id'] as $category_id)
3002  {
3003    if ($category_id > 0)
3004    {
3005      array_push($category_ids, $category_id);
3006    }
3007  }
3008
3009  if (count($category_ids) == 0)
3010  {
3011    return;
3012  }
3013
3014  $query = '
3015SELECT id
3016  FROM '.CATEGORIES_TABLE.'
3017  WHERE id IN ('.implode(',', $category_ids).')
3018;';
3019  $category_ids = array_from_query($query, 'id');
3020
3021  if (count($category_ids) == 0)
3022  {
3023    return;
3024  }
3025
3026  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3027  delete_categories($category_ids, $params['photo_deletion_mode']);
3028  update_global_rank();
3029}
3030
3031function ws_categories_move($params, &$service)
3032{
3033  global $conf, $page;
3034
3035  if (!is_admin())
3036  {
3037    return new PwgError(401, 'Access denied');
3038  }
3039
3040  if (!$service->isPost())
3041  {
3042    return new PwgError(405, "This method requires HTTP POST");
3043  }
3044
3045  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3046  {
3047    return new PwgError(403, 'Invalid security token');
3048  }
3049
3050  $params['category_id'] = preg_split(
3051    '/[\s,;\|]/',
3052    $params['category_id'],
3053    -1,
3054    PREG_SPLIT_NO_EMPTY
3055    );
3056  $params['category_id'] = array_map('intval', $params['category_id']);
3057
3058  $category_ids = array();
3059  foreach ($params['category_id'] as $category_id)
3060  {
3061    if ($category_id > 0)
3062    {
3063      array_push($category_ids, $category_id);
3064    }
3065  }
3066
3067  if (count($category_ids) == 0)
3068  {
3069    return new PwgError(403, 'Invalid category_id input parameter, no category to move');
3070  }
3071
3072  // we can't move physical categories
3073  $categories_in_db = array();
3074
3075  $query = '
3076SELECT
3077    id,
3078    name,
3079    dir
3080  FROM '.CATEGORIES_TABLE.'
3081  WHERE id IN ('.implode(',', $category_ids).')
3082;';
3083  $result = pwg_query($query);
3084  while ($row = pwg_db_fetch_assoc($result))
3085  {
3086    $categories_in_db[$row['id']] = $row;
3087    // we break on error at first physical category detected
3088    if (!empty($row['dir']))
3089    {
3090      $row['name'] = strip_tags(
3091        trigger_event(
3092          'render_category_name',
3093          $row['name'],
3094          'ws_categories_move'
3095          )
3096        );
3097
3098      return new PwgError(
3099        403,
3100        sprintf(
3101          'Category %s (%u) is not a virtual category, you cannot move it',
3102          $row['name'],
3103          $row['id']
3104          )
3105        );
3106    }
3107  }
3108
3109  if (count($categories_in_db) != count($category_ids))
3110  {
3111    $unknown_category_ids = array_diff($category_ids, array_keys($categories_in_db));
3112
3113    return new PwgError(
3114      403,
3115      sprintf(
3116        'Category %u does not exist',
3117        $unknown_category_ids[0]
3118        )
3119      );
3120  }
3121
3122  // does this parent exists? This check should be made in the
3123  // move_categories function, not here
3124  //
3125  // 0 as parent means "move categories at gallery root"
3126  if (!is_numeric($params['parent']))
3127  {
3128    return new PwgError(403, 'Invalid parent input parameter');
3129  }
3130
3131  if (0 != $params['parent']) {
3132    $params['parent'] = intval($params['parent']);
3133    $subcat_ids = get_subcat_ids(array($params['parent']));
3134    if (count($subcat_ids) == 0)
3135    {
3136      return new PwgError(403, 'Unknown parent category id');
3137    }
3138  }
3139
3140  $page['infos'] = array();
3141  $page['errors'] = array();
3142  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3143  move_categories($category_ids, $params['parent']);
3144  invalidate_user_cache();
3145
3146  if (count($page['errors']) != 0)
3147  {
3148    return new PwgError(403, implode('; ', $page['errors']));
3149  }
3150}
3151
3152function ws_logfile($string)
3153{
3154  global $conf;
3155
3156  if (!$conf['ws_enable_log']) {
3157    return true;
3158  }
3159
3160  file_put_contents(
3161    $conf['ws_log_filepath'],
3162    '['.date('c').'] '.$string."\n",
3163    FILE_APPEND
3164    );
3165}
3166
3167function ws_images_checkUpload($params, &$service)
3168{
3169  global $conf;
3170
3171  if (!is_admin())
3172  {
3173    return new PwgError(401, 'Access denied');
3174  }
3175
3176  include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
3177  $ret['message'] = ready_for_upload_message();
3178  $ret['ready_for_upload'] = true;
3179
3180  if (!empty($ret['message']))
3181  {
3182    $ret['ready_for_upload'] = false;
3183  }
3184
3185  return $ret;
3186}
3187
3188function ws_plugins_getList($params, &$service)
3189{
3190  global $conf;
3191
3192  if (!is_admin())
3193  {
3194    return new PwgError(401, 'Access denied');
3195  }
3196
3197  include_once(PHPWG_ROOT_PATH.'admin/include/plugins.class.php');
3198  $plugins = new plugins();
3199  $plugins->sort_fs_plugins('name');
3200  $plugin_list = array();
3201
3202  foreach($plugins->fs_plugins as $plugin_id => $fs_plugin)
3203  {
3204    if (isset($plugins->db_plugins_by_id[$plugin_id]))
3205    {
3206      $state = $plugins->db_plugins_by_id[$plugin_id]['state'];
3207    }
3208    else
3209    {
3210      $state = 'uninstalled';
3211    }
3212
3213    array_push(
3214      $plugin_list,
3215      array(
3216        'id' => $plugin_id,
3217        'name' => $fs_plugin['name'],
3218        'version' => $fs_plugin['version'],
3219        'state' => $state,
3220        'description' => $fs_plugin['description'],
3221        )
3222      );
3223  }
3224
3225  return $plugin_list;
3226}
3227
3228function ws_plugins_performAction($params, &$service)
3229{
3230  global $template;
3231
3232  if (!is_admin())
3233  {
3234    return new PwgError(401, 'Access denied');
3235  }
3236
3237  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3238  {
3239    return new PwgError(403, 'Invalid security token');
3240  }
3241
3242  define('IN_ADMIN', true);
3243  include_once(PHPWG_ROOT_PATH.'admin/include/plugins.class.php');
3244  $plugins = new plugins();
3245  $errors = $plugins->perform_action($params['action'], $params['plugin']);
3246
3247
3248  if (!empty($errors))
3249  {
3250    return new PwgError(500, $errors);
3251  }
3252  else
3253  {
3254    if (in_array($params['action'], array('activate', 'deactivate')))
3255    {
3256      $template->delete_compiled_templates();
3257    }
3258    return true;
3259  }
3260}
3261
3262function ws_themes_performAction($params, &$service)
3263{
3264  global $template;
3265
3266  if (!is_admin())
3267  {
3268    return new PwgError(401, 'Access denied');
3269  }
3270
3271  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3272  {
3273    return new PwgError(403, 'Invalid security token');
3274  }
3275
3276  define('IN_ADMIN', true);
3277  include_once(PHPWG_ROOT_PATH.'admin/include/themes.class.php');
3278  $themes = new themes();
3279  $errors = $themes->perform_action($params['action'], $params['theme']);
3280
3281  if (!empty($errors))
3282  {
3283    return new PwgError(500, $errors);
3284  }
3285  else
3286  {
3287    if (in_array($params['action'], array('activate', 'deactivate')))
3288    {
3289      $template->delete_compiled_templates();
3290    }
3291    return true;
3292  }
3293}
3294
3295function ws_extensions_update($params, &$service)
3296{
3297  if (!is_webmaster())
3298  {
3299    return new PwgError(401, l10n('Webmaster status is required.'));
3300  }
3301
3302  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3303  {
3304    return new PwgError(403, 'Invalid security token');
3305  }
3306
3307  if (empty($params['type']) or !in_array($params['type'], array('plugins', 'themes', 'languages')))
3308  {
3309    return new PwgError(403, "invalid extension type");
3310  }
3311
3312  if (empty($params['id']) or empty($params['revision']))
3313  {
3314    return new PwgError(null, 'Wrong parameters');
3315  }
3316
3317  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3318  include_once(PHPWG_ROOT_PATH.'admin/include/'.$params['type'].'.class.php');
3319
3320  $type = $params['type'];
3321  $extension_id = $params['id'];
3322  $revision = $params['revision'];
3323
3324  $extension = new $type();
3325
3326  if ($type == 'plugins')
3327  {
3328    if (isset($extension->db_plugins_by_id[$extension_id]) and $extension->db_plugins_by_id[$extension_id]['state'] == 'active')
3329    {
3330      $extension->perform_action('deactivate', $extension_id);
3331
3332      redirect(PHPWG_ROOT_PATH
3333        . 'ws.php'
3334        . '?method=pwg.extensions.update'
3335        . '&type=plugins'
3336        . '&id=' . $extension_id
3337        . '&revision=' . $revision
3338        . '&reactivate=true'
3339        . '&pwg_token=' . get_pwg_token()
3340        . '&format=json'
3341      );
3342    }
3343
3344    $upgrade_status = $extension->extract_plugin_files('upgrade', $revision, $extension_id);
3345    $extension_name = $extension->fs_plugins[$extension_id]['name'];
3346
3347    if (isset($params['reactivate']))
3348    {
3349      $extension->perform_action('activate', $extension_id);
3350    }
3351  }
3352  elseif ($type == 'themes')
3353  {
3354    $upgrade_status = $extension->extract_theme_files('upgrade', $revision, $extension_id);
3355    $extension_name = $extension->fs_themes[$extension_id]['name'];
3356  }
3357  elseif ($type == 'languages')
3358  {
3359    $upgrade_status = $extension->extract_language_files('upgrade', $revision, $extension_id);
3360    $extension_name = $extension->fs_languages[$extension_id]['name'];
3361  }
3362
3363  global $template;
3364  $template->delete_compiled_templates();
3365
3366  switch ($upgrade_status)
3367  {
3368    case 'ok':
3369      return sprintf(l10n('%s has been successfully updated.'), $extension_name);
3370
3371    case 'temp_path_error':
3372      return new PwgError(null, l10n('Can\'t create temporary file.'));
3373
3374    case 'dl_archive_error':
3375      return new PwgError(null, l10n('Can\'t download archive.'));
3376
3377    case 'archive_error':
3378      return new PwgError(null, l10n('Can\'t read or extract archive.'));
3379
3380    default:
3381      return new PwgError(null, sprintf(l10n('An error occured during extraction (%s).'), $upgrade_status));
3382  }
3383}
3384
3385function ws_extensions_ignoreupdate($params, &$service)
3386{
3387  global $conf;
3388
3389  define('IN_ADMIN', true);
3390  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3391
3392  if (!is_webmaster())
3393  {
3394    return new PwgError(401, 'Access denied');
3395  }
3396
3397  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3398  {
3399    return new PwgError(403, 'Invalid security token');
3400  }
3401
3402  $conf['updates_ignored'] = unserialize($conf['updates_ignored']);
3403
3404  // Reset ignored extension
3405  if ($params['reset'])
3406  {
3407    if (!empty($params['type']) and isset($conf['updates_ignored'][$params['type']]))
3408    {
3409      $conf['updates_ignored'][$params['type']] = array();
3410    }
3411    else
3412    {
3413      $conf['updates_ignored'] = array(
3414        'plugins'=>array(),
3415        'themes'=>array(),
3416        'languages'=>array()
3417      );
3418    }
3419    conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored'])));
3420    unset($_SESSION['extensions_need_update']);
3421    return true;
3422  }
3423
3424  if (empty($params['id']) or empty($params['type']) or !in_array($params['type'], array('plugins', 'themes', 'languages')))
3425  {
3426    return new PwgError(403, 'Invalid parameters');
3427  }
3428
3429  // Add or remove extension from ignore list
3430  if (!in_array($params['id'], $conf['updates_ignored'][$params['type']]))
3431  {
3432    array_push($conf['updates_ignored'][$params['type']], $params['id']);
3433  }
3434  conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored'])));
3435  unset($_SESSION['extensions_need_update']);
3436  return true;
3437}
3438
3439function ws_extensions_checkupdates($params, &$service)
3440{
3441  global $conf;
3442
3443  define('IN_ADMIN', true);
3444  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3445  include_once(PHPWG_ROOT_PATH.'admin/include/updates.class.php');
3446  $update = new updates();
3447
3448  if (!is_admin())
3449  {
3450    return new PwgError(401, 'Access denied');
3451  }
3452
3453  $result = array();
3454
3455  if (!isset($_SESSION['need_update']))
3456    $update->check_piwigo_upgrade();
3457
3458  $result['piwigo_need_update'] = $_SESSION['need_update'];
3459
3460  $conf['updates_ignored'] = unserialize($conf['updates_ignored']);
3461
3462  if (!isset($_SESSION['extensions_need_update']))
3463    $update->check_extensions();
3464  else
3465    $update->check_updated_extensions();
3466
3467  if (!is_array($_SESSION['extensions_need_update']))
3468    $result['ext_need_update'] = null;
3469  else
3470    $result['ext_need_update'] = !empty($_SESSION['extensions_need_update']);
3471
3472  return $result;
3473}
3474?>
Note: See TracBrowser for help on using the repository browser.