diff options
Diffstat (limited to 'include/ws_functions/pwg.categories.php')
-rw-r--r-- | include/ws_functions/pwg.categories.php | 838 |
1 files changed, 838 insertions, 0 deletions
diff --git a/include/ws_functions/pwg.categories.php b/include/ws_functions/pwg.categories.php new file mode 100644 index 000000000..70a555398 --- /dev/null +++ b/include/ws_functions/pwg.categories.php @@ -0,0 +1,838 @@ +<?php +// +-----------------------------------------------------------------------+ +// | Piwigo - a PHP based photo gallery | +// +-----------------------------------------------------------------------+ +// | Copyright(C) 2008-2013 Piwigo Team http://piwigo.org | +// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net | +// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick | +// +-----------------------------------------------------------------------+ +// | This program is free software; you can redistribute it and/or modify | +// | it under the terms of the GNU General Public License as published by | +// | the Free Software Foundation | +// | | +// | This program is distributed in the hope that it will be useful, but | +// | WITHOUT ANY WARRANTY; without even the implied warranty of | +// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | +// | General Public License for more details. | +// | | +// | You should have received a copy of the GNU General Public License | +// | along with this program; if not, write to the Free Software | +// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | +// | USA. | +// +-----------------------------------------------------------------------+ + +/** + * API method + * Returns images per category + * @param mixed[] $params + * @option int[] cat_id (optional) + * @option bool recursive + * @option int per_page + * @option int page + * @option string order (optional) + */ +function ws_categories_getImages($params, &$service) +{ + global $user, $conf; + + $images = array(); + + //------------------------------------------------- get the related categories + $where_clauses = array(); + foreach ($params['cat_id'] as $cat_id) + { + if ($params['recursive']) + { + $where_clauses[] = 'uppercats '.DB_REGEX_OPERATOR.' \'(^|,)'.$cat_id.'(,|$)\''; + } + else + { + $where_clauses[] = 'id='.$cat_id; + } + } + if (!empty($where_clauses)) + { + $where_clauses = array('('. implode("\n OR ", $where_clauses) . ')'); + } + $where_clauses[] = get_sql_condition_FandF( + array('forbidden_categories' => 'id'), + null, true + ); + + $query = ' +SELECT id, name, permalink, image_order + FROM '. CATEGORIES_TABLE .' + WHERE '. implode("\n AND ", $where_clauses) .' +;'; + $result = pwg_query($query); + + $cats = array(); + while ($row = pwg_db_fetch_assoc($result)) + { + $row['id'] = (int)$row['id']; + $cats[ $row['id'] ] = $row; + } + + //-------------------------------------------------------- get the images + if (!empty($cats)) + { + $where_clauses = ws_std_image_sql_filter($params, 'i.'); + $where_clauses[] = 'category_id IN ('. implode(',', array_keys($cats)) .')'; + $where_clauses[] = get_sql_condition_FandF( + array('visible_images' => 'i.id'), + null, true + ); + + $order_by = ws_std_image_sql_order($params, 'i.'); + if ( empty($order_by) + and count($params['cat_id'])==1 + and isset($cats[ $params['cat_id'][0] ]['image_order']) + ) + { + $order_by = $cats[ $params['cat_id'][0] ]['image_order']; + } + $order_by = empty($order_by) ? $conf['order_by'] : 'ORDER BY '.$order_by; + + $query = ' +SELECT i.*, GROUP_CONCAT(category_id) AS cat_ids + FROM '. IMAGES_TABLE .' i + INNER JOIN '. IMAGE_CATEGORY_TABLE .' ON i.id=image_id + WHERE '. implode("\n AND ", $where_clauses) .' + GROUP BY i.id + '. $order_by .' + LIMIT '. $params['per_page'] .' + OFFSET '. ($params['per_page']*$params['page']) .' +;'; + $result = pwg_query($query); + + while ($row = pwg_db_fetch_assoc($result)) + { + $image = array(); + foreach (array('id', 'width', 'height', 'hit') as $k) + { + if (isset($row[$k])) + { + $image[$k] = (int)$row[$k]; + } + } + foreach (array('file', 'name', 'comment', 'date_creation', 'date_available') as $k) + { + $image[$k] = $row[$k]; + } + $image = array_merge($image, ws_std_get_urls($row)); + + $image_cats = array(); + foreach (explode(',', $row['cat_ids']) as $cat_id) + { + $url = make_index_url( + array( + 'category' => $cats[$cat_id], + ) + ); + $page_url = make_picture_url( + array( + 'category' => $cats[$cat_id], + 'image_id' => $row['id'], + 'image_file' => $row['file'], + ) + ); + $image_cats[] = array( + 'id' => (int)$cat_id, + 'url' => $url, + 'page_url' => $page_url, + ); + } + + $image['categories'] = new PwgNamedArray( + $image_cats, + 'category', + array('id', 'url', 'page_url') + ); + $images[] = $image; + } + } + + return array( + 'paging' => new PwgNamedStruct( + array( + 'page' => $params['page'], + 'per_page' => $params['per_page'], + 'count' => count($images) + ) + ), + 'images' => new PwgNamedArray( + $images, 'image', + ws_std_get_image_xml_attributes() + ) + ); +} + +/** + * API method + * Returns a list of categories + * @param mixed[] $params + * @option int cat_id (optional) + * @option bool recursive + * @option bool public + * @option bool tree_output + * @option bool fullname + */ +function ws_categories_getList($params, &$service) +{ + global $user, $conf; + + $where = array('1=1'); + $join_type = 'INNER'; + $join_user = $user['id']; + + if (!$params['recursive']) + { + if ($params['cat_id']>0) + { + $where[] = '( + id_uppercat = '. (int)($params['cat_id']) .' + OR id='.(int)($params['cat_id']).' + )'; + } + else + { + $where[] = 'id_uppercat IS NULL'; + } + } + else if ($params['cat_id']>0) + { + $where[] = 'uppercats '. DB_REGEX_OPERATOR .' \'(^|,)'. + (int)($params['cat_id']) .'(,|$)\''; + } + + if ($params['public']) + { + $where[] = 'status = "public"'; + $where[] = 'visible = "true"'; + + $join_user = $conf['guest_id']; + } + else if (is_admin()) + { + // in this very specific case, we don't want to hide empty + // categories. Function calculate_permissions will only return + // categories that are either locked or private and not permitted + // + // calculate_permissions does not consider empty categories as forbidden + $forbidden_categories = calculate_permissions($user['id'], $user['status']); + $where[]= 'id NOT IN ('.$forbidden_categories.')'; + $join_type = 'LEFT'; + } + + $query = ' +SELECT + id, name, comment, permalink, + uppercats, global_rank, id_uppercat, + nb_images, count_images AS total_nb_images, + representative_picture_id, user_representative_picture_id, count_images, count_categories, + date_last, max_date_last, count_categories AS nb_categories + FROM '. CATEGORIES_TABLE .' + '.$join_type.' JOIN '. USER_CACHE_CATEGORIES_TABLE .' + ON id=cat_id AND user_id='.$join_user.' + WHERE '. implode("\n AND ", $where) .' +;'; + $result = pwg_query($query); + + // management of the album thumbnail -- starts here + $image_ids = array(); + $categories = array(); + $user_representative_updates_for = array(); + // management of the album thumbnail -- stops here + + $cats = array(); + while ($row = pwg_db_fetch_assoc($result)) + { + $row['url'] = make_index_url( + array( + 'category' => $row + ) + ); + foreach (array('id','nb_images','total_nb_images','nb_categories') as $key) + { + $row[$key] = (int)$row[$key]; + } + + if ($params['fullname']) + { + $row['name'] = strip_tags(get_cat_display_name_cache($row['uppercats'], null, false)); + } + else + { + $row['name'] = strip_tags( + trigger_event( + 'render_category_name', + $row['name'], + 'ws_categories_getList' + ) + ); + } + + $row['comment'] = strip_tags( + trigger_event( + 'render_category_description', + $row['comment'], + 'ws_categories_getList' + ) + ); + + // management of the album thumbnail -- starts here + // + // on branch 2.3, the algorithm is duplicated from + // include/category_cats, but we should use a common code for Piwigo 2.4 + // + // warning : if the API method is called with $params['public'], the + // album thumbnail may be not accurate. The thumbnail can be viewed by + // the connected user, but maybe not by the guest. Changing the + // filtering method would be too complicated for now. We will simply + // avoid to persist the user_representative_picture_id in the database + // if $params['public'] + if (!empty($row['user_representative_picture_id'])) + { + $image_id = $row['user_representative_picture_id']; + } + else if (!empty($row['representative_picture_id'])) + { // if a representative picture is set, it has priority + $image_id = $row['representative_picture_id']; + } + else if ($conf['allow_random_representative']) + { + // searching a random representant among elements in sub-categories + $image_id = get_random_image_in_category($row); + } + else + { // searching a random representant among representant of sub-categories + if ($row['count_categories']>0 and $row['count_images']>0) + { + $query = ' +SELECT representative_picture_id + FROM '. CATEGORIES_TABLE .' + INNER JOIN '. USER_CACHE_CATEGORIES_TABLE .' + ON id=cat_id AND user_id='.$user['id'].' + WHERE uppercats LIKE \''.$row['uppercats'].',%\' + AND representative_picture_id IS NOT NULL + '.get_sql_condition_FandF( + array('visible_categories' => 'id'), + "\n AND" + ).' + ORDER BY '. DB_RANDOM_FUNCTION .'() + LIMIT 1 +;'; + $subresult = pwg_query($query); + + if (pwg_db_num_rows($subresult) > 0) + { + list($image_id) = pwg_db_fetch_row($subresult); + } + } + } + + if (isset($image_id)) + { + if ($conf['representative_cache_on_subcats'] and $row['user_representative_picture_id'] != $image_id) + { + $user_representative_updates_for[ $row['id'] ] = $image_id; + } + + $row['representative_picture_id'] = $image_id; + $image_ids[] = $image_id; + $categories[] = $row; + } + unset($image_id); + // management of the album thumbnail -- stops here + + $cats[] = $row; + } + usort($cats, 'global_rank_compare'); + + // management of the album thumbnail -- starts here + if (count($categories) > 0) + { + $thumbnail_src_of = array(); + $new_image_ids = array(); + + $query = ' +SELECT id, path, representative_ext, level + FROM '. IMAGES_TABLE .' + WHERE id IN ('. implode(',', $image_ids) .') +;'; + $result = pwg_query($query); + + while ($row = pwg_db_fetch_assoc($result)) + { + if ($row['level'] <= $user['level']) + { + $thumbnail_src_of[$row['id']] = DerivativeImage::thumb_url($row); + } + else + { + // problem: we must not display the thumbnail of a photo which has a + // higher privacy level than user privacy level + // + // * what is the represented category? + // * find a random photo matching user permissions + // * register it at user_representative_picture_id + // * set it as the representative_picture_id for the category + foreach ($categories as &$category) + { + if ($row['id'] == $category['representative_picture_id']) + { + // searching a random representant among elements in sub-categories + $image_id = get_random_image_in_category($category); + + if (isset($image_id) and !in_array($image_id, $image_ids)) + { + $new_image_ids[] = $image_id; + } + if ($conf['representative_cache_on_level']) + { + $user_representative_updates_for[ $category['id'] ] = $image_id; + } + + $category['representative_picture_id'] = $image_id; + } + } + unset($category); + } + } + + if (count($new_image_ids) > 0) + { + $query = ' +SELECT id, path, representative_ext + FROM '. IMAGES_TABLE .' + WHERE id IN ('. implode(',', $new_image_ids) .') +;'; + $result = pwg_query($query); + + while ($row = pwg_db_fetch_assoc($result)) + { + $thumbnail_src_of[ $row['id'] ] = DerivativeImage::thumb_url($row); + } + } + } + + // compared to code in include/category_cats, we only persist the new + // user_representative if we have used $user['id'] and not the guest id, + // or else the real guest may see thumbnail that he should not + if (!$params['public'] and count($user_representative_updates_for)) + { + $updates = array(); + + foreach ($user_representative_updates_for as $cat_id => $image_id) + { + $updates[] = array( + 'user_id' => $user['id'], + 'cat_id' => $cat_id, + 'user_representative_picture_id' => $image_id, + ); + } + + mass_updates( + USER_CACHE_CATEGORIES_TABLE, + array( + 'primary' => array('user_id', 'cat_id'), + 'update' => array('user_representative_picture_id') + ), + $updates + ); + } + + foreach ($cats as &$cat) + { + foreach ($categories as $category) + { + if ($category['id'] == $cat['id'] and isset($category['representative_picture_id'])) + { + $cat['tn_url'] = $thumbnail_src_of[$category['representative_picture_id']]; + } + } + // we don't want them in the output + unset($cat['user_representative_picture_id'], $cat['count_images'], $cat['count_categories']); + } + unset($cat); + // management of the album thumbnail -- stops here + + if ($params['tree_output']) + { + $cats = categories_flatlist_to_tree($cats); + } + + return array( + 'categories' => new PwgNamedArray( + $cats, + 'category', + ws_std_get_category_xml_attributes() + ) + ); +} + +/** + * API method + * Returns the list of categories as you can see them in administration + * @param mixed[] $params + * + * Only admin can run this method and permissions are not taken into + * account. + */ +function ws_categories_getAdminList($params, &$service) +{ + $query = ' +SELECT category_id, COUNT(*) AS counter + FROM '. IMAGE_CATEGORY_TABLE .' + GROUP BY category_id +;'; + $nb_images_of = simple_hash_from_query($query, 'category_id', 'counter'); + + $query = ' +SELECT id, name, comment, uppercats, global_rank + FROM '. CATEGORIES_TABLE .' +;'; + $result = pwg_query($query); + + $cats = array(); + while ($row = pwg_db_fetch_assoc($result)) + { + $id = $row['id']; + $row['nb_images'] = isset($nb_images_of[$id]) ? $nb_images_of[$id] : 0; + + $row['name'] = strip_tags( + trigger_event( + 'render_category_name', + $row['name'], + 'ws_categories_getAdminList' + ) + ); + $row['comment'] = strip_tags( + trigger_event( + 'render_category_description', + $row['comment'], + 'ws_categories_getAdminList' + ) + ); + + $cats[] = $row; + } + + usort($cats, 'global_rank_compare'); + return array( + 'categories' => new PwgNamedArray( + $cats, + 'category', + array('id', 'nb_images', 'name', 'uppercats', 'global_rank') + ) + ); +} + +/** + * API method + * Adds a category + * @param mixed[] $params + * @option string name + * @option int parent (optional) + * @option string comment (optional) + * @option bool visible + * @option string status (optional) + * @option bool commentable + */ +function ws_categories_add($params, &$service) +{ + include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); + + $options = array(); + if (!empty($params['status']) and in_array($params['status'], array('private','public'))) + { + $options['status'] = $params['status']; + } + + if (!empty($params['comment'])) + { + $options['comment'] = $params['comment']; + } + + $creation_output = create_virtual_category( + $params['name'], + $params['parent'], + $options + ); + + if (isset($creation_output['error'])) + { + return new PwgError(500, $creation_output['error']); + } + + invalidate_user_cache(); + + return $creation_output; +} + +/** + * API method + * Sets details of a category + * @param mixed[] $params + * @option int cat_id + * @option string name (optional) + * @option string comment (optional) + */ +function ws_categories_setInfo($params, &$service) +{ + $update = array( + 'id' => $params['category_id'], + ); + + $info_columns = array('name', 'comment',); + + $perform_update = false; + foreach ($info_columns as $key) + { + if (isset($params[$key])) + { + $perform_update = true; + $update[$key] = $params[$key]; + } + } + + if ($perform_update) + { + single_update( + CATEGORIES_TABLE, + $update, + array('id' => $update['id']) + ); + } +} + +/** + * API method + * Sets representative image of a category + * @param mixed[] $params + * @option int category_id + * @option int image_id + */ +function ws_categories_setRepresentative($params, &$service) +{ + // does the category really exist? + $query = ' +SELECT COUNT(*) + FROM '. CATEGORIES_TABLE .' + WHERE id = '. $params['category_id'] .' +;'; + list($count) = pwg_db_fetch_row(pwg_query($query)); + if ($count == 0) + { + return new PwgError(404, 'category_id not found'); + } + + // does the image really exist? + $query = ' +SELECT COUNT(*) + FROM '. IMAGES_TABLE .' + WHERE id = '. $params['image_id'] .' +;'; + list($count) = pwg_db_fetch_row(pwg_query($query)); + if ($count == 0) + { + return new PwgError(404, 'image_id not found'); + } + + // apply change + $query = ' +UPDATE '. CATEGORIES_TABLE .' + SET representative_picture_id = '. $params['image_id'] .' + WHERE id = '. $params['category_id'] .' +;'; + pwg_query($query); + + $query = ' +UPDATE '. USER_CACHE_CATEGORIES_TABLE .' + SET user_representative_picture_id = NULL + WHERE cat_id = '. $params['category_id'] .' +;'; + pwg_query($query); +} + +/** + * API method + * Deletes a category + * @param mixed[] $params + * @option string|int[] category_id + * @option string photo_deletion_mode + * @option string pwg_token + */ +function ws_categories_delete($params, &$service) +{ + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, 'Invalid security token'); + } + + $modes = array('no_delete', 'delete_orphans', 'force_delete'); + if (!in_array($params['photo_deletion_mode'], $modes)) + { + return new PwgError(500, + '[ws_categories_delete]' + .' invalid parameter photo_deletion_mode "'.$params['photo_deletion_mode'].'"' + .', possible values are {'.implode(', ', $modes).'}.' + ); + } + + if (!is_array($params['category_id'])) + { + $params['category_id'] = preg_split( + '/[\s,;\|]/', + $params['category_id'], + -1, + PREG_SPLIT_NO_EMPTY + ); + } + $params['category_id'] = array_map('intval', $params['category_id']); + + $category_ids = array(); + foreach ($params['category_id'] as $category_id) + { + if ($category_id > 0) + { + $category_ids[] = $category_id; + } + } + + if (count($category_ids) == 0) + { + return; + } + + $query = ' +SELECT id + FROM '. CATEGORIES_TABLE .' + WHERE id IN ('. implode(',', $category_ids) .') +;'; + $category_ids = array_from_query($query, 'id'); + + if (count($category_ids) == 0) + { + return; + } + + include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); + delete_categories($category_ids, $params['photo_deletion_mode']); + update_global_rank(); +} + +/** + * API method + * Moves a category + * @param mixed[] $params + * @option string|int[] category_id + * @option int parent + * @option string pwg_token + */ +function ws_categories_move($params, &$service) +{ + global $page; + + if (get_pwg_token() != $params['pwg_token']) + { + return new PwgError(403, 'Invalid security token'); + } + + if (!is_array($params['category_id'])) + { + $params['category_id'] = preg_split( + '/[\s,;\|]/', + $params['category_id'], + -1, + PREG_SPLIT_NO_EMPTY + ); + } + $params['category_id'] = array_map('intval', $params['category_id']); + + $category_ids = array(); + foreach ($params['category_id'] as $category_id) + { + if ($category_id > 0) + { + $category_ids[] = $category_id; + } + } + + if (count($category_ids) == 0) + { + return new PwgError(403, 'Invalid category_id input parameter, no category to move'); + } + + // we can't move physical categories + $categories_in_db = array(); + + $query = ' +SELECT id, name, dir + FROM '. CATEGORIES_TABLE .' + WHERE id IN ('. implode(',', $category_ids) .') +;'; + $result = pwg_query($query); + while ($row = pwg_db_fetch_assoc($result)) + { + $categories_in_db[ $row['id'] ] = $row; + + // we break on error at first physical category detected + if (!empty($row['dir'])) + { + $row['name'] = strip_tags( + trigger_event( + 'render_category_name', + $row['name'], + 'ws_categories_move' + ) + ); + + return new PwgError(403, + sprintf( + 'Category %s (%u) is not a virtual category, you cannot move it', + $row['name'], + $row['id'] + ) + ); + } + } + + if (count($categories_in_db) != count($category_ids)) + { + $unknown_category_ids = array_diff($category_ids, array_keys($categories_in_db)); + + return new PwgError(403, + sprintf( + 'Category %u does not exist', + $unknown_category_ids[0] + ) + ); + } + + // does this parent exists? This check should be made in the + // move_categories function, not here + // 0 as parent means "move categories at gallery root" + if (0 != $params['parent']) + { + $subcat_ids = get_subcat_ids(array($params['parent'])); + if (count($subcat_ids) == 0) + { + return new PwgError(403, 'Unknown parent category id'); + } + } + + $page['infos'] = array(); + $page['errors'] = array(); + + include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); + move_categories($category_ids, $params['parent']); + invalidate_user_cache(); + + if (count($page['errors']) != 0) + { + return new PwgError(403, implode('; ', $page['errors'])); + } +} + +?>
\ No newline at end of file |