'
- * @param containerClass (string) - Class applied to the status container.
- * Default: 'limit-text-status' (no definition)
- */
-
-
-+function ($) {
-
- 'use strict';
-
- /**
- * LimitText Class Definition
- */
- var LimitText = function (element, options) {
- this.options = options;
- this.$element = $(element);
- // if an existing container is not defined, then a default will be created
- this.$status = (this.options.statusMessage.length) ?
- $(this.options.statusMessage) : $(this.options.containerElement);
-
- // Add classes to the status container, and insert base text
- this.$status
- .addClass(this.options.containerClass + ' ' + this.options.counterClass)
- .append('
');
-
- // reference not available til we've appended the html snippet
- this.$count = $('strong', this.$status);
-
- // insert the default message container if one isn't already defined
- if (!this.options.statusMessage.length) this.$element.after(this.$status);
-
- // set our event handler and proxy it to properly set the context
- this.$element.on('input.limitText.data-api propertychange.limitText.data-api', $.proxy(this.checkCount, this));
-
- // and run initial check of current value
- this.checkCount();
- };
-
- LimitText.VERSION = '0.0.1';
- LimitText.NAME = 'limitText';
-
- LimitText.DEFAULTS = {
- limit: 200,
- warningLimit: 10,
- statusMessage: '',
- // These two are Bootstrap text emphasis classes
- // that you can override in the config, or roll
- // your own of the same name
- counterClass: 'text-primary',
- warningClass: 'text-danger',
- // The default container element is only used if an
- // existing container (statusMessage) is not defined
- containerElement: '
',
- containerClass: 'limit-text-status'
- };
-
- LimitText.prototype.checkCount = function () {
- var currVal = this.$element.val();
-
- if (currVal.length > this.options.limit) {
- // reset the currVal, so that it stays within the limit
- currVal = currVal.substr(0, this.options.limit - 1);
- this.$element.val(currVal);
- }
-
- var remaining = this.options.limit - currVal.length;
-
- this.$count.html(remaining);
-
- if (remaining <= this.options.warningLimit) {
- this.$status.removeClass(this.options.counterClass).addClass(this.options.warningClass);
- } else {
- this.$status.removeClass(this.options.warningClass).addClass(this.options.counterClass);
- }
- };
-
- LimitText.prototype.destroy = function () {
- $.removeData(this.$element[0], 'limitText');
-
- // remove the inserted status container
- if (!this.options.statusMessage.length) {
- this.$status.remove();
- } else {
- this.$status
- .removeClass(
- this.options.containerClass + ' ' +
- this.options.counterClass + ' ' +
- this.options.warningClass)
- .empty();
- }
-
- this.$element.off('input.limitText.data-api propertychange.limitText.data-api');
- this.$element = null;
- };
-
- // limitText Plugin Definition
-
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this),
- data = $this.data('limitText'),
- options = $.extend({}, LimitText.DEFAULTS, $this.data(), typeof option == 'object' && option);
-
- if (!data) $this.data('limitText', (data = new LimitText(this, options)));
- if (typeof option == 'string') data[option]();
- });
- }
-
- var old = $.fn.limitText;
-
- $.fn.limitText = Plugin;
- $.fn.limitText.Constructor = LimitText;
-
- // limitText No Conflict
-
- $.fn.limitText.noConflict = function () {
- $.fn.limitText = old;
- return this;
- };
-
-}(jQuery);
\ No newline at end of file
diff --git a/assets/jquery.limitText.min.js b/assets/jquery.limitText.min.js
deleted file mode 100644
index 69108bc..0000000
--- a/assets/jquery.limitText.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/* jquery.limitText.js v0.0.1 - (c)2015 Phillip Molaro - MIT Licensed https://github.com/twbs/bootstrap/blob/master/LICENSE */
-+function(t){"use strict";var s=function(s,i){this.options=i,this.$element=t(s),this.$status=t(this.options.statusMessage.length?this.options.statusMessage:this.options.containerElement),this.$status.addClass(this.options.containerClass+" "+this.options.counterClass).append("
"+this.options.limit+" characters remaining"),this.$count=t("strong",this.$status),this.options.statusMessage.length||this.$element.after(this.$status),this.$element.on("input.limitText.data-api propertychange.limitText.data-api",t.proxy(this.checkCount,this)),this.checkCount()};s.VERSION="0.0.1",s.NAME="limitText",s.DEFAULTS={limit:200,warningLimit:10,statusMessage:"",counterClass:"text-primary",warningClass:"text-danger",containerElement:"
",containerClass:"limit-text-status"},s.prototype.checkCount=function(){var t=this.$element.val();t.length>this.options.limit&&(t=t.substr(0,this.options.limit-1),this.$element.val(t));var s=this.options.limit-t.length;this.$count.html(s),s<=this.options.warningLimit?this.$status.removeClass(this.options.counterClass).addClass(this.options.warningClass):this.$status.removeClass(this.options.warningClass).addClass(this.options.counterClass)},s.prototype.destroy=function(){t.removeData(this.$element[0],"limitText"),this.options.statusMessage.length?this.$status.removeClass(this.options.containerClass+" "+this.options.counterClass+" "+this.options.warningClass).empty():this.$status.remove(),this.$element.off("input.limitText.data-api propertychange.limitText.data-api"),this.$element=null};var i=t.fn.limitText;t.fn.limitText=function(i){return this.each(function(){var n=t(this),e=n.data("limitText"),a=t.extend({},s.DEFAULTS,n.data(),"object"==typeof i&&i);e||n.data("limitText",e=new s(this,a)),"string"==typeof i&&e[i]()})},t.fn.limitText.Constructor=s,t.fn.limitText.noConflict=function(){return t.fn.limitText=i,this}}(jQuery);
\ No newline at end of file
diff --git a/babel.config.json b/babel.config.json
new file mode 100644
index 0000000..8aa924d
--- /dev/null
+++ b/babel.config.json
@@ -0,0 +1,3 @@
+{
+ "presets": ["@babel/preset-env"]
+}
\ No newline at end of file
diff --git a/composer.json b/composer.json
index c387c14..8ca0d9d 100644
--- a/composer.json
+++ b/composer.json
@@ -12,9 +12,19 @@
}
],
"require": {
- "scrivo/highlight.php": "v9.18.1.7",
"ext-pdo": "*",
"ext-openssl": "*",
- "erusev/parsedown": "^1.7"
+ "ext-gd": "*",
+ "ext-mbstring": "*",
+ "scrivo/highlight.php": "^9.18",
+ "erusev/parsedown": "^1.7",
+ "illuminate/database": "^9.4",
+ "ext-redis": "*",
+ "erusev/parsedown-extra": "^0.8.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "PonePaste\\": "includes/"
+ }
}
}
diff --git a/composer.lock b/composer.lock
index dbbb333..8b425ec 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,154 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "b4c459c6c247b3748e5ccb0910498fcf",
+ "content-hash": "84a10eea0cdac506e1b208869ec7879e",
"packages": [
+ {
+ "name": "brick/math",
+ "version": "0.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/brick/math.git",
+ "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
+ "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^9.0",
+ "vimeo/psalm": "5.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Brick\\Math\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Arbitrary-precision arithmetic library",
+ "keywords": [
+ "Arbitrary-precision",
+ "BigInteger",
+ "BigRational",
+ "arithmetic",
+ "bigdecimal",
+ "bignum",
+ "brick",
+ "math"
+ ],
+ "support": {
+ "issues": "https://github.com/brick/math/issues",
+ "source": "https://github.com/brick/math/tree/0.11.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/BenMorel",
+ "type": "github"
+ }
+ ],
+ "time": "2023-01-15T23:15:59+00:00"
+ },
+ {
+ "name": "doctrine/inflector",
+ "version": "2.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
+ "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^10",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.3",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "vimeo/psalm": "^4.25"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-20T09:10:12+00:00"
+ },
{
"name": "erusev/parsedown",
"version": "1.7.4",
@@ -57,39 +203,685 @@
"time": "2019-12-30T22:54:17+00:00"
},
{
- "name": "scrivo/highlight.php",
- "version": "v9.18.1.7",
+ "name": "erusev/parsedown-extra",
+ "version": "0.8.1",
"source": {
"type": "git",
- "url": "https://github.com/scrivo/highlight.php.git",
- "reference": "05996fcc61e97978d76ca7d1ac14b65e7cd26f91"
+ "url": "https://github.com/erusev/parsedown-extra.git",
+ "reference": "91ac3ff98f0cea243bdccc688df43810f044dcef"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/05996fcc61e97978d76ca7d1ac14b65e7cd26f91",
- "reference": "05996fcc61e97978d76ca7d1ac14b65e7cd26f91",
+ "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/91ac3ff98f0cea243bdccc688df43810f044dcef",
+ "reference": "91ac3ff98f0cea243bdccc688df43810f044dcef",
+ "shasum": ""
+ },
+ "require": {
+ "erusev/parsedown": "^1.7.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "ParsedownExtra": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Emanuil Rusev",
+ "email": "hello@erusev.com",
+ "homepage": "http://erusev.com"
+ }
+ ],
+ "description": "An extension of Parsedown that adds support for Markdown Extra.",
+ "homepage": "https://github.com/erusev/parsedown-extra",
+ "keywords": [
+ "markdown",
+ "markdown extra",
+ "parsedown",
+ "parser"
+ ],
+ "support": {
+ "issues": "https://github.com/erusev/parsedown-extra/issues",
+ "source": "https://github.com/erusev/parsedown-extra/tree/0.8.x"
+ },
+ "time": "2019-12-30T23:20:37+00:00"
+ },
+ {
+ "name": "illuminate/collections",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/collections.git",
+ "reference": "0168d0e44ea0c4fe5451fe08cde7049b9e9f9741"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/collections/zipball/0168d0e44ea0c4fe5451fe08cde7049b9e9f9741",
+ "reference": "0168d0e44ea0c4fe5451fe08cde7049b9e9f9741",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/conditionable": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/macroable": "^9.0",
+ "php": "^8.0.2"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Required to use the dump method (^6.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Collections package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-02-22T11:32:27+00:00"
+ },
+ {
+ "name": "illuminate/conditionable",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/conditionable.git",
+ "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/conditionable/zipball/bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364",
+ "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Conditionable package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-02-01T21:42:32+00:00"
+ },
+ {
+ "name": "illuminate/container",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/container.git",
+ "reference": "1641dda2d0750b68bb1264a3b37ff3973f2e6265"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/container/zipball/1641dda2d0750b68bb1264a3b37ff3973f2e6265",
+ "reference": "1641dda2d0750b68bb1264a3b37ff3973f2e6265",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/contracts": "^9.0",
+ "php": "^8.0.2",
+ "psr/container": "^1.1.1|^2.0.1"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Container\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Container package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-01-24T16:54:18+00:00"
+ },
+ {
+ "name": "illuminate/contracts",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/contracts.git",
+ "reference": "44f65d723b13823baa02ff69751a5948bde60c22"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/44f65d723b13823baa02ff69751a5948bde60c22",
+ "reference": "44f65d723b13823baa02ff69751a5948bde60c22",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0.2",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/simple-cache": "^1.0|^2.0|^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Contracts\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Contracts package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-02-08T14:36:30+00:00"
+ },
+ {
+ "name": "illuminate/database",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/database.git",
+ "reference": "fc7e9cf5d4c7c4c0a2800c0d345cc9985fb1b040"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/database/zipball/fc7e9cf5d4c7c4c0a2800c0d345cc9985fb1b040",
+ "reference": "fc7e9cf5d4c7c4c0a2800c0d345cc9985fb1b040",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.9.3|^0.10.2|^0.11",
+ "ext-pdo": "*",
+ "illuminate/collections": "^9.0",
+ "illuminate/container": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/macroable": "^9.0",
+ "illuminate/support": "^9.0",
+ "php": "^8.0.2",
+ "symfony/console": "^6.0.9"
+ },
+ "suggest": {
+ "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
+ "ext-filter": "Required to use the Postgres database driver.",
+ "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).",
+ "illuminate/console": "Required to use the database commands (^9.0).",
+ "illuminate/events": "Required to use the observers with Eloquent (^9.0).",
+ "illuminate/filesystem": "Required to use the migrations (^9.0).",
+ "illuminate/pagination": "Required to paginate the result set (^9.0).",
+ "symfony/finder": "Required to use Eloquent model factories (^6.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Database\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Database package.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "database",
+ "laravel",
+ "orm",
+ "sql"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-02-12T20:16:50+00:00"
+ },
+ {
+ "name": "illuminate/macroable",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/macroable.git",
+ "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/macroable/zipball/e3bfaf6401742a9c6abca61b9b10e998e5b6449a",
+ "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Macroable package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2022-08-09T13:29:29+00:00"
+ },
+ {
+ "name": "illuminate/support",
+ "version": "v9.52.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/support.git",
+ "reference": "63dcb4523ccdfc01cdf5be17791f07cc20982a1e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/support/zipball/63dcb4523ccdfc01cdf5be17791f07cc20982a1e",
+ "reference": "63dcb4523ccdfc01cdf5be17791f07cc20982a1e",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/inflector": "^2.0",
+ "ext-ctype": "*",
+ "ext-filter": "*",
+ "ext-mbstring": "*",
+ "illuminate/collections": "^9.0",
+ "illuminate/conditionable": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/macroable": "^9.0",
+ "nesbot/carbon": "^2.62.1",
+ "php": "^8.0.2",
+ "voku/portable-ascii": "^2.0"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "suggest": {
+ "illuminate/filesystem": "Required to use the composer class (^9.0).",
+ "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).",
+ "ramsey/uuid": "Required to use Str::uuid() (^4.7).",
+ "symfony/process": "Required to use the composer class (^6.0).",
+ "symfony/uid": "Required to use Str::ulid() (^6.0).",
+ "symfony/var-dumper": "Required to use the dd function (^6.0).",
+ "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Support package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2023-02-13T16:54:43+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "2.66.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "496712849902241f04902033b0441b269effe001"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001",
+ "reference": "496712849902241f04902033b0441b269effe001",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.1.8 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^2.0 || ^3.1.4",
+ "doctrine/orm": "^2.7",
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "kylekatarnls/multi-tester": "^2.0",
+ "ondrejmirtes/better-reflection": "*",
+ "phpmd/phpmd": "^2.9",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12.99 || ^1.7.14",
+ "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-3.x": "3.x-dev",
+ "dev-master": "2.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbon.nesbot.com/docs",
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon#sponsor",
+ "type": "opencollective"
+ },
+ {
+ "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-29T18:53:47+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
+ {
+ "name": "scrivo/highlight.php",
+ "version": "v9.18.1.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/scrivo/highlight.php.git",
+ "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/850f4b44697a2552e892ffe71490ba2733c2fc6e",
+ "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e",
"shasum": ""
},
"require": {
"ext-json": "*",
- "ext-mbstring": "*",
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7",
"sabberworm/php-css-parser": "^8.3",
- "symfony/finder": "^2.8|^3.4",
- "symfony/var-dumper": "^2.8|^3.4"
+ "symfony/finder": "^2.8|^3.4|^5.4",
+ "symfony/var-dumper": "^2.8|^3.4|^5.4"
+ },
+ "suggest": {
+ "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords"
},
"type": "library",
"autoload": {
+ "files": [
+ "HighlightUtilities/functions.php"
+ ],
"psr-0": {
"Highlight\\": "",
"HighlightUtilities\\": ""
- },
- "files": [
- "HighlightUtilities/functions.php"
- ]
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -130,7 +922,1007 @@
"type": "github"
}
],
- "time": "2021-07-09T00:30:39+00:00"
+ "time": "2022-12-17T21:53:22+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v6.2.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "3e294254f2191762c1d137aed4b94e966965e985"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985",
+ "reference": "3e294254f2191762c1d137aed4b94e966965e985",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.4|^6.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4",
+ "symfony/dotenv": "<5.4",
+ "symfony/event-dispatcher": "<5.4",
+ "symfony/lock": "<5.4",
+ "symfony/process": "<5.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/lock": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/var-dumper": "^5.4|^6.0"
+ },
+ "suggest": {
+ "psr/log": "For using the console logger",
+ "symfony/event-dispatcher": "",
+ "symfony/lock": "",
+ "symfony/process": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v6.2.5"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-01T08:38:09+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3",
+ "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-25T10:21:52+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
+ "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "511a08c03c1960e08a883f4cffcacd219b758354"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
+ "reference": "511a08c03c1960e08a883f4cffcacd219b758354",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+ "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75",
+ "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^2.0"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-25T10:21:52+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v6.2.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0",
+ "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.0"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/intl": "^6.2",
+ "symfony/translation-contracts": "^2.0|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v6.2.5"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-01T08:38:09+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v6.2.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/60556925a703cfbc1581cde3b3f35b0bb0ea904c",
+ "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^2.3|^3.0"
+ },
+ "conflict": {
+ "symfony/config": "<5.4",
+ "symfony/console": "<5.4",
+ "symfony/dependency-injection": "<5.4",
+ "symfony/http-kernel": "<5.4",
+ "symfony/twig-bundle": "<5.4",
+ "symfony/yaml": "<5.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3|3.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^4.13",
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/finder": "^5.4|^6.0",
+ "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
+ "symfony/http-kernel": "^5.4|^6.0",
+ "symfony/intl": "^5.4|^6.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/routing": "^5.4|^6.0",
+ "symfony/service-contracts": "^1.1.2|^2|^3",
+ "symfony/yaml": "^5.4|^6.0"
+ },
+ "suggest": {
+ "nikic/php-parser": "To use PhpAstExtractor",
+ "psr/log-implementation": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v6.2.5"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-05T07:00:27+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "68cce71402305a015f8c1589bfada1280dc64fe7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/68cce71402305a015f8c1589bfada1280dc64fe7",
+ "reference": "68cce71402305a015f8c1589bfada1280dc64fe7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "suggest": {
+ "symfony/translation-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-25T10:21:52+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "b56450eed252f6801410d810c8e1727224ae0743"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743",
+ "reference": "b56450eed252f6801410d810c8e1727224ae0743",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lars Moelleken",
+ "homepage": "http://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
+ "keywords": [
+ "ascii",
+ "clean",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-08T17:03:00+00:00"
}
],
"packages-dev": [],
@@ -141,8 +1933,11 @@
"prefer-lowest": false,
"platform": {
"ext-pdo": "*",
- "ext-openssl": "*"
+ "ext-openssl": "*",
+ "ext-gd": "*",
+ "ext-mbstring": "*",
+ "ext-redis": "*"
},
"platform-dev": [],
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.3.0"
}
diff --git a/config/.htaccess b/config/.htaccess
deleted file mode 100644
index 3418e55..0000000
--- a/config/.htaccess
+++ /dev/null
@@ -1 +0,0 @@
-deny from all
\ No newline at end of file
diff --git a/config/site.example.php b/config/site.example.php
new file mode 100644
index 0000000..3ad1765
--- /dev/null
+++ b/config/site.example.php
@@ -0,0 +1,40 @@
+
+ array (
+ 'title' => 'PonePaste',
+ 'description' => 'PonePaste can store green',
+ 'keywords' => '',
+ 'site_name' => 'PonePaste',
+ 'email' => ''
+ ),
+ 'interface' =>
+ array (
+ 'language' => 'en',
+ 'theme' => 'bulma',
+ ),
+ 'permissions' =>
+ array (
+ 'disable_guest' => false,
+ 'private' => false,
+ ),
+ 'mail' =>
+ array (
+ 'verification' => false,
+ 'smtp_host' => '',
+ 'smtp_port' => '',
+ 'smtp_user' => '',
+ 'socket' => '',
+ 'auth' => '',
+ 'protocol' => '',
+ ),
+ 'captcha' =>
+ array (
+ 'enabled' => true,
+ 'multiple' => false,
+ 'mode' => 'Normal',
+ 'allowed' => 'ABCDEFGHIJKLMNOPQRSTUVYXYZabcdefghijklmnopqrstuvwxyz0123456789',
+ 'colour' => '#000000',
+ ),
+);
\ No newline at end of file
diff --git a/config/site.php b/config/site.php
deleted file mode 100644
index 58c18dc..0000000
--- a/config/site.php
+++ /dev/null
@@ -1,39 +0,0 @@
-
- array(
- 'title' => 'PonePaste',
- 'description' => 'PonePaste can store green',
- 'baseurl' => 'ponepaste.local/',
- 'keywords' => '',
- 'site_name' => 'PonePaste',
- 'email' => '',
- 'google_analytics' => '',
- 'additional_scripts' => 'PonePaste',
- ),
- 'interface' =>
- array(
- 'language' => 'en',
- 'theme' => 'bulma',
- ),
- 'permissions' => [
- 'disable_guest' => false,
- 'private' => false
- ],
- 'mail' => [
- 'verification' => false,
- 'smtp_host' => '',
- 'smtp_port' => '',
- 'smtp_user' => '',
- 'socket' => '',
- 'auth' => '',
- 'protocol' => ''
- ],
- 'captcha' => [
- 'enabled' => true,
- 'multiple' => false,
- 'mode' => 'Normal',
- 'allowed' => 'ABCDEFGHIJKLMNOPQRSTUVYXYZabcdefghijklmnopqrstuvwxyz0123456789',
- 'colour' => '#000000'
- ]
-);
\ No newline at end of file
diff --git a/discover.php b/discover.php
deleted file mode 100644
index 0e9c5c7..0000000
--- a/discover.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/functions.php');
-
-// UTF-8
-header('Content-Type: text/html; charset=utf-8');
-
-function getMonthPopularPastes(DatabaseHandle $conn, int $count) : array {
- $query = $conn->prepare(
- "SELECT pastes.id AS id, title, created_at, updated_at, users.username AS member
- FROM pastes
- INNER JOIN users ON users.id = pastes.user_id
- WHERE MONTH(created_at) = MONTH(NOW()) AND visible = '0' ORDER BY views DESC LIMIT ?");
- $query->execute([$count]);
- return $query->fetchAll();
-}
-
-function getRecentUpdatesPastes(DatabaseHandle $conn, int $count) : array {
- $query = $conn->prepare(
- "SELECT pastes.id AS id, title, created_at, updated_at, users.username AS member
- FROM pastes
- INNER JOIN users ON users.id = pastes.user_id
- WHERE visible = '0' ORDER BY updated_at DESC
- LIMIT ?");
- $query->execute([$count]);
- return $query->fetchAll();
-}
-
-function getRecentCreatedPastes(DatabaseHandle $conn, int $count) : array {
- $query = $conn->prepare("
- SELECT pastes.id, title, created_at, updated_at, users.username AS member
- FROM pastes
- INNER JOIN users ON pastes.user_id = users.id
- WHERE visible = '0'
- ORDER BY created_at DESC
- LIMIT ?");
- $query->execute([$count]);
- return $query->fetchAll();
-}
-
-function getMostViewedPastes(DatabaseHandle $conn, int $count) : array {
- $query = $conn->prepare("
- SELECT pastes.id AS id, title, created_at, updated_at, views, users.username AS member
- FROM pastes INNER JOIN users ON users.id = pastes.user_id
- WHERE visible = '0'
- ORDER BY views DESC
- LIMIT ?
- ");
- $query->execute([$count]);
- return $query->fetchAll();
-}
-
-function getRandomPastes(DatabaseHandle $conn, int $count) : array {
- $query = $conn->prepare("
- SELECT pastes.id, title, created_at, updated_at, views, users.username AS member
- FROM pastes
- INNER JOIN users ON users.id = pastes.user_id
- WHERE visible = '0'
- ORDER BY RAND()
- LIMIT ?");
- $query->execute([$count]);
- return $query->fetchAll();
-}
-
-function transformPasteRow(array $row) : array {
- global $conn;
-
- return [
- 'id' => $row['id'],
- 'title' => $row['title'],
- 'member' => $row['member'],
- 'time' => $row['created_at'],
- 'time_update' => $row['updated_at'],
- 'friendly_update_time' => friendlyDateDifference(new DateTime($row['updated_at']), new DateTime()),
- 'friendly_time' => friendlyDateDifference(new DateTime($row['created_at']), new DateTime()),
- 'tags' => getPasteTags($conn, $row['id'])
- ];
-}
-
-$popular_pastes = array_map('transformPasteRow', getMostViewedPastes($conn, 10));
-$monthly_popular_pastes = array_map('transformPasteRow', getMonthPopularPastes($conn, 10));
-$recent_pastes = array_map('transformPasteRow', getRecentCreatedPastes($conn, 10));
-$updated_pastes = array_map('transformPasteRow', getRecentUpdatesPastes($conn, 10));
-$random_pastes = array_map('transformPasteRow', getRandomPastes($conn, 10));
-
-// Theme
-$p_title = $lang['archive']; // "Pastes Archive";
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/discover.php');
-require_once('theme/' . $default_theme . '/footer.php');
diff --git a/dist/bootstrap-tagsinput-angular.js b/dist/bootstrap-tagsinput-angular.js
deleted file mode 100644
index 7ba1d14..0000000
--- a/dist/bootstrap-tagsinput-angular.js
+++ /dev/null
@@ -1,93 +0,0 @@
-angular.module('bootstrap-tagsinput', [])
- .directive('bootstrapTagsinput', [function () {
-
- function getItemProperty(scope, property) {
- if (!property)
- return undefined;
-
- if (angular.isFunction(scope.$parent[property]))
- return scope.$parent[property];
-
- return function (item) {
- return item[property];
- };
- }
-
- return {
- restrict: 'EA',
- scope: {
- model: '=ngModel'
- },
- template: ' ',
- replace: false,
- link: function (scope, element, attrs) {
- $(function () {
- if (!angular.isArray(scope.model))
- scope.model = [];
-
- var select = $('select', element);
- var typeaheadSourceArray = attrs.typeaheadSource ? attrs.typeaheadSource.split('.') : null;
- var typeaheadSource = typeaheadSourceArray ?
- (typeaheadSourceArray.length > 1 ?
- scope.$parent[typeaheadSourceArray[0]][typeaheadSourceArray[1]]
- : scope.$parent[typeaheadSourceArray[0]])
- : null;
-
- select.tagsinput(scope.$parent[attrs.options || ''] || {
- typeahead: {
- source: angular.isFunction(typeaheadSource) ? typeaheadSource : null
- },
- itemValue: getItemProperty(scope, attrs.itemvalue),
- itemText: getItemProperty(scope, attrs.itemtext),
- confirmKeys: getItemProperty(scope, attrs.confirmkeys) ? JSON.parse(attrs.confirmkeys) : [13],
- tagClass: angular.isFunction(scope.$parent[attrs.tagclass]) ? scope.$parent[attrs.tagclass] : function (item) {
- return attrs.tagclass;
- }
- });
-
- for (var i = 0; i < scope.model.length; i++) {
- select.tagsinput('add', scope.model[i]);
- }
-
- select.on('itemAdded', function (event) {
- if (scope.model.indexOf(event.item) === -1)
- scope.model.push(event.item);
- });
-
- select.on('itemRemoved', function (event) {
- var idx = scope.model.indexOf(event.item);
- if (idx !== -1)
- scope.model.splice(idx, 1);
- });
-
- // create a shallow copy of model's current state, needed to determine
- // diff when model changes
- var prev = scope.model.slice();
- scope.$watch("model", function () {
- var added = scope.model.filter(function (i) {
- return prev.indexOf(i) === -1;
- }),
- removed = prev.filter(function (i) {
- return scope.model.indexOf(i) === -1;
- }),
- i;
-
- prev = scope.model.slice();
-
- // Remove tags no longer in binded model
- for (i = 0; i < removed.length; i++) {
- select.tagsinput('remove', removed[i]);
- }
-
- // Refresh remaining tags
- select.tagsinput('refresh');
-
- // Add new items in model as tags
- for (i = 0; i < added.length; i++) {
- select.tagsinput('add', added[i]);
- }
- }, true);
- });
- }
- };
- }]);
diff --git a/dist/bootstrap-tagsinput-angular.min.js b/dist/bootstrap-tagsinput-angular.min.js
deleted file mode 100644
index b1372b1..0000000
--- a/dist/bootstrap-tagsinput-angular.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * bootstrap-tagsinput v0.6.1 by Tim Schlechter
- *
- */
-
-angular.module("bootstrap-tagsinput",[]).directive("bootstrapTagsinput",[function(){function a(a,b){return b?angular.isFunction(a.$parent[b])?a.$parent[b]:function(a){return a[b]}:void 0}return{restrict:"EA",scope:{model:"=ngModel"},template:" ",replace:!1,link:function(b,c,d){$(function(){angular.isArray(b.model)||(b.model=[]);var e=$("select",c),f=d.typeaheadSource?d.typeaheadSource.split("."):null,g=f?f.length>1?b.$parent[f[0]][f[1]]:b.$parent[f[0]]:null;e.tagsinput(b.$parent[d.options||""]||{typeahead:{source:angular.isFunction(g)?g:null},itemValue:a(b,d.itemvalue),itemText:a(b,d.itemtext),confirmKeys:a(b,d.confirmkeys)?JSON.parse(d.confirmkeys):[13],tagClass:angular.isFunction(b.$parent[d.tagclass])?b.$parent[d.tagclass]:function(a){return d.tagclass}});for(var h=0;h
');
- this.$input = $('
').appendTo(this.$container);
-
- this.$element.before(this.$container);
-
- this.build(options);
- }
-
- TagsInput.prototype = {
- constructor: TagsInput,
-
- /**
- * Adds the given item as a new tag. Pass true to dontPushVal to prevent
- * updating the elements val()
- */
- add: function (item, dontPushVal, options) {
- var self = this;
-
- if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
- return;
-
- // Ignore falsey values, except false
- if (item !== false && !item)
- return;
-
- // Trim value
- if (typeof item === "string" && self.options.trimValue) {
- item = $.trim(item);
- }
-
- // Throw an error when trying to add an object while the itemValue option was not set
- if (typeof item === "object" && !self.objectItems)
- throw("Can't add objects when itemValue option is not set");
-
- // Ignore strings only containg whitespace
- if (item.toString().match(/^\s*$/))
- return;
-
- // If SELECT but not multiple, remove current tag
- if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
- self.remove(self.itemsArray[0]);
-
- if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
- var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
- var items = item.split(delimiter);
- if (items.length > 1) {
- for (var i = 0; i < items.length; i++) {
- this.add(items[i], true);
- }
-
- if (!dontPushVal)
- self.pushVal();
- return;
- }
- }
-
- var itemValue = self.options.itemValue(item),
- itemText = self.options.itemText(item),
- tagClass = self.options.tagClass(item),
- itemTitle = self.options.itemTitle(item);
-
- // Ignore items allready added
- var existing = $.grep(self.itemsArray, function (item) {
- return self.options.itemValue(item) === itemValue;
- })[0];
- if (existing && !self.options.allowDuplicates) {
- // Invoke onTagExists
- if (self.options.onTagExists) {
- var $existingTag = $(".tag", self.$container).filter(function () {
- return $(this).data("item") === existing;
- });
- self.options.onTagExists(item, $existingTag);
- }
- return;
- }
-
- // if length greater than limit
- if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
- return;
-
- // raise beforeItemAdd arg
- var beforeItemAddEvent = $.Event('beforeItemAdd', {item: item, cancel: false, options: options});
- self.$element.trigger(beforeItemAddEvent);
- if (beforeItemAddEvent.cancel)
- return;
-
- // register item in internal array and map
- self.itemsArray.push(item);
-
- // add a tag element
-
- var $tag = $('
' + htmlEncode(itemText) + ' ');
- $tag.data('item', item);
- self.findInputWrapper().before($tag);
- $tag.after(' ');
-
- // add
if item represents a value not present in one of the
's options
- if (self.isSelect && !$('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element)[0]) {
- var $option = $('
' + htmlEncode(itemText) + ' ');
- $option.data('item', item);
- $option.attr('value', itemValue);
- self.$element.append($option);
- }
-
- if (!dontPushVal)
- self.pushVal();
-
- // Add class when reached maxTags
- if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
- self.$container.addClass('bootstrap-tagsinput-max');
-
- self.$element.trigger($.Event('itemAdded', {item: item, options: options}));
- },
-
- /**
- * Removes the given item. Pass true to dontPushVal to prevent updating the
- * elements val()
- */
- remove: function (item, dontPushVal, options) {
- var self = this;
-
- if (self.objectItems) {
- if (typeof item === "object")
- item = $.grep(self.itemsArray, function (other) {
- return self.options.itemValue(other) == self.options.itemValue(item);
- });
- else
- item = $.grep(self.itemsArray, function (other) {
- return self.options.itemValue(other) == item;
- });
-
- item = item[item.length - 1];
- }
-
- if (item) {
- var beforeItemRemoveEvent = $.Event('beforeItemRemove', {item: item, cancel: false, options: options});
- self.$element.trigger(beforeItemRemoveEvent);
- if (beforeItemRemoveEvent.cancel)
- return;
-
- $('.tag', self.$container).filter(function () {
- return $(this).data('item') === item;
- }).remove();
- $('option', self.$element).filter(function () {
- return $(this).data('item') === item;
- }).remove();
- if ($.inArray(item, self.itemsArray) !== -1)
- self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
- }
-
- if (!dontPushVal)
- self.pushVal();
-
- // Remove class when reached maxTags
- if (self.options.maxTags > self.itemsArray.length)
- self.$container.removeClass('bootstrap-tagsinput-max');
-
- self.$element.trigger($.Event('itemRemoved', {item: item, options: options}));
- },
-
- /**
- * Removes all items
- */
- removeAll: function () {
- var self = this;
-
- $('.tag', self.$container).remove();
- $('option', self.$element).remove();
-
- while (self.itemsArray.length > 0)
- self.itemsArray.pop();
-
- self.pushVal();
- },
-
- /**
- * Refreshes the tags so they match the text/value of their corresponding
- * item.
- */
- refresh: function () {
- var self = this;
- $('.tag', self.$container).each(function () {
- var $tag = $(this),
- item = $tag.data('item'),
- itemValue = self.options.itemValue(item),
- itemText = self.options.itemText(item),
- tagClass = self.options.tagClass(item);
-
- // Update tag's class and inner text
- $tag.attr('class', null);
- $tag.addClass('tag ' + htmlEncode(tagClass));
- $tag.contents().filter(function () {
- return this.nodeType == 3;
- })[0].nodeValue = htmlEncode(itemText);
-
- if (self.isSelect) {
- var option = $('option', self.$element).filter(function () {
- return $(this).data('item') === item;
- });
- option.attr('value', itemValue);
- }
- });
- },
-
- /**
- * Returns the items added as tags
- */
- items: function () {
- return this.itemsArray;
- },
-
- /**
- * Assembly value by retrieving the value of each item, and set it on the
- * element.
- */
- pushVal: function () {
- var self = this,
- val = $.map(self.items(), function (item) {
- return self.options.itemValue(item).toString();
- });
-
- self.$element.val(val, true).trigger('change');
- },
-
- /**
- * Initializes the tags input behaviour on the element
- */
- build: function (options) {
- var self = this;
-
- self.options = $.extend({}, defaultOptions, options);
- // When itemValue is set, freeInput should always be false
- if (self.objectItems)
- self.options.freeInput = false;
-
- makeOptionItemFunction(self.options, 'itemValue');
- makeOptionItemFunction(self.options, 'itemText');
- makeOptionFunction(self.options, 'tagClass');
-
- // Typeahead Bootstrap version 2.3.2
- if (self.options.typeahead) {
- var typeahead = self.options.typeahead || {};
-
- makeOptionFunction(typeahead, 'source');
-
- self.$input.typeahead($.extend({}, typeahead, {
- source: function (query, process) {
- function processItems(items) {
- var texts = [];
-
- for (var i = 0; i < items.length; i++) {
- var text = self.options.itemText(items[i]);
- map[text] = items[i];
- texts.push(text);
- }
- process(texts);
- }
-
- this.map = {};
- var map = this.map,
- data = typeahead.source(query);
-
- if ($.isFunction(data.success)) {
- // support for Angular callbacks
- data.success(processItems);
- } else if ($.isFunction(data.then)) {
- // support for Angular promises
- data.then(processItems);
- } else {
- // support for functions and jquery promises
- $.when(data)
- .then(processItems);
- }
- },
- updater: function (text) {
- self.add(this.map[text]);
- return this.map[text];
- },
- matcher: function (text) {
- return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
- },
- sorter: function (texts) {
- return texts.sort();
- },
- highlighter: function (text) {
- var regex = new RegExp('(' + this.query + ')', 'gi');
- return text.replace(regex, "
$1 ");
- }
- }));
- }
-
- // typeahead.js
- if (self.options.typeaheadjs) {
- var typeaheadConfig = null;
- var typeaheadDatasets = {};
-
- // Determine if main configurations were passed or simply a dataset
- var typeaheadjs = self.options.typeaheadjs;
- if ($.isArray(typeaheadjs)) {
- typeaheadConfig = typeaheadjs[0];
- typeaheadDatasets = typeaheadjs[1];
- } else {
- typeaheadDatasets = typeaheadjs;
- }
-
- self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
- if (typeaheadDatasets.valueKey)
- self.add(datum[typeaheadDatasets.valueKey]);
- else
- self.add(datum);
- self.$input.typeahead('val', '');
- }, self));
- }
-
- self.$container.on('click', $.proxy(function (event) {
- if (!self.$element.attr('disabled')) {
- self.$input.removeAttr('disabled');
- }
- self.$input.focus();
- }, self));
-
- if (self.options.addOnBlur && self.options.freeInput) {
- self.$input.on('focusout', $.proxy(function (event) {
- // HACK: only process on focusout when no typeahead opened, to
- // avoid adding the typeahead text as tag
- if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
- self.add(self.$input.val());
- self.$input.val('');
- }
- }, self));
- }
-
-
- self.$container.on('keydown', 'input', $.proxy(function (event) {
- var $input = $(event.target),
- $inputWrapper = self.findInputWrapper();
-
- if (self.$element.attr('disabled')) {
- self.$input.attr('disabled', 'disabled');
- return;
- }
-
- switch (event.which) {
- // BACKSPACE
- case 8:
- if (doGetCaretPosition($input[0]) === 0) {
- var prev = $inputWrapper.prev();
- if (prev.length) {
- self.remove(prev.data('item'));
- }
- }
- break;
-
- // DELETE
- case 46:
- if (doGetCaretPosition($input[0]) === 0) {
- var next = $inputWrapper.next();
- if (next.length) {
- self.remove(next.data('item'));
- }
- }
- break;
-
- // LEFT ARROW
- case 37:
- // Try to move the input before the previous tag
- var $prevTag = $inputWrapper.prev();
- if ($input.val().length === 0 && $prevTag[0]) {
- $prevTag.before($inputWrapper);
- $input.focus();
- }
- break;
- // RIGHT ARROW
- case 39:
- // Try to move the input after the next tag
- var $nextTag = $inputWrapper.next();
- if ($input.val().length === 0 && $nextTag[0]) {
- $nextTag.after($inputWrapper);
- $input.focus();
- }
- break;
- default:
- // ignore
- }
-
- // Reset internal input's size
- var textLength = $input.val().length,
- wordSpace = Math.ceil(textLength / 5),
- size = textLength + wordSpace + 1;
- $input.attr('size', Math.max(this.inputSize, $input.val().length));
- }, self));
-
- self.$container.on('keypress', 'input', $.proxy(function (event) {
- var $input = $(event.target);
-
- if (self.$element.attr('disabled')) {
- self.$input.attr('disabled', 'disabled');
- return;
- }
-
- var text = $input.val(),
- maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
- if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
- // Only attempt to add a tag if there is data in the field
- if (text.length !== 0) {
- self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
- $input.val('');
- }
-
- // If the field is empty, let the event triggered fire as usual
- if (self.options.cancelConfirmKeysOnEmpty === false) {
- event.preventDefault();
- }
- }
-
- // Reset internal input's size
- var textLength = $input.val().length,
- wordSpace = Math.ceil(textLength / 5),
- size = textLength + wordSpace + 1;
- $input.attr('size', Math.max(this.inputSize, $input.val().length));
- }, self));
-
- // Remove icon clicked
- self.$container.on('click', '[data-role=remove]', $.proxy(function (event) {
- if (self.$element.attr('disabled')) {
- return;
- }
- self.remove($(event.target).closest('.tag').data('item'));
- }, self));
-
- // Only add existing value as tags when using strings as tags
- if (self.options.itemValue === defaultOptions.itemValue) {
- if (self.$element[0].tagName === 'INPUT') {
- self.add(self.$element.val());
- } else {
- $('option', self.$element).each(function () {
- self.add($(this).attr('value'), true);
- });
- }
- }
- },
-
- /**
- * Removes all tagsinput behaviour and unregsiter all event handlers
- */
- destroy: function () {
- var self = this;
-
- // Unbind events
- self.$container.off('keypress', 'input');
- self.$container.off('click', '[role=remove]');
-
- self.$container.remove();
- self.$element.removeData('tagsinput');
- self.$element.show();
- },
-
- /**
- * Sets focus on the tagsinput
- */
- focus: function () {
- this.$input.focus();
- },
-
- /**
- * Returns the internal input element
- */
- input: function () {
- return this.$input;
- },
-
- /**
- * Returns the element which is wrapped around the internal input. This
- * is normally the $container, but typeahead.js moves the $input element.
- */
- findInputWrapper: function () {
- var elt = this.$input[0],
- container = this.$container[0];
- while (elt && elt.parentNode !== container)
- elt = elt.parentNode;
-
- return $(elt);
- }
- };
-
- /**
- * Register JQuery plugin
- */
- $.fn.tagsinput = function (arg1, arg2, arg3) {
- var results = [];
-
- this.each(function () {
- var tagsinput = $(this).data('tagsinput');
- // Initialize a new tags input
- if (!tagsinput) {
- tagsinput = new TagsInput(this, arg1);
- $(this).data('tagsinput', tagsinput);
- results.push(tagsinput);
-
- if (this.tagName === 'SELECT') {
- $('option', $(this)).attr('selected', 'selected');
- }
-
- // Init tags from $(this).val()
- $(this).val($(this).val());
- } else if (!arg1 && !arg2) {
- // tagsinput already exists
- // no function, trying to init
- results.push(tagsinput);
- } else if (tagsinput[arg1] !== undefined) {
- // Invoke function on existing tags input
- if (tagsinput[arg1].length === 3 && arg3 !== undefined) {
- var retVal = tagsinput[arg1](arg2, null, arg3);
- } else {
- var retVal = tagsinput[arg1](arg2);
- }
- if (retVal !== undefined)
- results.push(retVal);
- }
- });
-
- if (typeof arg1 == 'string') {
- // Return the results from the invoked function calls
- return results.length > 1 ? results : results[0];
- } else {
- return results;
- }
- };
-
- $.fn.tagsinput.Constructor = TagsInput;
-
- /**
- * Most options support both a string or number as well as a function as
- * option value. This function makes sure that the option with the given
- * key in the given options is wrapped in a function
- */
- function makeOptionItemFunction(options, key) {
- if (typeof options[key] !== 'function') {
- var propertyName = options[key];
- options[key] = function (item) {
- return item[propertyName];
- };
- }
- }
-
- function makeOptionFunction(options, key) {
- if (typeof options[key] !== 'function') {
- var value = options[key];
- options[key] = function () {
- return value;
- };
- }
- }
-
- /**
- * HtmlEncodes the given value
- */
- var htmlEncodeContainer = $('
');
-
- function htmlEncode(value) {
- if (value) {
- return htmlEncodeContainer.text(value).html();
- } else {
- return '';
- }
- }
-
- /**
- * Returns the position of the caret in the given input field
- * http://flightschool.acylt.com/devnotes/caret-position-woes/
- */
- function doGetCaretPosition(oField) {
- var iCaretPos = 0;
- if (document.selection) {
- oField.focus();
- var oSel = document.selection.createRange();
- oSel.moveStart('character', -oField.value.length);
- iCaretPos = oSel.text.length;
- } else if (oField.selectionStart || oField.selectionStart == '0') {
- iCaretPos = oField.selectionStart;
- }
- return (iCaretPos);
- }
-
- /**
- * Returns boolean indicates whether user has pressed an expected key combination.
- * @param object keyPressEvent: JavaScript event object, refer
- * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
- * @param object lookupList: expected key combinations, as in:
- * [13, {which: 188, shiftKey: true}]
- */
- function keyCombinationInList(keyPressEvent, lookupList) {
- var found = false;
- $.each(lookupList, function (index, keyCombination) {
- if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
- found = true;
- return false;
- }
-
- if (keyPressEvent.which === keyCombination.which) {
- var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
- shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
- ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
- if (alt && shift && ctrl) {
- found = true;
- return false;
- }
- }
- });
-
- return found;
- }
-
- /**
- * Initialize tagsinput behaviour on inputs and selects which have
- * data-role=tagsinput
- */
- $(function () {
- $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
- });
-})(window.jQuery);
diff --git a/dist/bootstrap-tagsinput.less b/dist/bootstrap-tagsinput.less
deleted file mode 100644
index d5f03f8..0000000
--- a/dist/bootstrap-tagsinput.less
+++ /dev/null
@@ -1,53 +0,0 @@
-.bootstrap-tagsinput {
- background-color: #fff;
- border: 1px solid #ccc;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- display: inline-block;
- padding: 4px 6px;
- margin-bottom: 10px;
- color: #555;
- vertical-align: middle;
- border-radius: 4px;
- max-width: 100%;
- line-height: 22px;
- cursor: text;
-
- input {
- border: none;
- box-shadow: none;
- outline: none;
- background-color: transparent;
- padding: 0;
- margin: 0;
- width: auto !important;
- max-width: inherit;
-
- &:focus {
- border: none;
- box-shadow: none;
- }
- }
-
- .tag {
- margin-right: 2px;
- color: white;
-
- [data-role="remove"] {
- margin-left: 8px;
- cursor: pointer;
-
- &:after {
- content: "x";
- padding: 0px 2px;
- }
-
- &:hover {
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-
- &:active {
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- }
- }
- }
- }
-}
diff --git a/dist/bootstrap-tagsinput.min.js b/dist/bootstrap-tagsinput.min.js
deleted file mode 100644
index 3adbfd9..0000000
--- a/dist/bootstrap-tagsinput.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * bootstrap-tagsinput v0.6.1 by Tim Schlechter
- *
- */
-
-!function(a){"use strict";function b(b,c){this.itemsArray=[],this.$element=a(b),this.$element.hide(),this.isSelect="SELECT"===b.tagName,this.multiple=this.isSelect&&b.hasAttribute("multiple"),this.objectItems=c&&c.itemValue,this.placeholderText=b.hasAttribute("placeholder")?this.$element.attr("placeholder"):"",this.inputSize=Math.max(1,this.placeholderText.length),this.$container=a('
'),this.$input=a('
').appendTo(this.$container),this.$element.before(this.$container),this.build(c)}function c(a,b){if("function"!=typeof a[b]){var c=a[b];a[b]=function(a){return a[c]}}}function d(a,b){if("function"!=typeof a[b]){var c=a[b];a[b]=function(){return c}}}function e(a){return a?i.text(a).html():""}function f(a){var b=0;if(document.selection){a.focus();var c=document.selection.createRange();c.moveStart("character",-a.value.length),b=c.text.length}else(a.selectionStart||"0"==a.selectionStart)&&(b=a.selectionStart);return b}function g(b,c){var d=!1;return a.each(c,function(a,c){if("number"==typeof c&&b.which===c)return d=!0,!1;if(b.which===c.which){var e=!c.hasOwnProperty("altKey")||b.altKey===c.altKey,f=!c.hasOwnProperty("shiftKey")||b.shiftKey===c.shiftKey,g=!c.hasOwnProperty("ctrlKey")||b.ctrlKey===c.ctrlKey;if(e&&f&&g)return d=!0,!1}}),d}var h={tagClass:function(a){return"label label-info"},itemValue:function(a){return a?a.toString():a},itemText:function(a){return this.itemValue(a)},itemTitle:function(a){return null},freeInput:!0,addOnBlur:!0,maxTags:void 0,maxChars:void 0,confirmKeys:[13,44],delimiter:",",delimiterRegex:null,cancelConfirmKeysOnEmpty:!0,onTagExists:function(a,b){b.hide().fadeIn()},trimValue:!1,allowDuplicates:!1};b.prototype={constructor:b,add:function(b,c,d){var f=this;if(!(f.options.maxTags&&f.itemsArray.length>=f.options.maxTags)&&(b===!1||b)){if("string"==typeof b&&f.options.trimValue&&(b=a.trim(b)),"object"==typeof b&&!f.objectItems)throw"Can't add objects when itemValue option is not set";if(!b.toString().match(/^\s*$/)){if(f.isSelect&&!f.multiple&&f.itemsArray.length>0&&f.remove(f.itemsArray[0]),"string"==typeof b&&"INPUT"===this.$element[0].tagName){var g=f.options.delimiterRegex?f.options.delimiterRegex:f.options.delimiter,h=b.split(g);if(h.length>1){for(var i=0;i
f.options.maxInputLength)){var o=a.Event("beforeItemAdd",{item:b,cancel:!1,options:d});if(f.$element.trigger(o),!o.cancel){f.itemsArray.push(b);var p=a(''+e(k)+' ');if(p.data("item",b),f.findInputWrapper().before(p),p.after(" "),f.isSelect&&!a('option[value="'+encodeURIComponent(j)+'"]',f.$element)[0]){var q=a(""+e(k)+" ");q.data("item",b),q.attr("value",j),f.$element.append(q)}c||f.pushVal(),(f.options.maxTags===f.itemsArray.length||f.items().toString().length===f.options.maxInputLength)&&f.$container.addClass("bootstrap-tagsinput-max"),f.$element.trigger(a.Event("itemAdded",{item:b,options:d}))}}}else if(f.options.onTagExists){var r=a(".tag",f.$container).filter(function(){return a(this).data("item")===n});f.options.onTagExists(b,r)}}}},remove:function(b,c,d){var e=this;if(e.objectItems&&(b="object"==typeof b?a.grep(e.itemsArray,function(a){return e.options.itemValue(a)==e.options.itemValue(b)}):a.grep(e.itemsArray,function(a){return e.options.itemValue(a)==b}),b=b[b.length-1]),b){var f=a.Event("beforeItemRemove",{item:b,cancel:!1,options:d});if(e.$element.trigger(f),f.cancel)return;a(".tag",e.$container).filter(function(){return a(this).data("item")===b}).remove(),a("option",e.$element).filter(function(){return a(this).data("item")===b}).remove(),-1!==a.inArray(b,e.itemsArray)&&e.itemsArray.splice(a.inArray(b,e.itemsArray),1)}c||e.pushVal(),e.options.maxTags>e.itemsArray.length&&e.$container.removeClass("bootstrap-tagsinput-max"),e.$element.trigger(a.Event("itemRemoved",{item:b,options:d}))},removeAll:function(){var b=this;for(a(".tag",b.$container).remove(),a("option",b.$element).remove();b.itemsArray.length>0;)b.itemsArray.pop();b.pushVal()},refresh:function(){var b=this;a(".tag",b.$container).each(function(){var c=a(this),d=c.data("item"),f=b.options.itemValue(d),g=b.options.itemText(d),h=b.options.tagClass(d);if(c.attr("class",null),c.addClass("tag "+e(h)),c.contents().filter(function(){return 3==this.nodeType})[0].nodeValue=e(g),b.isSelect){var i=a("option",b.$element).filter(function(){return a(this).data("item")===d});i.attr("value",f)}})},items:function(){return this.itemsArray},pushVal:function(){var b=this,c=a.map(b.items(),function(a){return b.options.itemValue(a).toString()});b.$element.val(c,!0).trigger("change")},build:function(b){var e=this;if(e.options=a.extend({},h,b),e.objectItems&&(e.options.freeInput=!1),c(e.options,"itemValue"),c(e.options,"itemText"),d(e.options,"tagClass"),e.options.typeahead){var i=e.options.typeahead||{};d(i,"source"),e.$input.typeahead(a.extend({},i,{source:function(b,c){function d(a){for(var b=[],d=0;d$1")}}))}if(e.options.typeaheadjs){var j=null,k={},l=e.options.typeaheadjs;a.isArray(l)?(j=l[0],k=l[1]):k=l,e.$input.typeahead(j,k).on("typeahead:selected",a.proxy(function(a,b){k.valueKey?e.add(b[k.valueKey]):e.add(b),e.$input.typeahead("val","")},e))}e.$container.on("click",a.proxy(function(a){e.$element.attr("disabled")||e.$input.removeAttr("disabled"),e.$input.focus()},e)),e.options.addOnBlur&&e.options.freeInput&&e.$input.on("focusout",a.proxy(function(b){0===a(".typeahead, .twitter-typeahead",e.$container).length&&(e.add(e.$input.val()),e.$input.val(""))},e)),e.$container.on("keydown","input",a.proxy(function(b){var c=a(b.target),d=e.findInputWrapper();if(e.$element.attr("disabled"))return void e.$input.attr("disabled","disabled");switch(b.which){case 8:if(0===f(c[0])){var g=d.prev();g.length&&e.remove(g.data("item"))}break;case 46:if(0===f(c[0])){var h=d.next();h.length&&e.remove(h.data("item"))}break;case 37:var i=d.prev();0===c.val().length&&i[0]&&(i.before(d),c.focus());break;case 39:var j=d.next();0===c.val().length&&j[0]&&(j.after(d),c.focus())}var k=c.val().length;Math.ceil(k/5);c.attr("size",Math.max(this.inputSize,c.val().length))},e)),e.$container.on("keypress","input",a.proxy(function(b){var c=a(b.target);if(e.$element.attr("disabled"))return void e.$input.attr("disabled","disabled");var d=c.val(),f=e.options.maxChars&&d.length>=e.options.maxChars;e.options.freeInput&&(g(b,e.options.confirmKeys)||f)&&(0!==d.length&&(e.add(f?d.substr(0,e.options.maxChars):d),c.val("")),e.options.cancelConfirmKeysOnEmpty===!1&&b.preventDefault());var h=c.val().length;Math.ceil(h/5);c.attr("size",Math.max(this.inputSize,c.val().length))},e)),e.$container.on("click","[data-role=remove]",a.proxy(function(b){e.$element.attr("disabled")||e.remove(a(b.target).closest(".tag").data("item"))},e)),e.options.itemValue===h.itemValue&&("INPUT"===e.$element[0].tagName?e.add(e.$element.val()):a("option",e.$element).each(function(){e.add(a(this).attr("value"),!0)}))},destroy:function(){var a=this;a.$container.off("keypress","input"),a.$container.off("click","[role=remove]"),a.$container.remove(),a.$element.removeData("tagsinput"),a.$element.show()},focus:function(){this.$input.focus()},input:function(){return this.$input},findInputWrapper:function(){for(var b=this.$input[0],c=this.$container[0];b&&b.parentNode!==c;)b=b.parentNode;return a(b)}},a.fn.tagsinput=function(c,d,e){var f=[];return this.each(function(){var g=a(this).data("tagsinput");if(g)if(c||d){if(void 0!==g[c]){if(3===g[c].length&&void 0!==e)var h=g[c](d,null,e);else var h=g[c](d);void 0!==h&&f.push(h)}}else f.push(g);else g=new b(this,c),a(this).data("tagsinput",g),f.push(g),"SELECT"===this.tagName&&a("option",a(this)).attr("selected","selected"),a(this).val(a(this).val())}),"string"==typeof c?f.length>1?f:f[0]:f},a.fn.tagsinput.Constructor=b;var i=a("
");a(function(){a("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput()})}(window.jQuery);
-//# sourceMappingURL=bootstrap-tagsinput.min.js.map
\ No newline at end of file
diff --git a/dist/bootstrap-tagsinput.min.js.map b/dist/bootstrap-tagsinput.min.js.map
deleted file mode 100644
index c3e12e1..0000000
--- a/dist/bootstrap-tagsinput.min.js.map
+++ /dev/null
@@ -1,194 +0,0 @@
-{
- "version": 3,
- "sources": [
- "../src/bootstrap-tagsinput.js"
- ],
- "names": [
- "$",
- "TagsInput",
- "element",
- "options",
- "this",
- "itemsArray",
- "$element",
- "hide",
- "isSelect",
- "tagName",
- "multiple",
- "hasAttribute",
- "objectItems",
- "itemValue",
- "placeholderText",
- "attr",
- "inputSize",
- "Math",
- "max",
- "length",
- "$container",
- "$input",
- "appendTo",
- "before",
- "build",
- "makeOptionItemFunction",
- "key",
- "propertyName",
- "item",
- "makeOptionFunction",
- "value",
- "htmlEncode",
- "htmlEncodeContainer",
- "text",
- "html",
- "doGetCaretPosition",
- "oField",
- "iCaretPos",
- "document",
- "selection",
- "focus",
- "oSel",
- "createRange",
- "moveStart",
- "selectionStart",
- "keyCombinationInList",
- "keyPressEvent",
- "lookupList",
- "found",
- "each",
- "index",
- "keyCombination",
- "which",
- "alt",
- "hasOwnProperty",
- "altKey",
- "shift",
- "shiftKey",
- "ctrl",
- "ctrlKey",
- "defaultOptions",
- "tagClass",
- "toString",
- "itemText",
- "itemTitle",
- "freeInput",
- "addOnBlur",
- "maxTags",
- "undefined",
- "maxChars",
- "confirmKeys",
- "delimiter",
- "delimiterRegex",
- "cancelConfirmKeysOnEmpty",
- "onTagExists",
- "$tag",
- "fadeIn",
- "trimValue",
- "allowDuplicates",
- "prototype",
- "constructor",
- "add",
- "dontPushVal",
- "self",
- "trim",
- "match",
- "remove",
- "items",
- "split",
- "i",
- "pushVal",
- "existing",
- "grep",
- "maxInputLength",
- "beforeItemAddEvent",
- "Event",
- "cancel",
- "trigger",
- "push",
- "data",
- "findInputWrapper",
- "after",
- "encodeURIComponent",
- "$option",
- "append",
- "addClass",
- "$existingTag",
- "filter",
- "other",
- "beforeItemRemoveEvent",
- "inArray",
- "splice",
- "removeClass",
- "removeAll",
- "pop",
- "refresh",
- "contents",
- "nodeType",
- "nodeValue",
- "option",
- "val",
- "map",
- "extend",
- "typeahead",
- "source",
- "query",
- "process",
- "processItems",
- "texts",
- "isFunction",
- "success",
- "then",
- "when",
- "updater",
- "matcher",
- "toLowerCase",
- "indexOf",
- "sorter",
- "sort",
- "highlighter",
- "regex",
- "RegExp",
- "replace",
- "typeaheadjs",
- "typeaheadConfig",
- "typeaheadDatasets",
- "isArray",
- "on",
- "proxy",
- "obj",
- "datum",
- "valueKey",
- "event",
- "removeAttr",
- "target",
- "$inputWrapper",
- "prev",
- "next",
- "$prevTag",
- "$nextTag",
- "textLength",
- "ceil",
- "maxLengthReached",
- "substr",
- "preventDefault",
- "closest",
- "destroy",
- "off",
- "removeData",
- "show",
- "input",
- "elt",
- "container",
- "parentNode",
- "fn",
- "tagsinput",
- "arg1",
- "arg2",
- "arg3",
- "results",
- "retVal",
- "Constructor",
- "window",
- "jQuery"
- ],
- "mappings": ";;;;;CAAA,SAAWA,GACT,YAiCA,SAASC,GAAUC,EAASC,GAC1BC,KAAKC,cAELD,KAAKE,SAAWN,EAAEE,GAClBE,KAAKE,SAASC,OAEdH,KAAKI,SAAgC,WAApBN,EAAQO,QACzBL,KAAKM,SAAYN,KAAKI,UAAYN,EAAQS,aAAa,YACvDP,KAAKQ,YAAcT,GAAWA,EAAQU,UACtCT,KAAKU,gBAAkBZ,EAAQS,aAAa,eAAiBP,KAAKE,SAASS,KAAK,eAAiB,GACjGX,KAAKY,UAAYC,KAAKC,IAAI,EAAGd,KAAKU,gBAAgBK,QAElDf,KAAKgB,WAAapB,EAAE,2CACpBI,KAAKiB,OAASrB,EAAE,mCAAqCI,KAAKU,gBAAkB,OAAOQ,SAASlB,KAAKgB,YAEjGhB,KAAKE,SAASiB,OAAOnB,KAAKgB,YAE1BhB,KAAKoB,MAAMrB,GAqgBb,QAASsB,GAAuBtB,EAASuB,GACvC,GAA4B,kBAAjBvB,GAAQuB,GAAqB,CACtC,GAAIC,GAAexB,EAAQuB,EAC3BvB,GAAQuB,GAAO,SAASE,GAAQ,MAAOA,GAAKD,KAGhD,QAASE,GAAmB1B,EAASuB,GACnC,GAA4B,kBAAjBvB,GAAQuB,GAAqB,CACtC,GAAII,GAAQ3B,EAAQuB,EACpBvB,GAAQuB,GAAO,WAAa,MAAOI,KAOvC,QAASC,GAAWD,GAClB,MAAIA,GACKE,EAAoBC,KAAKH,GAAOI,OAEhC,GAQX,QAASC,GAAmBC,GAC1B,GAAIC,GAAY,CAChB,IAAIC,SAASC,UAAW,CACtBH,EAAOI,OACP,IAAIC,GAAOH,SAASC,UAAUG,aAC9BD,GAAKE,UAAW,aAAcP,EAAON,MAAMX,QAC3CkB,EAAYI,EAAKR,KAAKd,YACbiB,EAAOQ,gBAA2C,KAAzBR,EAAOQ,kBACzCP,EAAYD,EAAOQ,eAErB,OAAO,GAUT,QAASC,GAAqBC,EAAeC,GACzC,GAAIC,IAAQ,CAkBZ,OAjBAhD,GAAEiD,KAAKF,EAAY,SAAUG,EAAOC,GAChC,GAAgC,gBAArB,IAAiCL,EAAcM,QAAUD,EAEhE,MADAH,IAAQ,GACD,CAGX,IAAIF,EAAcM,QAAUD,EAAeC,MAAO,CAC9C,GAAIC,IAAOF,EAAeG,eAAe,WAAaR,EAAcS,SAAWJ,EAAeI,OAC1FC,GAASL,EAAeG,eAAe,aAAeR,EAAcW,WAAaN,EAAeM,SAChGC,GAAQP,EAAeG,eAAe,YAAcR,EAAca,UAAYR,EAAeQ,OACjG,IAAIN,GAAOG,GAASE,EAEhB,MADAV,IAAQ,GACD,KAKZA,EAxnBX,GAAIY,IACFC,SAAU,SAASjC,GACjB,MAAO,oBAETf,UAAW,SAASe,GAClB,MAAOA,GAAOA,EAAKkC,WAAalC,GAElCmC,SAAU,SAASnC,GACjB,MAAOxB,MAAKS,UAAUe,IAExBoC,UAAW,SAASpC,GAClB,MAAO,OAETqC,WAAW,EACXC,WAAW,EACXC,QAASC,OACTC,SAAUD,OACVE,aAAc,GAAI,IAClBC,UAAW,IACXC,eAAgB,KAChBC,0BAA0B,EAC1BC,YAAa,SAAS9C,EAAM+C,GAC1BA,EAAKpE,OAAOqE,UAEdC,WAAW,EACXC,iBAAiB,EA0BnB7E,GAAU8E,WACRC,YAAa/E,EAMbgF,IAAK,SAASrD,EAAMsD,EAAa/E,GAC/B,GAAIgF,GAAO/E,IAEX,MAAI+E,EAAKhF,QAAQgE,SAAWgB,EAAK9E,WAAWc,QAAUgE,EAAKhF,QAAQgE,WAI/DvC,KAAS,GAAUA,GAAvB,CASA,GALoB,gBAATA,IAAqBuD,EAAKhF,QAAQ0E,YAC3CjD,EAAO5B,EAAEoF,KAAKxD,IAII,gBAATA,KAAsBuD,EAAKvE,YACpC,KAAK,oDAGP,KAAIgB,EAAKkC,WAAWuB,MAAM,SAA1B,CAOA,GAHIF,EAAK3E,WAAa2E,EAAKzE,UAAYyE,EAAK9E,WAAWc,OAAS,GAC9DgE,EAAKG,OAAOH,EAAK9E,WAAW,IAEV,gBAATuB,IAAkD,UAA7BxB,KAAKE,SAAS,GAAGG,QAAqB,CACpE,GAAI8D,GAAaY,EAAKhF,QAAsB,eAAIgF,EAAKhF,QAAQqE,eAAiBW,EAAKhF,QAAQoE,UACvFgB,EAAQ3D,EAAK4D,MAAMjB,EACvB,IAAIgB,EAAMpE,OAAS,EAAG,CACpB,IAAK,GAAIsE,GAAI,EAAGA,EAAIF,EAAMpE,OAAQsE,IAChCrF,KAAK6E,IAAIM,EAAME,IAAI,EAKrB,aAFKP,GACHC,EAAKO,YAKX,GAAI7E,GAAYsE,EAAKhF,QAAQU,UAAUe,GACnCmC,EAAWoB,EAAKhF,QAAQ4D,SAASnC,GACjCiC,EAAWsB,EAAKhF,QAAQ0D,SAASjC,GACjCoC,EAAYmB,EAAKhF,QAAQ6D,UAAUpC,GAGnC+D,EAAW3F,EAAE4F,KAAKT,EAAK9E,WAAY,SAASuB,GAAQ,MAAOuD,GAAKhF,QAAQU,UAAUe,KAAUf,IAAe,EAC/G,KAAI8E,GAAaR,EAAKhF,QAAQ2E,iBAU9B,KAAIK,EAAKI,QAAQzB,WAAW3C,OAASS,EAAKT,OAAS,EAAIgE,EAAKhF,QAAQ0F,gBAApE,CAIA,GAAIC,GAAqB9F,EAAE+F,MAAM,iBAAmBnE,KAAMA,EAAMoE,QAAQ,EAAO7F,QAASA,GAExF,IADAgF,EAAK7E,SAAS2F,QAAQH,IAClBA,EAAmBE,OAAvB,CAIAb,EAAK9E,WAAW6F,KAAKtE,EAIrB,IAAI+C,GAAO3E,EAAE,oBAAsB+B,EAAW8B,IAA2B,OAAdG,EAAsB,YAAcA,EAAa,IAAM,KAAOjC,EAAWgC,GAAY,0CAMhJ,IALAY,EAAKwB,KAAK,OAAQvE,GAClBuD,EAAKiB,mBAAmB7E,OAAOoD,GAC/BA,EAAK0B,MAAM,KAGPlB,EAAK3E,WAAaR,EAAE,iBAAmBsG,mBAAmBzF,GAAa,KAAKsE,EAAK7E,UAAU,GAAI,CACjG,GAAIiG,GAAUvG,EAAE,oBAAsB+B,EAAWgC,GAAY,YAC7DwC,GAAQJ,KAAK,OAAQvE,GACrB2E,EAAQxF,KAAK,QAASF,GACtBsE,EAAK7E,SAASkG,OAAOD,GAGlBrB,GACHC,EAAKO,WAGHP,EAAKhF,QAAQgE,UAAYgB,EAAK9E,WAAWc,QAAUgE,EAAKI,QAAQzB,WAAW3C,SAAWgE,EAAKhF,QAAQ0F,iBACrGV,EAAK/D,WAAWqF,SAAS,2BAE3BtB,EAAK7E,SAAS2F,QAAQjG,EAAE+F,MAAM,aAAenE,KAAMA,EAAMzB,QAASA,WA1ChE,IAAIgF,EAAKhF,QAAQuE,YAAa,CAC5B,GAAIgC,GAAe1G,EAAE,OAAQmF,EAAK/D,YAAYuF,OAAO,WAAa,MAAO3G,GAAEI,MAAM+F,KAAK,UAAYR,GAClGR,GAAKhF,QAAQuE,YAAY9C,EAAM8E,OA+CrCpB,OAAQ,SAAS1D,EAAMsD,EAAa/E,GAClC,GAAIgF,GAAO/E,IAWX,IATI+E,EAAKvE,cAELgB,EADkB,gBAATA,GACF5B,EAAE4F,KAAKT,EAAK9E,WAAY,SAASuG,GAAS,MAAOzB,GAAKhF,QAAQU,UAAU+F,IAAWzB,EAAKhF,QAAQU,UAAUe,KAE1G5B,EAAE4F,KAAKT,EAAK9E,WAAY,SAASuG,GAAS,MAAOzB,GAAKhF,QAAQU,UAAU+F,IAAWhF,IAE5FA,EAAOA,EAAKA,EAAKT,OAAO,IAGtBS,EAAM,CACR,GAAIiF,GAAwB7G,EAAE+F,MAAM,oBAAsBnE,KAAMA,EAAMoE,QAAQ,EAAO7F,QAASA,GAE9F,IADAgF,EAAK7E,SAAS2F,QAAQY,GAClBA,EAAsBb,OACxB,MAEFhG,GAAE,OAAQmF,EAAK/D,YAAYuF,OAAO,WAAa,MAAO3G,GAAEI,MAAM+F,KAAK,UAAYvE,IAAS0D,SACxFtF,EAAE,SAAUmF,EAAK7E,UAAUqG,OAAO,WAAa,MAAO3G,GAAEI,MAAM+F,KAAK,UAAYvE,IAAS0D,SAChD,KAArCtF,EAAE8G,QAAQlF,EAAMuD,EAAK9E,aACtB8E,EAAK9E,WAAW0G,OAAO/G,EAAE8G,QAAQlF,EAAMuD,EAAK9E,YAAa,GAGxD6E,GACHC,EAAKO,UAGHP,EAAKhF,QAAQgE,QAAUgB,EAAK9E,WAAWc,QACzCgE,EAAK/D,WAAW4F,YAAY,2BAE9B7B,EAAK7E,SAAS2F,QAAQjG,EAAE+F,MAAM,eAAkBnE,KAAMA,EAAMzB,QAASA,MAMvE8G,UAAW,WACT,GAAI9B,GAAO/E,IAKX,KAHAJ,EAAE,OAAQmF,EAAK/D,YAAYkE,SAC3BtF,EAAE,SAAUmF,EAAK7E,UAAUgF,SAErBH,EAAK9E,WAAWc,OAAS,GAC7BgE,EAAK9E,WAAW6G,KAElB/B,GAAKO,WAOPyB,QAAS,WACP,GAAIhC,GAAO/E,IACXJ,GAAE,OAAQmF,EAAK/D,YAAY6B,KAAK,WAC9B,GAAI0B,GAAO3E,EAAEI,MACTwB,EAAO+C,EAAKwB,KAAK,QACjBtF,EAAYsE,EAAKhF,QAAQU,UAAUe,GACnCmC,EAAWoB,EAAKhF,QAAQ4D,SAASnC,GACjCiC,EAAWsB,EAAKhF,QAAQ0D,SAASjC,EASnC,IANA+C,EAAK5D,KAAK,QAAS,MACnB4D,EAAK8B,SAAS,OAAS1E,EAAW8B,IAClCc,EAAKyC,WAAWT,OAAO,WACrB,MAAwB,IAAjBvG,KAAKiH,WACX,GAAGC,UAAYvF,EAAWgC,GAEzBoB,EAAK3E,SAAU,CACjB,GAAI+G,GAASvH,EAAE,SAAUmF,EAAK7E,UAAUqG,OAAO,WAAa,MAAO3G,GAAEI,MAAM+F,KAAK,UAAYvE,GAC5F2F,GAAOxG,KAAK,QAASF,OAQ7B0E,MAAO,WACL,MAAOnF,MAAKC,YAOdqF,QAAS,WACP,GAAIP,GAAO/E,KACPoH,EAAMxH,EAAEyH,IAAItC,EAAKI,QAAS,SAAS3D,GACjC,MAAOuD,GAAKhF,QAAQU,UAAUe,GAAMkC,YAG1CqB,GAAK7E,SAASkH,IAAIA,GAAK,GAAMvB,QAAQ,WAMvCzE,MAAO,SAASrB,GACd,GAAIgF,GAAO/E,IAYX,IAVA+E,EAAKhF,QAAUH,EAAE0H,UAAW9D,EAAgBzD,GAExCgF,EAAKvE,cACPuE,EAAKhF,QAAQ8D,WAAY,GAE3BxC,EAAuB0D,EAAKhF,QAAS,aACrCsB,EAAuB0D,EAAKhF,QAAS,YACrC0B,EAAmBsD,EAAKhF,QAAS,YAG7BgF,EAAKhF,QAAQwH,UAAW,CAC1B,GAAIA,GAAYxC,EAAKhF,QAAQwH,aAE7B9F,GAAmB8F,EAAW,UAE9BxC,EAAK9D,OAAOsG,UAAU3H,EAAE0H,UAAWC,GACjCC,OAAQ,SAAUC,EAAOC,GACvB,QAASC,GAAaxC,GAGpB,IAAK,GAFDyC,MAEKvC,EAAI,EAAGA,EAAIF,EAAMpE,OAAQsE,IAAK,CACrC,GAAIxD,GAAOkD,EAAKhF,QAAQ4D,SAASwB,EAAME,GACvCgC,GAAIxF,GAAQsD,EAAME,GAClBuC,EAAM9B,KAAKjE,GAEb6F,EAAQE,GAGV5H,KAAKqH,MACL,IAAIA,GAAMrH,KAAKqH,IACXtB,EAAOwB,EAAUC,OAAOC,EAExB7H,GAAEiI,WAAW9B,EAAK+B,SAEpB/B,EAAK+B,QAAQH,GACJ/H,EAAEiI,WAAW9B,EAAKgC,MAE3BhC,EAAKgC,KAAKJ,GAGV/H,EAAEoI,KAAKjC,GACLgC,KAAKJ,IAGXM,QAAS,SAAUpG,GAEjB,MADAkD,GAAKF,IAAI7E,KAAKqH,IAAIxF,IACX7B,KAAKqH,IAAIxF,IAElBqG,QAAS,SAAUrG,GACjB,MAAwE,KAAhEA,EAAKsG,cAAcC,QAAQpI,KAAKyH,MAAMzC,OAAOmD,gBAEvDE,OAAQ,SAAUT,GAChB,MAAOA,GAAMU,QAEfC,YAAa,SAAU1G,GACrB,GAAI2G,GAAQ,GAAIC,QAAQ,IAAMzI,KAAKyH,MAAQ,IAAK,KAChD,OAAO5F,GAAK6G,QAASF,EAAO,2BAMlC,GAAIzD,EAAKhF,QAAQ4I,YAAa,CAC1B,GAAIC,GAAkB,KAClBC,KAGAF,EAAc5D,EAAKhF,QAAQ4I,WAC3B/I,GAAEkJ,QAAQH,IACZC,EAAkBD,EAAY,GAC9BE,EAAoBF,EAAY,IAEhCE,EAAoBF,EAGtB5D,EAAK9D,OAAOsG,UAAUqB,EAAiBC,GAAmBE,GAAG,qBAAsBnJ,EAAEoJ,MAAM,SAAUC,EAAKC,GACpGL,EAAkBM,SACpBpE,EAAKF,IAAIqE,EAAML,EAAkBM,WAEjCpE,EAAKF,IAAIqE,GACXnE,EAAK9D,OAAOsG,UAAU,MAAO,KAC5BxC,IAGPA,EAAK/D,WAAW+H,GAAG,QAASnJ,EAAEoJ,MAAM,SAASI,GACrCrE,EAAK7E,SAASS,KAAK,aACvBoE,EAAK9D,OAAOoI,WAAW,YAEzBtE,EAAK9D,OAAOmB,SACX2C,IAEGA,EAAKhF,QAAQ+D,WAAaiB,EAAKhF,QAAQ8D,WACzCkB,EAAK9D,OAAO8H,GAAG,WAAYnJ,EAAEoJ,MAAM,SAASI,GAG4B,IAAhExJ,EAAE,iCAAkCmF,EAAK/D,YAAYD,SACvDgE,EAAKF,IAAIE,EAAK9D,OAAOmG,OACrBrC,EAAK9D,OAAOmG,IAAI,MAEnBrC,IAIPA,EAAK/D,WAAW+H,GAAG,UAAW,QAASnJ,EAAEoJ,MAAM,SAASI,GACtD,GAAInI,GAASrB,EAAEwJ,EAAME,QACjBC,EAAgBxE,EAAKiB,kBAEzB,IAAIjB,EAAK7E,SAASS,KAAK,YAErB,WADAoE,GAAK9D,OAAON,KAAK,WAAY,WAI/B,QAAQyI,EAAMpG,OAEZ,IAAK,GACH,GAAsC,IAAlCjB,EAAmBd,EAAO,IAAW,CACvC,GAAIuI,GAAOD,EAAcC,MACrBA,GAAKzI,QACPgE,EAAKG,OAAOsE,EAAKzD,KAAK,SAG1B,KAGF,KAAK,IACH,GAAsC,IAAlChE,EAAmBd,EAAO,IAAW,CACvC,GAAIwI,GAAOF,EAAcE,MACrBA,GAAK1I,QACPgE,EAAKG,OAAOuE,EAAK1D,KAAK,SAG1B,KAGF,KAAK,IAEH,GAAI2D,GAAWH,EAAcC,MACD,KAAxBvI,EAAOmG,MAAMrG,QAAgB2I,EAAS,KACxCA,EAASvI,OAAOoI,GAChBtI,EAAOmB,QAET,MAEF,KAAK,IAEH,GAAIuH,GAAWJ,EAAcE,MACD,KAAxBxI,EAAOmG,MAAMrG,QAAgB4I,EAAS,KACxCA,EAAS1D,MAAMsD,GACftI,EAAOmB,SAQb,GAAIwH,GAAa3I,EAAOmG,MAAMrG,MACdF,MAAKgJ,KAAKD,EAAa,EAEvC3I,GAAON,KAAK,OAAQE,KAAKC,IAAId,KAAKY,UAAWK,EAAOmG,MAAMrG,UACzDgE,IAEHA,EAAK/D,WAAW+H,GAAG,WAAY,QAASnJ,EAAEoJ,MAAM,SAASI,GACtD,GAAInI,GAASrB,EAAEwJ,EAAME,OAErB,IAAIvE,EAAK7E,SAASS,KAAK,YAEpB,WADAoE,GAAK9D,OAAON,KAAK,WAAY,WAIhC,IAAIkB,GAAOZ,EAAOmG,MAClB0C,EAAmB/E,EAAKhF,QAAQkE,UAAYpC,EAAKd,QAAUgE,EAAKhF,QAAQkE,QACpEc,GAAKhF,QAAQ8D,YAAcpB,EAAqB2G,EAAOrE,EAAKhF,QAAQmE,cAAgB4F,KAEjE,IAAhBjI,EAAKd,SACNgE,EAAKF,IAAIiF,EAAmBjI,EAAKkI,OAAO,EAAGhF,EAAKhF,QAAQkE,UAAYpC,GACpEZ,EAAOmG,IAAI,KAIVrC,EAAKhF,QAAQsE,4BAA6B,GAC3C+E,EAAMY,iBAKZ,IAAIJ,GAAa3I,EAAOmG,MAAMrG,MACfF,MAAKgJ,KAAKD,EAAa,EAEtC3I,GAAON,KAAK,OAAQE,KAAKC,IAAId,KAAKY,UAAWK,EAAOmG,MAAMrG,UAC1DgE,IAGHA,EAAK/D,WAAW+H,GAAG,QAAS,qBAAsBnJ,EAAEoJ,MAAM,SAASI,GAC7DrE,EAAK7E,SAASS,KAAK,aAGvBoE,EAAKG,OAAOtF,EAAEwJ,EAAME,QAAQW,QAAQ,QAAQlE,KAAK,UAChDhB,IAGCA,EAAKhF,QAAQU,YAAc+C,EAAe/C,YACX,UAA7BsE,EAAK7E,SAAS,GAAGG,QACjB0E,EAAKF,IAAIE,EAAK7E,SAASkH,OAEzBxH,EAAE,SAAUmF,EAAK7E,UAAU2C,KAAK,WAC9BkC,EAAKF,IAAIjF,EAAEI,MAAMW,KAAK,UAAU,OASxCuJ,QAAS,WACP,GAAInF,GAAO/E,IAGX+E,GAAK/D,WAAWmJ,IAAI,WAAY,SAChCpF,EAAK/D,WAAWmJ,IAAI,QAAS,iBAE7BpF,EAAK/D,WAAWkE,SAChBH,EAAK7E,SAASkK,WAAW,aACzBrF,EAAK7E,SAASmK,QAMhBjI,MAAO,WACLpC,KAAKiB,OAAOmB,SAMdkI,MAAO,WACL,MAAOtK,MAAKiB,QAOd+E,iBAAkB,WAGhB,IAFA,GAAIuE,GAAMvK,KAAKiB,OAAO,GAClBuJ,EAAYxK,KAAKgB,WAAW,GAC1BuJ,GAAOA,EAAIE,aAAeD,GAC9BD,EAAMA,EAAIE,UAEZ,OAAO7K,GAAE2K,KAOb3K,EAAE8K,GAAGC,UAAY,SAASC,EAAMC,EAAMC,GACpC,GAAIC,KAgCJ,OA9BA/K,MAAK6C,KAAK,WACR,GAAI8H,GAAY/K,EAAEI,MAAM+F,KAAK,YAE7B,IAAK4E,EAWE,GAAKC,GAASC,GAId,GAAuB7G,SAApB2G,EAAUC,GAAqB,CAEnC,GAA8B,IAA3BD,EAAUC,GAAM7J,QAAyBiD,SAAT8G,EAChC,GAAIE,GAASL,EAAUC,GAAMC,EAAM,KAAMC,OAEzC,IAAIE,GAASL,EAAUC,GAAMC,EAEnB7G,UAAXgH,GACAD,EAAQjF,KAAKkF,QATjBD,GAAQjF,KAAK6E,OAbbA,GAAY,GAAI9K,GAAUG,KAAM4K,GAChChL,EAAEI,MAAM+F,KAAK,YAAa4E,GAC1BI,EAAQjF,KAAK6E,GAEQ,WAAjB3K,KAAKK,SACLT,EAAE,SAAUA,EAAEI,OAAOW,KAAK,WAAY,YAI1Cf,EAAEI,MAAMoH,IAAIxH,EAAEI,MAAMoH,SAiBN,gBAARwD,GAEHG,EAAQhK,OAAS,EAAIgK,EAAUA,EAAQ,GAEvCA,GAIXnL,EAAE8K,GAAGC,UAAUM,YAAcpL,CAsB7B,IAAI+B,GAAsBhC,EAAE,UA2D5BA,GAAE,WACAA,EAAE,qEAAqE+K,eAExEO,OAAOC",
- "file": "bootstrap-tagsinput.min.js"
-}
\ No newline at end of file
diff --git a/dist/bootstrap-tagsinput.zip b/dist/bootstrap-tagsinput.zip
deleted file mode 100644
index 2570b67..0000000
Binary files a/dist/bootstrap-tagsinput.zip and /dev/null differ
diff --git a/doc/nginx.conf b/doc/nginx.conf
new file mode 100644
index 0000000..7e0b4cf
--- /dev/null
+++ b/doc/nginx.conf
@@ -0,0 +1,80 @@
+# Request limit zone to help mitigate attacks
+limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s;
+
+# Cleartext listener for LetsEncrypt and HTTPS redirects.
+server {
+ listen 80;
+
+ server_name ponepaste.org;
+
+ location ^~ /.well-known/acme-challenge/ {
+ root /var/www/letsencrypt/;
+ }
+
+ location / {
+ return 301 https://ponepaste.org$request_uri;
+ }
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ # SSL Configuration
+ ssl_certificate /etc/letsencrypt/live/ponepaste.org/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/ponepaste.org/privkey.pem;
+ add_header Strict-Transport-Security "max-age=63072000" always;
+
+ # ModSecurity config; optional, but recommended.
+ modsecurity on;
+ modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
+
+ root /srv/http/ponepaste.org;
+ index index.html index.php;
+
+ server_name ponepaste.org;
+
+ rewrite ^/$ /index.php last;
+ rewrite ^/([0-9]+)$ /paste.php?id=$1 last;
+ rewrite ^/page/([a-zA-Z0-9]+)/?$ /pages.php?page=$1 last;
+
+ # simple routes that just map to $1.php
+ rewrite ^/(archive|discover|profile|contact|report|event|captcha|login|logout)/?$ /$1.php last;
+
+ # routes for users
+ rewrite ^/user/([^/]+)/?$ /user.php?user=$1 last;
+ rewrite ^/user/([^/]+)/([^/]+)/?$ /user.php?user=$1&q=$2 last;
+
+ # routes for pastes
+ rewrite ^/(download|raw|embed)/(.+)$ /paste.php?$1&id=$2 last;
+
+ # weird registration routes that use a URL parameter rather than a different page (FIXME)
+ rewrite ^/register$ /login.php last;
+ rewrite ^/forgot$ /login.php last;
+
+
+ location ~* \.(jpg|jpeg|png|gif|ico|css|js) {
+ add_header "Cache-Control" "public";
+ expires 1h;
+ }
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+
+ location ~ \.php$ {
+ limit_req zone=ip burst=10 delay=8;
+ include snippets/fastcgi-php.conf;
+
+ fastcgi_pass unix:/run/php/php-fpm.sock;
+ }
+
+ # Deny directories that should not be publicly accessible.
+ location ~ (/doc|/tmp|/includes|/config|/.git|/.ht|/js|/node_modules|/composer).* {
+ deny all;
+ }
+
+ location ~ /\.ht {
+ deny all;
+ }
+}
diff --git a/event.php b/event.php
deleted file mode 100644
index 3fa02b0..0000000
--- a/event.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/functions.php');
-
-// UTF-8
-header('Content-Type: text/html; charset=utf-8');
-
-$date = date('jS F Y');
-$ip = $_SERVER['REMOTE_ADDR'];
-
-$p_title = $lang['archive']; // "Pastes Archive";
-
-// Theme
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/event.php');
-require_once('theme/' . $default_theme . '/footer.php');
diff --git a/fav.php b/fav.php
deleted file mode 100644
index cb89ef1..0000000
--- a/fav.php
+++ /dev/null
@@ -1,26 +0,0 @@
-prepare('SELECT 1 FROM pins WHERE paste_id = ? AND user_id = ?');
- $query->execute([$paste_id, $current_user->user_id]);
-
- if ($query->fetch()) { /* Already favorited */
- $query = $conn->prepare('DELETE FROM pins WHERE paste_id = ? AND user_id = ?');
- } else {
- $query = $conn->prepare('INSERT INTO pins (paste_id, user_id, f_time) VALUES (?, ?, NOW())');
- }
-
- $query->execute([$paste_id, $current_user->user_id]);
- $error = 'Paste has been favorited.';
-}
-
-// Theme
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/report.php');
diff --git a/includes/DatabaseHandle.class.php b/includes/DatabaseHandle.class.php
deleted file mode 100644
index 35d86c8..0000000
--- a/includes/DatabaseHandle.class.php
+++ /dev/null
@@ -1,44 +0,0 @@
-conn = new PDO($conString, $username, $password, [
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- PDO::ATTR_EMULATE_PREPARES => false
- ]);
- }
-
- public function prepare(string $query) : PDOStatement {
- return $this->conn->prepare($query);
- }
-
- public function query(string $query, array $params = null) : PDOStatement {
- if (empty($params)) {
- return $this->conn->query($query);
- }
-
- $stmt = $this->conn->prepare($query);
- $stmt->execute($params);
-
- return $stmt;
- }
-
- public function querySelectOne(string $query, array $params = null, int $fetchMode = PDO::FETCH_ASSOC) : array|null {
- $stmt = $this->query($query, $params);
-
- if ($row = $stmt->fetch($fetchMode)) {
- return $row;
- }
-
- return null;
- }
-
- public function queryInsert(string $query, array $params = null) : int {
- $this->query($query, $params);
-
- return (int)$this->conn->lastInsertId();
- }
-}
diff --git a/includes/Helpers/AbilityHelper.php b/includes/Helpers/AbilityHelper.php
new file mode 100644
index 0000000..c45c5df
--- /dev/null
+++ b/includes/Helpers/AbilityHelper.php
@@ -0,0 +1,62 @@
+user = $user;
+ $this->setupAllowedActions();
+ }
+
+ public function can(string $action, mixed $subject) : bool {
+ if ($this->user !== null
+ && $this->user->role == User::ROLE_ADMIN) { // Admins can do anything
+ return true;
+ }
+
+ return $this->modelToActions[$subject::class][$action]($this->user, $subject);
+ }
+
+ private function setupAllowedActions() : void {
+ $this->modelToActions['PonePaste\\Models\\Paste'] = [
+ 'view' => function(User | null $user, Paste $paste) {
+ $publicly_visible = ((int) $paste->visible !== Paste::VISIBILITY_PRIVATE) && !$paste->is_hidden;
+
+ return $publicly_visible // Everyone can see public pastes
+ || ($user !== null && $user->id === $paste->user_id) // Creators of pastes can see their own private pastes
+ || $user->role >= User::ROLE_MODERATOR; // Moderators and above can see all pastes
+ },
+ 'edit' => function(User | null $user, Paste $paste) {
+ return $user !== null
+ && $user->id === $paste->user_id; // Creators of non-anonymous pastes can edit their own pastes
+ },
+ 'hide' => function(User | null $user, Paste $paste) {
+ return $user !== null
+ && $user->role >= User::ROLE_MODERATOR; // Moderators and above can hide pastes
+ },
+ 'delete' => function(User | null $user, Paste $paste) {
+ return $user !== null
+ && ($user->id === $paste->user_id // Creators of pastes can delete their own pastes
+ || $user->role >= User::ROLE_ADMIN); // Admins can delete all pastes
+ }
+ ];
+ $this->modelToActions['PonePaste\\Models\\User'] = [
+ 'view' => function(User | null $user, User $subject) {
+ return true; // Everyone can view users
+ },
+ 'edit' => function(User | null $user, User $subject) {
+ return $user !== null
+ && $user->id === $subject->id; // Users can edit their own profiles
+ },
+ 'administrate' => function(User | null $user, User $subject) {
+ return $user !== null
+ && $user->role >= User::ROLE_ADMIN; // Admins can edit all users
+ }
+ ];
+ }
+}
diff --git a/includes/Helpers/SessionHelper.php b/includes/Helpers/SessionHelper.php
new file mode 100644
index 0000000..b1b3e99
--- /dev/null
+++ b/includes/Helpers/SessionHelper.php
@@ -0,0 +1,66 @@
+user_id;
+ return $session;
+ }
+
+ return null;
+ }
+
+ public static function destroySession() : void {
+ $token = $_COOKIE[SessionHelper::REMEMBER_TOKEN_COOKIE];
+
+ UserSession::where('token', $token)->delete();
+
+ unset($_COOKIE[SessionHelper::REMEMBER_TOKEN_COOKIE]);
+ setcookie(SessionHelper::REMEMBER_TOKEN_COOKIE, null, time() - 3600);
+ }
+
+ private static function currentUserFromRememberToken(string $remember_token) {
+ $session = UserSession
+ ::with('user')
+ ->where('token', $remember_token)
+ ->first();
+
+ if (!$session) {
+ return null;
+ }
+
+ $session_expiry = $session->expire_at;
+ $now = new DateTime();
+
+ /* Session is expired (diff is negative) */
+ if ($now->diff($session_expiry)->invert === 1) {
+ $session->delete();
+ return null;
+ }
+
+ return $session->user;
+ }
+
+ private static function currentUserFromPhpSession() {
+ if (empty($_SESSION['user_id'])) {
+ return null;
+ }
+
+ return User::find(intval($_SESSION['user_id']));
+ }
+}
\ No newline at end of file
diff --git a/includes/Models/AdminLog.php b/includes/Models/AdminLog.php
new file mode 100644
index 0000000..f50f797
--- /dev/null
+++ b/includes/Models/AdminLog.php
@@ -0,0 +1,25 @@
+belongsto(User::class);
+ }
+}
diff --git a/includes/Models/Badge.php b/includes/Models/Badge.php
new file mode 100644
index 0000000..0491d7e
--- /dev/null
+++ b/includes/Models/Badge.php
@@ -0,0 +1,13 @@
+ 'integer',
+ 'encrypt' => 'boolean'
+ ];
+ public $timestamps = false;
+
+ public function user() {
+ return $this->belongsTo(User::class);
+ }
+
+ public function tags() {
+ return $this->belongsToMany(Tag::class, 'paste_taggings');
+ }
+
+ public function favouriters() {
+ return $this->belongsToMany(User::class, 'user_favourites');
+ }
+
+ public function reports() {
+ return $this->hasMany(Report::class);
+ }
+
+ public function replaceTags(array $tags) : void {
+ $this->tags()->detach();
+
+ foreach ($tags as $tagName) {
+ $tag = Tag::getOrCreateByName($tagName);
+ $this->tags()->attach($tag);
+ }
+
+ $this->save();
+ }
+
+ public function expiryDisplay() {
+ $expiry = $this->expiry;
+ if (!$expiry || $expiry === 'NULL') { // TODO: Investigate why this is a string
+ return 'Never';
+ }
+
+ if ($expiry == 'SELF') {
+ return 'View Once ';
+ }
+
+ var_dump($expiry);
+
+ $dateTime = new DateTime($expiry);
+ $dateTime->setTimestamp($expiry);
+ $ret = $dateTime->format('Y-m-d H:i:s');
+ if ($dateTime->diff(new DateTime())->days < 1) {
+ $ret = "$ret ";
+ }
+ return $ret;
+ }
+
+ public static function getRecent(int $count = 10) : Collection {
+ return Paste::with('user')
+ ->orderBy('created_at', 'DESC')
+ ->where('visible', self::VISIBILITY_PUBLIC)
+ ->where('is_hidden', false)
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
+ ->limit($count)->get();
+ }
+
+ public static function getRecentlyUpdated(int $count = 10) : Collection {
+ return Paste::with('user')
+ ->orderBy('updated_at', 'DESC')
+ ->where('visible', self::VISIBILITY_PUBLIC)
+ ->where('is_hidden', false)
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
+ ->limit($count)->get();
+ }
+
+ public static function getMostViewed(int $count = 10) : Collection {
+ return Paste::with('user')
+ ->orderBy('views')
+ ->where('visible', self::VISIBILITY_PUBLIC)
+ ->where('is_hidden', false)
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
+ ->limit($count)->get();
+ }
+
+ public static function getMonthPopular(int $count = 10) : Collection {
+ return Paste::with('user')
+ ->whereRaw('MONTH(created_at) = MONTH(NOW())')
+ ->where('visible', self::VISIBILITY_PUBLIC)
+ ->where('is_hidden', false)
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
+ ->orderBy('views')
+ ->limit($count)->get();
+ }
+
+ public static function getRandom(int $count = 10) : array {
+ $total_pastes = Paste::count();
+ $pastes = [];
+
+ for ($i = 0; $i < $count; $i++) {
+ $pastes[] = self::getSingleRandom($total_pastes);
+ }
+
+ return $pastes;
+ }
+
+ private static function getSingleRandom(int $total) {
+ $paste = null;
+
+ do {
+ $paste_id = rand(1, $total);
+
+ $paste = Paste::with('user')
+ ->where('visible', self::VISIBILITY_PUBLIC)
+ ->where('is_hidden', false)
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))")
+ ->where('id', $paste_id)
+ ->first();
+ } while (!$paste);
+
+ return $paste;
+ }
+}
diff --git a/includes/Models/Report.php b/includes/Models/Report.php
new file mode 100644
index 0000000..a14600f
--- /dev/null
+++ b/includes/Models/Report.php
@@ -0,0 +1,22 @@
+belongsTo(User::class);
+ }
+
+ public function paste() {
+ return $this->belongsTo(Paste::class);
+ }
+}
diff --git a/includes/Tag.class.php b/includes/Models/Tag.php
similarity index 50%
rename from includes/Tag.class.php
rename to includes/Models/Tag.php
index aa50c37..89f87a9 100644
--- a/includes/Tag.class.php
+++ b/includes/Models/Tag.php
@@ -1,54 +1,26 @@
id = (int)$row['id'];
- $this->name = $row['name'];
- $this->slug = $row['slug'];
- }
+class Tag extends Model {
+ protected $table = 'tags';
+ protected $fillable = ['name', 'slug'];
+ public $timestamps = false;
- public static function getOrCreateByName(DatabaseHandle $conn, string $name) : Tag {
+ public static function getOrCreateByName(string $name) : Tag {
$name = Tag::cleanTagName($name);
- if ($row = $conn->querySelectOne('SELECT id, name, slug FROM tags WHERE name = ?', [$name])) {
- return new Tag($row);
+ if ($tag = Tag::where('name', $name)->first()) {
+ return $tag;
}
- $new_slug = Tag::encodeSlug($name);
- $new_tag_id = $conn->queryInsert('INSERT INTO tags (name, slug) VALUES (?, ?)', [$name, $new_slug]);
-
- return new Tag([
- 'id' => $new_tag_id,
+ return Tag::create([
'name' => $name,
- 'slug' => $new_slug
+ 'slug' => Tag::encodeSlug($name)
]);
}
- public static function findBySlug(DatabaseHandle $conn, string $slug) : Tag|null {
- if ($row = $conn->querySelectOne('SELECT id, name, slug FROM tags WHERE slug = ?', [$slug])) {
- return new Tag($row);
- }
-
- return null;
- }
-
- public static function replacePasteTags(DatabaseHandle $conn, int $pasteId, array $tags) {
- $conn->query('DELETE FROM paste_taggings WHERE paste_id = ?', [$pasteId]);
-
- foreach ($tags as $tagName) {
- $tag = Tag::getOrCreateByName($conn, $tagName);
-
- $conn->query('INSERT INTO paste_taggings (paste_id, tag_id) VALUES (?, ?)', [$pasteId, $tag->id]);
- }
-
- // FIXME: We need to get rid of tagsys.
- $conn->query('UPDATE pastes SET tagsys = ? WHERE id = ?', [implode(',', $tags), $pasteId]);
- }
-
/**
* Normalize a tag name, which involves downcasing it, normalizing smart quotes, trimming the string, and
* normalizing runs of whitespace to a single space.
@@ -75,7 +47,7 @@ class Tag {
$cleanName = Tag::cleanTagName($tagName);
if (!empty($cleanName)) {
- array_push($cleanTags, $cleanName);
+ $cleanTags[] = $cleanName;
}
}
@@ -93,4 +65,4 @@ class Tag {
/* urlencode it. for URLs, dipshit. */
return str_replace('%20', '+', urlencode($name));
}
-}
+}
\ No newline at end of file
diff --git a/includes/Models/User.php b/includes/Models/User.php
new file mode 100644
index 0000000..32b791d
--- /dev/null
+++ b/includes/Models/User.php
@@ -0,0 +1,33 @@
+hasOne(UserSession::class);
+ }
+
+ public function favourites() {
+ return $this->belongsToMany(Paste::class, 'user_favourites')->withPivot('created_at')
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))");
+ }
+
+ public function pastes() {
+ return $this->hasMany(Paste::class)
+ ->whereRaw("((expiry IS NULL) OR ((expiry != 'SELF') AND (expiry > NOW())))");
+ }
+
+ public function badges() {
+ return $this->hasMany(Badge::class);
+ }
+}
+
diff --git a/includes/Models/UserSession.php b/includes/Models/UserSession.php
new file mode 100644
index 0000000..d1b3b58
--- /dev/null
+++ b/includes/Models/UserSession.php
@@ -0,0 +1,20 @@
+ 'datetime'
+ ];
+
+ protected $fillable = [
+ 'user_id', 'token', 'expire_at'
+ ];
+
+ public function user() {
+ return $this->belongsTo(User::class);
+ }
+}
diff --git a/includes/NonRetardedSSP.class.php b/includes/NonRetardedSSP.class.php
deleted file mode 100644
index 96ef246..0000000
--- a/includes/NonRetardedSSP.class.php
+++ /dev/null
@@ -1,36 +0,0 @@
-querySelectOne($countQuery, [], PDO::FETCH_NUM)[0];
-
- /* build query */
- $params = [];
-
- if ($length != 0) {
- $query .= ' LIMIT ?';
- array_push($params, $length);
-
- if ($start != 0) {
- $query .= ' OFFSET ?';
- array_push($params, $start);
- }
- }
-
- /* fire it off */
- $stmt = $conn->query($query, $params);
- $data = $stmt->fetchAll(PDO::FETCH_NUM);
-
- return [
- 'draw' => $draw,
- 'recordsTotal' => $recordsTotal,
- 'recordsFiltered' => ($recordsTotal - count($data)),
- 'data' => $data
- ];
- }
-}
diff --git a/includes/Pastedown.php b/includes/Pastedown.php
new file mode 100644
index 0000000..3a8bdc1
--- /dev/null
+++ b/includes/Pastedown.php
@@ -0,0 +1,69 @@
+BlockTypes['>'] = ['Greentext'];
+ array_unshift($this->BlockTypes['<'], 'Redtext');
+ $this->BlockTypes['@'] = ['Purpletext'];
+ }
+
+ protected function blockGreentext($Line)
+ {
+ if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'span',
+ 'attributes' => [
+ 'class' => 'greentext'
+ ],
+ 'handler' => 'line',
+ 'text' => $matches[0],
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockRedtext($Line)
+ {
+ if (preg_match('/^<[ ]?(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'span',
+ 'handler' => 'line',
+ 'attributes' => [
+ 'class' => 'redtext'
+ ],
+ 'text' => $matches[0],
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockPurpletext($Line)
+ {
+ if (preg_match('/^@[ ]?(.*)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'span',
+ 'handler' => 'line',
+ 'attributes' => [
+ 'class' => 'purpletext'
+ ],
+ 'text' => $matches[0],
+ ),
+ );
+
+ return $Block;
+ }
+ }
+}
\ No newline at end of file
diff --git a/includes/User.class.php b/includes/User.class.php
deleted file mode 100644
index 68ad595..0000000
--- a/includes/User.class.php
+++ /dev/null
@@ -1,76 +0,0 @@
-user_id = intval($row['id']);
- $this->username = $row['username'];
- }
-
- public function destroySession(DatabaseHandle $conn, string $token) {
- $conn->query('DELETE FROM user_sessions WHERE user_id = ? AND token = ?', [$this->user_id, $token]);
- }
-
- public static function findByUsername(DatabaseHandle $conn, string $username) : User|null {
- $query = $conn->query('SELECT id, username FROM users WHERE username = ?', [$username]);
- $row = $query->fetch();
-
- return empty($row) ? null : new User($row);
- }
-
- public static function current(DatabaseHandle $conn) : User|null {
- $session_user = User::createFromPhpSession($conn);
-
- if ($session_user !== null) {
- return $session_user;
- }
-
- if (!empty($_COOKIE[self::REMEMBER_TOKEN_COOKIE]) &&
- ($token_user = User::createFromRememberToken($conn, $_COOKIE[self::REMEMBER_TOKEN_COOKIE]))) {
- $_SESSION['user_id'] = $token_user->user_id;
- return $token_user;
- }
-
- return null;
- }
-
- public static function createFromRememberToken(DatabaseHandle $conn, string $remember_token) : User|null {
- $result = $conn->query(
- 'SELECT users.id AS id, users.username AS username, users.banned AS banned, user_sessions.id AS session_id, user_sessions.expire_at AS session_expiry
- FROM user_sessions
- INNER JOIN users ON users.id = user_sessions.user_id
- WHERE user_sessions.token = ?', [$remember_token]
- );
-
- if ($row = $result->fetch()) {
- $session_expiry = new DateTime($row['session_expiry']);
- $now = new DateTime();
-
- /* Session is expired (diff is negative) */
- if ($now->diff($session_expiry)->invert === 1) {
- $conn->query('DELETE FROM user_sessions WHERE id = ?', [$row['session_id']]);
- return null;
- }
-
- return new User($row);
- }
-
- return null;
- }
-
- public static function createFromPhpSession(DatabaseHandle $conn) : User|null {
- if (empty($_SESSION['user_id'])) {
- return null;
- }
-
- $user_id = intval($_SESSION['user_id']);
-
- $row = $conn->query('SELECT id, username, banned FROM users WHERE id = ?', [$user_id])->fetch();
-
- return $row ? new User($row) : null;
- }
-}
diff --git a/includes/captcha.php b/includes/captcha.php
index b598870..5b35dbc 100644
--- a/includes/captcha.php
+++ b/includes/captcha.php
@@ -1,179 +1,31 @@
"mixed|string", 'image_src' => "string"])]
-function captcha($color, $mode, $mul, $allowed) : array {
-
- $bg_path = dirname(__FILE__) . '/captchabg/';
- $font_path = dirname(__FILE__) . '/fonts/';
-
- if ($mul == "on") {
- $captcha_config = array(
- 'code' => '',
- 'min_length' => 5,
- 'max_length' => 6,
- 'backgrounds' => array(
- $bg_path . 'text3.png',
- $bg_path . 'text2.png',
- $bg_path . 'text1.png'
- ),
- 'fonts' => array(
- $font_path . 'LMS Pretty Pony.ttf',
- $font_path . 'PonyvilleMedium0.4.ttf',
- $font_path . 'PonyvilleMedium0.4.ttf'
- ),
- 'characters' => $allowed,
- 'min_font_size' => 20,
- 'max_font_size' => 28,
- 'color' => $color,
- 'angle_min' => 0,
- 'angle_max' => 5,
- 'shadow' => true,
- 'shadow_color' => '#fff',
- 'shadow_offset_x' => -2,
- 'shadow_offset_y' => 4
- );
- } else {
- $captcha_config = array(
- 'code' => '',
- 'min_length' => 5,
- 'max_length' => 5,
- 'backgrounds' => array(
- $bg_path . 'text2.png'
- ),
- 'fonts' => array(
- $font_path . 'times_new_yorker.ttf'
- ),
- 'characters' => $allowed,
- 'min_font_size' => 28,
- 'max_font_size' => 28,
- 'color' => $color,
- 'angle_min' => 0,
- 'angle_max' => 10,
- 'shadow' => true,
- 'shadow_color' => '#fff',
- 'shadow_offset_x' => -1,
- 'shadow_offset_y' => 1
- );
+ $code = '';
+ for ($i = 0; $i < 5; $i++) {
+ $code .= substr($allowed, rand() % (strlen($allowed)), 1);
}
- // Overwrite defaults with custom config values
- if (!empty($config) && is_array($config)) {
- foreach ($config as $key => $value)
- $captcha_config[$key] = $value;
+ if ($token === null) {
+ $token = pp_random_password();
}
- // Restrict certain values
- if ($captcha_config['min_length'] < 1)
- $captcha_config['min_length'] = 1;
- if ($captcha_config['angle_min'] < 0)
- $captcha_config['angle_min'] = 0;
- if ($captcha_config['angle_max'] > 10)
- $captcha_config['angle_max'] = 10;
- if ($captcha_config['angle_max'] < $captcha_config['angle_min'])
- $captcha_config['angle_max'] = $captcha_config['angle_min'];
- if ($captcha_config['min_font_size'] < 10)
- $captcha_config['min_font_size'] = 10;
- if ($captcha_config['max_font_size'] < $captcha_config['min_font_size'])
- $captcha_config['max_font_size'] = $captcha_config['min_font_size'];
+ $redis->setex('captcha/' . md5($token), 600, $code);
-
- // Generate CAPTCHA code if not set by user
- if (empty($captcha_config['code'])) {
- $captcha_config['code'] = '';
- $length = rand($captcha_config['min_length'], $captcha_config['max_length']);
- while (strlen($captcha_config['code']) < $length) {
- $captcha_config['code'] .= substr($captcha_config['characters'], rand() % (strlen($captcha_config['characters'])), 1);
- }
- }
-
- // Generate HTML for image src
- $image_src = substr(__FILE__, strlen(realpath($_SERVER['DOCUMENT_ROOT']))) . '?_CAPTCHA&t=' . urlencode(microtime());
- $image_src = '/' . ltrim(preg_replace('/\\\\/', '/', $image_src), '/');
-
- $_SESSION['_CAPTCHA']['config'] = serialize($captcha_config);
-
- return [
- 'code' => $captcha_config['code'],
- 'image_src' => $image_src
- ];
+ return $token;
}
-if (!function_exists('hex2rgb')) {
- function hex2rgb($hex_str, $return_string = false, $separator = ',') {
- $hex_str = preg_replace("/[^0-9A-Fa-f]/", '', $hex_str); // Gets a proper hex string
- $rgb_array = array();
- if (strlen($hex_str) == 6) {
- $color_val = hexdec($hex_str);
- $rgb_array['r'] = 0xFF & ($color_val >> 0x10);
- $rgb_array['g'] = 0xFF & ($color_val >> 0x8);
- $rgb_array['b'] = 0xFF & $color_val;
- } elseif (strlen($hex_str) == 3) {
- $rgb_array['r'] = hexdec(str_repeat(substr($hex_str, 0, 1), 2));
- $rgb_array['g'] = hexdec(str_repeat(substr($hex_str, 1, 1), 2));
- $rgb_array['b'] = hexdec(str_repeat(substr($hex_str, 2, 1), 2));
- } else {
- return false;
- }
- return $return_string ? implode($separator, $rgb_array) : $rgb_array;
+function checkCaptcha(string $token, string $answer) : bool {
+ global $redis;
+
+ $redis_answer = $redis->get('captcha/' . md5($token));
+ if (!$redis_answer) {
+ return false;
}
+
+ $redis->del('captcha/' . $token);
+
+ return strtolower($redis_answer) === strtolower($answer);
}
-
-// Draw the image
-if (isset($_GET['_CAPTCHA'])) {
-
- session_start();
-
- $captcha_config = unserialize($_SESSION['_CAPTCHA']['config']);
- if (!$captcha_config)
- exit();
-
- // Pick random background, get info, and start captcha
- $background = $captcha_config['backgrounds'][rand(0, count($captcha_config['backgrounds']) - 1)];
- list($bg_width, $bg_height, $bg_type, $bg_attr) = getimagesize($background);
-
- $captcha = imagecreatefrompng($background);
-
- $color = hex2rgb($captcha_config['color']);
- $color = imagecolorallocate($captcha, $color['r'], $color['g'], $color['b']);
-
- // Determine text angle
- $angle = rand($captcha_config['angle_min'], $captcha_config['angle_max']) * (rand(0, 1) == 1 ? -1 : 1);
-
- // Select font randomly
- $font = $captcha_config['fonts'][rand(0, count($captcha_config['fonts']) - 1)];
-
- // Verify font file exists
- if (!file_exists($font))
- throw new Exception('Font file not found: ' . $font);
-
- // Set the font size
- $font_size = rand($captcha_config['min_font_size'], $captcha_config['max_font_size']);
- $text_box_size = imagettfbbox($font_size, $angle, $font, $captcha_config['code']);
-
- // Determine text position
- $box_width = abs($text_box_size[6] - $text_box_size[2]);
- $box_height = abs($text_box_size[5] - $text_box_size[1]);
- $text_pos_x_min = 0;
- $text_pos_x_max = ($bg_width) - ($box_width);
- $text_pos_x = rand($text_pos_x_min, $text_pos_x_max);
- $text_pos_y_min = $box_height;
- $text_pos_y_max = ($bg_height) - ($box_height / 2);
- $text_pos_y = rand($text_pos_y_min, $text_pos_y_max);
-
- // Draw shadow
- if ($captcha_config['shadow']) {
- $shadow_color = hex2rgb($captcha_config['shadow_color']);
- $shadow_color = imagecolorallocate($captcha, $shadow_color['r'], $shadow_color['g'], $shadow_color['b']);
- imagettftext($captcha, $font_size, $angle, $text_pos_x + $captcha_config['shadow_offset_x'], $text_pos_y + $captcha_config['shadow_offset_y'], $shadow_color, $font, $captcha_config['code']);
- }
-
- // Draw text
- imagettftext($captcha, $font_size, $angle, $text_pos_x, $text_pos_y, $color, $font, $captcha_config['code']);
-
- // Output image
- header("Content-type: image/png");
- imagepng($captcha);
-
-}
\ No newline at end of file
diff --git a/includes/common.php b/includes/common.php
index a5b1993..a21a399 100644
--- a/includes/common.php
+++ b/includes/common.php
@@ -5,24 +5,146 @@ if (!defined('IN_PONEPASTE')) {
require_once(__DIR__ . '/../vendor/autoload.php');
require_once(__DIR__ . '/config.php');
require_once(__DIR__ . '/functions.php');
-require_once(__DIR__ . '/DatabaseHandle.class.php');
-require_once(__DIR__ . '/User.class.php');
+require_once(__DIR__ . '/passwords.php');
+require_once(__DIR__ . '/captcha.php');
+
+use Illuminate\Database\Capsule\Manager as Capsule;
+use PonePaste\Helpers\SessionHelper;
+use PonePaste\Models\IPBan;
+use PonePaste\Models\PageView;
+use PonePaste\Models\Paste;
+use PonePaste\Models\User;
+use PonePaste\Helpers\AbilityHelper;
/* View functions */
-function urlForPaste($paste_id) : string {
- if (PP_MOD_REWRITE) {
- return "/${paste_id}";
+function javascriptIncludeTag(string $name) : string {
+ if (PP_DEBUG) {
+ return "";
}
- return "/paste.php?id=${paste_id}";
+ return "";
}
-function urlForMember(string $member_name) : string {
- if (PP_MOD_REWRITE) {
- return '/user/' . urlencode($member_name);
+function urlForPage($page = '') : string {
+ if (!PP_MOD_REWRITE) {
+ $page .= '.php';
}
- return '/user.php?name=' . urlencode($member_name);
+ return (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . '/' . $page;
+}
+
+function urlForPaste(Paste $paste) : string {
+ if (PP_MOD_REWRITE) {
+ return "/{$paste->id}";
+ }
+
+ return "/paste.php?id={$paste->id}";
+}
+
+function urlForReport(Paste $paste) : string {
+ if (PP_MOD_REWRITE) {
+ return "/{$paste->id}/report";
+ }
+
+ return "/report.php?id={$paste->id}";
+}
+
+function urlForMember(User $user) : string {
+ if (PP_MOD_REWRITE) {
+ return '/user/' . urlencode($user->username);
+ }
+
+ return '/user.php?name=' . urlencode($user->username);
+}
+
+/**
+ * @throws Exception if the names and values aren't the same length
+ */
+function optionsForSelect(array $displays, array $values, string $currentSelection = null) : string {
+ $size = count($displays);
+
+ if (count($values) !== $size) {
+ throw new Exception('Option names and option values must be the same count');
+ }
+
+ $html = '';
+
+ for ($i = 0; $i < $size; $i++) {
+ $html .= '';
+ }
+
+ return $html;
+}
+
+/**
+ * @throws Exception if the flash level is invalid
+ */
+function flash(string $level, string $message) : void {
+ if (!isset($_SESSION['flashes'])) {
+ $_SESSION['flashes'] = [
+ 'success' => [],
+ 'warning' => [],
+ 'error' => []
+ ];
+ }
+
+ if (!array_key_exists($level, $_SESSION['flashes'])) {
+ throw new Exception('Invalid flash level ' . $level);
+ }
+
+ $_SESSION['flashes'][$level][] = $message;
+}
+
+
+function flashError(string $message) : void {
+ flash('error', $message);
+}
+
+function flashWarning(string $message) : void {
+ flash('warning', $message);
+}
+
+function flashSuccess(string $message) : void {
+ flash('success', $message);
+}
+
+function getFlashes() {
+ if (!isset($_SESSION['flashes'])) {
+ return ['success' => [], 'warning' => [], 'error' => []];
+ }
+
+ $flashes = $_SESSION['flashes'];
+
+ unset($_SESSION['flashes']);
+
+ return $flashes;
+}
+
+function outputFlashes($flashes) : void {
+ function __outputFlash($level, $flash) : void {
+ echo '
+ '
+ . pp_html_escape($flash) .
+ '
';
+ }
+
+ foreach ($flashes['success'] as $flash) {
+ __outputFlash('info', $flash);
+ }
+
+ foreach ($flashes['warning'] as $flash) {
+ __outputFlash('warning', $flash);
+ }
+
+ foreach ($flashes['error'] as $flash) {
+ __outputFlash('danger', $flash);
+ }
}
/* Database functions */
@@ -30,22 +152,6 @@ function getSiteInfo() : array {
return require(__DIR__ . '/../config/site.php');
}
-function getSiteAds(DatabaseHandle $conn) : array|bool {
- return $conn->query('SELECT text_ads, ads_1, ads_2 FROM ads LIMIT 1')->fetch();
-}
-
-function getSiteTotalPastes(DatabaseHandle $conn) : int {
- return intval($conn->query('SELECT COUNT(*) FROM pastes')->fetch(PDO::FETCH_NUM)[0]);
-}
-
-function getSiteTotalviews(DatabaseHandle $conn) : int {
- return intval($conn->query('SELECT tpage FROM page_view ORDER BY id DESC LIMIT 1')->fetch(PDO::FETCH_NUM)[0]);
-}
-
-function getSiteTotal_unique_views(DatabaseHandle $conn) : int {
- return intval($conn->query('SELECT tvisit FROM page_view ORDER BY id DESC LIMIT 1')->fetch(PDO::FETCH_NUM)[0]);
-}
-
/**
* Specialization of `htmlentities()` that avoids double escaping and uses UTF-8.
*
@@ -56,105 +162,134 @@ function pp_html_escape(string $unescaped) : string {
return htmlspecialchars($unescaped, ENT_QUOTES, 'UTF-8', false);
}
-function updatePageViews(DatabaseHandle $conn) : void {
+/* I think there is one row for each day, and in that row, tpage = non-unique, tvisit = unique page views for that day */
+function updatePageViews() : void {
+ global $redis;
+
$ip = $_SERVER['REMOTE_ADDR'];
$date = date('jS F Y');
- $data_ip = file_get_contents('tmp/temp.tdata');
- $last_page_view = $conn->query('SELECT * FROM page_view ORDER BY id DESC LIMIT 1')->fetch();
- $last_date = $last_page_view['date'];
+ $last_page_view = PageView::orderBy('id', 'desc')->limit(1)->first();
- if ($last_date == $date) {
- $last_tpage = intval($last_page_view['tpage']) + 1;
-
- if (str_contains($data_ip, $ip)) {
- // IP already exists, Update view count
- $statement = $conn->prepare("UPDATE page_view SET tpage = ? WHERE id = ?");
- $statement->execute([$last_tpage, $last_page_view['id']]);
- } else {
- $last_tvisit = intval($last_page_view['tvisit']) + 1;
-
- // Update both tpage and tvisit.
- $statement = $conn->prepare("UPDATE page_view SET tpage = ?,tvisit = ? WHERE id = ?");
- $statement->execute([$last_tpage, $last_tvisit, $last_page_view['id']]);
- file_put_contents('tmp/temp.tdata', $data_ip . "\r\n" . $ip);
+ if ($last_page_view && $last_page_view->date == $date) {
+ if (!$redis->sIsMember('page_view_ips', $ip)) {
+ $last_page_view->tvisit++;
+ $redis->sAdd('page_view_ips', $ip);
}
+
+ $last_page_view->tpage++;
+ $last_page_view->save();
} else {
- // Delete the file and clear data_ip
- unlink("tmp/temp.tdata");
+ $redis->del('page_view_ips');
// New date is created
- $statement = $conn->prepare("INSERT INTO page_view (date, tpage, tvisit) VALUES (?, '1', '1')");
- $statement->execute([$date]);
+ $new_page_view = new PageView(['date' => $date]);
+ $new_page_view->save();
- // Update the IP
- file_put_contents('tmp/temp.tdata', $ip);
+ $redis->sAdd('page_view_ips', $ip);
}
}
+function setupCsrfToken() : string {
+ if (isset($_SESSION[SessionHelper::CSRF_TOKEN_KEY])) {
+ return $_SESSION[SessionHelper::CSRF_TOKEN_KEY];
+ }
+
+ $token = pp_random_token();
+ $_SESSION[SessionHelper::CSRF_TOKEN_KEY] = $token;
+
+ return $token;
+}
+
+function verifyCsrfToken($token = null) : bool {
+ if ($token === null) {
+ $token = $_POST[SessionHelper::CSRF_TOKEN_KEY];
+ }
+
+ if (empty($token) || empty($_SESSION[SessionHelper::CSRF_TOKEN_KEY])) {
+ return false;
+ }
+
+ $success = hash_equals($_SESSION[SessionHelper::CSRF_TOKEN_KEY], $token);
+
+ unset($_SESSION[SessionHelper::CSRF_TOKEN_KEY]);
+
+ return $success;
+}
+
session_start();
-$conn = new DatabaseHandle("mysql:host=$db_host;dbname=$db_schema;charset=utf8mb4", $db_user, $db_pass);
+/* Set up the database and Eloquent ORM */
+$capsule = new Capsule();
-// Setup site info
-$site_info = getSiteInfo();
-$row = $site_info['site_info'];
-$title = Trim($row['title']);
-$des = Trim($row['description']);
-$baseurl = Trim($row['baseurl']);
-$keyword = Trim($row['keywords']);
-$site_name = Trim($row['site_name']);
-$email = Trim($row['email']);
-$ga = Trim($row['google_analytics']);
-$additional_scripts = Trim($row['additional_scripts']);
-
-
-// Setup theme and language
-$lang_and_theme = $site_info['interface'];
-$default_lang = $lang_and_theme['language'];
-$default_theme = $lang_and_theme['theme'];
-
-// site permissions
-$site_permissions = $site_info['permissions'];
-
-if ($site_permissions) {
- $siteprivate = $site_permissions['private'];
- $disableguest = $site_permissions['disable_guest'];
-} else {
- $siteprivate = 'off';
- $disableguest = 'off';
-}
-
-$privatesite = $siteprivate;
-$noguests = $disableguest;
-
-// CAPTCHA configuration
-$captcha_config = $site_info['captcha'];
-$captcha_enabled = (bool)$captcha_config['enabled'];
-
-// Prevent a potential LFI (you never know :p)
-$lang_file = "${default_lang}.php";
-if (in_array($lang_file, scandir(__DIR__ . '/langs/'))) {
- require_once(__DIR__ . "/langs/${lang_file}");
-}
+$capsule->addConnection([
+ 'driver' => 'mysql',
+ 'host' => $db_host,
+ 'database' => $db_schema,
+ 'username' => $db_user,
+ 'password' => $db_pass ,
+ 'charset' => 'utf8mb4',
+ 'prefix' => ''
+]);
+$capsule->setAsGlobal();
+$capsule->bootEloquent();
// Check if IP is banned
$ip = $_SERVER['REMOTE_ADDR'];
-if ($conn->query('SELECT 1 FROM ban_user WHERE ip = ?', [$ip])->fetch()) {
- die($lang['banned']); // "You have been banned from " . $site_name;
+if (IPBan::where('ip', $ip)->first()) {
+ die('You have been banned.');
}
-$site_ads = getSiteAds($conn);
-$total_pastes = getSiteTotalPastes($conn);
-$total_page_views = getSiteTotalviews($conn);
-$total_unique_views = getSiteTotal_unique_views($conn);
+/* Set up Redis */
+$redis = new Redis();
+$redis->pconnect(PP_REDIS_HOST);
-$current_user = User::current($conn);
+// Setup site info
+$site_info = getSiteInfo();
+$global_site_info = $site_info['site_info'];
+$row = $site_info['site_info'];
+$title = trim($row['title']);
+$site_name = trim($row['site_name']);
+$email = trim($row['email']);
-if ($current_user) {
- $noguests = "off";
+// Setup theme
+$default_theme = 'bulma';
+
+// Site permissions
+$site_permissions = $site_info['permissions'];
+
+$site_is_private = false;
+$site_disable_guests = false;
+
+if ($site_permissions) {
+ $site_is_private = (bool) $site_permissions['private'];
+ $site_disable_guests = (bool) $site_permissions['disable_guest'];
}
+// CAPTCHA configuration
+$captcha_enabled = (bool) $site_info['captcha']['enabled'];
+
+$total_pastes = Paste::count();
+$total_page_views = PageView::select('tpage')->orderBy('id', 'desc')->first()->tpage;
+$total_unique_views = PageView::select('tvisit')->orderBy('id', 'desc')->first()->tvisit;
+
+$current_user = SessionHelper::currentUser();
+$start = microtime(true);
+
+function can(string $action, mixed $subject) : bool {
+ global $current_user;
+ static $current_ability = null;
+
+ if ($current_ability === null) {
+ $current_ability = new AbilityHelper($current_user);
+ }
+
+ return $current_ability->can($action, $subject);
+}
+
+$script_bundles = [];
+
/* Security headers */
header('X-Frame-Options: SAMEORIGIN');
header('X-Content-Type-Options: nosniff');
+header("Content-Security-Policy: default-src 'self' data: 'unsafe-inline'");
diff --git a/includes/config.php b/includes/config.php
deleted file mode 100644
index ed66d85..0000000
--- a/includes/config.php
+++ /dev/null
@@ -1,65 +0,0 @@
- 'Green Text',
- 'text' => 'Plain Text',
- 'pastedown' => 'pastedown',
- 'pastedown_old' => 'pastedown old'
-];
-
-// Popular formats that are listed first.
-$popular_formats = [
- 'green',
- 'text',
- 'pastedown',
- 'pastedown_old'
-];
-
-// Cookie - I want a cookie, can I have one?
diff --git a/includes/config.sample.php b/includes/config.sample.php
new file mode 100644
index 0000000..62b60bc
--- /dev/null
+++ b/includes/config.sample.php
@@ -0,0 +1,41 @@
+ 'Green Text',
+ 'text' => 'Plain Text',
+ 'pastedown' => 'pastedown',
+ 'pastedown_old' => 'pastedown old'
+];
+
+// Cookie - I want a cookie, can I have one?
diff --git a/includes/functions.php b/includes/functions.php
index 06390b7..c698bab 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -1,114 +1,60 @@
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
+use Illuminate\Database\Eloquent\Collection;
+use PonePaste\Models\Paste;
-function getPasteTags(DatabaseHandle $conn, int $paste_id) : array {
- return $conn->query(
- 'SELECT name, slug FROM tags
- INNER JOIN paste_taggings ON paste_taggings.tag_id = tags.id
- WHERE paste_taggings.paste_id = ?',
- [$paste_id])->fetchAll();
-}
-
-function getUserFavs(DatabaseHandle $conn, int $user_id) : array {
- $query = $conn->prepare(
- "SELECT pins.f_time, pastes.id, pins.paste_id, pastes.title, pastes.created_at, pastes.updated_at
- FROM pins
- INNER JOIN pastes ON pastes.id = pins.paste_id
- WHERE pins.user_id = ?");
- $query->execute([$user_id]);
- return $query->fetchAll();
-}
-
-function checkFavorite(DatabaseHandle $conn, int $paste_id, int $user_id) : string {
- $query = $conn->prepare("SELECT 1 FROM pins WHERE user_id = ? AND paste_id = ?");
- $query->execute([$user_id, $paste_id]);
-
- if ($query->fetch()) {
- return " ";
- } else {
- return " ";
- }
-}
-
-
-function getreports($conn, $count = 10) {
- $query = $conn->prepare('SELECT * FROM user_reports LIMIT ?');
- $query->execute([$count]);
-
- return $query->fetchAll();
-}
-
-
-function tagsToHtml(string | array $tags) : string {
+function tagsToHtml(array | Collection $tags) : string {
$output = "";
+ foreach ($tags as $tagObj) {
+ $tag = $tagObj->name;
+ $tag_lower = strtolower($tag);
+ if ($tag_lower === 'nsfw' || $tag_lower === 'explicit') {
+ $tagcolor = "tag is-danger";
+ } elseif ($tag_lower === 'safe') {
+ $tagcolor = "tag is-success";
+ } elseif ($tag[0] === '/' && $tag[-1] === '/') {
+ $tagcolor = "tag is-primary";
+ } else {
+ $tagcolor = "tag is-info";
+ }
+ $output .= '' . pp_html_escape($tag) . ' ';
+ }
+ return $output;
+}
+
+function tagsToHtmlUser(string | array | Collection $tags, $profile_username) : string {
+ $output = "";
+
+ if (is_a($tags, Collection::class)) {
+ $tags = $tags->toArray();
+ }
+
if (is_array($tags)) {
$tagsSplit = array_map(function($tag) { return $tag['name']; }, $tags);
} else {
$tagsSplit = explode(",", $tags);
}
+ if (count($tagsSplit) === 0) {
+ return 'No tags ';
+ }
+
foreach ($tagsSplit as $tag) {
- if (stripos($tag, 'nsfw') !== false) {
- $tag = strtoupper($tag);
+ $tag_lower = strtolower($tag);
+ if ($tag_lower === 'nsfw' || $tag_lower === 'explicit') {
$tagcolor = "tag is-danger";
- } elseif (stripos($tag, 'SAFE') !== false) {
- $tag = strtoupper($tag);
+ } elseif ($tag_lower === 'safe') {
$tagcolor = "tag is-success";
- } elseif (str_contains($tag, '/')) {
+ } elseif ($tag[0] === '/' && $tag[-1] === '/') {
$tagcolor = "tag is-primary";
} else {
$tagcolor = "tag is-info";
}
- $output .= '' . pp_html_escape(ucfirst($tag)) . ' ';
+ $output .= '' . pp_html_escape($tag) . ' ';
}
return $output;
}
-function tagsToHtmlUser(string | array $tags, $profile_username) : string {
- $output = "";
- if (is_array($tags)) {
- $tagsSplit = array_map(function($tag) { return $tag['name']; }, $tags);
- } else {
- $tagsSplit = explode(",", $tags);
- }
-
- foreach ($tagsSplit as $tag) {
- if (stripos($tag, 'nsfw') !== false) {
- $tag = strtoupper($tag);
- $tagcolor = "tag is-danger";
- } elseif (stripos($tag, 'SAFE') !== false) {
- $tag = strtoupper($tag);
- $tagcolor = "tag is-success";
- } elseif (str_contains($tag, '/')) {
- $tagcolor = "tag is-primary";
- } else {
- $tagcolor = "tag is-info";
- }
- $output .= '' . pp_html_escape(ucfirst($tag)) . ' ';
- }
- return $output;
-}
-
-function getevent($conn, $event_name, $count) {
- $query = $conn->prepare("SELECT id, visible, title, date, now_time, views, member FROM pastes WHERE visible='1' AND tagsys LIKE '%?%'
- ORDER BY RAND () LIMIT 0, ?");
- $query->execute([$event_name, $count]);
- return $query->fetchAll();
-}
-
-function linkify($value, $protocols = array('http', 'mail'), array $attributes = array()) {
+function linkify($value, $protocols = array('http', 'mail'), array $attributes = array()) : array|string|null {
// Link attributes
$attr = '';
foreach ($attributes as $key => $val) {
@@ -123,7 +69,7 @@ function linkify($value, $protocols = array('http', 'mail'), array $attributes =
}, $value);
// Extract text links for each protocol
- foreach ((array)$protocols as $protocol) {
+ foreach ((array) $protocols as $protocol) {
$value = match ($protocol) {
'http', 'https' => preg_replace_callback('~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?prepare(
- "SELECT pastes.id AS id, users.username AS member, title, visible
- FROM pastes
- INNER JOIN users ON pastes.user_id = users.id
- WHERE pastes.visible = '0' AND users.id = ?
- ORDER BY id DESC
- LIMIT 0, 5");
- $query->execute([$user_id]);
- return $query->fetchAll();
-}
-
-function formatBytes($size, $precision = 2) {
+function formatBytes($size, $precision = 2) : string {
$base = log($size, 1024);
- $suffixes = array('B', 'KB', 'MB', 'GB', 'TB');
+ $suffixes = ['B', 'KB', 'MB', 'GB', 'TB'];
+
+ if ($size === 0) {
+ return '0 B';
+ }
return round(pow(1024, $base - floor($base)), $precision) . ' ' . $suffixes[floor($base)];
}
-function getRecentadmin($conn, $count = 5) {
- $query = $conn->prepare(
- 'SELECT pastes.id AS id, pastes.ip AS ip, title, created_at, views, users.username AS member
- FROM pastes
- INNER JOIN users ON users.id = pastes.user_id
- ORDER BY id DESC LIMIT 0, ?');
- $query->execute([$count]);
-
- return $query->fetchAll();
-}
-
-function getUserPastes(DatabaseHandle $conn, int $user_id) : array {
- return $conn->query(
- "SELECT id, title, visible, code, created_at, views FROM pastes
- WHERE user_id = ?
- ORDER by pastes.id DESC", [$user_id])->fetchAll();
-}
-
-function getTotalPastes(DatabaseHandle $conn, int $user_id) : int {
- $query = $conn->prepare("SELECT COUNT(*) AS total_pastes
- FROM pastes INNER JOIN users ON users.id = pastes.user_id
- WHERE users.id = ?");
- $query->execute([$user_id]);
-
- return intval($query->fetch(PDO::FETCH_NUM)[0]);
-}
-
function friendlyDateDifference(DateTime $lesser, DateTime $greater) : string {
$delta = $greater->diff($lesser, true);
@@ -205,7 +116,7 @@ function friendlyDateDifference(DateTime $lesser, DateTime $greater) : string {
foreach ($parts as $part => $value) {
if ($value !== 0) {
$pluralizer = ($value === 1 ? '' : 's');
- $friendly .= "${value} ${part}${pluralizer} ";
+ $friendly .= "{$value} {$part}{$pluralizer} ";
}
}
@@ -235,35 +146,16 @@ function truncate(string $input, int $maxWords, int $maxChars) : string {
return $result . ($input == $result ? '' : '[...]');
}
-function doDownload($paste_id, $p_title, $p_member, $p_conntent, $p_code) {
- $stats = false;
- if ($p_code) {
- // Figure out extensions.
- $ext = match ($p_code) {
- default => 'txt',
- };
+function embedView($paste_id, $p_title, $content, $title) : bool {
+ $baseurl = pp_site_url();
- // Download
- $p_title = stripslashes($p_title);
- header('content-type: text/plain');
- header('content-Disposition: attachment; filename="' . $paste_id . '_' . $p_title . '_' . $p_member . '.' . $ext . '"');
- echo $p_conntent;
- $stats = true;
- } else {
- // 404
- header('HTTP/1.1 404 Not Found');
- }
- return $stats;
-}
-
-function embedView($paste_id, $p_title, $p_conntent, $p_code, $title, $baseurl, $ges_style, $lang) {
$stats = false;
- if ($p_conntent) {
+ if ($content) {
// Build the output
- $output = "";
+ $output = "
";
$output .= "";
- $output .= "$ges_style"; // Dynamic GeSHI Style
- $output .= $p_conntent; // Paste content
+ $output .= $content; // Paste content
$output .= "";
$output .= "
";
// Display embed conntent using json_encode since that escapes
// characters well enough to satisfy javascript. http://stackoverflow.com/a/169035
- header('conntent-type: text/javascript; charset=utf-8;');
+ header('Content-Type: text/javascript; charset=utf-8;');
echo 'document.write(' . json_encode($output) . ')';
$stats = true;
} else {
@@ -325,22 +216,14 @@ function embedView($paste_id, $p_title, $p_conntent, $p_code, $title, $baseurl,
return $stats;
}
-function addToSitemap($paste_id, $priority, $changefreq, $mod_rewrite) {
+function addToSitemap(Paste $paste, $priority, $changefreq) : void {
$c_date = date('Y-m-d');
$site_data = file_get_contents("sitemap.xml");
$site_data = str_replace("", "", $site_data);
- // which protocol are we on
- $protocol = paste_protocol();
-
- if (PP_MOD_REWRITE) {
- $server_name = $protocol . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "/" . $paste_id;
- } else {
- $server_name = $protocol . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "/paste.php?id=" . $paste_id;
- }
$c_sitemap =
'
- ' . $server_name . '
+ ' . urlForPaste($paste) . '
' . $priority . '
' . $changefreq . '
' . $c_date . '
@@ -352,7 +235,101 @@ function addToSitemap($paste_id, $priority, $changefreq, $mod_rewrite) {
}
function paste_protocol() : string {
- return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") ? 'https://' : 'http://';
+ return !empty($_SERVER['HTTPS']) ? 'https://' : 'http://';
}
+function pp_site_url() : string {
+ return paste_protocol() . $_SERVER['HTTP_HOST'];
+}
+/* get rid of unintended wildcards in a parameter to LIKE queries; not a security issue, just unexpected behaviour. */
+function escapeLikeQuery(string $query) : string {
+ return str_replace(['\\', '_', '%'], ['\\\\', '\\_', '\\%'], $query);
+}
+
+function paginate(int $current_page, int $per_page, int $total_records) : string {
+ $first_page = 0;
+ $last_page = floor($total_records / $per_page);
+ $window = 2;
+
+ if ($first_page == $last_page) {
+ // Do something?
+ }
+
+ $_page_button = function(int $page, string $text, bool $disabled = false) use ($current_page) : string {
+ /* We need to update the 'page' parameter in the request URI, or add it if it doesn't exist. */
+ $request_uri = parse_url($_SERVER['REQUEST_URI']);
+ parse_str((string) @$request_uri['query'], $parsed_query);
+ $parsed_query['page'] = (string) $page;
+ $page_uri = ((string) @$request_uri['path']) . '?' . http_build_query($parsed_query);
+
+ $selected_class = $current_page == $page ? ' paginator__button--selected' : '';
+
+ $disabled_text = $disabled ? ' aria-disabled="true"' : '';
+ return sprintf("%s ", $page_uri, $disabled_text, $text);
+ };
+
+ $html = '';
+
+ /* First and last page the main paginator will show */
+ $first_page_show = max(($current_page - $window), $first_page);
+ $last_page_show = min(($current_page + $window), $last_page);
+
+ /* Whether to show the first and last pages in existence at the ends of the paginator */
+ $show_first_page = (abs($first_page - $current_page)) > ($window);
+ $show_last_page = (abs($last_page - $current_page)) > ($window);
+
+ $prev_button_disabled = $current_page == $first_page ? 'disabled' : '';
+ $next_button_disabled = $current_page == $last_page ? 'disabled' : '';
+
+ $html .= $_page_button($current_page - 1, 'Previous', $prev_button_disabled);
+
+ if ($show_first_page) {
+ $html .= $_page_button($first_page, $first_page);
+ $html .= '… ';
+ }
+
+ for ($i = $first_page_show; $i <= $last_page_show; $i++) {
+ $html .= $_page_button($i, $i);
+ }
+
+ if ($show_last_page) {
+ $html .= '… ';
+ $html .= $_page_button($last_page, $last_page);
+ }
+
+ $html .= $_page_button($current_page + 1, 'Next', $next_button_disabled);
+
+ return $html;
+}
+
+function pp_filename_escape(string $filename, string $extension) : string {
+ /* Remove NTFS invalid characters */
+ $filename = preg_replace('#[<>:"/|?*]#', '-', $filename);
+
+ /* Windows MAX_PATH limit */
+ if (strlen($filename . $extension) > 255) {
+ $filename = substr($filename, 0, 255 - strlen($extension));
+ }
+
+ return $filename . $extension;
+}
+
+function pp_setup_pagination() : array {
+ $per_page = 20;
+ $current_page = 0;
+
+ if (!empty($_GET['page'])) {
+ $current_page = max(0, intval($_GET['page']));
+ }
+
+ if (!empty($_GET['per_page'])) {
+ $per_page = max(1, min(100, intval($_GET['per_page'])));
+ }
+
+ return [$per_page, $current_page];
+}
+
+function pp_output_paginator(int $per_page, int $current_page) : void {
+
+}
\ No newline at end of file
diff --git a/includes/langs/bg.php b/includes/langs/bg.php
deleted file mode 100644
index 75d2e57..0000000
--- a/includes/langs/bg.php
+++ /dev/null
@@ -1,120 +0,0 @@
- (Oct, 2017)
- */
-$lang = array();
-$lang['banned'] = "Достъпа ви до " . $site_name . "е ограничен";
-$lang['expired'] = "Документа, който се опитваш да достъпиш е изтекъл.";
-$lang['guestwelcome'] = $site_name . " ти позволява да публикуваш текст & код.";
-$lang['pleaseregister'] = " Влез или се Регистрирай за да публикуваш съдържание. Безплатно е.";
-$lang['registertoedit'] = "Влез или се Регистрирай за да редактираш или задържиш това съдържание.";
-$lang['editpaste'] = "Редактирай";
-$lang['forkpaste'] = "Задръж";
-$lang['guestmsgtitle'] = $site_name . " е място за публикуване на код или текст за по-лесно отстраняване на грешки.";
-$lang['guestmsgbody'] = "Влез или се Регистрай за да редактираш, изтриваш или преглеждаш хронология на твоето публикувано съдържание";
-$lang['emptypastebin'] = "Няма публикувано съдържание";
-$lang['siteprivate'] = "Този документ е частен.";
-$lang['image_wrong'] = "Wrong captcha.";
-$lang['missing-input-response'] = "The reCAPTCHA response parameter is missing. Please verify your PASTE settings.";
-$lang['missing-input-secret'] = "The reCAPTCHA secret parameter is missing. Please add it to your PASTE settings.";
-$lang['missing-input-response'] = "The reCAPTCHA response parameter is invalid. Please try to complete the reCAPTCHA again.";
-$lang['invalid-input-secret'] = "The reCAPTCHA secret parameter is invalid or malformed. Please double check your PASTE settings.";
-$lang['empty_paste'] = "Не може да добавите публикацията без съдържание";
-$lang['large_paste'] = "Вашата публикация е прекалено голяма. Максималния размер е " . $pastelimit . "MB";
-$lang['paste_db_error'] = "Unable to post to database.";
-$lang['error'] = "Something went wrong.";
-$lang['archive'] = "Pastes Archive";
-$lang['contact'] = "Contact Us";
-$lang['full_name'] = "Your full name is required.";
-$lang['email'] = "Your email address is required.";
-$lang['email_invalid'] = "Your email address seems to be invalid.";
-$lang['message'] = "Your message is required.";
-$lang['login/register'] = "Вход/Регистрация";
-$lang['rememberme'] = "Keep me signed in.";
-$lang['mail_acc_con'] = "$site_name Account Confirmation";
-$lang['mail_suc'] = "На имейла адреса ти е изпратен код за верификация.";
-$lang['email_ver'] = "Имейла вече е бил потвърден.";
-$lang['email_not'] = "Имейла не е намерен.";
-$lang['pass_change'] = "Паролата е променена успешно и е изпратена на имейла ти.";
-$lang['notverified'] = "Акаунта не е верифициран.";
-$lang['incorrect'] = "Невалидни Потребител/Парола";
-$lang['missingfields'] = "All fields must be filled out.";
-$lang['userexists'] = "Това потребителско име вече се използва";
-$lang['emailexists'] = "Този имейл адрес вече съществува в системата";
-$lang['registered'] = "Акаунтът ти беше регистриран успешно";
-$lang['usrinvalid'] = "Your username can only contain letters or numbers.";
-$lang['mypastes'] = "My Pastes";
-$lang['pastedeleted'] = "Paste deleted.";
-$lang['databaseerror'] = "Unable to post to database.";
-$lang['userchanged'] = "Username changed successfully.";
-$lang['usernotvalid'] = "Username not vaild.";
-$lang['privatepaste'] = "This is a private paste.";
-$lang['wrongpassword'] = 'Грешна парола.';
-$lang['pwdprotected'] = 'Съдържание с парола';
-$lang['notfound'] = "Not found";
-$lang['wrongpwd'] = "Въведената парола е грешна. Опитай отново.";
-$lang['myprofile'] = "My Profile";
-$lang['profileerror'] = "Unable to update the profile information ";
-$lang['profileupdated'] = "Your profile information is updated ";
-$lang['oldpasswrong'] = "Your old password is wrong.";
-$lang['archives'] = "Pastes Archive";
-$lang['archivestitle'] = "This page contains the most recently created 100 public pastes.";
-$lang['pastetitle'] = "Paste Title";
-$lang['pastetime'] = "Paste Time";
-$lang['pastesyntax'] = "Paste Syntax";
-$lang['pasteviews'] = "Paste Views";
-$lang['wentwrong'] = "Something went wrong.";
-$lang['versent'] = "A verification email has been sent to your email address.";
-$lang['modpaste'] = "Modify Paste";
-$lang['newpaste'] = "New Paste";
-$lang['highlighting'] = "Syntax Highlighting";
-$lang['expiration'] = "Paste Expiration";
-$lang['visibility'] = "Paste Visibility";
-$lang['pwopt'] = "Password (Optional)";
-$lang['encrypt'] = "Encrypt in database";
-$lang['entercode'] = "Enter Code";
-$lang['almostthere'] = "Almost there. One more step to go.";
-$lang['username'] = "Username";
-$lang['autogen'] = "Auto generated name";
-$lang['setuser'] = "Set your Username";
-$lang['keepuser'] = "Keep autogenerated name";
-$lang['enterpwd'] = "Enter the password";
-$lang['totalpastes'] = "Total Pastes:";
-$lang['membtype'] = "Membership Type:";
-$lang['email'] = "Email";
-$lang['fullname'] = "Full Name";
-$lang['chgpwd'] = "Change Password";
-$lang['curpwd'] = "Current Password";
-$lang['newpwd'] = "New Password";
-$lang['confpwd'] = "Confirm Password";
-$lang['mypastes'] = "My Pastes";
-$lang['viewpastes'] = "View all my pastes";
-$lang['recentpastes'] = "Recent Pastes";
-$lang['user_public_pastes'] = "'s Pastes";
-$lang['yourpastes'] = "Your Pastes";
-$lang['mypastestitle'] = "All of your pastes, in one place.";
-$lang['delete'] = "Delete";
-$lang['highlighted'] = "The text below is selected, press Ctrl+C to copy to your clipboard. (⌘+C on Mac)";
-$lang['newpaste'] = "New Paste";
-$lang['download'] = "Download";
-$lang['showlineno'] = "Покажи/Скрий номерата на редовете";
-$lang['copyto'] = "Копирай съдържанието в клипборда";
-$lang['rawpaste'] = "Raw Paste";
-$lang['membersince'] = "Joined: ";
-$lang['delete_error_invalid'] = "Error: Paste not deleted because it does not exist or you do not own the paste.";
-$lang['not_logged_in'] = "Грешка: Нужно е да си логнат в системата, за да направиш това.";
-$lang['public'] = "Public";
-$lang['unlisted'] = "Unlisted";
-$lang['private'] = "Private";
-$lang['hello'] = "Hello";
-$lang['profile-message'] = "This is your profile page where you can manage your pastes. All of your public, private and unlisted pastes will be shown here. You can also delete your pastes from this page. If other users visit your page they will only see pastes you have set public.";
-$lang['profile-stats'] = "Some of your statistics:";
-$lang['profile-total-pastes'] = "Total Pastes:";
-$lang['profile-total-pub'] = "Total public pastes:";
-$lang['profile-total-unl'] = "Total unlisted pastes:";
-$lang['profile-total-pri'] = "Total private pastes:";
-$lang['profile-total-views'] = "Total views of all your pastes:";
-$lang['embed-hosted-by'] = "hosted by";
-$lang['view-raw'] = "Покажи в необработен текст";
-
diff --git a/includes/langs/br.php b/includes/langs/br.php
deleted file mode 100644
index cd2acd6..0000000
--- a/includes/langs/br.php
+++ /dev/null
@@ -1,121 +0,0 @@
- Entrar ou Cadastre-se para criar um novo paste.";
-$lang['registertoedit'] = "Entrre ou Cadastre-se para editar ou duplicar este paste.";
-$lang['editpaste'] = "Editar";
-$lang['forkpaste'] = "Duplicar";
-$lang['guestmsgtitle'] = $site_name . ", um lugar para salvar e compartilhar textos e codigos.";
-$lang['guestmsgbody'] = "Entrar ou Cadastre-se para editar e acomparnhar os seus pastes.";
-$lang['emptypastebin'] = "Este pastebin está vazio";
-$lang['siteprivate'] = "Este é um paste privado.";
-$lang['image_wrong'] = "Captha invalido.";
-$lang['missing-input-response'] = "O parametro de resposta do reCapcha está faltando. Por favor verifique as configurações do seu paste.";
-$lang['missing-input-secret'] = "O parametro de seguredo do reCapcha está faltando. Por favor verifique as configurações do seu paste.";
-$lang['missing-input-response'] = "O parametro de resposta do reCapcha é inválido. Por favor tente novamente.";
-$lang['invalid-input-secret'] = "O parametro de seguredo do reCapcha está faltando ou é inválido. Please double check your PASTE settings.";
-$lang['empty_paste'] = "Você não pode publicar um paste vazio.";
-$lang['large_paste'] = "O seu paste é muito grande. O tamanho maximo é: " . $pastelimit . "MB";
-$lang['paste_db_error'] = "Não conseguimos eviar seu paste para o banco de dados.";
-$lang['error'] = "Algo deu errado.";
-$lang['archive'] = "Arquivo de pastes";
-$lang['contact'] = "Entre em contato";
-$lang['full_name'] = "É nescessario inserir o seu nome completo.";
-$lang['email'] = "É nescessario inserir o seu email.";
-$lang['email_invalid'] = "O seu endereçõ de email não parece ser válido.";
-$lang['message'] = "É nescessario inserir uma mensagem válida.";
-$lang['login/register'] = "Entrar/Cadastro";
-$lang['rememberme'] = "Manter me logado.";
-$lang['mail_acc_con'] = "Informações da conta em $site_name";
-$lang['mail_suc'] = "O seu codigo de verificção foi enviado ao email preenchido.";
-$lang['email_ver'] = "Email já verificado.";
-$lang['email_not'] = "Email não encontrado, já fez o seu cadastro?.";
-$lang['pass_change'] = "Senha alterada com sucesso, a enviamos para o seu email.";
-$lang['notverified'] = "Conta não verificada.";
-$lang['incorrect'] = "Senha ou usuario incorretos";
-$lang['missingfields'] = "Todos os campos devem ser preenchidos.";
-$lang['userexists'] = "Nome de usuario já em uso.";
-$lang['emailexists'] = "Email já em uso.";
-$lang['registered'] = "Conta cadastrada com sucesso.";
-$lang['usrinvalid'] = "Seu nome de usuario deve conter apenas letras e numeros.";
-$lang['mypastes'] = "Meus pastes";
-$lang['pastedeleted'] = "Paste apagado.";
-$lang['databaseerror'] = "Incapaz de enviar para o banco de dados.";
-$lang['userchanged'] = "Nome de usuario alterado com sucesso.";
-$lang['usernotvalid'] = "Nome de usuario inválido.";
-$lang['privatepaste'] = "Este paste é privado.";
-$lang['wrongpassword'] = 'Senha incorreta.';
-$lang['pwdprotected'] = 'Testo protegido por senha';
-$lang['notfound'] = "Não encontrado";
-$lang['wrongpwd'] = "Senha inválida, tente novamente.";
-$lang['myprofile'] = "Meu perfil";
-$lang['profileerror'] = "Incapaz de atualizar as informações do perfil.";
-$lang['profileupdated'] = "Informações do perfil atualizadas.";
-$lang['oldpasswrong'] = "Senha antiga incorreta.";
-$lang['archives'] = "Arquivo de pastes";
-$lang['archivestitle'] = "Esta pagina contém os 100 pastes mais recentes.";
-$lang['pastetitle'] = "Nome do paste";
-$lang['pastetime'] = "Momento de publicação do paste";
-$lang['pastesyntax'] = "Sintaxe do paste";
-$lang['pasteviews'] = "Visualizações do paste";
-$lang['wentwrong'] = "Algo deu errado.";
-$lang['versent'] = "Um email de verificação foi enviado ao seu endereço de email.";
-$lang['modpaste'] = "Modificar paste";
-$lang['newpaste'] = "Novo paste";
-$lang['highlighting'] = "Destaque de síntaxe";
-$lang['expiration'] = "Expiraçaão do paste";
-$lang['visibility'] = "Visibilidade do paste";
-$lang['pwopt'] = "Senha (Opcional)";
-$lang['encrypt'] = "Encriptar no banco de dados";
-$lang['entercode'] = "Entre o codigo";
-$lang['almostthere'] = "Quase lá, apenas mais um passo.";
-$lang['username'] = "Nome de usuario";
-$lang['autogen'] = "Nome Gerado automaticamente";
-$lang['setuser'] = "Defina o seu nome de usuario";
-$lang['keepuser'] = "Manter nome gerado automaticamente";
-$lang['enterpwd'] = "Entre sua senha";
-$lang['totalpastes'] = "Total de pastes:";
-$lang['membtype'] = "Tipo de Membro:";
-$lang['email'] = "Email";
-$lang['fullname'] = "Nome completo";
-$lang['chgpwd'] = "Mudar senha";
-$lang['curpwd'] = "Senha atual";
-$lang['newpwd'] = "Nova senha";
-$lang['confpwd'] = "Confirmar senha";
-$lang['mypastes'] = "Meus pastes";
-$lang['viewpastes'] = "Ver todos os meus pastes";
-$lang['recentpastes'] = "pastes recentes";
-$lang['user_public_pastes'] = " publicou:";
-$lang['yourpastes'] = "Seus pastes";
-$lang['mypastestitle'] = "Todos os seus pastes, em um só lugar.";
-$lang['delete'] = "Apagar";
-$lang['highlighted'] = "O paste abaixo está selecionado, pressione Ctrl+C Para copiar para a area de transferencia. (⌘+C no mac)";
-$lang['newpaste'] = "Novo paste";
-$lang['download'] = "Baixar";
-$lang['showlineno'] = "Mostrar/Esconder numero da linha";
-$lang['copyto'] = "Copiar paste para a area de transferência";
-$lang['rawpaste'] = "Novo texto bruto";
-$lang['membersince'] = "Se cadastrou em: ";
-$lang['delete_error_invalid'] = "Erro: paste não foi apagado por que você não é dono dele ou ele nao existe mais.";
-$lang['not_logged_in'] = "Erro: Você deve estar logado para poder fazer isso.";
-$lang['public'] = "Publico";
-$lang['unlisted'] = "Não listado";
-$lang['private'] = "Privado";
-$lang['hello'] = "Olá";
-$lang['profile-message'] = "Esta é a pagina do seu perfil, onde você pode ver e adminnistrar todos os seus pastes. Todos os seus pastes, publicos, privados e não listados serão mostrados aqui. Você também pode deletar os seus pastes nesta pagina. Se outros usuários acessarem esta pagina eles verão apenas seus pastes públicos.";
-$lang['profile-stats'] = "Algumas de suas estatísticas:";
-$lang['profile-total-pastes'] = "Numero de pastes:";
-$lang['profile-total-pub'] = "Numero de pastes públicos:";
-$lang['profile-total-unl'] = "Numero de pastes não listados:";
-$lang['profile-total-pri'] = "Numero de pastes privados:";
-$lang['profile-total-views'] = "Numero de visualizações nos seus pastes:";
-$lang['embed-hosted-by'] = "hospedado por";
-$lang['view-raw'] = "Ver texto bruto";
-
diff --git a/includes/langs/de.php b/includes/langs/de.php
deleted file mode 100644
index 6d1c51e..0000000
--- a/includes/langs/de.php
+++ /dev/null
@@ -1,122 +0,0 @@
- (December, 2017)
- */
-
-$lang = array();
-
-$lang['banned'] = "Du wurdest von " . $site_name . " gebannt.";
-$lang['expired'] = "Dieser Paste ist abgelaufen.";
-$lang['guestwelcome'] = $site_name . " erlaubt es dir Text & Code hochzuladen.";
-$lang['pleaseregister'] = " Login oder Registrieren um einen neuen Paste hochzuladen. Es ist kostenlos.";
-$lang['registertoedit'] = "Login oder Registrieren um diesen Paste zu bearbeiten oder zu forken. Es ist kostenlos.";
-$lang['editpaste'] = "Bearbeiten";
-$lang['forkpaste'] = "Fork";
-$lang['guestmsgtitle'] = $site_name . " ist f$uuml;r Quelltexte und generellen Debugging Text.";
-$lang['guestmsgbody'] = "Login oder Registrieren um zu bearbeiten, löschen, um deine Pastes zu verfolgen und mehr.";
-$lang['emptypastebin'] = "Hier sind keine Pasts zum anzeigen.";
-$lang['siteprivate'] = "Dieser Pastebin ist privat.";
-$lang['image_wrong'] = "Falsches Captcha.";
-$lang['missing-input-response'] = "Der reCAPTCHA Response Parameter fehlt. Bitte Überprüfe deine PASTE Einstellungen.";
-$lang['missing-input-secret'] = "Der reCAPTCHA Secret Parameter fehlt. Bitte füge ihn zu deinen PASTE Einstellungen hinzu.";
-$lang['missing-input-response'] = "Der reCAPTCHA Response Parameter fehlt. Bitte versuche das reCAPTCHA erneut zu vollenden.";
-$lang['invalid-input-secret'] = "Der reCAPTCHA Secret Parameter fehlt oder ist falsch. Bitte überprüfe noch mal deine PASTE Einstellungen.";
-$lang['empty_paste'] = "Du kannst keinen leeren Paste hochladen.";
-$lang['large_paste'] = "Dein Paste ist zu groß. Die maximale Größe beträgt " . $pastelimit . "MB";
-$lang['paste_db_error'] = "Der Eintrag wurde nicht in die Datenbank geschrieben.";
-$lang['error'] = "Etwas ist schief gegangen.";
-$lang['archive'] = "Paste Archiv";
-$lang['contact'] = "Kontaktiere uns";
-$lang['full_name'] = "Es wird dein voller Name benötigt.";
-$lang['email'] = "Deine eMail-Adresse wird benötigt.";
-$lang['email_invalid'] = "Deine eMail-Adresse scheint ungültig zu sein.";
-$lang['message'] = "Deine Nachricht wird benötigt.";
-$lang['login/register'] = "Login/Registrieren";
-$lang['rememberme'] = "Lass mich eingeloggt.";
-$lang['mail_acc_con'] = "$site_name Konto Bestätigung";
-$lang['mail_suc'] = "Der Verifikationscode wurde erfolgreich an deine eMail-Adresse gesendet.";
-$lang['email_ver'] = "Diese eMail-Adresse wurde bereits verifiziert.";
-$lang['email_not'] = "eMail-Adresse nicht gefunden.";
-$lang['pass_change'] = "Passwort erfolgreich geändert und per eMail versendet.";
-$lang['notverified'] = "Konto nicht verifiziert.";
-$lang['incorrect'] = "Falscher Benutzername/Passwort";
-$lang['missingfields'] = "Alle Felder müssen ausgefüllt sein.";
-$lang['userexists'] = "Benutzername existiert bereits.";
-$lang['emailexists'] = "eMail-Adresse wurde bereits registriert.";
-$lang['registered'] = "Dein Konto wurde erfolgreich registriert.";
-$lang['usrinvalid'] = "Dein Benutzername kann nur Buchstaben und Zahlen enthalten.";
-$lang['mypastes'] = "Meine Pastes";
-$lang['pastedeleted'] = "Paste gelöscht.";
-$lang['databaseerror'] = "Der Eintrag wurde nicht in die Datenbank geschrieben.";
-$lang['userchanged'] = "Benutzername erfolgreich geändert.";
-$lang['usernotvalid'] = "Benutzername ist nicht gültig.";
-$lang['privatepaste'] = "Das ist ein privater Paste.";
-$lang['wrongpassword'] = 'Falsches Passwort.';
-$lang['pwdprotected'] = 'Passwortgeschützter Paste';
-$lang['notfound'] = "Nicht gefunden";
-$lang['wrongpwd'] = "Falsches Passwort. Versuche es erneut.";
-$lang['myprofile'] = "Mein Profil";
-$lang['profileerror'] = "Kann Profilinformationen nicht aktualisieren ";
-$lang['profileupdated'] = "Profilinformationen aktualisiert ";
-$lang['oldpasswrong'] = "Dein altes Passwort ist falsch.";
-$lang['archives'] = "Paste Archiv";
-$lang['archivestitle'] = "Diese Seite zeigt die 100 zuletzt erstellten, öffentlichen Pastes.";
-$lang['pastetitle'] = "Paste Titel";
-$lang['pastetime'] = "Paste Zeit";
-$lang['pastesyntax'] = "Paste Syntax";
-$lang['pasteviews'] = "Paste Ansichten";
-$lang['wentwrong'] = "Etwas ging schief.";
-$lang['versent'] = "Eine Bestätigungsmail wurde an deine eMail-Adresse gesendet.";
-$lang['modpaste'] = "Paste bearbeiten ";
-$lang['newpaste'] = "Neuer Paste";
-$lang['highlighting'] = "Syntax Highlighting";
-$lang['expiration'] = "Paste Ablaufdatum";
-$lang['visibility'] = "Paste Sichtbarkeit";
-$lang['pwopt'] = "Passwort (Optional)";
-$lang['encrypt'] = "Verschlüsselt in Datenbank";
-$lang['entercode'] = "Code eingeben";
-$lang['almostthere'] = "Fast geschafft. Noch ein Schritt.";
-$lang['username'] = "Benutzername";
-$lang['autogen'] = "Generierter Name";
-$lang['setuser'] = "Setze deinen Benutzernamen";
-$lang['keepuser'] = "Behalte generierten Namen";
-$lang['enterpwd'] = "Gib das Passwort ein";
-$lang['totalpastes'] = "Total Pastes:";
-$lang['membtype'] = "Art der Mitgliedschaft:";
-$lang['email'] = "Email";
-$lang['fullname'] = "Voller Name";
-$lang['chgpwd'] = "Passwort ändern";
-$lang['curpwd'] = "Aktuelles Passwort";
-$lang['newpwd'] = "Neues Passwort";
-$lang['confpwd'] = "Bestä#tige Passwort";
-$lang['mypastes'] = "Meine Pastes";
-$lang['viewpastes'] = "Zeige alle meine Pastes";
-$lang['recentpastes'] = "Letzte Pastes";
-$lang['user_public_pastes'] = "'s Pastes";
-$lang['yourpastes'] = "Deine Pastes";
-$lang['mypastestitle'] = "Alle deine Pastes, in einem Platz.";
-$lang['delete'] = "Löschen";
-$lang['highlighted'] = "Der unten stehende Text ist ausgewählt, drücke Strg+C um ihn zu kopieren. (⌘+C beim Mac)";
-$lang['newpaste'] = "Neuer Paste";
-$lang['download'] = "Download";
-$lang['showlineno'] = "Zeige/Verstecke Zeilennr.";
-$lang['copyto'] = "Kopiere Text";
-$lang['rawpaste'] = "Raw Paste";
-$lang['membersince'] = "Beigetreten: ";
-$lang['delete_error_invalid'] = "Fehler: Paste wurde nicht gelöscht, da dieser nicht existiert oder du nicht der Eigentümer bist.";
-$lang['not_logged_in'] = "Fehler: Du musst eingeloggt sein, um das zu tun.";
-$lang['public'] = "Öffentlich";
-$lang['unlisted'] = "Ungelistet";
-$lang['private'] = "Privat";
-$lang['hello'] = "Hallo";
-$lang['profile-message'] = "Dies ist deine Profilseite in der du deine Pasts verwalten kannst. All deine öffentlichen, privaten und ungelisteten Pastes werden hier gezeigt. Du kannst sie hier auch löschen. Falls andere Benutzer deine Seite besuchen werden sie nur deine öffentlich gesetzten Pastes sehen können.";
-$lang['profile-stats'] = "Ein paar deiner Statistiken:";
-$lang['profile-total-pastes'] = "Total Pastes:";
-$lang['profile-total-pub'] = "Total öffentliche Pastes:";
-$lang['profile-total-unl'] = "Total ungelistete Pastes:";
-$lang['profile-total-pri'] = "Total private Pastes:";
-$lang['profile-total-views'] = "Total Ansichten all deiner Pastes:";
-$lang['embed-hosted-by'] = "gehostet von";
-$lang['view-raw'] = "Zeige Raw";
-
diff --git a/includes/langs/en.php b/includes/langs/en.php
deleted file mode 100644
index 6e7b716..0000000
--- a/includes/langs/en.php
+++ /dev/null
@@ -1,135 +0,0 @@
- Login or Register to submit a new paste. It's free.";
-$lang['registertoedit'] = "Login or Register to edit or fork this paste. It's free.";
-$lang['editpaste'] = "Edit";
-$lang['forkpaste'] = "Fork";
-$lang['guestmsgtitle'] = $site_name . " is for horse greens and general shitposts.";
-$lang['guestmsgbody'] = "Login or Register to edit, delete and keep track of your pastes and more.";
-$lang['emptypastebin'] = "There are no pastes to show.";
-$lang['siteprivate'] = "This pastebin is private.";
-$lang['image_wrong'] = "Wrong captcha.";
-$lang['missing-input-response'] = "The reCAPTCHA response parameter is missing. Please verify your PASTE settings.";
-$lang['missing-input-secret'] = "The reCAPTCHA secret parameter is missing. Please add it to your PASTE settings.";
-$lang['missing-input-response'] = "The reCAPTCHA response parameter is invalid. Please try to complete the reCAPTCHA again.";
-$lang['invalid-input-secret'] = "The reCAPTCHA secret parameter is invalid or malformed. Please double check your PASTE settings.";
-$lang['empty_paste'] = "You cannot post an empty paste.";
-$lang['large_paste'] = "Your paste is too large. Max size is " . PP_PASTE_LIMIT_BYTES . " bytes";
-$lang['paste_db_error'] = "Unable to post to database.";
-$lang['error'] = "Something went wrong.";
-$lang['archive'] = "Pastes Archive";
-$lang['contact'] = "Contact Us";
-$lang['full_name'] = "Account name is required.";
-$lang['email'] = "Your email address is required.";
-$lang['email_invalid'] = "Your email address seems to be invalid.";
-$lang['message'] = "Your message is required.";
-$lang['login/register'] = "Login/Register";
-$lang['rememberme'] = "Keep me signed in.";
-$lang['mail_acc_con'] = "$site_name Account Confirmation";
-$lang['mail_suc'] = "Verification code successfully sent to your email address.";
-$lang['email_ver'] = "Email already verified.";
-$lang['email_not'] = "Email not found.";
-$lang['pass_change'] = "Password changed successfully and sent to your email.";
-$lang['notverified'] = "Account not verified.";
-$lang['incorrect'] = "Incorrect User/Password";
-$lang['missingfields'] = "All fields must be filled out.";
-$lang['userexists'] = "Username already taken.";
-$lang['emailexists'] = "Email already registered.";
-$lang['registered'] = "Your account was successfully registered.";
-$lang['usrinvalid'] = "Your username can only contain letters or numbers.";
-$lang['mypastes'] = "My Pastes";
-$lang['pastedeleted'] = "Paste deleted.";
-$lang['databaseerror'] = "Unable to post to database.";
-$lang['userchanged'] = "Username changed successfully.";
-$lang['usernotvalid'] = "Username not vaild.";
-$lang['privatepaste'] = "This is a private paste.";
-$lang['wrongpassword'] = 'Wrong password.';
-$lang['pwdprotected'] = 'Password protected paste';
-$lang['notfound'] = "Not found";
-$lang['wrongpwd'] = "Wrong password. Try again.";
-$lang['myprofile'] = "My Profile";
-$lang['profileerror'] = "Unable to update the profile information ";
-$lang['profileupdated'] = "Your profile information is updated ";
-$lang['oldpasswrong'] = "Your old password is wrong.";
-$lang['archives'] = "Pastes Archive";
-$lang['archivestitle'] = "This page contains the most recently created 100 public pastes.";
-$lang['pastetitle'] = "Paste Title";
-$lang['pastetime'] = "Paste Time";
-$lang['pastedate'] = "Paste Date";
-$lang['pastesyntax'] = "Paste Syntax";
-$lang['pasteviews'] = "Views";
-$lang['wentwrong'] = "Something went wrong.";
-$lang['versent'] = "A verification email has been sent to your email address.";
-$lang['modpaste'] = "Modify Paste";
-$lang['newpaste'] = "New Paste";
-$lang['highlighting'] = "Syntax Highlighting";
-$lang['expiration'] = "Paste Expiration";
-$lang['visibility'] = "Paste Visibility";
-$lang['pwopt'] = "Password (Optional)";
-$lang['encrypt'] = "Encrypt in database";
-$lang['entercode'] = "Enter Code";
-$lang['almostthere'] = "Almost there. One more step to go.";
-$lang['username'] = "Username";
-$lang['autogen'] = "Auto generated name";
-$lang['setuser'] = "Set your Username";
-$lang['keepuser'] = "Keep autogenerated name";
-$lang['enterpwd'] = "Enter the password";
-$lang['totalpastes'] = "Total Pastes:";
-$lang['membtype'] = "Membership Type:";
-$lang['email'] = "Email";
-$lang['fullname'] = "Full Name";
-$lang['chgpwd'] = "Change Password";
-$lang['curpwd'] = "Current Password";
-$lang['newpwd'] = "New Password";
-$lang['confpwd'] = "Confirm Password";
-$lang['mypastes'] = "My Pastes";
-$lang['viewpastes'] = "View all my pastes";
-$lang['recentpastes'] = "Recent Pastes";
-$lang['user_public_pastes'] = "'s Pastes";
-$lang['yourpastes'] = "Your Pastes";
-$lang['mypastestitle'] = "All of your pastes, in one place.";
-$lang['delete'] = "Delete";
-$lang['highlighted'] = "The text below is selected, press Ctrl+C to copy to your clipboard. (⌘+C on Mac)";
-$lang['newpaste'] = "New Paste";
-$lang['download'] = "Download";
-$lang['showlineno'] = "Show/Hide line no.";
-$lang['copyto'] = "Copy text to clipboard";
-$lang['rawpaste'] = "Raw Paste";
-$lang['membersince'] = "Joined: ";
-$lang['delete_error_invalid'] = "Error: Paste not deleted because it does not exist or you do not own the paste.";
-$lang['not_logged_in'] = "Error: You must be logged in to do that.";
-$lang['public'] = "Public";
-$lang['unlisted'] = "Unlisted";
-$lang['private'] = "Private";
-$lang['hello'] = "Hello";
-$lang['profile-message'] = "This is your profile page where you can manage your pastes. All of your public, private and unlisted pastes will be shown here. You can also delete your pastes from this page. If other users visit your page they will only see pastes you have set public.";
-$lang['profile-stats'] = "Some of your statistics:";
-$lang['profile-total-pastes'] = "Total Pastes:";
-$lang['profile-total-pub'] = "Total public pastes:";
-$lang['profile-total-unl'] = "Total unlisted pastes:";
-$lang['profile-total-pri'] = "Total private pastes:";
-$lang['profile-total-views'] = "Total views of all your pastes:";
-$lang['pastfavs-total'] = "Total Favorites of your pastes:";
-$lang['yourfavs-total'] = "Total Favorites by you:";
-$lang['embed-hosted-by'] = "hosted by";
-$lang['view-raw'] = "View Raw";
-
diff --git a/includes/langs/es.php b/includes/langs/es.php
deleted file mode 100644
index ad8951d..0000000
--- a/includes/langs/es.php
+++ /dev/null
@@ -1,120 +0,0 @@
- Iniciar sesión o Registrarse para enviar un pegado. Es gratis.";
-$lang['registertoedit'] = "Iniciar sesión o Registrarse para editar o bifurcar este pegado. Es gratis.";
-$lang['editpaste'] = "Editar";
-$lang['forkpaste'] = "Bifurcar";
-$lang['guestmsgtitle'] = $site_name . " es para el código fuente y el texto de depuración general.";
-$lang['guestmsgbody'] = "Iniciar sesión o Registrarse para editar, eliminar y mantener un seguimiento de sus pegados y mucho más.";
-$lang['emptypastebin'] = "No hay pegado para mostrar.";
-$lang['siteprivate'] = "Este pegado es privado";
-$lang['image_wrong'] = "Captcha incorrecto.";
-$lang['missing-input-response'] = "Falta el parámetro de respuesta reCAPTCHA. Verifique la configuración de PASTE.";
-$lang['missing-input-secret'] = "Falta el parámetro secreto reCAPTCHA. Añádala a su configuración de PASTE.";
-$lang['missing-input-response'] = "El parámetro de respuesta reCAPTCHA no es válido. Por favor, intenta completar el reCAPTCHA de nuevo.";
-$lang['invalid-input-secret'] = "El parámetro secreto reCAPTCHA no es válido o está mal formado. Por favor revise su configuración de PASTE.";
-$lang['empty_paste'] = "No puede publicar un pegado vacío.";
-$lang['large_paste'] = "El pegado es demasiado grande. El tamaño máximo es " . $pastelimit . "MB";
-$lang['paste_db_error'] = "No se puede publicar en la base de datos.";
-$lang['error'] = "Algo salió mal.";
-$lang['archive'] = "Archivo de pegado";
-$lang['contact'] = "Contactenos";
-$lang['full_name'] = "Su nombre completo es obligatorio.";
-$lang['email'] = "Se requiere tu dirección de correo electrónico.";
-$lang['email_invalid'] = "Su dirección de correo electrónico parece no ser válida.";
-$lang['message'] = "Su mensaje es obligatorio.";
-$lang['login/register'] = "Iniciar sesión/Registro";
-$lang['rememberme'] = "Manténgame conectado.";
-$lang['mail_acc_con'] = "$site_name Cuenta confirmada";
-$lang['mail_suc'] = "El código de verificación se envió correctamente a su dirección de correo electrónico.";
-$lang['email_ver'] = "Correo electrónico ya verificado.";
-$lang['email_not'] = "Correo electrónico no encontrado.";
-$lang['pass_change'] = "La contraseña se ha cambiado correctamente y se ha enviado a tu correo electrónico.";
-$lang['notverified'] = "Cuenta no verificada.";
-$lang['incorrect'] = "Incorrecto usuario/contraseña";
-$lang['missingfields'] = "Todos los campos deben ser llenados.";
-$lang['userexists'] = "Nombre de usuario ya tomado.";
-$lang['emailexists'] = "Correo electrónico ya registrado.";
-$lang ['registered'] = "Tu cuenta se ha registrado correctamente.";
-$lang ['usrinvalid'] = "Tu nombre de usuario solo puede contener letras o números.";
-$lang ['mypastes'] = "Mis pegados";
-$lang ['pastedeleted'] = "Pegado borrado.";
-$lang ['databaseerror'] = "No se puede publicar en la base de datos.";
-$lang ['userchanged'] = "El nombre de usuario ha cambiado correctamente.";
-$lang ['usernotvalid'] = "Nombre de usuario no válido.";
-$lang ['privatepaste'] = "Este es un pegado privada.";
-$lang ['wrongpassword'] = "Contraseña incorrecta.";
-$lang ['pwdprotected'] = "Pegar con contraseña protegida";
-$lang ['notfound'] = "No encontrado";
-$lang ['wrongpwd'] = "Contraseña incorrecta. Vuelva a intentarlo.";
-$lang ['myprofile'] = "Mi perfil";
-$lang ['profileerror'] = "No se puede actualizar la información del perfil";
-$lang ['profileupdated'] = "Se ha actualizado la información de tu perfil";
-$lang ['oldpasswrong'] = "Su contraseña antigua es incorrecta.";
-$lang ['archives'] = "Archivos de pegado";
-$lang ['archivestitle'] = "Esta página contiene los 100 pegados publicados más recientemente.";
-$lang ['pastetitle'] = "Pegar título";
-$lang ['pastetime'] = "Tiempo de pegado";
-$lang ['pastesyntax'] = "Pegar Sintaxis";
-$lang ['pasteviews'] = "Pegar vistas";
-$lang ['wentwrong'] = "Algo salió mal.";
-$lang ['versent'] = "Se ha enviado un correo electrónico de verificación a su dirección de correo electrónico.";
-$lang ['modpaste'] = "Modificar Pegado";
-$lang ['newpaste'] = "Nueva Pega";
-$lang ['highlighting'] = "Resaltado de sintaxis";
-$lang ['expiration'] = "Pegar Expiración";
-$lang ['visibility'] = "Pegar Visibilidad";
-$lang ['pwopt'] = "Contraseña (Opcional)";
-$lang ['encrypt'] = "Cifrar en la base de datos";
-$lang ['entercode'] = "Introducir código";
-$lang ['almostthere'] = "Casi allí, un paso más.";
-$lang ['username'] = "Nombre de usuario";
-$lang ['autogen'] = "Nombre generado automáticamente";
-$lang ['setuser'] = "Establecer su nombre de usuario";
-$lang ['keepuser'] = "Mantener el nombre autogenerado";
-$lang ['enterpwd'] = "Introduzca la contraseña";
-$lang ['totalpastes'] = "Total pegado:";
-$lang ['membtype'] = "Tipo de membresía:";
-$lang ['email'] = "Correo electrónico";
-$lang ['fullname'] = "Nombre completo";
-$lang ['chgpwd'] = "Cambiar contraseña";
-$lang ['curpwd'] = "Contraseña actual";
-$lang ['newpwd'] = "Nueva contraseña";
-$lang ['confpwd'] = "Confirmar contraseña";
-$lang ['mypastes'] = "Mis pegados";
-$lang ['viewpastes'] = "Ver todos mis pegados";
-$lang ['recentpastes'] = "Pegados recientes";
-$lang ['user_public_pastes'] = "'s pegados";
-$lang ['yourpastes'] = "Sus pegados";
-$lang ['mypastestitle'] = "Todos tus pegados, en un solo lugar.";
-$lang ['delete'] = "Eliminar";
-$lang['highlighted'] = "El texto siguiente está seleccionado, presione Ctrl+C para copiar en su portapapeles. (⌘+C en Mac)";
-$lang ['newpaste'] = "Nueva Pega";
-$lang ['download'] = "Descargar";
-$lang ['showlineno'] = "Mostrar / Ocultar línea no";
-$lang ['copyto'] = "Copiar texto al portapapeles";
-$lang ['rawpaste'] = "Pasta cruda";
-$lang ['membersince'] = "Registrado:";
-$lang ['delete_error_invalid'] = "Error: Pegado no borrado porque no existe o no posee el pegado.";
-$lang ['not_logged_in'] = "Error: Debes haber iniciado sesión para hacer eso.";
-$lang ['public'] = "Público";
-$lang ['unlisted'] = "No listado";
-$lang ['private'] = "Privado";
-$lang ['hello'] = "Hola";
-$lang['profile-message'] = "Esta es su página de perfil donde puede administrar sus pegados. Todos sus pegados públicos, privados y no listados se mostrarán aquí. También puede eliminar sus pegados de esta página. Si otros usuarios visitan tu página, solo verán las pastas que hayas puesto público.";
-$lang ['profile-stats'] = "Algunas de sus estadísticas:";
-$lang ['profile-total-pastes'] = "Pegados totales:";
-$lang ['profile-total-pub'] = "Pegados públicos totales:";
-$lang ['profile-total-unl'] = "Total de pegados no listados:";
-$lang ['perfil-total-pri'] = "Total de pegados privados:";
-$lang ['profile-total-views'] = "Total de vistas de todos sus pegados:";
-$lang ['embed-hosted-by'] = "alojado por";
-$lang ['view-raw'] = "Ver crudo";
-
diff --git a/includes/langs/fr.php b/includes/langs/fr.php
deleted file mode 100644
index 04b7011..0000000
--- a/includes/langs/fr.php
+++ /dev/null
@@ -1,121 +0,0 @@
- Connectez-vous ou Enregistrez-vous pour soumettre un nouveau paste. C'est gratuit !";
-$lang['registertoedit'] = "Connectez-vous ou Enregistrez-vous pour éditer ou dupliquer ce paste. C'est gratuit !";
-$lang['editpaste'] = "Éditer";
-$lang['forkpaste'] = "Dupliquer";
-$lang['guestmsgtitle'] = $site_name . " est fait pour le code source et le texte général de débogage.";
-$lang['guestmsgbody'] = "Connectez-vous ou Enregistrez-vous pour éditer, supprimer et suivre vos pastes et bien plus.";
-$lang['emptypastebin'] = "Pas de pastes.";
-$lang['siteprivate'] = "Ce pastebin est privé.";
-$lang['image_wrong'] = "Erreur de captcha.";
-$lang['missing-input-response'] = "Il manque le paramètre réponse de reCAPTCHA. Vérifier la configuration de PASTE.";
-$lang['missing-input-secret'] = "Il manque le paramètre secret de reCAPTCHA. Merci de l'ajouter à la configuration de PASTE.";
-$lang['missing-input-response'] = "Le paramètre réponse de reCAPTCHA est invalide. Recommencez.";
-$lang['invalid-input-secret'] = "Le paramètre secret de reCAPTCHA est invalide ou incorrect. Vérifiez la configuration de PASTE.";
-$lang['empty_paste'] = "Vous ne pouvez pas publier un paste vide.";
-$lang['large_paste'] = "Le paste est trop volumineux. La taille maximale est " . $pastelimit . "MB";
-$lang['paste_db_error'] = "Impossible de publier dans la base de données.";
-$lang['error'] = "Erreur fatale.";
-$lang['archive'] = "Archives";
-$lang['contact'] = "Nous contacter";
-$lang['full_name'] = "Le champ nom doit être renseigné.";
-$lang['email'] = "Le champ email doit être renseigné.";
-$lang['email_invalid'] = "Adresse email invalide.";
-$lang['message'] = "Le champ message doit être renseigné.";
-$lang['login/register'] = "Se connecter/S'enregistrer";
-$lang['rememberme'] = "Rester connecter.";
-$lang['mail_acc_con'] = "Confirmation de votre compte pour $site_name";
-$lang['mail_suc'] = "Le code de vérification a été transmis à votre adresse email.";
-$lang['email_ver'] = "Adresse email déjà vérifiée.";
-$lang['email_not'] = "Adresse email non trouvée.";
-$lang['pass_change'] = "Le mot de passe a été changé et transmis à votre adresse email.";
-$lang['notverified'] = "Compte non vérifié.";
-$lang['incorrect'] = "Nom d'utilisateur ou mot de passe incorrect.";
-$lang['missingfields'] = "Tous les champs doivent être renseignés";
-$lang['userexists'] = "Le nom d'utilisateur existe déjà.";
-$lang['emailexists'] = "Adresse email déjà enregistrée.";
-$lang['registered'] = "Votre compte a été enregistré.";
-$lang['usrinvalid'] = "Votre nom d'utilisateur ne doit contenir que des lettres ou des chiffres.";
-$lang['mypastes'] = "Mes Pastes";
-$lang['pastedeleted'] = "Paste supprimé.";
-$lang['databaseerror'] = "Impossible d'enregistrer dans la base de données.";
-$lang['userchanged'] = "Nom d'utilisateur changé.";
-$lang['usernotvalid'] = "Nom d'utilisateur non valide.";
-$lang['privatepaste'] = "Ce paste est privé.";
-$lang['wrongpassword'] = 'Erreur mot de passe.';
-$lang['pwdprotected'] = 'Ce paste est protégé par mot de passe.';
-$lang['notfound'] = "Non trouvé";
-$lang['wrongpwd'] = "Erreur mot de passe, re-essayez.";
-$lang['myprofile'] = "Mon profil";
-$lang['profileerror'] = "Impossible de mettre à jour vos informations de profil.";
-$lang['profileupdated'] = "Vos informations de profil ont été mises à jour";
-$lang['oldpasswrong'] = "Erreur ancien mot de passe";
-$lang['archives'] = "Archives";
-$lang['archivestitle'] = "Cet écran réuni les 100 derniers paste enregistrés.";
-$lang['pastetitle'] = "Titre du paste";
-$lang['pastetime'] = "Durée de vie du paste";
-$lang['pastesyntax'] = "Syntaxe du paste";
-$lang['pasteviews'] = "Nombre de vues du paste";
-$lang['wentwrong'] = "Erreur fatale.";
-$lang['versent'] = "Un email de vérification a été transmis à votre adresse email.";
-$lang['modpaste'] = "Modifier le paste";
-$lang['newpaste'] = "Nouveau paste";
-$lang['highlighting'] = "Mise en évidence de la syntaxe.";
-$lang['expiration'] = "Durée de vie du paste";
-$lang['visibility'] = "Portée du paste";
-$lang['pwopt'] = "Mot de passe (Optionnel)";
-$lang['encrypt'] = "Crypter dans la base de données";
-$lang['entercode'] = "Entrez le code";
-$lang['almostthere'] = "Vous avez presque terminé, il ne reste qu'une étape";
-$lang['username'] = "Nom d'utilisateur";
-$lang['autogen'] = "Nom généré automatiquement";
-$lang['setuser'] = "Saisir votre nom d'utilisateur";
-$lang['keepuser'] = "Garder le nom généré automatiquement";
-$lang['enterpwd'] = "Saisir le mot de passe";
-$lang['totalpastes'] = "Total pastes:";
-$lang['membtype'] = "Type de compte:";
-$lang['email'] = "Email";
-$lang['fullname'] = "Nom complet";
-$lang['chgpwd'] = "Changer le mot de passe";
-$lang['curpwd'] = "Mot de passe actuel";
-$lang['newpwd'] = "Nouveau mot de passe";
-$lang['confpwd'] = "Confirmez le nouveau mot de passe";
-$lang['mypastes'] = "Mes pastes";
-$lang['viewpastes'] = "Voir tous mes pastes";
-$lang['recentpastes'] = "Pastes récents";
-$lang['user_public_pastes'] = "'s Pastes";
-$lang['yourpastes'] = "Vos pastes";
-$lang['mypastestitle'] = "Tous vos pastes au même endroit.";
-$lang['delete'] = "Supprimer";
-$lang['highlighted'] = "Le texte ci-dessus est sélectionné, presser Ctrl+C pour le copier dans votre presse-papier. (⌘+C sur Mac)";
-$lang['newpaste'] = "Nouveau paste";
-$lang['download'] = "Télécharger";
-$lang['showlineno'] = "Afficher/Cacher les numéros de ligne.";
-$lang['copyto'] = "Copier le texte dans le presse-papier.";
-$lang['rawpaste'] = "Paste brut";
-$lang['membersince'] = "Enregistré depuis : ";
-$lang['delete_error_invalid'] = "Erreur : le paste n'a pas été supprimé car il n'existe pas ou vous n'êtes pas son propriétaire.";
-$lang['not_logged_in'] = "Erreur : vous devez être connecté.";
-$lang['public'] = "Public";
-$lang['unlisted'] = "Fantôme";
-$lang['private'] = "Privé";
-$lang['hello'] = "Bonjour";
-$lang['profile-message'] = "Vous pouvez gérer vos pastes sur cet écran de profil. Tous vos pastes publics, privés et fantômes sont listés ici. Vous pouvez également supprimer vos pastes depuis cet écran. Si un autre utilisateur visite votre profil, il ne verra que vos pastes publics.";
-$lang['profile-stats'] = "Quelques statistiques :";
-$lang['profile-total-pastes'] = "Nombre total de pastes :";
-$lang['profile-total-pub'] = "Nombre de pastes publics :";
-$lang['profile-total-unl'] = "Nombre de pastes fantômes :";
-$lang['profile-total-pri'] = "Nombre de pastes privés :";
-$lang['profile-total-views'] = "Nombre total de vues :";
-$lang['embed-hosted-by'] = "hébergé par";
-$lang['view-raw'] = "Voir les données brutes";
-
diff --git a/includes/langs/index.php b/includes/langs/index.php
deleted file mode 100644
index 0519ecb..0000000
--- a/includes/langs/index.php
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/includes/langs/pl.php b/includes/langs/pl.php
deleted file mode 100644
index c5cd5c0..0000000
--- a/includes/langs/pl.php
+++ /dev/null
@@ -1,122 +0,0 @@
- (June, 2017)
- */
-
-$lang = array();
-
-$lang['banned'] = "Zostałeś zablokowany na " . $site_name;
-$lang['expired'] = "Wklejka, którą próbujesz odwiedzić, utraciła ważność.";
-$lang['guestwelcome'] = $site_name . " pozwala przechowywać tekst i kod.";
-$lang['pleaseregister'] = " Zaloguj się lub zarejestruj , aby wysłać nową wklejkę. To nic nie kosztuje.";
-$lang['registertoedit'] = "Zaloguj się lub zarejestruj , aby edytować lub powielić tą wklejkę. To nic nie kosztuje.";
-$lang['editpaste'] = "Edytuj";
-$lang['forkpaste'] = "Powiel";
-$lang['guestmsgtitle'] = $site_name . " pozwala na przechowywanie kodu źródłowego i tekstu.";
-$lang['guestmsgbody'] = "Zaloguj się lub zarejestruj aby edytować, usuwać i mieć kontrolę nad swoimi wklejkami.";
-$lang['emptypastebin'] = "Brak wklejek do pokazania.";
-$lang['siteprivate'] = "Ta strona jest prywatna.";
-$lang['image_wrong'] = "Nieprawidłowy kod.";
-$lang['missing-input-response'] = "Parametr odpowiedzi reCAPTCHA nie jest prawidłowy. Zweryfikuj ustawienia PASTE.";
-$lang['missing-input-secret'] = "Brak tajnego parametru reCAPTCHA. Dodaj go do ustawień reCAPTCHA.";
-$lang['missing-input-response'] = "Parametr odpowiedzi reCAPTCHA jest nieprawidłowy. Spróbuj wykonać reCAPTCHA ponownie.";
-$lang['invalid-input-secret'] = "Patametr odpowiedzi reCAPTCHA jest nieprawidłowy. Sprawdź poprawność ustawień PASTE.";
-$lang['empty_paste'] = "Nie możesz umieścić pustej wklejki.";
-$lang['large_paste'] = "Wysłana wklejka jest zbyt duża. Maksymalny rozmiar wynosi " . $pastelimit . "MB.";
-$lang['paste_db_error'] = "Nie udało się umieścić w bazie danych.";
-$lang['error'] = "Coś poszło nie tak.";
-$lang['archive'] = "Archiwum wklejek";
-$lang['contact'] = "Kontakt";
-$lang['full_name'] = "Musisz wprowadzić imię i nazwisko.";
-$lang['email'] = "Musisz wprowadzić adres e-mail.";
-$lang['email_invalid'] = "Wprowadzony adres e-mail jest nieprawidłowy.";
-$lang['message'] = "Musisz wprowadzić wiadomość.";
-$lang['login/register'] = "Zaloguj się/Zarejestruj";
-$lang['rememberme'] = "Nie wylogowywuj mnie.";
-$lang['mail_acc_con'] = "Potwierdzenie konta na $site_name";
-$lang['mail_suc'] = "Pomyślnie przesłano kod weryfikacyjny na podany e-mail.";
-$lang['email_ver'] = "Już zweryfikowano adres e-mail.";
-$lang['email_not'] = "Nie znaleziono adresu e-mail.";
-$lang['pass_change'] = "Pomyślnie zmieniono hasło i przesłano na adres e-mail.";
-$lang['notverified'] = "Nie zweryfikowano konto.";
-$lang['incorrect'] = "Nieprawidłowa nazwa użytkownika/hasło";
-$lang['missingfields'] = "Wszystkie pola muszą być wypełnione.";
-$lang['userexists'] = "Nazwa użytkownika jest zajęta.";
-$lang['emailexists'] = "Istnieje konto przypisane do tego adresu e-mail.";
-$lang['registered'] = "Pomyślnie zarejestrowano konto.";
-$lang['usrinvalid'] = "Nazwa użytkownika może zawierać wyłącznie litery i cyfry.";
-$lang['mypastes'] = "Moje wklejki";
-$lang['pastedeleted'] = "Usunięto wklejkę.";
-$lang['databaseerror'] = "Nie udało się umieścić w bazie danych.";
-$lang['userchanged'] = "Pomyślnie zmieniono nazwę użytkownika.";
-$lang['usernotvalid'] = "Nieprawidłowa nazwa użytkownika.";
-$lang['privatepaste'] = "To jest prywatna wklejka.";
-$lang['wrongpassword'] = 'Nieprawidłowe hasło.';
-$lang['pwdprotected'] = 'Wklejka chroniona hasłem';
-$lang['notfound'] = "Nie znaleziono";
-$lang['wrongpwd'] = "Nieprawidłowe hasło. Spróbuj ponownie.";
-$lang['myprofile'] = "Moje konto";
-$lang['profileerror'] = "Nie udało się zaktualizować informacje o koncie ";
-$lang['profileupdated'] = "Pomyślnie zaktualizowano informacje o koncie ";
-$lang['oldpasswrong'] = "Nieprawidłowe aktualne hasło.";
-$lang['archives'] = "Archiwum wklejek";
-$lang['archivestitle'] = "Ta strona zawiera 100 najnowszych publicznych wklejek.";
-$lang['pastetitle'] = "Tytuł wklejki";
-$lang['pastetime'] = "Czas dodania wklejki";
-$lang['pastesyntax'] = "Składnia wklejki";
-$lang['pasteviews'] = "Wyświetlenia wklejki";
-$lang['wentwrong'] = "Coś poszło nie tak.";
-$lang['versent'] = "Link weryfikacyjny został wysłany na podany adres e-mail.";
-$lang['modpaste'] = "Modyfikuj wklejkę";
-$lang['newpaste'] = "Nowa wklejka";
-$lang['highlighting'] = "Podświetlanie składni";
-$lang['expiration'] = "Data ważności wklejki";
-$lang['visibility'] = "Widoczność wklejki";
-$lang['pwopt'] = "Hasło (opcjonalne)";
-$lang['encrypt'] = "Zaszyfruj w bazie danych";
-$lang['entercode'] = "Wprowadź kod";
-$lang['almostthere'] = "Już prawie. Pozostał jeden krok.";
-$lang['username'] = "Nazwa użytkownika";
-$lang['autogen'] = "Wygenerowana nazwa";
-$lang['setuser'] = "Ustaw swoją nazwę";
-$lang['keepuser'] = "Pozostaw wygenerowaną nazwę";
-$lang['enterpwd'] = "Wprowadź hasło";
-$lang['totalpastes'] = "Wklejki łącznie:";
-$lang['membtype'] = "Rodzaj konta:";
-$lang['email'] = "E-mail";
-$lang['fullname'] = "Nazwa";
-$lang['chgpwd'] = "Zmień hasło";
-$lang['curpwd'] = "Obecne hasło";
-$lang['newpwd'] = "Nowe hasło";
-$lang['confpwd'] = "Potwierdź hasło";
-$lang['mypastes'] = "Moje wklejki";
-$lang['viewpastes'] = "Pokaż wszystkie moje wklejki";
-$lang['recentpastes'] = "Najnowsze wklejki";
-$lang['user_public_pastes'] = "Wklejki użytkownika";
-$lang['yourpastes'] = "Twoje wklejki";
-$lang['mypastestitle'] = "Wszystkie twoje wklejki w jednym miejscu.";
-$lang['delete'] = "Usuń";
-$lang['highlighted'] = "Tekst jest zaznaczony, naciśnij Ctrl+C aby skopiować. (⌘+C na Macu)";
-$lang['newpaste'] = "Nowa wklejka";
-$lang['download'] = "Pobierz";
-$lang['showlineno'] = "Pokaż/ukryj wiersz nr.";
-$lang['copyto'] = "Kopiuj tekst do schowka";
-$lang['rawpaste'] = "Surowy plik wklejki";
-$lang['membersince'] = "Data dołączenia: ";
-$lang['delete_error_invalid'] = "Błąd: Nie usunięto wklejki. Wklejka nie istnieje lub nie jesteś jej autorem.";
-$lang['not_logged_in'] = "Błąd: Musisz zalogować się, aby to zrobić.";
-$lang['public'] = "Publiczna";
-$lang['unlisted'] = "Niewidoczna";
-$lang['private'] = "Prywatna";
-$lang['hello'] = "Witaj";
-$lang['profile-message'] = "To jest strona twojego konta, na której możesz zarządzać wklejkami. Znajdują się tu twoje wszystkie – prywatne, publiczne i niewidoczne wklejki. Możesz je tutaj usunąć. Inni użytkownicy mogą zobaczyć tu tylko twoje publiczne wklejki.";
-$lang['profile-stats'] = "Twoje statystyki:";
-$lang['profile-total-pastes'] = "Wklejki łącznie:";
-$lang['profile-total-pub'] = "Publiczne wklejki:";
-$lang['profile-total-unl'] = "Niewidoczne wklejki:";
-$lang['profile-total-pri'] = "Prywatne wklejki:";
-$lang['profile-total-views'] = "Wyświetlenia wszystkich wklejek:";
-$lang['embed-hosted-by'] = "przechowywane na";
-$lang['view-raw'] = "Surowy plik";
-
diff --git a/includes/langs/ru.php b/includes/langs/ru.php
deleted file mode 100644
index 3304baa..0000000
--- a/includes/langs/ru.php
+++ /dev/null
@@ -1,7 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-
-$directory = 'install';
-
-if (file_exists($directory)) {
- header("Location: install");
- exit();
-}
-
-// Required functions
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/captcha.php');
-require_once('includes/functions.php');
-require_once('includes/Tag.class.php');
-
-function verifyCaptcha() : string|bool {
- global $captcha_config;
- global $lang;
- global $current_user;
-
- if ($captcha_config['enabled'] && !$current_user) {
- $scode = strtolower(htmlentities(Trim($_POST['scode'])));
- $cap_code = strtolower($_SESSION['captcha']['code']);
- if ($cap_code !== $scode) {
- return $lang['image_wrong']; // Wrong captcha.
- }
- }
-
- return true;
-}
-
-/**
- * Calculate the expiry of a paste based on user input.
- *
- * @param string $expiry Expiry time.
- * SELF means to expire upon one view. +10M, +1H, +1D, +1W, +2W, +1M all do the obvious.
- * Anything unhandled means to expire never.
- * @return string|null Expiry time, or NULL if expires never.
- */
-function calculatePasteExpiry(string $expiry) {
- // used to use mktime
- if ($expiry === 'self') {
- return 'SELF'; // What does this do?
- }
-
- $valid_expiries = ['10M', '1H', '1D', '1W', '2W', '1M'];
-
- return in_array($expiry, $valid_expiries)
- ? (new DateTime())->add(new DateInterval("P{$expiry}"))->format('U')
- : null;
-}
-
-function validatePasteFields() : string|null {
- global $lang;
-
- if (empty($_POST["paste_data"]) || trim($_POST['paste_data'] === '')) { /* Empty paste input */
- return $lang['empty_paste'];
- } elseif (!isset($_POST['title'])) { /* No paste title POSTed */
- return $lang['error'];
- } elseif (empty($_POST["tag_input"])) { /* No tags provided */
- return $lang['notags'];
- } elseif (strlen($_POST["title"]) > 70) { /* Paste title too long */
- return $lang['titlelen'];
- } elseif (mb_strlen($_POST["paste_data"], '8bit') > PP_PASTE_LIMIT_BYTES) { /* Paste size too big */
- return $lang['large_paste'];
- }
-
- return null;
-}
-
-// UTF-8
-header('Content-Type: text/html; charset=utf-8');
-
-// Current date & user IP
-$date = date('jS F Y');
-$ip = $_SERVER['REMOTE_ADDR'];
-
-// Sitemap
-$site_sitemap_rows = $conn->query('SELECT * FROM sitemap_options LIMIT 1');
-if ($row = $site_sitemap_rows->fetch()) {
- $priority = $row['priority'];
- $changefreq = $row['changefreq'];
-}
-
-if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
- if ($captcha_config['enabled']) {
- $_SESSION['captcha'] = captcha($captcha_config['colour'], $captcha_config['mode'], $captcha_config['multiple'], $captcha_config['allowed']);
- }
-}
-
-updatePageViews($conn);
-
-// POST Handler
-if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- $error = validatePasteFields();
-
- if ($error !== null) {
- goto OutPut;
- }
-
- $captchaResponse = verifyCaptcha();
-
- if ($captchaResponse !== true) {
- $error = $captchaResponse;
- goto OutPut;
- }
-
- $editing = isset($_POST['edit']);
-
- $p_title = trim(htmlspecialchars($_POST['title']));
-
- if (empty($p_title)) {
- $p_title = 'Untitled';
- }
-
- $p_content = htmlspecialchars($_POST['paste_data']);
- $p_visible = trim(htmlspecialchars($_POST['visibility']));
- $p_code = trim(htmlspecialchars($_POST['format']));
- $p_expiry = trim(htmlspecialchars($_POST['paste_expire_date']));
- $p_password = $_POST['pass'];
- $tag_input = $_POST['tag_input'];
-
- if (empty($p_password)) {
- $p_password = null;
- } else {
- $p_password = password_hash($p_password, PASSWORD_DEFAULT);
- }
-
- $p_encrypt = $_POST['encrypted'];
- if ($p_encrypt == "" || $p_encrypt == null) {
- $p_encrypt = "0";
- } else {
- // Encrypt option
- $p_encrypt = "1";
- $p_content = openssl_encrypt($p_content, PP_ENCRYPTION_ALGO, PP_ENCRYPTION_KEY);
- }
-
- // Set expiry time
- $expires = calculatePasteExpiry($p_expiry);
-
- // Edit existing paste or create new?
- if ($editing) {
- if ($current_user &&
- $current_user->user_id === (int)$conn->querySelectOne('SELECT user_id FROM pastes WHERE id = ?', [$_POST['paste_id']])['user_id']) {
- $paste_id = intval($_POST['paste_id']);
-
- $conn->query(
- "UPDATE pastes SET title = ?, content = ?, visible = ?, code = ?, expiry = ?, password = ?, encrypt = ?, ip = ?, updated_at = NOW()
- WHERE id = ?",
- [$p_title, $p_content, $p_visible, $p_code, $expires, $p_password, $p_encrypt, $ip, $paste_id]
- );
-
- Tag::replacePasteTags($conn, $paste_id, Tag::parseTagInput($tag_input));
- } else {
- $error = $lang['loginwarning']; //"You must be logged in to do that."
- }
- } else {
- $paste_owner = $current_user ? $current_user->user_id : 1; /* 1 is the guest user's user ID */
-
- $paste_id = $conn->queryInsert(
- "INSERT INTO pastes (title, content, visible, code, expiry, password, encrypt, user_id, created_at, ip, views) VALUES
- (?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, 0)",
- [$p_title, $p_content, $p_visible, $p_code, $expires, $p_password, $p_encrypt, $paste_owner, $ip]
- );
-
- Tag::replacePasteTags($conn, $paste_id, Tag::parseTagInput($tag_input));
-
- if ($p_visible == '0') {
- addToSitemap($paste_id, $priority, $changefreq, $mod_rewrite);
- }
- }
-
- // Redirect to paste on successful entry, or on successful edit redirect back to edited paste
- if (isset($paste_id)) {
- header('Location: ' . urlForPaste($paste_id));
- die();
- }
-}
-
-OutPut:
-// Theme
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/main.php');
-require_once('theme/' . $default_theme . '/footer.php');
diff --git a/js/archive.js b/js/archive.js
new file mode 100644
index 0000000..097881a
--- /dev/null
+++ b/js/archive.js
@@ -0,0 +1,30 @@
+import { escape, whenReady } from './dom';
+import { DataTable, dumbFilterCallback } from './data_tables';
+import { tagsToHtml } from "./utils";
+import { globalSetup } from './main';
+
+whenReady(() => {
+ globalSetup();
+
+ const urlParams = new URLSearchParams(window.location.search);
+ const myParam = urlParams.get('q');
+ const apiUrl = /* myParam !== null ? '/api/ajax_pastes.php?q=' + myParam : */ '/api/ajax_pastes.php';
+
+ const table = new DataTable(document.getElementById('archive'), {
+ ajaxCallback: (resolve) => {
+ fetch(apiUrl)
+ .then(r => r.json())
+ .then(resolve);
+ },
+ rowCallback: (rowData) => {
+ return `
+ ${escape(rowData.title)}
+ ${escape(rowData.author)}
+ ${tagsToHtml(rowData.tags)}
+ `;
+ },
+ filterCallback: dumbFilterCallback,
+ preFilter: myParam
+ });
+ table.attach();
+});
\ No newline at end of file
diff --git a/js/data_tables.js b/js/data_tables.js
new file mode 100644
index 0000000..b6586f7
--- /dev/null
+++ b/js/data_tables.js
@@ -0,0 +1,256 @@
+import { makeEl, clearEl } from "./dom";
+
+class SimplePaginator {
+ constructor(element) {
+ this.element = element;
+ }
+
+ attach(pageCallback) {
+ this.element.addEventListener('click', evt => {
+ if (evt.target && evt.target.classList.contains('paginator__button')) {
+ pageCallback(+evt.target.dataset.page);
+ }
+ });
+ }
+
+ update(totalRecords, perPage, currentPage) {
+ clearEl(this.element);
+
+ /* First and last page in existence */
+ const firstPage = 0;
+ const lastPage = Math.floor(totalRecords / perPage); // ish?
+ const numPagesToShow = 2;
+
+ if (lastPage === firstPage) {
+ return;
+ }
+
+ /* First and last page the main paginator will show */
+ const firstPageShow = (currentPage - numPagesToShow) < firstPage ? firstPage : (currentPage - numPagesToShow);
+ const lastPageShow = (currentPage + numPagesToShow) > lastPage ? lastPage : (currentPage + numPagesToShow);
+
+ /* Whether to show the first and last pages in existence at the ends of the paginator */
+ const showFirstPage = (Math.abs(firstPage - currentPage)) > (numPagesToShow);
+ const showLastPage = (Math.abs(lastPage - currentPage)) > (numPagesToShow);
+
+
+ const prevButtonDisabled = currentPage === firstPage ? 'disabled' : ''
+
+ /* Previous button */
+ this.element.appendChild(makeEl(
+ `Previous `
+ ));
+
+ /* First page button */
+ if (showFirstPage) {
+ this.element.appendChild(makeEl(
+ `${firstPage} `
+ ));
+ this.element.appendChild(makeEl(`… `));
+ }
+
+ /* "window" buttons */
+ for (let i = firstPageShow; i <= lastPageShow; i++) {
+ const selected = (i === currentPage ? 'paginator__button--selected' : '');
+ this.element.appendChild(makeEl(
+ `${i} `
+ ));
+ }
+
+ /* Last page button */
+ if (showLastPage) {
+ this.element.appendChild(makeEl(`… `));
+ this.element.appendChild(makeEl(
+ `${lastPage} `
+ ));
+ }
+
+ const nextButtonDisabled = currentPage === lastPage ? 'disabled' : ''
+ /* Next button */
+ this.element.appendChild(makeEl(
+ `Next `
+ ));
+ }
+}
+
+class DataTable {
+ constructor(element, options) {
+ this.element = element;
+ this.container = element.parentElement;
+ this.options = options;
+
+ this.ajaxCallback = options.ajaxCallback;
+ this.data = [];
+ this.unfilteredData = [];
+
+ this.totalRecords = -1;
+ this.perPage = 20;
+ this.currentPage = 0;
+
+ this.paginator = new SimplePaginator(this.container.querySelector('.paginator'));
+
+ this.filterCallback = options.filterCallback;
+ this.sortField = null;
+ this.sortDir = true;
+ }
+
+ attach() {
+ this.filterField = this.container.querySelector('input.search');
+ if (this.filterField && this.filterCallback) {
+ this.filterField.addEventListener('keyup', evt => {
+ if (evt.target) {
+ this._updateFilter(evt.target.value);
+ }
+ });
+
+ if (this.options.preFilter) {
+ this.filterField.value = this.options.preFilter;
+ }
+ }
+
+ this.perPageField = this.container.querySelector('select[name=per_page]');
+
+ if (this.perPageField) {
+ this.perPageField.addEventListener('change', evt => {
+ this.perPage = Number(evt.target.value);
+ this._updatePage(0);
+ });
+ }
+
+ const header = this.element.querySelector('tr.paginator__sort');
+
+ if (header) {
+ header.addEventListener('click', evt => {
+ const target = evt.target;
+
+ if (!target.dataset.sortField) {
+ return;
+ }
+
+ if (this.sortField) {
+ const elem = this.element.querySelector(`th[data-sort-field=${this.sortField}]`)
+ elem.classList.remove('paginator__sort--down');
+ elem.classList.remove('paginator__sort--up');
+ }
+
+ this._updateSort(target.dataset.sortField, !this.sortDir);
+
+ target.classList.add(this.sortDir ? 'paginator__sort--up' : 'paginator__sort--down');
+ });
+ }
+
+ this.paginator.attach(this._updatePage.bind(this));
+ this._loadEntries();
+ }
+
+ /* Load the requested data from the server, and when done, update the DOM. */
+ _loadEntries() {
+ new Promise(this.ajaxCallback)
+ .then(data => {
+ this.element.classList.remove('hidden');
+ this.unfilteredData = data.data;
+ this._updateFilter(this.options.preFilter);
+ });
+ }
+
+ /* Update the DOM to reflect the current state of the data we have loaded */
+ _updateEntries(data) {
+ this.data = data;
+ this.totalRecords = this.data.length;
+
+ const bodyElement = this.element.querySelector('tbody');
+ clearEl(bodyElement);
+
+ const firstIndex = (this.perPage * this.currentPage);
+ const lastIndex = (firstIndex + this.perPage) > this.totalRecords ? this.totalRecords : (firstIndex + this.perPage);
+
+
+ for (let i = firstIndex; i < lastIndex; i++) {
+ const rowElem = makeEl(this.options.rowCallback(this.data[i]));
+ rowElem.classList.add(i % 2 === 0 ? 'odd' : 'even');
+
+ bodyElement.appendChild(rowElem);
+ }
+
+ this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
+ }
+
+ _updatePage(n) {
+ this.currentPage = n;
+ this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
+ this._updateEntries(this.data);
+ }
+
+ _updateFilter(query) {
+ /* clearing the query */
+ if (query === null || query === '') {
+ this._updateEntries(this.unfilteredData);
+ return;
+ }
+
+ let data = [];
+ for (const datum of this.unfilteredData) {
+ if (this.filterCallback(datum, query)) {
+ data.push(datum);
+ }
+ }
+
+ this._updatePage(0)
+ this._updateEntries(data);
+ }
+
+ _updateSort(field, direction) {
+ this.sortField = field;
+ this.sortDir = direction;
+
+ let newEntries = [...this.data].sort((a, b) => {
+ let sorter = 0;
+
+ if (a[field] > b[field]) {
+ sorter = 1;
+ } else if (a[field] < b[field]) {
+ sorter = -1;
+ }
+
+ if (!direction) {
+ sorter = -sorter;
+ }
+
+ return sorter;
+ });
+
+ this._updatePage(0);
+ this._updateEntries(newEntries);
+ }
+}
+
+const dumbFilterCallback = (datum, query) => {
+ if (!query) {
+ return true;
+ }
+
+ const queryLower = query.toLowerCase();
+
+ if (queryLower === 'untagged' && datum.tags.length === 0) {
+ return true;
+ }
+
+ if (datum.title.toLowerCase().indexOf(queryLower) !== -1) {
+ return true;
+ }
+
+ if (datum.author.toLowerCase().indexOf(queryLower) !== -1) {
+ return true;
+ }
+
+ /* this is inefficient */
+ for (const tag of datum.tags) {
+ if (tag.name.toLowerCase().indexOf(queryLower) !== -1) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+export { DataTable, dumbFilterCallback };
diff --git a/js/dom.js b/js/dom.js
new file mode 100644
index 0000000..3e8cbe1
--- /dev/null
+++ b/js/dom.js
@@ -0,0 +1,48 @@
+const $ = function(selector) {
+ return document.querySelector(selector);
+};
+
+const $$ = function(selector) {
+ return document.querySelectorAll(selector) || [];
+};
+
+const makeEl = function(html) {
+ const template = document.createElement('template');
+
+ template.innerHTML = html.trim();
+
+ return template.content.firstChild;
+};
+
+const clearEl = function(el) {
+ while (el.firstChild) {
+ el.removeChild(el.firstChild);
+ }
+};
+
+const toggleEl = function(el) {
+ if (el.classList.contains('is-hidden')) {
+ el.classList.remove('is-hidden');
+ } else {
+ el.classList.add('is-hidden');
+ }
+};
+
+const escape = function(unsafe) {
+ return unsafe
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+const whenReady = function(funcp) {
+ if (document.readyState !== 'loading') {
+ funcp();
+ } else {
+ document.addEventListener('DOMContentLoaded', funcp);
+ }
+}
+
+export { whenReady, $, $$, makeEl, clearEl, toggleEl, escape };
\ No newline at end of file
diff --git a/js/generic.js b/js/generic.js
new file mode 100644
index 0000000..a9cc177
--- /dev/null
+++ b/js/generic.js
@@ -0,0 +1,4 @@
+import { whenReady } from "./dom";
+import { globalSetup } from "./main";
+
+whenReady(globalSetup);
diff --git a/js/main.js b/js/main.js
new file mode 100644
index 0000000..b90a407
--- /dev/null
+++ b/js/main.js
@@ -0,0 +1,115 @@
+import { $, $$, toggleEl } from './dom';
+import { TagsInput } from "./tag_input";
+
+const setupSignupModal = () => {
+ const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
+
+ if (signupButton) {
+ signupButton.href = 'javascript:void(0)';
+
+ signupButton.addEventListener('click', () => {
+ $('.modal').classList.add('is-active');
+ });
+
+ $('.modal-button-close').addEventListener('click', () => {
+ $('.modal').classList.remove('is-active');
+ });
+ }
+}
+
+const globalSetup = () => {
+ Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
+ new TagsInput(el).attach();
+ });
+
+ setupSignupModal();
+
+ const embedButton = $('.panel-tools .embed-tool');
+
+ if (embedButton){
+ embedButton.addEventListener('click', (evt) => {
+ if (evt.target && evt.target.closest('.panel-tools')) {
+ toggleEl(evt.target.closest('.panel-tools').querySelector('.panel-embed'));
+ }
+ });
+ }
+
+ const expandButton = $('.expand-tool');
+
+ if (expandButton) {
+ expandButton.addEventListener('click', (evt) => {
+ if (evt.target && evt.target.closest('.panel')) {
+ const panel = evt.target.closest('.panel');
+
+ if (panel.classList.contains('panel-fullsize')) {
+ panel.classList.remove('panel-fullsize');
+ } else {
+ panel.classList.add('panel-fullsize');
+ }
+ }
+ });
+ }
+
+ // Notifications
+ (document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
+ const $notification = $delete.parentNode;
+
+ $delete.addEventListener('click', () => {
+ $notification.parentNode.removeChild($notification);
+ });
+ });
+
+ // Hamburger menu
+ const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
+ if ($navbarBurgers.length > 0) {
+ $navbarBurgers.forEach(el => {
+ el.addEventListener('click', () => {
+ const target = el.dataset.target;
+ const $target = document.getElementById(target);
+ el.classList.toggle('is-active');
+ $target.classList.toggle('is-active');
+ });
+ });
+ }
+
+ const preloader = $('.preloader');
+ const main = $('main');
+
+ if (preloader && main) {
+ preloader.remove();
+ main.id = '';
+ }
+
+ // CAPTCHA refresh
+ const captchaContainer = $('.captcha_container');
+
+ if (captchaContainer) {
+ const refreshElement = captchaContainer.querySelector('a');
+ const imageElement = captchaContainer.querySelector('img');
+
+ if (refreshElement && imageElement) {
+ refreshElement.addEventListener('click', () => {
+ let src = imageElement.src;
+
+ if (src.indexOf('&refresh') !== -1) {
+ // yeah, it's kinda cancerous. fuck off.
+ src = src.split('&rand=')[0];
+ } else {
+ src += '&refresh';
+ }
+
+ imageElement.src = src + '&rand=' + Math.random();
+ });
+ }
+ }
+
+ const hiddenElements = $$('.js-hidden');
+
+ if (hiddenElements) {
+ Array.prototype.forEach.call(hiddenElements, (elem) => {
+ toggleEl(elem);
+ });
+ }
+}
+
+export { globalSetup };
\ No newline at end of file
diff --git a/js/tag_input.js b/js/tag_input.js
new file mode 100644
index 0000000..73f266a
--- /dev/null
+++ b/js/tag_input.js
@@ -0,0 +1,106 @@
+import { makeEl, escape } from "./dom";
+
+class TagsInput {
+ constructor(element, options = {}) {
+ this.element = element;
+ this.tags = [];
+ this.options = options
+
+ this.maxTags = options.maxTags || 32;
+ this.inputNode = null;
+ this.containerNode = null;
+ }
+
+ attach() {
+ this.element.style.display = 'none';
+
+ this.containerNode = makeEl('
');
+ this.inputNode = makeEl(' ');
+ this.containerNode.appendChild(this.inputNode);
+
+ this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);
+
+ /* Load existing tags from input */
+ if (this.element.value) {
+ for (const tag of this.element.value.split(',')) {
+ this.addTag(tag);
+ }
+ }
+
+ /* Handle addition and removal of tags via key-presses */
+ this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));
+
+ /* Handle deletions by clicking the delete button */
+ this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));
+ }
+
+ detach() {
+ this.tags.clear();
+ this.containerNode.remove();
+ this.element.style.display = 'inline-block';
+ }
+
+ updateHiddenInputValue() {
+ this.element.value = this.tags.join(',');
+ }
+
+ deleteTagNode(node) {
+ this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);
+ node.remove();
+
+ /* Below the limit? Make sure the input is enabled. */
+ if (this.tags.length < this.maxTags) {
+ this.inputNode.disabled = false;
+ }
+ }
+
+ addTag(tagValue) {
+ tagValue = tagValue.trim();
+
+ /* Tag value is probably not empty and we don't already have the same tag. */
+ if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {
+ this.tags.push(tagValue.toLowerCase());
+
+ this.inputNode.parentNode.insertBefore(
+ makeEl('' + escape(tagValue) + ' '),
+ this.inputNode
+ );
+
+ /* Too many tags, disable the input for now. */
+ if (this.tags.length >= this.maxTags) {
+ this.inputNode.disabled = true;
+ }
+ }
+ }
+
+ _handleInputKeyUp(evt) {
+ let tagValue = this.inputNode.value;
+
+ if (evt.key === 'Backspace' && tagValue === '') {
+ // Remove the child
+ if (this.inputNode.previousSibling) {
+ this.deleteTagNode(this.inputNode.previousSibling);
+
+ this.updateHiddenInputValue();
+ }
+ } else if (evt.key === ',') {
+ this.addTag(tagValue);
+
+ this.inputNode.value = ''
+ this.updateHiddenInputValue();
+
+ evt.preventDefault();
+ } else if (evt.key !== 'Backspace' && tagValue.length > 255) { // This could be improved to check if it would actually result in a new char being typed...
+ evt.preventDefault();
+ }
+ }
+
+ _handleContainerClick(evt) {
+ if (evt.target && evt.target.classList.contains('delete')) {
+ this.deleteTagNode(evt.target.closest('.tag'));
+ this.updateHiddenInputValue();
+ }
+ }
+}
+
+export { TagsInput };
diff --git a/js/user_profile.js b/js/user_profile.js
new file mode 100644
index 0000000..03457d7
--- /dev/null
+++ b/js/user_profile.js
@@ -0,0 +1,97 @@
+import { escape, whenReady } from './dom';
+import { DataTable, dumbFilterCallback } from './data_tables';
+import { tagsToHtml } from "./utils";
+import { globalSetup } from './main';
+
+const getUserInfo = () => {
+ const elem = document.getElementById('js-data-holder');
+
+ if (!elem) {
+ return { userId: null, csrfToken: null };
+ }
+
+ return { userId: elem.dataset.userId, csrfToken: elem.dataset.csrfToken };
+};
+
+const parsePasteInfo = (elem) => {
+ if (!elem.dataset.pasteInfo) {
+ return null;
+ }
+
+ return JSON.parse(elem.dataset.pasteInfo);
+};
+
+whenReady(() => {
+ globalSetup();
+
+ const urlParams = new URLSearchParams(window.location.search);
+ const myParam = urlParams.get('q');
+ const myPastesElem = document.getElementById('archive');
+ const apiUrl = '/api/user_pastes.php?user_id=' + myPastesElem.dataset.userId;
+ console.log('myPastesElem', myPastesElem);
+ const table = new DataTable(myPastesElem, {
+ ajaxCallback: (resolve) => {
+ fetch(apiUrl)
+ .then(r => r.json())
+ .then(resolve);
+ },
+ rowCallback: (rowData) => {
+ console.log('rowData', rowData);
+ const userData = getUserInfo();
+ const ownedByUser = (parseInt(rowData.author_id) === parseInt(userData.userId));
+ console.log(ownedByUser);
+ const deleteElem = ownedByUser ? `
+
+ ` : '';
+ const pasteCreatedAt = new Date(rowData.created_at).toLocaleString();
+ const pasteVisibility = ownedByUser ? `${['Public', 'Unlisted', 'Private'][rowData.visibility]} ` : '';
+
+ return `
+ ${escape(rowData.title)}
+ ${pasteCreatedAt}
+ ${pasteVisibility}
+ ${rowData.views || 0}
+ ${tagsToHtml(rowData.tags)}
+ ${deleteElem}
+ `;
+ },
+ filterCallback: dumbFilterCallback,
+ preFilter: myParam
+ });
+ table.attach();
+
+ const myFavesElem = document.getElementById('favs');
+
+ if (!myFavesElem) {
+ return;
+ }
+
+ const faveTable = new DataTable(myFavesElem, {
+ ajaxCallback: (resolve) => {
+ console.log('invoker invoked');
+ resolve({
+ data: Array.prototype.map.call(myFavesElem.querySelectorAll('tbody > tr'), parsePasteInfo)
+ });
+ },
+ rowCallback: (rowData) => {
+ const recentUpdate = rowData.recently_updated ?
+ ` ` :
+ ` `;
+ const pasteFavedAt = new Date(rowData.favourited_at).toLocaleString();
+
+ // ${escape(rowData.author)}
+ return `
+ ${escape(rowData.title)}
+ ${pasteFavedAt}
+ ${recentUpdate}
+ ${tagsToHtml(rowData.tags)}
+ `;
+ },
+ filterCallback: dumbFilterCallback
+ });
+ faveTable.attach();
+});
\ No newline at end of file
diff --git a/js/utils.js b/js/utils.js
new file mode 100644
index 0000000..0f41bea
--- /dev/null
+++ b/js/utils.js
@@ -0,0 +1,23 @@
+import { escape } from "./dom";
+
+const tagsToHtml = (tags) => {
+ return tags.map(tagData => {
+ let tagColorClass;
+ const tagLower = tagData.name.toLowerCase();
+ if (tagLower === 'nsfw' || tagLower === 'explicit') {
+ tagColorClass = 'is-danger';
+ } else if (tagLower === 'safe') {
+ tagColorClass = 'is-success';
+ } else if (tagLower.charAt(0) === '/' && tagLower.charAt(tagLower.length - 1) === '/') {
+ tagColorClass = 'is-primary';
+ } else {
+ tagColorClass = 'is-info';
+ }
+
+ return `
+ ${escape(tagData.name)}
+ `;
+ }).join('');
+};
+
+export { tagsToHtml };
diff --git a/login.php b/login.php
deleted file mode 100644
index 41d9151..0000000
--- a/login.php
+++ /dev/null
@@ -1,145 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-
-// Required functions
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/functions.php');
-require_once('includes/passwords.php');
-
-// Current Date & User IP
-$date = date('jS F Y');
-$ip = $_SERVER['REMOTE_ADDR'];
-
-
-// Check if already logged in
-if ($current_user !== null) {
- header("Location: ./");
- die();
-}
-
-// Page title
-$p_title = $lang['login/register']; // "Login/Register";
-
-updatePageViews($conn);
-
-if (isset($_POST['forgot'])) {
- if (!empty($_POST['username']) && !empty($_POST['recovery_code'])) {
- $username = trim($_POST['username']);
- $recovery_code = trim($_POST['recovery_code']);
-
- $query = $conn->query("SELECT id, recovery_code_hash FROM users WHERE username = ?", [$username]);
- $row = $query->fetch();
-
- if ($row && pp_password_verify($_POST['recovery_code'], $row['recovery_code_hash'])) {
- $new_password = pp_random_password();
- $new_password_hash = pp_password_hash($new_password);
-
- $recovery_code = pp_random_token();
- $new_recovery_code_hash = pp_password_hash($recovery_code);
-
- $conn->prepare('UPDATE users SET password = ?, recovery_code_hash = ? WHERE id = ?')
- ->execute([$new_password_hash, $new_recovery_code_hash, $row['id']]);
-
- $success = 'Your password has been changed. A new recovery code has also been generated. Please note the recovery code and then sign in with the new password.';
- } else {
- $error = $lang['incorrect'];
- }
- } else {
- $error = $lang['missingfields']; // "All fields must be filled out";
- }
-} elseif (isset($_POST['signin'])) { // Login process
- if (!empty($_POST['username']) && !empty($_POST['password'])) {
- $remember_me = (bool)$_POST['remember_me'];
- $username = trim($_POST['username']);
- $row = $conn->query("SELECT id, password, banned FROM users WHERE username = ?", [$username])
- ->fetch();
-
- $needs_rehash = false;
-
- /* This is designed to be a constant time lookup, hence the warning suppression operator so that
- * we always call pp_password_verify, even if row is null.
- */
- if (pp_password_verify($_POST['password'], @$row['password'], $needs_rehash)) {
- $user_id = $row['id'];
-
- if ($needs_rehash) {
- $new_password_hash = pp_password_hash($_POST['password']);
-
- $conn->query('UPDATE users SET password = ? WHERE id = ?',
- [$new_password_hash, $user_id]);
- }
-
- if ($row['banned']) {
- // User is banned
- $error = $lang['banned'];
- } else {
- // Login successful
- $_SESSION['user_id'] = (string)$user_id;
-
- if ($remember_me) {
- $remember_token = pp_random_token();
- $expire_at = (new DateTime())->add(new DateInterval('P1Y'));
-
- $conn->query('INSERT INTO user_sessions (user_id, token, expire_at) VALUES (?, ?, FROM_UNIXTIME(?))', [$user_id, $remember_token, $expire_at->format('U')]);
-
- setcookie(User::REMEMBER_TOKEN_COOKIE, $remember_token, [
- 'expires' => (int)$expire_at->format('U'),
- 'secure' => !empty($_SERVER['HTTPS']), /* Local dev environment is non-HTTPS */
- 'httponly' => true,
- 'samesite' => 'Lax'
- ]);
- }
-
- header('Location: ' . $_SERVER['HTTP_REFERER']);
- exit();
- }
- } else {
- // Username not found or password incorrect.
- $error = $lang['incorrect'];
- }
- } else {
- $error = $lang['missingfields']; // "All fields must be filled out.";
- }
-} elseif (isset($_POST['signup'])) { // Registration process
- $username = htmlentities(trim($_POST['username'], ENT_QUOTES));
- $password = pp_password_hash($_POST['password']);
- $chara_max = 25; //characters for max input
-
- if (empty($_POST['password']) || empty($_POST['username'])) {
- $error = $lang['missingfields']; // "All fields must be filled out";
- } elseif (strlen($username) > $chara_max) {
- $error = $lang['maxnamelimit']; // "Username already taken.";
- } elseif (preg_match('/[^A-Za-z0-9._\\-$]/', $username)) {
- $error = $lang['usrinvalid']; // "Username not valid. Usernames can't contain special characters.";
- } else {
- if ($conn->querySelectOne('SELECT 1 FROM users WHERE username = ?', [$username])) {
- $error = $lang['userexists']; // "Username already taken.";
- } else {
- $recovery_code = pp_random_token();
- $recovery_code_hash = pp_password_hash($recovery_code);
- $conn->query(
- "INSERT INTO users (username, password, recovery_code_hash, picture, date, ip, badge) VALUES (?, ?, ?, 'NONE', ?, ?, '0')",
- [$username, $password, $recovery_code_hash, $date, $ip]
- );
-
- $success = $lang['registered']; // "Your account was successfully registered.";
- }
- }
-}
-// Theme
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/login.php');
-require_once('theme/' . $default_theme . '/footer.php');
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..70a9356
--- /dev/null
+++ b/package.json
@@ -0,0 +1,19 @@
+{
+ "devDependencies": {
+ "@babel/core": "^7.15.0",
+ "@babel/preset-env": "^7.15.0",
+ "@rollup/plugin-babel": "^5.3.0",
+ "rollup": "^2.56.3",
+ "rollup-plugin-terser": "^7.0.2"
+ },
+ "name": "punishedponepaste",
+ "version": "1.0.0",
+ "main": "index.js",
+ "repository": "git@github.com:aftercase/punishedponepaste.git",
+ "author": "AppleDash ",
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/plugin-commonjs": "^24.0.1",
+ "@rollup/plugin-node-resolve": "^15.0.1"
+ }
+}
diff --git a/pages.php b/pages.php
deleted file mode 100644
index ba003d6..0000000
--- a/pages.php
+++ /dev/null
@@ -1,44 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/functions.php');
-
-// UTF-8
-header('Content-Type: text/html; charset=utf-8');
-
-$date = date('jS F Y');
-$ip = $_SERVER['REMOTE_ADDR'];
-
-updatePageViews($conn);
-
-if (isset($_GET['page'])) {
- $page_name = htmlspecialchars(trim($_GET['page']));
-
- $query = $conn->prepare('SELECT page_title, page_content, last_date FROM pages WHERE page_name = ?');
- $query->execute([$page_name]);
- if ($row = $query->fetch()) {
- $page_title = $row['page_title'];
- $page_content = $row['page_content'];
- $last_date = $row['last_date'];
- $stats = "OK";
- $p_title = $page_title;
- }
-}
-// Theme
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/pages.php');
-require_once('theme/' . $default_theme . '/footer.php');
-
diff --git a/paste.php b/paste.php
deleted file mode 100644
index 52f8ab9..0000000
--- a/paste.php
+++ /dev/null
@@ -1,216 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-
-// UTF-8
-header('Content-Type: text/html; charset=utf-8');
-
-// Required functions
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/functions.php');
-require_once('includes/Tag.class.php');
-require_once('includes/passwords.php');
-
-use Highlight\Highlighter;
-
-function rawView($content, $p_code) {
- if ($p_code) {
- header('Content-Type: text/plain');
- echo $content;
- } else {
- header('HTTP/1.1 404 Not Found');
- }
-}
-
-$paste_id = intval(trim($_REQUEST['id']));
-
-updatePageViews($conn);
-
-// This is used in the theme files.
-$totalpastes = getSiteTotalPastes($conn);
-
-// Get paste favorite count
-$fav_count = $conn->querySelectOne('SELECT COUNT(*) FROM pins WHERE paste_id = ?', [$paste_id], PDO::FETCH_NUM)[0];
-
-// Get paste info
-$row = $conn->querySelectOne(
- 'SELECT title, content, visible, code, expiry, pastes.password AS password, created_at, updated_at, encrypt, views, users.username AS member, users.id AS user_id
- FROM pastes
- INNER JOIN users ON users.id = pastes.user_id
- WHERE pastes.id = ?', [$paste_id]);
-
-
-$notfound = null;
-$is_private = false;
-
-if ($row === null) {
- header('HTTP/1.1 404 Not Found');
- $notfound = $lang['notfound']; // "Not found";
- goto Not_Valid_Paste;
-}
-
-$paste_owner_id = (int) $row['user_id'];
-$paste_title = $row['title'];
-$paste_code = $row['code'];
-$using_highlighter = $paste_code !== 'pastedown';
-
-$paste = [
- 'title' => $paste_title,
- 'created_at' => (new DateTime($row['created_at']))->format('jS F Y h:i:s A'),
- 'updated_at' => (new DateTime($row['updated_at']))->format('jS F Y h:i:s A'),
- 'user_id' => $paste_owner_id,
- 'member' => $row['member'],
- 'views' => $row['views'],
- 'code' => $paste_code,
- 'tags' => getPasteTags($conn, $paste_id)
-];
-
- $p_member = $row['member'];
- $p_content = $row['content'];
- $p_visible = $row['visible'];
- $p_expiry = $row['expiry'];
- $p_password = $row['password'];
- $p_encrypt = (bool) $row['encrypt'];
-
-
-$is_private = $row['visible'] === '2';
-
-if ($is_private && (!$current_user || $current_user->user_id !== $paste_owner_id)) {
- $notfound = $lang['privatepaste']; //" This is a private paste. If you created this paste, please login to view it.";
- goto Not_Valid_Paste;
-}
-
-/* Verify paste password */
-$password_required = $p_password !== null && $p_password !== 'NONE';
-$password_valid = true;
-$password_candidate = '';
-
-if ($password_required) {
- if (!empty($_POST['mypass'])) {
- $password_candidate = $_POST['mypass'];
- } else if (!empty($_GET['password'])) {
- $password_candidate = @base64_decode($_GET['password']);
- }
-
- if (empty($password_candidate)) {
- $password_valid = false;
- $error = $lang['pwdprotected']; // 'Password protected paste';
- goto Not_Valid_Paste;
- } elseif (!pp_password_verify($password_candidate, $p_password)) {
- $password_valid = false;
- $error = $lang['wrongpassword']; // 'Wrong password';
- goto Not_Valid_Paste;
- }
-}
-
-if (!empty($p_expiry) && $p_expiry !== 'SELF') {
- $input_time = $p_expiry;
- $current_time = mktime(date("H"), date("i"), date("s"), date("n"), date("j"), date("Y"));
- if ($input_time < $current_time) {
- $notfound = $lang['expired'];
- goto Not_Valid_Paste;
- }
-}
-
-if ($p_encrypt == 1) {
- $p_content = openssl_decrypt($p_content, PP_ENCRYPTION_ALGO, PP_ENCRYPTION_KEY);
-}
-
-$op_content = trim(htmlspecialchars_decode($p_content));
-
-// Download the paste
-if (isset($_GET['download'])) {
- doDownload($paste_id, $paste_title, $p_member, $op_content, $paste_code);
- exit();
-}
-
-// Raw view
-if (isset($_GET['raw'])) {
- rawView($op_content, $paste_code);
- exit();
-}
-
-// Preprocess
-$highlight = array();
-$prefix_size = strlen('!highlight!');
-$lines = explode("\n", $p_content);
-$p_content = "";
-foreach ($lines as $idx => $line) {
- if (substr($line, 0, $prefix_size) == '!highlight!') {
- $highlight[] = $idx + 1;
- $line = substr($line, $prefix_size);
- }
- $p_content .= $line . "\n";
-}
-
-$p_content = rtrim($p_content);
-
-// Apply syntax highlight
-$p_content = htmlspecialchars_decode($p_content);
-if ($paste_code === "pastedown") {
- $parsedown = new Parsedown();
- $parsedown->setSafeMode(true);
- $p_content = $parsedown->text($p_content);
-} else {
- Highlighter::registerLanguage('green', 'config/green.lang.json');
- $hl = new Highlighter();
- $highlighted = $hl->highlight($paste_code == 'text' ? 'plaintext' : $paste_code, $p_content)->value;
- $lines = HighlightUtilities\splitCodeIntoArray($highlighted);
-}
-
-// Embed view after highlighting is applied so that $p_code is syntax highlighted as it should be.
-if (isset($_GET['embed'])) {
- embedView($paste_id, $paste_title, $p_content, $paste_code, $title, $baseurl, $ges_style, $lang);
- exit();
-}
-
-require_once('theme/' . $default_theme . '/header.php');
-if ($password_required && $password_valid) {
- /* base64 here means that the password is exposed in the URL, technically - how to handle this better? */
- $p_download = "paste.php?download&id=$paste_id&password=" . base64_encode($password_candidate);
- $p_raw = "paste.php?raw&id=$paste_id&password=" . base64_encode($password_candidate);
- $p_embed = "paste.php?embed&id=$paste_id&password=" . base64_encode($password_candidate);
-} else {
- // Set download URL
- if (PP_MOD_REWRITE) {
- $p_download = "download/$paste_id";
- $p_raw = "raw/$paste_id";
- $p_embed = "embed/$paste_id";
- } else {
- $p_download = "paste.php?download&id=$paste_id";
- $p_raw = "paste.php?raw&id=$paste_id";
- $p_embed = "paste.php?embed&id=$paste_id";
- }
-}
-
-// View counter
-if (@$_SESSION['not_unique'] !== $paste_id) {
- $_SESSION['not_unique'] = $paste_id;
- $conn->query("UPDATE pastes SET views = (views + 1) where id = ?", [$paste_id]);
-}
-
-require_once('theme/' . $default_theme . '/view.php');
-
-Not_Valid_Paste:
-
-if ($is_private || $notfound || !$password_valid) {
- // Display errors
- require_once('theme/' . $default_theme . '/header.php');
- require_once('theme/' . $default_theme . '/errors.php');
-}
-
-// Footer
- require_once('theme/' . $default_theme . '/footer.php');
-
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..3947f27
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,21 @@
+
+ My custom coding standard.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/profile.php b/profile.php
deleted file mode 100644
index 6f8e116..0000000
--- a/profile.php
+++ /dev/null
@@ -1,73 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
-
-define('IN_PONEPASTE', 1);
-require_once('includes/common.php');
-require_once('includes/functions.php');
-require_once('includes/passwords.php');
-
-// UTF-8
-header('Content-Type: text/html; charset=utf-8');
-
-$date = date('jS F Y');
-$ip = $_SERVER['REMOTE_ADDR'];
-
-
-$p_title = $lang['myprofile']; //"My Profile";
-
-
-// Check if already logged in
-if ($current_user === null) {
- header("Location: ./login.php");
- die();
-}
-
-$user_username = $current_user->username;
-
-$query = $conn->query('SELECT * FROM users WHERE id = ?', [$current_user->user_id]);
-$row = $query->fetch();
-$user_id = $row['id'];
-$user_platform = Trim($row['platform']);
-$user_date = $row['date'];
-$user_ip = $row['ip'];
-$user_password = $row['password'];
-
-if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- if (isset($_POST['cpassword'])) {
- $user_new_full = trim(htmlspecialchars($_POST['full']));
- $user_old_pass = $_POST['old_password'];
- if (pp_password_verify($user_old_pass, $user_password)) {
- $user_new_cpass = pp_password_hash($_POST['password']);
-
- $conn->prepare('UPDATE users SET password = ? WHERE id = ?')
- ->execute([$user_new_cpass, $user_id]);
-
- $success = $lang['profileupdated']; //" Your profile information is updated ";
- } else {
- $error = $lang['oldpasswrong']; // " Your old password is wrong.";
- }
- } else {
- $error = $lang['error']; //"Something went wrong.";
- }
-}
-
-updatePageViews($conn);
-
-$total_user_pastes = getTotalPastes($conn, $current_user->user_id);
-
-// Theme
-require_once('theme/' . $default_theme . '/header.php');
-require_once('theme/' . $default_theme . '/profile.php');
-require_once('theme/' . $default_theme . '/footer.php');
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 0000000..008f2d8
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/admin/admin.php b/public/admin/admin.php
similarity index 56%
rename from admin/admin.php
rename to public/admin/admin.php
index 8704688..b01f60b 100644
--- a/admin/admin.php
+++ b/public/admin/admin.php
@@ -1,24 +1,14 @@
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 3
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License in GPL.txt for more details.
- */
+
+use PonePaste\Models\AdminLog;
define('IN_PONEPASTE', 1);
require_once('common.php');
-$query = $conn->query('SELECT user FROM admin LIMIT 1');
-$adminid = $query->fetch()['user'];
-
+$admin_logs = AdminLog::with('user')
+ ->orderBy('time', 'DESC')
+ ->limit(10)
+ ->get();
?>
@@ -62,23 +52,13 @@ $adminid = $query->fetch()['user'];
- ' . mysqli_error($con) . '
- ';
-
- } else {
- $msg = '
+ $current_user->admin_password_hash = $password;
+ $current_user->save();
+ $msg = '
Account details updated.
';
- }
}
?>
@@ -104,14 +84,9 @@ $adminid = $query->fetch()['user'];
-
-