Piwigo/include/ws_functions/pwg.images.php
plegall 8effbe8e95 fixes #1356 pwg.images.uploadAsync, move auth code to inc/user.inc.php
In order to avoid loading user context as "guest" and then wait to be in ws_images_uploadAsync function to authenticate and load a new user context. This way we deal with "automatic" authentication in the same place (as apache auth or url auth) and we use the more common user context loading mecanism. Making Community compatible is now much easier.

To avoid duplicating too many lines of ws.php into inc/user.inc.php, I have moved the init of ws.php into inc/ws_init.inc.php
2021-10-07 14:53:27 +02:00

2235 lines
55 KiB
PHP

<?php
// +-----------------------------------------------------------------------+
// | This file is part of Piwigo. |
// | |
// | For copyright and license information, please view the COPYING.txt |
// | file that was distributed with this source code. |
// +-----------------------------------------------------------------------+
// +-----------------------------------------------------------------------+
// | UTILITIES |
// +-----------------------------------------------------------------------+
/**
* Sets associations of an image
* @param int $image_id
* @param string $categories_string - "cat_id[,rank];cat_id[,rank]"
* @param bool $replace_mode - removes old associations
*/
function ws_add_image_category_relations($image_id, $categories_string, $replace_mode=false)
{
// let's add links between the image and the categories
//
// $params['categories'] should look like 123,12;456,auto;789 which means:
//
// 1. associate with category 123 on rank 12
// 2. associate with category 456 on automatic rank
// 3. associate with category 789 on automatic rank
$cat_ids = array();
$rank_on_category = array();
$search_current_ranks = false;
$tokens = explode(';', $categories_string);
foreach ($tokens as $token)
{
@list($cat_id, $rank) = explode(',', $token);
if (!preg_match('/^\d+$/', $cat_id))
{
continue;
}
$cat_ids[] = $cat_id;
if (!isset($rank))
{
$rank = 'auto';
}
$rank_on_category[$cat_id] = $rank;
if ($rank == 'auto')
{
$search_current_ranks = true;
}
}
$cat_ids = array_unique($cat_ids);
if (count($cat_ids) == 0)
{
return new PwgError(500,
'[ws_add_image_category_relations] there is no category defined in "'.$categories_string.'"'
);
}
$query = '
SELECT id
FROM '.CATEGORIES_TABLE.'
WHERE id IN ('.implode(',', $cat_ids).')
;';
$db_cat_ids = query2array($query, null, 'id');
$unknown_cat_ids = array_diff($cat_ids, $db_cat_ids);
if (count($unknown_cat_ids) != 0)
{
return new PwgError(500,
'[ws_add_image_category_relations] the following categories are unknown: '.implode(', ', $unknown_cat_ids)
);
}
$to_update_cat_ids = array();
// in case of replace mode, we first check the existing associations
$query = '
SELECT category_id
FROM '.IMAGE_CATEGORY_TABLE.'
WHERE image_id = '.$image_id.'
;';
$existing_cat_ids = query2array($query, null, 'category_id');
if ($replace_mode)
{
$to_remove_cat_ids = array_diff($existing_cat_ids, $cat_ids);
if (count($to_remove_cat_ids) > 0)
{
$query = '
DELETE
FROM '.IMAGE_CATEGORY_TABLE.'
WHERE image_id = '.$image_id.'
AND category_id IN ('.implode(', ', $to_remove_cat_ids).')
;';
pwg_query($query);
update_category($to_remove_cat_ids);
}
}
$new_cat_ids = array_diff($cat_ids, $existing_cat_ids);
if (count($new_cat_ids) == 0)
{
return true;
}
if ($search_current_ranks)
{
$query = '
SELECT category_id, MAX(`rank`) AS max_rank
FROM '.IMAGE_CATEGORY_TABLE.'
WHERE `rank` IS NOT NULL
AND category_id IN ('.implode(',', $new_cat_ids).')
GROUP BY category_id
;';
$current_rank_of = query2array(
$query,
'category_id',
'max_rank'
);
foreach ($new_cat_ids as $cat_id)
{
if (!isset($current_rank_of[$cat_id]))
{
$current_rank_of[$cat_id] = 0;
}
if ('auto' == $rank_on_category[$cat_id])
{
$rank_on_category[$cat_id] = $current_rank_of[$cat_id] + 1;
}
}
}
$inserts = array();
foreach ($new_cat_ids as $cat_id)
{
$inserts[] = array(
'image_id' => $image_id,
'category_id' => $cat_id,
'rank' => $rank_on_category[$cat_id],
);
}
mass_inserts(
IMAGE_CATEGORY_TABLE,
array_keys($inserts[0]),
$inserts
);
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
update_category($new_cat_ids);
}
/**
* Merge chunks added by pwg.images.addChunk
* @param string $output_filepath
* @param string $original_sum
* @param string $type
*/
function merge_chunks($output_filepath, $original_sum, $type)
{
global $conf, $logger;
$logger->debug('[merge_chunks] input parameter $output_filepath : '.$output_filepath, 'WS');
if (is_file($output_filepath))
{
unlink($output_filepath);
if (is_file($output_filepath))
{
return new PwgError(500, '[merge_chunks] error while trying to remove existing '.$output_filepath);
}
}
$upload_dir = $conf['upload_dir'].'/buffer';
$pattern = '/'.$original_sum.'-'.$type.'/';
$chunks = array();
if ($handle = opendir($upload_dir))
{
while (false !== ($file = readdir($handle)))
{
if (preg_match($pattern, $file))
{
$logger->debug($file, 'WS');
$chunks[] = $upload_dir.'/'.$file;
}
}
closedir($handle);
}
sort($chunks);
if (function_exists('memory_get_usage')) {
$logger->debug('[merge_chunks] memory_get_usage before loading chunks: '.memory_get_usage(), 'WS');
}
$i = 0;
foreach ($chunks as $chunk)
{
$string = file_get_contents($chunk);
if (function_exists('memory_get_usage')) {
$logger->debug('[merge_chunks] memory_get_usage on chunk '.++$i.': '.memory_get_usage(), 'WS');
}
if (!file_put_contents($output_filepath, $string, FILE_APPEND))
{
return new PwgError(500, '[merge_chunks] error while writting chunks for '.$output_filepath);
}
unlink($chunk);
}
if (function_exists('memory_get_usage')) {
$logger->debug('[merge_chunks] memory_get_usage after loading chunks: '.memory_get_usage(), 'WS');
}
}
/**
* Deletes chunks added with pwg.images.addChunk
* @param string $original_sum
* @param string $type
*
* Function introduced for Piwigo 2.4 and the new "multiple size"
* (derivatives) feature. As we only need the biggest sent photo as
* "original", we remove chunks for smaller sizes. We can't make it earlier
* in ws_images_add_chunk because at this moment we don't know which $type
* will be the biggest (we could remove the thumb, but let's use the same
* algorithm)
*/
function remove_chunks($original_sum, $type)
{
global $conf;
$upload_dir = $conf['upload_dir'].'/buffer';
$pattern = '/'.$original_sum.'-'.$type.'/';
$chunks = array();
if ($handle = opendir($upload_dir))
{
while (false !== ($file = readdir($handle)))
{
if (preg_match($pattern, $file))
{
$chunks[] = $upload_dir.'/'.$file;
}
}
closedir($handle);
}
foreach ($chunks as $chunk)
{
unlink($chunk);
}
}
// +-----------------------------------------------------------------------+
// | METHODS |
// +-----------------------------------------------------------------------+
/**
* API method
* Adds a comment to an image
* @param mixed[] $params
* @option int image_id
* @option string author
* @option string content
* @option string key
*/
function ws_images_addComment($params, $service)
{
$query = '
SELECT DISTINCT image_id
FROM '. IMAGE_CATEGORY_TABLE .'
INNER JOIN '.CATEGORIES_TABLE.' ON category_id=id
WHERE commentable="true"
AND image_id='.$params['image_id'].
get_sql_condition_FandF(
array(
'forbidden_categories' => 'id',
'visible_categories' => 'id',
'visible_images' => 'image_id'
),
' AND'
).'
;';
if (!pwg_db_num_rows(pwg_query($query)))
{
return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid image_id');
}
$comm = array(
'author' => trim($params['author']),
'content' => trim($params['content']),
'image_id' => $params['image_id'],
);
include_once(PHPWG_ROOT_PATH.'include/functions_comment.inc.php');
$comment_action = insert_user_comment($comm, $params['key'], $infos);
switch ($comment_action)
{
case 'reject':
$infos[] = l10n('Your comment has NOT been registered because it did not pass the validation rules');
return new PwgError(403, implode("; ", $infos) );
case 'validate':
case 'moderate':
$ret = array(
'id' => $comm['id'],
'validation' => $comment_action=='validate',
);
return array('comment' => new PwgNamedStruct($ret));
default:
return new PwgError(500, "Unknown comment action ".$comment_action );
}
}
/**
* API method
* Returns detailed information for an element
* @param mixed[] $params
* @option int image_id
* @option int comments_page
* @option int comments_per_page
*/
function ws_images_getInfo($params, $service)
{
global $user, $conf;
$query='
SELECT *
FROM '. IMAGES_TABLE .'
WHERE id='. $params['image_id'] .
get_sql_condition_FandF(
array('visible_images' => 'id'),
' AND'
).'
LIMIT 1
;';
$result = pwg_query($query);
if (pwg_db_num_rows($result) == 0)
{
return new PwgError(404, 'image_id not found');
}
$image_row = pwg_db_fetch_assoc($result);
$image_row = array_merge($image_row, ws_std_get_urls($image_row));
//-------------------------------------------------------- related categories
$query = '
SELECT id, name, permalink, uppercats, global_rank, commentable
FROM '. IMAGE_CATEGORY_TABLE .'
INNER JOIN '. CATEGORIES_TABLE .' ON category_id = id
WHERE image_id = '. $image_row['id'] .
get_sql_condition_FandF(
array('forbidden_categories' => 'category_id'),
' AND'
).'
;';
$result = pwg_query($query);
$is_commentable = false;
$related_categories = array();
while ($row = pwg_db_fetch_assoc($result))
{
if ($row['commentable']=='true')
{
$is_commentable = true;
}
unset($row['commentable']);
$row['url'] = make_index_url(
array(
'category' => $row
)
);
$row['page_url'] = make_picture_url(
array(
'image_id' => $image_row['id'],
'image_file' => $image_row['file'],
'category' => $row
)
);
$row['id']=(int)$row['id'];
$related_categories[] = $row;
}
usort($related_categories, 'global_rank_compare');
if (empty($related_categories) and !is_admin())
{
// photo might be in the lounge? or simply orphan. A standard user should not get
// info. An admin should still be able to get info.
return new PwgError(401, 'Access denied');
}
//-------------------------------------------------------------- related tags
$related_tags = get_common_tags(array($image_row['id']), -1);
foreach ($related_tags as $i=>$tag)
{
$tag['url'] = make_index_url(
array(
'tags' => array($tag)
)
);
$tag['page_url'] = make_picture_url(
array(
'image_id' => $image_row['id'],
'image_file' => $image_row['file'],
'tags' => array($tag),
)
);
unset($tag['counter']);
$tag['id'] = (int)$tag['id'];
$related_tags[$i] = $tag;
}
//------------------------------------------------------------- related rates
$rating = array(
'score' => $image_row['rating_score'],
'count' => 0,
'average' => null,
);
if (isset($rating['score']))
{
$query = '
SELECT COUNT(rate) AS count, ROUND(AVG(rate),2) AS average
FROM '. RATE_TABLE .'
WHERE element_id = '. $image_row['id'] .'
;';
$row = pwg_db_fetch_assoc(pwg_query($query));
$rating['score'] = (float)$rating['score'];
$rating['average'] = (float)$row['average'];
$rating['count'] = (int)$row['count'];
}
//---------------------------------------------------------- related comments
$related_comments = array();
$where_comments = 'image_id = '.$image_row['id'];
if (!is_admin())
{
$where_comments .= ' AND validated="true"';
}
$query = '
SELECT COUNT(id) AS nb_comments
FROM '. COMMENTS_TABLE .'
WHERE '. $where_comments .'
;';
list($nb_comments) = query2array($query, null, 'nb_comments');
$nb_comments = (int)$nb_comments;
if ($nb_comments>0 and $params['comments_per_page']>0)
{
$query = '
SELECT id, date, author, content
FROM '. COMMENTS_TABLE .'
WHERE '. $where_comments .'
ORDER BY date
LIMIT '. (int)$params['comments_per_page'] .'
OFFSET '. (int)($params['comments_per_page']*$params['comments_page']) .'
;';
$result = pwg_query($query);
while ($row = pwg_db_fetch_assoc($result))
{
$row['id'] = (int)$row['id'];
$related_comments[] = $row;
}
}
$comment_post_data = null;
if ($is_commentable and
(!is_a_guest()
or (is_a_guest() and $conf['comments_forall'] )
)
)
{
$comment_post_data['author'] = stripslashes($user['username']);
$comment_post_data['key'] = get_ephemeral_key(2, $params['image_id']);
}
$ret = $image_row;
foreach (array('id','width','height','hit','filesize') as $k)
{
if (isset($ret[$k]))
{
$ret[$k] = (int)$ret[$k];
}
}
foreach (array('path', 'storage_category_id') as $k)
{
unset($ret[$k]);
}
$ret['rates'] = array(
WS_XML_ATTRIBUTES => $rating
);
$ret['categories'] = new PwgNamedArray(
$related_categories,
'category',
array('id','url', 'page_url')
);
$ret['tags'] = new PwgNamedArray(
$related_tags,
'tag',
ws_std_get_tag_xml_attributes()
);
if (isset($comment_post_data))
{
$ret['comment_post'] = array(
WS_XML_ATTRIBUTES => $comment_post_data
);
}
$ret['comments_paging'] = new PwgNamedStruct(
array(
'page' => $params['comments_page'],
'per_page' => $params['comments_per_page'],
'count' => count($related_comments),
'total_count' => $nb_comments,
)
);
$ret['comments'] = new PwgNamedArray(
$related_comments,
'comment',
array('id','date')
);
if ($service->_responseFormat != 'rest')
{
return $ret; // for backward compatibility only
}
else
{
return array(
'image' => new PwgNamedStruct($ret, null, array('name','comment'))
);
}
}
/**
* API method
* Rates an image
* @param mixed[] $params
* @option int image_id
* @option float rate
*/
function ws_images_rate($params, $service)
{
$query = '
SELECT DISTINCT id
FROM '. IMAGES_TABLE .'
INNER JOIN '. IMAGE_CATEGORY_TABLE .' ON id=image_id
WHERE id='. $params['image_id']
.get_sql_condition_FandF(
array(
'forbidden_categories' => 'category_id',
'forbidden_images' => 'id',
),
' AND'
).'
LIMIT 1
;';
if (pwg_db_num_rows(pwg_query($query))==0)
{
return new PwgError(404, 'Invalid image_id or access denied');
}
include_once(PHPWG_ROOT_PATH.'include/functions_rate.inc.php');
$res = rate_picture($params['image_id'], (int)$params['rate']);
if ($res==false)
{
global $conf;
return new PwgError(403, 'Forbidden or rate not in '. implode(',', $conf['rate_items']));
}
return $res;
}
/**
* API method
* Returns a list of elements corresponding to a query search
* @param mixed[] $params
* @option string query
* @option int per_page
* @option int page
* @option string order (optional)
*/
function ws_images_search($params, $service)
{
include_once(PHPWG_ROOT_PATH .'include/functions_search.inc.php');
$images = array();
$where_clauses = ws_std_image_sql_filter($params, 'i.');
$order_by = ws_std_image_sql_order($params, 'i.');
$super_order_by = false;
if (!empty($order_by))
{
global $conf;
$conf['order_by'] = 'ORDER BY '.$order_by;
$super_order_by = true; // quick_search_result might be faster
}
$search_result = get_quick_search_results(
$params['query'],
array(
'super_order_by' => $super_order_by,
'images_where' => implode(' AND ', $where_clauses)
)
);
$image_ids = array_slice(
$search_result['items'],
$params['page']*$params['per_page'],
$params['per_page']
);
if (count($image_ids))
{
$query = '
SELECT *
FROM '. IMAGES_TABLE .'
WHERE id IN ('. implode(',', $image_ids) .')
;';
$result = pwg_query($query);
$image_ids = array_flip($image_ids);
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));
$images[ $image_ids[ $image['id'] ] ] = $image;
}
ksort($images, SORT_NUMERIC);
$images = array_values($images);
}
return array (
'paging' => new PwgNamedStruct(
array(
'page' => $params['page'],
'per_page' => $params['per_page'],
'count' => count($images),
'total_count' => count($search_result['items']),
)
),
'images' => new PwgNamedArray(
$images,
'image',
ws_std_get_image_xml_attributes()
)
);
}
/**
* API method
* Sets the level of an image
* @param mixed[] $params
* @option int image_id
* @option int level
*/
function ws_images_setPrivacyLevel($params, $service)
{
global $conf;
if (!in_array($params['level'], $conf['available_permission_levels']))
{
return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid level');
}
$query = '
UPDATE '. IMAGES_TABLE .'
SET level='. (int)$params['level'] .'
WHERE id IN ('. implode(',',$params['image_id']) .')
;';
$result = pwg_query($query);
pwg_activity('photo', $params['image_id'], 'edit');
$affected_rows = pwg_db_changes($result);
if ($affected_rows)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
invalidate_user_cache();
}
return $affected_rows;
}
/**
* API method
* Sets the rank of an image in a category
* @param mixed[] $params
* @option int image_id
* @option int category_id
* @option int rank
*/
function ws_images_setRank($params, $service)
{
if (count($params['image_id']) > 1)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
save_images_order(
$params['category_id'],
$params['image_id']
);
$query = '
SELECT
image_id
FROM '.IMAGE_CATEGORY_TABLE.'
WHERE category_id = '.$params['category_id'].'
ORDER BY `rank` ASC
;';
$image_ids = query2array($query, null, 'image_id');
// return data for client
return array(
'image_id' => $image_ids,
'category_id' => $params['category_id'],
);
}
// turns image_id into a simple int instead of array
$params['image_id'] = array_shift($params['image_id']);
if (empty($params['rank']))
{
return new PwgError(WS_ERR_MISSING_PARAM, 'rank is missing');
}
// 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');
}
// is the image associated to this category?
$query = '
SELECT COUNT(*)
FROM '. IMAGE_CATEGORY_TABLE .'
WHERE image_id = '. $params['image_id'] .'
AND category_id = '. $params['category_id'] .'
;';
list($count) = pwg_db_fetch_row(pwg_query($query));
if ($count == 0)
{
return new PwgError(404, 'This image is not associated to this category');
}
// what is the current higher rank for this category?
$query = '
SELECT MAX(`rank`) AS max_rank
FROM '. IMAGE_CATEGORY_TABLE .'
WHERE category_id = '. $params['category_id'] .'
;';
$row = pwg_db_fetch_assoc(pwg_query($query));
if (is_numeric($row['max_rank']))
{
if ($params['rank'] > $row['max_rank'])
{
$params['rank'] = $row['max_rank'] + 1;
}
}
else
{
$params['rank'] = 1;
}
// update rank for all other photos in the same category
$query = '
UPDATE '. IMAGE_CATEGORY_TABLE .'
SET `rank` = `rank` + 1
WHERE category_id = '. $params['category_id'] .'
AND `rank` IS NOT NULL
AND `rank` >= '. $params['rank'] .'
;';
pwg_query($query);
// set the new rank for the photo
$query = '
UPDATE '. IMAGE_CATEGORY_TABLE .'
SET `rank` = '. $params['rank'] .'
WHERE image_id = '. $params['image_id'] .'
AND category_id = '. $params['category_id'] .'
;';
pwg_query($query);
// return data for client
return array(
'image_id' => $params['image_id'],
'category_id' => $params['category_id'],
'rank' => $params['rank'],
);
}
/**
* API method
* Adds a file chunk
* @param mixed[] $params
* @option string data
* @option string original_sum
* @option string type = 'file'
* @option int position
*/
function ws_images_add_chunk($params, $service)
{
global $conf, $logger;
foreach ($params as $param_key => $param_value)
{
if ('data' == $param_key)
{
continue;
}
$logger->debug(sprintf(
'[ws_images_add_chunk] input param "%s" : "%s"',
$param_key,
is_null($param_value) ? 'NULL' : $param_value
), 'WS');
}
$upload_dir = $conf['upload_dir'].'/buffer';
// create the upload directory tree if not exists
if (!mkgetdir($upload_dir, MKGETDIR_DEFAULT&~MKGETDIR_DIE_ON_ERROR))
{
return new PwgError(500, 'error during buffer directory creation');
}
$filename = sprintf(
'%s-%s-%05u.block',
$params['original_sum'],
$params['type'],
$params['position']
);
$logger->debug('[ws_images_add_chunk] data length : '.strlen($params['data']), 'WS');
$bytes_written = file_put_contents(
$upload_dir.'/'.$filename,
base64_decode($params['data'])
);
if (false === $bytes_written)
{
return new PwgError(500,
'an error has occured while writting chunk '.$params['position'].' for '.$params['type']
);
}
}
/**
* API method
* Adds a file
* @param mixed[] $params
* @option int image_id
* @option string type = 'file'
* @option string sum
*/
function ws_images_addFile($params, $service)
{
global $conf, $logger;
$logger->debug(__FUNCTION__, 'WS', $params);
// what is the path and other infos about the photo?
$query = '
SELECT
path, file, md5sum,
width, height, filesize
FROM '. IMAGES_TABLE .'
WHERE id = '. $params['image_id'] .'
;';
$result = pwg_query($query);
if (pwg_db_num_rows($result) == 0)
{
return new PwgError(404, "image_id not found");
}
$image = pwg_db_fetch_assoc($result);
// since Piwigo 2.4 and derivatives, we do not take the imported "thumb" into account
if ('thumb' == $params['type'])
{
remove_chunks($image['md5sum'], $type);
return true;
}
// since Piwigo 2.4 and derivatives, we only care about the "original"
$original_type = 'file';
if ('high' == $params['type'])
{
$original_type = 'high';
}
$file_path = $conf['upload_dir'].'/buffer/'.$image['md5sum'].'-original';
merge_chunks($file_path, $image['md5sum'], $original_type);
chmod($file_path, 0644);
include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
// if we receive the "file", we only update the original if the "file" is
// bigger than current original
if ('file' == $params['type'])
{
$do_update = false;
$infos = pwg_image_infos($file_path);
foreach (array('width', 'height', 'filesize') as $image_info)
{
if ($infos[$image_info] > $image[$image_info])
{
$do_update = true;
}
}
if (!$do_update)
{
unlink($file_path);
return true;
}
}
$image_id = add_uploaded_file(
$file_path,
$image['file'],
null,
null,
$params['image_id'],
$image['md5sum'] // we force the md5sum to remain the same
);
}
/**
* API method
* Adds an image
* @param mixed[] $params
* @option string original_sum
* @option string original_filename (optional)
* @option string name (optional)
* @option string author (optional)
* @option string date_creation (optional)
* @option string comment (optional)
* @option string categories (optional) - "cat_id[,rank];cat_id[,rank]"
* @option string tags_ids (optional) - "tag_id,tag_id"
* @option int level
* @option bool check_uniqueness
* @option int image_id (optional)
*/
function ws_images_add($params, $service)
{
global $conf, $user, $logger;
foreach ($params as $param_key => $param_value)
{
$logger->debug(sprintf(
'[pwg.images.add] input param "%s" : "%s"',
$param_key,
is_null($param_value) ? 'NULL' : $param_value
), 'WS');
}
if ($params['image_id'] > 0)
{
$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');
}
}
// does the image already exists ?
if ($params['check_uniqueness'])
{
if ('md5sum' == $conf['uniqueness_mode'])
{
$where_clause = "md5sum = '".$params['original_sum']."'";
}
if ('filename' == $conf['uniqueness_mode'])
{
$where_clause = "file = '".$params['original_filename']."'";
}
$query = '
SELECT COUNT(*)
FROM '. IMAGES_TABLE .'
WHERE '. $where_clause .'
;';
list($counter) = pwg_db_fetch_row(pwg_query($query));
if ($counter != 0)
{
return new PwgError(500, 'file already exists');
}
}
// due to the new feature "derivatives" (multiple sizes) introduced for
// Piwigo 2.4, we only take the biggest photos sent on
// pwg.images.addChunk. If "high" is available we use it as "original"
// else we use "file".
remove_chunks($params['original_sum'], 'thumb');
if (isset($params['high_sum']))
{
$original_type = 'high';
remove_chunks($params['original_sum'], 'file');
}
else
{
$original_type = 'file';
}
$file_path = $conf['upload_dir'].'/buffer/'.$params['original_sum'].'-original';
merge_chunks($file_path, $params['original_sum'], $original_type);
chmod($file_path, 0644);
include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
$image_id = add_uploaded_file(
$file_path,
$params['original_filename'],
null, // categories
isset($params['level']) ? $params['level'] : null,
$params['image_id'] > 0 ? $params['image_id'] : null,
$params['original_sum']
);
$info_columns = array(
'name',
'author',
'comment',
'date_creation',
);
$update = array();
foreach ($info_columns as $key)
{
if (isset($params[$key]))
{
$update[$key] = $params[$key];
}
}
if (count(array_keys($update)) > 0)
{
single_update(
IMAGES_TABLE,
$update,
array('id' => $image_id)
);
}
$url_params = array('image_id' => $image_id);
// let's add links between the image and the categories
if (isset($params['categories']))
{
ws_add_image_category_relations($image_id, $params['categories']);
if (preg_match('/^\d+/', $params['categories'], $matches))
{
$category_id = $matches[0];
$query = '
SELECT id, name, permalink
FROM '. CATEGORIES_TABLE .'
WHERE id = '. $category_id .'
;';
$result = pwg_query($query);
$category = pwg_db_fetch_assoc($result);
$url_params['section'] = 'categories';
$url_params['category'] = $category;
}
}
// and now, let's create tag associations
if (isset($params['tag_ids']) and !empty($params['tag_ids']))
{
set_tags(
explode(',', $params['tag_ids']),
$image_id
);
}
invalidate_user_cache();
return array(
'image_id' => $image_id,
'url' => make_picture_url($url_params),
);
}
/**
* API method
* Adds a image (simple way)
* @param mixed[] $params
* @option int[] category
* @option string name (optional)
* @option string author (optional)
* @option string comment (optional)
* @option int level
* @option string|string[] tags
* @option int image_id (optional)
*/
function ws_images_addSimple($params, $service)
{
global $conf, $logger;
if (!isset($_FILES['image']))
{
return new PwgError(405, 'The image (file) is missing');
}
if (isset($_FILES['image']['error']) && $_FILES['image']['error'] != 0)
{
switch($_FILES['image']['error'])
{
case UPLOAD_ERR_INI_SIZE:
$message = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
break;
case UPLOAD_ERR_FORM_SIZE:
$message = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.';
break;
case UPLOAD_ERR_PARTIAL:
$message = 'The uploaded file was only partially uploaded.';
break;
case UPLOAD_ERR_NO_FILE:
$message = 'No file was uploaded.';
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = 'Missing a temporary folder.';
break;
case UPLOAD_ERR_CANT_WRITE:
$message = 'Failed to write file to disk.';
break;
case UPLOAD_ERR_EXTENSION:
$message = 'A PHP extension stopped the file upload. ' .
'PHP does not provide a way to ascertain which extension caused the file ' .
'upload to stop; examining the list of loaded extensions with phpinfo() may help.';
break;
default:
$message = "Error number {$_FILES['image']['error']} occurred while uploading a file.";
}
$logger->error(__FUNCTION__ . " " . $message);
return new PwgError(500, $message);
}
if ($params['image_id'] > 0)
{
$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');
}
}
include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
$image_id = add_uploaded_file(
$_FILES['image']['tmp_name'],
$_FILES['image']['name'],
$params['category'],
8,
$params['image_id'] > 0 ? $params['image_id'] : null
);
$info_columns = array(
'name',
'author',
'comment',
'level',
'date_creation',
);
$update = array();
foreach ($info_columns as $key)
{
if (isset($params[$key]))
{
$update[$key] = $params[$key];
}
}
single_update(
IMAGES_TABLE,
$update,
array('id' => $image_id)
);
if (isset($params['tags']) and !empty($params['tags']))
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$tag_ids = array();
if (is_array($params['tags']))
{
foreach ($params['tags'] as $tag_name)
{
$tag_ids[] = tag_id_from_tag_name($tag_name);
}
}
else
{
$tag_names = preg_split('~(?<!\\\),~', $params['tags']);
foreach ($tag_names as $tag_name)
{
$tag_ids[] = tag_id_from_tag_name(preg_replace('#\\\\*,#', ',', $tag_name));
}
}
add_tags($tag_ids, array($image_id));
}
$url_params = array('image_id' => $image_id);
if (!empty($params['category']))
{
$query = '
SELECT id, name, permalink
FROM '. CATEGORIES_TABLE .'
WHERE id = '. $params['category'][0] .'
;';
$result = pwg_query($query);
$category = pwg_db_fetch_assoc($result);
$url_params['section'] = 'categories';
$url_params['category'] = $category;
}
// update metadata from the uploaded file (exif/iptc), even if the sync
// was already performed by add_uploaded_file().
require_once(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
sync_metadata(array($image_id));
return array(
'image_id' => $image_id,
'url' => make_picture_url($url_params),
);
}
/**
* API method
* Adds a image (simple way)
* @param mixed[] $params
* @option int[] category
* @option string name (optional)
* @option string author (optional)
* @option string comment (optional)
* @option int level
* @option string|string[] tags
* @option int image_id (optional)
*/
function ws_images_upload($params, $service)
{
global $conf;
if (get_pwg_token() != $params['pwg_token'])
{
return new PwgError(403, 'Invalid security token');
}
// usleep(100000);
// if (!isset($_FILES['image']))
// {
// return new PwgError(405, 'The image (file) is missing');
// }
// file_put_contents('/tmp/plupload.log', "[".date('c')."] ".__FUNCTION__."\n\n", FILE_APPEND);
// file_put_contents('/tmp/plupload.log', '$_FILES = '.var_export($_FILES, true)."\n", FILE_APPEND);
// file_put_contents('/tmp/plupload.log', '$_POST = '.var_export($_POST, true)."\n", FILE_APPEND);
$upload_dir = $conf['upload_dir'].'/buffer';
// create the upload directory tree if not exists
if (!mkgetdir($upload_dir, MKGETDIR_DEFAULT&~MKGETDIR_DIE_ON_ERROR))
{
return new PwgError(500, 'error during buffer directory creation');
}
// Get a file name
if (isset($_REQUEST["name"]))
{
$fileName = $_REQUEST["name"];
}
elseif (!empty($_FILES))
{
$fileName = $_FILES["file"]["name"];
}
else
{
$fileName = uniqid("file_");
}
// change the name of the file in the buffer to avoid any unexpected
// extension. Function add_uploaded_file will eventually clean the mess.
$fileName = md5($fileName);
$filePath = $upload_dir.DIRECTORY_SEPARATOR.$fileName;
// Chunking might be enabled
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
// file_put_contents('/tmp/plupload.log', "[".date('c')."] ".__FUNCTION__.', '.$fileName.' '.($chunk+1).'/'.$chunks."\n", FILE_APPEND);
// Open temp file
if (!$out = @fopen("{$filePath}.part", $chunks ? "ab" : "wb"))
{
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}
if (!empty($_FILES))
{
if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"]))
{
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
}
// Read binary input stream and append it to temp file
if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb"))
{
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
}
}
else
{
if (!$in = @fopen("php://input", "rb"))
{
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
}
}
while ($buff = fread($in, 4096))
{
fwrite($out, $buff);
}
@fclose($out);
@fclose($in);
// Check if file has been uploaded
if (!$chunks || $chunk == $chunks - 1)
{
// Strip the temp .part suffix off
rename("{$filePath}.part", $filePath);
include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
$image_id = add_uploaded_file(
$filePath,
stripslashes($params['name']), // function add_uploaded_file will secure before insert
$params['category'],
$params['level'],
null // image_id = not provided, this is a new photo
);
$query = '
SELECT
id,
name,
representative_ext,
path
FROM '.IMAGES_TABLE.'
WHERE id = '.$image_id.'
;';
$image_infos = pwg_db_fetch_assoc(pwg_query($query));
$query = '
SELECT
COUNT(*) AS nb_photos
FROM '.IMAGE_CATEGORY_TABLE.'
WHERE category_id = '.$params['category'][0].'
;';
$category_infos = pwg_db_fetch_assoc(pwg_query($query));
$category_name = get_cat_display_name_from_id($params['category'][0], null);
return array(
'image_id' => $image_id,
'src' => DerivativeImage::thumb_url($image_infos),
'square_src' => DerivativeImage::url(ImageStdParams::get_by_type(IMG_SQUARE), $image_infos),
'name' => $image_infos['name'],
'category' => array(
'id' => $params['category'][0],
'nb_photos' => $category_infos['nb_photos'],
'label' => $category_name,
)
);
}
}
/**
* API method
* Adds a chunk of an image. Chunks don't have to be uploaded in the right sort order. When the last chunk is added, they get merged.
* @since 11
* @param mixed[] $params
* @option string username
* @option string password
* @option chunk int number of the chunk
* @option string chunk_sum MD5 sum of the chunk
* @option chunks int total number of chunks for this image
* @option string original_sum MD5 sum of the final image
* @option int[] category
* @option string filename
* @option string name (optional)
* @option string author (optional)
* @option string comment (optional)
* @option string date_creation (optional)
* @option int level
* @option string tag_ids (optional) - "tag_id,tag_id"
* @option int image_id (optional)
*/
function ws_images_uploadAsync($params, &$service)
{
global $conf, $user, $logger;
// the username/password parameters have been used in include/user.inc.php
// to authenticate the request (a much better time/place than here)
// additional check for some parameters
if (!preg_match('/^[a-fA-F0-9]{32}$/', $params['original_sum']))
{
return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid original_sum');
}
if ($params['image_id'] > 0)
{
$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, __FUNCTION__.' : image_id not found');
}
}
// handle upload error as in ws_images_addSimple
// if (isset($_FILES['image']['error']) && $_FILES['image']['error'] != 0)
$output_filepath_prefix = $conf['upload_dir'].'/buffer/'.$params['original_sum'].'-u'.$user['id'];
$chunkfile_path_pattern = $output_filepath_prefix.'-%03uof%03u.chunk';
$chunkfile_path = sprintf($chunkfile_path_pattern, $params['chunk']+1, $params['chunks']);
// create the upload directory tree if not exists
if (!mkgetdir(dirname($chunkfile_path), MKGETDIR_DEFAULT&~MKGETDIR_DIE_ON_ERROR))
{
return new PwgError(500, 'error during buffer directory creation');
}
secure_directory(dirname($chunkfile_path));
// move uploaded file
move_uploaded_file($_FILES['file']['tmp_name'], $chunkfile_path);
$logger->debug(__FUNCTION__.' uploaded '.$chunkfile_path);
// MD5 checksum
$chunk_md5 = md5_file($chunkfile_path);
if ($chunk_md5 != $params['chunk_sum'])
{
unlink($chunkfile_path);
$logger->error(__FUNCTION__.' '.$chunkfile_path.' MD5 checksum mismatched');
return new PwgError(500, "MD5 checksum chunk file mismatched");
}
// are all chunks uploaded?
$chunk_ids_uploaded = array();
for ($i = 1; $i <= $params['chunks']; $i++)
{
$chunkfile = sprintf($chunkfile_path_pattern, $i, $params['chunks']);
if ( file_exists($chunkfile) && ($fp = fopen($chunkfile, "rb"))!==false )
{
$chunk_ids_uploaded[] = $i;
fclose($fp);
}
}
if ($params['chunks'] != count($chunk_ids_uploaded))
{
// all chunks are not yet available
$logger->debug(__FUNCTION__.' all chunks are not uploaded yet, maybe on next chunk, exit for now');
return array('message' => 'chunks uploaded = '.implode(',', $chunk_ids_uploaded));
}
// all chunks available
$logger->debug(__FUNCTION__.' '.$params['original_sum'].' '.$params['chunks'].' chunks available, try now to get lock for merging');
$output_filepath = $output_filepath_prefix.'.merged';
// chunks already being merged?
if ( file_exists($output_filepath) && ($fp = fopen($output_filepath, "rb"))!==false )
{
// merge file already exists
fclose($fp);
$logger->error(__FUNCTION__.' '.$output_filepath.' already exists, another merge is under process');
return array('message' => 'chunks uploaded = '.implode(',', $chunk_ids_uploaded));
}
// create merged and open it for writing only
$fp = fopen($output_filepath, "wb");
if ( !$fp )
{
// unable to create file and open it for writing only
$logger->error(__FUNCTION__.' '.$chunkfile_path.' unable to create merge file');
return new PwgError(500, 'error while creating merged '.$chunkfile_path);
}
// acquire an exclusive lock and keep it until merge completes
// this postpones another uploadAsync task running in another thread
if (!flock($fp, LOCK_EX))
{
// unable to obtain lock
fclose($fp);
$logger->error(__FUNCTION__.' '.$chunkfile_path.' unable to obtain lock');
return new PwgError(500, 'error while locking merged '.$chunkfile_path);
}
$logger->debug(__FUNCTION__.' lock obtained to merge chunks');
// loop over all chunks
foreach ($chunk_ids_uploaded as $chunk_id)
{
$chunkfile_path = sprintf($chunkfile_path_pattern, $chunk_id, $params['chunks']);
// chunk deleted by preceding merge?
if (!file_exists($chunkfile_path))
{
// cancel merge
$logger->error(__FUNCTION__.' '.$chunkfile_path.' already merged');
flock($fp, LOCK_UN);
fclose($fp);
return array('message' => 'chunks uploaded = '.implode(',', $chunk_ids_uploaded));
}
if (!fwrite($fp, file_get_contents($chunkfile_path)))
{
// could not append chunk
$logger->error(__FUNCTION__.' error merging chunk '.$chunkfile_path);
flock($fp, LOCK_UN);
fclose($fp);
// delete merge file without returning an error
@unlink($output_filepath);
return new PwgError(500, 'error while merging chunk '.$chunk_id);
}
$logger->debug(__FUNCTION__.' original_sum='.$params['original_sum'].', chunk '.$chunk_id.'/'.$params['chunks'].' merged');
// delete chunk and clear cache
unlink($chunkfile_path);
}
// flush output before releasing lock
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
$logger->debug(__FUNCTION__.' merged file '.$output_filepath.' saved');
// MD5 checksum
$merged_md5 = md5_file($output_filepath);
if ($merged_md5 != $params['original_sum'])
{
unlink($output_filepath);
$logger->error(__FUNCTION__.' '.$output_filepath.' MD5 checksum mismatched!');
return new PwgError(500, "MD5 checksum merged file mismatched");
}
$logger->debug(__FUNCTION__.' '.$output_filepath.' MD5 checksum OK');
include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
$image_id = add_uploaded_file(
$output_filepath,
$params['filename'],
$params['category'],
$params['level'],
$params['image_id'],
$params['original_sum']
);
$logger->debug(__FUNCTION__.' image_id after add_uploaded_file = '.$image_id);
// and now, let's create tag associations
if (isset($params['tag_ids']) and !empty($params['tag_ids']))
{
set_tags(
explode(',', $params['tag_ids']),
$image_id
);
}
// time to set other infos
$info_columns = array(
'name',
'author',
'comment',
'date_creation',
);
$update = array();
foreach ($info_columns as $key)
{
if (isset($params[$key]))
{
$update[$key] = $params[$key];
}
}
if (count(array_keys($update)) > 0)
{
single_update(
IMAGES_TABLE,
$update,
array('id' => $image_id)
);
}
// final step, reset user cache
invalidate_user_cache();
// trick to bypass get_sql_condition_FandF
if (!empty($params['level']) and $params['level'] > $user['level'])
{
// this will not persist
$user['level'] = $params['level'];
}
// delete chunks older than a week
$now = time();
foreach (glob($conf['upload_dir'].'/buffer/'."*.chunk") as $file)
{
if (is_file($file))
{
if ($now - filemtime($file) >= 60 * 60 * 24 * 7) // 7 days
{
$logger->info(__FUNCTION__.' delete '.$file);
unlink($file);
}
else
{
$logger->debug(__FUNCTION__.' keep '.$file);
}
}
}
// delete merged older than a week
foreach (glob($conf['upload_dir'].'/buffer/'."*.merged") as $file)
{
if (is_file($file))
{
if ($now - filemtime($file) >= 60 * 60 * 24 * 7) // 7 days
{
$logger->info(__FUNCTION__.' delete '.$file);
unlink($file);
}
else
{
$logger->debug(__FUNCTION__.' keep '.$file);
}
}
}
return $service->invoke('pwg.images.getInfo', array('image_id' => $image_id));
}
/**
* API method
* Check if an image exists by it's name or md5 sum
* @param mixed[] $params
* @option string md5sum_list (optional)
* @option string filename_list (optional)
*/
function ws_images_exist($params, $service)
{
global $conf, $logger;
$logger->debug(__FUNCTION__, 'WS', $params);
$split_pattern = '/[\s,;\|]/';
$result = array();
if ('md5sum' == $conf['uniqueness_mode'])
{
// search among photos the list of photos already added, based on md5sum list
$md5sums = preg_split(
$split_pattern,
$params['md5sum_list'],
-1,
PREG_SPLIT_NO_EMPTY
);
$query = '
SELECT id, md5sum
FROM '. IMAGES_TABLE .'
WHERE md5sum IN (\''. implode("','", $md5sums) .'\')
;';
$id_of_md5 = query2array($query, 'md5sum', 'id');
foreach ($md5sums as $md5sum)
{
$result[$md5sum] = null;
if (isset($id_of_md5[$md5sum]))
{
$result[$md5sum] = $id_of_md5[$md5sum];
}
}
}
elseif ('filename' == $conf['uniqueness_mode'])
{
// search among photos the list of photos already added, based on
// filename list
$filenames = preg_split(
$split_pattern,
$params['filename_list'],
-1,
PREG_SPLIT_NO_EMPTY
);
$query = '
SELECT id, file
FROM '.IMAGES_TABLE.'
WHERE file IN (\''. implode("','", $filenames) .'\')
;';
$id_of_filename = query2array($query, 'file', 'id');
foreach ($filenames as $filename)
{
$result[$filename] = null;
if (isset($id_of_filename[$filename]))
{
$result[$filename] = $id_of_filename[$filename];
}
}
}
return $result;
}
/**
* API method
* Check is file has been update
* @param mixed[] $params
* @option int image_id
* @option string file_sum
*/
function ws_images_checkFiles($params, $service)
{
global $logger;
$logger->debug(__FUNCTION__, 'WS', $params);
$query = '
SELECT path
FROM '. IMAGES_TABLE .'
WHERE id = '. $params['image_id'] .'
;';
$result = pwg_query($query);
if (pwg_db_num_rows($result) == 0)
{
return new PwgError(404, 'image_id not found');
}
list($path) = pwg_db_fetch_row($result);
$ret = array();
if (isset($params['thumbnail_sum']))
{
// We always say the thumbnail is equal to create no reaction on the
// other side. Since Piwigo 2.4 and derivatives, the thumbnails and web
// sizes are always generated by Piwigo
$ret['thumbnail'] = 'equals';
}
if (isset($params['high_sum']))
{
$ret['file'] = 'equals';
$compare_type = 'high';
}
elseif (isset($params['file_sum']))
{
$compare_type = 'file';
}
if (isset($compare_type))
{
$logger->debug(__FUNCTION__.', md5_file($path) = '.md5_file($path), 'WS');
if (md5_file($path) != $params[$compare_type.'_sum'])
{
$ret[$compare_type] = 'differs';
}
else
{
$ret[$compare_type] = 'equals';
}
}
$logger->debug(__FUNCTION__, 'WS', $ret);
return $ret;
}
/**
* API method
* Sets details of an image
* @param mixed[] $params
* @option int image_id
* @option string file (optional)
* @option string name (optional)
* @option string author (optional)
* @option string date_creation (optional)
* @option string comment (optional)
* @option string categories (optional) - "cat_id[,rank];cat_id[,rank]"
* @option string tags_ids (optional) - "tag_id,tag_id"
* @option int level (optional)
* @option string single_value_mode
* @option string multiple_value_mode
*/
function ws_images_setInfo($params, $service)
{
global $conf;
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$query='
SELECT *
FROM '. IMAGES_TABLE .'
WHERE id = '. $params['image_id'] .'
;';
$result = pwg_query($query);
if (pwg_db_num_rows($result) == 0)
{
return new PwgError(404, 'image_id not found');
}
$image_row = pwg_db_fetch_assoc($result);
// database registration
$update = array();
$info_columns = array(
'name',
'author',
'comment',
'level',
'date_creation',
);
foreach ($info_columns as $key)
{
if (isset($params[$key]))
{
if (!$conf['allow_html_descriptions'])
{
$params[$key] = strip_tags($params[$key], '<b><strong><em><i>');
}
// TODO do not strip tags if pwg_token is provided (and valid)
$params[$key] = strip_tags($params[$key]);
if ('fill_if_empty' == $params['single_value_mode'])
{
if (empty($image_row[$key]))
{
$update[$key] = $params[$key];
}
}
elseif ('replace' == $params['single_value_mode'])
{
$update[$key] = $params[$key];
}
else
{
return new PwgError(500,
'[ws_images_setInfo]'
.' invalid parameter single_value_mode "'.$params['single_value_mode'].'"'
.', possible values are {fill_if_empty, replace}.'
);
}
}
}
if (isset($params['file']))
{
if (!empty($image_row['storage_category_id']))
{
return new PwgError(500,
'[ws_images_setInfo] updating "file" is forbidden on photos added by synchronization'
);
}
// prevent XSS, remove HTML tags
$update['file'] = strip_tags($params['file']);
if (empty($update['file']))
{
unset($update['file']);
}
}
if (count(array_keys($update)) > 0)
{
$update['id'] = $params['image_id'];
single_update(
IMAGES_TABLE,
$update,
array('id' => $update['id'])
);
pwg_activity('photo', $update['id'], 'edit');
}
if (isset($params['categories']))
{
ws_add_image_category_relations(
$params['image_id'],
$params['categories'],
('replace' == $params['multiple_value_mode'] ? true : false)
);
}
// and now, let's create tag associations
if (isset($params['tag_ids']))
{
$tag_ids = array();
foreach (explode(',', $params['tag_ids']) as $candidate)
{
$candidate = trim($candidate);
if (preg_match(PATTERN_ID, $candidate))
{
$tag_ids[] = $candidate;
}
}
if ('replace' == $params['multiple_value_mode'])
{
set_tags(
$tag_ids,
$params['image_id']
);
}
elseif ('append' == $params['multiple_value_mode'])
{
add_tags(
$tag_ids,
array($params['image_id'])
);
}
else
{
return new PwgError(500,
'[ws_images_setInfo]'
.' invalid parameter multiple_value_mode "'.$params['multiple_value_mode'].'"'
.', possible values are {replace, append}.'
);
}
}
invalidate_user_cache();
}
/**
* API method
* Deletes an image
* @param mixed[] $params
* @option int|int[] image_id
* @option string pwg_token
*/
function ws_images_delete($params, $service)
{
if (get_pwg_token() != $params['pwg_token'])
{
return new PwgError(403, 'Invalid security token');
}
if (!is_array($params['image_id']))
{
$params['image_id'] = preg_split(
'/[\s,;\|]/',
$params['image_id'],
-1,
PREG_SPLIT_NO_EMPTY
);
}
$params['image_id'] = array_map('intval', $params['image_id']);
$image_ids = array();
foreach ($params['image_id'] as $image_id)
{
if ($image_id > 0)
{
$image_ids[] = $image_id;
}
}
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$ret = delete_elements($image_ids, true);
invalidate_user_cache();
return $ret;
}
/**
* API method
* Checks if Piwigo is ready for upload
* @param mixed[] $params
*/
function ws_images_checkUpload($params, $service)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php');
$ret['message'] = ready_for_upload_message();
$ret['ready_for_upload'] = true;
if (!empty($ret['message']))
{
$ret['ready_for_upload'] = false;
}
return $ret;
}
/**
* API method
* Empties the lounge, where photos may wait before taking off.
* @since 12
* @param mixed[] $params
*/
function ws_images_emptyLounge($params, $service)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$ret = array('rows' => empty_lounge());
return $ret;
}
/**
* API method
* Empties the lounge, where photos may wait before taking off.
* @since 12
* @param mixed[] $params
*/
function ws_images_uploadCompleted($params, $service)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
if (get_pwg_token() != $params['pwg_token'])
{
return new PwgError(403, 'Invalid security token');
}
if (!is_array($params['image_id']))
{
$params['image_id'] = preg_split(
'/[\s,;\|]/',
$params['image_id'],
-1,
PREG_SPLIT_NO_EMPTY
);
}
$params['image_id'] = array_map('intval', $params['image_id']);
$image_ids = array();
foreach ($params['image_id'] as $image_id)
{
if ($image_id > 0)
{
$image_ids[] = $image_id;
}
}
// the list of images moved from the lounge might not be the same than
// $image_ids (canbe a subset or more image_ids from another upload too)
$moved_from_lounge = empty_lounge();
trigger_notify(
'ws_images_uploadCompleted',
array(
'image_ids' => $image_ids,
'category_id' => $params['category_id'],
'moved_from_lounge' => $moved_from_lounge,
)
);
return array('moved_from_lounge' => $moved_from_lounge);
}
/**
* API method
* add md5sum at photos, by block. Returns how md5sum were added and how many are remaining.
* @param mixed[] $params
* @option int block_size
*/
function ws_images_setMd5sum($params, $service)
{
if (get_pwg_token() != $params['pwg_token'])
{
return new PwgError(403, 'Invalid security token');
}
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$md5sum_ids_to_add = array_slice(get_photos_no_md5sum(), 0, $params['block_size']);
$added_count = add_md5sum($md5sum_ids_to_add);
return array(
'nb_added' => $added_count,
'nb_no_md5sum' => count(get_photos_no_md5sum()),
);
}
/**
* API method
* Synchronize metadatas photos. Returns how many metadatas were sync.
* @param mixed[] $params
* @option int image_id
*/
function ws_images_syncMetadata($params, $service)
{
if (get_pwg_token() != $params['pwg_token'])
{
return new PwgError(403, 'Invalid security token');
}
$query = '
SELECT id
FROM '.IMAGES_TABLE.'
WHERE id IN ('.implode(', ', $params['image_id']).')
;';
$params['image_id'] = query2array($query, null, 'id');
if (empty($params['image_id']))
{
return new PwgError(403, 'No image found');
}
include_once(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
sync_metadata($params['image_id']);
return array(
'nb_synchronized' => count($params['image_id'])
);
}
/**
* API method
* Deletes orphan photos, by block. Returns how many orphans were deleted and how many are remaining.
* @param mixed[] $params
* @option int block_size
*/
function ws_images_deleteOrphans($params, $service)
{
if (get_pwg_token() != $params['pwg_token'])
{
return new PwgError(403, 'Invalid security token');
}
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$orphan_ids_to_delete = array_slice(get_orphans(), 0, $params['block_size']);
$deleted_count = delete_elements($orphan_ids_to_delete, true);
return array(
'nb_deleted' => $deleted_count,
'nb_orphans' => count(get_orphans()),
);
}
?>