mirror of
https://github.com/standardebooks/web.git
synced 2025-07-04 22:00:35 -04:00
Allow VCS URLs to be null in projects
This commit is contained in:
parent
d902074285
commit
7a3c7ad503
12 changed files with 94 additions and 36 deletions
|
@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS `Projects` (
|
||||||
`ProducerName` varchar(151) NOT NULL DEFAULT '',
|
`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) DEFAULT NULL,
|
||||||
`Created` timestamp NOT NULL DEFAULT current_timestamp(),
|
`Created` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
`Updated` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
`Updated` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||||
`Started` datetime NOT NULL,
|
`Started` datetime NOT NULL,
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?
|
|
||||||
namespace Exceptions;
|
|
||||||
|
|
||||||
class VcsUrlRequiredException extends AppException{
|
|
||||||
/** @var string $message */
|
|
||||||
protected $message = 'VCS URL is required.';
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ use function Safe\curl_getinfo;
|
||||||
use function Safe\curl_init;
|
use function Safe\curl_init;
|
||||||
use function Safe\curl_setopt;
|
use function Safe\curl_setopt;
|
||||||
use function Safe\json_decode;
|
use function Safe\json_decode;
|
||||||
|
use function Safe\parse_url;
|
||||||
use function Safe\preg_match;
|
use function Safe\preg_match;
|
||||||
use function Safe\preg_match_all;
|
use function Safe\preg_match_all;
|
||||||
use function Safe\preg_replace;
|
use function Safe\preg_replace;
|
||||||
|
@ -17,6 +18,8 @@ use Safe\DateTimeImmutable;
|
||||||
* @property string $Url
|
* @property string $Url
|
||||||
* @property DateTimeImmutable $LastActivityTimestamp The timestamp of the latest activity, whether it's a commit, a discussion post, or simply the started timestamp.
|
* @property DateTimeImmutable $LastActivityTimestamp The timestamp of the latest activity, whether it's a commit, a discussion post, or simply the started timestamp.
|
||||||
* @property array<ProjectReminder> $Reminders
|
* @property array<ProjectReminder> $Reminders
|
||||||
|
* @property ?string $VcsUrlDomain
|
||||||
|
* @property ?string $DiscussionUrlDomain
|
||||||
*/
|
*/
|
||||||
class Project{
|
class Project{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
@ -28,7 +31,7 @@ class Project{
|
||||||
public string $ProducerName;
|
public string $ProducerName;
|
||||||
public ?string $ProducerEmail = null;
|
public ?string $ProducerEmail = null;
|
||||||
public ?string $DiscussionUrl = null;
|
public ?string $DiscussionUrl = null;
|
||||||
public string $VcsUrl;
|
public ?string $VcsUrl;
|
||||||
public DateTimeImmutable $Created;
|
public DateTimeImmutable $Created;
|
||||||
public DateTimeImmutable $Updated;
|
public DateTimeImmutable $Updated;
|
||||||
public DateTimeImmutable $Started;
|
public DateTimeImmutable $Started;
|
||||||
|
@ -46,12 +49,64 @@ class Project{
|
||||||
protected DateTimeImmutable $_LastActivityTimestamp;
|
protected DateTimeImmutable $_LastActivityTimestamp;
|
||||||
/** @var array<ProjectReminder> $_Reminders */
|
/** @var array<ProjectReminder> $_Reminders */
|
||||||
protected array $_Reminders;
|
protected array $_Reminders;
|
||||||
|
protected ?string $_VcsUrlDomain;
|
||||||
|
protected ?string $_DiscussionUrlDomain;
|
||||||
|
|
||||||
|
|
||||||
// *******
|
// *******
|
||||||
// GETTERS
|
// GETTERS
|
||||||
// *******
|
// *******
|
||||||
|
|
||||||
|
protected function GetVcsUrlDomain(): ?string{
|
||||||
|
if(!isset($this->_VcsUrlDomain)){
|
||||||
|
if($this->VcsUrl === null){
|
||||||
|
$this->_VcsUrlDomain = null;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
$domain = parse_url($this->VcsUrl, PHP_URL_HOST);
|
||||||
|
|
||||||
|
if(is_string($domain)){
|
||||||
|
$this->_VcsUrlDomain = strtolower($domain);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->_VcsUrlDomain = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(\Exception){
|
||||||
|
$this->_VcsUrlDomain = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_VcsUrlDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GetDiscussionUrlDomain(): ?string{
|
||||||
|
if(!isset($this->_DiscussionUrlDomain)){
|
||||||
|
if($this->DiscussionUrl === null){
|
||||||
|
$this->_DiscussionUrlDomain = null;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
$domain = parse_url($this->DiscussionUrl, PHP_URL_HOST);
|
||||||
|
|
||||||
|
if(is_string($domain)){
|
||||||
|
$this->_DiscussionUrlDomain = strtolower($domain);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->_DiscussionUrlDomain = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(\Exception){
|
||||||
|
$this->_DiscussionUrlDomain = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_DiscussionUrlDomain;
|
||||||
|
}
|
||||||
|
|
||||||
protected function GetUrl(): string{
|
protected function GetUrl(): string{
|
||||||
if(!isset($this->_Url)){
|
if(!isset($this->_Url)){
|
||||||
$this->_Url = '/projects/' . $this->ProjectId;
|
$this->_Url = '/projects/' . $this->ProjectId;
|
||||||
|
@ -158,12 +213,15 @@ class Project{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->VcsUrl = rtrim(trim($this->VcsUrl ?? ''), '/');
|
$this->VcsUrl = trim($this->VcsUrl ?? '');
|
||||||
if($this->VcsUrl == ''){
|
if($this->VcsUrl == ''){
|
||||||
$error->Add(new Exceptions\VcsUrlRequiredException());
|
$this->VcsUrl = null;
|
||||||
}
|
}
|
||||||
elseif(!preg_match('|^https://github.com/[^/]+/[^/]+|ius', $this->VcsUrl)){
|
elseif(preg_match('|^https?://(www\.)?github.com/|ius', $this->VcsUrl)){
|
||||||
$error->Add(new Exceptions\InvalidVcsUrlException());
|
$this->VcsUrl = rtrim($this->VcsUrl, '/');
|
||||||
|
if(!preg_match('|^https://github.com/[^/]+/[^/]+|ius', $this->VcsUrl)){
|
||||||
|
$error->Add(new Exceptions\InvalidVcsUrlException());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isset($this->ManagerUserId)){
|
if(!isset($this->ManagerUserId)){
|
||||||
|
@ -373,7 +431,7 @@ class Project{
|
||||||
* @throws Exceptions\AppException If the operation failed.
|
* @throws Exceptions\AppException If the operation failed.
|
||||||
*/
|
*/
|
||||||
public function FetchLatestCommitTimestamp(?string $apiKey = null): void{
|
public function FetchLatestCommitTimestamp(?string $apiKey = null): void{
|
||||||
if(!preg_match('|^https://github\.com/|iu', $this->VcsUrl)){
|
if(!preg_match('|^https://github\.com/|iu', $this->VcsUrl ?? '')){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
?>
|
?>
|
||||||
<table class="data-table bulk-downloads-table">
|
<table class="data-table bulk-downloads-table">
|
||||||
<caption aria-hidden="hidden">Scroll right →</caption>
|
<caption aria-hidden="true">Scroll right →</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="mid-header">
|
<tr class="mid-header">
|
||||||
<th scope="col"><?= Formatter::EscapeHtml($label) ?></th>
|
<th scope="col"><?= Formatter::EscapeHtml($label) ?></th>
|
||||||
|
|
|
@ -36,17 +36,19 @@
|
||||||
<a href="<?= SITE_URL ?><?= $project->Reviewer->Url ?>/projects"><?= Formatter::EscapeHtml($project->Reviewer->DisplayName) ?></a>
|
<a href="<?= SITE_URL ?><?= $project->Reviewer->Url ?>/projects"><?= Formatter::EscapeHtml($project->Reviewer->DisplayName) ?></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<? if($project->VcsUrl !== null){ ?>
|
||||||
<td>Repository:</td>
|
<tr>
|
||||||
<td>
|
<td>Repository:</td>
|
||||||
<a href="<?= Formatter::EscapeHtml($project->VcsUrl) ?>">GitHub</a>
|
<td>
|
||||||
</td>
|
<a href="<?= Formatter::EscapeHtml($project->VcsUrl) ?>"><?= Formatter::EscapeHtml($project->VcsUrlDomain) ?></a>
|
||||||
</tr>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<? } ?>
|
||||||
<? if($project->DiscussionUrl !== null){ ?>
|
<? if($project->DiscussionUrl !== null){ ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Discussion:</td>
|
<td>Discussion:</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="<?= Formatter::EscapeHtml($project->DiscussionUrl) ?>">Google Groups</a>
|
<a href="<?= Formatter::EscapeHtml($project->DiscussionUrl) ?>"><?= Formatter::EscapeHtml($project->DiscussionUrlDomain) ?></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
|
|
@ -16,10 +16,12 @@ You’ve been assigned a new ebook project to **<?= $role ?>**:
|
||||||
|
|
||||||
- Reviewer: [<?= Formatter::EscapeMarkdown($project->Reviewer->DisplayName) ?>](<?= Formatter::EscapeMarkdown(SITE_URL . $project->Reviewer->Url . '/projects') ?>)
|
- Reviewer: [<?= Formatter::EscapeMarkdown($project->Reviewer->DisplayName) ?>](<?= Formatter::EscapeMarkdown(SITE_URL . $project->Reviewer->Url . '/projects') ?>)
|
||||||
|
|
||||||
- Repository: [GitHub](<?= Formatter::EscapeMarkdown($project->VcsUrl) ?>)
|
<? if($project->VcsUrl !== null){ ?>
|
||||||
|
- Repository: [<?= Formatter::EscapeHtml($project->VcsUrlDomain) ?>](<?= Formatter::EscapeMarkdown($project->VcsUrl) ?>)
|
||||||
|
|
||||||
|
<? } ?>
|
||||||
<? if($project->DiscussionUrl !== null){ ?>
|
<? if($project->DiscussionUrl !== null){ ?>
|
||||||
- Discussion: [Google Groups](<?= Formatter::EscapeMarkdown($project->DiscussionUrl) ?>)
|
- Discussion: [<?= Formatter::EscapeHtml($project->DiscussionUrlDomain) ?>](<?= Formatter::EscapeMarkdown($project->DiscussionUrl) ?>)
|
||||||
|
|
||||||
<? } ?>
|
<? } ?>
|
||||||
If you’re unable to <?= $role ?> this ebook project, [email the Editor-in-Chief](mailto:<?= Formatter::EscapeMarkdown(EDITOR_IN_CHIEF_EMAIL_ADDRESS) ?>) and we’ll reassign it.
|
If you’re unable to <?= $role ?> this ebook project, [email the Editor-in-Chief](mailto:<?= Formatter::EscapeMarkdown(EDITOR_IN_CHIEF_EMAIL_ADDRESS) ?>) and we’ll reassign it.
|
||||||
|
|
|
@ -76,8 +76,6 @@ $areFieldsRequired = $areFieldsRequired ?? true;
|
||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
name="project-vcs-url"
|
name="project-vcs-url"
|
||||||
placeholder="https://github.com/..."
|
|
||||||
pattern="^https:\/\/github\.com\/[^\/]+/[^\/]+/?$"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
value="<?= Formatter::EscapeHtml($project->VcsUrl ?? '') ?>"
|
value="<?= Formatter::EscapeHtml($project->VcsUrl ?? '') ?>"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,7 +7,7 @@ $includeTitle = $includeTitle ?? true;
|
||||||
$includeStatus = $includeStatus ?? true;
|
$includeStatus = $includeStatus ?? true;
|
||||||
?>
|
?>
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<caption aria-hidden="hidden">Scroll right →</caption>
|
<caption aria-hidden="true">Scroll right →</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="mid-header">
|
<tr class="mid-header">
|
||||||
<? if($includeTitle){ ?>
|
<? if($includeTitle){ ?>
|
||||||
|
@ -20,8 +20,8 @@ $includeStatus = $includeStatus ?? true;
|
||||||
<? if($includeStatus){ ?>
|
<? if($includeStatus){ ?>
|
||||||
<th scope="col">Status</th>
|
<th scope="col">Status</th>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<th/>
|
<th></th>
|
||||||
<th/>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -56,7 +56,9 @@ $includeStatus = $includeStatus ?? true;
|
||||||
</td>
|
</td>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<td>
|
<td>
|
||||||
<a href="<?= Formatter::EscapeHtml($project->VcsUrl) ?>">Repository</a>
|
<? if($project->VcsUrl !== null){ ?>
|
||||||
|
<a href="<?= Formatter::EscapeHtml($project->VcsUrl) ?>">Repository</a>
|
||||||
|
<? } ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<? if($project->DiscussionUrl !== null){ ?>
|
<? if($project->DiscussionUrl !== null){ ?>
|
||||||
|
|
|
@ -37,7 +37,7 @@ $title = preg_replace('/s$/', '', ucfirst($class));
|
||||||
<p>These zip files contain each ebook in every format we offer, and are kept updated with the latest versions of each ebook. Read about <a href="/help/how-to-use-our-ebooks#which-file-to-download">which file format to download</a>.</p>
|
<p>These zip files contain each ebook in every format we offer, and are kept updated with the latest versions of each ebook. Read about <a href="/help/how-to-use-our-ebooks#which-file-to-download">which file format to download</a>.</p>
|
||||||
<? if($class == 'months'){ ?>
|
<? if($class == 'months'){ ?>
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<caption aria-hidden="hidden">Scroll right →</caption>
|
<caption aria-hidden="true">Scroll right →</caption>
|
||||||
<tbody>
|
<tbody>
|
||||||
<? foreach($collection as $year => $months){ ?>
|
<? foreach($collection as $year => $months){ ?>
|
||||||
<? $yearHeader = Formatter::EscapeHtml($year); ?>
|
<? $yearHeader = Formatter::EscapeHtml($year); ?>
|
||||||
|
|
|
@ -364,7 +364,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li id="i-still-have-questions">
|
<li id="i-still-have-questions">
|
||||||
<h2>I still have questions!</h2>
|
<h2>I still have questions!</h2>
|
||||||
<p>If you’re unsure about anything, or have a question that isn’t answered here, please ask on the <a href="https://groups.google.com/g/standardebooks">Google Groups mailing list.</a> The experienced producers there can answer any question you might have.</p>
|
<p>If you’re unsure about anything, or have a question that isn’t answered here, please ask on our <a href="https://groups.google.com/g/standardebooks">mailing list.</a> The experienced producers there can answer any question you might have.</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -72,10 +72,12 @@ catch(Exceptions\InvalidPermissionsException){
|
||||||
|
|
||||||
<?= Template::Error(['exception' => $exception]) ?>
|
<?= Template::Error(['exception' => $exception]) ?>
|
||||||
|
|
||||||
<? if($isOnlyProjectCreated){ ?>
|
<? if(isset($createdEbook)){ ?>
|
||||||
<p class="message success">An ebook placeholder <a href="<?= $createdEbook->Url ?>">already exists</a> for this ebook, but a a new project was created!</p>
|
<? if($isOnlyProjectCreated){ ?>
|
||||||
<? }elseif($isCreated && isset($createdEbook)){ ?>
|
<p class="message success">An ebook placeholder <a href="<?= $createdEbook->Url ?>">already exists</a> for this ebook, but a a new project was created!</p>
|
||||||
<p class="message success">Ebook placeholder created: <a href="<?= $createdEbook->Url ?>"><?= Formatter::EscapeHtml($createdEbook->Title) ?></a>!</p>
|
<? }elseif($isCreated){ ?>
|
||||||
|
<p class="message success">Ebook placeholder created: <a href="<?= $createdEbook->Url ?>"><?= Formatter::EscapeHtml($createdEbook->Title) ?></a>!</p>
|
||||||
|
<? } ?>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
|
|
||||||
<form class="create-update-ebook-placeholder" method="<?= Enums\HttpMethod::Post->value ?>" action="/ebook-placeholders" autocomplete="off">
|
<form class="create-update-ebook-placeholder" method="<?= Enums\HttpMethod::Post->value ?>" action="/ebook-placeholders" autocomplete="off">
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?
|
<?
|
||||||
|
use function Safe\session_unset;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
if(Session::$User === null){
|
if(Session::$User === null){
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue