From c6d6d86b32afd6fa7296a35047941c551b18c823 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Fri, 21 May 2021 08:48:24 +0100 Subject: [PATCH 01/18] Create dependabot.yml --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a51bb0b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "composer" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" From ddc988fff73cd1f582acb9043a22027b5d70a520 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Sat, 22 May 2021 18:18:57 +0100 Subject: [PATCH 02/18] Add ARIA label to cover photo upload input --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index fa8631c..03c90a9 100644 --- a/index.php +++ b/index.php @@ -213,7 +213,7 @@
+ aria-describedby="imageHelp" aria-label="Show cover image"> You can upload JPG or PNG files up to . From 1bcf70da69e5cd1f857e15d5ead07ff75fa157fb Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Sat, 22 May 2021 18:58:31 +0100 Subject: [PATCH 03/18] Improved label for image choice dropdown --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 03c90a9..3c78bef 100644 --- a/index.php +++ b/index.php @@ -203,7 +203,7 @@ - + Show missing? Please report it to technical staff.
@@ -124,7 +124,7 @@ - + Enter the show's presenter. From 0b3af566aaea298c2b84c74a9d91bd0b0d87b242 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Sat, 22 May 2021 19:08:30 +0100 Subject: [PATCH 06/18] Improved invalid date message --- index.php | 4 ---- resources/script.js | 12 ++++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/index.php b/index.php index 4bd733a..7173301 100644 --- a/index.php +++ b/index.php @@ -140,10 +140,6 @@ - - From 4776584bf6144f5063f692b4fd4cc7e863ea9f54 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Sat, 22 May 2021 19:25:08 +0100 Subject: [PATCH 08/18] Fix #40 by using relative path in script.js --- resources/script.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/script.js b/resources/script.js index 69a84c2..f0d7f86 100644 --- a/resources/script.js +++ b/resources/script.js @@ -163,7 +163,7 @@ form1.addEventListener("submit", function (event) { document.getElementById("saveAsDefaults").checked = false; } else { // This isn't a one-off show, so we can go and fetch the default data. - fetch("/resources/default/data.php?show=" + nameDropdown.value) + fetch("resources/default/data.php?show=" + nameDropdown.value) .then(function(response) { if (response.ok) return response.json() else return null @@ -186,7 +186,7 @@ form1.addEventListener("submit", function (event) { } }) - fetch("/resources/default/image.php?show=" + nameDropdown.value, + fetch("resources/default/image.php?show=" + nameDropdown.value, { // Only get headers, since we just need the HTTP status for now to see if an image exists. method: "HEAD" @@ -195,7 +195,7 @@ form1.addEventListener("submit", function (event) { .then(function(data) { if (data.ok) { // Default image exists document.getElementById("defaultImageSection").hidden = false; - document.getElementById("defaultImage").src = "/resources/default/image.php?show=" + nameDropdown.value; + document.getElementById("defaultImage").src = "resources/default/image.php?show=" + nameDropdown.value; document.getElementById("imageSource").value = "default"; imageUploader.hidden = true; } From 7b48a3888f1de606216aed1f2ca29022d2dd62f8 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Sun, 23 May 2021 16:19:24 +0100 Subject: [PATCH 09/18] Close #36 with ability to disable old show deletion --- processing/config.php.template | 19 ++++++++++++++++--- processing/cron.php | 13 ++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/processing/config.php.template b/processing/config.php.template index 6fffc74..d6222fb 100644 --- a/processing/config.php.template +++ b/processing/config.php.template @@ -71,9 +71,22 @@ return array( 'secret' => '', ), - // How long a show should be kept in storage after being published to Mixcloud - - // format at https://www.php.net/manual/en/datetime.formats.relative.php - 'retentionPeriod' => '1 day', + // Set this to `true` to delete the copy of the shows submitted after it's been uploaded to Mixcloud. + // BEWARE: Changing this setting could have a profound effect on your show archive. Take great care, and make sure + // you've always got an up-to-date backup (but you should have that anyway, even if you're not changing + // this setting). + // Before enabling this setting, check what is in the `submissions` table of the database. Anything with a + // `deletion-datetime` in the past relative to your database server time will be deleted the next time the + // cron job runs. + // TAKE BACKUPS. YOU HAVE BEEN WARNED. + 'deleteStoredCopies' => false, + // If `deleteStoredCopies` is true, this how long a show should be kept in storage after being published to + // Mixcloud. After this period, the file is deleted. + // If `deleteStoredCopies` is false, this is how long a show's information will be kept in the submissions + // database which powers this system. After this period, the reference to the + // show is deleted. "3 days" is a sufficient value in most cases. + // Format at https://www.php.net/manual/en/datetime.formats.relative.php. + 'retentionPeriod' => '3 days', // Text appended to the description of every upload. Use '{n}' for a new line. 'fixedDescription' => '', diff --git a/processing/cron.php b/processing/cron.php index c68bace..0e90b16 100644 --- a/processing/cron.php +++ b/processing/cron.php @@ -169,11 +169,14 @@ function publishShow(array $show, array $config, Storage $storage, Database $dat * @throws Exception */ function deleteShow(array $show, array $config, Storage $storage, Database $database) { - if ($show["file-location"] == Storage::$LOCATION_WAITING) { - if (!unlink($config["waitingDirectory"] . "/" . $show["file"])) - throw new Exception("Couldn't delete file from waiting directory."); - } else { - $storage->delete($show["file"]); + // If old files should be deleted + if ($config["deleteStoredCopies"]) { + if ($show["file-location"] == Storage::$LOCATION_WAITING) { + if (!unlink($config["waitingDirectory"] . "/" . $show["file"])) + throw new Exception("Couldn't delete file from waiting directory."); + } else { + $storage->delete($show["file"]); + } } $database->deleteSubmission($show["id"]); From 9fd6a7d09256b33c9f6994dcc63ce7f903809cdc Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Mon, 24 May 2021 10:27:50 +0100 Subject: [PATCH 10/18] Config for custom file names (#26) --- processing/config.php.template | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/processing/config.php.template b/processing/config.php.template index d6222fb..6cf0423 100644 --- a/processing/config.php.template +++ b/processing/config.php.template @@ -71,6 +71,20 @@ return array( 'secret' => '', ), + // The filename format to use for submitted shows. You can use the `/` character to organise the files into + // directories. The following variables are available: + // {s} Show name + // {p} Presenter name + // {d} Date without leading 0 + // {dd} Date with leading 0 + // {m} Month number without leading 0 + // {mm} Month number with leading 0 + // {y} 2-digit year (e.g. 21) + // {yy} 4-digit year (e.g. 2021) + // The extension of the uploaded file is added automatically (i.e. if a user uploads an MP3, the final file name + // will end in `.mp3`). + 'uploadFileName' => '{p}-{s} {y}{mm}{dd}', + // Set this to `true` to delete the copy of the shows submitted after it's been uploaded to Mixcloud. // BEWARE: Changing this setting could have a profound effect on your show archive. Take great care, and make sure // you've always got an up-to-date backup (but you should have that anyway, even if you're not changing From c4c3a2a9deea29fde07e92320053641180c155a7 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Mon, 24 May 2021 10:57:36 +0100 Subject: [PATCH 11/18] Custom file names can be used for uploading shows (#26) --- processing/Recording.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/processing/Recording.php b/processing/Recording.php index 970883a..cc90c4d 100644 --- a/processing/Recording.php +++ b/processing/Recording.php @@ -321,27 +321,33 @@ public function getPublicationAlertEmail(): ?string { * @throws Exception If prerequisite data is missing or invalid. */ public function getFileName(): string { - if (empty($this->name)) throw new Exception("No show name stored before requesting file name."); + if (empty($this->name)) throw new Exception("No show name stored before requesting file name."); if (empty($this->presenter)) throw new Exception("No presenter name stored before requesting file name."); - if (empty($this->start)) throw new Exception("No start date stored before requesting file name."); + if (empty($this->start)) throw new Exception("No start date stored before requesting file name."); if (empty($this->extension)) throw new Exception("No file extension stored before requesting file name."); - // Put the date into the correct format - $date = date("ymd", strtotime($this->start)); - // Decode any encoded special characters $name = htmlspecialchars_decode($this->name, ENT_QUOTES); $presenter = htmlspecialchars_decode($this->presenter, ENT_QUOTES); - // Replace special characters in show details with spaces $name = preg_replace("/\W/", " ", $name); $presenter = preg_replace("/\W/", " ", $presenter); - // replace multiple spaces with a single space and trim whitespace from ends $name = trim(preg_replace("/\s+/", " ", $name)); $presenter = trim(preg_replace("/\s+/", " ", $presenter)); - return $presenter . "-" . $name . " " . $date . "." . $this->extension; + $replacements = array( + '{s}' => $name, + '{p}' => $presenter, + '{d}' => date("j", strtotime($this->start)), + '{dd}' => date("d", strtotime($this->start)), + '{m}' => date("n", strtotime($this->start)), + '{mm}' => date("m", strtotime($this->start)), + '{y}' => date("y", strtotime($this->start)), + '{yy}' => date("Y", strtotime($this->start)), + ); + + return strtr($this->config["uploadFileName"], $replacements) . "." . $this->extension; } /** From 013243f42c8146b87084f59737875183aa02ce46 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Mon, 24 May 2021 12:27:17 +0100 Subject: [PATCH 12/18] Create function for directory creation, handle file uploads File upload metadata is not correct --- processing/Storage.php | 17 +++++++++++++++++ upload.php | 8 +++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/processing/Storage.php b/processing/Storage.php index f0adbfb..2dd4669 100644 --- a/processing/Storage.php +++ b/processing/Storage.php @@ -42,6 +42,23 @@ public static function getProvider(): Storage { } } + /** + * Takes a path to a file (such as {@code /var/storage/a/b/file.mp3}) and creates the holding directories + * (such as {@code /var/storage/a/b/}) if they don't already exist. This can be used before attempting to write + * a file, to ensure the required directories exist. + * @param string $path The path to analyse. + * @return bool true if the operation is successful or the directories already existed, false otherwise. + */ + public static function createParentDirectories(string $path): bool { + $split = explode("/", $path); + // Take the last section off the array, since that will be the file name + array_pop($split); + $directory = implode("/", $split); + + if (is_dir($directory)) return true; + else return mkdir($directory, 0775, true); + } + /** * Move a file from the holding location (specified in the config file) to a waiting location (again, specified in * the config file). This should be a quick, local operation, so that a file moves into semi-permanent storage and diff --git a/upload.php b/upload.php index 850e57f..a0d1eed 100644 --- a/upload.php +++ b/upload.php @@ -10,6 +10,7 @@ require_once 'processing/Database.php'; require_once 'processing/Recording.php'; require_once 'processing/Input.php'; +require_once 'processing/Storage.php'; $config = require 'processing/config.php'; $database = new Database(); @@ -76,10 +77,15 @@ // set the path to upload to $uploadPath = $config["holdingDirectory"] . "/" . $fileName; +if (!Storage::createParentDirectories($uploadPath)) { + error_log("Failed to create parent directories for " . $uploadPath); + http_response_code(500); + exit; +} // If this is the final chunk of the file if (Basic::save($uploadPath, $flowConfig, $request)) { - $removingMetadataLocation = $config["holdingDirectory"] . "/meta-" . $fileName; + $removingMetadataLocation = $uploadPath . "-meta." . $extension; // Remove metadata from uploaded file, put in the show presenter and title instead // $metadata[0] is presenter, [1] is title, [2] is file extension From 12f15c0e402327b0d93f5d767259ce2ec6f2667a Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Mon, 24 May 2021 12:39:52 +0100 Subject: [PATCH 13/18] Write file metadata using info in Recording object Instead of extracting details from the file name, which we can't do now since it's of an inconsistent format --- processing/Recording.php | 23 +++++++++++++++++++++++ upload.php | 21 +++++++++++---------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/processing/Recording.php b/processing/Recording.php index cc90c4d..c3cbe17 100644 --- a/processing/Recording.php +++ b/processing/Recording.php @@ -252,6 +252,29 @@ public function getShowID(): ?int { return $this->showID; } + /** + * @return string + */ + public function getName(): string { + return $this->name; + } + + /** + * @return string + */ + public function getPresenter(): string { + return $this->presenter; + } + + /** + * @return string + * @throws Exception + */ + public function get6DigitStartDate(): string { + if (empty($this->start)) throw new Exception("No start date stored before requesting 6 digit date."); + return date("ymd", strtotime($this->start)); + } + /** * Get a nicely-formatted title of the show to publish to Mixcloud. Ensure a show name, presenter, and start time * are set before calling this function. diff --git a/upload.php b/upload.php index a0d1eed..496bb07 100644 --- a/upload.php +++ b/upload.php @@ -85,16 +85,17 @@ // If this is the final chunk of the file if (Basic::save($uploadPath, $flowConfig, $request)) { - $removingMetadataLocation = $uploadPath . "-meta." . $extension; - - // Remove metadata from uploaded file, put in the show presenter and title instead - // $metadata[0] is presenter, [1] is title, [2] is file extension - // TODO Use info from Recording object instead - $metadata = preg_split("/[-.]/", $fileName, 3); - shell_exec("ffmpeg -i \"$uploadPath\" -map_metadata -1 -metadata title=\"$metadata[1]\" -metadata artist=\"$metadata[0]\" -c:v copy -c:a copy \"$removingMetadataLocation\""); - - // move metadata-removed file back to the upload path - rename($removingMetadataLocation, $uploadPath); + try { + // Write metadata about the show into the file + $removingMetadataLocation = $uploadPath . "-meta." . $extension; + shell_exec("ffmpeg -i \"$uploadPath\" -map_metadata -1 -metadata title=\"" . $recording->getName() . " " . $recording->get6DigitStartDate() . "\" -metadata artist=\"" . $recording->getPresenter() . "\" -c:v copy -c:a copy \"$removingMetadataLocation\""); + // move metadata-removed file back to the upload path + rename($removingMetadataLocation, $uploadPath); + } catch (Exception $e) { + error_log($e->getMessage()); + http_response_code(500); + exit; + } // Log upload completed try { From 0a376eebcd8b4d728a2087b1cab53ca697c9e257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 15:45:39 +0000 Subject: [PATCH 14/18] Bump phpmailer/phpmailer from 6.4.1 to 6.5.0 Bumps [phpmailer/phpmailer](https://github.com/PHPMailer/PHPMailer) from 6.4.1 to 6.5.0. - [Release notes](https://github.com/PHPMailer/PHPMailer/releases) - [Changelog](https://github.com/PHPMailer/PHPMailer/blob/master/changelog.md) - [Commits](https://github.com/PHPMailer/PHPMailer/compare/v6.4.1...v6.5.0) --- updated-dependencies: - dependency-name: phpmailer/phpmailer dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index ab51430..5bd4489 100644 --- a/composer.lock +++ b/composer.lock @@ -504,16 +504,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.4.1", + "version": "v6.5.0", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d" + "reference": "a5b5c43e50b7fba655f793ad27303cd74c57363c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9256f12d8fb0cd0500f93b19e18c356906cbed3d", - "reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a5b5c43e50b7fba655f793ad27303cd74c57363c", + "reference": "a5b5c43e50b7fba655f793ad27303cd74c57363c", "shasum": "" }, "require": { @@ -568,7 +568,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.4.1" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.0" }, "funding": [ { @@ -576,7 +576,7 @@ "type": "github" } ], - "time": "2021-04-29T12:25:04+00:00" + "time": "2021-06-16T14:33:43+00:00" }, { "name": "psr/http-client", @@ -863,5 +863,5 @@ "ext-curl": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } From 4fe70bc15e23a2fc8cb3f65f6ceb2ce9f567657a Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Wed, 21 Jul 2021 15:52:47 +0100 Subject: [PATCH 15/18] Create directories when moving file to waiting directory --- processing/Storage.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/processing/Storage.php b/processing/Storage.php index 2dd4669..c98136c 100644 --- a/processing/Storage.php +++ b/processing/Storage.php @@ -69,10 +69,16 @@ public static function createParentDirectories(string $path): bool { public function moveToWaiting(string $file) { if (empty($file)) throw new Exception("No file name provided."); - $config = require __DIR__ . '/config.php'; + $config = require __DIR__ . '/config.php'; + $holdingLocation = $config["holdingDirectory"] . "/" . $file; + $targetLocation = $config["waitingDirectory"] . "/" . $file; - if (file_exists($config["holdingDirectory"] . "/" . $file)) { - if (!rename($config["holdingDirectory"] . "/" . $file, $config["waitingDirectory"] . "/" . $file)) { + if (file_exists($holdingLocation)) { + if (!Storage::createParentDirectories($targetLocation)) { + throw new Exception("Couldn't make parent directories in target location."); + } + + if (!rename($holdingLocation, $targetLocation)) { throw new Exception("Couldn't move file from holding to waiting."); } } else { From f78e87b4eabe28cdf38b07461f0c97aa61674d09 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Wed, 21 Jul 2021 16:06:48 +0100 Subject: [PATCH 16/18] Directory creation for LocalStorage operations --- processing/LocalStorage.php | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/processing/LocalStorage.php b/processing/LocalStorage.php index 92942d7..97425bc 100644 --- a/processing/LocalStorage.php +++ b/processing/LocalStorage.php @@ -15,8 +15,17 @@ public function __construct() { * @inheritDoc */ public function offload(string $file) { - if (file_exists($this->config["waitingDirectory"] . "/" . $file)) { - if (!rename($this->config["waitingDirectory"] . "/" . $file, $this->config["localStorage"]["uploadsDirectory"] . "/" . $file)) { + if (empty($file)) throw new Exception("No file name provided."); + + $waitingLocation = $this->config["waitingDirectory"] . "/" . $file; + $targetLocation = $this->config["localStorage"]["uploadsDirectory"] . "/" . $file; + + if (file_exists($waitingLocation)) { + if (!Storage::createParentDirectories($targetLocation)) { + throw new Exception("Couldn't make parent directories in target location."); + } + + if (!rename($waitingLocation, $targetLocation)) { throw new Exception("Couldn't move file from waiting to local storage."); } } else { @@ -30,14 +39,21 @@ public function offload(string $file) { public function retrieve(string $file): string { if (empty($file)) throw new Exception("No file name provided."); - if (file_exists($this->config["localStorage"]["uploadsDirectory"] . "/" . $file)) { - if (!copy($this->config["localStorage"]["uploadsDirectory"] . "/" . $file, $this->config["tempDirectory"] . "/" . $file)) { - throw new Exception("Couldn't move file from local to temporary storage."); + $uploadsLocation = $this->config["localStorage"]["uploadsDirectory"] . "/" . $file; + $targetLocation = $this->config["tempDirectory"] . "/" . $file; + + if (file_exists($uploadsLocation)) { + if (!Storage::createParentDirectories($targetLocation)) { + throw new Exception("Couldn't make parent directories in target location."); } - return $this->config["tempDirectory"] . "/" . $file; + if (!copy($uploadsLocation, $targetLocation)) { + throw new Exception("Couldn't move file from waiting to local storage."); + } + + return $targetLocation; } else { - throw new Exception("Couldn't find specified file in uploads folder."); + throw new Exception("Couldn't find specified file in waiting folder."); } } From ebffa399f0421202c7dad409b89007aa0cf58e24 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Wed, 21 Jul 2021 16:25:17 +0100 Subject: [PATCH 17/18] Directory creation for S3 operations, closes #26 --- processing/S3Storage.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/processing/S3Storage.php b/processing/S3Storage.php index 07b2353..22e13f5 100644 --- a/processing/S3Storage.php +++ b/processing/S3Storage.php @@ -61,14 +61,20 @@ public function offload(string $file) { public function retrieve(string $file): string { if (empty($file)) throw new Exception("No file name provided."); + $targetLocation = $this->config["tempDirectory"] . "/" . $file; + try { + if (!Storage::createParentDirectories($targetLocation)) { + throw new Exception("Couldn't make parent directories in target location."); + } + $this->s3Client->getObject(array( 'Bucket' => $this->config["s3Storage"]["bucket"], 'Key' => $file, - 'SaveAs' => $this->config["tempDirectory"] . "/" . $file + 'SaveAs' => $targetLocation )); - return $this->config["tempDirectory"] . "/" . $file; + return $targetLocation; } catch (S3Exception $e) { error_log($e->getMessage()); throw new Exception("Couldn't retrieve file from S3."); From b5769f7dd704a9d9c3a8af60dfc6750d38bc5d42 Mon Sep 17 00:00:00 2001 From: Andrew Barron Date: Wed, 21 Jul 2021 16:30:54 +0100 Subject: [PATCH 18/18] Close #51 with new completions for show/presenter name without spaces --- processing/Recording.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/processing/Recording.php b/processing/Recording.php index c3cbe17..c2c1f61 100644 --- a/processing/Recording.php +++ b/processing/Recording.php @@ -350,18 +350,24 @@ public function getFileName(): string { if (empty($this->extension)) throw new Exception("No file extension stored before requesting file name."); // Decode any encoded special characters - $name = htmlspecialchars_decode($this->name, ENT_QUOTES); - $presenter = htmlspecialchars_decode($this->presenter, ENT_QUOTES); + $name = htmlspecialchars_decode($this->name, ENT_QUOTES); + $presenter = htmlspecialchars_decode($this->presenter, ENT_QUOTES); // Replace special characters in show details with spaces - $name = preg_replace("/\W/", " ", $name); - $presenter = preg_replace("/\W/", " ", $presenter); + $name = preg_replace("/\W/", " ", $name); + $presenter = preg_replace("/\W/", " ", $presenter); // replace multiple spaces with a single space and trim whitespace from ends - $name = trim(preg_replace("/\s+/", " ", $name)); - $presenter = trim(preg_replace("/\s+/", " ", $presenter)); + $name = trim(preg_replace("/\s+/", " ", $name)); + $presenter = trim(preg_replace("/\s+/", " ", $presenter)); + + // make a version of the name and presenter name without spaces + $nameNoSpaces = trim(preg_replace("/\s+/", "-", $name)); + $presenterNoSpaces = trim(preg_replace("/\s+/", "-", $presenter)); $replacements = array( '{s}' => $name, '{p}' => $presenter, + '{s-}' => $nameNoSpaces, + '{p-}' => $presenterNoSpaces, '{d}' => date("j", strtotime($this->start)), '{dd}' => date("d", strtotime($this->start)), '{m}' => date("n", strtotime($this->start)),