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

Last change on this file since 14498 was 14143, checked in by rvelices, 12 years ago

bug 2615 php notice in calendar amd web service
multisize improve handling of cases where the original is smaller than a requested derivative, but rotation/watermarking is required

  • Property svn:eol-style set to LF
File size: 83.7 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2012 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  $src_image = new SrcImage($image_row);
152
153  global $user;
154  if ($user['enabled_high'])
155  {
156    $ret['element_url'] = get_element_url($image_row);
157  }
158
159  $derivatives = DerivativeImage::get_all($src_image);
160  $derivatives_arr = array();
161  foreach($derivatives as $type=>$derivative)
162  {
163    $size = $derivative->get_size();
164    $size != null or $size=array(null,null);
165    $derivatives_arr[$type] = array('url' => $derivative->get_url(), 'width'=>$size[0], 'height'=>$size[1] );
166  }
167  $ret['derivatives'] = $derivatives_arr;;
168  return $ret;
169}
170
171/**
172 * returns an array of image attributes that are to be encoded as xml attributes
173 * instead of xml elements
174 */
175function ws_std_get_image_xml_attributes()
176{
177  return array(
178    'id','element_url', 'file','width','height','hit','date_available','date_creation'
179    );
180}
181
182function ws_getMissingDerivatives($params, &$service)
183{
184  if (!is_admin())
185  {
186    return new PwgError(403, 'Forbidden');
187  }
188
189  if ( empty($params['types']) )
190  {
191    $types = array_keys(ImageStdParams::get_defined_type_map());
192  }
193  else
194  {
195    $types = array_intersect(array_keys(ImageStdParams::get_defined_type_map()), $params['types']);
196    if (count($types)==0)
197    {
198      return new PwgError(WS_ERR_INVALID_PARAM, "Invalid types");
199    }
200  }
201
202  if ( ($max_urls = intval($params['max_urls'])) <= 0)
203  {
204    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid max_urls");
205  }
206
207  list($max_id, $image_count) = pwg_db_fetch_row( pwg_query('SELECT MAX(id)+1, COUNT(*) FROM '.IMAGES_TABLE) );
208  $start_id = intval($params['prev_page']);
209  if ($start_id<=0)
210  {
211    $start_id = $max_id;
212  }
213
214  $uid = '&b='.time();
215  global $conf;
216  $conf['question_mark_in_urls'] = $conf['php_extension_in_urls'] = true;
217  $conf['derivative_url_style']=2; //script
218
219  $qlimit = min(5000, ceil(max($image_count/500, $max_urls/count($types))));
220  $where_clauses = ws_std_image_sql_filter( $params, '' );
221  $where_clauses[] = 'id<start_id';
222  if ( !empty($params['ids']) )
223  {
224    $where_clauses[] = 'id IN ('.implode(',',$params['ids']).')';
225  }
226
227  $query_model = 'SELECT id, path, representative_ext, width,height,rotation
228  FROM '.IMAGES_TABLE.'
229  WHERE '.implode(' AND ', $where_clauses).'
230  ORDER BY id DESC
231  LIMIT '.$qlimit;
232
233  $urls=array();
234  do
235  {
236    $result = pwg_query( str_replace('start_id', $start_id, $query_model));
237    $is_last = pwg_db_num_rows($result) < $qlimit;
238    while ($row=pwg_db_fetch_assoc($result))
239    {
240      $start_id = $row['id'];
241      $src_image = new SrcImage($row);
242      if ($src_image->is_mimetype())
243        continue;
244      foreach($types as $type)
245      {
246        $derivative = new DerivativeImage($type, $src_image);
247        if ($type != $derivative->get_type())
248          continue;
249        if (@filemtime($derivative->get_path())===false)
250        {
251          $urls[] = $derivative->get_url().$uid;
252        }
253      }
254      if (count($urls)>=$max_urls && !$is_last)
255        break;
256    }
257    if ($is_last)
258    {
259      $start_id = 0;
260    }
261  }while (count($urls)<$max_urls && $start_id);
262
263  $ret = array();
264  if ($start_id)
265  {
266    $ret['next_page']=$start_id;
267  }
268  $ret['urls']=$urls;
269  return $ret;
270}
271
272/**
273 * returns PWG version (web service method)
274 */
275function ws_getVersion($params, &$service)
276{
277  global $conf;
278  if ($conf['show_version'] or is_admin() )
279    return PHPWG_VERSION;
280  else
281    return new PwgError(403, 'Forbidden');
282}
283
284/**
285 * returns general informations (web service method)
286 */
287function ws_getInfos($params, &$service)
288{
289  if (!is_admin())
290  {
291    return new PwgError(403, 'Forbidden');
292  }
293
294  $infos['version'] = PHPWG_VERSION;
295
296  $query = 'SELECT COUNT(*) FROM '.IMAGES_TABLE.';';
297  list($infos['nb_elements']) = pwg_db_fetch_row(pwg_query($query));
298
299  $query = 'SELECT COUNT(*) FROM '.CATEGORIES_TABLE.';';
300  list($infos['nb_categories']) = pwg_db_fetch_row(pwg_query($query));
301
302  $query = 'SELECT COUNT(*) FROM '.CATEGORIES_TABLE.' WHERE dir IS NULL;';
303  list($infos['nb_virtual']) = pwg_db_fetch_row(pwg_query($query));
304
305  $query = 'SELECT COUNT(*) FROM '.CATEGORIES_TABLE.' WHERE dir IS NOT NULL;';
306  list($infos['nb_physical']) = pwg_db_fetch_row(pwg_query($query));
307
308  $query = 'SELECT COUNT(*) FROM '.IMAGE_CATEGORY_TABLE.';';
309  list($infos['nb_image_category']) = pwg_db_fetch_row(pwg_query($query));
310
311  $query = 'SELECT COUNT(*) FROM '.TAGS_TABLE.';';
312  list($infos['nb_tags']) = pwg_db_fetch_row(pwg_query($query));
313
314  $query = 'SELECT COUNT(*) FROM '.IMAGE_TAG_TABLE.';';
315  list($infos['nb_image_tag']) = pwg_db_fetch_row(pwg_query($query));
316
317  $query = 'SELECT COUNT(*) FROM '.USERS_TABLE.';';
318  list($infos['nb_users']) = pwg_db_fetch_row(pwg_query($query));
319
320  $query = 'SELECT COUNT(*) FROM '.GROUPS_TABLE.';';
321  list($infos['nb_groups']) = pwg_db_fetch_row(pwg_query($query));
322
323  $query = 'SELECT COUNT(*) FROM '.COMMENTS_TABLE.';';
324  list($infos['nb_comments']) = pwg_db_fetch_row(pwg_query($query));
325
326  // first element
327  if ($infos['nb_elements'] > 0)
328  {
329    $query = 'SELECT MIN(date_available) FROM '.IMAGES_TABLE.';';
330    list($infos['first_date']) = pwg_db_fetch_row(pwg_query($query));
331  }
332
333  // unvalidated comments
334  if ($infos['nb_comments'] > 0)
335  {
336    $query = 'SELECT COUNT(*) FROM '.COMMENTS_TABLE.' WHERE validated=\'false\';';
337    list($infos['nb_unvalidated_comments']) = pwg_db_fetch_row(pwg_query($query));
338  }
339
340  foreach ($infos as $name => $value)
341  {
342    $output[] = array(
343      'name' => $name,
344      'value' => $value,
345    );
346  }
347
348  return array('infos' => new PwgNamedArray($output, 'item'));
349}
350
351function ws_caddie_add($params, &$service)
352{
353  if (!is_admin())
354  {
355    return new PwgError(401, 'Access denied');
356  }
357  $params['image_id'] = array_map( 'intval',$params['image_id'] );
358  if ( empty($params['image_id']) )
359  {
360    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
361  }
362  global $user;
363  $query = '
364SELECT id
365  FROM '.IMAGES_TABLE.' LEFT JOIN '.CADDIE_TABLE.' ON id=element_id AND user_id='.$user['id'].'
366  WHERE id IN ('.implode(',',$params['image_id']).')
367    AND element_id IS NULL';
368  $datas = array();
369  foreach ( array_from_query($query, 'id') as $id )
370  {
371    array_push($datas, array('element_id'=>$id, 'user_id'=>$user['id']) );
372  }
373  if (count($datas))
374  {
375    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
376    mass_inserts(CADDIE_TABLE, array('element_id','user_id'), $datas);
377  }
378  return count($datas);
379}
380
381/**
382 * returns images per category (web service method)
383 */
384function ws_categories_getImages($params, &$service)
385{
386  @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
387  global $user, $conf;
388
389  $images = array();
390
391  //------------------------------------------------- get the related categories
392  $where_clauses = array();
393  foreach($params['cat_id'] as $cat_id)
394  {
395    $cat_id = (int)$cat_id;
396    if ($cat_id<=0)
397      continue;
398    if ($params['recursive'])
399    {
400      $where_clauses[] = 'uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.$cat_id.'(,|$)\'';
401    }
402    else
403    {
404      $where_clauses[] = 'id='.$cat_id;
405    }
406  }
407  if (!empty($where_clauses))
408  {
409    $where_clauses = array( '('.
410    implode('
411    OR ', $where_clauses) . ')'
412      );
413  }
414  $where_clauses[] = get_sql_condition_FandF(
415        array('forbidden_categories' => 'id'),
416        NULL, true
417      );
418
419  $query = '
420SELECT id, name, permalink, image_order
421  FROM '.CATEGORIES_TABLE.'
422  WHERE '. implode('
423    AND ', $where_clauses);
424  $result = pwg_query($query);
425  $cats = array();
426  while ($row = pwg_db_fetch_assoc($result))
427  {
428    $row['id'] = (int)$row['id'];
429    $cats[ $row['id'] ] = $row;
430  }
431
432  //-------------------------------------------------------- get the images
433  if ( !empty($cats) )
434  {
435    $where_clauses = ws_std_image_sql_filter( $params, 'i.' );
436    $where_clauses[] = 'category_id IN ('
437      .implode(',', array_keys($cats) )
438      .')';
439    $where_clauses[] = get_sql_condition_FandF( array(
440          'visible_images' => 'i.id'
441        ), null, true
442      );
443
444    $order_by = ws_std_image_sql_order($params, 'i.');
445    if ( empty($order_by)
446          and count($params['cat_id'])==1
447          and isset($cats[ $params['cat_id'][0] ]['image_order'])
448        )
449    {
450      $order_by = $cats[ $params['cat_id'][0] ]['image_order'];
451    }
452    $order_by = empty($order_by) ? $conf['order_by'] : 'ORDER BY '.$order_by;
453
454    $query = '
455SELECT i.*, GROUP_CONCAT(category_id) AS cat_ids
456  FROM '.IMAGES_TABLE.' i
457    INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON i.id=image_id
458  WHERE '. implode('
459    AND ', $where_clauses).'
460GROUP BY i.id
461'.$order_by.'
462LIMIT '.(int)$params['per_page'].' OFFSET '.(int)($params['per_page']*$params['page']);
463
464    $result = pwg_query($query);
465    while ($row = pwg_db_fetch_assoc($result))
466    {
467      $image = array();
468      foreach ( array('id', 'width', 'height', 'hit') as $k )
469      {
470        if (isset($row[$k]))
471        {
472          $image[$k] = (int)$row[$k];
473        }
474      }
475      foreach ( array('file', 'name', 'comment', 'date_creation', 'date_available') as $k )
476      {
477        $image[$k] = $row[$k];
478      }
479      $image = array_merge( $image, ws_std_get_urls($row) );
480
481      $image_cats = array();
482      foreach ( explode(',', $row['cat_ids']) as $cat_id )
483      {
484        $url = make_index_url(
485                array(
486                  'category' => $cats[$cat_id],
487                  )
488                );
489        $page_url = make_picture_url(
490                array(
491                  'category' => $cats[$cat_id],
492                  'image_id' => $row['id'],
493                  'image_file' => $row['file'],
494                  )
495                );
496        array_push( $image_cats,  array(
497              WS_XML_ATTRIBUTES => array (
498                  'id' => (int)$cat_id,
499                  'url' => $url,
500                  'page_url' => $page_url,
501                )
502            )
503          );
504      }
505
506      $image['categories'] = new PwgNamedArray(
507            $image_cats,'category', array('id','url','page_url')
508          );
509      array_push($images, $image);
510    }
511  }
512
513  return array( 'images' =>
514    array (
515      WS_XML_ATTRIBUTES =>
516        array(
517            'page' => $params['page'],
518            'per_page' => $params['per_page'],
519            'count' => count($images)
520          ),
521       WS_XML_CONTENT => new PwgNamedArray($images, 'image',
522          ws_std_get_image_xml_attributes() )
523      )
524    );
525}
526
527
528/**
529 * create a tree from a flat list of categories, no recursivity for high speed
530 */
531function categories_flatlist_to_tree($categories)
532{
533  $tree = array();
534  $key_of_cat = array();
535
536  foreach ($categories as $key => &$node)
537  {
538    $key_of_cat[$node['id']] = $key;
539
540    if (!isset($node['id_uppercat']))
541    {
542      $tree[$key] = &$node;
543    }
544    else
545    {
546      if (!isset($categories[ $key_of_cat[ $node['id_uppercat'] ] ]['sub_categories']))
547      {
548        $categories[ $key_of_cat[ $node['id_uppercat'] ] ]['sub_categories'] = array();
549      }
550
551      $categories[ $key_of_cat[ $node['id_uppercat'] ] ]['sub_categories'][$key] = &$node;
552    }
553  }
554
555  return $tree;
556}
557
558/**
559 * returns a list of categories (web service method)
560 */
561function ws_categories_getList($params, &$service)
562{
563  global $user,$conf;
564
565  if ($params['tree_output'])
566  {
567    if (!isset($_GET['format']) or !in_array($_GET['format'], array('php', 'json')))
568    {
569      // the algorithm used to build a tree from a flat list of categories
570      // keeps original array keys, which is not compatible with
571      // PwgNamedArray.
572      //
573      // PwgNamedArray is useful to define which data is an attribute and
574      // which is an element in the XML output. The "hierarchy" output is
575      // only compatible with json/php output.
576
577      return new PwgError(405, "The tree_output option is only compatible with json/php output formats");
578    }
579  }
580
581  $where = array('1=1');
582  $join_type = 'INNER';
583  $join_user = $user['id'];
584
585  if (!$params['recursive'])
586  {
587    if ($params['cat_id']>0)
588      $where[] = '(id_uppercat='.(int)($params['cat_id']).'
589    OR id='.(int)($params['cat_id']).')';
590    else
591      $where[] = 'id_uppercat IS NULL';
592  }
593  else if ($params['cat_id']>0)
594  {
595    $where[] = 'uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.
596      (int)($params['cat_id'])
597      .'(,|$)\'';
598  }
599
600  if ($params['public'])
601  {
602    $where[] = 'status = "public"';
603    $where[] = 'visible = "true"';
604
605    $join_user = $conf['guest_id'];
606  }
607  elseif (is_admin())
608  {
609    // in this very specific case, we don't want to hide empty
610    // categories. Function calculate_permissions will only return
611    // categories that are either locked or private and not permitted
612    //
613    // calculate_permissions does not consider empty categories as forbidden
614    $forbidden_categories = calculate_permissions($user['id'], $user['status']);
615    $where[]= 'id NOT IN ('.$forbidden_categories.')';
616    $join_type = 'LEFT';
617  }
618
619  $query = '
620SELECT id, name, permalink, uppercats, global_rank, id_uppercat,
621    comment,
622    nb_images, count_images AS total_nb_images,
623    representative_picture_id, user_representative_picture_id, count_images, count_categories,
624    date_last, max_date_last, count_categories AS nb_categories
625  FROM '.CATEGORIES_TABLE.'
626   '.$join_type.' JOIN '.USER_CACHE_CATEGORIES_TABLE.' ON id=cat_id AND user_id='.$join_user.'
627  WHERE '. implode('
628    AND ', $where);
629
630  $result = pwg_query($query);
631
632  // management of the album thumbnail -- starts here
633  $image_ids = array();
634  $categories = array();
635  $user_representative_updates_for = array();
636  // management of the album thumbnail -- stops here
637
638  $cats = array();
639  while ($row = pwg_db_fetch_assoc($result))
640  {
641    $row['url'] = make_index_url(
642        array(
643          'category' => $row
644          )
645      );
646    foreach( array('id','nb_images','total_nb_images','nb_categories') as $key)
647    {
648      $row[$key] = (int)$row[$key];
649    }
650
651    if ($params['fullname'])
652    {
653      $row['name'] = strip_tags(get_cat_display_name_cache($row['uppercats'], null, false));
654    }
655    else
656    {
657      $row['name'] = strip_tags(
658        trigger_event(
659          'render_category_name',
660          $row['name'],
661          'ws_categories_getList'
662          )
663        );
664    }
665
666    $row['comment'] = strip_tags(
667      trigger_event(
668        'render_category_description',
669        $row['comment'],
670        'ws_categories_getList'
671        )
672      );
673
674    // management of the album thumbnail -- starts here
675    //
676    // on branch 2.3, the algorithm is duplicated from
677    // include/category_cats, but we should use a common code for Piwigo 2.4
678    //
679    // warning : if the API method is called with $params['public'], the
680    // album thumbnail may be not accurate. The thumbnail can be viewed by
681    // the connected user, but maybe not by the guest. Changing the
682    // filtering method would be too complicated for now. We will simply
683    // avoid to persist the user_representative_picture_id in the database
684    // if $params['public']
685    if (!empty($row['user_representative_picture_id']))
686    {
687      $image_id = $row['user_representative_picture_id'];
688    }
689    else if (!empty($row['representative_picture_id']))
690    { // if a representative picture is set, it has priority
691      $image_id = $row['representative_picture_id'];
692    }
693    else if ($conf['allow_random_representative'])
694    {
695      // searching a random representant among elements in sub-categories
696      $image_id = get_random_image_in_category($row);
697    }
698    else
699    { // searching a random representant among representant of sub-categories
700      if ($row['count_categories']>0 and $row['count_images']>0)
701      {
702        $query = '
703  SELECT representative_picture_id
704    FROM '.CATEGORIES_TABLE.' INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
705    ON id = cat_id and user_id = '.$user['id'].'
706    WHERE uppercats LIKE \''.$row['uppercats'].',%\'
707      AND representative_picture_id IS NOT NULL'
708          .get_sql_condition_FandF
709          (
710            array
711            (
712              'visible_categories' => 'id',
713              ),
714            "\n  AND"
715            ).'
716    ORDER BY '.DB_RANDOM_FUNCTION.'()
717    LIMIT 1
718  ;';
719        $subresult = pwg_query($query);
720        if (pwg_db_num_rows($subresult) > 0)
721        {
722          list($image_id) = pwg_db_fetch_row($subresult);
723        }
724      }
725    }
726
727    if (isset($image_id))
728    {
729      if ($conf['representative_cache_on_subcats'] and $row['user_representative_picture_id'] != $image_id)
730      {
731        $user_representative_updates_for[ $user['id'].'#'.$row['id'] ] = $image_id;
732      }
733
734      $row['representative_picture_id'] = $image_id;
735      array_push($image_ids, $image_id);
736      array_push($categories, $row);
737    }
738    unset($image_id);
739    // management of the album thumbnail -- stops here
740
741
742    array_push($cats, $row);
743  }
744  usort($cats, 'global_rank_compare');
745
746  // management of the album thumbnail -- starts here
747  if (count($categories) > 0)
748  {
749    $thumbnail_src_of = array();
750    $new_image_ids = array();
751
752    $query = '
753SELECT id, path, representative_ext, level
754  FROM '.IMAGES_TABLE.'
755  WHERE id IN ('.implode(',', $image_ids).')
756;';
757    $result = pwg_query($query);
758    while ($row = pwg_db_fetch_assoc($result))
759    {
760      if ($row['level'] <= $user['level'])
761      {
762        $thumbnail_src_of[$row['id']] = DerivativeImage::thumb_url($row);
763      }
764      else
765      {
766        // problem: we must not display the thumbnail of a photo which has a
767        // higher privacy level than user privacy level
768        //
769        // * what is the represented category?
770        // * find a random photo matching user permissions
771        // * register it at user_representative_picture_id
772        // * set it as the representative_picture_id for the category
773
774        foreach ($categories as &$category)
775        {
776          if ($row['id'] == $category['representative_picture_id'])
777          {
778            // searching a random representant among elements in sub-categories
779            $image_id = get_random_image_in_category($category);
780
781            if (isset($image_id) and !in_array($image_id, $image_ids))
782            {
783              array_push($new_image_ids, $image_id);
784            }
785
786            if ($conf['representative_cache_on_level'])
787            {
788              $user_representative_updates_for[ $user['id'].'#'.$category['id'] ] = $image_id;
789            }
790
791            $category['representative_picture_id'] = $image_id;
792          }
793        }
794        unset($category);
795      }
796    }
797
798    if (count($new_image_ids) > 0)
799    {
800      $query = '
801SELECT id, path, representative_ext
802  FROM '.IMAGES_TABLE.'
803  WHERE id IN ('.implode(',', $new_image_ids).')
804;';
805      $result = pwg_query($query);
806      while ($row = pwg_db_fetch_assoc($result))
807      {
808        $thumbnail_src_of[$row['id']] = DerivativeImage::thumb_url($row);
809      }
810    }
811  }
812
813  // compared to code in include/category_cats, we only persist the new
814  // user_representative if we have used $user['id'] and not the guest id,
815  // or else the real guest may see thumbnail that he should not
816  if (!$params['public'] and count($user_representative_updates_for))
817  {
818    $updates = array();
819
820    foreach ($user_representative_updates_for as $user_cat => $image_id)
821    {
822      list($user_id, $cat_id) = explode('#', $user_cat);
823
824      array_push(
825        $updates,
826        array(
827          'user_id' => $user_id,
828          'cat_id' => $cat_id,
829          'user_representative_picture_id' => $image_id,
830          )
831        );
832    }
833
834    mass_updates(
835      USER_CACHE_CATEGORIES_TABLE,
836      array(
837        'primary' => array('user_id', 'cat_id'),
838        'update'  => array('user_representative_picture_id')
839        ),
840      $updates
841      );
842  }
843
844  foreach ($cats as &$cat)
845  {
846    foreach ($categories as $category)
847    {
848      if ($category['id'] == $cat['id'])
849      {
850        $cat['tn_url'] = $thumbnail_src_of[$category['representative_picture_id']];
851      }
852    }
853    // we don't want them in the output
854    unset($cat['user_representative_picture_id']);
855    unset($cat['count_images']);
856    unset($cat['count_categories']);
857  }
858  unset($cat);
859  // management of the album thumbnail -- stops here
860
861  if ($params['tree_output'])
862  {
863    return categories_flatlist_to_tree($cats);
864  }
865  else
866  {
867    return array(
868      'categories' => new PwgNamedArray(
869        $cats,
870        'category',
871        array(
872          'id',
873          'url',
874          'nb_images',
875          'total_nb_images',
876          'nb_categories',
877          'date_last',
878          'max_date_last',
879          )
880        )
881      );
882  }
883}
884
885/**
886 * returns the list of categories as you can see them in administration (web
887 * service method).
888 *
889 * Only admin can run this method and permissions are not taken into
890 * account.
891 */
892function ws_categories_getAdminList($params, &$service)
893{
894  if (!is_admin())
895  {
896    return new PwgError(401, 'Access denied');
897  }
898
899  $query = '
900SELECT
901    category_id,
902    COUNT(*) AS counter
903  FROM '.IMAGE_CATEGORY_TABLE.'
904  GROUP BY category_id
905;';
906  $nb_images_of = simple_hash_from_query($query, 'category_id', 'counter');
907
908  $query = '
909SELECT
910    id,
911    name,
912    comment,
913    uppercats,
914    global_rank
915  FROM '.CATEGORIES_TABLE.'
916;';
917  $result = pwg_query($query);
918  $cats = array();
919
920  while ($row = pwg_db_fetch_assoc($result))
921  {
922    $id = $row['id'];
923    $row['nb_images'] = isset($nb_images_of[$id]) ? $nb_images_of[$id] : 0;
924    $row['name'] = strip_tags(
925      trigger_event(
926        'render_category_name',
927        $row['name'],
928        'ws_categories_getAdminList'
929        )
930      );
931    $row['comment'] = strip_tags(
932      trigger_event(
933        'render_category_description',
934        $row['comment'],
935        'ws_categories_getAdminList'
936        )
937      );
938    array_push($cats, $row);
939  }
940
941  usort($cats, 'global_rank_compare');
942  return array(
943    'categories' => new PwgNamedArray(
944      $cats,
945      'category',
946      array(
947        'id',
948        'nb_images',
949        'name',
950        'uppercats',
951        'global_rank',
952        )
953      )
954    );
955}
956
957/**
958 * returns detailed information for an element (web service method)
959 */
960function ws_images_addComment($params, &$service)
961{
962  if (!$service->isPost())
963  {
964    return new PwgError(405, "This method requires HTTP POST");
965  }
966  $params['image_id'] = (int)$params['image_id'];
967  $query = '
968SELECT DISTINCT image_id
969  FROM '.IMAGE_CATEGORY_TABLE.' INNER JOIN '.CATEGORIES_TABLE.' ON category_id=id
970  WHERE commentable="true"
971    AND image_id='.$params['image_id'].
972    get_sql_condition_FandF(
973      array(
974        'forbidden_categories' => 'id',
975        'visible_categories' => 'id',
976        'visible_images' => 'image_id'
977      ),
978      ' AND'
979    );
980  if ( !pwg_db_num_rows( pwg_query( $query ) ) )
981  {
982    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
983  }
984
985  $comm = array(
986    'author' => trim( $params['author'] ),
987    'content' => trim( $params['content'] ),
988    'image_id' => $params['image_id'],
989   );
990
991  include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
992
993  $comment_action = insert_user_comment(
994      $comm, $params['key'], $infos
995    );
996
997  switch ($comment_action)
998  {
999    case 'reject':
1000      array_push($infos, l10n('Your comment has NOT been registered because it did not pass the validation rules') );
1001      return new PwgError(403, implode("; ", $infos) );
1002    case 'validate':
1003    case 'moderate':
1004      $ret = array(
1005          'id' => $comm['id'],
1006          'validation' => $comment_action=='validate',
1007        );
1008      return new PwgNamedStruct(
1009          'comment',
1010          $ret,
1011          null, array()
1012        );
1013    default:
1014      return new PwgError(500, "Unknown comment action ".$comment_action );
1015  }
1016}
1017
1018/**
1019 * returns detailed information for an element (web service method)
1020 */
1021function ws_images_getInfo($params, &$service)
1022{
1023  @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
1024  global $user, $conf;
1025  $params['image_id'] = (int)$params['image_id'];
1026  if ( $params['image_id']<=0 )
1027  {
1028    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1029  }
1030
1031  $query='
1032SELECT * FROM '.IMAGES_TABLE.'
1033  WHERE id='.$params['image_id'].
1034    get_sql_condition_FandF(
1035      array('visible_images' => 'id'),
1036      ' AND'
1037    ).'
1038LIMIT 1';
1039
1040  $image_row = pwg_db_fetch_assoc(pwg_query($query));
1041  if ($image_row==null)
1042  {
1043    return new PwgError(404, "image_id not found");
1044  }
1045  $image_row = array_merge( $image_row, ws_std_get_urls($image_row) );
1046
1047  //-------------------------------------------------------- related categories
1048  $query = '
1049SELECT id, name, permalink, uppercats, global_rank, commentable
1050  FROM '.IMAGE_CATEGORY_TABLE.'
1051    INNER JOIN '.CATEGORIES_TABLE.' ON category_id = id
1052  WHERE image_id = '.$image_row['id'].
1053  get_sql_condition_FandF(
1054      array( 'forbidden_categories' => 'category_id' ),
1055      ' AND'
1056    ).'
1057;';
1058  $result = pwg_query($query);
1059  $is_commentable = false;
1060  $related_categories = array();
1061  while ($row = pwg_db_fetch_assoc($result))
1062  {
1063    if ($row['commentable']=='true')
1064    {
1065      $is_commentable = true;
1066    }
1067    unset($row['commentable']);
1068    $row['url'] = make_index_url(
1069        array(
1070          'category' => $row
1071          )
1072      );
1073
1074    $row['page_url'] = make_picture_url(
1075        array(
1076          'image_id' => $image_row['id'],
1077          'image_file' => $image_row['file'],
1078          'category' => $row
1079          )
1080      );
1081    $row['id']=(int)$row['id'];
1082    array_push($related_categories, $row);
1083  }
1084  usort($related_categories, 'global_rank_compare');
1085  if ( empty($related_categories) )
1086  {
1087    return new PwgError(401, 'Access denied');
1088  }
1089
1090  //-------------------------------------------------------------- related tags
1091  $related_tags = get_common_tags( array($image_row['id']), -1 );
1092  foreach( $related_tags as $i=>$tag)
1093  {
1094    $tag['url'] = make_index_url(
1095        array(
1096          'tags' => array($tag)
1097          )
1098      );
1099    $tag['page_url'] = make_picture_url(
1100        array(
1101          'image_id' => $image_row['id'],
1102          'image_file' => $image_row['file'],
1103          'tags' => array($tag),
1104          )
1105      );
1106    unset($tag['counter']);
1107    $tag['id']=(int)$tag['id'];
1108    $related_tags[$i]=$tag;
1109  }
1110  //------------------------------------------------------------- related rates
1111        $rating = array('score'=>$image_row['rating_score'], 'count'=>0, 'average'=>null);
1112        if (isset($rating['score']))
1113        {
1114                $query = '
1115SELECT COUNT(rate) AS count
1116     , ROUND(AVG(rate),2) AS average
1117  FROM '.RATE_TABLE.'
1118  WHERE element_id = '.$image_row['id'].'
1119;';
1120                $row = pwg_db_fetch_assoc(pwg_query($query));
1121                $rating['score'] = (float)$rating['score'];
1122                $rating['average'] = (float)$row['average'];
1123                $rating['count'] = (int)$row['count'];
1124        }
1125
1126  //---------------------------------------------------------- related comments
1127  $related_comments = array();
1128
1129  $where_comments = 'image_id = '.$image_row['id'];
1130  if ( !is_admin() )
1131  {
1132    $where_comments .= '
1133    AND validated="true"';
1134  }
1135
1136  $query = '
1137SELECT COUNT(id) AS nb_comments
1138  FROM '.COMMENTS_TABLE.'
1139  WHERE '.$where_comments;
1140  list($nb_comments) = array_from_query($query, 'nb_comments');
1141  $nb_comments = (int)$nb_comments;
1142
1143  if ( $nb_comments>0 and $params['comments_per_page']>0 )
1144  {
1145    $query = '
1146SELECT id, date, author, content
1147  FROM '.COMMENTS_TABLE.'
1148  WHERE '.$where_comments.'
1149  ORDER BY date
1150  LIMIT '.(int)$params['comments_per_page'].
1151    ' OFFSET '.(int)($params['comments_per_page']*$params['comments_page']);
1152
1153    $result = pwg_query($query);
1154    while ($row = pwg_db_fetch_assoc($result))
1155    {
1156      $row['id']=(int)$row['id'];
1157      array_push($related_comments, $row);
1158    }
1159  }
1160
1161  $comment_post_data = null;
1162  if ($is_commentable and
1163      (!is_a_guest()
1164        or (is_a_guest() and $conf['comments_forall'] )
1165      )
1166      )
1167  {
1168    $comment_post_data['author'] = stripslashes($user['username']);
1169    $comment_post_data['key'] = get_ephemeral_key(2, $params['image_id']);
1170  }
1171
1172  $ret = $image_row;
1173  foreach ( array('id','width','height','hit','filesize') as $k )
1174  {
1175    if (isset($ret[$k]))
1176    {
1177      $ret[$k] = (int)$ret[$k];
1178    }
1179  }
1180  foreach ( array('path', 'storage_category_id') as $k )
1181  {
1182    unset($ret[$k]);
1183  }
1184
1185  $ret['rates'] = array( WS_XML_ATTRIBUTES => $rating );
1186  $ret['categories'] = new PwgNamedArray($related_categories, 'category', array('id','url', 'page_url') );
1187  $ret['tags'] = new PwgNamedArray($related_tags, 'tag', array('id','url_name','url','name','page_url') );
1188  if ( isset($comment_post_data) )
1189  {
1190    $ret['comment_post'] = array( WS_XML_ATTRIBUTES => $comment_post_data );
1191  }
1192  $ret['comments'] = array(
1193     WS_XML_ATTRIBUTES =>
1194        array(
1195          'page' => $params['comments_page'],
1196          'per_page' => $params['comments_per_page'],
1197          'count' => count($related_comments),
1198          'nb_comments' => $nb_comments,
1199        ),
1200     WS_XML_CONTENT => new PwgNamedArray($related_comments, 'comment', array('id','date') )
1201      );
1202
1203  return new PwgNamedStruct('image',$ret, null, array('name','comment') );
1204}
1205
1206
1207/**
1208 * rates the image_id in the parameter
1209 */
1210function ws_images_Rate($params, &$service)
1211{
1212  $image_id = (int)$params['image_id'];
1213  $query = '
1214SELECT DISTINCT id FROM '.IMAGES_TABLE.'
1215  INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id=image_id
1216  WHERE id='.$image_id
1217  .get_sql_condition_FandF(
1218    array(
1219        'forbidden_categories' => 'category_id',
1220        'forbidden_images' => 'id',
1221      ),
1222    '    AND'
1223    ).'
1224    LIMIT 1';
1225  if ( pwg_db_num_rows( pwg_query($query) )==0 )
1226  {
1227    return new PwgError(404, "Invalid image_id or access denied" );
1228  }
1229  $rate = (int)$params['rate'];
1230  include_once(PHPWG_ROOT_PATH.'include/functions_rate.inc.php');
1231  $res = rate_picture( $image_id, $rate );
1232  if ($res==false)
1233  {
1234    global $conf;
1235    return new PwgError( 403, "Forbidden or rate not in ". implode(',',$conf['rate_items']));
1236  }
1237  return $res;
1238}
1239
1240
1241/**
1242 * returns a list of elements corresponding to a query search
1243 */
1244function ws_images_search($params, &$service)
1245{
1246  global $page;
1247  $images = array();
1248  include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' );
1249  include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
1250
1251  $where_clauses = ws_std_image_sql_filter( $params, 'i.' );
1252  $order_by = ws_std_image_sql_order($params, 'i.');
1253
1254  $super_order_by = false;
1255  if ( !empty($order_by) )
1256  {
1257    global $conf;
1258    $conf['order_by'] = 'ORDER BY '.$order_by;
1259    $super_order_by=true; // quick_search_result might be faster
1260  }
1261
1262  $search_result = get_quick_search_results($params['query'],
1263      $super_order_by,
1264      implode(',', $where_clauses)
1265    );
1266
1267  $image_ids = array_slice(
1268      $search_result['items'],
1269      $params['page']*$params['per_page'],
1270      $params['per_page']
1271    );
1272
1273  if ( count($image_ids) )
1274  {
1275    $query = '
1276SELECT * FROM '.IMAGES_TABLE.'
1277  WHERE id IN ('.implode(',', $image_ids).')';
1278
1279    $image_ids = array_flip($image_ids);
1280    $result = pwg_query($query);
1281    while ($row = pwg_db_fetch_assoc($result))
1282    {
1283      $image = array();
1284      foreach ( array('id', 'width', 'height', 'hit') as $k )
1285      {
1286        if (isset($row[$k]))
1287        {
1288          $image[$k] = (int)$row[$k];
1289        }
1290      }
1291      foreach ( array('file', 'name', 'comment', 'date_creation', 'date_available') as $k )
1292      {
1293        $image[$k] = $row[$k];
1294      }
1295      $image = array_merge( $image, ws_std_get_urls($row) );
1296      $images[$image_ids[$image['id']]] = $image;
1297    }
1298    ksort($images, SORT_NUMERIC);
1299    $images = array_values($images);
1300  }
1301
1302
1303  return array( 'images' =>
1304    array (
1305      WS_XML_ATTRIBUTES =>
1306        array(
1307            'page' => $params['page'],
1308            'per_page' => $params['per_page'],
1309            'count' => count($images)
1310          ),
1311       WS_XML_CONTENT => new PwgNamedArray($images, 'image',
1312          ws_std_get_image_xml_attributes() )
1313      )
1314    );
1315}
1316
1317function ws_images_setPrivacyLevel($params, &$service)
1318{
1319  if (!is_admin())
1320  {
1321    return new PwgError(401, 'Access denied');
1322  }
1323  if (!$service->isPost())
1324  {
1325    return new PwgError(405, "This method requires HTTP POST");
1326  }
1327  $params['image_id'] = array_map( 'intval',$params['image_id'] );
1328  if ( empty($params['image_id']) )
1329  {
1330    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1331  }
1332  global $conf;
1333  if ( !in_array( (int)$params['level'], $conf['available_permission_levels']) )
1334  {
1335    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid level");
1336  }
1337
1338  $query = '
1339UPDATE '.IMAGES_TABLE.'
1340  SET level='.(int)$params['level'].'
1341  WHERE id IN ('.implode(',',$params['image_id']).')';
1342  $result = pwg_query($query);
1343  $affected_rows = pwg_db_changes($result);
1344  if ($affected_rows)
1345  {
1346    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
1347    invalidate_user_cache();
1348  }
1349  return $affected_rows;
1350}
1351
1352function ws_images_setRank($params, &$service)
1353{
1354  if (!is_admin())
1355  {
1356    return new PwgError(401, 'Access denied');
1357  }
1358
1359  if (!$service->isPost())
1360  {
1361    return new PwgError(405, "This method requires HTTP POST");
1362  }
1363
1364  // is the image_id valid?
1365  $params['image_id'] = (int)$params['image_id'];
1366  if ($params['image_id'] <= 0)
1367  {
1368    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
1369  }
1370
1371  // is the category valid?
1372  $params['category_id'] = (int)$params['category_id'];
1373  if ($params['category_id'] <= 0)
1374  {
1375    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
1376  }
1377
1378  // is the rank valid?
1379  $params['rank'] = (int)$params['rank'];
1380  if ($params['rank'] <= 0)
1381  {
1382    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid rank");
1383  }
1384
1385  // does the image really exist?
1386  $query='
1387SELECT
1388    *
1389  FROM '.IMAGES_TABLE.'
1390  WHERE id = '.$params['image_id'].'
1391;';
1392
1393  $image_row = pwg_db_fetch_assoc(pwg_query($query));
1394  if ($image_row == null)
1395  {
1396    return new PwgError(404, "image_id not found");
1397  }
1398
1399  // is the image associated to this category?
1400  $query = '
1401SELECT
1402    image_id,
1403    category_id,
1404    rank
1405  FROM '.IMAGE_CATEGORY_TABLE.'
1406  WHERE image_id = '.$params['image_id'].'
1407    AND category_id = '.$params['category_id'].'
1408;';
1409  $category_row = pwg_db_fetch_assoc(pwg_query($query));
1410  if ($category_row == null)
1411  {
1412    return new PwgError(404, "This image is not associated to this category");
1413  }
1414
1415  // what is the current higher rank for this category?
1416  $query = '
1417SELECT
1418    MAX(rank) AS max_rank
1419  FROM '.IMAGE_CATEGORY_TABLE.'
1420  WHERE category_id = '.$params['category_id'].'
1421;';
1422  $result = pwg_query($query);
1423  $row = pwg_db_fetch_assoc($result);
1424
1425  if (is_numeric($row['max_rank']))
1426  {
1427    if ($params['rank'] > $row['max_rank'])
1428    {
1429      $params['rank'] = $row['max_rank'] + 1;
1430    }
1431  }
1432  else
1433  {
1434    $params['rank'] = 1;
1435  }
1436
1437  // update rank for all other photos in the same category
1438  $query = '
1439UPDATE '.IMAGE_CATEGORY_TABLE.'
1440  SET rank = rank + 1
1441  WHERE category_id = '.$params['category_id'].'
1442    AND rank IS NOT NULL
1443    AND rank >= '.$params['rank'].'
1444;';
1445  pwg_query($query);
1446
1447  // set the new rank for the photo
1448  $query = '
1449UPDATE '.IMAGE_CATEGORY_TABLE.'
1450  SET rank = '.$params['rank'].'
1451  WHERE image_id = '.$params['image_id'].'
1452    AND category_id = '.$params['category_id'].'
1453;';
1454  pwg_query($query);
1455
1456  // return data for client
1457  return array(
1458    'image_id' => $params['image_id'],
1459    'category_id' => $params['category_id'],
1460    'rank' => $params['rank'],
1461    );
1462}
1463
1464function ws_images_add_chunk($params, &$service)
1465{
1466  global $conf;
1467
1468  // data
1469  // original_sum
1470  // type {thumb, file, high}
1471  // position
1472
1473  if (!is_admin())
1474  {
1475    return new PwgError(401, 'Access denied');
1476  }
1477
1478  if (!$service->isPost())
1479  {
1480    return new PwgError(405, "This method requires HTTP POST");
1481  }
1482
1483  foreach ($params as $param_key => $param_value) {
1484    if ('data' == $param_key) {
1485      continue;
1486    }
1487
1488    ws_logfile(
1489      sprintf(
1490        '[ws_images_add_chunk] input param "%s" : "%s"',
1491        $param_key,
1492        is_null($param_value) ? 'NULL' : $param_value
1493        )
1494      );
1495  }
1496
1497  $upload_dir = $conf['upload_dir'].'/buffer';
1498
1499  // create the upload directory tree if not exists
1500  if (!is_dir($upload_dir)) {
1501    umask(0000);
1502    if (!@mkdir($upload_dir, 0777, true))
1503    {
1504      return new PwgError(500, 'error during buffer directory creation');
1505    }
1506  }
1507
1508  if (!is_writable($upload_dir))
1509  {
1510    // last chance to make the directory writable
1511    @chmod($upload_dir, 0777);
1512
1513    if (!is_writable($upload_dir))
1514    {
1515      return new PwgError(500, 'buffer directory has no write access');
1516    }
1517  }
1518
1519  secure_directory($upload_dir);
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  @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
2169  global $conf;
2170
2171  // first build all the tag_ids we are interested in
2172  $params['tag_id'] = array_map( 'intval',$params['tag_id'] );
2173  $tags = find_tags($params['tag_id'], $params['tag_url_name'], $params['tag_name']);
2174  $tags_by_id = array();
2175  foreach( $tags as $tag )
2176  {
2177    $tags['id'] = (int)$tag['id'];
2178    $tags_by_id[ $tag['id'] ] = $tag;
2179  }
2180  unset($tags);
2181  $tag_ids = array_keys($tags_by_id);
2182
2183
2184  $where_clauses = ws_std_image_sql_filter($params);
2185  if (!empty($where_clauses))
2186  {
2187    $where_clauses = implode( ' AND ', $where_clauses);
2188  }
2189  $image_ids = get_image_ids_for_tags(
2190    $tag_ids,
2191    $params['tag_mode_and'] ? 'AND' : 'OR',
2192    $where_clauses,
2193    ws_std_image_sql_order($params) );
2194
2195
2196  $image_ids = array_slice($image_ids, (int)($params['per_page']*$params['page']), (int)$params['per_page'] );
2197
2198  $image_tag_map = array();
2199  if ( !empty($image_ids) and !$params['tag_mode_and'] )
2200  { // build list of image ids with associated tags per image
2201    $query = '
2202SELECT image_id, GROUP_CONCAT(tag_id) AS tag_ids
2203  FROM '.IMAGE_TAG_TABLE.'
2204  WHERE tag_id IN ('.implode(',',$tag_ids).') AND image_id IN ('.implode(',',$image_ids).')
2205  GROUP BY image_id';
2206    $result = pwg_query($query);
2207    while ( $row=pwg_db_fetch_assoc($result) )
2208    {
2209      $row['image_id'] = (int)$row['image_id'];
2210      array_push( $image_ids, $row['image_id'] );
2211      $image_tag_map[ $row['image_id'] ] = explode(',', $row['tag_ids']);
2212    }
2213  }
2214
2215  $images = array();
2216  if (!empty($image_ids))
2217  {
2218    $rank_of = array_flip($image_ids);
2219    $result = pwg_query('
2220SELECT * FROM '.IMAGES_TABLE.'
2221  WHERE id IN ('.implode(',',$image_ids).')');
2222    while ($row = pwg_db_fetch_assoc($result))
2223    {
2224      $image = array();
2225      $image['rank'] = $rank_of[ $row['id'] ];
2226      foreach ( array('id', 'width', 'height', 'hit') as $k )
2227      {
2228        if (isset($row[$k]))
2229        {
2230          $image[$k] = (int)$row[$k];
2231        }
2232      }
2233      foreach ( array('file', 'name', 'comment', 'date_creation', 'date_available') as $k )
2234      {
2235        $image[$k] = $row[$k];
2236      }
2237      $image = array_merge( $image, ws_std_get_urls($row) );
2238
2239      $image_tag_ids = ($params['tag_mode_and']) ? $tag_ids : $image_tag_map[$image['id']];
2240      $image_tags = array();
2241      foreach ($image_tag_ids as $tag_id)
2242      {
2243        $url = make_index_url(
2244                 array(
2245                  'section'=>'tags',
2246                  'tags'=> array($tags_by_id[$tag_id])
2247                )
2248              );
2249        $page_url = make_picture_url(
2250                 array(
2251                  'section'=>'tags',
2252                  'tags'=> array($tags_by_id[$tag_id]),
2253                  'image_id' => $row['id'],
2254                  'image_file' => $row['file'],
2255                )
2256              );
2257        array_push($image_tags, array(
2258                'id' => (int)$tag_id,
2259                'url' => $url,
2260                'page_url' => $page_url,
2261              )
2262            );
2263      }
2264      $image['tags'] = new PwgNamedArray($image_tags, 'tag',
2265              array('id','url_name','url','page_url')
2266            );
2267      array_push($images, $image);
2268    }
2269    usort($images, 'rank_compare');
2270    unset($rank_of);
2271  }
2272
2273  return array( 'images' =>
2274    array (
2275      WS_XML_ATTRIBUTES =>
2276        array(
2277            'page' => $params['page'],
2278            'per_page' => $params['per_page'],
2279            'count' => count($images)
2280          ),
2281       WS_XML_CONTENT => new PwgNamedArray($images, 'image',
2282          ws_std_get_image_xml_attributes() )
2283      )
2284    );
2285}
2286
2287function ws_categories_add($params, &$service)
2288{
2289  if (!is_admin())
2290  {
2291    return new PwgError(401, 'Access denied');
2292  }
2293
2294  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2295
2296  $creation_output = create_virtual_category(
2297    $params['name'],
2298    $params['parent']
2299    );
2300
2301  if (isset($creation_output['error']))
2302  {
2303    return new PwgError(500, $creation_output['error']);
2304  }
2305
2306  invalidate_user_cache();
2307
2308  return $creation_output;
2309}
2310
2311function ws_tags_add($params, &$service)
2312{
2313  if (!is_admin())
2314  {
2315    return new PwgError(401, 'Access denied');
2316  }
2317
2318  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2319
2320  $creation_output = create_tag($params['name']);
2321
2322  if (isset($creation_output['error']))
2323  {
2324    return new PwgError(500, $creation_output['error']);
2325  }
2326
2327  return $creation_output;
2328}
2329
2330function ws_images_exist($params, &$service)
2331{
2332  ws_logfile(__FUNCTION__.' '.var_export($params, true));
2333
2334  global $conf;
2335
2336  if (!is_admin())
2337  {
2338    return new PwgError(401, 'Access denied');
2339  }
2340
2341  $split_pattern = '/[\s,;\|]/';
2342
2343  if ('md5sum' == $conf['uniqueness_mode'])
2344  {
2345    // search among photos the list of photos already added, based on md5sum
2346    // list
2347    $md5sums = preg_split(
2348      $split_pattern,
2349      $params['md5sum_list'],
2350      -1,
2351      PREG_SPLIT_NO_EMPTY
2352    );
2353
2354    $query = '
2355SELECT
2356    id,
2357    md5sum
2358  FROM '.IMAGES_TABLE.'
2359  WHERE md5sum IN (\''.implode("','", $md5sums).'\')
2360;';
2361    $id_of_md5 = simple_hash_from_query($query, 'md5sum', 'id');
2362
2363    $result = array();
2364
2365    foreach ($md5sums as $md5sum)
2366    {
2367      $result[$md5sum] = null;
2368      if (isset($id_of_md5[$md5sum]))
2369      {
2370        $result[$md5sum] = $id_of_md5[$md5sum];
2371      }
2372    }
2373  }
2374
2375  if ('filename' == $conf['uniqueness_mode'])
2376  {
2377    // search among photos the list of photos already added, based on
2378    // filename list
2379    $filenames = preg_split(
2380      $split_pattern,
2381      $params['filename_list'],
2382      -1,
2383      PREG_SPLIT_NO_EMPTY
2384    );
2385
2386    $query = '
2387SELECT
2388    id,
2389    file
2390  FROM '.IMAGES_TABLE.'
2391  WHERE file IN (\''.implode("','", $filenames).'\')
2392;';
2393    $id_of_filename = simple_hash_from_query($query, 'file', 'id');
2394
2395    $result = array();
2396
2397    foreach ($filenames as $filename)
2398    {
2399      $result[$filename] = null;
2400      if (isset($id_of_filename[$filename]))
2401      {
2402        $result[$filename] = $id_of_filename[$filename];
2403      }
2404    }
2405  }
2406
2407  return $result;
2408}
2409
2410function ws_images_checkFiles($params, &$service)
2411{
2412  ws_logfile(__FUNCTION__.', input :  '.var_export($params, true));
2413
2414  if (!is_admin())
2415  {
2416    return new PwgError(401, 'Access denied');
2417  }
2418
2419  // input parameters
2420  //
2421  // image_id
2422  // thumbnail_sum
2423  // file_sum
2424  // high_sum
2425
2426  $params['image_id'] = (int)$params['image_id'];
2427  if ($params['image_id'] <= 0)
2428  {
2429    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
2430  }
2431
2432  $query = '
2433SELECT
2434    path
2435  FROM '.IMAGES_TABLE.'
2436  WHERE id = '.$params['image_id'].'
2437;';
2438  $result = pwg_query($query);
2439  if (pwg_db_num_rows($result) == 0)
2440  {
2441    return new PwgError(404, "image_id not found");
2442  }
2443  list($path) = pwg_db_fetch_row($result);
2444
2445  $ret = array();
2446
2447  if (isset($params['thumbnail_sum']))
2448  {
2449    // We always say the thumbnail is equal to create no reaction on the
2450    // other side. Since Piwigo 2.4 and derivatives, the thumbnails and web
2451    // sizes are always generated by Piwigo
2452    $ret['thumbnail'] = 'equals';
2453  }
2454
2455  if (isset($params['high_sum']))
2456  {
2457    $ret['file'] = 'equals';
2458    $compare_type = 'high';
2459  }
2460  elseif (isset($params['file_sum']))
2461  {
2462    $compare_type = 'file';
2463  }
2464
2465  if (isset($compare_type))
2466  {
2467    ws_logfile(__FUNCTION__.', md5_file($path) = '.md5_file($path));
2468    if (md5_file($path) != $params[$compare_type.'_sum'])
2469    {
2470      $ret[$compare_type] = 'differs';
2471    }
2472    else
2473    {
2474      $ret[$compare_type] = 'equals';
2475    }
2476  }
2477
2478  ws_logfile(__FUNCTION__.', output :  '.var_export($ret, true));
2479
2480  return $ret;
2481}
2482
2483function ws_images_setInfo($params, &$service)
2484{
2485  global $conf;
2486  if (!is_admin())
2487  {
2488    return new PwgError(401, 'Access denied');
2489  }
2490
2491  if (!$service->isPost())
2492  {
2493    return new PwgError(405, "This method requires HTTP POST");
2494  }
2495
2496  $params['image_id'] = (int)$params['image_id'];
2497  if ($params['image_id'] <= 0)
2498  {
2499    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
2500  }
2501
2502  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2503
2504  $query='
2505SELECT *
2506  FROM '.IMAGES_TABLE.'
2507  WHERE id = '.$params['image_id'].'
2508;';
2509
2510  $image_row = pwg_db_fetch_assoc(pwg_query($query));
2511  if ($image_row == null)
2512  {
2513    return new PwgError(404, "image_id not found");
2514  }
2515
2516  // database registration
2517  $update = array();
2518
2519  $info_columns = array(
2520    'name',
2521    'author',
2522    'comment',
2523    'level',
2524    'date_creation',
2525    );
2526
2527  foreach ($info_columns as $key)
2528  {
2529    if (isset($params[$key]))
2530    {
2531      if ('fill_if_empty' == $params['single_value_mode'])
2532      {
2533        if (empty($image_row[$key]))
2534        {
2535          $update[$key] = $params[$key];
2536        }
2537      }
2538      elseif ('replace' == $params['single_value_mode'])
2539      {
2540        $update[$key] = $params[$key];
2541      }
2542      else
2543      {
2544        return new PwgError(
2545          500,
2546          '[ws_images_setInfo]'
2547          .' invalid parameter single_value_mode "'.$params['single_value_mode'].'"'
2548          .', possible values are {fill_if_empty, replace}.'
2549          );
2550      }
2551    }
2552  }
2553
2554  if (isset($params['file']))
2555  {
2556    if (!empty($image_row['storage_category_id']))
2557    {
2558      return new PwgError(500, '[ws_images_setInfo] updating "file" is forbidden on photos added by synchronization');
2559    }
2560
2561    $update['file'] = $params['file'];
2562  }
2563
2564  if (count(array_keys($update)) > 0)
2565  {
2566    $update['id'] = $params['image_id'];
2567
2568    mass_updates(
2569      IMAGES_TABLE,
2570      array(
2571        'primary' => array('id'),
2572        'update'  => array_diff(array_keys($update), array('id'))
2573        ),
2574      array($update)
2575      );
2576  }
2577
2578  if (isset($params['categories']))
2579  {
2580    ws_add_image_category_relations(
2581      $params['image_id'],
2582      $params['categories'],
2583      ('replace' == $params['multiple_value_mode'] ? true : false)
2584      );
2585  }
2586
2587  // and now, let's create tag associations
2588  if (isset($params['tag_ids']))
2589  {
2590    $tag_ids = explode(',', $params['tag_ids']);
2591
2592    if ('replace' == $params['multiple_value_mode'])
2593    {
2594      set_tags(
2595        $tag_ids,
2596        $params['image_id']
2597        );
2598    }
2599    elseif ('append' == $params['multiple_value_mode'])
2600    {
2601      add_tags(
2602        $tag_ids,
2603        array($params['image_id'])
2604        );
2605    }
2606    else
2607    {
2608      return new PwgError(
2609        500,
2610        '[ws_images_setInfo]'
2611        .' invalid parameter multiple_value_mode "'.$params['multiple_value_mode'].'"'
2612        .', possible values are {replace, append}.'
2613        );
2614    }
2615  }
2616
2617  invalidate_user_cache();
2618}
2619
2620function ws_images_delete($params, &$service)
2621{
2622  global $conf;
2623  if (!is_admin())
2624  {
2625    return new PwgError(401, 'Access denied');
2626  }
2627
2628  if (!$service->isPost())
2629  {
2630    return new PwgError(405, "This method requires HTTP POST");
2631  }
2632
2633  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
2634  {
2635    return new PwgError(403, 'Invalid security token');
2636  }
2637
2638  $params['image_id'] = preg_split(
2639    '/[\s,;\|]/',
2640    $params['image_id'],
2641    -1,
2642    PREG_SPLIT_NO_EMPTY
2643    );
2644  $params['image_id'] = array_map('intval', $params['image_id']);
2645
2646  $image_ids = array();
2647  foreach ($params['image_id'] as $image_id)
2648  {
2649    if ($image_id > 0)
2650    {
2651      array_push($image_ids, $image_id);
2652    }
2653  }
2654
2655  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2656  delete_elements($image_ids, true);
2657}
2658
2659function ws_add_image_category_relations($image_id, $categories_string, $replace_mode=false)
2660{
2661  // let's add links between the image and the categories
2662  //
2663  // $params['categories'] should look like 123,12;456,auto;789 which means:
2664  //
2665  // 1. associate with category 123 on rank 12
2666  // 2. associate with category 456 on automatic rank
2667  // 3. associate with category 789 on automatic rank
2668  $cat_ids = array();
2669  $rank_on_category = array();
2670  $search_current_ranks = false;
2671
2672  $tokens = explode(';', $categories_string);
2673  foreach ($tokens as $token)
2674  {
2675    @list($cat_id, $rank) = explode(',', $token);
2676
2677    if (!preg_match('/^\d+$/', $cat_id))
2678    {
2679      continue;
2680    }
2681
2682    array_push($cat_ids, $cat_id);
2683
2684    if (!isset($rank))
2685    {
2686      $rank = 'auto';
2687    }
2688    $rank_on_category[$cat_id] = $rank;
2689
2690    if ($rank == 'auto')
2691    {
2692      $search_current_ranks = true;
2693    }
2694  }
2695
2696  $cat_ids = array_unique($cat_ids);
2697
2698  if (count($cat_ids) == 0)
2699  {
2700    return new PwgError(
2701      500,
2702      '[ws_add_image_category_relations] there is no category defined in "'.$categories_string.'"'
2703      );
2704  }
2705
2706  $query = '
2707SELECT
2708    id
2709  FROM '.CATEGORIES_TABLE.'
2710  WHERE id IN ('.implode(',', $cat_ids).')
2711;';
2712  $db_cat_ids = array_from_query($query, 'id');
2713
2714  $unknown_cat_ids = array_diff($cat_ids, $db_cat_ids);
2715  if (count($unknown_cat_ids) != 0)
2716  {
2717    return new PwgError(
2718      500,
2719      '[ws_add_image_category_relations] the following categories are unknown: '.implode(', ', $unknown_cat_ids)
2720      );
2721  }
2722
2723  $to_update_cat_ids = array();
2724
2725  // in case of replace mode, we first check the existing associations
2726  $query = '
2727SELECT
2728    category_id
2729  FROM '.IMAGE_CATEGORY_TABLE.'
2730  WHERE image_id = '.$image_id.'
2731;';
2732  $existing_cat_ids = array_from_query($query, 'category_id');
2733
2734  if ($replace_mode)
2735  {
2736    $to_remove_cat_ids = array_diff($existing_cat_ids, $cat_ids);
2737    if (count($to_remove_cat_ids) > 0)
2738    {
2739      $query = '
2740DELETE
2741  FROM '.IMAGE_CATEGORY_TABLE.'
2742  WHERE image_id = '.$image_id.'
2743    AND category_id IN ('.implode(', ', $to_remove_cat_ids).')
2744;';
2745      pwg_query($query);
2746      update_category($to_remove_cat_ids);
2747    }
2748  }
2749
2750  $new_cat_ids = array_diff($cat_ids, $existing_cat_ids);
2751  if (count($new_cat_ids) == 0)
2752  {
2753    return true;
2754  }
2755
2756  if ($search_current_ranks)
2757  {
2758    $query = '
2759SELECT
2760    category_id,
2761    MAX(rank) AS max_rank
2762  FROM '.IMAGE_CATEGORY_TABLE.'
2763  WHERE rank IS NOT NULL
2764    AND category_id IN ('.implode(',', $new_cat_ids).')
2765  GROUP BY category_id
2766;';
2767    $current_rank_of = simple_hash_from_query(
2768      $query,
2769      'category_id',
2770      'max_rank'
2771      );
2772
2773    foreach ($new_cat_ids as $cat_id)
2774    {
2775      if (!isset($current_rank_of[$cat_id]))
2776      {
2777        $current_rank_of[$cat_id] = 0;
2778      }
2779
2780      if ('auto' == $rank_on_category[$cat_id])
2781      {
2782        $rank_on_category[$cat_id] = $current_rank_of[$cat_id] + 1;
2783      }
2784    }
2785  }
2786
2787  $inserts = array();
2788
2789  foreach ($new_cat_ids as $cat_id)
2790  {
2791    array_push(
2792      $inserts,
2793      array(
2794        'image_id' => $image_id,
2795        'category_id' => $cat_id,
2796        'rank' => $rank_on_category[$cat_id],
2797        )
2798      );
2799  }
2800
2801  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2802  mass_inserts(
2803    IMAGE_CATEGORY_TABLE,
2804    array_keys($inserts[0]),
2805    $inserts
2806    );
2807
2808  update_category($new_cat_ids);
2809}
2810
2811function ws_categories_setInfo($params, &$service)
2812{
2813  global $conf;
2814  if (!is_admin())
2815  {
2816    return new PwgError(401, 'Access denied');
2817  }
2818
2819  if (!$service->isPost())
2820  {
2821    return new PwgError(405, "This method requires HTTP POST");
2822  }
2823
2824  // category_id
2825  // name
2826  // comment
2827
2828  $params['category_id'] = (int)$params['category_id'];
2829  if ($params['category_id'] <= 0)
2830  {
2831    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
2832  }
2833
2834  // database registration
2835  $update = array(
2836    'id' => $params['category_id'],
2837    );
2838
2839  $info_columns = array(
2840    'name',
2841    'comment',
2842    );
2843
2844  $perform_update = false;
2845  foreach ($info_columns as $key)
2846  {
2847    if (isset($params[$key]))
2848    {
2849      $perform_update = true;
2850      $update[$key] = $params[$key];
2851    }
2852  }
2853
2854  if ($perform_update)
2855  {
2856    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
2857    mass_updates(
2858      CATEGORIES_TABLE,
2859      array(
2860        'primary' => array('id'),
2861        'update'  => array_diff(array_keys($update), array('id'))
2862        ),
2863      array($update)
2864      );
2865  }
2866
2867}
2868
2869function ws_categories_setRepresentative($params, &$service)
2870{
2871  global $conf;
2872
2873  if (!is_admin())
2874  {
2875    return new PwgError(401, 'Access denied');
2876  }
2877
2878  if (!$service->isPost())
2879  {
2880    return new PwgError(405, "This method requires HTTP POST");
2881  }
2882
2883  // category_id
2884  // image_id
2885
2886  $params['category_id'] = (int)$params['category_id'];
2887  if ($params['category_id'] <= 0)
2888  {
2889    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
2890  }
2891
2892  // does the category really exist?
2893  $query='
2894SELECT
2895    *
2896  FROM '.CATEGORIES_TABLE.'
2897  WHERE id = '.$params['category_id'].'
2898;';
2899  $row = pwg_db_fetch_assoc(pwg_query($query));
2900  if ($row == null)
2901  {
2902    return new PwgError(404, "category_id not found");
2903  }
2904
2905  $params['image_id'] = (int)$params['image_id'];
2906  if ($params['image_id'] <= 0)
2907  {
2908    return new PwgError(WS_ERR_INVALID_PARAM, "Invalid image_id");
2909  }
2910
2911  // does the image really exist?
2912  $query='
2913SELECT
2914    *
2915  FROM '.IMAGES_TABLE.'
2916  WHERE id = '.$params['image_id'].'
2917;';
2918
2919  $row = pwg_db_fetch_assoc(pwg_query($query));
2920  if ($row == null)
2921  {
2922    return new PwgError(404, "image_id not found");
2923  }
2924
2925  // apply change
2926  $query = '
2927UPDATE '.CATEGORIES_TABLE.'
2928  SET representative_picture_id = '.$params['image_id'].'
2929  WHERE id = '.$params['category_id'].'
2930;';
2931  pwg_query($query);
2932
2933  $query = '
2934UPDATE '.USER_CACHE_CATEGORIES_TABLE.'
2935  SET user_representative_picture_id = NULL
2936  WHERE cat_id = '.$params['category_id'].'
2937;';
2938  pwg_query($query);
2939}
2940
2941function ws_categories_delete($params, &$service)
2942{
2943  global $conf;
2944  if (!is_admin())
2945  {
2946    return new PwgError(401, 'Access denied');
2947  }
2948
2949  if (!$service->isPost())
2950  {
2951    return new PwgError(405, "This method requires HTTP POST");
2952  }
2953
2954  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
2955  {
2956    return new PwgError(403, 'Invalid security token');
2957  }
2958
2959  $modes = array('no_delete', 'delete_orphans', 'force_delete');
2960  if (!in_array($params['photo_deletion_mode'], $modes))
2961  {
2962    return new PwgError(
2963      500,
2964      '[ws_categories_delete]'
2965      .' invalid parameter photo_deletion_mode "'.$params['photo_deletion_mode'].'"'
2966      .', possible values are {'.implode(', ', $modes).'}.'
2967      );
2968  }
2969
2970  $params['category_id'] = preg_split(
2971    '/[\s,;\|]/',
2972    $params['category_id'],
2973    -1,
2974    PREG_SPLIT_NO_EMPTY
2975    );
2976  $params['category_id'] = array_map('intval', $params['category_id']);
2977
2978  $category_ids = array();
2979  foreach ($params['category_id'] as $category_id)
2980  {
2981    if ($category_id > 0)
2982    {
2983      array_push($category_ids, $category_id);
2984    }
2985  }
2986
2987  if (count($category_ids) == 0)
2988  {
2989    return;
2990  }
2991
2992  $query = '
2993SELECT id
2994  FROM '.CATEGORIES_TABLE.'
2995  WHERE id IN ('.implode(',', $category_ids).')
2996;';
2997  $category_ids = array_from_query($query, 'id');
2998
2999  if (count($category_ids) == 0)
3000  {
3001    return;
3002  }
3003
3004  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3005  delete_categories($category_ids, $params['photo_deletion_mode']);
3006  update_global_rank();
3007}
3008
3009function ws_categories_move($params, &$service)
3010{
3011  global $conf, $page;
3012
3013  if (!is_admin())
3014  {
3015    return new PwgError(401, 'Access denied');
3016  }
3017
3018  if (!$service->isPost())
3019  {
3020    return new PwgError(405, "This method requires HTTP POST");
3021  }
3022
3023  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3024  {
3025    return new PwgError(403, 'Invalid security token');
3026  }
3027
3028  $params['category_id'] = preg_split(
3029    '/[\s,;\|]/',
3030    $params['category_id'],
3031    -1,
3032    PREG_SPLIT_NO_EMPTY
3033    );
3034  $params['category_id'] = array_map('intval', $params['category_id']);
3035
3036  $category_ids = array();
3037  foreach ($params['category_id'] as $category_id)
3038  {
3039    if ($category_id > 0)
3040    {
3041      array_push($category_ids, $category_id);
3042    }
3043  }
3044
3045  if (count($category_ids) == 0)
3046  {
3047    return new PwgError(403, 'Invalid category_id input parameter, no category to move');
3048  }
3049
3050  // we can't move physical categories
3051  $categories_in_db = array();
3052
3053  $query = '
3054SELECT
3055    id,
3056    name,
3057    dir
3058  FROM '.CATEGORIES_TABLE.'
3059  WHERE id IN ('.implode(',', $category_ids).')
3060;';
3061  $result = pwg_query($query);
3062  while ($row = pwg_db_fetch_assoc($result))
3063  {
3064    $categories_in_db[$row['id']] = $row;
3065    // we break on error at first physical category detected
3066    if (!empty($row['dir']))
3067    {
3068      $row['name'] = strip_tags(
3069        trigger_event(
3070          'render_category_name',
3071          $row['name'],
3072          'ws_categories_move'
3073          )
3074        );
3075
3076      return new PwgError(
3077        403,
3078        sprintf(
3079          'Category %s (%u) is not a virtual category, you cannot move it',
3080          $row['name'],
3081          $row['id']
3082          )
3083        );
3084    }
3085  }
3086
3087  if (count($categories_in_db) != count($category_ids))
3088  {
3089    $unknown_category_ids = array_diff($category_ids, array_keys($categories_in_db));
3090
3091    return new PwgError(
3092      403,
3093      sprintf(
3094        'Category %u does not exist',
3095        $unknown_category_ids[0]
3096        )
3097      );
3098  }
3099
3100  // does this parent exists? This check should be made in the
3101  // move_categories function, not here
3102  //
3103  // 0 as parent means "move categories at gallery root"
3104  if (!is_numeric($params['parent']))
3105  {
3106    return new PwgError(403, 'Invalid parent input parameter');
3107  }
3108
3109  if (0 != $params['parent']) {
3110    $params['parent'] = intval($params['parent']);
3111    $subcat_ids = get_subcat_ids(array($params['parent']));
3112    if (count($subcat_ids) == 0)
3113    {
3114      return new PwgError(403, 'Unknown parent category id');
3115    }
3116  }
3117
3118  $page['infos'] = array();
3119  $page['errors'] = array();
3120  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3121  move_categories($category_ids, $params['parent']);
3122  invalidate_user_cache();
3123
3124  if (count($page['errors']) != 0)
3125  {
3126    return new PwgError(403, implode('; ', $page['errors']));
3127  }
3128}
3129
3130function ws_logfile($string)
3131{
3132  global $conf;
3133
3134  if (!$conf['ws_enable_log']) {
3135    return true;
3136  }
3137
3138  file_put_contents(
3139    $conf['ws_log_filepath'],
3140    '['.date('c').'] '.$string."\n",
3141    FILE_APPEND
3142    );
3143}
3144
3145function ws_images_checkUpload($params, &$service)
3146{
3147  global $conf;
3148
3149  if (!is_admin())
3150  {
3151    return new PwgError(401, 'Access denied');
3152  }
3153
3154  include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
3155  $ret['message'] = ready_for_upload_message();
3156  $ret['ready_for_upload'] = true;
3157
3158  if (!empty($ret['message']))
3159  {
3160    $ret['ready_for_upload'] = false;
3161  }
3162
3163  return $ret;
3164}
3165
3166function ws_plugins_getList($params, &$service)
3167{
3168  global $conf;
3169
3170  if (!is_admin())
3171  {
3172    return new PwgError(401, 'Access denied');
3173  }
3174
3175  include_once(PHPWG_ROOT_PATH.'admin/include/plugins.class.php');
3176  $plugins = new plugins();
3177  $plugins->sort_fs_plugins('name');
3178  $plugin_list = array();
3179
3180  foreach($plugins->fs_plugins as $plugin_id => $fs_plugin)
3181  {
3182    if (isset($plugins->db_plugins_by_id[$plugin_id]))
3183    {
3184      $state = $plugins->db_plugins_by_id[$plugin_id]['state'];
3185    }
3186    else
3187    {
3188      $state = 'uninstalled';
3189    }
3190
3191    array_push(
3192      $plugin_list,
3193      array(
3194        'id' => $plugin_id,
3195        'name' => $fs_plugin['name'],
3196        'version' => $fs_plugin['version'],
3197        'state' => $state,
3198        'description' => $fs_plugin['description'],
3199        )
3200      );
3201  }
3202
3203  return $plugin_list;
3204}
3205
3206function ws_plugins_performAction($params, &$service)
3207{
3208  global $template;
3209
3210  if (!is_admin())
3211  {
3212    return new PwgError(401, 'Access denied');
3213  }
3214
3215  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3216  {
3217    return new PwgError(403, 'Invalid security token');
3218  }
3219
3220  define('IN_ADMIN', true);
3221  include_once(PHPWG_ROOT_PATH.'admin/include/plugins.class.php');
3222  $plugins = new plugins();
3223  $errors = $plugins->perform_action($params['action'], $params['plugin']);
3224
3225
3226  if (!empty($errors))
3227  {
3228    return new PwgError(500, $errors);
3229  }
3230  else
3231  {
3232    if (in_array($params['action'], array('activate', 'deactivate')))
3233    {
3234      $template->delete_compiled_templates();
3235    }
3236    return true;
3237  }
3238}
3239
3240function ws_themes_performAction($params, &$service)
3241{
3242  global $template;
3243
3244  if (!is_admin())
3245  {
3246    return new PwgError(401, 'Access denied');
3247  }
3248
3249  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3250  {
3251    return new PwgError(403, 'Invalid security token');
3252  }
3253
3254  define('IN_ADMIN', true);
3255  include_once(PHPWG_ROOT_PATH.'admin/include/themes.class.php');
3256  $themes = new themes();
3257  $errors = $themes->perform_action($params['action'], $params['theme']);
3258
3259  if (!empty($errors))
3260  {
3261    return new PwgError(500, $errors);
3262  }
3263  else
3264  {
3265    if (in_array($params['action'], array('activate', 'deactivate')))
3266    {
3267      $template->delete_compiled_templates();
3268    }
3269    return true;
3270  }
3271}
3272
3273function ws_extensions_update($params, &$service)
3274{
3275  if (!is_webmaster())
3276  {
3277    return new PwgError(401, l10n('Webmaster status is required.'));
3278  }
3279
3280  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3281  {
3282    return new PwgError(403, 'Invalid security token');
3283  }
3284
3285  if (empty($params['type']) or !in_array($params['type'], array('plugins', 'themes', 'languages')))
3286  {
3287    return new PwgError(403, "invalid extension type");
3288  }
3289
3290  if (empty($params['id']) or empty($params['revision']))
3291  {
3292    return new PwgError(null, 'Wrong parameters');
3293  }
3294
3295  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3296  include_once(PHPWG_ROOT_PATH.'admin/include/'.$params['type'].'.class.php');
3297
3298  $type = $params['type'];
3299  $extension_id = $params['id'];
3300  $revision = $params['revision'];
3301
3302  $extension = new $type();
3303
3304  if ($type == 'plugins')
3305  {
3306    if (isset($extension->db_plugins_by_id[$extension_id]) and $extension->db_plugins_by_id[$extension_id]['state'] == 'active')
3307    {
3308      $extension->perform_action('deactivate', $extension_id);
3309
3310      redirect(PHPWG_ROOT_PATH
3311        . 'ws.php'
3312        . '?method=pwg.extensions.update'
3313        . '&type=plugins'
3314        . '&id=' . $extension_id
3315        . '&revision=' . $revision
3316        . '&reactivate=true'
3317        . '&pwg_token=' . get_pwg_token()
3318        . '&format=json'
3319      );
3320    }
3321
3322    $upgrade_status = $extension->extract_plugin_files('upgrade', $revision, $extension_id);
3323    $extension_name = $extension->fs_plugins[$extension_id]['name'];
3324
3325    if (isset($params['reactivate']))
3326    {
3327      $extension->perform_action('activate', $extension_id);
3328    }
3329  }
3330  elseif ($type == 'themes')
3331  {
3332    $upgrade_status = $extension->extract_theme_files('upgrade', $revision, $extension_id);
3333    $extension_name = $extension->fs_themes[$extension_id]['name'];
3334  }
3335  elseif ($type == 'languages')
3336  {
3337    $upgrade_status = $extension->extract_language_files('upgrade', $revision, $extension_id);
3338    $extension_name = $extension->fs_languages[$extension_id]['name'];
3339  }
3340
3341  global $template;
3342  $template->delete_compiled_templates();
3343
3344  switch ($upgrade_status)
3345  {
3346    case 'ok':
3347      return sprintf(l10n('%s has been successfully updated.'), $extension_name);
3348
3349    case 'temp_path_error':
3350      return new PwgError(null, l10n('Can\'t create temporary file.'));
3351
3352    case 'dl_archive_error':
3353      return new PwgError(null, l10n('Can\'t download archive.'));
3354
3355    case 'archive_error':
3356      return new PwgError(null, l10n('Can\'t read or extract archive.'));
3357
3358    default:
3359      return new PwgError(null, sprintf(l10n('An error occured during extraction (%s).'), $upgrade_status));
3360  }
3361}
3362
3363function ws_extensions_ignoreupdate($params, &$service)
3364{
3365  global $conf;
3366
3367  define('IN_ADMIN', true);
3368  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3369
3370  if (!is_webmaster())
3371  {
3372    return new PwgError(401, 'Access denied');
3373  }
3374
3375  if (empty($params['pwg_token']) or get_pwg_token() != $params['pwg_token'])
3376  {
3377    return new PwgError(403, 'Invalid security token');
3378  }
3379
3380  $conf['updates_ignored'] = unserialize($conf['updates_ignored']);
3381
3382  // Reset ignored extension
3383  if ($params['reset'])
3384  {
3385    if (!empty($params['type']) and isset($conf['updates_ignored'][$params['type']]))
3386    {
3387      $conf['updates_ignored'][$params['type']] = array();
3388    }
3389    else
3390    {
3391      $conf['updates_ignored'] = array(
3392        'plugins'=>array(),
3393        'themes'=>array(),
3394        'languages'=>array()
3395      );
3396    }
3397    conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored'])));
3398    unset($_SESSION['extensions_need_update']);
3399    return true;
3400  }
3401
3402  if (empty($params['id']) or empty($params['type']) or !in_array($params['type'], array('plugins', 'themes', 'languages')))
3403  {
3404    return new PwgError(403, 'Invalid parameters');
3405  }
3406
3407  // Add or remove extension from ignore list
3408  if (!in_array($params['id'], $conf['updates_ignored'][$params['type']]))
3409  {
3410    array_push($conf['updates_ignored'][$params['type']], $params['id']);
3411  }
3412  conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored'])));
3413  unset($_SESSION['extensions_need_update']);
3414  return true;
3415}
3416
3417function ws_extensions_checkupdates($params, &$service)
3418{
3419  global $conf;
3420
3421  define('IN_ADMIN', true);
3422  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
3423  include_once(PHPWG_ROOT_PATH.'admin/include/updates.class.php');
3424  $update = new updates();
3425
3426  if (!is_admin())
3427  {
3428    return new PwgError(401, 'Access denied');
3429  }
3430
3431  $result = array();
3432
3433  if (!isset($_SESSION['need_update']))
3434    $update->check_piwigo_upgrade();
3435
3436  $result['piwigo_need_update'] = $_SESSION['need_update'];
3437
3438  $conf['updates_ignored'] = unserialize($conf['updates_ignored']);
3439
3440  if (!isset($_SESSION['extensions_need_update']))
3441    $update->check_extensions();
3442  else
3443    $update->check_updated_extensions();
3444
3445  if (!is_array($_SESSION['extensions_need_update']))
3446    $result['ext_need_update'] = null;
3447  else
3448    $result['ext_need_update'] = !empty($_SESSION['extensions_need_update']);
3449
3450  return $result;
3451}
3452?>
Note: See TracBrowser for help on using the repository browser.