diff --git a/lib/Exceptions/InvalidDiscussionUrlException.php b/lib/Exceptions/InvalidDiscussionUrlException.php new file mode 100644 index 00000000..ab3dcd42 --- /dev/null +++ b/lib/Exceptions/InvalidDiscussionUrlException.php @@ -0,0 +1,7 @@ +DiscussionUrl = preg_replace('|^(https://groups\.google\.com/g/standardebooks/c/[^/]+).*|iu', '\1', $this->DiscussionUrl); } + + if(!preg_match('|^https://groups\.google\.com/g/standardebooks/c/[^/\?]+$|iu', $this->DiscussionUrl)){ + $error->Add(new Exceptions\InvalidDiscussionUrlException($this->DiscussionUrl)); + } } $this->VcsUrl = trim($this->VcsUrl ?? ''); @@ -231,7 +235,7 @@ final class Project{ elseif(preg_match('|^https?://(www\.)?github.com/|ius', $this->VcsUrl)){ $this->VcsUrl = rtrim($this->VcsUrl, '/'); if(!preg_match('|^https://github.com/[^/]+/[^/]+$|ius', $this->VcsUrl)){ - $error->Add(new Exceptions\InvalidVcsUrlException()); + $error->Add(new Exceptions\InvalidVcsUrlException($this->VcsUrl)); } } diff --git a/templates/EbookPlaceholderForm.php b/templates/EbookPlaceholderForm.php index 4d95e8da..42fa897f 100644 --- a/templates/EbookPlaceholderForm.php +++ b/templates/EbookPlaceholderForm.php @@ -1,6 +1,7 @@
Contributors @@ -190,11 +191,11 @@ $isEditForm = $isEditForm ?? false;
- +
Project
- diff --git a/www/css/core.css b/www/css/core.css index 524fd25e..de484b3f 100644 --- a/www/css/core.css +++ b/www/css/core.css @@ -2562,6 +2562,10 @@ details summary ~ *{ margin-left: 1rem; } +fieldset label.controls-following-fieldset + fieldset{ + margin-top: -.5rem; +} + fieldset p{ border-bottom: 1px dashed var(--input-border); } diff --git a/www/css/ebook-placeholder.css b/www/css/ebook-placeholder.css index 5b901dc1..3f5f957d 100644 --- a/www/css/ebook-placeholder.css +++ b/www/css/ebook-placeholder.css @@ -1,65 +1,65 @@ -form.create-update-ebook-placeholder fieldset{ +.create-update-ebook-placeholder fieldset{ display: grid; gap: 2rem; } -form.create-update-ebook-placeholder details + fieldset, -form.create-update-ebook-placeholder fieldset + fieldset{ +.create-update-ebook-placeholder details + fieldset, +.create-update-ebook-placeholder fieldset + fieldset{ margin-top: 2rem; } -form.create-update-ebook-placeholder > fieldset:nth-of-type(1), -form.create-update-ebook-placeholder details:nth-of-type(1) fieldset{ +.create-update-ebook-placeholder > fieldset:nth-of-type(1), +.create-update-ebook-placeholder details:nth-of-type(1) fieldset{ grid-template-columns: 1fr 1fr; } -form.create-update-ebook-placeholder > fieldset:nth-of-type(2), -form.create-update-ebook-placeholder details:nth-of-type(2) fieldset{ +.create-update-ebook-placeholder > fieldset:nth-of-type(2), +.create-update-ebook-placeholder details:nth-of-type(2) fieldset{ grid-template-columns: 1fr 200px; } -form.create-update-ebook-placeholder fieldset label:has(input[name="ebook-placeholder-transcription-url"]), -form.create-update-ebook-placeholder fieldset label:has(textarea[name="ebook-placeholder-notes"]){ +.create-update-ebook-placeholder fieldset label:has(input[name="ebook-placeholder-transcription-url"]), +.create-update-ebook-placeholder fieldset label:has(textarea[name="ebook-placeholder-notes"]){ grid-column: 1 / span 2; } -form.create-update-ebook-placeholder fieldset:has(input[name="sequence-number-collection-name-1"]), -form.create-update-ebook-placeholder fieldset:has(input[name="sequence-number-collection-name-2"]), -form.create-update-ebook-placeholder fieldset:has(input[name="sequence-number-collection-name-3"]){ +.create-update-ebook-placeholder fieldset:has(input[name="sequence-number-collection-name-1"]), +.create-update-ebook-placeholder fieldset:has(input[name="sequence-number-collection-name-2"]), +.create-update-ebook-placeholder fieldset:has(input[name="sequence-number-collection-name-3"]){ display: grid; grid-template-columns: 1fr 200px 200px; gap: 2rem; } -form.create-update-ebook-placeholder fieldset label:has(input[type="checkbox"]){ +.create-update-ebook-placeholder fieldset label:has(input[type="checkbox"]){ grid-column: 1 / span 2; } -form.create-update-ebook-placeholder details{ +.create-update-ebook-placeholder details{ margin-top: 1rem; } -form.create-update-ebook-placeholder summary{ +.create-update-ebook-placeholder summary{ font-style: italic; } -form.create-update-ebook-placeholder p{ +.create-update-ebook-placeholder p{ margin-bottom: 1rem; margin-top: 1.5rem; font-style: italic; } -form.create-update-ebook-placeholder fieldset p{ +.create-update-ebook-placeholder fieldset p{ font-style: italic; margin: 0; border: none; } -form.create-update-ebook-placeholder fieldset p:first-of-type{ +.create-update-ebook-placeholder fieldset p:first-of-type{ margin-top: 0; } -form.create-update-ebook-placeholder legend{ +.create-update-ebook-placeholder legend{ font-size: 1.4rem; font-family: "League Spartan", Arial, sans-serif; margin-bottom: 1rem; @@ -68,7 +68,7 @@ form.create-update-ebook-placeholder legend{ text-transform: uppercase; } -form.create-update-ebook-placeholder label{ +.create-update-ebook-placeholder label{ display: block; } @@ -78,12 +78,12 @@ form div.footer{ } /* Hide the next fieldset unless the ebook-placeholder-is-wanted checkbox is checked. */ -form.create-update-ebook-placeholder label.controls-following-fieldset + fieldset{ +.create-update-ebook-placeholder label.controls-following-fieldset + fieldset{ display: none; grid-column: 1 / span 2; } -form.create-update-ebook-placeholder label.controls-following-fieldset:has(input[type="checkbox"]:checked) + fieldset{ +.create-update-ebook-placeholder label.controls-following-fieldset:has(input[type="checkbox"]:checked) + fieldset{ display: grid; } diff --git a/www/css/project.css b/www/css/project.css index 26d33380..6aa59e87 100644 --- a/www/css/project.css +++ b/www/css/project.css @@ -25,6 +25,10 @@ text-align: right; } +.project-form .placeholder-form{ + grid-column: 1 / span 2; +} + table.data-table .status, table.data-table .producer{ white-space: nowrap; diff --git a/www/ebook-placeholders/edit.php b/www/ebook-placeholders/edit.php index 8c6bbc7e..ba660f44 100644 --- a/www/ebook-placeholders/edit.php +++ b/www/ebook-placeholders/edit.php @@ -56,6 +56,9 @@ catch(Exceptions\InvalidPermissionsException){
$ebook, 'isEditForm' => true]) ?> +
diff --git a/www/ebook-placeholders/new.php b/www/ebook-placeholders/new.php index e455dcfc..4f728aba 100644 --- a/www/ebook-placeholders/new.php +++ b/www/ebook-placeholders/new.php @@ -82,6 +82,9 @@ catch(Exceptions\InvalidPermissionsException){
$ebook]) ?> +
diff --git a/www/ebook-placeholders/post.php b/www/ebook-placeholders/post.php index ba67b163..e0065aca 100644 --- a/www/ebook-placeholders/post.php +++ b/www/ebook-placeholders/post.php @@ -42,9 +42,9 @@ try{ $_SESSION['is-only-ebook-project-created'] = true; } else{ - // No `Project`, throw the exception and really fail. + // The existing ebook already has a `Project`, throw the exception and really fail. $ebook = $existingEbook; - throw $ex; + throw new Exceptions\ProjectExistsException('This ebook already exists, and already has an in-progress project.'); } } @@ -85,7 +85,7 @@ catch(Exceptions\LoginRequiredException){ catch(Exceptions\InvalidPermissionsException | Exceptions\InvalidHttpMethodException | Exceptions\HttpMethodNotAllowedException){ Template::ExitWithCode(Enums\HttpCode::Forbidden); } -catch(Exceptions\InvalidEbookException | Exceptions\InvalidProjectException $ex){ +catch(Exceptions\InvalidEbookException | Exceptions\ProjectExistsException | Exceptions\InvalidProjectException $ex){ $_SESSION['ebook'] = $ebook; $_SESSION['exception'] = $ex; diff --git a/www/newsletter/subscriptions/new.php b/www/newsletter/subscriptions/new.php index 12fce89b..4a1d3b75 100644 --- a/www/newsletter/subscriptions/new.php +++ b/www/newsletter/subscriptions/new.php @@ -47,10 +47,10 @@ if($exception){

What kind of email would you like to receive?

diff --git a/www/newsletter/subscriptions/post.php b/www/newsletter/subscriptions/post.php index 667aa12e..2b40a0e7 100644 --- a/www/newsletter/subscriptions/post.php +++ b/www/newsletter/subscriptions/post.php @@ -32,8 +32,8 @@ try{ $subscription->User = new User(); $subscription->User->Email = HttpInput::Str(POST, 'email'); - $subscription->IsSubscribedToNewsletter = HttpInput::Bool(POST, 'issubscribedtonewsletter') ?? false; - $subscription->IsSubscribedToSummary = HttpInput::Bool(POST, 'issubscribedtosummary') ?? false; + $subscription->IsSubscribedToNewsletter = HttpInput::Bool(POST, 'is-subscribed-to-newsletter') ?? false; + $subscription->IsSubscribedToSummary = HttpInput::Bool(POST, 'is-subscribed-to-summary') ?? false; $expectedCaptcha = HttpInput::Str(SESSION, 'captcha') ?? ''; $receivedCaptcha = HttpInput::Str(POST, 'captcha'); diff --git a/www/projects/index.php b/www/projects/index.php index ec4b0413..f2c2cc6a 100644 --- a/www/projects/index.php +++ b/www/projects/index.php @@ -19,8 +19,10 @@ try{ session_start(); $isCreated = HttpInput::Bool(SESSION, 'is-project-created') ?? false; + $isOnlyProjectCreated = HttpInput::Bool(SESSION, 'is-only-ebook-project-created') ?? false; + $createdProject = HttpInput::SessionObject('project', Project::class); - if($isCreated){ + if($isCreated || $isOnlyProjectCreated){ // We got here because a `Project` was successfully submitted. http_response_code(Enums\HttpCode::Created->value); session_unset(); @@ -43,9 +45,19 @@ catch(Exceptions\InvalidPermissionsException){

Projects

+ Benefits->CanEditProjects){ ?> +

+ New project +

+ + + +

Project for Ebook->Title) ?> created!

+ - -

Project created!

+ +

An ebook placeholder already exists for this ebook, but a new project was created!

+
diff --git a/www/projects/new.php b/www/projects/new.php index f6cc0648..e26fd8b0 100644 --- a/www/projects/new.php +++ b/www/projects/new.php @@ -54,7 +54,7 @@ catch(Exceptions\InvalidPermissionsException){ } ?> 'New Project', - 'css' => ['/css/project.css'], + 'css' => ['/css/project.css', '/css/ebook-placeholder.css'], 'description' => 'Add a new ebook project.' ]) ?>
@@ -69,7 +69,13 @@ catch(Exceptions\InvalidPermissionsException){ $exception]) ?>
- $project, 'areFieldsRequired' => true]) ?> + $project]) ?> + EbookId)){ ?> +
+ Placeholder + $project->Ebook ?? new Ebook(), 'showProjectForm' => false]) ?> +
+ diff --git a/www/projects/post.php b/www/projects/post.php index 367d3ad4..b7c25b09 100644 --- a/www/projects/post.php +++ b/www/projects/post.php @@ -20,7 +20,36 @@ try{ $project->FillFromHttpPost(); - $project->Create(); + // Are we creating a new placeholder at the same time? + if(!isset($project->EbookId)){ + $project->Ebook = new Ebook(); + $project->Ebook->FillFromEbookPlaceholderForm(); + $project->Ebook->Validate(); + + $project->Validate(true, true); + + try{ + $project->Ebook->Create(); + $project->EbookId = $project->Ebook->EbookId; + } + catch(Exceptions\DuplicateEbookException $ex){ + // If the `Ebook` already exists, create the `Project` anyway. + $project->Ebook = Ebook::GetByIdentifier($project->Ebook->Identifier); + if(!$project->Ebook->EbookPlaceholder?->IsInProgress){ + $project->EbookId = $project->Ebook->EbookId; + $_SESSION['is-only-ebook-project-created'] = true; + } + else{ + // `Ebook` exists and it's not a placeholder, so really fail. + throw new Exceptions\EbookIsNotAPlaceholderException(); + } + } + + $project->Create(); + } + else{ + $project->Create(); + } $_SESSION['project'] = $project; $_SESSION['is-project-created'] = true; @@ -52,7 +81,7 @@ catch(Exceptions\LoginRequiredException){ catch(Exceptions\InvalidPermissionsException){ Template::ExitWithCode(Enums\HttpCode::Forbidden); } -catch(Exceptions\InvalidProjectException | Exceptions\ProjectExistsException | Exceptions\EbookIsNotAPlaceholderException $ex){ +catch(Exceptions\InvalidProjectException | Exceptions\InvalidEbookException | Exceptions\ProjectExistsException | Exceptions\DuplicateEbookException | Exceptions\EbookIsNotAPlaceholderException $ex){ $_SESSION['project'] = $project; $_SESSION['exception'] = $ex;