Browse Source

Add image uploading for items (close #8)

Skylar Ittner 7 months ago
parent
commit
9a5aefbeb5
15 changed files with 426 additions and 24 deletions
  1. 2
    0
      .gitignore
  2. 123
    0
      action.php
  3. 7
    8
      composer.lock
  4. BIN
      database.mwb
  5. 55
    13
      database.sql
  6. 8
    2
      database_upgrade/v1.1_1.2.sql
  7. 62
    0
      image.php
  8. 1
    0
      images/.htaccess
  9. 10
    0
      langs/en/images.json
  10. 10
    0
      pages.php
  11. 87
    0
      pages/editimages.php
  12. 13
    1
      pages/edititem.php
  13. 5
    0
      settings.template.php
  14. 24
    0
      static/css/files.css
  15. 19
    0
      static/js/files.js

+ 2
- 0
.gitignore View File

@@ -3,3 +3,5 @@ settings.php
3 3
 nbproject/private
4 4
 database.mwb.bak
5 5
 *.sync-conflict*
6
+images/
7
+!images/.htaccess

+ 123
- 0
action.php View File

@@ -229,6 +229,129 @@ switch ($VARS['action']) {
229 229
             exit("[]");
230 230
         }
231 231
         break;
232
+    case "imageupload":
233
+        $destpath = FILE_UPLOAD_PATH;
234
+        if (!is_writable($destpath)) {
235
+            returnToSender("unwritable_folder", "&id=$VARS[itemid]");
236
+        }
237
+
238
+        if (empty($VARS['itemid']) || !$database->has('items', ['itemid' => $VARS['itemid']])) {
239
+            returnToSender("invalid_itemid", "&id=$VARS[itemid]");
240
+        }
241
+
242
+        $files = [];
243
+        foreach ($_FILES['files'] as $key => $all) {
244
+            foreach ($all as $i => $val) {
245
+                $files[$i][$key] = $val;
246
+            }
247
+        }
248
+
249
+        $errors = [];
250
+        foreach ($files as $f) {
251
+            if ($f['error'] !== UPLOAD_ERR_OK) {
252
+                $err = "could not be uploaded.";
253
+                switch ($f['error']) {
254
+                    case UPLOAD_ERR_INI_SIZE:
255
+                    case UPLOAD_ERR_FORM_SIZE:
256
+                        $err = "is too big.";
257
+                        break;
258
+                    case UPLOAD_ERR_CANT_WRITE:
259
+                        $err = "could not be saved to disk.";
260
+                        break;
261
+                    case UPLOAD_ERR_NO_FILE:
262
+                        $err = "was not actually sent.";
263
+                        break;
264
+                    case UPLOAD_ERR_PARTIAL:
265
+                        $err = "was only partially sent.";
266
+                        break;
267
+                    default:
268
+                        $err = "could not be uploaded.";
269
+                }
270
+                $errors[] = htmlspecialchars($f['name']) . " $err";
271
+                continue;
272
+            }
273
+
274
+            if (filesize($f['tmp_name']) > 11) {
275
+                $imagetype = exif_imagetype($f['tmp_name']);
276
+            } else {
277
+                $imagetype = false;
278
+            }
279
+
280
+            switch ($imagetype) {
281
+                case IMAGETYPE_JPEG:
282
+                case IMAGETYPE_GIF:
283
+                case IMAGETYPE_PNG:
284
+                case IMAGETYPE_WEBP:
285
+                    $imagevalid = true;
286
+                    break;
287
+                default:
288
+                    $imagevalid = false;
289
+            }
290
+
291
+            if (!$imagevalid) {
292
+                $errors[] = htmlspecialchars($f['name']) . " is not a supported image type (JPEG, GIF, PNG, WEBP).";
293
+                continue;
294
+            }
295
+
296
+            $filename = basename($f['name']);
297
+            $filename = preg_replace("/[^a-z0-9\._\-]/", "_", strtolower($filename));
298
+            $n = 1;
299
+            if (file_exists($destpath . "/" . $filename)) {
300
+                while (file_exists($destpath . '/' . $n . '_' . $filename)) {
301
+                    $n++;
302
+                }
303
+                $filename = $n . '_' . $filename;
304
+            }
305
+
306
+            $finalpath = $destpath . "/" . $filename;
307
+
308
+            if (move_uploaded_file($f['tmp_name'], $finalpath)) {
309
+                $primary = false;
310
+                if (!$database->has('images', ['AND' => ['itemid' => $VARS['itemid'], 'primary' => true]])) {
311
+                    $primary = true;
312
+                }
313
+                $database->insert('images', ['itemid' => $VARS['itemid'], 'imagename' => $filename, 'primary' => $primary]);
314
+            } else {
315
+                $errors[] = htmlspecialchars($f['name']) . " could not be uploaded.";
316
+            }
317
+        }
318
+
319
+        if (count($errors) > 0) {
320
+            returnToSender("upload_warning", implode("<br>", $errors) . "&id=$VARS[itemid]");
321
+        }
322
+        returnToSender("upload_success", "&id=$VARS[itemid]");
323
+        break;
324
+    case "promoteimage":
325
+        if (empty($VARS['itemid']) || !$database->has('items', ['itemid' => $VARS['itemid']])) {
326
+            returnToSender("invalid_itemid", "&id=$VARS[itemid]");
327
+        }
328
+        if (empty($VARS['imageid']) || !$database->has('images', ['AND' => ['itemid' => $VARS['itemid'], 'imageid' => $VARS['imageid']]])) {
329
+            returnToSender("invalid_imageid", "&id=$VARS[itemid]");
330
+        }
331
+
332
+        $database->update('images', ['primary' => false], ['itemid' => $VARS['itemid']]);
333
+        $database->update('images', ['primary' => true], ['AND' => ['itemid' => $VARS['itemid'], 'imageid' => $VARS['imageid']]]);
334
+        returnToSender("image_promoted", "&id=$VARS[itemid]");
335
+        break;
336
+    case "deleteimage":
337
+        if (empty($VARS['itemid']) || !$database->has('items', ['itemid' => $VARS['itemid']])) {
338
+            returnToSender("invalid_itemid", "&id=$VARS[itemid]");
339
+        }
340
+        if (empty($VARS['imageid']) || !$database->has('images', ['AND' => ['itemid' => $VARS['itemid'], 'imageid' => $VARS['imageid']]])) {
341
+            returnToSender("invalid_imageid", "&id=$VARS[itemid]");
342
+        }
343
+
344
+        $imagename = $database->get('images', 'imagename', ['imageid' => $VARS['imageid']]);
345
+        if ($database->count('images', ['imagename' => $imagename]) <= 1) {
346
+            unlink(FILE_UPLOAD_PATH . "/" . $imagename);
347
+        }
348
+        $database->delete('images', ['AND' => ['itemid' => $VARS['itemid'], 'imageid' => $VARS['imageid']]]);
349
+
350
+        if (!$database->has('images', ['AND' => ['itemid' => $VARS['itemid'], 'primary' => true]])) {
351
+            $database->update('images', ['primary' => true], ['itemid' => $VARS['itemid'], 'LIMIT' => 1]);
352
+        }
353
+
354
+        returnToSender("image_deleted", "&id=$VARS[itemid]");
232 355
     case "signout":
233 356
         session_destroy();
234 357
         header('Location: index.php');

+ 7
- 8
composer.lock View File

@@ -4,7 +4,6 @@
4 4
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 5
         "This file is @generated automatically"
6 6
     ],
7
-    "hash": "831ae7560f5a64a68c52c69b772a8fc5",
8 7
     "content-hash": "a7d5f4f0d86fc84b983b1a3095ba3288",
9 8
     "packages": [
10 9
         {
@@ -64,7 +63,7 @@
64 63
                 "sql",
65 64
                 "sqlite"
66 65
             ],
67
-            "time": "2018-06-14 18:59:08"
66
+            "time": "2018-06-14T18:59:08+00:00"
68 67
         },
69 68
         {
70 69
             "name": "guzzlehttp/guzzle",
@@ -129,7 +128,7 @@
129 128
                 "rest",
130 129
                 "web service"
131 130
             ],
132
-            "time": "2018-04-22 15:46:56"
131
+            "time": "2018-04-22T15:46:56+00:00"
133 132
         },
134 133
         {
135 134
             "name": "guzzlehttp/promises",
@@ -180,7 +179,7 @@
180 179
             "keywords": [
181 180
                 "promise"
182 181
             ],
183
-            "time": "2016-12-20 10:07:11"
182
+            "time": "2016-12-20T10:07:11+00:00"
184 183
         },
185 184
         {
186 185
             "name": "guzzlehttp/psr7",
@@ -245,7 +244,7 @@
245 244
                 "uri",
246 245
                 "url"
247 246
             ],
248
-            "time": "2017-03-20 17:10:46"
247
+            "time": "2017-03-20T17:10:46+00:00"
249 248
         },
250 249
         {
251 250
             "name": "lapinator/ods-php-generator",
@@ -288,7 +287,7 @@
288 287
                 "LibreOffice",
289 288
                 "ods"
290 289
             ],
291
-            "time": "2016-04-14 21:51:27"
290
+            "time": "2016-04-14T21:51:27+00:00"
292 291
         },
293 292
         {
294 293
             "name": "league/csv",
@@ -355,7 +354,7 @@
355 354
                 "read",
356 355
                 "write"
357 356
             ],
358
-            "time": "2018-05-01 18:32:48"
357
+            "time": "2018-05-01T18:32:48+00:00"
359 358
         },
360 359
         {
361 360
             "name": "psr/http-message",
@@ -405,7 +404,7 @@
405 404
                 "request",
406 405
                 "response"
407 406
             ],
408
-            "time": "2016-08-06 14:39:51"
407
+            "time": "2016-08-06T14:39:51+00:00"
409 408
         }
410 409
     ],
411 410
     "packages-dev": [],

BIN
database.mwb View File


+ 55
- 13
database.sql View File

@@ -1,5 +1,5 @@
1 1
 -- MySQL Script generated by MySQL Workbench
2
+-- Sat 22 Sep 2018 02:40:11 AM MDT
2 3
 -- Model: New Model    Version: 1.0
3 4
 -- MySQL Workbench Forward Engineering
4 5
 
@@ -12,15 +12,9 @@ SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
12 12
 -- -----------------------------------------------------
13 13
 
14 14
 -- -----------------------------------------------------
15
-CREATE SCHEMA IF NOT EXISTS `inventory` DEFAULT CHARACTER SET utf8 ;
16
-USE `inventory` ;
17
-
15
+-- Table `categories`
18 16
 -- -----------------------------------------------------
19
-CREATE TABLE IF NOT EXISTS `inventory`.`categories` (
17
+CREATE TABLE IF NOT EXISTS `categories` (
20 18
   `catid` INT NOT NULL AUTO_INCREMENT,
21 19
   `catname` VARCHAR(45) NOT NULL,
22 20
   PRIMARY KEY (`catid`),
@@ -29,9 +23,9 @@ ENGINE = InnoDB;
29 23
 
30 24
 
31 25
 -- -----------------------------------------------------
26
+-- Table `locations`
32 27
 -- -----------------------------------------------------
33
-CREATE TABLE IF NOT EXISTS `inventory`.`locations` (
28
+CREATE TABLE IF NOT EXISTS `locations` (
34 29
   `locid` INT NOT NULL AUTO_INCREMENT,
35 30
   `locname` VARCHAR(100) NOT NULL,
36 31
   `loccode` VARCHAR(45) NOT NULL,
@@ -42,9 +36,9 @@ ENGINE = InnoDB;
42 36
 
43 37
 
44 38
 -- -----------------------------------------------------
39
+-- Table `items`
45 40
 -- -----------------------------------------------------
46
-CREATE TABLE IF NOT EXISTS `inventory`.`items` (
41
+CREATE TABLE IF NOT EXISTS `items` (
47 42
   `itemid` INT NOT NULL AUTO_INCREMENT,
48 43
   `catid` INT NOT NULL,
49 44
   `locid` INT NOT NULL,
@@ -57,27 +51,29 @@ CREATE TABLE IF NOT EXISTS `inventory`.`items` (
57 51
   `qty` INT NOT NULL DEFAULT 1,
58 52
   `want` INT NOT NULL DEFAULT 0,
59 53
   `userid` INT NULL,
60
-  PRIMARY KEY (`itemid`, `catid`, `locid`),
54
+  `cost` DECIMAL(10,2) NULL,
55
+  `price` DECIMAL(10,2) NULL,
56
+  PRIMARY KEY (`itemid`),
61 57
   INDEX `fk_items_categories_idx` (`catid` ASC),
62 58
   INDEX `fk_items_locations1_idx` (`locid` ASC),
63 59
   UNIQUE INDEX `itemid_UNIQUE` (`itemid` ASC),
64 60
   CONSTRAINT `fk_items_categories`
65 61
     FOREIGN KEY (`catid`)
66
-    REFERENCES `inventory`.`categories` (`catid`)
62
+    REFERENCES `categories` (`catid`)
67 63
     ON DELETE NO ACTION
68 64
     ON UPDATE NO ACTION,
69 65
   CONSTRAINT `fk_items_locations1`
70 66
     FOREIGN KEY (`locid`)
71
-    REFERENCES `inventory`.`locations` (`locid`)
67
+    REFERENCES `locations` (`locid`)
72 68
     ON DELETE NO ACTION
73 69
     ON UPDATE NO ACTION)
74 70
 ENGINE = InnoDB;
75 71
 
76 72
 
77 73
 -- -----------------------------------------------------
74
+-- Table `labels`
78 75
 -- -----------------------------------------------------
79
-CREATE TABLE IF NOT EXISTS `inventory`.`labels` (
76
+CREATE TABLE IF NOT EXISTS `labels` (
80 77
   `rowid` INT NOT NULL AUTO_INCREMENT,
81 78
   `name` VARCHAR(50) NOT NULL,
82 79
   `value` VARCHAR(100) NOT NULL,
@@ -86,9 +82,9 @@ ENGINE = InnoDB;
86 82
 
87 83
 
88 84
 -- -----------------------------------------------------
85
+-- Table `permissions`
89 86
 -- -----------------------------------------------------
90
-CREATE TABLE IF NOT EXISTS `inventory`.`permissions` (
87
+CREATE TABLE IF NOT EXISTS `permissions` (
91 88
   `userid` INT NOT NULL,
92 89
   `itemid` INT NOT NULL,
93 90
   `canedit` TINYINT(1) NOT NULL DEFAULT 0,
@@ -96,16 +92,16 @@ CREATE TABLE IF NOT EXISTS `inventory`.`permissions` (
96 92
   INDEX `fk_permissions_items1_idx` (`itemid` ASC),
97 93
   CONSTRAINT `fk_permissions_items1`
98 94
     FOREIGN KEY (`itemid`)
99
-    REFERENCES `inventory`.`items` (`itemid`)
95
+    REFERENCES `items` (`itemid`)
100 96
     ON DELETE NO ACTION
101 97
     ON UPDATE NO ACTION)
102 98
 ENGINE = InnoDB;
103 99
 
104 100
 
105 101
 -- -----------------------------------------------------
102
+-- Table `report_access_codes`
106 103
 -- -----------------------------------------------------
107
-CREATE TABLE IF NOT EXISTS `inventory`.`report_access_codes` (
104
+CREATE TABLE IF NOT EXISTS `report_access_codes` (
108 105
   `id` INT NOT NULL AUTO_INCREMENT,
109 106
   `code` VARCHAR(45) NULL,
110 107
   `expires` DATETIME NULL,
@@ -114,6 +110,42 @@ CREATE TABLE IF NOT EXISTS `inventory`.`report_access_codes` (
114 110
 ENGINE = InnoDB;
115 111
 
116 112
 
113
+-- -----------------------------------------------------
114
+-- Table `images`
115
+-- -----------------------------------------------------
116
+CREATE TABLE IF NOT EXISTS `images` (
117
+  `imageid` INT NOT NULL AUTO_INCREMENT,
118
+  `itemid` INT NOT NULL,
119
+  `imagename` VARCHAR(255) NOT NULL,
120
+  `primary` TINYINT(1) NOT NULL DEFAULT 0,
121
+  PRIMARY KEY (`imageid`, `itemid`),
122
+  UNIQUE INDEX `imageid_UNIQUE` (`imageid` ASC),
123
+  INDEX `fk_images_items1_idx` (`itemid` ASC),
124
+  CONSTRAINT `fk_images_items1`
125
+    FOREIGN KEY (`itemid`)
126
+    REFERENCES `items` (`itemid`)
127
+    ON DELETE NO ACTION
128
+    ON UPDATE NO ACTION)
129
+ENGINE = InnoDB;
130
+
131
+
117 132
 SET SQL_MODE=@OLD_SQL_MODE;
118 133
 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
119 134
 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
135
+
136
+-- -----------------------------------------------------
137
+-- Data for table `labels`
138
+-- -----------------------------------------------------
139
+START TRANSACTION;
140
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (1, 'itemid', 'Item ID');
141
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (2, 'catid', 'Category ID');
142
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (3, 'itemname', 'Item Name');
143
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (4, 'itemnumber1', 'Number Value 1');
144
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (5, 'itemnumber2', 'Number Value 2');
145
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (6, 'itemtext1', 'Text Value 1');
146
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (7, 'itemtext2', 'Text Value 2');
147
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (8, 'itemtext3', 'Text Value 3');
148
+INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (9, 'catname', 'Category Name');
149
+
150
+COMMIT;
151
+

+ 8
- 2
database_upgrade/v1.1_1.2.sql View File

@@ -5,12 +5,18 @@
5 5
  */
6 6
 ALTER TABLE `items`
7 7
 ADD COLUMN `cost` DECIMAL(10,2) NULL DEFAULT NULL AFTER `userid`,
8
-ADD COLUMN `price` DECIMAL(10,2) NULL DEFAULT NULL AFTER `cost`;
8
+ADD COLUMN `price` DECIMAL(10,2) NULL DEFAULT NULL AFTER `cost`;\
9
+
10
+ALTER TABLE `items`
11
+DROP PRIMARY KEY,
12
+ADD PRIMARY KEY (`itemid`);
13
+
9 14
 
10 15
 CREATE TABLE IF NOT EXISTS `images` (
11 16
   `imageid` INT(11) NOT NULL AUTO_INCREMENT,
12 17
   `itemid` INT(11) NOT NULL,
13 18
   `imagename` VARCHAR(255) NOT NULL,
19
+  `primary` TINYINT(1) NOT NULL DEFAULT 0,
14 20
   PRIMARY KEY (`imageid`, `itemid`),
15 21
   UNIQUE INDEX `imageid_UNIQUE` (`imageid` ASC),
16 22
   INDEX `fk_images_items1_idx` (`itemid` ASC),
@@ -20,4 +26,4 @@ CREATE TABLE IF NOT EXISTS `images` (
20 26
     ON DELETE NO ACTION
21 27
     ON UPDATE NO ACTION)
22 28
 ENGINE = InnoDB
23
-DEFAULT CHARACTER SET = utf8
29
+DEFAULT CHARACTER SET = utf8

+ 62
- 0
image.php View File

@@ -0,0 +1,62 @@
1
+<?php
2
+
3
+/*
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ */
8
+
9
+require_once __DIR__ . "/required.php";
10
+
11
+$base = FILE_UPLOAD_PATH . "/";
12
+if (isset($_GET['i'])) {
13
+    $file = $_GET['i'];
14
+    $filepath = $base . $file;
15
+    if (!file_exists($filepath) || is_dir($filepath)) {
16
+        http_response_code(404);
17
+        die("404 File Not Found");
18
+    }
19
+    if (strpos(realpath($filepath), FILE_UPLOAD_PATH) !== 0) {
20
+        http_response_code(404);
21
+        die("404 File Not Found");
22
+    }
23
+} else {
24
+    http_response_code(404);
25
+    die("404 File Not Found");
26
+}
27
+
28
+if (filesize($filepath) > 11) {
29
+    $imagetype = exif_imagetype($filepath);
30
+} else {
31
+    $imagetype = false;
32
+}
33
+
34
+switch ($imagetype) {
35
+    case IMAGETYPE_JPEG:
36
+        $mimetype = "image/jpeg";
37
+        break;
38
+    case IMAGETYPE_GIF:
39
+        $mimetype = "image/gif";
40
+        break;
41
+    case IMAGETYPE_PNG:
42
+        $mimetype = "image/png";
43
+        break;
44
+    case IMAGETYPE_WEBP:
45
+        $mimetype = "image/webp";
46
+        break;
47
+    default:
48
+        $mimetype = "application/octet-stream";
49
+}
50
+
51
+header("Content-Type: $mimetype");
52
+header('Content-Length: ' . filesize($filepath));
53
+header("X-Content-Type-Options: nosniff");
54
+$seconds_to_cache = 60 * 60 * 12; // 12 hours
55
+$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";
56
+header("Expires: $ts");
57
+header("Pragma: cache");
58
+header("Cache-Control: max-age=$seconds_to_cache");
59
+
60
+ob_end_flush();
61
+
62
+readfile($filepath);

+ 1
- 0
images/.htaccess View File

@@ -0,0 +1 @@
1
+Deny from all

+ 10
- 0
langs/en/images.json View File

@@ -0,0 +1,10 @@
1
+{
2
+    "Editing images for item": "Editing images for {item}",
3
+    "Edit Images": "Edit Images",
4
+    "Browse": "Browse",
5
+    "Upload": "Upload",
6
+    "Promoted": "Promoted",
7
+    "Promote": "Promote",
8
+    "Delete": "Delete",
9
+    "Back": "Back"
10
+}

+ 10
- 0
pages.php View File

@@ -61,6 +61,16 @@ define("PAGES", [
61 61
             "static/js/edititem.js"
62 62
         ],
63 63
     ],
64
+    "editimages" => [
65
+        "title" => "Edit item images",
66
+        "navbar" => false,
67
+        "styles" => [
68
+            "static/css/files.css"
69
+        ],
70
+        "scripts" => [
71
+            "static/js/files.js"
72
+        ]
73
+    ],
64 74
     "editcat" => [
65 75
         "title" => "Edit category",
66 76
         "navbar" => false,

+ 87
- 0
pages/editimages.php View File

@@ -0,0 +1,87 @@
1
+<?php
2
+/*
3
+ * This Source Code Form is subject to the terms of the Mozilla Public
4
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+ */
7
+
8
+require_once __DIR__ . '/../required.php';
9
+
10
+redirectifnotloggedin();
11
+
12
+$images = [];
13
+
14
+if (!empty($VARS['id']) && $database->has('items', ['itemid' => $VARS['id']])) {
15
+    $images = $database->select('images', ['imageid', 'imagename', 'primary'], ['itemid' => $VARS['id']]);
16
+} else {
17
+    header('Location: app.php?page=items&msg=invalid_itemid');
18
+    die();
19
+}
20
+?>
21
+
22
+<div class="card border-green">
23
+    <h3 class="card-header text-green">
24
+        <i class="fas fa-images"></i> <?php $Strings->build("Editing images for item", ['item' => "<span id=\"name_title\">" . htmlspecialchars($database->get('items', 'name', ['itemid' => $VARS['id']])) . "</span>"]); ?>
25
+    </h3>
26
+    <div class="card-body">
27
+        <div class="ml-auto my-auto">
28
+            <form action="action.php" method="POST" enctype="multipart/form-data">
29
+                <div class="input-group">
30
+                    <input type="text" id="uploadstatus" class="form-control" readonly />
31
+                    <div class="input-group-append">
32
+                        <span class="btn btn-primary btn-file">
33
+                            <i class="fas fa-folder-open"></i> <?php $Strings->get("Browse"); ?> <input id="fileupload" type="file" name="files[]" accept=".png,.jpg,.jpeg,.gif,.webp,image/png,image/jpeg,image/gif,image/webp" multiple required />
34
+                        </span>
35
+                        <button class="btn btn-success" type="submit"><i class="fas fa-cloud-upload-alt"></i> <?php $Strings->get("Upload"); ?></button>
36
+                    </div>
37
+                </div>
38
+                <input type="hidden" name="action" value="imageupload" />
39
+                <input type="hidden" name="source" value="editimages" />
40
+                <input type="hidden" name="itemid" value="<?php echo htmlspecialchars($VARS['id']); ?>" />
41
+            </form>
42
+        </div>
43
+        <div class="row">
44
+            <?php
45
+            foreach ($images as $i) {
46
+                ?>
47
+                <div class="col-12 col-sm-6 col-md-4 col-lg-4 col-xl-3">
48
+                    <div class="card">
49
+                        <img class="card-img" src="image.php?i=<?php echo $i['imagename']; ?>" alt="">
50
+                        <div class="card-img-overlay text-right">
51
+                            <?php
52
+                            if ($i['primary']) {
53
+                                ?>
54
+                                <span class="btn btn-green btn-sm" data-toggle="tooltip" data-placement="bottom" title=""><i class="fas fa-check"></i> <?php $Strings->get("Promoted"); ?></span>
55
+                                <?php
56
+                            } else {
57
+                                ?>
58
+                                <form action="action.php" method="POST">
59
+                                    <input type="hidden" name="action" value="promoteimage" />
60
+                                    <input type="hidden" name="itemid" value="<?php echo $VARS['id']; ?>" />
61
+                                    <input type="hidden" name="imageid" value="<?php echo $i['imageid']; ?>" />
62
+                                    <input type="hidden" name="source" value="editimages" />
63
+                                    <button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-level-up-alt"></i> <?php $Strings->get("Promote"); ?></button>
64
+                                </form>
65
+                                <?php
66
+                            }
67
+                            ?>
68
+                            <form action="action.php" method="POST">
69
+                                <input type="hidden" name="action" value="deleteimage" />
70
+                                <input type="hidden" name="itemid" value="<?php echo $VARS['id']; ?>" />
71
+                                <input type="hidden" name="imageid" value="<?php echo $i['imageid']; ?>" />
72
+                                <input type="hidden" name="source" value="editimages" />
73
+                                <button type="submit" class="btn btn-danger btn-sm mt-1"><i class="fas fa-trash"></i> <?php $Strings->get("Delete"); ?></button>
74
+                            </form>
75
+                        </div>
76
+                    </div>
77
+                </div>
78
+                <?php
79
+            }
80
+            ?>
81
+        </div>
82
+    </div>
83
+
84
+    <div class="card-footer d-flex">
85
+        <a href="./app.php?page=edititem&id=<?php echo $_GET['id']; ?>" class="btn btn-success mr-auto"><i class="fas fa-arrow-left"></i> <?php $Strings->get("Back"); ?></a>
86
+    </div>
87
+</div>

+ 13
- 1
pages/edititem.php View File

@@ -214,14 +214,26 @@ if (!empty($VARS['id'])) {
214 214
         ?>" />
215 215
         <input type="hidden" name="action" value="edititem" />
216 216
         <input type="hidden" name="source" value="items" />
217
+        <?php
218
+        if ($cloning) {
219
+            ?>
220
+            <input type="hidden" name="cloneof" value="<?php echo $VARS['id']; ?>" />
221
+            <?php
222
+        }
223
+        ?>
217 224
 
218 225
         <div class="card-footer d-flex">
219
-            <button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php $Strings->get("save"); ?></button>
220 226
             <?php
221 227
             if ($editing && !$cloning) {
222 228
                 ?>
229
+                <button type="submit" class="btn btn-success mr-1"><i class="fas fa-save"></i> <?php $Strings->get("save"); ?></button>
230
+                <a href="./app.php?page=editimages&id=<?php echo $_GET['id']; ?>" class="btn btn-primary mr-auto"><i class="fas fa-images"></i> <?php $Strings->get("Edit Images"); ?></a>
223 231
                 <a href="action.php?action=deleteitem&source=items&itemid=<?php echo htmlspecialchars($VARS['id']); ?>" class="btn btn-danger ml-auto"><i class="fas fa-times"></i> <?php $Strings->get('delete'); ?></a>
224 232
                 <?php
233
+            } else {
234
+                ?>
235
+                <button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php $Strings->get("save"); ?></button>
236
+                <?php
225 237
             }
226 238
             ?>
227 239
         </div>

+ 5
- 0
settings.template.php View File

@@ -34,6 +34,11 @@ define("TIMEZONE", "America/Denver");
34 34
 // Base URL for site links.
35 35
 define('URL', '.');
36 36
 
37
+// Folder for item images
38
+// If in the webroot, verify that the contents of the folder are not accessible
39
+// from a client (web browser).
40
+define('FILE_UPLOAD_PATH', __DIR__ . '/images');
41
+
37 42
 // Use Captcheck on login screen
38 43
 // https://captcheck.netsyms.com
39 44
 define("CAPTCHA_ENABLED", FALSE);

+ 24
- 0
static/css/files.css View File

@@ -0,0 +1,24 @@
1
+/*
2
+This Source Code Form is subject to the terms of the Mozilla Public
3
+License, v. 2.0. If a copy of the MPL was not distributed with this
4
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+*/
6
+.btn-file {
7
+    position: relative;
8
+    overflow: hidden;
9
+}
10
+.btn-file input[type=file] {
11
+    position: absolute;
12
+    top: 0;
13
+    right: 0;
14
+    min-width: 100%;
15
+    min-height: 100%;
16
+    font-size: 100px;
17
+    text-align: right;
18
+    filter: alpha(opacity=0);
19
+    opacity: 0;
20
+    outline: none;
21
+    background: white;
22
+    cursor: inherit;
23
+    display: block;
24
+}

+ 19
- 0
static/js/files.js View File

@@ -0,0 +1,19 @@
1
+/* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
+
5
+$(document).on('change', ':file', function () {
6
+    var input = $(this),
7
+            numFiles = input.get(0).files ? input.get(0).files.length : 1,
8
+            label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
9
+    input.trigger('fileselect', [numFiles, label]);
10
+});
11
+
12
+$(':file').on('fileselect', function (event, numFiles, label) {
13
+    var message = numFiles > 1 ? numFiles + ' files selected' : label;
14
+    $("#uploadstatus").val(message);
15
+});
16
+
17
+$(function () {
18
+    $('[data-toggle="tooltip"]').tooltip();
19
+})

Loading…
Cancel
Save