diff --git a/config/sql/se/Benefits.sql b/config/sql/se/Benefits.sql index 2cd9161a..0837a98a 100644 --- a/config/sql/se/Benefits.sql +++ b/config/sql/se/Benefits.sql @@ -12,6 +12,7 @@ CREATE TABLE IF NOT EXISTS `Benefits` ( `CanCreateEbookPlaceholders` tinyint(1) unsigned NOT NULL DEFAULT 0, `CanManageProjects` tinyint(1) unsigned NOT NULL DEFAULT 0, `CanReviewProjects` tinyint(1) unsigned NOT NULL DEFAULT 0, + `CanEditProjects` tinyint(1) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`UserId`), KEY `idxBenefits` (`CanAccessFeeds`,`CanVote`,`CanBulkDownload`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/lib/Benefits.php b/lib/Benefits.php index b43a60fa..ca566a09 100644 --- a/lib/Benefits.php +++ b/lib/Benefits.php @@ -18,6 +18,9 @@ class Benefits{ public bool $CanEditCollections = false; public bool $CanEditEbooks = false; public bool $CanCreateEbookPlaceholders = false; + public bool $CanManageProjects = false; + public bool $CanReviewProjects = false; + public bool $CanEditProjects = false; protected bool $_HasBenefits; @@ -36,6 +39,12 @@ class Benefits{ $this->CanEditEbooks || $this->CanCreateEbookPlaceholders + || + $this->CanManageProjects + || + $this->CanReviewProjects + || + $this->CanEditProjects ){ return true; } diff --git a/lib/Ebook.php b/lib/Ebook.php index 7be5f8e4..308139c0 100644 --- a/lib/Ebook.php +++ b/lib/Ebook.php @@ -1028,6 +1028,17 @@ class Ebook{ return $ebook; } + /** + * @throws Exceptions\EbookNotFoundException If the `Ebook` can't be found. + */ + public static function Get(?int $ebookId): Ebook{ + if($ebookId === null){ + throw new Exceptions\EbookNotFoundException(); + } + + return Db::Query('SELECT * from Ebooks where EbookId = ?', [$ebookId], Ebook::class)[0] ?? throw new Exceptions\EbookNotFoundException(); + } + /** * Joins the `Name` properites of `Contributor` objects as a URL slug, e.g., * diff --git a/lib/Enums/DateTimeFormat.php b/lib/Enums/DateTimeFormat.php index 000fb314..d378690e 100644 --- a/lib/Enums/DateTimeFormat.php +++ b/lib/Enums/DateTimeFormat.php @@ -22,4 +22,7 @@ enum DateTimeFormat: string{ /** Like `1641426132`. */ case UnixTimestamp = 'U'; + + /** Like Jan 5, 2024 */ + case ShortDate = 'M j, Y'; } diff --git a/lib/Exceptions/ProjectNotFoundException.php b/lib/Exceptions/ProjectNotFoundException.php new file mode 100644 index 00000000..48dd4a3e --- /dev/null +++ b/lib/Exceptions/ProjectNotFoundException.php @@ -0,0 +1,7 @@ +UpdatedString = preg_replace('/^(.+?)(?UpdatedString); if($obj->Updated->format('Y') != NOW->format('Y')){ - $obj->UpdatedString = $obj->Updated->format('M j, Y'); + $obj->UpdatedString = $obj->Updated->format(Enums\DateTimeFormat::ShortDate->value); } // Sort the downloads by filename extension diff --git a/lib/Project.php b/lib/Project.php index 272fe5f5..bc7eda8e 100644 --- a/lib/Project.php +++ b/lib/Project.php @@ -31,6 +31,11 @@ class Project{ protected User $_ReviewerUser; protected string $_Url; + + // ******* + // GETTERS + // ******* + protected function GetUrl(): string{ if(!isset($this->_Url)){ $this->_Url = '/projects/' . $this->ProjectId; @@ -39,6 +44,11 @@ class Project{ return $this->_Url; } + + // ******* + // METHODS + // ******* + /** * @throws Exceptions\InvalidProjectException If the `Project` is invalid. */ @@ -218,4 +228,27 @@ class Project{ $this->PropertyFromHttp('ManagerUserId'); $this->PropertyFromHttp('ReviewerUserId'); } + + + // *********** + // ORM METHODS + // *********** + + /** + * @throws Exceptions\ProjectNotFoundException If the `Project` can't be found. + */ + public static function Get(?int $projectId): Project{ + if($projectId === null){ + throw new Exceptions\ProjectNotFoundException(); + } + + return Db::Query('SELECT * from Projects where ProjectId = ?', [$projectId], Project::class)[0] ?? throw new Exceptions\ProjectNotFoundException(); + } + + /** + * @return array + */ + public static function GetAllByStatus(Enums\ProjectStatusType $status): array{ + return Db::Query('SELECT * from Projects where Status = ? order by Started desc', [$status], Project::class); + } } diff --git a/templates/BulkDownloadTable.php b/templates/BulkDownloadTable.php index 18f37685..5de36427 100644 --- a/templates/BulkDownloadTable.php +++ b/templates/BulkDownloadTable.php @@ -4,7 +4,7 @@ * @var array $collections */ ?> - +
diff --git a/templates/EbookMetadata.php b/templates/EbookMetadata.php index 3f10bba3..a02e11b5 100644 --- a/templates/EbookMetadata.php +++ b/templates/EbookMetadata.php @@ -10,10 +10,6 @@ - - - - IsPlaceholder() && $ebook->EbookPlaceholder !== null){ ?> @@ -27,24 +23,13 @@ - - - - Projects) > 0){ ?> - - - +
Scroll right →
Ebook ID: EbookId ?>
Identifier:Identifier) ?>
Is wanted:
Difficulty:EbookPlaceholder->Difficulty->value) ?>
Projects: -
    - Projects as $project){ ?> -
  • -

    - Started->format(Enums\DateTimeFormat::FullDateTime->value) ?> — Status->GetDisplayName() ?> — ProducerEmail !== null){ ?>ProducerName) ?>ProducerName) ?>Link -

    -
  • - -
-
EbookPlaceholder->Difficulty->value ?? '') ?>
+ +Projects) > 0){ ?> +

Projects

+ $ebook->Projects, 'includeTitle' => false]) ?> + diff --git a/templates/ProjectsTable.php b/templates/ProjectsTable.php new file mode 100644 index 00000000..0e323b1f --- /dev/null +++ b/templates/ProjectsTable.php @@ -0,0 +1,48 @@ + $projects + */ + +$includeTitle = $includeTitle ?? true; +?> + + + + + + + + + + + + + + + + + + + + + + + + + +
Scroll right →
TitleStartedProducerStatus +
+ Ebook->Title) ?> + + Started->format(Enums\DateTimeFormat::ShortDate->value) ?> + + ProducerEmail !== null){ ?> + ProducerName) ?> + + ProducerName) ?> + + + Status->GetDisplayName()) ?> + + GitHub repo +
diff --git a/www/bulk-downloads/collection.php b/www/bulk-downloads/collection.php index f651e5ea..fa6559ae 100644 --- a/www/bulk-downloads/collection.php +++ b/www/bulk-downloads/collection.php @@ -36,7 +36,7 @@ $title = preg_replace('/s$/', '', ucfirst($class));

These zip files contain each ebook in every format we offer, and are kept updated with the latest versions of each ebook. Read about which file format to download.

- +
$months){ ?> diff --git a/www/css/core.css b/www/css/core.css index fa39d4fc..7bfa3a93 100644 --- a/www/css/core.css +++ b/www/css/core.css @@ -731,90 +731,93 @@ ul.message.error > li + li{ text-align: center; } -.download-list{ +.data-table{ margin: auto; } -.download-list caption{ +.data-table caption{ font-style: italic; text-align: right; display: none; } -.download-list .mid-header{ +.data-table .mid-header{ font-style: italic; } -.download-list thead tr.mid-header:first-child > *{ +.data-table thead tr.mid-header:first-child > *{ padding-top: 2rem; } -.download-list th.row-header, -.download-list .mid-header th:first-child, -.download-list .mid-header th:last-child{ - text-align: left; -} - -.download-list td, -.download-list th{ +.data-table td, +.data-table th{ padding: .25rem .5rem; hyphens: none; white-space: nowrap; } -.download-list th{ +.data-table th{ font-weight: normal; +} + +.data-table.bulk-downloads-table th.row-header, +.data-table.bulk-downloads-table .mid-header th:first-child, +.data-table.bulk-downloads-table .mid-header th:last-child{ + text-align: left; +} + +.data-table.bulk-downloads-table th{ text-align: right; } -.download-list .number{ +.data-table .number{ text-align: right; } -.download-list td.download{ +.data-table td.download{ padding-right: 0; color: var(--body-text); } -.download-list td.download + td{ +.data-table td.download + td{ padding-left: .25rem; font-size: .75em; color: var(--sub-text); } -.download-list tbody .row-header{ +.data-table tbody .row-header{ font-weight: bold; text-align: left; white-space: normal; } -.download-list tbody tr td, -.download-list tbody tr th{ +.data-table tbody tr td, +.data-table tbody tr th{ border-top: 1px dashed var(--table-border); } -.download-list tbody tr:first-child > *, -.download-list tbody tr.year-header > *, -.download-list tbody tr.year-header + tr > *, -.download-list tbody tr.mid-header tr > *, -.download-list tbody tr.mid-header + tr td, -.download-list tbody tr.mid-header + tr th{ +.data-table tbody tr:first-child > *, +.data-table tbody tr.year-header > *, +.data-table tbody tr.year-header + tr > *, +.data-table tbody tr.mid-header tr > *, +.data-table tbody tr.mid-header + tr td, +.data-table tbody tr.mid-header + tr th{ border: none; } -.download-list tbody tr:not([class]):hover > *{ +.data-table tbody tr:not([class]):hover > *{ background: var(--table-row-hover); } -.download-list tbody tr:only-child:not([class]):hover > *{ +.data-table tbody tr:only-child:not([class]):hover > *{ background: unset; /* Don't highlight on hover if there's only one row */ } -h2 + .download-list tr.year-header:first-child th{ +h2 + .data-table tr.year-header:first-child th{ padding-top: 2rem; } -.download-list .year-header th{ +.data-table .year-header th{ padding-top: 4rem; font-size: 1.4rem; font-family: "League Spartan", Arial, sans-serif; @@ -3327,23 +3330,23 @@ table.admin-table td + td{ } @media(max-width: 1200px){ - .download-list{ + .data-table{ overflow-x: scroll; display: block; /* needed to make overflow work */ width: 100%; } - .download-list .year-header th{ + .data-table .year-header th{ text-align: left; } - .download-list thead tr.mid-header:first-child th, - .download-list tr.year-header:first-child th{ + .data-table thead tr.mid-header:first-child th, + .data-table tr.year-header:first-child th{ padding-top: 0; } - .download-list caption{ + .data-table caption{ display: block; padding-top: 2rem; } diff --git a/www/css/project.css b/www/css/project.css index f80fe82b..460df40f 100644 --- a/www/css/project.css +++ b/www/css/project.css @@ -11,3 +11,16 @@ .project-form label:has(input[name="project-manager-user-id"]){ grid-column-start: 1; } + +h2 + table.projects{ + margin-top: 2rem; +} + +table.projects thead{ + font-style: italic; +} + +table.data-table .status, +table.data-table .producer{ + white-space: nowrap; +} diff --git a/www/ebooks/get.php b/www/ebooks/get.php index 431faf3e..1851b723 100644 --- a/www/ebooks/get.php +++ b/www/ebooks/get.php @@ -283,7 +283,7 @@ catch(Exceptions\EbookNotFoundException){
    GitCommits as $commit){ ?>
  1. - +

    Message) ?>

    diff --git a/www/projects/index.php b/www/projects/index.php new file mode 100644 index 00000000..14bbdae3 --- /dev/null +++ b/www/projects/index.php @@ -0,0 +1,39 @@ +Benefits->CanEditProjects){ + throw new Exceptions\InvalidPermissionsException(); + } + + $inProgressProjects = Project::GetAllByStatus(Enums\ProjectStatusType::InProgress); + $stalledProjects = Project::GetAllByStatus(Enums\ProjectStatusType::Stalled); +} +catch(Exceptions\LoginRequiredException){ + Template::RedirectToLogin(); +} +catch(Exceptions\InvalidPermissionsException){ + Template::Emit403(); +} +?> 'Projects', 'css' => ['/css/project.css'], 'description' => 'Ebook projects currently underway at Standard Ebooks.']) ?> +
    +
    +

    Projects

    +

    Active projects

    + +

    + None. +

    + + $inProgressProjects]) ?> + + + 0){ ?> +

    Stalled projects

    + $stalledProjects]) ?> + +
    +
    +
Scroll right →