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

Last change on this file since 12906 was 12906, checked in by plg, 12 years ago

feature 2548 (multiple sizes): adapt the web API method pwg.images.add (used
by pLoader, Digikam, Lightroom, iPhoto), pwg.images.checkFiles (pLoader only).

The "resize" parameter was removed for pwg.images.add since this behavior
becomes the default behavior in Piwigo 2.4.

Just like pwg.images.addSimple, pwg.images.add now uses the add_uploaded_file
function (next step is to merge pwg.images.add and pwg.images.addSimple)

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