From 5ee3facfa9ad5e4190b1988ee52ce9ccff5d6281 Mon Sep 17 00:00:00 2001 From: Stef Dawson Date: Thu, 17 Nov 2016 22:27:59 +0000 Subject: [PATCH 1/9] Fix status assumptions (again) First part. See issue #663. Public side changes to be worked out and committed in future. --- textpattern/include/txp_article.php | 41 ++++++++-------- textpattern/include/txp_file.php | 4 +- textpattern/include/txp_list.php | 22 ++++----- textpattern/lib/txplib_misc.php | 72 +++++++++++++++++++++++++++++ textpattern/publish.php | 8 ++-- textpattern/publish/taghandlers.php | 2 +- 6 files changed, 113 insertions(+), 36 deletions(-) diff --git a/textpattern/include/txp_article.php b/textpattern/include/txp_article.php index 960b383c4d..fe24300974 100644 --- a/textpattern/include/txp_article.php +++ b/textpattern/include/txp_article.php @@ -234,11 +234,11 @@ function article_post() $Keywords = doSlash(trim(preg_replace('/( ?[\r\n\t,])+ ?/s', ',', preg_replace('/ +/', ' ', ps('Keywords'))), ', ')); $msg = ''; - if (!has_privs('article.publish') && $Status >= STATUS_LIVE) { + if (!has_privs('article.publish') && has_status_group($Status, 'published')) { $Status = STATUS_PENDING; } - if ($is_clone && $Status >= STATUS_LIVE) { + if ($is_clone && has_status_group($Status, 'published')) { $Status = STATUS_DRAFT; $url_title = ''; } @@ -301,7 +301,7 @@ function article_post() ); } - if ($Status >= STATUS_LIVE) { + if (has_status_group($Status, 'published')) { do_pings(); update_lastmod('article_posted', $rs); now('posted', true); @@ -338,10 +338,13 @@ function article_save() UNIX_TIMESTAMP(Expires) AS sExpires", 'textpattern', "ID = ".(int) $incoming['ID']); - if (!(($oldArticle['Status'] >= STATUS_LIVE and has_privs('article.edit.published')) - or ($oldArticle['Status'] >= STATUS_LIVE and $incoming['AuthorID'] === $txp_user and has_privs('article.edit.own.published')) - or ($oldArticle['Status'] < STATUS_LIVE and has_privs('article.edit')) - or ($oldArticle['Status'] < STATUS_LIVE and $incoming['AuthorID'] === $txp_user and has_privs('article.edit.own')))) { + $wasPublished = has_status_group($oldArticle['Status'], 'published'); + $wasUnpublished = has_status_group($oldArticle['Status'], 'unpublished'); + + if (!(($wasPublished and has_privs('article.edit.published')) + or ($wasPublished and $incoming['AuthorID'] === $txp_user and has_privs('article.edit.own.published')) + or ($wasUnpublished and has_privs('article.edit')) + or ($wasUnpublished and $incoming['AuthorID'] === $txp_user and has_privs('article.edit.own')))) { // Not allowed, you silly rabbit, you shouldn't even be here. // Show default editing screen. article_edit(); @@ -368,7 +371,7 @@ function article_save() // Comments may be on, off, or disabled. $Annotate = (int) $Annotate; - if (!has_privs('article.publish') && $Status >= STATUS_LIVE) { + if (!has_privs('article.publish') && has_status_group($Status, 'published')) { $Status = STATUS_PENDING; } @@ -441,7 +444,7 @@ function article_save() // Auto-update custom-titles according to Title, as long as unpublished and // NOT customised. if (empty($url_title) - || (($oldArticle['Status'] < STATUS_LIVE) + || (($wasUnpublished) && ($oldArticle['url_title'] === $url_title) && ($oldArticle['url_title'] === stripSpace($oldArticle['Title'], 1)) && ($oldArticle['Title'] !== $Title) @@ -490,11 +493,13 @@ function article_save() $whenexpires", "ID = $ID" )) { - if ($Status >= STATUS_LIVE && $oldArticle['Status'] < STATUS_LIVE) { + $isNowPublished = has_status_group($Status, 'published'); + + if ($isNowPublished && $wasUnpublished) { do_pings(); } - if ($Status >= STATUS_LIVE || $oldArticle['Status'] >= STATUS_LIVE) { + if ($isNowPublished || $wasPublished) { update_lastmod('article_saved', $rs); } @@ -872,7 +877,7 @@ function article_edit($message = '', $concurrent = false, $refresh_partials = fa $response[] = announce($message); $response[] = '$("#article_form [type=submit]").val(textpattern.gTxt("save"))'; - if ($Status < STATUS_LIVE) { + if (has_status_group($Status, 'unpublished')) { $response[] = '$("#article_form").addClass("saved").removeClass("published")'; } else { $response[] = '$("#article_form").addClass("published").removeClass("saved")'; @@ -894,7 +899,7 @@ function article_edit($message = '', $concurrent = false, $refresh_partials = fa $class = array(); - if ($Status >= STATUS_LIVE) { + if (has_status_group($Status, 'published')) { $class[] = 'published'; } elseif ($ID) { $class[] = 'saved'; @@ -1012,10 +1017,10 @@ function article_edit($message = '', $concurrent = false, $refresh_partials = fa echo graf($push_button, array('class' => 'txp-save')); } elseif ( - ($Status >= STATUS_LIVE && has_privs('article.edit.published')) || - ($Status >= STATUS_LIVE && $AuthorID === $txp_user && has_privs('article.edit.own.published')) || - ($Status < STATUS_LIVE && has_privs('article.edit')) || - ($Status < STATUS_LIVE && $AuthorID === $txp_user && has_privs('article.edit.own')) + (($isPublished = has_status_group($Status, 'published')) && has_privs('article.edit.published')) || + ($isPublished && $AuthorID === $txp_user && has_privs('article.edit.own.published')) || + (($isUnpublished = has_status_group($Status, 'unpublished')) && has_privs('article.edit')) || + ($isUnpublished && $AuthorID === $txp_user && has_privs('article.edit.own')) ) { echo graf(fInput('submit', 'save', gTxt('save'), 'publish'), array('class' => 'txp-save')); } @@ -1821,7 +1826,7 @@ function article_partial_article_view($rs) { extract($rs); - if ($Status != STATUS_LIVE and $Status != STATUS_STICKY) { + if (has_status_group($Status, 'unpublished')) { $url = '?txpreview='.intval($ID).'.'.time(); // Article ID plus cachebuster. } else { include_once txpath.'/publish/taghandlers.php'; diff --git a/textpattern/include/txp_file.php b/textpattern/include/txp_file.php index bb08bd82ab..a5bd3e48a8 100644 --- a/textpattern/include/txp_file.php +++ b/textpattern/include/txp_file.php @@ -48,7 +48,7 @@ ); global $file_statuses; -$file_statuses = status_list(true, array(STATUS_DRAFT, STATUS_STICKY)); +$file_statuses = status_group('files', true); if ($event == 'file') { require_privs('file'); @@ -668,7 +668,7 @@ function file_edit($message = '', $id = '') $permissions = '-1'; } - if (!has_privs('file.publish') && $status >= STATUS_LIVE) { + if (!has_privs('file.publish') && has_status_group($status, 'published')) { $status = STATUS_PENDING; } diff --git a/textpattern/include/txp_list.php b/textpattern/include/txp_list.php index f5105431bb..8639c727e3 100644 --- a/textpattern/include/txp_list.php +++ b/textpattern/include/txp_list.php @@ -395,7 +395,7 @@ function list_list($message = '', $post = '') $Category1 = ($Category1) ? span(txpspecialchars($category1_title), array('title' => $Category1)) : ''; $Category2 = ($Category2) ? span(txpspecialchars($category2_title), array('title' => $Category2)) : ''; - if ($Status != STATUS_LIVE and $Status != STATUS_STICKY) { + if (has_status_group($Status, 'unpublished')) { $view_url = '?txpreview='.intval($ID).'.'.time(); } else { $view_url = permlinkurl($a); @@ -435,10 +435,10 @@ function list_list($message = '', $post = '') td( ( ( - ($a['Status'] >= STATUS_LIVE and has_privs('article.edit.published')) - or ($a['Status'] >= STATUS_LIVE and $AuthorID === $txp_user and has_privs('article.edit.own.published')) - or ($a['Status'] < STATUS_LIVE and has_privs('article.edit')) - or ($a['Status'] < STATUS_LIVE and $AuthorID === $txp_user and has_privs('article.edit.own')) + (($isPublished = has_status_group($a['Status'], 'published')) and has_privs('article.edit.published')) + or ($isPublished and $AuthorID === $txp_user and has_privs('article.edit.own.published')) + or (($isUnpublished = has_status_group($a['Status'], 'unpublished')) and has_privs('article.edit')) + or ($isUnpublished and $AuthorID === $txp_user and has_privs('article.edit.own')) ) ? fInput('checkbox', 'selected[]', $ID, 'checkbox') : '' @@ -672,7 +672,7 @@ function list_multi_edit() $field = 'Status'; } - if (!has_privs('article.publish') && $value >= STATUS_LIVE) { + if (!has_privs('article.publish') && has_status_group($value, 'published')) { $value = STATUS_PENDING; } break; @@ -686,10 +686,10 @@ function list_multi_edit() foreach ($selected as $item) { if ( - ($item['Status'] >= STATUS_LIVE && has_privs('article.edit.published')) || - ($item['Status'] >= STATUS_LIVE && $item['AuthorID'] === $txp_user && has_privs('article.edit.own.published')) || - ($item['Status'] < STATUS_LIVE && has_privs('article.edit')) || - ($item['Status'] < STATUS_LIVE && $item['AuthorID'] === $txp_user && has_privs('article.edit.own')) + (($isPublished = has_status_group($item['Status'], 'published')) && has_privs('article.edit.published')) || + ($isPublished && $item['AuthorID'] === $txp_user && has_privs('article.edit.own.published')) || + (($isUnpublished = has_status_group($item['Status'], 'unpublished')) && has_privs('article.edit')) || + ($isUnpublished && $item['AuthorID'] === $txp_user && has_privs('article.edit.own')) ) { $allowed[] = $item['ID']; } @@ -709,7 +709,7 @@ function list_multi_edit() $a['uid'] = md5(uniqid(rand(), true)); $a['AuthorID'] = $txp_user; $a['LastModID'] = $txp_user; - $a['Status'] = ($a['Status'] >= STATUS_LIVE) ? STATUS_DRAFT : $a['Status']; + $a['Status'] = (has_status_group($a['Status'], 'published')) ? STATUS_DRAFT : $a['Status']; foreach ($a as $name => &$value) { if ($name == 'Expires' && !$value) { diff --git a/textpattern/lib/txplib_misc.php b/textpattern/lib/txplib_misc.php index 6a4020a21b..2610342de6 100644 --- a/textpattern/lib/txplib_misc.php +++ b/textpattern/lib/txplib_misc.php @@ -6641,6 +6641,78 @@ function status_list($labels = true, $exclude = array()) return $status_list; } +/** + * Return a list of status codes and their associated names by group. + * + * The groups can be extended with a 'status.types > groups' callback event. + * Callback functions get passed three arguments: '$event', '$step' and + * '$status_list'. The third parameter contains a nested array of $group => + * 'status_code => label' pairs. + * + * Note the unpublished statuses are not in numerical order, by design. + * This facilitates plugins that may wish to introduce a 'publisher workflow'. + * Such plugins could define a new group and/or could introduce a + * 'next status' feature which might step through the status codes within + * a group to indicate a document's flow through an editorial chain. + * Hidden->Draft->Pending (i.e. draft, then review by an editor prior to + * publication) is a logical default workflow. + * + * @param string The group to return + * @param bool Return the list with L10n labels (for UI purposes) or raw values (for comparisons) + * @return array A status array + * @since 4.7.0 + */ + +function status_group($group = 'published', $labels = true) +{ + $status_list = array( + 'unpublished' => array( + STATUS_HIDDEN => 'hidden', + STATUS_DRAFT => 'draft', + STATUS_PENDING => 'pending', + ), + 'published' => array( + STATUS_LIVE => 'live', + STATUS_STICKY => 'sticky', + ), + 'files' => array( + STATUS_HIDDEN => 'hidden', + STATUS_PENDING => 'pending', + STATUS_LIVE => 'live', + ), + ); + + callback_event_ref('status.types', 'groups', 0, $status_list); + + $outList = array(); + + if (array_key_exists($group, $status_list)) { + $outList = $status_list[$group]; + } + + if ($labels) { + $outList = array_map('gTxt', $outList); + } + + return $outList; +} + +/** + * Determine if the passed $status is in the nominated $group. + * + * @param int $status Status code + * @param string $group Group name + * @return boolean + * @since 4.7.0 + */ + +function has_status_group($status, $group) +{ + $statuses = status_group($group, false); + + return array_key_exists($status, $statuses); +} + /** * Translates article status names into numerical status codes. * diff --git a/textpattern/publish.php b/textpattern/publish.php index bbb9c924d4..bdc9ab2c41 100644 --- a/textpattern/publish.php +++ b/textpattern/publish.php @@ -508,7 +508,7 @@ function preText($s, $prefs) $a = safe_row( "*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(Expires) AS uExpires, UNIX_TIMESTAMP(LastMod) AS uLastMod", 'textpattern', - "ID = ".intval($id).(gps('txpreview') ? '' : " AND Status IN (".STATUS_LIVE.",".STATUS_STICKY.")") + "ID = ".intval($id).(gps('txpreview') ? '' : " AND Status IN (".implode(',', array_keys(status_group('published', false))).")") ); if ($a) { @@ -920,9 +920,9 @@ function doArticles($atts, $iscustom, $thing = null) } if ($q && $searchsticky) { - $statusq = " AND Status >= ".STATUS_LIVE; + $statusq = " AND Status IN (".implode(',', array_keys(status_group('published', false))).")"; } elseif ($id) { - $statusq = " AND Status >= ".STATUS_LIVE; + $statusq = " AND Status IN (".implode(',', array_keys(status_group('published', false))).")"; } else { $statusq = " AND Status = ".intval($status); } @@ -1066,7 +1066,7 @@ function doArticle($atts, $thing = null) $id = assert_int($id); $thisarticle = null; - $q_status = ($status ? "AND Status = ".intval($status) : "AND Status IN (".STATUS_LIVE.",".STATUS_STICKY.")"); + $q_status = ($status ? "AND Status = ".intval($status) : "AND Status IN (".implode(',', array_keys(status_group('published', false))).")"); $rs = safe_row( "*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(Expires) AS uExpires, UNIX_TIMESTAMP(LastMod) AS uLastMod", diff --git a/textpattern/publish/taghandlers.php b/textpattern/publish/taghandlers.php index 17f3078f7c..8a7271b553 100644 --- a/textpattern/publish/taghandlers.php +++ b/textpattern/publish/taghandlers.php @@ -1049,7 +1049,7 @@ function recent_comments($atts, $thing = null) $rs = startRows("SELECT d.name, d.email, d.web, d.message, d.discussid, UNIX_TIMESTAMP(d.Posted) AS time, t.ID AS thisid, UNIX_TIMESTAMP(t.Posted) AS posted, t.Title AS title, t.Section AS section, t.url_title FROM ".safe_pfx('txp_discuss')." AS d INNER JOIN ".safe_pfx('textpattern')." AS t ON d.parentid = t.ID - WHERE t.Status >= ".STATUS_LIVE.$expired." AND d.visible = ".VISIBLE." + WHERE t.Status IN(".implode(',', array_keys(status_group('published', false))).")".$expired." AND d.visible = ".VISIBLE." ORDER BY ".doSlash($sort)." LIMIT ".intval($offset).", ".intval($limit)); From 3417072e73371e878fe20de27dfc776b58b24204 Mon Sep 17 00:00:00 2001 From: Stef Dawson Date: Fri, 28 May 2021 10:05:13 +0100 Subject: [PATCH 2/9] Oops, fix incorrect merge wrt article save after duplicate --- textpattern/include/txp_article.php | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/textpattern/include/txp_article.php b/textpattern/include/txp_article.php index 312c1c8e72..cc33aab5a4 100644 --- a/textpattern/include/txp_article.php +++ b/textpattern/include/txp_article.php @@ -837,23 +837,6 @@ function article_edit($message = '', $concurrent = false, $refresh_partials = fa echo n.'
'; // 'Publish/Save' button. - if (empty($ID)) { - if (has_privs('article.publish') && has_status_group(get_pref('default_publish_status', STATUS_LIVE), 'published')) { - $push_button = fInput('submit', 'publish', gTxt('publish'), 'publish'); - } else { - $push_button = fInput('submit', 'publish', gTxt('save'), 'publish'); - } - - echo graf(''.$push_button.'', array('class' => 'txp-save')); - } elseif ( - (($isPublished = has_status_group($Status, 'published')) && has_privs('article.edit.published')) || - ($isPublished && $AuthorID === $txp_user && has_privs('article.edit.own.published')) || - (($isUnpublished = has_status_group($Status, 'unpublished')) && has_privs('article.edit')) || - ($isUnpublished && $AuthorID === $txp_user && has_privs('article.edit.own')) - ) { - echo graf(''.fInput('submit', 'save', gTxt('save'), 'publish').'', array('class' => 'txp-save')); - } - echo $partials['actions']['html']; echo n.'
'; @@ -1427,7 +1410,7 @@ function article_partial_actions($rs) $push_button = ''; if (empty($rs['ID'])) { - if (has_privs('article.publish') && get_pref('default_publish_status', STATUS_LIVE) >= STATUS_LIVE) { + if (has_privs('article.publish') && has_status_group(get_pref('default_publish_status', STATUS_LIVE), 'published')) { $push_button = fInput('submit', 'publish', gTxt('publish'), 'publish'); } else { $push_button = fInput('submit', 'publish', gTxt('save'), 'publish'); @@ -1435,10 +1418,10 @@ function article_partial_actions($rs) $push_button = graf($push_button, array('class' => 'txp-save')); } elseif ( - ($rs['Status'] >= STATUS_LIVE && has_privs('article.edit.published')) || - ($rs['Status'] >= STATUS_LIVE && $rs['AuthorID'] === $txp_user && has_privs('article.edit.own.published')) || - ($rs['Status'] < STATUS_LIVE && has_privs('article.edit')) || - ($rs['Status'] < STATUS_LIVE && $rs['AuthorID'] === $txp_user && has_privs('article.edit.own')) + (($isPublished = has_status_group($rs['Status'], 'published')) && has_privs('article.edit.published')) || + ($isPublished && $rs['AuthorID'] === $txp_user && has_privs('article.edit.own.published')) || + (($isUnpublished = has_status_group($rs['Status'], 'unpublished')) && has_privs('article.edit')) || + ($isUnpublished && $rs['AuthorID'] === $txp_user && has_privs('article.edit.own')) ) { $push_button = graf(fInput('submit', 'save', gTxt('save'), 'publish'), array('class' => 'txp-save')); } From 79be5a5b49e7574e79678d32136592f1c0c72008 Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Sat, 18 Sep 2021 08:51:04 +0100 Subject: [PATCH 3/9] Update README.md tweak top matter --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fa91113192..235ac7ecb8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Textpattern CMS [![Crowdin](https://badges.crowdin.net/textpattern-cms-textpacks/localized.svg)](https://crowdin.com/project/textpattern-cms-textpacks) +[![Known Vulnerabilities](https://snyk.io/test/github/textpattern/textpattern/badge.svg)](https://snyk.io/test/github/textpattern/textpattern/) [![GitHub Sponsors](https://img.shields.io/github/sponsors/textpattern)](https://github.com/sponsors/textpattern) Textpattern Logo From 8b8350897cca5949886194c72c7c39945ed33c75 Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Sun, 5 Dec 2021 23:54:04 +0000 Subject: [PATCH 4/9] Update README.md readme parity --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 235ac7ecb8..9128d64af5 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ We are targeting Textpattern 4.9 as the next minor release. Refer to the followi | | Minimum | Recommended | |--------|:-------:|:-----:| -| PHP | 5.6 | [vendor supported](https://php.net/supported-versions.php)
(7.4 or 8.0) | +| PHP | 5.6 | [vendor supported](https://php.net/supported-versions.php)
(7.4, 8.0 or 8.1) | | MySQL | 5.5 | [vendor supported](https://www.mysql.com/support/supportedplatforms/database.html)
(5.7 or 8.0) | | Apache | — | vendor supported
(2.4) | | Nginx | — | mainline (1.21) or stable (1.20) | From 4460df8eea8d46a95f064ddfc6a900dcd6cd6fdf Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Sat, 11 Dec 2021 17:27:54 +0000 Subject: [PATCH 5/9] Update UPGRADE.txt parity with `v4.8.8` --- UPGRADE.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UPGRADE.txt b/UPGRADE.txt index a53438673e..d1e5af003a 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -1,4 +1,4 @@ -Textpattern CMS 4.8.8 +Textpattern CMS 4.8.8 (upcoming) == Upgrade == @@ -68,7 +68,8 @@ Table of Contents confirm the Textpattern version and note any warnings or errors. Note: refer to the resources at the end of this file for further details. * Verify all preference settings (Admin -> Preferences). - +* Optionally set website Production status (Admin -> Preferences) to + "Testing" and check for any warning or error messages on the front-side. == Resources == From de8affd938f4d1bc8be7cdae18bb3c5d9bd40e30 Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Mon, 12 Aug 2024 12:34:46 +0100 Subject: [PATCH 6/9] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7927642539..396327ef78 100644 --- a/README.md +++ b/README.md @@ -68,14 +68,14 @@ We are targeting Textpattern 4.9 as the next minor release. Refer to the followi | | Minimum | Recommended | |--------|:-------:|:-----:| -| PHP | 5.6 | [vendor supported](https://php.net/supported-versions.php)
(8.0, 8.1 or 8.2) | -| MySQL | 5.5 | [vendor supported](https://www.mysql.com/support/supportedplatforms/database.html)
(5.7 and/or 8.0, depends on platform) | +| PHP | 5.6 | [vendor supported](https://php.net/supported-versions.php)
(8.1, 8.2 or 8.3) | +| MySQL | 5.5 | [vendor supported LTS](https://www.mysql.com/support/supportedplatforms/database.html)
(8.0 or 8.4) | | Apache | — | vendor supported
(2.4) | -| Nginx | — | mainline (1.23) or stable (1.22) | +| Nginx | — | mainline (1.27) or stable (1.26) | ## Contributing -Do you want to help with the development of Textpattern? Please refer to the [contributing documentation](https://github.com/textpattern/textpattern/blob/dev/.github/CONTRIBUTING.md) for full details. +Do you want to help with the development of Textpattern? Please refer to the [contributing documentation](https://github.com/textpattern/textpattern/blob/dev/CONTRIBUTING.md) for full details. ## GitHub topic tags @@ -133,7 +133,7 @@ npm run txp-gitdist 1.2.3 ../my-dest-dir ## Thank You -Thank you to our [GitHub monthly sponsors](https://github.com/sponsors/textpattern). Your continued support is greatly appreciated! +Thank you to our [GitHub sponsors](https://github.com/sponsors/textpattern). Your continued support is greatly appreciated! We are grateful to [DigitalOcean](https://www.digitalocean.com/?utm_source=opensource&utm_campaign=textpattern), [BrowserStack](https://www.browserstack.com) and [1Password](https://1password.com) for their kind considerations in supporting Textpattern CMS development by way of web hosting infrastructure (DigitalOcean), cross-browser testing platform (BrowserStack) and secure password management (1Password). Thank you! From 2cd11e1e7124e7d6459449862c87f0b422af3a61 Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Mon, 5 Jan 2026 16:19:49 +0000 Subject: [PATCH 7/9] Update README.md --- README.md | 73 ++++++++++++------------------------------------------- 1 file changed, 16 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 396327ef78..9b381ff208 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,15 @@ Ensure the server meets or exceeds the [system requirements](https://textpattern ## Download Textpattern -The current production release is version 4.8.8. It can be downloaded from the Textpattern website or GitHub in .zip and .tar.gz varieties. +The current production release is version 4.9.0. It can be downloaded from the Textpattern website or GitHub in .zip, .tar.gz, and .tar.xz varieties. -If you want to use the multi-site functionality in Textpattern, get the .tar.gz archive. +If you want to use the multi-site functionality in Textpattern, get the .tar.gz or .tar.xz archive. | | textpattern.com | GitHub | |--------|:-------:|:-----:| -| .zip | [Download](https://textpattern.com/file_download/118/textpattern-4.8.8.zip) | [Download](https://github.com/textpattern/textpattern/releases/download/4.8.8/textpattern-4.8.8.zip) | -| .tar.gz | [Download](https://textpattern.com/file_download/117/textpattern-4.8.8.tar.gz) | [Download](https://github.com/textpattern/textpattern/releases/download/4.8.8/textpattern-4.8.8.tar.gz) | +| .zip | [Download](https://textpattern.com/file_download/124/textpattern-4.9.0.zip) | [Download](https://github.com/textpattern/textpattern/releases/download/4.9.0/textpattern-4.9.0.zip) | +| .tar.gz | [Download](https://textpattern.com/file_download/125/textpattern-4.9.0.tar.gz) | [Download](https://github.com/textpattern/textpattern/releases/download/4.9.0/textpattern-4.9.0.tar.gz) | +| .tar.xz | [Download](https://textpattern.com/file_download/126/textpattern-4.9.0.tar.xz) | [Download](https://github.com/textpattern/textpattern/releases/download/4.9.0/textpattern-4.9.0.tar.xz) | ## Install Textpattern @@ -44,11 +45,11 @@ Please see [README.txt](https://github.com/textpattern/textpattern/blob/main/REA ## Upgrade Textpattern -Please see [README.txt](https://github.com/textpattern/textpattern/blob/main/README.txt) for details on upgrading Textpattern. +Please see [UPGRADE.txt](https://github.com/textpattern/textpattern/blob/main/UPGRADE.txt) for details on upgrading Textpattern. ## Help and Support -The [Textpattern support forum](https://forum.textpattern.com) is home to a friendly and helpful community of Textpattern users and experts. Textpattern also has a social network presence on [Mastodon](https://textpattern.com/mastodon) and [Twitter](https://textpattern.com/twitter). +The [Textpattern support forum](https://forum.textpattern.com) is home to a friendly and helpful community of Textpattern users and experts. Textpattern also has a social network presence on [Mastodon](https://textpattern.com/mastodon) and [X](https://textpattern.com/x). ## Development @@ -62,20 +63,22 @@ The following table outlines anticipated forthcoming changes to system requireme #### Textpattern development versions -Note that targeted versions listed may change multiple times during the development process. +We are targeting Textpattern 5 as the next major release. Refer to the following table for anticipated changes to system requirements for Textpattern 5. -We are targeting Textpattern 4.9 as the next minor release. Refer to the following table for anticipated changes to system requirements. +We generally recommend running Textpattern on platforms with active vendor support where possible, though we also maintain a minimum system requirements list for situations where that isn't viable. + +Note that versions listed may change multiple times during the development process. | | Minimum | Recommended | |--------|:-------:|:-----:| -| PHP | 5.6 | [vendor supported](https://php.net/supported-versions.php)
(8.1, 8.2 or 8.3) | -| MySQL | 5.5 | [vendor supported LTS](https://www.mysql.com/support/supportedplatforms/database.html)
(8.0 or 8.4) | -| Apache | — | vendor supported
(2.4) | -| Nginx | — | mainline (1.27) or stable (1.26) | +| PHP | — | [vendor supported](https://php.net/supported-versions.php) | +| MySQL | — | [vendor supported LTS](https://www.mysql.com/support/supportedplatforms/database.html) | +| Apache | — | vendor supported | +| Nginx | — | mainline or stable | ## Contributing -Do you want to help with the development of Textpattern? Please refer to the [contributing documentation](https://github.com/textpattern/textpattern/blob/dev/CONTRIBUTING.md) for full details. +Please refer to the [contributing documentation](https://github.com/textpattern/textpattern/blob/dev/CONTRIBUTING.md) for more details of Textpattern development. ## GitHub topic tags @@ -87,50 +90,6 @@ If you use GitHub for Textpattern-related development please consider adding som * [`textpattern-website`](https://github.com/topics/textpattern-website) (for websites built with Textpattern) * [`textpattern-development`](https://github.com/topics/textpattern-development) (for development resources) -## Additional development tools - -Various components used within Textpattern (such as the bundled themes and language translations) are maintained in standalone repositories. Textpattern has a simple development toolset built on [Node.js](https://nodejs.org/) to pull the distribution files of those repositories into the core as required. - -You can install Node.js using the [installer](https://nodejs.org/en/download/) or [package manager](https://nodejs.org/en/download/package-manager/). - -Install required dev tools: - -```ShellSession -npm install -``` - -You can then pull the following components from the CLI, like so: - -```ShellSession -npm run get-default-theme -npm run get-classic-admin-theme -npm run get-hive-admin-theme -npm run get-pophelp -npm run get-textpacks -npm run get-dependencies -``` - -To request a specific tag or branch: - -```ShellSession -npm run get-default-theme 4.8.8 -npm run get-classic-admin-theme 4.8.8 -npm run get-classic-admin-theme 4.8.x -npm run get-hive-admin-theme 4.8.x -npm run get-textpacks 4.8.x -``` - -Release tools: - -Usage: `npm run txp-gitdist [dest-dir]` (`dest-dir` defaults to a -temporary location). - -```ShellSession -npm run txp-index -npm run txp-checksums -npm run txp-gitdist 1.2.3 ../my-dest-dir -``` - ## Thank You Thank you to our [GitHub sponsors](https://github.com/sponsors/textpattern). Your continued support is greatly appreciated! From b904132d3b4653e890bade8175ea4b4d6bcf35a0 Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Mon, 5 Jan 2026 16:26:45 +0000 Subject: [PATCH 8/9] Add section for additional development tools Added a section for additional development tools in the README. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9b381ff208..6e123d4094 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,10 @@ Note that versions listed may change multiple times during the development proce Please refer to the [contributing documentation](https://github.com/textpattern/textpattern/blob/dev/CONTRIBUTING.md) for more details of Textpattern development. +## Additional development tools + +Please refer to the [additional devevelopment tools](https://github.com/textpattern/textpattern/blob/dev/DEVTOOLS.md) document. + ## GitHub topic tags If you use GitHub for Textpattern-related development please consider adding some of the following [topic](https://help.github.com/articles/about-topics/) keywords to your public project repositories, so we can expand the network of discoverable resources: From b95832190dfc9a1fe873fe734bfd31ac0ed18a6e Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Mon, 5 Jan 2026 16:31:40 +0000 Subject: [PATCH 9/9] Document additional development tools and usage Added documentation for additional development tools and usage instructions. --- DEVTOOLS.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 DEVTOOLS.md diff --git a/DEVTOOLS.md b/DEVTOOLS.md new file mode 100644 index 0000000000..dfc3231b84 --- /dev/null +++ b/DEVTOOLS.md @@ -0,0 +1,42 @@ +# Additional development tools + +Various components used within Textpattern (such as the bundled themes and language translations) are maintained in other repositories. Textpattern has a simple development toolset built on [Node.js](https://nodejs.org/) to pull the distribution files of those repositories into the core as required. + +You can install Node.js using the [installer](https://nodejs.org/en/download/) or [package manager](https://nodejs.org/en/download/package-manager/). + +Install required dev tools: + +```ShellSession +npm install +``` + +Pull the following components from the CLI: + +```ShellSession +npm run get-default-theme +npm run get-classic-admin-theme +npm run get-hive-admin-theme +npm run get-pophelp +npm run get-textpacks +npm run get-dependencies +``` + +To request a specific tag or branch: + +```ShellSession +npm run get-default-theme 4.9.0 +npm run get-classic-admin-theme 4.9.0 +npm run get-classic-admin-theme 4.9.x +npm run get-hive-admin-theme 4.9.x +npm run get-textpacks 4.9.x +``` + +Release tools: + +Usage: `npm run txp-gitdist [dest-dir]` (`dest-dir` defaults to a temporary location). + +```ShellSession +npm run txp-index +npm run txp-checksums ./textpattern +npm run txp-gitdist 1.2.3 ../my-dest-dir +```