From 2c9af1c3afad3f3aedef073a7436d677283ea456 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Sun, 16 Apr 2017 02:10:48 -0400
Subject: Add global state for page errors, warnings, etc.

Added arrays to the global $_SCROTT variable to keep lists of page
errors, warnings, and notices.  Also added functions to globals.php for
interacting with these arrays.
---
 app/class/globals.php | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

(limited to 'app/class')

diff --git a/app/class/globals.php b/app/class/globals.php
index 8bfa029..5a03da9 100644
--- a/app/class/globals.php
+++ b/app/class/globals.php
@@ -19,6 +19,19 @@
 
 define("__VERSION__", "v0.0");
 
+/*
+ * These global variables are arrays of strings logged by Scrott business
+ * logic to report errors, warnings, or informational responses to the
+ * user in cases where an exception doesn't need to be thrown.
+ */
+define("ERROR", "errorlist");
+define("WARNING", "warninglist");
+define("NOTICE", "noticelist");
+
+$_SCROTT[ERROR] = array();
+$_SCROTT[WARNING] = array();
+$_SCROTT[NOTICE] = array();
+
 /*
  * Get the application root path.  This is an absolute path on the server.
  */
@@ -63,4 +76,31 @@ function require_https() : void
         redirect("https://" . $_SERVER['SERVER_NAME'] . ap());
 }
 
+/*
+ * Check for errors, warnings, or notices
+ */
+function isError(string $level) : bool
+{
+    global $_SCROTT;
+    return count($_SCROTT[$level]) > 0;
+}
+
+/*
+ * Log an error, warning, or notice
+ */
+function logError(string $level, string $error) : void
+{
+    global $_SCROTT;
+    $_SCROTT[$level][] = $error;
+}
+
+/*
+ * Get an array of all errors, warnings, or notices
+ */
+function getErrors(string $level) : array
+{
+    global $_SCROTT;
+    return $_SCROTT[$level];
+}
+
 ?>
-- 
cgit v1.2.3


From bbdbc8cbd9142c4377197964d48b2db5dfe00fa3 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Sun, 16 Apr 2017 03:17:09 -0400
Subject: Add global function saveFile()

---
 app/class/globals.php | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

(limited to 'app/class')

diff --git a/app/class/globals.php b/app/class/globals.php
index 5a03da9..615efa6 100644
--- a/app/class/globals.php
+++ b/app/class/globals.php
@@ -103,4 +103,45 @@ function getErrors(string $level) : array
     return $_SCROTT[$level];
 }
 
+/*
+ * Save an uploaded file and impose some constraints on supplied
+ * data.  Caller can optionally pass some strings by reference to
+ * be given the supplied file's original name and mime-type.
+ * Maxsize is in bytes.  If this function returns false, the
+ * appropriate error will be logged.
+ */
+function saveFile(array $file, string $path, int $maxsize, ?array $allowedMime = NULL,
+    ?string &$origName = NULL, ?string &$origMime = NULL) : bool
+{
+    if ($file['error'] > 0)
+    {
+        if ($file['error'] != UPLOAD_ERR_NO_FILE)
+            logError(ERROR, "An unknown error occurred");
+
+        return false;
+    }
+
+    if ($file['size'] > $maxsize)
+    {
+        logError(ERROR, "File must be no larger than " . $maxsize . " bytes");
+        return false;
+    }
+
+    if (is_array($allowedMime) && array_search($file['type'], $allowedMime) === false)
+    {
+        logError(ERROR, "File type is not supported");
+        return false;
+    }
+
+    if (!move_uploaded_file($file['tmp_name'], $path))
+    {
+        logError(ERROR, "Error saving uploaded file");
+        return false;
+    }
+
+    $origName = $file['name'];
+    $origMime = $file['type'];
+    return true;
+}
+
 ?>
-- 
cgit v1.2.3


From 065c8bbb637954c47259e312ef87667e8d281497 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 00:29:19 -0400
Subject: Add image cropping logic

User-supplied head images will need to be square in their dimensions, so
that bootstrap displays them correctly.  This function will crop image
files after they are uploaded to the server.
---
 app/class/image.php | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 app/class/image.php

(limited to 'app/class')

diff --git a/app/class/image.php b/app/class/image.php
new file mode 100644
index 0000000..6b73cae
--- /dev/null
+++ b/app/class/image.php
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to UNLICENSE
+ */
+
+/*
+ * This file defines custom image manipulation routines for Scrott.  All
+ * are available in the global namespace.
+ */
+
+/*
+ * Mappings from image MIME types to PHP open/write functions
+ */
+$_IMG_OPEN_FUNCS['image/jpeg'] = "imagecreatefromjpeg";
+$_IMG_OPEN_FUNCS['image/jpg']  = "imagecreatefromjpeg";
+$_IMG_OPEN_FUNCS['image/png']  = "imagecreatefrompng";
+
+$_IMG_WRITE_FUNCS['image/jpeg'] = "imagejpeg";
+$_IMG_WRITE_FUNCS['image/jpg']  = "imagejpeg";
+$_IMG_WRITE_FUNCS['image/png']  = "imagepng";
+
+/*
+ * Open the given image and crop it, such that the result is a square
+ * image whose side length is equal to the smaller of the original
+ * dimensions and whose area is centered on the area of the original
+ * image.  The resulting image is written to the same path as provided,
+ * overwriting the image.
+ */
+function imageSquareCrop(string $uri) : bool
+{
+    global $_IMG_OPEN_FUNCS;
+    global $_IMG_WRITE_FUNCS;
+
+    $mime = mime_content_type($uri);
+    $img = $_IMG_OPEN_FUNCS[$mime]($uri);
+
+    if ($img === false)
+        return false;
+
+    $wid = imagesx($img);
+    $hei = imagesy($img);
+
+    $rec = array();
+    $rec['width']  = min($wid, $hei);
+    $rec['height'] = $rec['width'];
+    $rec['y'] = ($hei / 2) - ($rec['height'] / 2);
+    $rec['x'] = ($wid / 2) - ($rec['width'] / 2);
+
+    $cropped = imagecrop($img, $rec);
+    imagedestroy($img);
+
+    if ($cropped === false)
+        return false;
+
+    $ret = $_IMG_WRITE_FUNCS[$mime]($cropped, $uri);
+    imagedestroy($cropped);
+
+    return $ret;
+}
+
+?>
-- 
cgit v1.2.3


From a6a828521c61ebed3ec7038b2a53c002312a8bd0 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 02:32:38 -0400
Subject: Add object function setHeadImg()

---
 app/class/object.class.php | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

(limited to 'app/class')

diff --git a/app/class/object.class.php b/app/class/object.class.php
index 14ab891..0dacd87 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -13,6 +13,7 @@
  */
 
 require_once "class/table.class.php";
+require_once "class/image.php";
 
 /*
  * This is a generic database object.  This is a supertype of all Scrott
@@ -20,6 +21,16 @@ require_once "class/table.class.php";
  */
 class object extends table
 {
+    /*
+     * Constants used for uploading images
+     */
+    public const HEAD_MAXSIZE = 1048576; // 1Mb
+    public const IMAGE_MIME = array(
+        "image/jpeg",
+        "image/jpg",
+        "image/png",
+    );
+
     /*
      * Constructor
      */
@@ -149,6 +160,27 @@ class object extends table
         database::query($query);
         return true;
     }
+
+    /*
+     * Set the head image for this object, overwriting any existing
+     * image.  $image should be an uploaded file to PHP, still
+     * unhandled.
+     */
+    public function setHeadImg(array $image) : bool
+    {
+        $path = "dynmic/heads/" . $this->guid;
+
+        if (!saveFile($image, $path, self::HEAD_MAXSIZE, self::IMAGE_MIME))
+            return false;
+
+        if (!imageSquareCrop($path))
+        {
+            $this->rmHeadImg();
+            return false;
+        }
+
+        return true;
+    }
 }
 
 ?>
-- 
cgit v1.2.3


From ae94f42a59c1f308d973dc91214a63a1afb6cae3 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 21:47:52 -0400
Subject: Add object function rmHeadImg()

---
 app/class/object.class.php | 12 ++++++++++++
 1 file changed, 12 insertions(+)

(limited to 'app/class')

diff --git a/app/class/object.class.php b/app/class/object.class.php
index 0dacd87..55d0874 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -181,6 +181,18 @@ class object extends table
 
         return true;
     }
+
+    /*
+     * Remove the head image for this object.  This deletes the image
+     * on disk.
+     */
+    public function rmHeadImg() : bool
+    {
+        if (!is_file("dynmic/heads/" . $this->guid))
+            return true;
+
+        return unlink("dynmic/heads/" . $this->guid);
+    }
 }
 
 ?>
-- 
cgit v1.2.3


From 77d16dcad807e80e439d636301ccf588195f53fc Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 21:50:58 -0400
Subject: Add object function getHeadImg()

---
 app/class/object.class.php | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'app/class')

diff --git a/app/class/object.class.php b/app/class/object.class.php
index 55d0874..8d889e7 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -161,6 +161,14 @@ class object extends table
         return true;
     }
 
+    /*
+     * Get the URL to the head image resource for this object
+     */
+    public function getHeadImg() : string
+    {
+        return ar() . "/df.php?d=heads&f=" . $this->guid;
+    }
+
     /*
      * Set the head image for this object, overwriting any existing
      * image.  $image should be an uploaded file to PHP, still
-- 
cgit v1.2.3


From 3c4d7755d0a88c148884e0cbb90b327a96d87973 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 22:45:18 -0400
Subject: Add object function setBgImg()

---
 app/class/object.class.php | 12 ++++++++++++
 1 file changed, 12 insertions(+)

(limited to 'app/class')

diff --git a/app/class/object.class.php b/app/class/object.class.php
index 8d889e7..f8f5460 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -25,6 +25,7 @@ class object extends table
      * Constants used for uploading images
      */
     public const HEAD_MAXSIZE = 1048576; // 1Mb
+    public const BG_MAXSIZE = 1048576; // 1Mb
     public const IMAGE_MIME = array(
         "image/jpeg",
         "image/jpg",
@@ -201,6 +202,17 @@ class object extends table
 
         return unlink("dynmic/heads/" . $this->guid);
     }
+
+    /*
+     * Set the background image for this object, overwriting any
+     * existing image.  $image should be an uploaded file to PHP,
+     * still unhandled.
+     */
+    public function setBgImg(array $image) : bool
+    {
+        $path = "dynmic/bgs/" . $this->guid;
+        return saveFile($image, $path, self::BG_MAXSIZE, self::IMAGE_MIME);
+    }
 }
 
 ?>
-- 
cgit v1.2.3


From 07a1a1b7f207101ad27084ad37082f96afad8ac6 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 22:47:00 -0400
Subject: Add object function rmBgImg()

---
 app/class/object.class.php | 12 ++++++++++++
 1 file changed, 12 insertions(+)

(limited to 'app/class')

diff --git a/app/class/object.class.php b/app/class/object.class.php
index f8f5460..3bf64ec 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -213,6 +213,18 @@ class object extends table
         $path = "dynmic/bgs/" . $this->guid;
         return saveFile($image, $path, self::BG_MAXSIZE, self::IMAGE_MIME);
     }
+
+    /*
+     * Remove the background image for this object.  This deletes
+     * the image on disk.
+     */
+    public function rmBgImg() : bool
+    {
+        if (!is_file("dynmic/bgs/" . $this->guid))
+            return true;
+
+        return unlink("dynmic/bgs/" . $this->guid);
+    }
 }
 
 ?>
-- 
cgit v1.2.3


From 8256cbd7976d463ddfd31660995540a9c8adc315 Mon Sep 17 00:00:00 2001
From: Malf Furious <m@lfurio.us>
Date: Wed, 19 Apr 2017 22:49:05 -0400
Subject: Add object function getBgImg()

---
 app/class/object.class.php | 12 ++++++++++++
 1 file changed, 12 insertions(+)

(limited to 'app/class')

diff --git a/app/class/object.class.php b/app/class/object.class.php
index 3bf64ec..7c80b5b 100644
--- a/app/class/object.class.php
+++ b/app/class/object.class.php
@@ -203,6 +203,18 @@ class object extends table
         return unlink("dynmic/heads/" . $this->guid);
     }
 
+    /*
+     * Get the URL to the background image resource for this
+     * object.  If no image is set, NULL is returned.
+     */
+    public function getBgImg() : ?string
+    {
+        if (!is_file("dynmic/bgs/" . $this->guid))
+            return NULL;
+
+        return ar() . "/df.php?d=bgs&f=" . $this->guid;
+    }
+
     /*
      * Set the background image for this object, overwriting any
      * existing image.  $image should be an uploaded file to PHP,
-- 
cgit v1.2.3