From 45befb5ff1865d12cad8902820f640fd46d13858 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Sun, 25 Aug 2019 21:38:28 +0100
Subject: [PATCH] Work on automatic theme index & preview generation

---
 build.sh                  | 80 +++++++++++++++++++++++++++++++--------
 core/40-page-renderer.php |  2 +-
 lantern-build-engine      |  2 +-
 module_index.json         | 10 +++++
 peppermint.guiconfig.json |  2 +-
 5 files changed, 77 insertions(+), 19 deletions(-)

diff --git a/build.sh b/build.sh
index 0309294..7a25e7f 100755
--- a/build.sh
+++ b/build.sh
@@ -58,7 +58,11 @@ task_setup() {
 	check_command jq true optional;
 	[[ "$?" -eq 0 ]] || echo -e "${FYEL}${HC}Warning: jq is required to update the theme index.${RS}";
 	check_command firefox true optional;
-	[[ "$?" -eq 0 ]] || echo -e "${FYEL}${HC}Warning: firefox is required to generate the theme previews.${RS}";
+	[[ "$?" -eq 0 ]] || echo -e "${FYEL}${HC}Warning: firefox is required to generate theme previews.${RS}";
+	check_command convert true optional;
+	[[ "$?" -eq 0 ]] || echo -e "${FYEL}${HC}Warning: The convert imagemagick command is required to generate theme previews.${RS}";
+	check_command nproc true optional;
+	[[ "$?" -eq 0 ]] || echo -e "${FYEL}${HC}Warning: nproc is required to generate theme previews.${RS}";
 	
 	task_end $?;
 	
@@ -90,17 +94,28 @@ task_build() {
 
 task_themes() {
 	if [[ ! -f "${server_pid_file}" ]]; then
-		tasks_run start-server; # TODO: Change this to call PHP directly, so we don't open the browser? Or maybe add an environment variable to start-server.
+		NO_BROWSER=true tasks_run start-server;
 	fi
 	
-	task_begin "Updating theme index";
-	cp "themes/themeindex.json" "themes/themeindex.json.old";
+	stage_begin "Updating theme index";
 	
-	# Temporary firefox profile
+	
+	task_begin "Preparing";
+	
+	[ -f "themes/themeindex.json" ] && cp "themes/themeindex.json" "themes/themeindex.json.old";
+	
+	# Temporary firefox profile directory
 	tmp_profile="$(mktemp -d /tmp/peppermint-firefox-profile-XXXXXXX)";
 	
-	while read filename; do
-		subtask_begin "Processing ${filename}";
+	# Temporary file for theme index items
+	tmp_themeindex_parts="$(mktemp /tmp/peppermint-themeindex-items-XXXXXXX)";
+	
+	task_end $?;
+	
+	preview_regen=false;
+	
+	while read -r filename; do
+		task_begin "Processing ${filename}";
 		
 		hash="$(sha256sum "${filename}" | cut -d' ' -f1)";
 		read -r -d "" awk_script <<'AWK'
@@ -125,27 +140,58 @@ END {
 }
 AWK
 		# TODO: Consider mapping it out as TSV, then using JQ to generate the object
-		awk -v prop_hash="${hash}" "${awk_script}" <"${filename}";
+		subtask_begin "Generating index entry";
+		awk -v prop_hash="${hash}" "${awk_script}" <"${filename}" >>"${tmp_themeindex_parts}";
+		subtask_end "$?";
 		
 		
 		# Capture the screenshot
+		if [ -f "themes/themeindex.json.old" ]; then 
+			theme_id="$(awk '/@id/ { print $3 }' <"${filename}")";
+			old_hash="$(jq --raw-output --arg theme_id "${theme_id}" '.[] | select(.id == $theme_id).hash' <"themes/themeindex.json")";
+			
+			# If the hash is the same as last time, don't bother to retake the screenshot
+			if [[ "${hash}" = "${hash_old}" ]]; then
+				continue;
+			fi
+		fi
+		preview_regen=true;
+		
 		screenshot_loc_full="$(dirname "${filename}")/preview_full.png";
 		screenshot_loc_small="$(dirname "${filename}")/preview_small.png";
 		
 		# Set the theme
+		cp "build/peppermint.json" "build/peppermint.json.bak";
 		tmp_file="$(mktemp /tmp/peppermint-json-XXXXXXX)";
 		jq --arg theme_css "$(cat "${filename}")" '.css = $theme_css' <"build/peppermint.json" >"${tmp_file}";
 		mv "${tmp_file}" "build/peppermint.json";
 		
 		# Capture the full-res screenshot
-		firefox --new-instance --headless --profile "${tmp_profile}" --window-size 1920x1080 --screenshot "${screenshot_loc_full}" "http://[::1]:35623/index.php";
+		execute firefox --new-instance --headless --profile "${tmp_profile}" --window-size 1920,1080 --screenshot "${screenshot_loc_full}" "http://[::1]:35623/index.php";
 		
-		subtask_end "$?";
-	done < <(find themes -type f -name "theme.css") | jq --tab --slurp . >themes/themeindex.json;
+		# Resize to get the smaller preview
+		execute convert "${screenshot_loc_full}" -resize 512x512 "${screenshot_loc_small}";
+		
+		mv "build/peppermint.json.bak" "build/peppermint.json";
+		
+		task_end "$?";
+	done < <(find themes -type f -name "theme.css");
 	
+	task_begin "Optimising new previews";
+	find "themes/" -iname "*.png" -print0 | xargs -0 -P"$(nproc)" -n1 optipng -preserve;
+	task_end "$?";
 	
-	rm -r "${tmp_profile}";
-	rm "themes/themeindex.json.old";
+	task_begin "Generating theme index";
+	jq --tab --slurp . <"${tmp_themeindex_parts}" >"themes/themeindex.json"
+	task_end "$?";
+	
+	# Clean up
+	task_begin "Cleaning up";
+	[[ -d "${tmp_profile}" ]] && rm -r "${tmp_profile}";
+	[[ -f "themes/themeindex.json.old" ]] && rm "themes/themeindex.json.old";
+	task_end 0;
+	
+	stage_end 0;
 }
 
 task_docs() {
@@ -198,9 +244,11 @@ task_start-server() {
 	echo "${pid}" >"${server_pid_file}";
 	task_end "${exit_code}" "";
 	
-	task_begin "Opening Browser";
-	sensible-browser [::]:35623;
-	task_end $?;
+	if [[ -z "${NO_BROWSER}" ]]; then
+		task_begin "Opening Browser";
+		sensible-browser [::]:35623;
+		task_end $?;
+	fi
 }
 
 task_stop-server() {
diff --git a/core/40-page-renderer.php b/core/40-page-renderer.php
index 420b6ad..96be61c 100644
--- a/core/40-page-renderer.php
+++ b/core/40-page-renderer.php
@@ -292,7 +292,7 @@ class page_renderer
 	 */
 	public static function is_css_url() {
 		global $settings;
-		return preg_match("/^[^\/]*\/\/|^\//", $settings->css);
+		return preg_match("/^[^\/]*\/\/|^\/[^\*]/", $settings->css);
 	}
 	/**
 	 * Renders all the CSS as HTML.
diff --git a/lantern-build-engine b/lantern-build-engine
index 7365f7d..5cf2cfe 160000
--- a/lantern-build-engine
+++ b/lantern-build-engine
@@ -1 +1 @@
-Subproject commit 7365f7d156a049d6f13987bbe55ff917e2a05254
+Subproject commit 5cf2cfebf0ee66db9167eadaa8e1956bba58a730
diff --git a/module_index.json b/module_index.json
index e2eb526..213b360 100755
--- a/module_index.json
+++ b/module_index.json
@@ -149,6 +149,16 @@
         "optional": false,
         "extra_data": []
     },
+    {
+        "id": "feature-theme-gallery",
+        "name": "Theme Gallery",
+        "version": "0.1",
+        "author": "Starbeamrainbowlabs",
+        "description": "Adds a theme gallery page and optional automatic theme updates.",
+        "lastupdate": 1566733743,
+        "optional": false,
+        "extra_data": []
+    },
     {
         "id": "feature-upload",
         "name": "Uploader",
diff --git a/peppermint.guiconfig.json b/peppermint.guiconfig.json
index cd6f0a4..8265bb9 100644
--- a/peppermint.guiconfig.json
+++ b/peppermint.guiconfig.json
@@ -180,7 +180,7 @@
 	"disable_peppermint_access_check": { "type": "checkbox", "description": "Disables the access check for peppermint.json on first-run. <strong>VERY DANGEROUS</strong>. Use only for development. Note that it's recommend to block access to peppermint.json for a reason - it contains your site secret and password hashes, so an attacker could do all <em>sorts</em> of nefarious things if it's left unblocked.", "default": false },
 	"css_theme_autoupdate_url": { "type": "url", "description": "A url that points to the css theme file to check for updates. If blank, then automatic updates are disabled.", "default": "" },
 	"css_theme_autoupdate_interval": { "type": "number", "description": "The interval, in seconds, that updates to the theme should be checked for. Defaults to every week. A value of -1 disables automatic updates.", "default": 604800 },
-	"css_theme_gallery_index_url": { "type": "text", "description": "A url that points  to an index file that contains a list of themes. Used to populate the gallary. Multiple urls are allowed - separate them with a space." },
+	"css_theme_gallery_index_url": { "type": "text", "description": "A url that points  to an index file that contains a list of themes. Used to populate the gallary. Multiple urls are allowed - separate them with a space.", "default": "https://starbeamrainbowlabs.com/labs/peppermint/themes/themeindex.php" },
 	"css_theme_gallery_selected_id": { "type": "text", "description": "The id of the currently selected theme. Defaults to the internal default theme.", "default": "default" },
 	"css": { "type": "textarea", "description": "A string of css to include. Will be included in the &lt;head&gt; of every page inside a &lt;style&gt; tag. This may also be an absolute url - urls will be referenced via a &lt;link rel='stylesheet' /&gt; tag. If the theme gallery is installed and automatic updates enabled, then the value of this property is managed by the theme gallery and changes may be overwritten (try the css_custom setting instead).", "default": "auto" },
 	"css_custom": { "type": "textarea", "description": "A string of custom CSS to include on top of the base theme css. Allows for theme customisations while still enabling automatic updates :D Just like the css setting, this one can also be a url.", "default": "/* Enter your custom css here. */" }
