From 2ce5c31971720746891a31ebdad36b8be5de7538 Mon Sep 17 00:00:00 2001 From: Hugo Dimpfelmoser Date: Thu, 23 Nov 2023 09:37:57 +0100 Subject: [PATCH 1/3] issue #628 save_user_coords --- okapi/core/OkapiServiceRunner.php | 1 + .../caches/save_user_coords/WebService.php | 108 ++++++++++++++++++ .../services/caches/save_user_coords/docs.xml | 29 +++++ 3 files changed, 138 insertions(+) create mode 100644 okapi/services/caches/save_user_coords/WebService.php create mode 100644 okapi/services/caches/save_user_coords/docs.xml diff --git a/okapi/core/OkapiServiceRunner.php b/okapi/core/OkapiServiceRunner.php index a1d0abba..5433e5d9 100644 --- a/okapi/core/OkapiServiceRunner.php +++ b/okapi/core/OkapiServiceRunner.php @@ -39,6 +39,7 @@ class OkapiServiceRunner 'services/caches/geocaches', 'services/caches/mark', 'services/caches/save_personal_notes', + 'services/caches/save_user_coords', 'services/caches/formatters/gpx', 'services/caches/formatters/garmin', 'services/caches/formatters/ggz', diff --git a/okapi/services/caches/save_user_coords/WebService.php b/okapi/services/caches/save_user_coords/WebService.php new file mode 100644 index 00000000..bdac1be3 --- /dev/null +++ b/okapi/services/caches/save_user_coords/WebService.php @@ -0,0 +1,108 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + + $user_coords = $request->get_parameter('user_coords'); + if ($user_coords == null) + throw new ParamMissing('user_coords'); + $parts = explode('|', $user_coords); + if (count($parts) != 2) + throw new InvalidParam('user_coords', "Expecting 2 pipe-separated parts, got ".count($parts)."."); + foreach ($parts as &$part_ref) + { + if (!preg_match("/^-?[0-9]+(\.?[0-9]*)$/", $part_ref)) + throw new InvalidParam('user_coords', "'$part_ref' is not a valid float number."); + $part_ref = floatval($part_ref); + } + list($latitude, $longitude) = $parts; + + # Verify cache_code + + $cache_code = $request->get_parameter('cache_code'); + if ($cache_code == null) + throw new ParamMissing('cache_code'); + $geocache = OkapiServiceRunner::call( + 'services/caches/geocache', + new OkapiInternalRequest($request->consumer, $request->token, array( + 'cache_code' => $cache_code, + 'fields' => 'internal_id' + )) + ); + $cache_id = $geocache['internal_id']; + + self::update_notes($cache_id, $request->token->user_id, $latitude, $longitude); + + $ret_value = 'ok'; + + $result = array( + 'status' => $ret_value + ); + return Okapi::formatted_response($request, $result); + } + + private static function update_notes($cache_id, $user_id, $latitude, $longitude) + { + /* See: + * + * - https://github.com/OpencachingDeutschland/oc-server3/tree/development/htdocs/src/Oc/Libse/CacheNote + * - https://www.opencaching.de/okapi/devel/dbstruct + */ + + $rs = Db::query(" + select max(id) as id + from coordinates + where + type = 2 -- personal note + and cache_id = '".Db::escape_string($cache_id)."' + and user_id = '".Db::escape_string($user_id)."' + "); + $id = null; + if($row = Db::fetch_assoc($rs)) { + $id = $row['id']; + } + if ($id == null) { + Db::query(" + insert into coordinates ( + type, latitude, longitude, cache_id, user_id + ) values ( + 2, + '".Db::escape_string($latitude)."', + '".Db::escape_string($longitude)."', + '".Db::escape_string($cache_id)."', + '".Db::escape_string($user_id)."' + ) + "); + } else { + Db::query(" + update coordinates + set latitude = '".Db::escape_string($latitude)."', + longitude = '".Db::escape_string($longitude)."', + where + id = '".Db::escape_string($id)."' + and type = 2 + "); + } + } + +} diff --git a/okapi/services/caches/save_user_coords/docs.xml b/okapi/services/caches/save_user_coords/docs.xml new file mode 100644 index 00000000..9689200d --- /dev/null +++ b/okapi/services/caches/save_user_coords/docs.xml @@ -0,0 +1,29 @@ + + Update personal coordinates of a geocache + 629 + +

This method allows your users to update the coordinates of their + personal geocache coordinates.

+ +

Current personal coordinates for the geocache can be retrieved + using the alt_wpts field in the + services/caches/geocache + method.

+
+ +

Code of the geocache

+
+ +

The coordinates are defined by a string in the format "lat|lon"

+

Use positive numbers for latitudes in the northern hemisphere and longitudes + in the eastern hemisphere (and negative for southern and western hemispheres + accordingly). These are full degrees with a dot as a decimal point (ex. "48.7|15.89").

+
+ + +

A dictionary of the following structure:

+
    +
  • status - ok
  • +
+
+
From 1c97e1b04db9f886b70940f3d9c8090f84682f1f Mon Sep 17 00:00:00 2001 From: Hugo Dimpfelmoser Date: Tue, 28 Nov 2023 13:54:14 +0100 Subject: [PATCH 2/3] issue#627 listservices --- okapi/core/OkapiServiceRunner.php | 7 ++ .../services/lists/add_caches/WebService.php | 67 ++++++++++ okapi/services/lists/add_caches/docs.xml | 23 ++++ okapi/services/lists/create/WebService.php | 93 ++++++++++++++ okapi/services/lists/create/docs.xml | 46 +++++++ okapi/services/lists/delete/WebService.php | 57 +++++++++ okapi/services/lists/delete/docs.xml | 20 +++ .../services/lists/get_caches/WebService.php | 61 +++++++++ okapi/services/lists/get_caches/docs.xml | 19 +++ okapi/services/lists/query/WebService.php | 70 +++++++++++ okapi/services/lists/query/docs.xml | 40 ++++++ .../lists/remove_caches/WebService.php | 74 +++++++++++ okapi/services/lists/remove_caches/docs.xml | 23 ++++ okapi/services/lists/update/WebService.php | 117 ++++++++++++++++++ okapi/services/lists/update/docs.xml | 50 ++++++++ 15 files changed, 767 insertions(+) create mode 100644 okapi/services/lists/add_caches/WebService.php create mode 100644 okapi/services/lists/add_caches/docs.xml create mode 100644 okapi/services/lists/create/WebService.php create mode 100644 okapi/services/lists/create/docs.xml create mode 100644 okapi/services/lists/delete/WebService.php create mode 100644 okapi/services/lists/delete/docs.xml create mode 100644 okapi/services/lists/get_caches/WebService.php create mode 100644 okapi/services/lists/get_caches/docs.xml create mode 100644 okapi/services/lists/query/WebService.php create mode 100644 okapi/services/lists/query/docs.xml create mode 100644 okapi/services/lists/remove_caches/WebService.php create mode 100644 okapi/services/lists/remove_caches/docs.xml create mode 100644 okapi/services/lists/update/WebService.php create mode 100644 okapi/services/lists/update/docs.xml diff --git a/okapi/core/OkapiServiceRunner.php b/okapi/core/OkapiServiceRunner.php index 5433e5d9..d87eeedf 100644 --- a/okapi/core/OkapiServiceRunner.php +++ b/okapi/core/OkapiServiceRunner.php @@ -44,6 +44,13 @@ class OkapiServiceRunner 'services/caches/formatters/garmin', 'services/caches/formatters/ggz', 'services/caches/map/tile', + 'services/lists/add_caches', + 'services/lists/create', + 'services/lists/delete', + 'services/lists/get_caches', + 'services/lists/remove_caches', + 'services/lists/query', + 'services/lists/update', 'services/logs/capabilities', 'services/logs/delete', 'services/logs/edit', diff --git a/okapi/services/lists/add_caches/WebService.php b/okapi/services/lists/add_caches/WebService.php new file mode 100644 index 00000000..5c3894bf --- /dev/null +++ b/okapi/services/lists/add_caches/WebService.php @@ -0,0 +1,67 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false + ); + + $user_id = $request->token->user_id; + + $listId = $request->get_parameter('list_id'); + $cacheCodes = $request->get_parameter('cache_codes'); + + if (empty($listId)) { + throw new InvalidParam('list_id', 'list_id is mandatory and must not be empty.'); + } + + if (empty($cacheCodes)) { + throw new InvalidParam('cache_codes', 'cache_codes is mandatory and must not be empty.'); + } + + $cacheCodesArray = array_unique(explode('|', $cacheCodes)); + + // Check the length + if (count($cacheCodesArray) > 500) { + throw new InvalidParam('cache_codes', 'The number of cache codes exceeds the limit of 500.'); + } + + // Escape cache codes and build the SQL query + $escapedCacheCodes = implode("','", array_map('\okapi\core\Db::escape_string', $cacheCodesArray)); + + // Fetch cache_ids from the caches table using INSERT IGNORE + $rs = Db::query(" + INSERT IGNORE INTO cache_list_items (cache_list_id, cache_id) + SELECT '$listId', cache_id + FROM caches + WHERE wp_oc IN ('$escapedCacheCodes') + "); + + $insertedCount = $rs->rowCount(); // Get the number of affected rows + + $result = array( + 'success' => true, + 'added_count' => $insertedCount + ); + + return Okapi::formatted_response($request, $result); + } +} + diff --git a/okapi/services/lists/add_caches/docs.xml b/okapi/services/lists/add_caches/docs.xml new file mode 100644 index 00000000..5146b97c --- /dev/null +++ b/okapi/services/lists/add_caches/docs.xml @@ -0,0 +1,23 @@ + + Add Caches To List + 627 + +

This method is used to add geocaches to an existing list.

+
+ +

The id of a list. List IDs can be obtained by ::service/lists/query

+
+ +

A pipe separated list of cache_codes to be added to the list.

+

Up to 500 geoaches can be added to the list by one request to + this method

+
+ + +

A dictionary of the following structure:

+
    +
  • success - true
  • +
  • added_count - number of geocaches added to the list
  • +
+
+
diff --git a/okapi/services/lists/create/WebService.php b/okapi/services/lists/create/WebService.php new file mode 100644 index 00000000..9e949d57 --- /dev/null +++ b/okapi/services/lists/create/WebService.php @@ -0,0 +1,93 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false + ); + + if (Settings::get('OC_BRANCH') == 'oc.de') + { + $user_id = $request->token->user_id; + + $listName = $request->get_parameter('list_name'); + $listDescription = $request->get_parameter('list_description'); + $listStatus = $request->get_parameter('list_status'); + $isWatched = $request->get_parameter('is_watched'); + $listPassword = $request->get_parameter('list_password'); + + if (empty($listName)) { + throw new InvalidParam('list_name', 'list_name is mandatory and must not be empty.'); + } + + $insertFields = array( + 'name' => Db::escape_string($listName), + 'user_id' => Db::escape_string($user_id) + ); + + if (!empty($listDescription)) { + $insertFields['description'] = Db::escape_string($listDescription); + } + + if ($listStatus !== null && $listStatus !== '') { + $listStatus = (int)$listStatus; + if (!in_array($listStatus, [0, 2, 3])) { + throw new InvalidParam('list_status', 'list_status must be a valid value (0, 2, 3).'); + } + $insertFields['is_public'] = $listStatus; + + // Handle list_password only if list_status is 0 (private) + if ($listStatus == 0) { + if (isset($listPassword) && $listPassword !== '') { + $insertFields['password'] = substr(Db::escape_string($listPassword), 0, 16); + } + } + } + + $columns = implode(', ', array_keys($insertFields)); + $values = "'" . implode("', '", $insertFields) . "'"; + + $insertQuery = "INSERT INTO cache_lists ($columns) VALUES ($values)"; + Db::query($insertQuery); + + $listId = Db::last_insert_id(); + + // Handle is_watched + if ($isWatched !== null && $isWatched !== '') { + $isWatched = (int)$isWatched; + if (!in_array($isWatched, [0, 1])) { + throw new InvalidParam('is_watched', 'is_watched must be a valid value (0, 1).'); + } + + // Insert a new record + Db::query("INSERT INTO cache_list_watches (cache_list_id, user_id, is_watched) VALUES (LAST_INSERT_ID(), '$user_id', $isWatched)"); + } + + $result = array( + 'success' => true, + 'message' => 'Cache list created successfully.', + 'list_id' => $listId + ); + } + return Okapi::formatted_response($request, $result); + } +} + diff --git a/okapi/services/lists/create/docs.xml b/okapi/services/lists/create/docs.xml new file mode 100644 index 00000000..f8d9fd05 --- /dev/null +++ b/okapi/services/lists/create/docs.xml @@ -0,0 +1,46 @@ + + Create Cache List + 627 + +

This method creates a list for adding geocaches to. Only the list is created, + no geocaches are added to it during the create. For adding and removing geocaches + to/from the list use specific methods within the ::services/lists namespace +

+
+ +

A string, defining the human readable name of the list

+
+ +

A string via which a description of the list's purpose and + potentially content can be defined.

+
+ +

This parameter can take the following values: +

    +
  • 0 - The list is private
  • +
  • 2 - The list is public
  • +
  • 3 - The list is public and is visible in cache listings
  • +
+

+
+ +

This parameter allows to add a password to a private list. The password + has no meaning if the list is public. The first 16 alphanumeric characters + are used as a password. No encryption is performed in the password, instead + it is stored in the database in plain text.

+
+ +

A boolean, either 0, 1, false, true. If set to 1 or true + a user wants to get a notification if the content of the list + changes.

+
+ + +

A dictionary of the following structure:

+
    +
  • success - true
  • +
  • message - Cache list created successfully
  • +
  • list_id - The id of the list by which it can be referenced
  • +
+
+
diff --git a/okapi/services/lists/delete/WebService.php b/okapi/services/lists/delete/WebService.php new file mode 100644 index 00000000..89dd1ea8 --- /dev/null +++ b/okapi/services/lists/delete/WebService.php @@ -0,0 +1,57 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false + ); + + if (Settings::get('OC_BRANCH') == 'oc.de') + { + $user_id = $request->token->user_id; + + $listId = $request->get_parameter('list_id'); + + if (empty($listId) || !is_numeric($listId)) { + throw new InvalidParam('list_id', 'list_id is mandatory and must be numeric.'); + } + + // Check if the list exists + $countQuery = Db::query("SELECT COUNT(*) AS count FROM cache_lists WHERE id = '$listId' AND user_id = '$user_id'"); + $listExists = Db::fetch_assoc($countQuery)['count']; + if ($listExists == 0) { + throw new InvalidParam('list_id', 'The specified list does not exist.'); + } + + // Proceed with the deletion process + Db::query("DELETE FROM cache_lists WHERE id = '$listId'"); + Db::query("DELETE FROM cache_list_watches WHERE cache_list_id = '$listId'"); + Db::query("DELETE FROM cache_list_items WHERE cache_list_id = '$listId'"); + + $result = array( + 'success' => true, + 'message' => 'Cache list deleted successfully.' + ); + } + return Okapi::formatted_response($request, $result); + } +} + diff --git a/okapi/services/lists/delete/docs.xml b/okapi/services/lists/delete/docs.xml new file mode 100644 index 00000000..f6d218cc --- /dev/null +++ b/okapi/services/lists/delete/docs.xml @@ -0,0 +1,20 @@ + + Delete Cache List + 627 + +

This method is used to delete a geocache list. The geocache objects + that were on the list will not be touched in any way.

+
+ +

The id of the list to be removed. IDs can be obtained by + the service ::services/lists/query

+
+ + +

A dictionary of the following structure:

+
    +
  • success - true
  • +
  • message - cache list deleted successfully
  • +
+
+
diff --git a/okapi/services/lists/get_caches/WebService.php b/okapi/services/lists/get_caches/WebService.php new file mode 100644 index 00000000..b5e6e751 --- /dev/null +++ b/okapi/services/lists/get_caches/WebService.php @@ -0,0 +1,61 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false + ); + + $user_id = $request->token->user_id; + $listId = $request->get_parameter('list_id'); + + if (empty($listId)) { + throw new InvalidParam('list_id', 'list_id is mandatory and must not be empty.'); + } + + // Fetch cache_ids associated with the specified list + $cacheIdsArray = Db::select_column(" + SELECT cache_id + FROM cache_list_items + WHERE cache_list_id = '$listId' + "); + + $cacheCount = count($cacheIdsArray); + + // Fetch cache_codes based on cache_ids + $cacheCodesArray = array(); + + if (!empty($cacheIdsArray)) { + $cacheIds = implode(',', $cacheIdsArray); + $cacheCodesArray = Db::select_column( + "SELECT wp_oc FROM caches WHERE cache_id IN ($cacheIds)" + ); + } + + $result = array( + 'success' => true, + 'cache_codes' => implode('|', $cacheCodesArray), + 'cache_count' => $cacheCount + ); + + return Okapi::formatted_response($request, $result); + } +} + diff --git a/okapi/services/lists/get_caches/docs.xml b/okapi/services/lists/get_caches/docs.xml new file mode 100644 index 00000000..c6885d8a --- /dev/null +++ b/okapi/services/lists/get_caches/docs.xml @@ -0,0 +1,19 @@ + + Get Caches On A List + 627 + +

This method is used to get the geocache codes for the caches that are on the list.

+
+ +

The id of a list. List IDs can be obtained by ::service/lists/query

+
+ + +

A dictionary of the following structure:

+
    +
  • success - true
  • +
  • cache_codes - A pipe separated string of cache_codes that are on the list
  • +
  • cache_count - the number of geocaches on the list
  • +
+
+
diff --git a/okapi/services/lists/query/WebService.php b/okapi/services/lists/query/WebService.php new file mode 100644 index 00000000..f0f3e490 --- /dev/null +++ b/okapi/services/lists/query/WebService.php @@ -0,0 +1,70 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false // if the installation doesn't support it + ); + + if (Settings::get('OC_BRANCH') == 'oc.de') + { + $user_id = $request->token->user_id; + $rs = Db::query(" + SELECT + id, + name, + date_created, + last_modified, + last_added, + description, + is_public, + ( + SELECT COUNT(*) + FROM cache_list_items + WHERE cache_list_id = cache_lists.id + ) AS caches_count, + ( + SELECT COUNT(*) + FROM cache_list_watches + WHERE cache_list_id = cache_lists.id + ) AS watches_count + FROM cache_lists + WHERE user_id = '".Db::escape_string($user_id)."' + "); + + $lists = []; + while ($list = Db::fetch_assoc($rs)) + { + $lists[] = $list; + } + + $result = json_encode($lists, JSON_PRETTY_PRINT); + } + return Okapi::formatted_response($request, $result); + } + + + // ------------------------------------------------------------------ + +} diff --git a/okapi/services/lists/query/docs.xml b/okapi/services/lists/query/docs.xml new file mode 100644 index 00000000..8c3d0c71 --- /dev/null +++ b/okapi/services/lists/query/docs.xml @@ -0,0 +1,40 @@ + + Query Cache Lists + 627 + +

This method is used to query the metadata of all user owned cache lists. + Such a query ist typically performed by a client application that wants to render + a list of geocache lists at the UI. The metadata of the list is necessary and sufficient + for this purpose. Please note, an id is also part of the returned meta data. + Using this id as a reference code, specific operations can be performed on selected lists + for instance adding geocaches to the list, removing them, updating the metadata of the + referenced list or deleting the referenced list entirely. Typically such entities as + geocaches, logs, or lists, are referenced by a referenceCode, for instance + for geocaches, there would be a cache_code in the namespace OCxxxxx where + cache_code would be interpreted as a referenceCode for geocaches. Unfortunately lists + do not yet have such a referenceCode. They can be referenced only by their internal id, + which is named id in the returned objects for this method. This id is used as the + list_id parameter in all subsequent methods dealing with lists. +

+
+ + +

An array of the following structure:

+
+[
+    {
+        "id": 13,
+        "name": "This is my list",
+        "date_created": "2023-11-28 09:29:34",
+        "last_modified": "2023-11-28 09:49:16",
+        "last_added": "2023-11-28 09:40:18",
+        "description": "Just a list for testing",
+        "is_public": 0,
+        "caches_count": 4,
+        "watches_count": 1
+    },
+    { .... }
+]
+        
+
+
diff --git a/okapi/services/lists/remove_caches/WebService.php b/okapi/services/lists/remove_caches/WebService.php new file mode 100644 index 00000000..1ff393c4 --- /dev/null +++ b/okapi/services/lists/remove_caches/WebService.php @@ -0,0 +1,74 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false + ); + + $user_id = $request->token->user_id; + + $listId = $request->get_parameter('list_id'); + $cacheCodes = $request->get_parameter('cache_codes'); + + if (empty($listId)) { + throw new InvalidParam('list_id', 'list_id is mandatory and must not be empty.'); + } + + if (empty($cacheCodes)) { + throw new InvalidParam('cache_codes', 'cache_codes is mandatory and must not be empty.'); + } + + $cacheCodesArray = array_unique(explode('|', $cacheCodes)); + + // Check the length + if (count($cacheCodesArray) > 500) { + throw new InvalidParam('cache_codes', 'The number of cache codes exceeds the limit of 500.'); + } + + // Escape cache codes and build the SQL query + $escapedCacheCodes = implode("','", array_map('\okapi\core\Db::escape_string', $cacheCodesArray)); + + // Delete cache_ids from the cache_list_items table + $rs = Db::query(" + DELETE FROM cache_list_items + WHERE cache_list_id = '$listId' + AND cache_id IN ( + SELECT cache_id + FROM caches + WHERE wp_oc IN ('$escapedCacheCodes') + ) + "); + + $removedCount = $rs->rowCount(); // Get the number of affected rows + + $result = array( + 'success' => true, + 'removed_count' => $removedCount + ); + + return Okapi::formatted_response($request, $result); + } +} + diff --git a/okapi/services/lists/remove_caches/docs.xml b/okapi/services/lists/remove_caches/docs.xml new file mode 100644 index 00000000..bbb1de4c --- /dev/null +++ b/okapi/services/lists/remove_caches/docs.xml @@ -0,0 +1,23 @@ + + Remove Caches From List + 627 + +

This method removes geocaches from a list.

+
+ +

The id of a list. List IDs can be obtained by ::service/lists/query

+
+ +

A pipe separated list of cache_codes to be removed to the list.

+

Up to 500 geoaches can be removed from the list by one request to + this method

+
+ + +

A dictionary of the following structure:

+
    +
  • success - true
  • +
  • removed_count - number of geocaches removed from the list
  • +
+
+
diff --git a/okapi/services/lists/update/WebService.php b/okapi/services/lists/update/WebService.php new file mode 100644 index 00000000..0330abcb --- /dev/null +++ b/okapi/services/lists/update/WebService.php @@ -0,0 +1,117 @@ + 3 + ); + } + + public static function call(OkapiRequest $request) + { + $result = array( + 'success' => false + ); + + if (Settings::get('OC_BRANCH') == 'oc.de') + { + $user_id = $request->token->user_id; + + $listId = $request->get_parameter('list_id'); + $listName = $request->get_parameter('list_name'); + $listDescription = $request->get_parameter('list_description'); + $listStatus = $request->get_parameter('list_status'); + $listWatch = $request->get_parameter('list_watch'); + $listPassword = $request->get_parameter('list_password'); + + if (empty($listId) || !is_numeric($listId)) { + throw new InvalidParam('list_id', 'list_id is mandatory and must be numeric.'); + } + + if (empty($listName) && empty($listDescription) && ($listStatus === null || $listStatus === '') && ($listWatch === null || $listWatch === '') && ($listPassword === null || $listPassword === '')) { + throw new InvalidParam('list_name, list_description, list_status, list_watch, list_password', 'At least one optional parameter is required.'); + } + + $updateFields = array(); + + if (!empty($listName)) { + $updateFields['name'] = Db::escape_string($listName); + } + + if (!empty($listDescription)) { + $updateFields['description'] = Db::escape_string($listDescription); + } + + if ($listStatus !== null && $listStatus !== '') { + $listStatus = (int)$listStatus; + if (!in_array($listStatus, [0, 2, 3])) { + throw new InvalidParam('list_status', 'list_status must be a valid value (0, 2, 3).'); + } + $updateFields['is_public'] = $listStatus; + + // Handle list_password only if list_status is 0 (private) + if ($listStatus == 0) { + if (isset($listPassword) && $listPassword !== '') { + $updateFields['password'] = substr(Db::escape_string($listPassword), 0, 16); + } else { + $updateFields['password'] = null; // Remove the password + } + } + } + + if ($listWatch !== null && $listWatch !== '') { + $listWatch = (int)$listWatch; + $currentWatchState = (int) Db::query(" + SELECT COUNT(*) + FROM cache_list_watches + WHERE cache_list_id = '" . Db::escape_string($listId) . "' + AND user_id = '" . Db::escape_string($user_id) . "' + ")->fetchColumn(); + + if ($listWatch == 1 && $currentWatchState == 0) { + // Watched and not in cache_list_watches, insert + Db::query(" + INSERT INTO cache_list_watches (cache_list_id, user_id) + VALUES ('" . Db::escape_string($listId) . "', '" . Db::escape_string($user_id) . "') + "); + } elseif ($listWatch == 0 && $currentWatchState > 0) { + // Unwatched and in cache_list_watches, delete + Db::query(" + DELETE FROM cache_list_watches + WHERE cache_list_id = '" . Db::escape_string($listId) . "' + AND user_id = '" . Db::escape_string($user_id) . "' + "); + } + } + + if (!empty($updateFields)) { + $updateQuery = "UPDATE cache_lists SET "; + $updateQuery .= implode(', ', array_map(function ($field, $value) { + return "$field = '$value'"; + }, array_keys($updateFields), $updateFields)); + $updateQuery .= " WHERE id = '" . Db::escape_string($listId) . "'"; + + Db::query($updateQuery); + } + + $result = array( + 'success' => true, + 'message' => 'Cache list updated successfully.' + ); + } + return Okapi::formatted_response($request, $result); + } +} + diff --git a/okapi/services/lists/update/docs.xml b/okapi/services/lists/update/docs.xml new file mode 100644 index 00000000..34c1e10e --- /dev/null +++ b/okapi/services/lists/update/docs.xml @@ -0,0 +1,50 @@ + + Update Cache List + 627 + +

This method is is used to update the meta data of an existing cache list.

+
+ +

The list_id uniquely references the + list for which the metadata should be updated. The list_id can be obtained + by ::services/lists/query

+
+ +

The new name of the list. The current name can be obtained by + ::services/lists/query

+
+ +

The new description of the list. The current description can be obtained by + ::services/lists/query

+
+ +

The new status of the list. The current status can be obtained by + ::services/lists/query

+

Status is defined as follows: +

    +
  • 0 - indicates a private list
  • +
  • 2 - indicates a public list
  • +
  • 3 - indicates a public list, visible for all users in cache listings
  • +
+

+
+ +

The password for the list. Passwords only have a meaning if the list status is set to + private. The parameter is silently ignored otherwise. If the list is private, the first + 16 alphanumeric charactes are taken as a password. No encryption is performed on the + password, instead it is stored as plain text in the database.

+
+ +

A boolean, either 0, 1, false, true. If set to 1 or true + a user wants to get a notification if the content of the list + changes.

+
+ + +

A dictionary of the following structure:

+
    +
  • success - true
  • +
  • message - Cache list updated sucessfully
  • +
+
+
From b3ec196ac536e10b46d9075de666f1e8fdcbebd9 Mon Sep 17 00:00:00 2001 From: hxdimpf Date: Thu, 12 Mar 2026 19:05:08 +0100 Subject: [PATCH 3/3] Fix list services: SQL injection, auth, bugs, style, docs Security: - Add Db::escape_string() to all SQL-interpolated list_id values - Add ownership verification (user_id check) to add_caches, get_caches, remove_caches, and update endpoints - Add oc.de gate to add_caches, get_caches, remove_caches Bug fixes: - Fix double JSON encoding in query (pass array, not json_encode) - Fix password truncation in create/update: truncate before escaping to avoid corrupting escape sequences - Fix deletion order in delete: remove child records (items, watches) before parent (cache_lists) to avoid FK constraint issues - Fix NULL password handling in update: generate 'password = NULL' instead of 'password = ''' when clearing password Style: - Rename all camelCase variables to snake_case - Remove unused imports (LogsCommon, OkapiServiceRunner, etc.) - Restore save_user_coords in OkapiServiceRunner (kept per PR #628) Documentation: - Fix typo ::service/lists/query -> ::services/lists/query - Fix "removed to the list" -> "removed from the list" - Fix "sucessfully" -> "successfully" --- .../services/lists/add_caches/WebService.php | 42 +++++---- okapi/services/lists/add_caches/docs.xml | 2 +- okapi/services/lists/create/WebService.php | 61 ++++++------ okapi/services/lists/delete/WebService.php | 27 +++--- .../services/lists/get_caches/WebService.php | 44 +++++---- okapi/services/lists/query/WebService.php | 16 +--- .../lists/remove_caches/WebService.php | 68 ++++++++------ okapi/services/lists/remove_caches/docs.xml | 4 +- okapi/services/lists/update/WebService.php | 94 ++++++++++--------- okapi/services/lists/update/docs.xml | 2 +- 10 files changed, 194 insertions(+), 166 deletions(-) diff --git a/okapi/services/lists/add_caches/WebService.php b/okapi/services/lists/add_caches/WebService.php index 5c3894bf..9e129bd4 100644 --- a/okapi/services/lists/add_caches/WebService.php +++ b/okapi/services/lists/add_caches/WebService.php @@ -3,10 +3,11 @@ namespace okapi\services\lists\add_caches; use okapi\core\Db; +use okapi\core\Exception\BadRequest; +use okapi\core\Exception\InvalidParam; use okapi\core\Okapi; use okapi\core\Request\OkapiRequest; use okapi\Settings; -use okapi\core\Exception\InvalidParam; class WebService { @@ -19,49 +20,58 @@ public static function options() public static function call(OkapiRequest $request) { - $result = array( - 'success' => false - ); + if (Settings::get('OC_BRANCH') != 'oc.de') + throw new BadRequest('This method is not supported in this OKAPI installation.'); $user_id = $request->token->user_id; - $listId = $request->get_parameter('list_id'); - $cacheCodes = $request->get_parameter('cache_codes'); + $list_id = $request->get_parameter('list_id'); + $cache_codes = $request->get_parameter('cache_codes'); - if (empty($listId)) { + if (empty($list_id)) { throw new InvalidParam('list_id', 'list_id is mandatory and must not be empty.'); } - if (empty($cacheCodes)) { + if (empty($cache_codes)) { throw new InvalidParam('cache_codes', 'cache_codes is mandatory and must not be empty.'); } - $cacheCodesArray = array_unique(explode('|', $cacheCodes)); + // Verify list ownership + $count = Db::select_value(" + SELECT COUNT(*) + FROM cache_lists + WHERE id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' + "); + if ($count == 0) { + throw new InvalidParam('list_id', 'The specified list does not exist or does not belong to you.'); + } + + $cache_codes_array = array_unique(explode('|', $cache_codes)); // Check the length - if (count($cacheCodesArray) > 500) { + if (count($cache_codes_array) > 500) { throw new InvalidParam('cache_codes', 'The number of cache codes exceeds the limit of 500.'); } // Escape cache codes and build the SQL query - $escapedCacheCodes = implode("','", array_map('\okapi\core\Db::escape_string', $cacheCodesArray)); + $escaped_cache_codes = implode("','", array_map('\okapi\core\Db::escape_string', $cache_codes_array)); // Fetch cache_ids from the caches table using INSERT IGNORE $rs = Db::query(" INSERT IGNORE INTO cache_list_items (cache_list_id, cache_id) - SELECT '$listId', cache_id + SELECT '".Db::escape_string($list_id)."', cache_id FROM caches - WHERE wp_oc IN ('$escapedCacheCodes') + WHERE wp_oc IN ('$escaped_cache_codes') "); - $insertedCount = $rs->rowCount(); // Get the number of affected rows + $inserted_count = $rs->rowCount(); $result = array( 'success' => true, - 'added_count' => $insertedCount + 'added_count' => $inserted_count ); return Okapi::formatted_response($request, $result); } } - diff --git a/okapi/services/lists/add_caches/docs.xml b/okapi/services/lists/add_caches/docs.xml index 5146b97c..8c01e123 100644 --- a/okapi/services/lists/add_caches/docs.xml +++ b/okapi/services/lists/add_caches/docs.xml @@ -5,7 +5,7 @@

This method is used to add geocaches to an existing list.

-

The id of a list. List IDs can be obtained by ::service/lists/query

+

The id of a list. List IDs can be obtained by ::services/lists/query

A pipe separated list of cache_codes to be added to the list.

diff --git a/okapi/services/lists/create/WebService.php b/okapi/services/lists/create/WebService.php index 9e949d57..ac7a3cf9 100644 --- a/okapi/services/lists/create/WebService.php +++ b/okapi/services/lists/create/WebService.php @@ -3,10 +3,10 @@ namespace okapi\services\lists\create; use okapi\core\Db; +use okapi\core\Exception\InvalidParam; use okapi\core\Okapi; use okapi\core\Request\OkapiRequest; use okapi\Settings; -use okapi\core\Exception\InvalidParam; class WebService { @@ -28,66 +28,67 @@ public static function call(OkapiRequest $request) { $user_id = $request->token->user_id; - $listName = $request->get_parameter('list_name'); - $listDescription = $request->get_parameter('list_description'); - $listStatus = $request->get_parameter('list_status'); - $isWatched = $request->get_parameter('is_watched'); - $listPassword = $request->get_parameter('list_password'); + $list_name = $request->get_parameter('list_name'); + $list_description = $request->get_parameter('list_description'); + $list_status = $request->get_parameter('list_status'); + $is_watched = $request->get_parameter('is_watched'); + $list_password = $request->get_parameter('list_password'); - if (empty($listName)) { + if (empty($list_name)) { throw new InvalidParam('list_name', 'list_name is mandatory and must not be empty.'); } - $insertFields = array( - 'name' => Db::escape_string($listName), + $insert_fields = array( + 'name' => Db::escape_string($list_name), 'user_id' => Db::escape_string($user_id) ); - if (!empty($listDescription)) { - $insertFields['description'] = Db::escape_string($listDescription); + if (!empty($list_description)) { + $insert_fields['description'] = Db::escape_string($list_description); } - if ($listStatus !== null && $listStatus !== '') { - $listStatus = (int)$listStatus; - if (!in_array($listStatus, [0, 2, 3])) { + if ($list_status !== null && $list_status !== '') { + $list_status = (int)$list_status; + if (!in_array($list_status, [0, 2, 3])) { throw new InvalidParam('list_status', 'list_status must be a valid value (0, 2, 3).'); } - $insertFields['is_public'] = $listStatus; + $insert_fields['is_public'] = $list_status; // Handle list_password only if list_status is 0 (private) - if ($listStatus == 0) { - if (isset($listPassword) && $listPassword !== '') { - $insertFields['password'] = substr(Db::escape_string($listPassword), 0, 16); + if ($list_status == 0) { + if (isset($list_password) && $list_password !== '') { + $insert_fields['password'] = Db::escape_string(substr($list_password, 0, 16)); } } } - $columns = implode(', ', array_keys($insertFields)); - $values = "'" . implode("', '", $insertFields) . "'"; + $columns = implode(', ', array_keys($insert_fields)); + $values = "'" . implode("', '", $insert_fields) . "'"; - $insertQuery = "INSERT INTO cache_lists ($columns) VALUES ($values)"; - Db::query($insertQuery); + $insert_query = "INSERT INTO cache_lists ($columns) VALUES ($values)"; + Db::query($insert_query); - $listId = Db::last_insert_id(); + $list_id = Db::last_insert_id(); // Handle is_watched - if ($isWatched !== null && $isWatched !== '') { - $isWatched = (int)$isWatched; - if (!in_array($isWatched, [0, 1])) { + if ($is_watched !== null && $is_watched !== '') { + $is_watched = (int)$is_watched; + if (!in_array($is_watched, [0, 1])) { throw new InvalidParam('is_watched', 'is_watched must be a valid value (0, 1).'); } - // Insert a new record - Db::query("INSERT INTO cache_list_watches (cache_list_id, user_id, is_watched) VALUES (LAST_INSERT_ID(), '$user_id', $isWatched)"); + Db::query(" + INSERT INTO cache_list_watches (cache_list_id, user_id, is_watched) + VALUES ('".Db::escape_string($list_id)."', '".Db::escape_string($user_id)."', '".Db::escape_string($is_watched)."') + "); } $result = array( 'success' => true, 'message' => 'Cache list created successfully.', - 'list_id' => $listId + 'list_id' => $list_id ); } return Okapi::formatted_response($request, $result); } } - diff --git a/okapi/services/lists/delete/WebService.php b/okapi/services/lists/delete/WebService.php index 89dd1ea8..64a7bdf1 100644 --- a/okapi/services/lists/delete/WebService.php +++ b/okapi/services/lists/delete/WebService.php @@ -3,10 +3,10 @@ namespace okapi\services\lists\delete; use okapi\core\Db; +use okapi\core\Exception\InvalidParam; use okapi\core\Okapi; use okapi\core\Request\OkapiRequest; use okapi\Settings; -use okapi\core\Exception\InvalidParam; class WebService { @@ -28,23 +28,27 @@ public static function call(OkapiRequest $request) { $user_id = $request->token->user_id; - $listId = $request->get_parameter('list_id'); + $list_id = $request->get_parameter('list_id'); - if (empty($listId) || !is_numeric($listId)) { + if (empty($list_id) || !is_numeric($list_id)) { throw new InvalidParam('list_id', 'list_id is mandatory and must be numeric.'); } - // Check if the list exists - $countQuery = Db::query("SELECT COUNT(*) AS count FROM cache_lists WHERE id = '$listId' AND user_id = '$user_id'"); - $listExists = Db::fetch_assoc($countQuery)['count']; - if ($listExists == 0) { + // Check if the list exists and belongs to the user + $count = Db::select_value(" + SELECT COUNT(*) + FROM cache_lists + WHERE id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' + "); + if ($count == 0) { throw new InvalidParam('list_id', 'The specified list does not exist.'); } - // Proceed with the deletion process - Db::query("DELETE FROM cache_lists WHERE id = '$listId'"); - Db::query("DELETE FROM cache_list_watches WHERE cache_list_id = '$listId'"); - Db::query("DELETE FROM cache_list_items WHERE cache_list_id = '$listId'"); + // Delete child records before parent to avoid FK constraint issues + Db::query("DELETE FROM cache_list_items WHERE cache_list_id = '".Db::escape_string($list_id)."'"); + Db::query("DELETE FROM cache_list_watches WHERE cache_list_id = '".Db::escape_string($list_id)."'"); + Db::query("DELETE FROM cache_lists WHERE id = '".Db::escape_string($list_id)."'"); $result = array( 'success' => true, @@ -54,4 +58,3 @@ public static function call(OkapiRequest $request) return Okapi::formatted_response($request, $result); } } - diff --git a/okapi/services/lists/get_caches/WebService.php b/okapi/services/lists/get_caches/WebService.php index b5e6e751..858075ed 100644 --- a/okapi/services/lists/get_caches/WebService.php +++ b/okapi/services/lists/get_caches/WebService.php @@ -3,10 +3,11 @@ namespace okapi\services\lists\get_caches; use okapi\core\Db; +use okapi\core\Exception\BadRequest; +use okapi\core\Exception\InvalidParam; use okapi\core\Okapi; use okapi\core\Request\OkapiRequest; use okapi\Settings; -use okapi\core\Exception\InvalidParam; class WebService { @@ -19,43 +20,52 @@ public static function options() public static function call(OkapiRequest $request) { - $result = array( - 'success' => false - ); + if (Settings::get('OC_BRANCH') != 'oc.de') + throw new BadRequest('This method is not supported in this OKAPI installation.'); $user_id = $request->token->user_id; - $listId = $request->get_parameter('list_id'); + $list_id = $request->get_parameter('list_id'); - if (empty($listId)) { + if (empty($list_id)) { throw new InvalidParam('list_id', 'list_id is mandatory and must not be empty.'); } + // Verify list ownership + $count = Db::select_value(" + SELECT COUNT(*) + FROM cache_lists + WHERE id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' + "); + if ($count == 0) { + throw new InvalidParam('list_id', 'The specified list does not exist or does not belong to you.'); + } + // Fetch cache_ids associated with the specified list - $cacheIdsArray = Db::select_column(" + $cache_ids_array = Db::select_column(" SELECT cache_id FROM cache_list_items - WHERE cache_list_id = '$listId' + WHERE cache_list_id = '".Db::escape_string($list_id)."' "); - $cacheCount = count($cacheIdsArray); + $cache_count = count($cache_ids_array); // Fetch cache_codes based on cache_ids - $cacheCodesArray = array(); + $cache_codes_array = array(); - if (!empty($cacheIdsArray)) { - $cacheIds = implode(',', $cacheIdsArray); - $cacheCodesArray = Db::select_column( - "SELECT wp_oc FROM caches WHERE cache_id IN ($cacheIds)" + if (!empty($cache_ids_array)) { + $escaped_ids = implode(',', array_map('intval', $cache_ids_array)); + $cache_codes_array = Db::select_column( + "SELECT wp_oc FROM caches WHERE cache_id IN ($escaped_ids)" ); } $result = array( 'success' => true, - 'cache_codes' => implode('|', $cacheCodesArray), - 'cache_count' => $cacheCount + 'cache_codes' => implode('|', $cache_codes_array), + 'cache_count' => $cache_count ); return Okapi::formatted_response($request, $result); } } - diff --git a/okapi/services/lists/query/WebService.php b/okapi/services/lists/query/WebService.php index f0f3e490..312200fb 100644 --- a/okapi/services/lists/query/WebService.php +++ b/okapi/services/lists/query/WebService.php @@ -2,14 +2,9 @@ namespace okapi\services\lists\query; -use okapi\core\Exception\InvalidParam; -use okapi\core\Exception\ParamMissing; use okapi\core\Db; use okapi\core\Okapi; -use okapi\core\OkapiServiceRunner; -use okapi\core\Request\OkapiInternalRequest; use okapi\core\Request\OkapiRequest; -use okapi\services\logs\LogsCommon; use okapi\Settings; class WebService @@ -24,7 +19,7 @@ public static function options() public static function call(OkapiRequest $request) { $result = array( - 'success' => false // if the installation doesn't support it + 'success' => false ); if (Settings::get('OC_BRANCH') == 'oc.de') @@ -59,12 +54,11 @@ public static function call(OkapiRequest $request) $lists[] = $list; } - $result = json_encode($lists, JSON_PRETTY_PRINT); + $result = array( + 'success' => true, + 'lists' => $lists + ); } return Okapi::formatted_response($request, $result); } - - - // ------------------------------------------------------------------ - } diff --git a/okapi/services/lists/remove_caches/WebService.php b/okapi/services/lists/remove_caches/WebService.php index 1ff393c4..0f535da6 100644 --- a/okapi/services/lists/remove_caches/WebService.php +++ b/okapi/services/lists/remove_caches/WebService.php @@ -2,18 +2,15 @@ namespace okapi\services\lists\remove_caches; -use okapi\core\Exception\InvalidParam; -use okapi\core\Exception\ParamMissing; use okapi\core\Db; +use okapi\core\Exception\BadRequest; +use okapi\core\Exception\InvalidParam; use okapi\core\Okapi; -use okapi\core\OkapiServiceRunner; -use okapi\core\Request\OkapiInternalRequest; use okapi\core\Request\OkapiRequest; -use okapi\services\logs\LogsCommon; use okapi\Settings; class WebService -{ +{ public static function options() { return array( @@ -22,53 +19,62 @@ public static function options() } public static function call(OkapiRequest $request) - { - $result = array( - 'success' => false - ); - + { + if (Settings::get('OC_BRANCH') != 'oc.de') + throw new BadRequest('This method is not supported in this OKAPI installation.'); + $user_id = $request->token->user_id; - $listId = $request->get_parameter('list_id'); - $cacheCodes = $request->get_parameter('cache_codes'); - - if (empty($listId)) { + $list_id = $request->get_parameter('list_id'); + $cache_codes = $request->get_parameter('cache_codes'); + + if (empty($list_id)) { throw new InvalidParam('list_id', 'list_id is mandatory and must not be empty.'); } - - if (empty($cacheCodes)) { + + if (empty($cache_codes)) { throw new InvalidParam('cache_codes', 'cache_codes is mandatory and must not be empty.'); } - - $cacheCodesArray = array_unique(explode('|', $cacheCodes)); - + + // Verify list ownership + $count = Db::select_value(" + SELECT COUNT(*) + FROM cache_lists + WHERE id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' + "); + if ($count == 0) { + throw new InvalidParam('list_id', 'The specified list does not exist or does not belong to you.'); + } + + $cache_codes_array = array_unique(explode('|', $cache_codes)); + // Check the length - if (count($cacheCodesArray) > 500) { + if (count($cache_codes_array) > 500) { throw new InvalidParam('cache_codes', 'The number of cache codes exceeds the limit of 500.'); } // Escape cache codes and build the SQL query - $escapedCacheCodes = implode("','", array_map('\okapi\core\Db::escape_string', $cacheCodesArray)); - + $escaped_cache_codes = implode("','", array_map('\okapi\core\Db::escape_string', $cache_codes_array)); + // Delete cache_ids from the cache_list_items table $rs = Db::query(" DELETE FROM cache_list_items - WHERE cache_list_id = '$listId' + WHERE cache_list_id = '".Db::escape_string($list_id)."' AND cache_id IN ( SELECT cache_id FROM caches - WHERE wp_oc IN ('$escapedCacheCodes') + WHERE wp_oc IN ('$escaped_cache_codes') ) "); - - $removedCount = $rs->rowCount(); // Get the number of affected rows - - $result = array( + + $removed_count = $rs->rowCount(); + + $result = array( 'success' => true, - 'removed_count' => $removedCount + 'removed_count' => $removed_count ); return Okapi::formatted_response($request, $result); } } - diff --git a/okapi/services/lists/remove_caches/docs.xml b/okapi/services/lists/remove_caches/docs.xml index bbb1de4c..5e57e94d 100644 --- a/okapi/services/lists/remove_caches/docs.xml +++ b/okapi/services/lists/remove_caches/docs.xml @@ -5,10 +5,10 @@

This method removes geocaches from a list.

-

The id of a list. List IDs can be obtained by ::service/lists/query

+

The id of a list. List IDs can be obtained by ::services/lists/query

-

A pipe separated list of cache_codes to be removed to the list.

+

A pipe separated list of cache_codes to be removed from the list.

Up to 500 geoaches can be removed from the list by one request to this method

diff --git a/okapi/services/lists/update/WebService.php b/okapi/services/lists/update/WebService.php index 0330abcb..a26a7985 100644 --- a/okapi/services/lists/update/WebService.php +++ b/okapi/services/lists/update/WebService.php @@ -2,10 +2,9 @@ namespace okapi\services\lists\update; -use okapi\core\Exception\InvalidParam; use okapi\core\Db; +use okapi\core\Exception\InvalidParam; use okapi\core\Okapi; -use okapi\core\OkapiServiceRunner; use okapi\core\Request\OkapiRequest; use okapi\Settings; @@ -29,81 +28,87 @@ public static function call(OkapiRequest $request) { $user_id = $request->token->user_id; - $listId = $request->get_parameter('list_id'); - $listName = $request->get_parameter('list_name'); - $listDescription = $request->get_parameter('list_description'); - $listStatus = $request->get_parameter('list_status'); - $listWatch = $request->get_parameter('list_watch'); - $listPassword = $request->get_parameter('list_password'); + $list_id = $request->get_parameter('list_id'); + $list_name = $request->get_parameter('list_name'); + $list_description = $request->get_parameter('list_description'); + $list_status = $request->get_parameter('list_status'); + $list_watch = $request->get_parameter('list_watch'); + $list_password = $request->get_parameter('list_password'); - if (empty($listId) || !is_numeric($listId)) { + if (empty($list_id) || !is_numeric($list_id)) { throw new InvalidParam('list_id', 'list_id is mandatory and must be numeric.'); } - if (empty($listName) && empty($listDescription) && ($listStatus === null || $listStatus === '') && ($listWatch === null || $listWatch === '') && ($listPassword === null || $listPassword === '')) { + // Verify list ownership + $count = Db::select_value(" + SELECT COUNT(*) + FROM cache_lists + WHERE id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' + "); + if ($count == 0) { + throw new InvalidParam('list_id', 'The specified list does not exist or does not belong to you.'); + } + + if (empty($list_name) && empty($list_description) && ($list_status === null || $list_status === '') && ($list_watch === null || $list_watch === '') && ($list_password === null || $list_password === '')) { throw new InvalidParam('list_name, list_description, list_status, list_watch, list_password', 'At least one optional parameter is required.'); } - $updateFields = array(); + $update_parts = array(); - if (!empty($listName)) { - $updateFields['name'] = Db::escape_string($listName); + if (!empty($list_name)) { + $update_parts[] = "name = '".Db::escape_string($list_name)."'"; } - if (!empty($listDescription)) { - $updateFields['description'] = Db::escape_string($listDescription); + if (!empty($list_description)) { + $update_parts[] = "description = '".Db::escape_string($list_description)."'"; } - if ($listStatus !== null && $listStatus !== '') { - $listStatus = (int)$listStatus; - if (!in_array($listStatus, [0, 2, 3])) { + if ($list_status !== null && $list_status !== '') { + $list_status = (int)$list_status; + if (!in_array($list_status, [0, 2, 3])) { throw new InvalidParam('list_status', 'list_status must be a valid value (0, 2, 3).'); } - $updateFields['is_public'] = $listStatus; + $update_parts[] = "is_public = '".Db::escape_string($list_status)."'"; // Handle list_password only if list_status is 0 (private) - if ($listStatus == 0) { - if (isset($listPassword) && $listPassword !== '') { - $updateFields['password'] = substr(Db::escape_string($listPassword), 0, 16); + if ($list_status == 0) { + if (isset($list_password) && $list_password !== '') { + $update_parts[] = "password = '".Db::escape_string(substr($list_password, 0, 16))."'"; } else { - $updateFields['password'] = null; // Remove the password + $update_parts[] = "password = NULL"; } } } - if ($listWatch !== null && $listWatch !== '') { - $listWatch = (int)$listWatch; - $currentWatchState = (int) Db::query(" + if ($list_watch !== null && $list_watch !== '') { + $list_watch = (int)$list_watch; + $current_watch_state = (int) Db::select_value(" SELECT COUNT(*) FROM cache_list_watches - WHERE cache_list_id = '" . Db::escape_string($listId) . "' - AND user_id = '" . Db::escape_string($user_id) . "' - ")->fetchColumn(); + WHERE cache_list_id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' + "); - if ($listWatch == 1 && $currentWatchState == 0) { - // Watched and not in cache_list_watches, insert + if ($list_watch == 1 && $current_watch_state == 0) { Db::query(" INSERT INTO cache_list_watches (cache_list_id, user_id) - VALUES ('" . Db::escape_string($listId) . "', '" . Db::escape_string($user_id) . "') + VALUES ('".Db::escape_string($list_id)."', '".Db::escape_string($user_id)."') "); - } elseif ($listWatch == 0 && $currentWatchState > 0) { - // Unwatched and in cache_list_watches, delete + } elseif ($list_watch == 0 && $current_watch_state > 0) { Db::query(" DELETE FROM cache_list_watches - WHERE cache_list_id = '" . Db::escape_string($listId) . "' - AND user_id = '" . Db::escape_string($user_id) . "' + WHERE cache_list_id = '".Db::escape_string($list_id)."' + AND user_id = '".Db::escape_string($user_id)."' "); } } - if (!empty($updateFields)) { - $updateQuery = "UPDATE cache_lists SET "; - $updateQuery .= implode(', ', array_map(function ($field, $value) { - return "$field = '$value'"; - }, array_keys($updateFields), $updateFields)); - $updateQuery .= " WHERE id = '" . Db::escape_string($listId) . "'"; - - Db::query($updateQuery); + if (!empty($update_parts)) { + $update_query = "UPDATE cache_lists SET " + . implode(', ', $update_parts) + . " WHERE id = '".Db::escape_string($list_id)."'"; + Db::query($update_query); } $result = array( @@ -114,4 +119,3 @@ public static function call(OkapiRequest $request) return Okapi::formatted_response($request, $result); } } - diff --git a/okapi/services/lists/update/docs.xml b/okapi/services/lists/update/docs.xml index 34c1e10e..d7186cf6 100644 --- a/okapi/services/lists/update/docs.xml +++ b/okapi/services/lists/update/docs.xml @@ -44,7 +44,7 @@

A dictionary of the following structure:

  • success - true
  • -
  • message - Cache list updated sucessfully
  • +
  • message - Cache list updated successfully