Changeset 25281 for trunk/include


Ignore:
Timestamp:
Nov 1, 2013, 12:03:10 PM (11 years ago)
Author:
mistic100
Message:

splits ws_functions.inc.php in 8 files + comments + code cleaning

Location:
trunk/include
Files:
10 added
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/ws_functions.inc.php

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