mirror of
https://github.com/standardebooks/web.git
synced 2025-07-07 15:20:32 -04:00
Add beginning of a project management system to placeholders
This commit is contained in:
parent
e56de4b19d
commit
adfe07aad9
42 changed files with 717 additions and 118 deletions
|
@ -10,6 +10,8 @@ CREATE TABLE IF NOT EXISTS `Benefits` (
|
||||||
`CanEditCollections` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
`CanEditCollections` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
`CanEditEbooks` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
`CanEditEbooks` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
`CanCreateEbookPlaceholders` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
`CanCreateEbookPlaceholders` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`CanManageProjects` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`CanReviewProjects` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (`UserId`),
|
PRIMARY KEY (`UserId`),
|
||||||
KEY `idxBenefits` (`CanAccessFeeds`,`CanVote`,`CanBulkDownload`)
|
KEY `idxBenefits` (`CanAccessFeeds`,`CanVote`,`CanBulkDownload`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
|
@ -2,7 +2,7 @@ CREATE TABLE IF NOT EXISTS `Projects` (
|
||||||
`ProjectId` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
`ProjectId` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`Status` enum('in_progress','stalled','completed','abandoned') NOT NULL DEFAULT 'in_progress',
|
`Status` enum('in_progress','stalled','completed','abandoned') NOT NULL DEFAULT 'in_progress',
|
||||||
`EbookId` int(11) NOT NULL,
|
`EbookId` int(11) NOT NULL,
|
||||||
`ProducerName` varchar(151) DEFAULT NULL,
|
`ProducerName` varchar(151) NOT NULL DEFAULT '',
|
||||||
`ProducerEmail` varchar(80) DEFAULT NULL,
|
`ProducerEmail` varchar(80) DEFAULT NULL,
|
||||||
`DiscussionUrl` varchar(255) DEFAULT NULL,
|
`DiscussionUrl` varchar(255) DEFAULT NULL,
|
||||||
`VcsUrl` varchar(255) NOT NULL,
|
`VcsUrl` varchar(255) NOT NULL,
|
||||||
|
|
|
@ -109,10 +109,10 @@ class Collection{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidCollectionException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidCollectionException();
|
||||||
|
|
||||||
if(isset($this->Name)){
|
if(isset($this->Name)){
|
||||||
$this->Name = trim($this->Name);
|
$this->Name = trim($this->Name);
|
||||||
|
@ -154,7 +154,7 @@ class Collection{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidCollectionException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
@ -169,7 +169,7 @@ class Collection{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidCollectionException
|
||||||
*/
|
*/
|
||||||
public function GetByUrlNameOrCreate(string $urlName): Collection{
|
public function GetByUrlNameOrCreate(string $urlName): Collection{
|
||||||
$result = Db::Query('
|
$result = Db::Query('
|
||||||
|
|
|
@ -64,10 +64,10 @@ class Contributor{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidContributorException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidContributorException();
|
||||||
|
|
||||||
if(!isset($this->EbookId)){
|
if(!isset($this->EbookId)){
|
||||||
$error->Add(new Exceptions\ContributorEbookIdRequiredException());
|
$error->Add(new Exceptions\ContributorEbookIdRequiredException());
|
||||||
|
@ -140,7 +140,7 @@ class Contributor{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidContributorException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
|
156
lib/Ebook.php
156
lib/Ebook.php
|
@ -42,6 +42,8 @@ use function Safe\shell_exec;
|
||||||
* @property string $TextSinglePageSizeFormatted
|
* @property string $TextSinglePageSizeFormatted
|
||||||
* @property string $IndexableText
|
* @property string $IndexableText
|
||||||
* @property ?EbookPlaceholder $EbookPlaceholder
|
* @property ?EbookPlaceholder $EbookPlaceholder
|
||||||
|
* @property array<Project> $Projects
|
||||||
|
* @property ?Project $ProjectInProgress
|
||||||
*/
|
*/
|
||||||
class Ebook{
|
class Ebook{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
@ -118,11 +120,48 @@ class Ebook{
|
||||||
protected string $_TextSinglePageSizeFormatted;
|
protected string $_TextSinglePageSizeFormatted;
|
||||||
protected string $_IndexableText;
|
protected string $_IndexableText;
|
||||||
protected ?EbookPlaceholder $_EbookPlaceholder = null;
|
protected ?EbookPlaceholder $_EbookPlaceholder = null;
|
||||||
|
/** @var array<Project> $_Projects */
|
||||||
|
protected array $_Projects;
|
||||||
|
protected ?Project $_ProjectInProgress;
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<Project>
|
||||||
|
*/
|
||||||
|
protected function GetProjects(): array{
|
||||||
|
if(!isset($this->_Projects)){
|
||||||
|
$this->_Projects = Db::Query('
|
||||||
|
SELECT *
|
||||||
|
from Projects
|
||||||
|
where EbookId = ?
|
||||||
|
order by Created desc
|
||||||
|
', [$this->EbookId], Project::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_Projects;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GetProjectInProgress(): ?Project{
|
||||||
|
if(!isset($this->_ProjectInProgress)){
|
||||||
|
if(!isset($this->EbookId)){
|
||||||
|
$this->_ProjectInProgress = null;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->_ProjectInProgress = Db::Query('
|
||||||
|
SELECT *
|
||||||
|
from Projects
|
||||||
|
where EbookId = ?
|
||||||
|
and Status in (?, ?)
|
||||||
|
', [$this->EbookId, Enums\ProjectStatusType::InProgress, Enums\ProjectStatusType::Stalled], Project::class)[0] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_ProjectInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<GitCommit>
|
* @return array<GitCommit>
|
||||||
*/
|
*/
|
||||||
|
@ -1048,10 +1087,10 @@ class Ebook{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidEbookException();
|
||||||
|
|
||||||
if(isset($this->Identifier)){
|
if(isset($this->Identifier)){
|
||||||
$this->Identifier = trim($this->Identifier);
|
$this->Identifier = trim($this->Identifier);
|
||||||
|
@ -1335,17 +1374,13 @@ class Ebook{
|
||||||
$error->Add(new Exceptions\EbookMissingPlaceholderException());
|
$error->Add(new Exceptions\EbookMissingPlaceholderException());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$this->IsPlaceholder() && isset($this->EbookPlaceholder)){
|
|
||||||
$error->Add(new Exceptions\EbookUnexpectedPlaceholderException());
|
|
||||||
}
|
|
||||||
|
|
||||||
if($error->HasExceptions){
|
if($error->HasExceptions){
|
||||||
throw $error;
|
throw $error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookException
|
||||||
* @throws Exceptions\DuplicateEbookException
|
* @throws Exceptions\DuplicateEbookException
|
||||||
*/
|
*/
|
||||||
public function CreateOrUpdate(): void{
|
public function CreateOrUpdate(): void{
|
||||||
|
@ -1360,7 +1395,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookTagException
|
||||||
*/
|
*/
|
||||||
private function CreateTags(): void{
|
private function CreateTags(): void{
|
||||||
$tags = [];
|
$tags = [];
|
||||||
|
@ -1371,7 +1406,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidLocSubjectException
|
||||||
*/
|
*/
|
||||||
private function CreateLocSubjects(): void{
|
private function CreateLocSubjects(): void{
|
||||||
$subjects = [];
|
$subjects = [];
|
||||||
|
@ -1382,7 +1417,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidCollectionException
|
||||||
*/
|
*/
|
||||||
private function CreateCollections(): void{
|
private function CreateCollections(): void{
|
||||||
$collectionMemberships = [];
|
$collectionMemberships = [];
|
||||||
|
@ -1633,7 +1668,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookException
|
||||||
* @throws Exceptions\DuplicateEbookException If an `Ebook` with the given identifier already exists.
|
* @throws Exceptions\DuplicateEbookException If an `Ebook` with the given identifier already exists.
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
|
@ -1647,9 +1682,16 @@ class Ebook{
|
||||||
// Pass.
|
// Pass.
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->CreateTags();
|
try{
|
||||||
$this->CreateLocSubjects();
|
$this->CreateTags();
|
||||||
$this->CreateCollections();
|
$this->CreateLocSubjects();
|
||||||
|
$this->CreateCollections();
|
||||||
|
}
|
||||||
|
catch(Exceptions\ValidationException $ex){
|
||||||
|
$error = new Exceptions\InvalidEbookException();
|
||||||
|
$error->Add($ex);
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
|
||||||
Db::Query('
|
Db::Query('
|
||||||
INSERT into Ebooks (Identifier, WwwFilesystemPath, RepoFilesystemPath, KindleCoverUrl, EpubUrl,
|
INSERT into Ebooks (Identifier, WwwFilesystemPath, RepoFilesystemPath, KindleCoverUrl, EpubUrl,
|
||||||
|
@ -1687,25 +1729,39 @@ class Ebook{
|
||||||
|
|
||||||
$this->EbookId = Db::GetLastInsertedId();
|
$this->EbookId = Db::GetLastInsertedId();
|
||||||
|
|
||||||
$this->AddTags();
|
try{
|
||||||
$this->AddLocSubjects();
|
$this->AddTags();
|
||||||
$this->AddCollectionMemberships();
|
$this->AddLocSubjects();
|
||||||
$this->AddGitCommits();
|
$this->AddCollectionMemberships();
|
||||||
$this->AddSources();
|
$this->AddGitCommits();
|
||||||
$this->AddContributors();
|
$this->AddSources();
|
||||||
$this->AddTocEntries();
|
$this->AddContributors();
|
||||||
$this->AddEbookPlaceholder();
|
$this->AddTocEntries();
|
||||||
|
$this->AddEbookPlaceholder();
|
||||||
|
}
|
||||||
|
catch(Exceptions\ValidationException $ex){
|
||||||
|
$error = new Exceptions\InvalidEbookException();
|
||||||
|
$error->Add($ex);
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookException
|
||||||
*/
|
*/
|
||||||
public function Save(): void{
|
public function Save(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
|
||||||
$this->CreateTags();
|
try{
|
||||||
$this->CreateLocSubjects();
|
$this->CreateTags();
|
||||||
$this->CreateCollections();
|
$this->CreateLocSubjects();
|
||||||
|
$this->CreateCollections();
|
||||||
|
}
|
||||||
|
catch(Exceptions\ValidationException $ex){
|
||||||
|
$error = new Exceptions\InvalidEbookException();
|
||||||
|
$error->Add($ex);
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
|
||||||
Db::Query('
|
Db::Query('
|
||||||
UPDATE Ebooks
|
UPDATE Ebooks
|
||||||
|
@ -1742,29 +1798,37 @@ class Ebook{
|
||||||
$this->EbookCreated, $this->EbookUpdated, $this->TextSinglePageByteCount, $this->IndexableText,
|
$this->EbookCreated, $this->EbookUpdated, $this->TextSinglePageByteCount, $this->IndexableText,
|
||||||
$this->EbookId]);
|
$this->EbookId]);
|
||||||
|
|
||||||
$this->RemoveTags();
|
|
||||||
$this->AddTags();
|
|
||||||
|
|
||||||
$this->RemoveLocSubjects();
|
try{
|
||||||
$this->AddLocSubjects();
|
$this->RemoveTags();
|
||||||
|
$this->AddTags();
|
||||||
|
|
||||||
$this->RemoveCollectionMemberships();
|
$this->RemoveLocSubjects();
|
||||||
$this->AddCollectionMemberships();
|
$this->AddLocSubjects();
|
||||||
|
|
||||||
$this->RemoveGitCommits();
|
$this->RemoveCollectionMemberships();
|
||||||
$this->AddGitCommits();
|
$this->AddCollectionMemberships();
|
||||||
|
|
||||||
$this->RemoveSources();
|
$this->RemoveGitCommits();
|
||||||
$this->AddSources();
|
$this->AddGitCommits();
|
||||||
|
|
||||||
$this->RemoveContributors();
|
$this->RemoveSources();
|
||||||
$this->AddContributors();
|
$this->AddSources();
|
||||||
|
|
||||||
$this->RemoveTocEntries();
|
$this->RemoveContributors();
|
||||||
$this->AddTocEntries();
|
$this->AddContributors();
|
||||||
|
|
||||||
$this->RemoveEbookPlaceholder();
|
$this->RemoveTocEntries();
|
||||||
$this->AddEbookPlaceholder();
|
$this->AddTocEntries();
|
||||||
|
|
||||||
|
$this->RemoveEbookPlaceholder();
|
||||||
|
$this->AddEbookPlaceholder();
|
||||||
|
}
|
||||||
|
catch(Exceptions\ValidationException $ex){
|
||||||
|
$error = new Exceptions\InvalidEbookException();
|
||||||
|
$error->Add($ex);
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function RemoveTags(): void{
|
private function RemoveTags(): void{
|
||||||
|
@ -1854,7 +1918,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidGitCommitException
|
||||||
*/
|
*/
|
||||||
private function AddGitCommits(): void{
|
private function AddGitCommits(): void{
|
||||||
foreach($this->GitCommits as $commit){
|
foreach($this->GitCommits as $commit){
|
||||||
|
@ -1872,7 +1936,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidSourceException
|
||||||
*/
|
*/
|
||||||
private function AddSources(): void{
|
private function AddSources(): void{
|
||||||
foreach($this->Sources as $sortOrder => $source){
|
foreach($this->Sources as $sortOrder => $source){
|
||||||
|
@ -1891,7 +1955,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidContributorException
|
||||||
*/
|
*/
|
||||||
private function AddContributors(): void{
|
private function AddContributors(): void{
|
||||||
$allContributors = array_merge($this->Authors, $this->Illustrators, $this->Translators, $this->Contributors);
|
$allContributors = array_merge($this->Authors, $this->Illustrators, $this->Translators, $this->Contributors);
|
||||||
|
@ -1932,7 +1996,7 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookPlaceholderException
|
||||||
*/
|
*/
|
||||||
private function AddEbookPlaceholder(): void{
|
private function AddEbookPlaceholder(): void{
|
||||||
if(isset($this->EbookPlaceholder)){
|
if(isset($this->EbookPlaceholder)){
|
||||||
|
|
|
@ -73,11 +73,11 @@ class EbookPlaceholder{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookPlaceholderException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$thisYear = intval(NOW->format('Y'));
|
$thisYear = intval(NOW->format('Y'));
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidEbookPlaceholderException();
|
||||||
|
|
||||||
if(isset($this->YearPublished) && ($this->YearPublished <= 0 || $this->YearPublished > $thisYear)){
|
if(isset($this->YearPublished) && ($this->YearPublished <= 0 || $this->YearPublished > $thisYear)){
|
||||||
$error->Add(new Exceptions\InvalidEbookPlaceholderYearPublishedException());
|
$error->Add(new Exceptions\InvalidEbookPlaceholderYearPublishedException());
|
||||||
|
@ -99,7 +99,7 @@ class EbookPlaceholder{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookPlaceholderException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
@ -117,4 +117,13 @@ class EbookPlaceholder{
|
||||||
', [$this->EbookId, $this->YearPublished, $this->Difficulty, $this->TranscriptionUrl,
|
', [$this->EbookId, $this->YearPublished, $this->Difficulty, $this->TranscriptionUrl,
|
||||||
$this->IsWanted, $this->IsInProgress, $this->IsPatron, $this->Notes]);
|
$this->IsWanted, $this->IsInProgress, $this->IsPatron, $this->Notes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function Delete(): void{
|
||||||
|
Db::Query('
|
||||||
|
DELETE
|
||||||
|
from EbookPlaceholders
|
||||||
|
where EbookId = ?
|
||||||
|
',
|
||||||
|
[$this->EbookId]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@ class EbookSource{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidSourceException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidSourceException();
|
||||||
|
|
||||||
if(!isset($this->EbookId)){
|
if(!isset($this->EbookId)){
|
||||||
$error->Add(new Exceptions\EbookSourceEbookIdRequiredException());
|
$error->Add(new Exceptions\EbookSourceEbookIdRequiredException());
|
||||||
|
@ -44,7 +44,7 @@ class EbookSource{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidSourceException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
|
|
@ -23,10 +23,10 @@ class EbookTag extends Tag{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookTagException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidEbookTagException();
|
||||||
|
|
||||||
if(isset($this->Name)){
|
if(isset($this->Name)){
|
||||||
$this->Name = trim($this->Name);
|
$this->Name = trim($this->Name);
|
||||||
|
@ -55,7 +55,7 @@ class EbookTag extends Tag{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookTagException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
@ -75,7 +75,7 @@ class EbookTag extends Tag{
|
||||||
// ***********
|
// ***********
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidEbookTagException
|
||||||
*/
|
*/
|
||||||
public function GetByNameOrCreate(string $name): EbookTag{
|
public function GetByNameOrCreate(string $name): EbookTag{
|
||||||
$result = Db::Query('
|
$result = Db::Query('
|
||||||
|
|
16
lib/Enums/ProjectStatusType.php
Normal file
16
lib/Enums/ProjectStatusType.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?
|
||||||
|
namespace Enums;
|
||||||
|
|
||||||
|
enum ProjectStatusType: string{
|
||||||
|
case InProgress = 'in_progress';
|
||||||
|
case Stalled = 'stalled';
|
||||||
|
case Completed = 'completed';
|
||||||
|
case Abandoned = 'abandoned';
|
||||||
|
|
||||||
|
public function GetDisplayName(): string{
|
||||||
|
return match($this){
|
||||||
|
self::InProgress => 'in progress',
|
||||||
|
default => $this->value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
7
lib/Exceptions/EbookIsNotAPlaceholderException.php
Normal file
7
lib/Exceptions/EbookIsNotAPlaceholderException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class EbookIsNotAPlaceholderException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'This project’s ebook is already released, and not a placeholder.';
|
||||||
|
}
|
7
lib/Exceptions/EbookRequiredException.php
Normal file
7
lib/Exceptions/EbookRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class EbookRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Ebook required.';
|
||||||
|
}
|
7
lib/Exceptions/InvalidCollectionException.php
Normal file
7
lib/Exceptions/InvalidCollectionException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidCollectionException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Collection is invalid.';
|
||||||
|
}
|
7
lib/Exceptions/InvalidContributorException.php
Normal file
7
lib/Exceptions/InvalidContributorException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidContributorException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Contributor is invalid.';
|
||||||
|
}
|
5
lib/Exceptions/InvalidEbookException.php
Normal file
5
lib/Exceptions/InvalidEbookException.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidEbookException extends ValidationException{
|
||||||
|
}
|
7
lib/Exceptions/InvalidEbookPlaceholderException.php
Normal file
7
lib/Exceptions/InvalidEbookPlaceholderException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidEbookPlaceholderException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Ebook placeholder is invalid.';
|
||||||
|
}
|
7
lib/Exceptions/InvalidEbookTagException.php
Normal file
7
lib/Exceptions/InvalidEbookTagException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidEbookTagException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Ebook tag is invalid.';
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<?
|
<?
|
||||||
namespace Exceptions;
|
namespace Exceptions;
|
||||||
|
|
||||||
class InvalidGitCommitException extends AppException{
|
class InvalidGitCommitException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Git commit is invalid.';
|
||||||
}
|
}
|
||||||
|
|
7
lib/Exceptions/InvalidLocSubjectException.php
Normal file
7
lib/Exceptions/InvalidLocSubjectException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidLocSubjectException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'LoC Subject is invalid.';
|
||||||
|
}
|
5
lib/Exceptions/InvalidProjectException.php
Normal file
5
lib/Exceptions/InvalidProjectException.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidProjectException extends ValidationException{
|
||||||
|
}
|
7
lib/Exceptions/InvalidSourceException.php
Normal file
7
lib/Exceptions/InvalidSourceException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidSourceException extends ValidationException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Source is invalid.';
|
||||||
|
}
|
7
lib/Exceptions/InvalidVcsUrlException.php
Normal file
7
lib/Exceptions/InvalidVcsUrlException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidVcsUrlException extends InvalidUrlException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Invalid VCS URL.';
|
||||||
|
}
|
7
lib/Exceptions/ManagerRequiredException.php
Normal file
7
lib/Exceptions/ManagerRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class ManagerRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Manager is required.';
|
||||||
|
}
|
7
lib/Exceptions/ProducerNameRequiredException.php
Normal file
7
lib/Exceptions/ProducerNameRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class ProducerNameRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Producer name is required.';
|
||||||
|
}
|
7
lib/Exceptions/ProjectExistsException.php
Normal file
7
lib/Exceptions/ProjectExistsException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class ProjectExistsException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'An active project already exists for this ebook.';
|
||||||
|
}
|
7
lib/Exceptions/ReviewerRequiredException.php
Normal file
7
lib/Exceptions/ReviewerRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class ReviewerRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Reviewer is required.';
|
||||||
|
}
|
7
lib/Exceptions/StartedTimestampRequiredException.php
Normal file
7
lib/Exceptions/StartedTimestampRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class StartedTimestampRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Started timestamp is required.';
|
||||||
|
}
|
7
lib/Exceptions/VcsUrlRequiredException.php
Normal file
7
lib/Exceptions/VcsUrlRequiredException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class VcsUrlRequiredException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'VCS URL is required.';
|
||||||
|
}
|
|
@ -37,10 +37,10 @@ class GitCommit{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidGitCommitException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidGitCommitException();
|
||||||
|
|
||||||
if(!isset($this->EbookId)){
|
if(!isset($this->EbookId)){
|
||||||
$error->Add(new Exceptions\GitCommitEbookIdRequiredException());
|
$error->Add(new Exceptions\GitCommitEbookIdRequiredException());
|
||||||
|
@ -83,7 +83,7 @@ class GitCommit{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidGitCommitException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
|
|
@ -8,10 +8,10 @@ class LocSubject{
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidLocSubjectException
|
||||||
*/
|
*/
|
||||||
public function Validate(): void{
|
public function Validate(): void{
|
||||||
$error = new Exceptions\ValidationException();
|
$error = new Exceptions\InvalidLocSubjectException();
|
||||||
|
|
||||||
if(isset($this->Name)){
|
if(isset($this->Name)){
|
||||||
$this->Name = trim($this->Name);
|
$this->Name = trim($this->Name);
|
||||||
|
@ -34,7 +34,7 @@ class LocSubject{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidLocSubjectException
|
||||||
*/
|
*/
|
||||||
public function Create(): void{
|
public function Create(): void{
|
||||||
$this->Validate();
|
$this->Validate();
|
||||||
|
@ -47,7 +47,7 @@ class LocSubject{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exceptions\ValidationException
|
* @throws Exceptions\InvalidLocSubjectException
|
||||||
*/
|
*/
|
||||||
public function GetByNameOrCreate(string $name): LocSubject{
|
public function GetByNameOrCreate(string $name): LocSubject{
|
||||||
$result = Db::Query('
|
$result = Db::Query('
|
||||||
|
|
221
lib/Project.php
Normal file
221
lib/Project.php
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
<?
|
||||||
|
use function Safe\preg_match;
|
||||||
|
use Safe\DateTimeImmutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property Ebook $Ebook
|
||||||
|
* @property User $ManagerUser
|
||||||
|
* @property User $ReviewerUser
|
||||||
|
* @property string $Url
|
||||||
|
*/
|
||||||
|
class Project{
|
||||||
|
use Traits\Accessor;
|
||||||
|
use Traits\PropertyFromHttp;
|
||||||
|
|
||||||
|
public int $ProjectId;
|
||||||
|
public int $EbookId;
|
||||||
|
public Enums\ProjectStatusType $Status = Enums\ProjectStatusType::InProgress;
|
||||||
|
public string $ProducerName;
|
||||||
|
public ?string $ProducerEmail = null;
|
||||||
|
public ?string $DiscussionUrl = null;
|
||||||
|
public string $VcsUrl;
|
||||||
|
public DateTimeImmutable $Created;
|
||||||
|
public DateTimeImmutable $Updated;
|
||||||
|
public DateTimeImmutable $Started;
|
||||||
|
public ?DateTimeImmutable $Ended = null;
|
||||||
|
public int $ManagerUserId;
|
||||||
|
public int $ReviewerUserId;
|
||||||
|
|
||||||
|
protected Ebook $_Ebook;
|
||||||
|
protected User $_ManagerUser;
|
||||||
|
protected User $_ReviewerUser;
|
||||||
|
protected string $_Url;
|
||||||
|
|
||||||
|
protected function GetUrl(): string{
|
||||||
|
if(!isset($this->_Url)){
|
||||||
|
$this->_Url = '/projects/' . $this->ProjectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\InvalidProjectException If the `Project` is invalid.
|
||||||
|
*/
|
||||||
|
public function Validate(): void{
|
||||||
|
$error = new Exceptions\InvalidProjectException();
|
||||||
|
|
||||||
|
if(!isset($this->EbookId)){
|
||||||
|
$error->Add(new Exceptions\EbookRequiredException());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ProducerEmail = trim($this->ProducerEmail ?? '');
|
||||||
|
if($this->ProducerEmail == ''){
|
||||||
|
$this->ProducerEmail = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have an email address, try to see if we have a matching `User` in the database that we can pull the name from.
|
||||||
|
if($this->ProducerEmail !== null){
|
||||||
|
try{
|
||||||
|
$user = User::GetByEmail($this->ProducerEmail);
|
||||||
|
if($user->Name !== null){
|
||||||
|
$this->ProducerName = $user->Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exceptions\UserNotFoundException){
|
||||||
|
// Pass.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ProducerName = trim($this->ProducerName ?? '');
|
||||||
|
if($this->ProducerName == ''){
|
||||||
|
$error->Add(new Exceptions\ProducerNameRequiredException());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->DiscussionUrl = trim($this->DiscussionUrl ?? '');
|
||||||
|
if($this->DiscussionUrl == ''){
|
||||||
|
$this->DiscussionUrl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->VcsUrl = rtrim(trim($this->VcsUrl ?? ''), '/');
|
||||||
|
if($this->VcsUrl == ''){
|
||||||
|
$error->Add(new Exceptions\VcsUrlRequiredException());
|
||||||
|
}
|
||||||
|
elseif(!preg_match('|^https://github.com/[^/]+/[^/]+|ius', $this->VcsUrl)){
|
||||||
|
$error->Add(new Exceptions\InvalidVcsUrlException());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($this->ManagerUserId)){
|
||||||
|
$error->Add(new Exceptions\ManagerRequiredException());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
$this->_ManagerUser = User::Get($this->ManagerUserId);
|
||||||
|
}
|
||||||
|
catch(Exceptions\UserNotFoundException){
|
||||||
|
$error->Add(new Exceptions\UserNotFoundException('Manager user not found.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($this->ReviewerUserId)){
|
||||||
|
$error->Add(new Exceptions\ManagerRequiredException());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
$this->_ReviewerUser = User::Get($this->ReviewerUserId);
|
||||||
|
}
|
||||||
|
catch(Exceptions\UserNotFoundException){
|
||||||
|
$error->Add(new Exceptions\UserNotFoundException('Reviewer user not found.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($this->Started)){
|
||||||
|
$error->Add(new Exceptions\StartedTimestampRequiredException());
|
||||||
|
}
|
||||||
|
|
||||||
|
if($error->HasExceptions){
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\InvalidProjectException If the `Project` is invalid.
|
||||||
|
* @throws Exceptions\EbookIsNotAPlaceholderException If the `Project`'s `Ebook` is not a placeholder.
|
||||||
|
* @throws Exceptions\ProjectExistsException If the `Project`'s `Ebook` already has an active `Project`.
|
||||||
|
*/
|
||||||
|
public function Create(): void{
|
||||||
|
$this->Validate();
|
||||||
|
|
||||||
|
// Is this ebook already released?
|
||||||
|
if(!$this->Ebook->IsPlaceholder()){
|
||||||
|
throw new Exceptions\EbookIsNotAPlaceholderException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does this `Ebook` already has an active `Project`?
|
||||||
|
if($this->Ebook->ProjectInProgress !== null){
|
||||||
|
throw new Exceptions\ProjectExistsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Db::Query('
|
||||||
|
INSERT into Projects
|
||||||
|
(
|
||||||
|
EbookId,
|
||||||
|
Status,
|
||||||
|
ProducerName,
|
||||||
|
ProducerEmail,
|
||||||
|
DiscussionUrl,
|
||||||
|
VcsUrl,
|
||||||
|
Created,
|
||||||
|
Updated,
|
||||||
|
Started,
|
||||||
|
Ended,
|
||||||
|
ManagerUserId,
|
||||||
|
ReviewerUserId
|
||||||
|
)
|
||||||
|
values
|
||||||
|
(
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?
|
||||||
|
)
|
||||||
|
', [$this->EbookId, $this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, NOW, NOW, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId]);
|
||||||
|
|
||||||
|
$this->ProjectId = Db::GetLastInsertedId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\InvalidProjectException If the `Project` is invalid.
|
||||||
|
*/
|
||||||
|
public function Save(): void{
|
||||||
|
$this->Validate();
|
||||||
|
|
||||||
|
Db::Query('
|
||||||
|
UPDATE
|
||||||
|
Projects
|
||||||
|
set
|
||||||
|
Status = ?,
|
||||||
|
ProducerName = ?,
|
||||||
|
ProducerEmail = ?,
|
||||||
|
DiscussionUrl = ?,
|
||||||
|
VcsUrl = ?,
|
||||||
|
Started = ?,
|
||||||
|
Ended = ?,
|
||||||
|
ManagerUserId = ?,
|
||||||
|
ReviewerUserId = ?
|
||||||
|
where
|
||||||
|
ProjectId = ?
|
||||||
|
', [$this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId, $this->ProjectId]);
|
||||||
|
|
||||||
|
if($this->Status == Enums\ProjectStatusType::Abandoned){
|
||||||
|
Db::Query('
|
||||||
|
UPDATE
|
||||||
|
EbookPlaceholders
|
||||||
|
set
|
||||||
|
IsInProgress = false
|
||||||
|
where
|
||||||
|
EbookId = ?
|
||||||
|
', [$this->EbookId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FillFromHttpPost(): void{
|
||||||
|
$this->PropertyFromHttp('ProducerName');
|
||||||
|
$this->PropertyFromHttp('ProducerEmail');
|
||||||
|
$this->PropertyFromHttp('DiscussionUrl');
|
||||||
|
$this->PropertyFromHttp('Status');
|
||||||
|
$this->PropertyFromHttp('VcsUrl');
|
||||||
|
$this->PropertyFromHttp('Started');
|
||||||
|
$this->PropertyFromHttp('Ended');
|
||||||
|
$this->PropertyFromHttp('ManagerUserId');
|
||||||
|
$this->PropertyFromHttp('ReviewerUserId');
|
||||||
|
}
|
||||||
|
}
|
28
lib/User.php
28
lib/User.php
|
@ -345,6 +345,34 @@ class User{
|
||||||
', [$uuid], User::class)[0] ?? throw new Exceptions\UserNotFoundException();
|
', [$uuid], User::class)[0] ?? throw new Exceptions\UserNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<User>
|
||||||
|
*/
|
||||||
|
public static function GetAllByCanManageProjects(): array{
|
||||||
|
return Db::Query('
|
||||||
|
SELECT u.*
|
||||||
|
from Users u
|
||||||
|
inner join Benefits b
|
||||||
|
using (UserId)
|
||||||
|
where b.CanManageProjects = true
|
||||||
|
order by Name asc
|
||||||
|
', [], User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<User>
|
||||||
|
*/
|
||||||
|
public static function GetAllByCanReviewProjects(): array{
|
||||||
|
return Db::Query('
|
||||||
|
SELECT u.*
|
||||||
|
from Users u
|
||||||
|
inner join Benefits b
|
||||||
|
using (UserId)
|
||||||
|
where b.CanReviewProjects = true
|
||||||
|
order by Name asc
|
||||||
|
', [], User::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a `User` if they are considered "registered".
|
* Get a `User` if they are considered "registered".
|
||||||
*
|
*
|
||||||
|
|
|
@ -39,6 +39,18 @@ if($verbose){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
$ebook->CreateOrUpdate();
|
$ebook->CreateOrUpdate();
|
||||||
|
|
||||||
|
// If there was an `EbookPlaceholder` for this ebook, delete it.
|
||||||
|
if($ebook->EbookPlaceholder !== null){
|
||||||
|
$ebook->EbookPlaceholder->Delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was a `Project` for this ebook, mark it as completed.
|
||||||
|
if($ebook->ProjectInProgress !== null){
|
||||||
|
$ebook->ProjectInProgress->Status = Enums\ProjectStatusType::Completed;
|
||||||
|
$ebook->ProjectInProgress->Ended = NOW;
|
||||||
|
$ebook->ProjectInProgress->Save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(Exceptions\ValidationException $validationException){
|
catch(Exceptions\ValidationException $validationException){
|
||||||
$exceptions = $validationException->Exceptions;
|
$exceptions = $validationException->Exceptions;
|
||||||
|
|
34
templates/EbookMetadata.php
Normal file
34
templates/EbookMetadata.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?
|
||||||
|
/**
|
||||||
|
* @var Ebook $ebook
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<h2>Metadata</h2>
|
||||||
|
<table class="admin-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Ebook ID:</td>
|
||||||
|
<td><?= $ebook->EbookId ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Identifier:</td>
|
||||||
|
<td><?= Formatter::EscapeHtml($ebook->Identifier) ?></td>
|
||||||
|
</tr>
|
||||||
|
<? if(sizeof($ebook->Projects) > 0){ ?>
|
||||||
|
<tr>
|
||||||
|
<td>Projects:</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<? foreach($ebook->Projects as $project){ ?>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<?= $project->Started->format(Enums\DateTimeFormat::FullDateTime->value) ?> — <?= $project->Status->GetDisplayName() ?> — <? if($project->ProducerEmail !== null){ ?><a href="mailto:<?= Formatter::EscapeHtml($project->ProducerEmail) ?>"><?= Formatter::EscapeHtml($project->ProducerName) ?></a><? }else{ ?><?= Formatter::EscapeHtml($project->ProducerName) ?><? } ?> — <a href="<?= $project->Url ?>">Link</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<? } ?>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<? } ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
|
@ -1,9 +1,5 @@
|
||||||
<?
|
<?
|
||||||
/**
|
|
||||||
* @var ?Ebook $ebook
|
|
||||||
*/
|
|
||||||
$ebook = $ebook ?? new Ebook();
|
$ebook = $ebook ?? new Ebook();
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Contributors</legend>
|
<legend>Contributors</legend>
|
||||||
|
@ -84,7 +80,7 @@ $ebook = $ebook ?? new Ebook();
|
||||||
name="ebook-placeholder-year-published"
|
name="ebook-placeholder-year-published"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
pattern="[0-9]{1,4}"
|
pattern="[0-9]{1,4}"
|
||||||
value="<?= Formatter::EscapeHtml((string)($ebook?->EbookPlaceholder?->YearPublished)) ?>"
|
value="<?= Formatter::EscapeHtml((string)($ebook->EbookPlaceholder?->YearPublished)) ?>"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -165,16 +161,21 @@ $ebook = $ebook ?? new Ebook();
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</details>
|
</details>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
<legend>Project</legend>
|
||||||
<label>
|
<label class="controls-following-fieldset">
|
||||||
<span>In progress?</span>
|
<span>In progress?</span>
|
||||||
<input type="hidden" name="ebook-placeholder-is-in-progress" value="false" />
|
<input type="hidden" name="ebook-placeholder-is-in-progress" value="false" />
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="ebook-placeholder-is-in-progress"
|
name="ebook-placeholder-is-in-progress"
|
||||||
<? if($ebook?->EbookPlaceholder?->IsInProgress){ ?>checked="checked"<? } ?>
|
<? if($ebook->EbookPlaceholder?->IsInProgress){ ?>checked="checked"<? } ?>
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<fieldset class="project-form">
|
||||||
|
<?= Template::ProjectForm(['project' => $ebook->ProjectInProgress]) ?>
|
||||||
|
</fieldset>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
<legend>Wanted list</legend>
|
<legend>Wanted list</legend>
|
||||||
<label class="controls-following-fieldset">
|
<label class="controls-following-fieldset">
|
||||||
<span>On the wanted list?</span>
|
<span>On the wanted list?</span>
|
||||||
|
@ -182,7 +183,7 @@ $ebook = $ebook ?? new Ebook();
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="ebook-placeholder-is-wanted"
|
name="ebook-placeholder-is-wanted"
|
||||||
<? if($ebook?->EbookPlaceholder?->IsWanted){ ?>checked="checked"<? } ?>
|
<? if($ebook->EbookPlaceholder?->IsWanted){ ?>checked="checked"<? } ?>
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -192,7 +193,7 @@ $ebook = $ebook ?? new Ebook();
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="ebook-placeholder-is-patron"
|
name="ebook-placeholder-is-patron"
|
||||||
<? if($ebook?->EbookPlaceholder?->IsPatron){ ?>checked="checked"<? } ?>
|
<? if($ebook->EbookPlaceholder?->IsPatron){ ?>checked="checked"<? } ?>
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label class="icon meter">
|
<label class="icon meter">
|
||||||
|
@ -200,9 +201,9 @@ $ebook = $ebook ?? new Ebook();
|
||||||
<span>
|
<span>
|
||||||
<select name="ebook-placeholder-difficulty">
|
<select name="ebook-placeholder-difficulty">
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
<option value="<?= Enums\EbookPlaceholderDifficulty::Beginner->value ?>"<? if($ebook?->EbookPlaceholder?->Difficulty == Enums\EbookPlaceholderDifficulty::Beginner){ ?> selected="selected"<? } ?>>Beginner</option>
|
<option value="<?= Enums\EbookPlaceholderDifficulty::Beginner->value ?>"<? if($ebook->EbookPlaceholder?->Difficulty == Enums\EbookPlaceholderDifficulty::Beginner){ ?> selected="selected"<? } ?>>Beginner</option>
|
||||||
<option value="<?= Enums\EbookPlaceholderDifficulty::Intermediate->value ?>"<? if($ebook?->EbookPlaceholder?->Difficulty == Enums\EbookPlaceholderDifficulty::Intermediate){ ?> selected="selected"<? } ?>>Intermediate</option>
|
<option value="<?= Enums\EbookPlaceholderDifficulty::Intermediate->value ?>"<? if($ebook->EbookPlaceholder?->Difficulty == Enums\EbookPlaceholderDifficulty::Intermediate){ ?> selected="selected"<? } ?>>Intermediate</option>
|
||||||
<option value="<?= Enums\EbookPlaceholderDifficulty::Advanced->value ?>"<? if($ebook?->EbookPlaceholder?->Difficulty == Enums\EbookPlaceholderDifficulty::Advanced){ ?> selected="selected"<? } ?>>Advanced</option>
|
<option value="<?= Enums\EbookPlaceholderDifficulty::Advanced->value ?>"<? if($ebook->EbookPlaceholder?->Difficulty == Enums\EbookPlaceholderDifficulty::Advanced){ ?> selected="selected"<? } ?>>Advanced</option>
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
@ -211,13 +212,13 @@ $ebook = $ebook ?? new Ebook();
|
||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
name="ebook-placeholder-transcription-url"
|
name="ebook-placeholder-transcription-url"
|
||||||
value="<?= Formatter::EscapeHtml($ebook?->EbookPlaceholder?->TranscriptionUrl) ?>"
|
value="<?= Formatter::EscapeHtml($ebook->EbookPlaceholder?->TranscriptionUrl) ?>"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Notes</span>
|
<span>Notes</span>
|
||||||
<span>Markdown accepted.</span>
|
<span>Markdown accepted.</span>
|
||||||
<textarea maxlength="1024" name="ebook-placeholder-notes"><?= Formatter::EscapeHtml($ebook?->EbookPlaceholder?->Notes) ?></textarea>
|
<textarea maxlength="1024" name="ebook-placeholder-notes"><?= Formatter::EscapeHtml($ebook->EbookPlaceholder?->Notes) ?></textarea>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
78
templates/ProjectForm.php
Normal file
78
templates/ProjectForm.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?
|
||||||
|
$project = $project ?? new Project();
|
||||||
|
$managers = User::GetAllByCanManageProjects();
|
||||||
|
$reviewers = User::GetAllByCanReviewProjects();
|
||||||
|
?>
|
||||||
|
<label class="icon user">
|
||||||
|
<span>Producer name</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="project-producer-name"
|
||||||
|
required="required"
|
||||||
|
value="<?= Formatter::EscapeHtml($project->ProducerName ?? '') ?>"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Producer Email</span>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="project-producer-email"
|
||||||
|
value="<?= Formatter::EscapeHtml($project->ProducerEmail) ?>"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="icon user">
|
||||||
|
<span>Manager</span>
|
||||||
|
<span>
|
||||||
|
<select name="project-manager-user-id">
|
||||||
|
<? foreach($managers as $manager){ ?>
|
||||||
|
<option value="<?= $manager->UserId ?>"<? if(isset($project->ManagerUserId) && $project->ManagerUserId == $manager->UserId){ ?> selected="selected"<? } ?>><?= Formatter::EscapeHtml($manager->Name) ?></option>
|
||||||
|
<? } ?>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="icon user">
|
||||||
|
<span>Reviewer</span>
|
||||||
|
<span>
|
||||||
|
<select name="project-reviewer-user-id">
|
||||||
|
<? foreach($reviewers as $reviewer){ ?>
|
||||||
|
<option value="<?= $reviewer->UserId ?>"<? if(isset($project->ReviewerUserId) && $project->ReviewerUserId == $reviewer->UserId){ ?> selected="selected"<? } ?>><?= Formatter::EscapeHtml($reviewer->Name) ?></option>
|
||||||
|
<? } ?>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="icon meter">
|
||||||
|
<span>Status</span>
|
||||||
|
<span>
|
||||||
|
<select name="project-status">
|
||||||
|
<option value="<?= Enums\ProjectStatusType::InProgress->value ?>"<? if($project->Status == Enums\ProjectStatusType::InProgress){?> selected="selected"<? } ?>>In progress</option>
|
||||||
|
<option value="<?= Enums\ProjectStatusType::Stalled->value ?>"<? if($project->Status == Enums\ProjectStatusType::Stalled){?> selected="selected"<? } ?>>Stalled</option>
|
||||||
|
<option value="<?= Enums\ProjectStatusType::Completed->value ?>"<? if($project->Status == Enums\ProjectStatusType::Completed){?> selected="selected"<? } ?>>Completed</option>
|
||||||
|
<option value="<?= Enums\ProjectStatusType::Abandoned->value ?>"<? if($project->Status == Enums\ProjectStatusType::Abandoned){?> selected="selected"<? } ?>>Abandoned</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>VCS URL</span>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
name="project-vcs-url"
|
||||||
|
required="required"
|
||||||
|
placeholder="https://github.com/"
|
||||||
|
pattern="^https:\/\/github\.com\/[^\/]+/[^\/]+/?$"
|
||||||
|
value="<?= Formatter::EscapeHtml($project->VcsUrl ?? '') ?>"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span>Discussion URL</span>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
name="project-discussion-url"
|
||||||
|
value="<?= Formatter::EscapeHtml($project->DiscussionUrl) ?>"
|
||||||
|
/>
|
||||||
|
</label>
|
|
@ -2516,6 +2516,14 @@ fieldset{
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label.controls-following-fieldset + fieldset{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.controls-following-fieldset:has(input[type="checkbox"]:checked) + fieldset{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
label.controls-following-fieldset + fieldset,
|
label.controls-following-fieldset + fieldset,
|
||||||
details summary ~ *{
|
details summary ~ *{
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
|
|
|
@ -73,12 +73,12 @@ form div.footer{
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide the next fieldset unless the ebook-placeholder-is-wanted checkbox is checked. */
|
/* Hide the next fieldset unless the ebook-placeholder-is-wanted checkbox is checked. */
|
||||||
form.create-update-ebook-placeholder fieldset:has(input[name="ebook-placeholder-is-wanted"]) fieldset{
|
form.create-update-ebook-placeholder label.controls-following-fieldset + fieldset{
|
||||||
display: none;
|
display: none;
|
||||||
grid-column: 1 / span 2;
|
grid-column: 1 / span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.create-update-ebook-placeholder fieldset:has(input[name="ebook-placeholder-is-wanted"]:checked) fieldset{
|
form.create-update-ebook-placeholder label.controls-following-fieldset:has(input[type="checkbox"]:checked) + fieldset{
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
www/css/project.css
Normal file
13
www/css/project.css
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.project-form{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-form label:has(input[type="url"]){
|
||||||
|
grid-column: 1 / span 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.project-form label:has(input[name="project-manager-user-id"]){
|
||||||
|
grid-column-start: 1;
|
||||||
|
}
|
|
@ -76,7 +76,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<section class="placeholder-details">
|
<section class="placeholder-details" id="details">
|
||||||
<? if($ebook->EbookPlaceholder->IsPublicDomain){ ?>
|
<? if($ebook->EbookPlaceholder->IsPublicDomain){ ?>
|
||||||
<? if($ebook->EbookPlaceholder->IsInProgress){ ?>
|
<? if($ebook->EbookPlaceholder->IsInProgress){ ?>
|
||||||
<p>We don’t have this ebook in our catalog yet, but someone is working on it now! We hope to have it available for you to read very soon.</p>
|
<p>We don’t have this ebook in our catalog yet, but someone is working on it now! We hope to have it available for you to read very soon.</p>
|
||||||
|
@ -87,7 +87,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<p><a href="/donate#sponsor-an-ebook">Sponsor this ebook</a> and we’ll get working on it immediately, so that you and everyone can read it for free forever. You can also choose to have your name inscribed in the ebook’s colophon.</p>
|
<p><a href="/donate#sponsor-an-ebook">Sponsor this ebook</a> and we’ll get working on it immediately, so that you and everyone can read it for free forever. You can also choose to have your name inscribed in the ebook’s colophon.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<? if($ebook->EbookPlaceholder->Difficulty == \Enums\EbookPlaceholderDifficulty::Beginner){ ?>
|
<? if($ebook->EbookPlaceholder->Difficulty == Enums\EbookPlaceholderDifficulty::Beginner){ ?>
|
||||||
<p><a href="/contribute#technical-contributors">Produce this ebook yourself</a> and your work will allow others to read it for free forever. <em>This book is a good choice to start with if you’ve never created an ebook for us before</em>—we’ll help you through the process!</p>
|
<p><a href="/contribute#technical-contributors">Produce this ebook yourself</a> and your work will allow others to read it for free forever. <em>This book is a good choice to start with if you’ve never created an ebook for us before</em>—we’ll help you through the process!</p>
|
||||||
<? }else{ ?>
|
<? }else{ ?>
|
||||||
<p>If you’ve created an ebook for us before, you can <a href="/contribute#technical-contributors">produce this ebook yourself</a> so that others can read it for free. Your name will be inscribed in the colophon as the ebook’s producer.</p>
|
<p>If you’ve created an ebook for us before, you can <a href="/contribute#technical-contributors">produce this ebook yourself</a> so that others can read it for free. Your name will be inscribed in the colophon as the ebook’s producer.</p>
|
||||||
|
@ -105,19 +105,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
|
|
||||||
<? if(Session::$User?->Benefits->CanEditEbooks){ ?>
|
<? if(Session::$User?->Benefits->CanEditEbooks){ ?>
|
||||||
<section id="metadata">
|
<section id="metadata">
|
||||||
<h2>Metadata</h2>
|
<?= Template::EbookMetadata(['ebook' => $ebook]) ?>
|
||||||
<table class="admin-table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Ebook ID:</td>
|
|
||||||
<td><?= $ebook->EbookId ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Identifier:</td>
|
|
||||||
<td><?= Formatter::EscapeHtml($ebook->Identifier) ?></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
</section>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -26,8 +26,11 @@ try{
|
||||||
// If the `EbookPlaceholder` we just added is part of a collection, prefill the form with the same data to make it easier to submit series.
|
// If the `EbookPlaceholder` we just added is part of a collection, prefill the form with the same data to make it easier to submit series.
|
||||||
unset($ebook->EbookId);
|
unset($ebook->EbookId);
|
||||||
unset($ebook->Title);
|
unset($ebook->Title);
|
||||||
|
unset($ebook->ProjectInProgress);
|
||||||
if($ebook->EbookPlaceholder !== null){
|
if($ebook->EbookPlaceholder !== null){
|
||||||
$ebook->EbookPlaceholder->YearPublished = null;
|
$ebook->EbookPlaceholder->YearPublished = null;
|
||||||
|
$ebook->EbookPlaceholder->IsWanted = false;
|
||||||
|
$ebook->EbookPlaceholder->IsInProgress = false;
|
||||||
}
|
}
|
||||||
foreach($ebook->CollectionMemberships as $collectionMembership){
|
foreach($ebook->CollectionMemberships as $collectionMembership){
|
||||||
$collectionMembership->SequenceNumber++;
|
$collectionMembership->SequenceNumber++;
|
||||||
|
@ -56,7 +59,7 @@ catch(Exceptions\InvalidPermissionsException){
|
||||||
<?= Template::Header(
|
<?= Template::Header(
|
||||||
[
|
[
|
||||||
'title' => 'Create an Ebook Placeholder',
|
'title' => 'Create an Ebook Placeholder',
|
||||||
'css' => ['/css/ebook-placeholder.css'],
|
'css' => ['/css/ebook-placeholder.css', '/css/project.css'],
|
||||||
'highlight' => '',
|
'highlight' => '',
|
||||||
'description' => 'Create a placeholder for an ebook not yet in the collection.'
|
'description' => 'Create a placeholder for an ebook not yet in the collection.'
|
||||||
]
|
]
|
||||||
|
|
|
@ -82,6 +82,16 @@ try{
|
||||||
// Pass and create the placeholder. There is no existing ebook with this identifier.
|
// Pass and create the placeholder. There is no existing ebook with this identifier.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do we have a `Project` to create at the same time?
|
||||||
|
$project = null;
|
||||||
|
if($ebookPlaceholder->IsInProgress){
|
||||||
|
$project = new Project();
|
||||||
|
$project->FillFromHttpPost();
|
||||||
|
$project->Started = NOW;
|
||||||
|
$project->EbookId = 0; // Dummy value to pass validation, we'll set it to the real value before creating the `Project`.
|
||||||
|
$project->Validate();
|
||||||
|
}
|
||||||
|
|
||||||
// These properties must be set before calling `Ebook::Create()` to prevent the getters from triggering DB queries or accessing `Ebook::$EbookId` before it is set.
|
// These properties must be set before calling `Ebook::Create()` to prevent the getters from triggering DB queries or accessing `Ebook::$EbookId` before it is set.
|
||||||
$ebook->Tags = [];
|
$ebook->Tags = [];
|
||||||
$ebook->LocSubjects = [];
|
$ebook->LocSubjects = [];
|
||||||
|
@ -89,6 +99,13 @@ try{
|
||||||
$ebook->Contributors = [];
|
$ebook->Contributors = [];
|
||||||
$ebook->Create();
|
$ebook->Create();
|
||||||
|
|
||||||
|
if($ebookPlaceholder->IsInProgress && $project !== null){
|
||||||
|
$project->EbookId = $ebook->EbookId;
|
||||||
|
$project->Ebook = $ebook;
|
||||||
|
$project->Create();
|
||||||
|
$ebook->ProjectInProgress = $project;
|
||||||
|
}
|
||||||
|
|
||||||
$_SESSION['ebook'] = $ebook;
|
$_SESSION['ebook'] = $ebook;
|
||||||
$_SESSION['is-ebook-placeholder-created'] = true;
|
$_SESSION['is-ebook-placeholder-created'] = true;
|
||||||
|
|
||||||
|
|
|
@ -399,19 +399,7 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
|
|
||||||
<? if(Session::$User?->Benefits->CanEditEbooks){ ?>
|
<? if(Session::$User?->Benefits->CanEditEbooks){ ?>
|
||||||
<section id="metadata">
|
<section id="metadata">
|
||||||
<h2>Metadata</h2>
|
<?= Template::EbookMetadata(['ebook' => $ebook]) ?>
|
||||||
<table class="admin-table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Ebook ID:</td>
|
|
||||||
<td><?= $ebook->EbookId ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Identifier:</td>
|
|
||||||
<td><?= Formatter::EscapeHtml($ebook->Identifier) ?></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
</section>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue