Commit: 7971068c37a43fdd24f37542682729ab8bc6e7dd
Author: Sara Golemon <[email protected]> Wed, 17 Jun 2020 20:19:30
+0000
Parents: 0df7236ff958b62aae073a66adc9fa25b8f3096a
Branches: master
Link:
http://git.php.net/?p=web/php.git;a=commitdiff;h=7971068c37a43fdd24f37542682729ab8bc6e7dd
Log:
Refactor createNewsEntry for non-interactive use
Add bin/createReleaseEntry script for common release announcements
Changed paths:
M bin/createNewsEntry
A bin/createReleaseEntry
A include/news_entry.inc
diff --git a/bin/createNewsEntry b/bin/createNewsEntry
index c83b18471..bf843cd08 100755
--- a/bin/createNewsEntry
+++ b/bin/createNewsEntry
@@ -2,20 +2,8 @@
<?php
PHP_SAPI == 'cli' or die("Please run this script using the cli sapi");
-// Script config
-const BASE = "https://www.php.net";
-const PHPWEB = __DIR__ . '/../';
-const ARCHIVE_FILE_REL = 'archive/archive.xml';
-const ARCHIVE_FILE_ABS = PHPWEB . ARCHIVE_FILE_REL;
-const ARCHIVE_ENTRIES_REL = 'archive/entries/';
-const ARCHIVE_ENTRIES_ABS = PHPWEB . ARCHIVE_ENTRIES_REL;
-
-$categories = [
- 'frontpage' => 'PHP.net frontpage news',
- 'releases' => 'New PHP release',
- 'conferences' => 'Conference announcement',
- 'cfp' => 'Call for Papers',
-];
+require(__DIR__ . '/../include/news_entry.inc');
+use phpweb\news\Entry;
$imageRestriction = [
'width' => 400,
@@ -23,119 +11,38 @@ $imageRestriction = [
];
// Create an entry!
-if (!file_exists(ARCHIVE_FILE_ABS)) {
- fwrite(STDERR, "Can't find " . ARCHIVE_FILE_REL . ", are you sure you
are in phpweb/?\n");
+if (!file_exists(Entry::ARCHIVE_FILE_ABS)) {
+ fwrite(STDERR, "Can't find " . Entry::ARCHIVE_FILE_REL . ", are you
sure you are in phpweb/?\n");
exit(1);
}
-$id = getNextId();
-createNewsEntry($id);
-updateArchiveXML($id, ARCHIVE_FILE_ABS);
-
-fwrite(STDOUT, "File saved.\nPlease git diff " . ARCHIVE_FILE_REL . " and
sanity-check the changes before committing\n");
-fwrite(STDOUT, "NOTE: Remeber to git add " . ARCHIVE_ENTRIES_REL . $id . ".xml
!!\n");
-
-// Implementation functions
-
-function getNextId(): string {
- $filename = date("Y-m-d", $_SERVER["REQUEST_TIME"]);
- $count = 0;
- do {
- ++$count;
- $id = $filename . "-" . $count;
- $basename = "{$id}.xml";
- fprintf(STDOUT, "Trying $basename\n");
- } while (file_exists(ARCHIVE_ENTRIES_ABS . $basename));
-
- return $id;
+if ($_SERVER['argc'] > 1) {
+ // getopt based non-interactive mode
+ $entry = parseOptions();
+} else {
+ // Classic interactive prompts
+ $entry = getEntry();
}
-function createNewsEntry(string $id): void {
- global $categories;
+$entry->save()->updateArchiveXML();
- $entry = getEntry($id);
-
- // Create the XML document.
- $dom = new DOMDocument("1.0", "utf-8");
- $dom->formatOutput = true;
- $dom->preserveWhiteSpace = false;
- $item = $dom->createElementNs("http://www.w3.org/2005/Atom", "entry");
-
- ce($dom, "title", $entry['title'], [], $item);
- ce($dom, "id", $entry['archive'], array(), $item);
- ce($dom, "published", date(DATE_ATOM), [], $item);
- ce($dom, "updated", date(DATE_ATOM), [], $item);
- ce($dom, "link", null, ['href' => "{$entry['href']}#id$id", "rel" =>
"alternate", "type" => "text/html"], $item);
- ce($dom, "link", null, ['href' => $entry['via'], 'rel' => 'via',
'type' => 'text/html'], $item);
-
- if (isset($entry['conf-time'])) {
-
$item->appendChild($dom->createElementNs("http://php.net/ns/news",
"finalTeaserDate", date("Y-m-d", $entry['conf-time'])));
- }
-
- foreach ($entry['categories'] as $cat) {
- ce($dom, "category", null, ['term' => $cat, "label" =>
$categories[$cat]], $item);
- }
+fwrite(STDOUT, "File saved.\nPlease git diff " . Entry::ARCHIVE_FILE_REL . "
and sanity-check the changes before committing\n");
+fwrite(STDOUT, "NOTE: Remeber to git add " . Entry::ARCHIVE_ENTRIES_REL .
$entry->getId() . ".xml !!\n");
- if ($entry['image'] ?? false) {
- $image =
$item->appendChild($dom->createElementNs("http://php.net/ns/news", "newsImage",
$entry['image']['path']));
- $image->setAttribute("link", $entry['image']['link']);
- $image->setAttribute("title", $entry['image']['title']);
- }
-
- $content = ce($dom, "content", null, [], $item);
-
- // Slurp content into our DOM.
- $tdoc = new DOMDocument("1.0", "utf-8");
- $tdoc->formatOutput = true;
- if ($tdoc->loadXML("<div>{$entry['content']} </div>")) {
- $content->setAttribute("type", "xhtml");
- $div = $content->appendChild($dom->createElement("div"));
- $div->setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
- foreach($tdoc->firstChild->childNodes as $node) {
- $div->appendChild($dom->importNode($node, true));
- }
- } else {
- fwrite(STDERR, "There is something wrong with your xhtml,
falling back to html");
- $content->setAttribute("type", "html");
- $content->nodeValue = $entry['content'];
- }
-
- $dom->appendChild($item);
- $dom->save(ARCHIVE_ENTRIES_ABS . $id . ".xml");
-}
+// Implementation functions
-function ce(DOMDocument $d, string $name, $value, array $attrs = [], ?DOMNode
$to = null) {
- if ($value) {
- $n = $d->createElement($name, $value);
- } else {
- $n = $d->createElement($name);
+function getEntry(): Entry {
+ $entry = new Entry;
+ $entry->setTitle(getTitle());
+ $entry->setCategories(selectCategories());
+ if ($entry->isConference()) {
+ $entry->setConfTime(getConfTime());
}
- foreach($attrs as $k => $v) {
- $n->setAttribute($k, $v);
- }
- if ($to) {
- return $to->appendChild($n);
- }
- return $n;
-}
-function getEntry(string $id): array {
- $entry = [];
+ $image = getImage();
+ $entry->setImage($image['path'] ?? '', $image['title'] ?? '',
$image['link'] ?? '');
- $entry['title'] = getTitle();
-
- $entry['categories'] = selectCategories();
- if (array_intersect($entry['categories'], ['cfp', 'conferences'])) {
- $entry['href'] = BASE . '/conferences/index.php';
- $entry['conf-time'] = getConfTime();
- } else {
- $entry['href'] = BASE . '/index.php';
- }
-
- $entry['image'] = getImage();
- $entry['content'] = getContent();
- $entry['archive'] = BASE . "/archive/" . date('Y',
$_SERVER['REQUEST_TIME']) . ".php#$id";
- $entry['via'] = $entry['image']['link'] ?? $entry['archive'];
+ $entry->setContent(getContent());
return $entry;
}
@@ -150,12 +57,11 @@ function getTitle(): string {
}
function selectCategories(): array { for(;;) {
- global $categories;
- $ids = array_keys($categories);
+ $ids = array_keys(Entry::CATEGORIES);
fwrite(STDOUT, "Categories:\n");
foreach($ids as $n => $id) {
- fprintf(STDOUT, "\t%d: %-11s\t [%s]\n", $n, $categories[$id],
$id);
+ fprintf(STDOUT, "\t%d: %-11s\t [%s]\n", $n,
Entry::CATEGORIES[$id], $id);
}
fwrite(STDOUT, "Please select appropriate categories, seperated with
space: ");
@@ -166,8 +72,8 @@ function selectCategories(): array { for(;;) {
},
array_filter(
explode(" ", rtrim(fgets(STDIN))),
- function ($c) use ($categories) {
- return is_numeric($c) && ($c >= 0) && ($c <
count($categories));
+ function ($c) {
+ return is_numeric($c) && ($c >= 0) && ($c <
count(Entry::CATEGORIES));
})
);
@@ -212,7 +118,7 @@ function getImage(): ?array {
fwrite(STDOUT, "Enter the image name (note: the image has to
exist in './images/news'): ");
$path = basename(rtrim(fgets(STDIN)));
- if (true === file_exists(PHPWEB . "/images/news/$path")) {
+ if (true === file_exists(Entry::PHPWEB . "/images/news/$path"))
{
$isValidImage = true;
if (extension_loaded('gd')) {
@@ -267,3 +173,83 @@ function updateArchiveXML(string $id, string
$archiveFile): void {
$arch->documentElement->insertBefore($first, $second);
$arch->save($archiveFile);
}
+
+function parseOptions(): Entry {
+ $opts = getopt('h', [
+ 'help',
+ 'title:',
+ 'category:',
+ 'conf-time:',
+ 'image-path:',
+ 'image-title:',
+ 'image-link:',
+ 'content:',
+ 'content-file:',
+ ]);
+ if (isset($opts['h']) || isset($opts['help'])) {
+ echo "Usage: {$_SERVER['argv'][0]} --title 'Name of event'
--category cfp ( --content 'text' | --content-file '-') [...options]\n\n";
+ echo " --title 'value' The title of the entry
(required)\n";
+ echo " --category 'value' 'frontpage', 'release', 'cfp',
or 'conference' (required; may repeat)\n";
+ echo " --conf-time 'value' When the event will be occurign
(cfp and conference categories only)\n";
+ echo " --content 'value' Text content for the entry, may
include XHTML\n";
+ echo " --content-file 'value' Name of file to load content
from, may not be specified with --content\n";
+ echo " --image-path 'value' Basename of image file in " .
Entry::IMAGE_PATH_REL . "\n";
+ echo " --image-title 'value' Title for the image provided\n";
+ echo " --image-link 'value' URI to direct to when clicking
the image\n";
+ exit(0);
+ }
+
+ $entry = new Entry;
+ if (!isset($opts['title'])) {
+ fwrite(STDERR, "--title required\n");
+ exit(1);
+ }
+ $entry->setTitle($opts['title']);
+
+ if (empty($opts['category'])) {
+ fwrite(STDERR, "--category required\n");
+ exit(1);
+ }
+ if (is_string($opts['category'])) {
+ $opts['category'] = [ $opts['category'] ];
+ }
+ foreach ($opts['category'] as $cat) {
+ $entry->addCategory($cat);
+ }
+ if ($entry->isConference()) {
+ if (empty($opts['conf-time'])) {
+ fwrite(STDERR, "--conf-time required for
conferences\n");
+ exit(1);
+ }
+ $t = strtotime($opts['conf-time']);
+ if (!is_int($t)) {
+ fwrite(STDERR, "Error parsing --conf-time\n");
+ exit(1);
+ }
+ $entry->setConfTime($t);
+ } elseif (!empty($opts['conf-time'])) {
+ fwrite(STDERR, "--conf-time not allowed with non-conference
events\n");
+ exit(1);
+ }
+
+ $entry->setImage($opts['image-path'] ?? '', $opts['image-title'] ?? '',
$opts['image-link'] ?? '');
+
+ if (isset($opts['content'])) {
+ if (isset($opts['content-file'])) {
+ fwrite(STDERR, "--content and --content-file may not be
specified together\n");
+ exit(1);
+ }
+ $entry->setContent($opts['content']);
+ } elseif (isset($opts['content-file'])) {
+ if ($opts['content-file'] === '-') {
+ $entry->setContent(stream_get_contents(STDIN));
+ } else {
+
$entry->setContent(file_get_contents($opts['content-file']));
+ }
+ } else {
+ fwrite(STDERR, "--content or --content-file required\n");
+ exit(1);
+ }
+
+ return $entry;
+}
diff --git a/bin/createReleaseEntry b/bin/createReleaseEntry
new file mode 100755
index 000000000..732510486
--- /dev/null
+++ b/bin/createReleaseEntry
@@ -0,0 +1,48 @@
+#!/usr/bin/env php
+<?php
+PHP_SAPI == 'cli' or die("Please run this script using the cli sapi");
+
+require(__DIR__ . '/../include/news_entry.inc');
+use phpweb\news\Entry;
+
+if (!file_exists(Entry::ARCHIVE_FILE_ABS)) {
+ fwrite(STDERR, "Can't find " . Entry::ARCHIVE_FILE_REL . ", are you
sure you are in phpweb/?\n");
+ exit(1);
+}
+
+$opts = getopt('v:',['security']);
+if (!isset($opts['v'])) {
+ echo "Usage: {$_SERVER['argv'][0]} -v 8.0.8 [ --security ]\n";
+ exit(0);
+}
+$version = $opts['v'];
+if (!preg_match('/^(\d+)\.(\d+)\.\d+?$/', $version, $matches)) {
+ fwrite(STDERR, "Unable to parse version identifier\n");
+ exit(1);
+}
+$major = $matches[1];
+$branch = "{$major}.{$matches[2]}";
+$security = isset($opts['security']) ? 'security' : 'bug fix';
+
+// Create content.
+$template = <<<EOD
+<p>The PHP development team announces the immediate availability of PHP
$version. This is a $security release.</p>
+
+<p>All PHP $branch users are encouraged to upgrade to this version.</p>
+
+<p>For source downloads of PHP $version please visit our <a
href="https://www.php.net/downloads.php">downloads page</a>,
+Windows source and binaries can be found on <a
href="https://windows.php.net/download/">windows.php.net/download/</a>.
+The list of changes is recorded in the <a
href="https://www.php.net/ChangeLog-{$major}.php#{$version}">ChangeLog</a>.
+</p>
+EOD;
+
+$entry = (new Entry)
+ ->setTitle("PHP $version Released!")
+ ->setCategories(['releases','frontpage'])
+ ->setContent($template);
+
+$entry->save()->updateArchiveXML();
+
+fwrite(STDOUT, "File saved.\nPlease git diff " . Entry::ARCHIVE_FILE_REL . "
and sanity-check the changes before committing\n");
+fwrite(STDOUT, "NOTE: Remeber to git add " . Entry::ARCHIVE_ENTRIES_REL .
$entry->getId() . ".xml !!\n");
+
diff --git a/include/news_entry.inc b/include/news_entry.inc
new file mode 100755
index 000000000..db9c7bdc5
--- /dev/null
+++ b/include/news_entry.inc
@@ -0,0 +1,210 @@
+<?php
+
+namespace phpweb\news;
+
+class Entry {
+ const CATEGORIES = [
+ 'frontpage' => 'PHP.net frontpage news',
+ 'releases' => 'New PHP release',
+ 'conferences' => 'Conference announcement',
+ 'cfp' => 'Call for Papers',
+ ];
+
+ const WEBROOT = "https://www.php.net";
+ const PHPWEB = __DIR__ . '/../';
+ const ARCHIVE_FILE_REL = 'archive/archive.xml';
+ const ARCHIVE_FILE_ABS = self::PHPWEB . self::ARCHIVE_FILE_REL;
+ const ARCHIVE_ENTRIES_REL = 'archive/entries/';
+ const ARCHIVE_ENTRIES_ABS = self::PHPWEB . self::ARCHIVE_ENTRIES_REL;
+ const IMAGE_PATH_REL = 'images/news/';
+ const IMAGE_PATH_ABS = self::PHPWEB . self::IMAGE_PATH_REL;
+
+
+ protected $title = '';
+ public function getTitle(): string {
+ return $this->title;
+ }
+
+ public function setTitle(string $title): self {
+ $this->title = $title;
+ return $this;
+ }
+
+ protected $categories = [];
+ public function setCategories(array $cats): self {
+ foreach ($cats as $cat) {
+ if (!isset(self::CATEGORIES[$cat])) {
+ throw new \Exception("Unknown category: $cat");
+ }
+ }
+ $this->categories = $cats;
+ return $this;
+ }
+ public function addCategory(string $cat): self {
+ if (!isset(self::CATEGORIES[$cat])) {
+ throw new \Exception("Unknown category: $cat");
+ }
+ if (!in_array($cat, $this->categories)) {
+ $this->categories[] = $cat;
+ }
+ return $this;
+ }
+ public function getCategories(): array {
+ return $this->categories;
+ }
+ public function isConference(): bool {
+ return (bool)array_intersect($this->categories, ['cfp',
'conferences']);
+ }
+
+ protected $conf_time = 0;
+ public function setConfTime(int $time): self {
+ $this->conf_time = $time;
+ return $this;
+ }
+ public function getConfTime(): int {
+ return $this->conf_time;
+ }
+
+ protected $image = [];
+ public function setImage(string $path, string $title, ?string $link):
self {
+ if (basename($path) !== $path) {
+ throw new \Exception('path must be a simple file name
under ' . self::IMAGE_PATH_REL);
+ }
+ if (!file_exists(self::IMAGE_PATH_ABS . $path)) {
+ throw new \Exception('Image not found at web-php/' .
self::IMAGE_PATH_REL . $path);
+ }
+ $this->image = [
+ 'path' => $path,
+ 'title' => $title,
+ 'link' => $link,
+ ];
+ return $this;
+ }
+ public function getImage(): array {
+ return $this->image;
+ }
+
+ protected $content = '';
+ public function setContent(string $content): self {
+ if (empty($content)) {
+ throw new \Exception('Content must not be empty');
+ }
+ $this->content = $content;
+ return $this;
+ }
+ public function getContent(): string {
+ return $this->content;
+ }
+
+ protected $id = '';
+ private static function selectNextId(): string {
+ $filename = date("Y-m-d", $_SERVER["REQUEST_TIME"]);
+ $count = 0;
+ do {
+ ++$count;
+ $id = $filename . "-" . $count;
+ $basename = "{$id}.xml";
+ } while (file_exists(self::ARCHIVE_ENTRIES_ABS . $basename));
+
+ return $id;
+ }
+ public function getId(): string {
+ return $this->id;
+ }
+
+ public function save(): self {
+ if (empty($this->id)) {
+ $this->id = self::selectNextId();
+ }
+
+ // Create the XML document.
+ $dom = new \DOMDocument("1.0", "utf-8");
+ $dom->formatOutput = true;
+ $dom->preserveWhiteSpace = false;
+ $item = $dom->createElementNs("http://www.w3.org/2005/Atom",
"entry");
+
+ $href = self::WEBROOT . ($this->isConference() ?
'/conferences/index.php' : '/index.php');
+ $archive = self::WEBROOT . "/archive/" . date('Y',
$_SERVER['REQUEST_TIME']) . ".php#{$this->id}";
+ $link = ($this->image['link'] ?? null) ?: $archive;
+
+ self::ce($dom, "title", $this->title, [], $item);
+ self::ce($dom, "id", $archive, [], $item);
+ self::ce($dom, "published", date(DATE_ATOM), [], $item);
+ self::ce($dom, "updated", date(DATE_ATOM), [], $item);
+ self::ce($dom, "link", null, ['href' =>
"{$href}#id{$this->id}", "rel" => "alternate", "type" => "text/html"], $item);
+ self::ce($dom, "link", null, ['href' => $link, 'rel' => 'via',
'type' => 'text/html'], $item);
+
+ if (!empty($this->conf_time)) {
+
$item->appendChild($dom->createElementNs("http://php.net/ns/news",
"finalTeaserDate", date("Y-m-d", $this->conf_time)));
+ }
+
+ foreach ($this->categories as $cat) {
+ self::ce($dom, "category", null, ['term' => $cat,
"label" => self::CATEGORIES[$cat]], $item);
+ }
+
+ if ($this->image['path'] ?? '') {
+ $image =
$item->appendChild($dom->createElementNs("http://php.net/ns/news", "newsImage",
$this->image['path']));
+ $image->setAttribute("link", $this->image['link']);
+ $image->setAttribute("title", $this->image['title']);
+ }
+
+ $content = self::ce($dom, "content", null, [], $item);
+
+ // Slurp content into our DOM.
+ $tdoc = new \DOMDocument("1.0", "utf-8");
+ $tdoc->formatOutput = true;
+ if ($tdoc->loadXML("<div>{$this->content} </div>")) {
+ $content->setAttribute("type", "xhtml");
+ $div =
$content->appendChild($dom->createElement("div"));
+ $div->setAttribute("xmlns",
"http://www.w3.org/1999/xhtml");
+ foreach($tdoc->firstChild->childNodes as $node) {
+ $div->appendChild($dom->importNode($node,
true));
+ }
+ } else {
+ fwrite(STDERR, "There is something wrong with your
xhtml, falling back to html");
+ $content->setAttribute("type", "html");
+ $content->nodeValue = $this->content;
+ }
+
+ $dom->appendChild($item);
+ $dom->save(self::ARCHIVE_ENTRIES_ABS . $this->id . ".xml");
+
+ return $this;
+ }
+
+ public function updateArchiveXML(): self {
+ if (empty($this->id)) {
+ throw new \Exception('Entry must be saved before
updating archive XML');
+ }
+
+ $arch = new \DOMDocument("1.0", "utf-8");
+ $arch->formatOutput = true;
+ $arch->preserveWhiteSpace = false;
+ $arch->load(self::ARCHIVE_FILE_ABS);
+
+ $first =
$arch->createElementNs("http://www.w3.org/2001/XInclude", "xi:include");
+ $first->setAttribute("href", "entries/{$this->id}.xml");
+
+ $second =
$arch->getElementsByTagNameNs("http://www.w3.org/2001/XInclude",
"include")->item(0);
+ $arch->documentElement->insertBefore($first, $second);
+ $arch->save(self::ARCHIVE_FILE_ABS);
+
+ return $this;
+ }
+
+ private static function ce(\DOMDocument $d, string $name, $value, array
$attrs = [], ?\DOMNode $to = null) {
+ if ($value) {
+ $n = $d->createElement($name, $value);
+ } else {
+ $n = $d->createElement($name);
+ }
+ foreach($attrs as $k => $v) {
+ $n->setAttribute($k, $v);
+ }
+ if ($to) {
+ return $to->appendChild($n);
+ }
+ return $n;
+ }
+}
+
--
PHP Webmaster List Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php