diff --git a/schema/patches/19.sql b/schema/patches/19.sql new file mode 100644 index 000000000..b63f2f673 --- /dev/null +++ b/schema/patches/19.sql @@ -0,0 +1,9 @@ +BEGIN; +ALTER TABLE schedule.show_season_timeslot + ADD COLUMN showplan_last_modified TIMESTAMPTZ DEFAULT NULL; + +UPDATE myradio.schema +SET value = 14 +WHERE attr='version'; + +COMMIT; diff --git a/src/Classes/ServiceAPI/MyRadio_Timeslot.php b/src/Classes/ServiceAPI/MyRadio_Timeslot.php index 5c8f1bab6..b2f406c5e 100644 --- a/src/Classes/ServiceAPI/MyRadio_Timeslot.php +++ b/src/Classes/ServiceAPI/MyRadio_Timeslot.php @@ -1063,13 +1063,27 @@ public function moveTimeslot($newStart, $newEnd) * This is the server-side implementation of the JSONON system for tracking Show Planner alterations. * * @param array[] $set A JSONON operation set + * @param int|null $last_modified Timestamp of the last time the client knows the show plan was modified */ - public function updateShowPlan($set) + public function updateShowPlan($set, $last_modified = null) { $result = []; //Being a Database Transaction - this all succeeds, or none of it does self::$db->query('BEGIN'); + // First, check when it was last modified - if it's been modified since + // the client last checked, reject the request as their view of the + // state of the showplan is out of date. + $last_mod_row = self::$db->fetchColumn( + 'SELECT showplan_last_modified FROM schedule.show_season_timeslot WHERE show_season_timeslot_id=$1 FOR UPDATE', + [$this->getID()] + ); + if (!empty($last_mod_row) && $last_modified !== null && CoreUtils::getTimestamp($last_mod_row[0]) !== $last_modified) { + self::$db->query('ROLLBACK'); + + return ['status' => 'OUTDATED']; + } + foreach ($set as $op) { switch ($op['op']) { case 'AddItem': @@ -1140,11 +1154,24 @@ public function updateShowPlan($set) } } + $last_mod_res = self::$db->fetchColumn( + 'UPDATE schedule.show_season_timeslot SET showplan_last_modified=NOW() WHERE show_season_timeslot_id=$1 RETURNING showplan_last_modified', + [$this->getID()] + ); self::$db->query('COMMIT'); //Update the legacy baps show plans database $this->updateLegacyShowPlan(); + if ($last_modified !== null) { + // We know the client will be able to understand this format + return [ + 'status' => $result[count($result)-1]['status'] ? 'OK' : 'ERROR', + 'result' => $result, + 'last_modified' => CoreUtils::getTimestamp($last_mod_res[0]) + ]; + } + return $result; } @@ -1156,7 +1183,7 @@ private function updateLegacyShowPlan() /** * Returns the tracks etc. and their associated channels as planned for this show. Mainly used by NIPSWeb. */ - public function getShowPlan() + public function getShowPlan(bool $include_last_modified = false) { // Check we can access it, if not, require permission if (!($this->isCurrentUserAnOwner())) { @@ -1183,6 +1210,17 @@ public function getShowPlan() NIPSWeb_TimeslotItem::getInstance($track['timeslot_item_id'])->toDataSource(); } + if ($include_last_modified) { + $last_modified = self::$db->fetchColumn( + 'SELECT showplan_last_modified FROM schedule.show_season_timeslot WHERE show_season_timeslot_id=$1', + [$this->getID()] + ); + return [ + 'plan' => $tracks, + 'last_modified' => empty($last_modified) ? null : CoreUtils::getTimestamp($last_modified[0]) + ]; + } + return $tracks; } } diff --git a/src/Controllers/root.php b/src/Controllers/root.php index afd0adef0..9ad288c7d 100644 --- a/src/Controllers/root.php +++ b/src/Controllers/root.php @@ -13,7 +13,7 @@ * This number is incremented every time a database patch is released. * Patches are scripts in schema/patches. */ -define('MYRADIO_CURRENT_SCHEMA_VERSION', 18); +define('MYRADIO_CURRENT_SCHEMA_VERSION', 19); /* * Turn on Error Reporting for the start. Once the Config object is loaded