Closed philwareham closed 4 years ago
@bloke throwing this open to debate - all yours! :)
Good question about betas. I'm of the opinion we allow beta updates. But we hide them by default and have some checkbox, either on the Plugins panel or as a pref, that allows you to consider betas/alphas or whatever as fair game. Does that work?
Also, could we utilise a more generic supersededBy
? Then the value can be core
or another plugin name (e.g. zem_contact_reborn is superseded by com_connect). That gives us scope to link to somewhere else; perhaps the plugin, or even a tutorial or something on TxpTips to explain how to replace abc_plugin with core tags?
{
"name": "zem_contact_reborn",
"supersededBy": {
"name": "com_connect",
"location": "https://github.com/textpattern/com_connect",
},
...
or
{
"name": "smd_macro",
"supersededBy": {
"name": "shortcodes",
"location": "https://docs.textpattern.com/tags/shortcodes/",
},
...
Scratching my head about how we output this in Textpattern. Effectively we want to use an article (the plugin individual article) in two sections: section 1 being the standard web page, section 2 being the JSON output (kind of like a feed).
For example...
https://plugins.textpattern.com/plugins/abc_example
: the standard web page with info about the plugin.2a. https://plugins.textpattern.com/plugins-json/abc_example
: the JSON extended data about the plugin, or
2b. https://plugins.textpattern.com/plugins/abc_example/json
: the JSON extended data about the plugin.
Can this be done via core or is it plugin territory? Am I missing something obvious?
If you register a JSON mime type in Advanced settings, this should be doable from a form. That means the single plugin page can render both visible components and JSON components if you like. Or, perhaps better, we could use your 2b endpoint, which I quite like:
https://plugins.textpattern.com/plugins/abc_example
returns the regular pagehttps://plugins.textpattern.com/plugins/abc_example/json
returns the JSON formatted blob of the same info.You can do that in any page or form - provided no output has been sent to the browser beforehand - with a little evaluate magic and txp:page_url's ability to extract numerical URL components. In this case we're testing the 3rd component is 'json', after 1=section, 2=title in our permlink scheme:
<txp:evaluate query='"<txp:page_url type="3" />" = "json"'>
<txp:header value="application/json; charset=utf-8" />{
... rest of JSON
<txp:else />
<html>
<head>
... regular individual article flow
</txp:evaluate>
Brilliant, that works perfectly!
If you register a JSON mime type in Advanced settings
What's this bit though (it seems to work already without me doing this)?
Yeah, forget the registration. That was an alternative if you wanted to use <txp:output_form>
and have it spit out the correct header on your behalf. Since you're using <txp:header>
manually, there's no need for the registration.
OK, plugin JSON files for hopeful use in the auto-update feature will reside in the following URL scheme
https://plugins.textpattern.com/plugins/abc_example/json
For example: https://plugins.textpattern.com/plugins/com_connect/json
I've not put most of the dynamic logic in yet, but it's a WIP.
That is superb work, thank you.
Just been browsing the (rah-heavy!) collection, filtering stuff and so forth, sticking the odd /json
on the end. That's gonna be great, and all the extra bits of info about a plugin and its compatibility are invaluable for people wanting to browse the repo and find what's available.
@bloke I'm working on the JSON output of legacy nodes. I have this PHP code although I have a some questions:
if (!empty($json->legacy)) {
echo ' "legacy": [';
foreach ($json->legacy as $avail) {
foreach ($avail as $txpver => $legacy) {
$safe_txpver = txpspecialchars($txpver);
$safe_plugver = txpspecialchars($legacy->version);
echo n.' {';
echo n.' "'.$safe_txpver.'" {';
echo n.' "version": "'.$safe_plugver.'",';
if (!empty($legacy->datePublished)) {
echo n.' "datePublished": "'.txpspecialchars($legacy->datePublished).'",';
}
if (!empty($legacy->downloadUrlPhp)) {
echo n.' "endpointUrl": "'.txpspecialchars($legacy->downloadUrlPhp).'"';
} else {
echo n.' "endpointUrl": "'.txpspecialchars($legacy->downloadUrlTxt).'"';
}
echo n.' }';
echo n.' },';
}
}
echo n.' ],';
}
Question 1: How would I trim the final comma in the foreach ($avail as $txpver => $legacy)
loop for the final node item in loop.
Question 2: Should I put whitespaces in the echo
output (this is mainly for readability, I know JSON whitespace is ignored), looks a bit hacky to me?
Question 3: most importantly, is this PHP code actually really necessary as it effectively remakes the JSON already supplied from the original curated-plugins-list JSON files. Is there a cleaner way of grabbing that original slug of data and outputting?
There is a caveat on question 3, there is a slight different in the legacy
JSON output from the site compared to the original curated-plugins-list JSON: the potential two download URLs downloadUrlPhp
and downloadUrlTxt
are a singleendpointUrl
where the downloadUrlPhp
value is used as preference, but if missing uses downloadUrlTxt
value instead.
Hope that makes some sort of sense?
The answer to all three questions is to use json_decode and json_encode.
If you read in the original file with json_decode($string_from_get_file_contents, true);
it becomes a bog standard array. You can simply add elements to it or remove others by using unset()
.
Then just json_encode($json)
it back up again and squirt it out.
Sorry, can you provide example code again! I have no PHP knowledge whatsoever so I just fudge it which takes quite a bit of time. Apologies for keeping you busy - it'll be worth it!
Sure, something like this:
$url = 'https://plugins.textpattern.com/plugins/rah_pathway/json';
$json = file_get_contents($url);
$json = json_decode($json, true);
// Create a legacy node from one of the other two.
if (!empty($json['legacy']['downloadUrlPhp'])) {
$json['legacy']['endpointUrl'] = txpspecialchars($json['legacy']['downloadUrlPhp']);
} elseif (!empty($json['legacy']['downloadUrlTxt'])) {
$json['legacy']['endpointUrl'] = txpspecialchars($json['legacy']['downloadUrlTxt']);
}
// Remove entries we don't need any more.
unset(
$json['legacy']['downloadUrlPhp'],
$json['legacy']['downloadUrlTxt']
);
// Bundle the array back up and write it to file.
$json = json_encode($json);
file_put_contents('/path/to/new/json', $json);
@bloke I only need to extract the legacy
nodes from the original JSON file, iterate over it to create any endpointUrl
s and remove downloadUrlPhp
and downloadUrlPhp
- then add that to my new JSON file. The rest of the new JSON file we are building from information available in the plugins site directly.
I think you example above takes the whole original JSON file and squirts that all back out with the legacy amends in place?
This is the last problem to solve for plugin pages. After that it's just getting search/version filters working on homepage and we can put this live for testing.
Yeah, if you're removing the content, just make a new variable to hold the new JSON file:
$url = 'https://plugins.textpattern.com/plugins/rah_pathway/json';
$json = file_get_contents($url);
$json = json_decode($json, true);
$newJson = array();
// Create a legacy node from one of the other two.
if (!empty($json['legacy']['downloadUrlPhp'])) {
$newJson['legacy']['endpointUrl'] = txpspecialchars($json['legacy']['downloadUrlPhp']);
} elseif (!empty($json['legacy']['downloadUrlTxt'])) {
$newJson['legacy']['endpointUrl'] = txpspecialchars($json['legacy']['downloadUrlTxt']);
}
// Remove entries we don't need any more if you intend to write the old file back,
// otherwise just delete this bit.
unset(
$json['legacy']['downloadUrlPhp'],
$json['legacy']['downloadUrlTxt']
);
// Write the current JSON file out with removed nodes, if necessary.
// Delete this bit otherwise.
$json = json_encode($json);
file_put_contents('/path/to/current/file.json', $json);
// Bundle the new array back up and write it to file.
$out = json_encode($newJson);
file_put_contents('/path/to/new/file.json', $out);
Hmmm, I can't seem to get this working. Is this correct...
['legacy']['downloadUrlPhp']
...as isn't there a node in between them kind of like...
['legacy']['the-txp-version']['downloadUrlPhp']
@bloke sorry to bump this, last issue I need to get fixed on the plugins pages now.
Ah yeah, you'll need an extra layer of iteration to get the Txp version. How do you want to write this out in the final JSON file? Do you want to keep the Txp version in the hierarchy?
"legacy": {
"4.6.0": {
"endpointUrl": "https://github.com/textpattern/com_connect/archive/4.6.0.zip"
}
}
OR:
"legacy": {
"txp-version": "4.6.0",
"endpointUrl": "https://github.com/textpattern/com_connect/archive/4.6.0.zip"
}
Like this:
"legacy": [
{
"4.6": {
"version": "v4.5.1",
"datePublished": "2016-12-03",
"endpointUrl": "https://github.com/textpattern/com_connect/archive/v4.5.1.tar.gz"
}
},
{
"4.5": {
"version": "v4.5.0.0-beta.4",
"datePublished": "2016-12-02",
"endpointUrl": "https://github.com/textpattern/com_connect/archive/v4.5.0.0-beta.4.tar.gz"
}
}
],
Cool. Got any plugins I can test it on? https://plugins.textpattern.com/plugins/com_connect/json is currently spewing out:
SyntaxError: JSON.parse: expected ':' after property name in object at line 11 column 13 of the JSON data
("4.5" and "4.6" need colons after them)
Just fixed that temporarily, but really we need to sort out the legacy output as discussed (grabbing the legacy JSON from original file, amending the endpoint, and re-encoding). Currently there is an erroneous extra comma in my output, using my dirty code method.
Oh, duh, I was supposed to be getting the original from GitHub, not the repo, sorry. Anyway, try this:
// Create legacy node from one of the endpoints.
if (!empty($json['legacy'])) {
foreach ($json['legacy'] as $txpBlock) {
foreach ($txpBlock as $txpver => $endpointData) {
$newJson['legacy'][$txpver]['version'] = txpspecialchars($endpointData['version']);
$newJson['legacy'][$txpver]['datePublished'] = txpspecialchars($endpointData['datePublished']);
if (!empty($endpointData['downloadUrlPhp'])) {
$newJson['legacy'][$txpver]['endpointUrl'] = txpspecialchars($endpointData['downloadUrlPhp']);
} elseif (!empty($endPointData['downloadUrlTxt'])) {
$newJson['legacy'][$txpver]['endpointUrl'] = txpspecialchars($endpointData['downloadUrlTxt']);
}
}
}
}
$out = json_encode($newJson);
file_put_contents('/path/to/new/file.json', $out);
EDIT: there's no defeinsive coding around the 'version' and 'datePublished' so you might need to add a corresponding if(!empty())
around each.
@bloke the output is now working as expected I think. See https://plugins.textpattern.com/plugins/com_connect/json for an example (it's also got a superseded flag on it just for testing).
Actually, thinking about the other discussion around endpoints, should we actually favour the TXT files here (i.e. check for them first, then fall back to PHP)? Seems wise.
Yeah, we could go for .txt first if it's available, then php/zip.
That endpoint is still showing as invalid to me:
{
"name": "com_connect",
"supersededBy": {
"name": "shortcodes",
"location": "https:\/\/example.com\/example.html"
},
"stable": {
"version": "4.6.0",
"verifiedMinTxpCompatibility": "4.6",
"datePublished": "2020-01-31",
"endpointUrl": "https:\/\/github.com\/textpattern\/com_connect\/archive\/4.6.0.zip"
}
,{
"legacy": {
"4.6": {
"version": "v4.5.1",
"datePublished": "2016-12-03",
"endpointUrl": "https:\/\/github.com\/textpattern\/com_connect\/archive\/v4.5.1.tar.gz"
},
"4.5": {
"version": "v4.5.0.0-beta.4",
"datePublished": "2016-12-02",
"endpointUrl": "https:\/\/github.com\/textpattern\/com_connect\/archive\/v4.5.0.0-beta.4.tar.gz"
}
}
}
}
Seems there's an extra pair of braces after the comma surrounding the legacy section. That's probably because the entire $json2 has been run through json_encode()
which creates an entire json structure, including surrounding braces. What we need to do is either:
1) paste the legacy fields into the existing JSON data; or 2) copy the data we want from the existing JSON data into the start of the new array
and then do a single json_encode()
on the result.
Can't we just trim
those braces out - I've spent way too much time on this already? :)
Sure, try it!
P.S. if we're sure this content isn't going to be consumed by, say, JavaScript then we could also pass the output through the JSON_UNESCAPED_SLASHES filter for neatness during encoding.
Hopefully json_decode()
handles the backslashes for us so we don't have to faff with stripping them out manually when we come to use these files.
EDIT: second thoughts, let's leave them in. Ignore me. We might want to grab this info via Ajax.
OK, the fixes have been done. Endpoints are now also TXT files where available, falling back to PHP if not.
Stub issue to track the format we will (hopefully) use for Textpattern 4.9 core plugin auto-update system.
Currently, JSON template will aim to give the following data (TBC):
For example (TBC):
The reason we don't showverifiedTxpCompatibility
for beta releases, is that these are only shown when their semver is higher than stable (or no stable exists). I'm open to opinion on this. Also TBC whether we even show beta releases here at all (i.e. should we exempt beta releases from the auto-update system)?