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

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

merge r17016 from branch 2.4 to trunk

bug 2706 fixed: pwg.getMissingDerivatives correctly manages galleries with not a single photo.

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