Skip to content

Commit

Permalink
Merge pull request #44 from wlabarron/v1.1
Browse files Browse the repository at this point in the history
v1.1
  • Loading branch information
wlabarron authored Aug 14, 2021
2 parents ee1947d + 877aee0 commit 82cddb4
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 74 deletions.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -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"
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 8 additions & 11 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<link rel="preconnect" href="https://cdn.jsdelivr.net">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Submit a show for scheduling and automatic upload to Mixcloud.">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-beta3/css/bootstrap.min.css"
integrity="sha512-N415hCJJdJx+1UBfULt+i+ihvOn42V/kOjOpp1UTh4CZ70Hx5bDlKryWaqEKfY/8EYOu/C2MuyaluJryK1Lb5Q=="
crossorigin="anonymous" />
Expand Down Expand Up @@ -58,7 +59,7 @@
<div class="alert alert-danger mt-2" role="alert">
Your browser doesn't support some of the technologies this uploader needs. Swap to another one, such as an
up-to-date version of Firefox, then try there.<br> If you've not got another web browser installed, you can
<a href="https://www.mozilla.org/en-GB/firefox/new/" target="_blank" class="alert-link">download Firefox</a>.
<a href="https://www.mozilla.org/en-GB/firefox/new/" target="_blank" class="alert-link" rel="noopener noreferrer">download Firefox</a>.
</div>
</div>

Expand Down Expand Up @@ -106,7 +107,7 @@
<option value='special'>One-off or Special Show</option>
</optgroup>
</select>
<small id="nameDropdown" class="form-text text-muted">
<small id="nameDropdownHelp" class="form-text text-muted">
Show missing? Please report it to technical staff.
</small>
</div>
Expand All @@ -124,7 +125,7 @@
<label for="presenter">Show presenter</label>
<input type="text" class="form-control" id="presenter" required aria-describedby="presenterHelp" name="presenter"
maxlength="50">
<small id="presenter" class="form-text text-muted">
<small id="presenterHelp" class="form-text text-muted">
Enter the show's presenter.
</small>
</div>
Expand All @@ -140,10 +141,6 @@
</small>
</div>

<div class="alert alert-warning" hidden role="alert" id="error-InitialFormInvalid">
Hang on! Make sure you've filled in all the fields above correctly.
</div>

<div class="alert alert-warning mt-2" hidden role="alert" id="error-ShowFileOversized">
<strong>The show file you chose is too big.</strong> The maximum size
is <?php echo $config["maxShowFileSizeFriendly"]; ?>.
Expand Down Expand Up @@ -203,7 +200,7 @@
<select class="form-select"
id="imageSource"
name="imageSource"
aria-label="Choose how to proceed with the cover image">
aria-label="Choose which cover image to use">
<option value="default">Use saved photo for this show</option>
<option value="upload" selected>Upload new photo</option>
<option value="none">Don't use a photo</option>
Expand All @@ -213,7 +210,7 @@

<div class="form-group" id="imageUploader">
<input type="file" class="form-control" id="image" name="image" accept="image/png,image/jpeg"
aria-describedby="imageHelp">
aria-describedby="imageHelp" aria-label="Show cover image">
<small id="imageHelp" class="form-text text-muted">
You can upload JPG or PNG files up to <?php echo $config["maxShowImageSizeFriendly"]; ?>.
</small>
Expand Down Expand Up @@ -312,7 +309,7 @@
above</strong>.</p>

<!-- Submit button behaviour modified by the <form> tag -->
<button type="submit" id="submit" class="btn btn-lg btn-outline-dark w-100" disabled>
<button type="submit" id="submit" class="btn btn-lg btn-outline-dark w-100" aria-describedby="uploadingHelpText" disabled>
<i class="spinner-border"></i> Uploading...
</button>

Expand All @@ -326,7 +323,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/autosize.js/4.0.2/autosize.min.js"
integrity="sha512-Fv9UOVSqZqj4FDYBbHkvdMFOEopbT/GvdTQfuWUwnlOC6KR49PnxOVMhNG8LzqyDf+tYivRqIWVxGdgsBWOmjg=="
crossorigin="anonymous"></script>
<script src="resources/script.js?version=12"></script>
<script src="resources/script.js?version=13"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/date-input-polyfill.dist.js"
integrity="sha256-FcR3bJqClBNWiJqgW1E9yEgSRoAqRNcjOfgRfaH0LVw="
crossorigin="anonymous"></script>
Expand Down
30 changes: 23 additions & 7 deletions processing/LocalStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.");
}
}

Expand Down
65 changes: 50 additions & 15 deletions processing/Recording.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -321,27 +344,39 @@ 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);

$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));

return $presenter . "-" . $name . " " . $date . "." . $this->extension;
$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)),
'{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;
}

/**
Expand Down
10 changes: 8 additions & 2 deletions processing/S3Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
29 changes: 26 additions & 3 deletions processing/Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,10 +69,16 @@ public static function getProvider(): Storage {
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($holdingLocation)) {
if (!Storage::createParentDirectories($targetLocation)) {
throw new Exception("Couldn't make parent directories in target location.");
}

if (file_exists($config["holdingDirectory"] . "/" . $file)) {
if (!rename($config["holdingDirectory"] . "/" . $file, $config["waitingDirectory"] . "/" . $file)) {
if (!rename($holdingLocation, $targetLocation)) {
throw new Exception("Couldn't move file from holding to waiting.");
}
} else {
Expand Down
33 changes: 30 additions & 3 deletions processing/config.php.template
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,36 @@ 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',
// 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
// 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' => '',
Expand Down
13 changes: 8 additions & 5 deletions processing/cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -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"]);
Expand Down
Loading

0 comments on commit 82cddb4

Please sign in to comment.