Переглянути джерело

Refactored translation of exception messages

El RIDO 8 місяців тому
батько
коміт
3a23117ebf

+ 3 - 1
CHANGELOG.md

@@ -1,7 +1,9 @@
 # PrivateBin version history
 
 ## 2.0.4 (not yet released)
-* CHANGED: Deduplicate JSON error message translations.
+* CHANGED: Deduplicate JSON error message translations
+* CHANGED: Refactored translation of exception messages
+* FIXED: Some exceptions not getting translated
 
 ## 1.7.9 (2025-11-13)
 * CHANGED: Upgrading libraries to: base-x 5.0.1, bootstrap 5.3.8, DOMpurify 3.2.7, ip-lib 1.21.0 & kjua 0.10.0

+ 12 - 12
composer.lock

@@ -397,16 +397,16 @@
         },
         {
             "name": "nikic/php-parser",
-            "version": "v5.6.1",
+            "version": "v5.6.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
+                "reference": "3a454ca033b9e06b63282ce19562e892747449bb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
-                "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
+                "reference": "3a454ca033b9e06b63282ce19562e892747449bb",
                 "shasum": ""
             },
             "require": {
@@ -449,9 +449,9 @@
             ],
             "support": {
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
+                "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
             },
-            "time": "2025-08-13T20:13:15+00:00"
+            "time": "2025-10-21T19:32:17+00:00"
         },
         {
             "name": "phar-io/manifest",
@@ -2014,16 +2014,16 @@
         },
         {
             "name": "theseer/tokenizer",
-            "version": "1.2.3",
+            "version": "1.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/theseer/tokenizer.git",
-                "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+                "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
-                "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb",
+                "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb",
                 "shasum": ""
             },
             "require": {
@@ -2052,7 +2052,7 @@
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
             "support": {
                 "issues": "https://github.com/theseer/tokenizer/issues",
-                "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+                "source": "https://github.com/theseer/tokenizer/tree/1.3.0"
             },
             "funding": [
                 {
@@ -2060,7 +2060,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2024-03-03T12:36:25+00:00"
+            "time": "2025-11-13T13:44:09+00:00"
         }
     ],
     "aliases": [],

+ 1 - 1
lib/Configuration.php

@@ -12,7 +12,7 @@
 namespace PrivateBin;
 
 use Exception;
-use PrivateBin\TranslatedException;
+use PrivateBin\Exception\TranslatedException;
 
 /**
  * Configuration

+ 11 - 17
lib/Controller.php

@@ -12,12 +12,13 @@
 namespace PrivateBin;
 
 use Exception;
+use PrivateBin\Exception\JsonException;
+use PrivateBin\Exception\TranslatedException;
 use PrivateBin\Persistence\ServerSalt;
 use PrivateBin\Persistence\TrafficLimiter;
 use PrivateBin\Proxy\AbstractProxy;
 use PrivateBin\Proxy\ShlinkProxy;
 use PrivateBin\Proxy\YourlsProxy;
-use PrivateBin\TranslatedException;
 
 /**
  * Controller
@@ -308,11 +309,10 @@ class Controller
                     $comment = $paste->getComment($data['parentid']);
                     $comment->setData($data);
                     $comment->store();
-                } catch (TranslatedException $e) {
+                    $this->_json_result($comment->getId());
+                } catch (Exception $e) {
                     $this->_json_error($e->getMessage());
-                    return;
                 }
-                $this->_json_result($comment->getId());
             } else {
                 $this->_json_error(I18n::_('Invalid data.'));
             }
@@ -321,21 +321,13 @@ class Controller
         else {
             try {
                 $this->_model->purge();
-            } catch (Exception $e) { // JSON error!!!
-                error_log('Error purging documents: ' . $e->getMessage() . PHP_EOL .
-                    'Use the administration scripts statistics to find ' .
-                    'damaged paste IDs and either delete them or restore them ' .
-                    'from backup.');
-            }
-            $paste = $this->_model->getPaste();
-            try {
+                $paste = $this->_model->getPaste();
                 $paste->setData($data);
                 $paste->store();
-            } catch (TranslatedException $e) {
+                $this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
+            } catch (Exception $e) {
                 $this->_json_error($e->getMessage());
-                return;
             }
-            $this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
         }
     }
 
@@ -365,7 +357,7 @@ class Controller
             } else {
                 $this->_error = self::GENERIC_ERROR;
             }
-        } catch (Exception $e) {
+        } catch (TranslatedException $e) {
             $this->_error = $e->getMessage();
         }
         if ($this->_request->isJsonApiCall()) {
@@ -470,7 +462,7 @@ class Controller
         }
         $page->assign('BASEPATH', I18n::_($this->_conf->getKey('basepath')));
         $page->assign('STATUS', I18n::_($this->_status));
-        $page->assign('ISDELETED', I18n::_(json_encode($this->_is_deleted)));
+        $page->assign('ISDELETED', $this->_is_deleted);
         $page->assign('VERSION', self::VERSION);
         $page->assign('DISCUSSION', $this->_conf->getKey('discussion'));
         $page->assign('OPENDISCUSSION', $this->_conf->getKey('opendiscussion'));
@@ -546,6 +538,7 @@ class Controller
      *
      * @access private
      * @param  string $error
+     * @throws JsonException
      */
     private function _json_error($error)
     {
@@ -562,6 +555,7 @@ class Controller
      * @access private
      * @param  string $dataid
      * @param  array $other
+     * @throws JsonException
      */
     private function _json_result($dataid, $other = array())
     {

+ 38 - 17
lib/Data/Database.php

@@ -15,6 +15,7 @@ use Exception;
 use PDO;
 use PDOException;
 use PrivateBin\Controller;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Json;
 
 /**
@@ -179,18 +180,24 @@ class Database extends AbstractData
                 'SELECT * FROM "' . $this->_sanitizeIdentifier('paste') .
                 '" WHERE "dataid" = ?', array($pasteid), true
             );
-        } catch (Exception $e) {
+        } catch (PDOException $e) {
             $row = false;
         }
         if ($row === false) {
             return false;
         }
         // create array
-        $paste = Json::decode($row['data']);
+        try {
+            $paste = Json::decode($row['data']);
+        } catch (JsonException $e) {
+            error_log('Error while reading a paste from the database: ' . $e->getMessage());
+            $paste = array();
+        }
 
         try {
             $paste['meta'] = Json::decode($row['meta']);
-        } catch (Exception $e) {
+        } catch (JsonException $e) {
+            error_log('Error while reading a paste from the database: ' . $e->getMessage());
             $paste['meta'] = array();
         }
         $expire_date = (int) $row['expiredate'];
@@ -233,7 +240,7 @@ class Database extends AbstractData
                 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') .
                 '" WHERE "dataid" = ?', array($pasteid), true
             );
-        } catch (Exception $e) {
+        } catch (PDOException $e) {
             return false;
         }
         return (bool) $row;
@@ -253,7 +260,7 @@ class Database extends AbstractData
     {
         try {
             $data = Json::encode($comment);
-        } catch (Exception $e) {
+        } catch (JsonException $e) {
             error_log('Error while attempting to insert a comment into the database: ' . $e->getMessage());
             return false;
         }
@@ -274,7 +281,7 @@ class Database extends AbstractData
                     $meta['created'],
                 )
             );
-        } catch (Exception $e) {
+        } catch (PDOException $e) {
             error_log('Error while attempting to insert a comment into the database: ' . $e->getMessage());
             return false;
         }
@@ -298,8 +305,14 @@ class Database extends AbstractData
         $comments = array();
         if (count($rows)) {
             foreach ($rows as $row) {
+                try {
+                    $data = Json::decode($row['data']);
+                } catch (JsonException $e) {
+                    error_log('Error while reading a comment from the database: ' . $e->getMessage());
+                    $data = array();
+                }
                 $i                          = $this->getOpenSlot($comments, (int) $row['postdate']);
-                $comments[$i]               = Json::decode($row['data']);
+                $comments[$i]               = $data;
                 $comments[$i]['id']         = $row['dataid'];
                 $comments[$i]['parentid']   = $row['parentid'];
                 $comments[$i]['meta']       = array('created' => (int) $row['postdate']);
@@ -329,7 +342,7 @@ class Database extends AbstractData
                 '" WHERE "pasteid" = ? AND "parentid" = ? AND "dataid" = ?',
                 array($pasteid, $parentid, $commentid), true
             );
-        } catch (Exception $e) {
+        } catch (PDOException $e) {
             return false;
         }
     }
@@ -349,7 +362,8 @@ class Database extends AbstractData
             $this->_last_cache[$key] = $value;
             try {
                 $value = Json::encode($this->_last_cache);
-            } catch (Exception $e) {
+            } catch (JsonException $e) {
+                error_log('Error encoding JSON for table "config", row "traffic_limiter": ' . $e->getMessage());
                 return false;
             }
         }
@@ -393,7 +407,8 @@ class Database extends AbstractData
         if ($value && $namespace === 'traffic_limiter') {
             try {
                 $this->_last_cache = Json::decode($value);
-            } catch (Exception $e) {
+            } catch (JsonException $e) {
+                error_log('Error decoding JSON from table "config", row "traffic_limiter": ' . $e->getMessage());
                 $this->_last_cache = array();
             }
             if (array_key_exists($key, $this->_last_cache)) {
@@ -412,13 +427,18 @@ class Database extends AbstractData
      */
     protected function _getExpiredPastes($batchsize)
     {
-        $statement = $this->_db->prepare(
-            'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') .
-            '" WHERE "expiredate" < ? AND "expiredate" != ? ' .
-            ($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?')
-        );
-        $statement->execute(array(time(), 0, $batchsize));
-        return $statement->fetchAll(PDO::FETCH_COLUMN, 0);
+        try {
+            $statement = $this->_db->prepare(
+                'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') .
+                '" WHERE "expiredate" < ? AND "expiredate" != ? ' .
+                ($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?')
+            );
+            $statement->execute(array(time(), 0, $batchsize));
+            return $statement->fetchAll(PDO::FETCH_COLUMN, 0);
+        } catch (PDOException $e) {
+            error_log('Error while attempting to find expired pastes in the database: ' . $e->getMessage());
+            return array();
+        }
     }
 
     /**
@@ -552,6 +572,7 @@ class Database extends AbstractData
                 '" WHERE "id" = ?', array($key), true
             );
         } catch (PDOException $e) {
+            error_log('Error while attempting to fetch configuration key "' . $key . '" in the database: ' . $e->getMessage());
             return '';
         }
         return $row ? $row['value'] : '';

+ 11 - 9
lib/Data/Filesystem.php

@@ -11,8 +11,8 @@
 
 namespace PrivateBin\Data;
 
-use Exception;
 use GlobIterator;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Json;
 
 /**
@@ -104,13 +104,10 @@ class Filesystem extends AbstractData
      */
     public function read($pasteid)
     {
-        if (
-            !$this->exists($pasteid) ||
-            !$paste = $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php')
-        ) {
-            return false;
+        if ($this->exists($pasteid)) {
+            return $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php');
         }
-        return $paste;
+        return false;
     }
 
     /**
@@ -346,7 +343,12 @@ class Filesystem extends AbstractData
             file_get_contents($filename),
             strlen(self::PROTECTION_LINE . PHP_EOL)
         );
-        return Json::decode($data);
+        try {
+            return Json::decode($data);
+        } catch (JsonException $e) {
+            error_log('Error decoding JSON from "' . $filename . '": ' . $e->getMessage());
+            return false;
+        }
     }
 
     /**
@@ -450,7 +452,7 @@ class Filesystem extends AbstractData
                 $filename,
                 self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
             );
-        } catch (Exception $e) {
+        } catch (JsonException $e) {
             error_log('Error while trying to store data to the filesystem at path "' . $filename . '": ' . $e->getMessage());
             return false;
         }

+ 7 - 1
lib/Data/GoogleCloudStorage.php

@@ -15,6 +15,7 @@ use Exception;
 use Google\Cloud\Core\Exception\NotFoundException;
 use Google\Cloud\Storage\Bucket;
 use Google\Cloud\Storage\StorageClient;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Json;
 
 class GoogleCloudStorage extends AbstractData
@@ -219,7 +220,12 @@ class GoogleCloudStorage extends AbstractData
         try {
             foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
                 $data            = $this->_bucket->object($key->name())->downloadAsString();
-                $comment         = Json::decode($data);
+                try {
+                    $comment = Json::decode($data);
+                } catch (JsonException $e) {
+                    error_log('failed to read comment from ' . $key->name() . ', ' . $e->getMessage());
+                    $comment = array();
+                }
                 $comment['id']   = basename($key->name());
                 $slot            = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
                 $comments[$slot] = $comment;

+ 8 - 3
lib/Data/S3Storage.php

@@ -37,6 +37,7 @@ namespace PrivateBin\Data;
 
 use Aws\S3\Exception\S3Exception;
 use Aws\S3\S3Client;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Json;
 
 class S3Storage extends AbstractData
@@ -177,12 +178,14 @@ class S3Storage extends AbstractData
                 'ContentType' => 'application/json',
                 'Metadata'    => $metadata,
             ));
+            return true;
         } catch (S3Exception $e) {
             error_log('failed to upload ' . $key . ' to ' . $this->_bucket . ', ' .
                 trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
-            return false;
+        } catch (JsonException $e) {
+            error_log('failed to JSON encode ' . $key . ', ' . $e->getMessage());
         }
-        return true;
+        return false;
     }
 
     /**
@@ -212,8 +215,10 @@ class S3Storage extends AbstractData
         } catch (S3Exception $e) {
             error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket . ', ' .
                 trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
-            return false;
+        } catch (JsonException $e) {
+            error_log('failed to JSON decode ' . $key . ', ' . $e->getMessage());
         }
+        return false;
     }
 
     /**

+ 37 - 0
lib/Exception/JsonException.php

@@ -0,0 +1,37 @@
+<?php declare(strict_types=1);
+/**
+ * PrivateBin
+ *
+ * a zero-knowledge paste bin
+ *
+ * @link      https://github.com/PrivateBin/PrivateBin
+ * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
+ * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
+ */
+
+namespace PrivateBin\Exception;
+
+use Exception;
+
+/**
+ * JsonException
+ *
+ * An Exception representing JSON en- or decoding errors.
+ */
+class JsonException extends Exception
+{
+    /**
+     * Exception constructor with mandatory JSON error code.
+     *
+     * @access public
+     * @param  int $code
+     */
+    public function __construct(int $code) {
+        $message = 'A JSON error occurred';
+        if (function_exists('json_last_error_msg')) {
+            $message .= ': ' . json_last_error_msg();
+        }
+        $message .= ' (' . $code . ')';
+        parent::__construct($message, 90);
+    }
+}

+ 1 - 1
lib/TranslatedException.php → lib/Exception/TranslatedException.php

@@ -9,7 +9,7 @@
  * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
  */
 
-namespace PrivateBin;
+namespace PrivateBin\Exception;
 
 use Exception;
 use PrivateBin\I18n;

+ 2 - 0
lib/I18n.php

@@ -162,6 +162,7 @@ class I18n
      *
      * @access public
      * @static
+     * @throws JsonException
      */
     public static function loadTranslations()
     {
@@ -270,6 +271,7 @@ class I18n
      * @access public
      * @static
      * @param  array $languages
+     * @throws JsonException
      * @return array
      */
     public static function getLanguageLabels($languages = array())

+ 7 - 14
lib/Json.php

@@ -11,7 +11,7 @@
 
 namespace PrivateBin;
 
-use Exception;
+use PrivateBin\Exception\JsonException;
 
 /**
  * Json
@@ -26,7 +26,7 @@ class Json
      * @access public
      * @static
      * @param  mixed $input
-     * @throws Exception
+     * @throws JsonException
      * @return string
      */
     public static function encode(&$input)
@@ -42,7 +42,7 @@ class Json
      * @access public
      * @static
      * @param  string $input
-     * @throws Exception
+     * @throws JsonException
      * @return mixed
      */
     public static function decode(&$input)
@@ -57,21 +57,14 @@ class Json
      *
      * @access private
      * @static
-     * @throws Exception
+     * @throws JsonException
      * @return void
      */
     private static function _detectError()
     {
         $errorCode = json_last_error();
-        if ($errorCode === JSON_ERROR_NONE) {
-            return;
+        if ($errorCode !== JSON_ERROR_NONE) {
+            throw new JsonException($errorCode);
         }
-
-        $message = 'A JSON error occurred';
-        if (function_exists('json_last_error_msg')) {
-            $message .= ': ' . json_last_error_msg();
-        }
-        $message .= ' (' . $errorCode . ')';
-        throw new Exception($message, 90);
     }
-}
+}

+ 1 - 1
lib/Model/AbstractModel.php

@@ -13,7 +13,7 @@ namespace PrivateBin\Model;
 
 use PrivateBin\Configuration;
 use PrivateBin\Data\AbstractData;
-use PrivateBin\TranslatedException;
+use PrivateBin\Exception\TranslatedException;
 
 /**
  * AbstractModel

+ 1 - 1
lib/Model/Comment.php

@@ -13,8 +13,8 @@ namespace PrivateBin\Model;
 
 use Identicon\Identicon;
 use Jdenticon\Identicon as Jdenticon;
+use PrivateBin\Exception\TranslatedException;
 use PrivateBin\Persistence\TrafficLimiter;
-use PrivateBin\TranslatedException;
 use PrivateBin\Vizhash16x16;
 
 /**

+ 1 - 1
lib/Model/Paste.php

@@ -12,8 +12,8 @@
 namespace PrivateBin\Model;
 
 use PrivateBin\Controller;
+use PrivateBin\Exception\TranslatedException;
 use PrivateBin\Persistence\ServerSalt;
-use PrivateBin\TranslatedException;
 
 /**
  * Paste

+ 1 - 1
lib/Persistence/TrafficLimiter.php

@@ -15,7 +15,7 @@ namespace PrivateBin\Persistence;
 use IPLib\Factory;
 use IPLib\ParseStringFlag;
 use PrivateBin\Configuration;
-use PrivateBin\TranslatedException;
+use PrivateBin\Exception\TranslatedException;
 
 /**
  * TrafficLimiter

+ 2 - 2
lib/Proxy/AbstractProxy.php

@@ -11,8 +11,8 @@
 
 namespace PrivateBin\Proxy;
 
-use Exception;
 use PrivateBin\Configuration;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Json;
 
 /**
@@ -90,7 +90,7 @@ abstract class AbstractProxy
 
         try {
             $jsonData = Json::decode($data);
-        } catch (Exception $e) {
+        } catch (JsonException $e) {
             $this->_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.';
             $this->logErrorWithClassName('Error calling proxy: ' . $e->getMessage());
             return;

+ 12 - 6
lib/Proxy/ShlinkProxy.php

@@ -12,6 +12,7 @@
 namespace PrivateBin\Proxy;
 
 use PrivateBin\Configuration;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Json;
 
 /**
@@ -48,12 +49,17 @@ class ShlinkProxy extends AbstractProxy
             'longUrl' => $link,
         );
 
-        return array(
-            'method'  => 'POST',
-            'header'  => "Content-Type: application/json\r\n" .
-                         'X-Api-Key: ' . $shlink_api_key . "\r\n",
-            'content' => Json::encode($body),
-        );
+        try {
+            return array(
+                'method'  => 'POST',
+                'header'  => "Content-Type: application/json\r\n" .
+                            'X-Api-Key: ' . $shlink_api_key . "\r\n",
+                'content' => Json::encode($body),
+            );
+        } catch (JsonException $e) {
+            error_log('[' . get_class($this) . '] Error encoding body: ' . $e->getMessage());
+            return array();
+        }
     }
 
     /**

+ 2 - 2
lib/Request.php

@@ -11,7 +11,7 @@
 
 namespace PrivateBin;
 
-use Exception;
+use PrivateBin\Exception\JsonException;
 use PrivateBin\Model\Paste;
 
 /**
@@ -113,7 +113,7 @@ class Request
                 try {
                     $data          = file_get_contents(self::$_inputStream);
                     $this->_params = Json::decode($data);
-                } catch (Exception $e) {
+                } catch (JsonException $e) {
                     // ignore error, $this->_params will remain empty
                 }
                 break;

+ 11 - 8
tpl/bootstrap.php

@@ -513,16 +513,19 @@ if ($FILEUPLOAD) :
 <?php
 endif;
 ?>
-				<div id="status" role="alert" class="clearfix alert alert-<?php echo (bool)$ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
+				<div id="status" role="alert" class="clearfix alert alert-<?php echo $ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
 					<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
 					<?php echo I18n::encode($STATUS), PHP_EOL; ?>
-					<?php
-						if ((bool)$ISDELETED):
-					?>
-						<button type="button" class="btn btn-default pull-right" id="new-from-alert">
-							<span class="glyphicon glyphicon-repeat"></span> <?php echo I18n::_('Start over'), PHP_EOL; ?>
-						</button>
-					<?php endif; ?>
+<?php
+if ($ISDELETED) :
+?>
+					<button type="button" class="btn btn-default pull-right" id="new-from-alert">
+						<span class="glyphicon glyphicon-repeat"></span>
+						<?php echo I18n::_('Start over'), PHP_EOL; ?>
+					</button>
+<?php
+endif;
+?>
 				</div>
 				<div id="errormessage" role="alert" class="<?php echo empty($ERROR) ? 'hidden' : '' ?> alert alert-danger">
 					<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>

+ 11 - 8
tpl/bootstrap5.php

@@ -378,18 +378,21 @@ if ($FILEUPLOAD) :
 <?php
 endif;
 ?>
-				<div id="status" role="alert" class="d-flex justify-content-between align-items-center alert alert-<?php echo (bool)$ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
+				<div id="status" role="alert" class="d-flex justify-content-between align-items-center alert alert-<?php echo $ISDELETED ? 'success' : 'info'; echo empty($STATUS) ? ' hidden' : '' ?>">
 					<div>
 						<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#info-circle" /></svg>
 						<?php echo I18n::encode($STATUS), PHP_EOL; ?>
 					</div>
-					<?php
-						if ((bool)$ISDELETED):
-					?>
-						<button type="button" class="btn btn-secondary d-flex justify-content-center align-items-center gap-1" id="new-from-alert">
-							<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#repeat" /></svg> <?php echo I18n::_('Start over'), PHP_EOL; ?>
-						</button>
-					<?php endif; ?>
+<?php
+if ($ISDELETED) :
+?>
+					<button type="button" class="btn btn-secondary d-flex justify-content-center align-items-center gap-1" id="new-from-alert">
+						<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#repeat" /></svg>
+						<?php echo I18n::_('Start over'), PHP_EOL; ?>
+					</button>
+<?php
+endif;
+?>
 				</div>
 				<div id="errormessage" role="alert" class="<?php echo empty($ERROR) ? 'hidden' : '' ?> alert alert-danger">
 					<svg width="16" height="16" fill="currentColor" aria-hidden="true"><use href="img/bootstrap-icons.svg#exclamation-triangle" /></svg>

+ 2 - 1
vendor/composer/autoload_classmap.php

@@ -74,6 +74,8 @@ return array(
     'PrivateBin\\Data\\Filesystem' => $baseDir . '/lib/Data/Filesystem.php',
     'PrivateBin\\Data\\GoogleCloudStorage' => $baseDir . '/lib/Data/GoogleCloudStorage.php',
     'PrivateBin\\Data\\S3Storage' => $baseDir . '/lib/Data/S3Storage.php',
+    'PrivateBin\\Exception\\JsonException' => $baseDir . '/lib/Exception/JsonException.php',
+    'PrivateBin\\Exception\\TranslatedException' => $baseDir . '/lib/Exception/TranslatedException.php',
     'PrivateBin\\Filter' => $baseDir . '/lib/Filter.php',
     'PrivateBin\\FormatV2' => $baseDir . '/lib/FormatV2.php',
     'PrivateBin\\I18n' => $baseDir . '/lib/I18n.php',
@@ -91,7 +93,6 @@ return array(
     'PrivateBin\\Proxy\\YourlsProxy' => $baseDir . '/lib/Proxy/YourlsProxy.php',
     'PrivateBin\\Request' => $baseDir . '/lib/Request.php',
     'PrivateBin\\TemplateSwitcher' => $baseDir . '/lib/TemplateSwitcher.php',
-    'PrivateBin\\TranslatedException' => $baseDir . '/lib/TranslatedException.php',
     'PrivateBin\\View' => $baseDir . '/lib/View.php',
     'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php',
     'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',

+ 2 - 1
vendor/composer/autoload_static.php

@@ -122,6 +122,8 @@ class ComposerStaticInitDontChange
         'PrivateBin\\Data\\Filesystem' => __DIR__ . '/../..' . '/lib/Data/Filesystem.php',
         'PrivateBin\\Data\\GoogleCloudStorage' => __DIR__ . '/../..' . '/lib/Data/GoogleCloudStorage.php',
         'PrivateBin\\Data\\S3Storage' => __DIR__ . '/../..' . '/lib/Data/S3Storage.php',
+        'PrivateBin\\Exception\\JsonException' => __DIR__ . '/../..' . '/lib/Exception/JsonException.php',
+        'PrivateBin\\Exception\\TranslatedException' => __DIR__ . '/../..' . '/lib/Exception/TranslatedException.php',
         'PrivateBin\\Filter' => __DIR__ . '/../..' . '/lib/Filter.php',
         'PrivateBin\\FormatV2' => __DIR__ . '/../..' . '/lib/FormatV2.php',
         'PrivateBin\\I18n' => __DIR__ . '/../..' . '/lib/I18n.php',
@@ -139,7 +141,6 @@ class ComposerStaticInitDontChange
         'PrivateBin\\Proxy\\YourlsProxy' => __DIR__ . '/../..' . '/lib/Proxy/YourlsProxy.php',
         'PrivateBin\\Request' => __DIR__ . '/../..' . '/lib/Request.php',
         'PrivateBin\\TemplateSwitcher' => __DIR__ . '/../..' . '/lib/TemplateSwitcher.php',
-        'PrivateBin\\TranslatedException' => __DIR__ . '/../..' . '/lib/TranslatedException.php',
         'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php',
         'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php',
         'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',

+ 2 - 2
vendor/composer/installed.php

@@ -3,7 +3,7 @@
         'name' => 'privatebin/privatebin',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'a051c4bd6b71fd56f0d7fc235e815dafc8eb54ea',
+        'reference' => '8eb39b4ffa8daed3d8ff0279c9870ee5b6b0bd95',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -31,7 +31,7 @@
         'privatebin/privatebin' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'a051c4bd6b71fd56f0d7fc235e815dafc8eb54ea',
+            'reference' => '8eb39b4ffa8daed3d8ff0279c9870ee5b6b0bd95',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),