瀏覽代碼

Merge pull request #896 from pixelfed/frontend-ui-refactor

Frontend ui refactor
daniel 6 年之前
父節點
當前提交
f67bb5028e

+ 2 - 1
app/Http/Controllers/SiteController.php

@@ -6,6 +6,7 @@ use Illuminate\Http\Request;
 use App, Auth, Cache, View;
 use App, Auth, Cache, View;
 use App\Util\Lexer\PrettyNumber;
 use App\Util\Lexer\PrettyNumber;
 use App\{Follower, Page, Profile, Status, User, UserFilter};
 use App\{Follower, Page, Profile, Status, User, UserFilter};
+use App\Util\Localization\Localization;
 
 
 class SiteController extends Controller
 class SiteController extends Controller
 {
 {
@@ -31,7 +32,7 @@ class SiteController extends Controller
     public function changeLocale(Request $request, $locale)
     public function changeLocale(Request $request, $locale)
     {
     {
         // todo: add other locales after pushing new l10n strings
         // todo: add other locales after pushing new l10n strings
-        $locales = ['en'];
+        $locales = Localization::languages();
         if(in_array($locale, $locales)) {
         if(in_array($locale, $locales)) {
           session()->put('locale', $locale);
           session()->put('locale', $locale);
         }
         }

+ 18 - 0
app/Util/Localization/Localization.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Util\Localization;
+
+use Cache;
+use Illuminate\Support\Arr;
+
+class Localization {
+
+	public static function languages()
+	{
+		return Cache::remember('core:localization:languages', now()->addDays(14), function() {
+			$dir = resource_path('lang');
+			return Arr::flatten(array_diff(scandir($dir), array('..', '.', 'vendor')));
+		});
+	}
+
+}

+ 3 - 3
composer.json

@@ -18,14 +18,14 @@
         "fideloper/proxy": "^4.0",
         "fideloper/proxy": "^4.0",
         "greggilbert/recaptcha": "dev-master",
         "greggilbert/recaptcha": "dev-master",
         "intervention/image": "^2.4",
         "intervention/image": "^2.4",
-        "laravel/framework": "5.7.*",
+        "laravel/framework": "5.8.*",
         "laravel/horizon": "^1.2",
         "laravel/horizon": "^1.2",
         "laravel/passport": "^7.0",
         "laravel/passport": "^7.0",
         "laravel/tinker": "^1.0",
         "laravel/tinker": "^1.0",
         "league/flysystem-aws-s3-v3": "~1.0",
         "league/flysystem-aws-s3-v3": "~1.0",
         "league/flysystem-cached-adapter": "~1.0",
         "league/flysystem-cached-adapter": "~1.0",
         "moontoast/math": "^1.1",
         "moontoast/math": "^1.1",
-        "pbmedia/laravel-ffmpeg": "3.0.0",
+        "pbmedia/laravel-ffmpeg": "4.0.0",
         "phpseclib/phpseclib": "~2.0",
         "phpseclib/phpseclib": "~2.0",
         "pixelfed/bacon-qr-code": "^3.0",
         "pixelfed/bacon-qr-code": "^3.0",
         "pixelfed/dotenv-editor": "^2.0",
         "pixelfed/dotenv-editor": "^2.0",
@@ -33,7 +33,7 @@
         "pixelfed/google2fa": "^4.0",
         "pixelfed/google2fa": "^4.0",
         "pixelfed/zttp": "^0.4",
         "pixelfed/zttp": "^0.4",
         "predis/predis": "^1.1",
         "predis/predis": "^1.1",
-        "spatie/laravel-backup": "^5.0.0",
+        "spatie/laravel-backup": "^6.0.0",
         "spatie/laravel-image-optimizer": "^1.1",
         "spatie/laravel-image-optimizer": "^1.1",
         "stevebauman/purify": "2.0.*"
         "stevebauman/purify": "2.0.*"
     },
     },

+ 134 - 406
composer.lock

@@ -4,11 +4,11 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
         "This file is @generated automatically"
     ],
     ],
-    "content-hash": "d94668170067c2954b732f591151e9e7",
+    "content-hash": "8f9feb6f0dd669b7a0974809de05d8bd",
     "packages": [
     "packages": [
         {
         {
             "name": "alchemy/binary-driver",
             "name": "alchemy/binary-driver",
-            "version": "5.0.0",
+            "version": "v2.0.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/alchemy-fr/BinaryDriver.git",
                 "url": "https://github.com/alchemy-fr/BinaryDriver.git",
@@ -153,16 +153,16 @@
         },
         },
         {
         {
             "name": "beyondcode/laravel-self-diagnosis",
             "name": "beyondcode/laravel-self-diagnosis",
-            "version": "1.1.0",
+            "version": "1.1.1",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/beyondcode/laravel-self-diagnosis.git",
                 "url": "https://github.com/beyondcode/laravel-self-diagnosis.git",
-                "reference": "83239c24716971a35b8a2418c4a4424a4c7bd12d"
+                "reference": "57d8707e9d7110d23da7a05ae48a7c51c8778476"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/beyondcode/laravel-self-diagnosis/zipball/83239c24716971a35b8a2418c4a4424a4c7bd12d",
-                "reference": "83239c24716971a35b8a2418c4a4424a4c7bd12d",
+                "url": "https://api.github.com/repos/beyondcode/laravel-self-diagnosis/zipball/57d8707e9d7110d23da7a05ae48a7c51c8778476",
+                "reference": "57d8707e9d7110d23da7a05ae48a7c51c8778476",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -170,12 +170,12 @@
                 "geerlingguy/ping": "^1.1",
                 "geerlingguy/ping": "^1.1",
                 "illuminate/support": "5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*|5.8.*",
                 "illuminate/support": "5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*|5.8.*",
                 "php": "^7.1",
                 "php": "^7.1",
-                "vlucas/phpdotenv": "~2.5"
+                "vlucas/phpdotenv": "~2.5|~3.3"
             },
             },
             "require-dev": {
             "require-dev": {
                 "larapack/dd": "^1.0",
                 "larapack/dd": "^1.0",
                 "mockery/mockery": "^1.0",
                 "mockery/mockery": "^1.0",
-                "orchestra/testbench": "~3.5",
+                "orchestra/testbench": "~3.5|~3.8",
                 "phpunit/phpunit": "^7.0",
                 "phpunit/phpunit": "^7.0",
                 "predis/predis": "^1.1",
                 "predis/predis": "^1.1",
                 "scrutinizer/ocular": "^1.5"
                 "scrutinizer/ocular": "^1.5"
@@ -211,7 +211,7 @@
                 "beyondcode",
                 "beyondcode",
                 "laravel-self-diagnosis"
                 "laravel-self-diagnosis"
             ],
             ],
-            "time": "2019-02-26T17:05:44+00:00"
+            "time": "2019-02-27T10:04:53+00:00"
         },
         },
         {
         {
             "name": "bitverse/identicon",
             "name": "bitverse/identicon",
@@ -1594,45 +1594,45 @@
         },
         },
         {
         {
             "name": "laravel/framework",
             "name": "laravel/framework",
-            "version": "v5.7.28",
+            "version": "v5.8.2",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "8e69728f1c80a024588adbd24c65c4fcf9aa9192"
+                "reference": "c3b7cbe700efb0f4c9a5359feaedb0a1f0a741ca"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/8e69728f1c80a024588adbd24c65c4fcf9aa9192",
-                "reference": "8e69728f1c80a024588adbd24c65c4fcf9aa9192",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/c3b7cbe700efb0f4c9a5359feaedb0a1f0a741ca",
+                "reference": "c3b7cbe700efb0f4c9a5359feaedb0a1f0a741ca",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
                 "doctrine/inflector": "^1.1",
                 "doctrine/inflector": "^1.1",
                 "dragonmantank/cron-expression": "^2.0",
                 "dragonmantank/cron-expression": "^2.0",
+                "egulias/email-validator": "^2.0",
                 "erusev/parsedown": "^1.7",
                 "erusev/parsedown": "^1.7",
+                "ext-json": "*",
                 "ext-mbstring": "*",
                 "ext-mbstring": "*",
                 "ext-openssl": "*",
                 "ext-openssl": "*",
-                "laravel/nexmo-notification-channel": "^1.0",
-                "laravel/slack-notification-channel": "^1.0",
                 "league/flysystem": "^1.0.8",
                 "league/flysystem": "^1.0.8",
                 "monolog/monolog": "^1.12",
                 "monolog/monolog": "^1.12",
-                "nesbot/carbon": "^1.26.3",
+                "nesbot/carbon": "^1.26.3 || ^2.0",
                 "opis/closure": "^3.1",
                 "opis/closure": "^3.1",
                 "php": "^7.1.3",
                 "php": "^7.1.3",
                 "psr/container": "^1.0",
                 "psr/container": "^1.0",
                 "psr/simple-cache": "^1.0",
                 "psr/simple-cache": "^1.0",
                 "ramsey/uuid": "^3.7",
                 "ramsey/uuid": "^3.7",
                 "swiftmailer/swiftmailer": "^6.0",
                 "swiftmailer/swiftmailer": "^6.0",
-                "symfony/console": "^4.1",
-                "symfony/debug": "^4.1",
-                "symfony/finder": "^4.1",
-                "symfony/http-foundation": "^4.1",
-                "symfony/http-kernel": "^4.1",
-                "symfony/process": "^4.1",
-                "symfony/routing": "^4.1",
-                "symfony/var-dumper": "^4.1",
+                "symfony/console": "^4.2",
+                "symfony/debug": "^4.2",
+                "symfony/finder": "^4.2",
+                "symfony/http-foundation": "^4.2",
+                "symfony/http-kernel": "^4.2",
+                "symfony/process": "^4.2",
+                "symfony/routing": "^4.2",
+                "symfony/var-dumper": "^4.2",
                 "tijsverkoyen/css-to-inline-styles": "^2.2.1",
                 "tijsverkoyen/css-to-inline-styles": "^2.2.1",
-                "vlucas/phpdotenv": "^2.2"
+                "vlucas/phpdotenv": "^3.3"
             },
             },
             "conflict": {
             "conflict": {
                 "tightenco/collect": "<5.5.33"
                 "tightenco/collect": "<5.5.33"
@@ -1675,12 +1675,12 @@
                 "league/flysystem-cached-adapter": "^1.0",
                 "league/flysystem-cached-adapter": "^1.0",
                 "mockery/mockery": "^1.0",
                 "mockery/mockery": "^1.0",
                 "moontoast/math": "^1.1",
                 "moontoast/math": "^1.1",
-                "orchestra/testbench-core": "3.7.*",
-                "pda/pheanstalk": "^3.0|^4.0",
-                "phpunit/phpunit": "^7.5",
+                "orchestra/testbench-core": "3.8.*",
+                "pda/pheanstalk": "^4.0",
+                "phpunit/phpunit": "^7.5|^8.0",
                 "predis/predis": "^1.1.1",
                 "predis/predis": "^1.1.1",
-                "symfony/css-selector": "^4.1",
-                "symfony/dom-crawler": "^4.1",
+                "symfony/css-selector": "^4.2",
+                "symfony/dom-crawler": "^4.2",
                 "true/punycode": "^2.1"
                 "true/punycode": "^2.1"
             },
             },
             "suggest": {
             "suggest": {
@@ -1698,17 +1698,18 @@
                 "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
                 "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
                 "moontoast/math": "Required to use ordered UUIDs (^1.1).",
                 "moontoast/math": "Required to use ordered UUIDs (^1.1).",
                 "nexmo/client": "Required to use the Nexmo transport (^1.0).",
                 "nexmo/client": "Required to use the Nexmo transport (^1.0).",
-                "pda/pheanstalk": "Required to use the beanstalk queue driver (^3.0|^4.0).",
+                "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
                 "predis/predis": "Required to use the redis cache and queue drivers (^1.0).",
                 "predis/predis": "Required to use the redis cache and queue drivers (^1.0).",
                 "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).",
                 "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).",
-                "symfony/css-selector": "Required to use some of the crawler integration testing tools (^4.1).",
-                "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (^4.1).",
-                "symfony/psr-http-message-bridge": "Required to psr7 bridging features (^1.0)."
+                "symfony/css-selector": "Required to use some of the crawler integration testing tools (^4.2).",
+                "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (^4.2).",
+                "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.1).",
+                "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
                 "branch-alias": {
                 "branch-alias": {
-                    "dev-master": "5.7-dev"
+                    "dev-master": "5.8-dev"
                 }
                 }
             },
             },
             "autoload": {
             "autoload": {
@@ -1736,7 +1737,7 @@
                 "framework",
                 "framework",
                 "laravel"
                 "laravel"
             ],
             ],
-            "time": "2019-02-26T15:41:34+00:00"
+            "time": "2019-02-27T14:02:36+00:00"
         },
         },
         {
         {
             "name": "laravel/horizon",
             "name": "laravel/horizon",
@@ -1806,63 +1807,6 @@
             ],
             ],
             "time": "2018-11-01T14:03:51+00:00"
             "time": "2018-11-01T14:03:51+00:00"
         },
         },
-        {
-            "name": "laravel/nexmo-notification-channel",
-            "version": "v1.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/laravel/nexmo-notification-channel.git",
-                "reference": "03edd42a55b306ff980c9950899d5a2b03260d48"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/laravel/nexmo-notification-channel/zipball/03edd42a55b306ff980c9950899d5a2b03260d48",
-                "reference": "03edd42a55b306ff980c9950899d5a2b03260d48",
-                "shasum": ""
-            },
-            "require": {
-                "nexmo/client": "^1.0",
-                "php": "^7.1.3"
-            },
-            "require-dev": {
-                "illuminate/notifications": "~5.7",
-                "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^7.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
-                },
-                "laravel": {
-                    "providers": [
-                        "Illuminate\\Notifications\\NexmoChannelServiceProvider"
-                    ]
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Illuminate\\Notifications\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Taylor Otwell",
-                    "email": "taylor@laravel.com"
-                }
-            ],
-            "description": "Nexmo Notification Channel for laravel.",
-            "keywords": [
-                "laravel",
-                "nexmo",
-                "notifications"
-            ],
-            "time": "2018-12-04T12:57:08+00:00"
-        },
         {
         {
             "name": "laravel/passport",
             "name": "laravel/passport",
             "version": "v7.2.0",
             "version": "v7.2.0",
@@ -1933,63 +1877,6 @@
             ],
             ],
             "time": "2019-02-14T16:29:26+00:00"
             "time": "2019-02-14T16:29:26+00:00"
         },
         },
-        {
-            "name": "laravel/slack-notification-channel",
-            "version": "v1.0.3",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/laravel/slack-notification-channel.git",
-                "reference": "6e164293b754a95f246faf50ab2bbea3e4923cc9"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/6e164293b754a95f246faf50ab2bbea3e4923cc9",
-                "reference": "6e164293b754a95f246faf50ab2bbea3e4923cc9",
-                "shasum": ""
-            },
-            "require": {
-                "guzzlehttp/guzzle": "^6.0",
-                "php": "^7.1.3"
-            },
-            "require-dev": {
-                "illuminate/notifications": "~5.7",
-                "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^7.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
-                },
-                "laravel": {
-                    "providers": [
-                        "Illuminate\\Notifications\\SlackChannelServiceProvider"
-                    ]
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Illuminate\\Notifications\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Taylor Otwell",
-                    "email": "taylor@laravel.com"
-                }
-            ],
-            "description": "Slack Notification Channel for laravel.",
-            "keywords": [
-                "laravel",
-                "notifications",
-                "slack"
-            ],
-            "time": "2018-12-12T13:12:06+00:00"
-        },
         {
         {
             "name": "laravel/tinker",
             "name": "laravel/tinker",
             "version": "v1.0.8",
             "version": "v1.0.8",
@@ -2600,28 +2487,30 @@
         },
         },
         {
         {
             "name": "nesbot/carbon",
             "name": "nesbot/carbon",
-            "version": "1.36.2",
+            "version": "2.14.1",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "cd324b98bc30290f233dd0e75e6ce49f7ab2a6c9"
+                "reference": "e68f869575bd703276be8d4264799cd3cce28107"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cd324b98bc30290f233dd0e75e6ce49f7ab2a6c9",
-                "reference": "cd324b98bc30290f233dd0e75e6ce49f7ab2a6c9",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e68f869575bd703276be8d4264799cd3cce28107",
+                "reference": "e68f869575bd703276be8d4264799cd3cce28107",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "php": ">=5.3.9",
-                "symfony/translation": "~2.6 || ~3.0 || ~4.0"
+                "ext-json": "*",
+                "php": "^7.1.8 || ^8.0",
+                "symfony/translation": "^3.4 || ^4.0"
             },
             },
             "require-dev": {
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7"
-            },
-            "suggest": {
-                "friendsofphp/php-cs-fixer": "Needed for the `composer phpcs` command. Allow to automatically fix code style.",
-                "phpstan/phpstan": "Needed for the `composer phpstan` command. Allow to detect potential errors."
+                "friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
+                "kylekatarnls/multi-tester": "^0.1",
+                "phpmd/phpmd": "^2.6",
+                "phpstan/phpstan": "^0.10.8",
+                "phpunit/phpunit": "^7.5 || ^8.0",
+                "squizlabs/php_codesniffer": "^3.4"
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
@@ -2633,7 +2522,7 @@
             },
             },
             "autoload": {
             "autoload": {
                 "psr-4": {
                 "psr-4": {
-                    "": "src/"
+                    "Carbon\\": "src/Carbon/"
                 }
                 }
             },
             },
             "notification-url": "https://packagist.org/downloads/",
             "notification-url": "https://packagist.org/downloads/",
@@ -2654,7 +2543,7 @@
                 "datetime",
                 "datetime",
                 "time"
                 "time"
             ],
             ],
-            "time": "2018-12-28T10:07:33+00:00"
+            "time": "2019-02-27T13:59:59+00:00"
         },
         },
         {
         {
             "name": "neutron/temporary-filesystem",
             "name": "neutron/temporary-filesystem",
@@ -2696,54 +2585,6 @@
             "description": "Symfony filesystem extension to handle temporary files",
             "description": "Symfony filesystem extension to handle temporary files",
             "time": "2018-02-07T21:11:57+00:00"
             "time": "2018-02-07T21:11:57+00:00"
         },
         },
-        {
-            "name": "nexmo/client",
-            "version": "1.6.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/Nexmo/nexmo-php.git",
-                "reference": "2f79f67f24225ea627ee14578e98c96276cdd4c5"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/Nexmo/nexmo-php/zipball/2f79f67f24225ea627ee14578e98c96276cdd4c5",
-                "reference": "2f79f67f24225ea627ee14578e98c96276cdd4c5",
-                "shasum": ""
-            },
-            "require": {
-                "lcobucci/jwt": "^3.2",
-                "php": ">=5.6",
-                "php-http/client-implementation": "^1.0",
-                "php-http/guzzle6-adapter": "^1.0",
-                "zendframework/zend-diactoros": "^1.3"
-            },
-            "require-dev": {
-                "estahn/phpunit-json-assertions": "^1.0.0",
-                "php-http/mock-client": "^0.3.0",
-                "phpunit/phpunit": "^5.7",
-                "squizlabs/php_codesniffer": "^3.1"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "Nexmo\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Tim Lytle",
-                    "email": "tim@nexmo.com",
-                    "homepage": "http://twitter.com/tjlytle",
-                    "role": "Developer"
-                }
-            ],
-            "description": "PHP Client for using Nexmo's API.",
-            "time": "2019-02-07T11:14:34+00:00"
-        },
         {
         {
             "name": "nikic/php-parser",
             "name": "nikic/php-parser",
             "version": "v4.2.1",
             "version": "v4.2.1",
@@ -2969,30 +2810,31 @@
         },
         },
         {
         {
             "name": "pbmedia/laravel-ffmpeg",
             "name": "pbmedia/laravel-ffmpeg",
-            "version": "3.0.0",
+            "version": "4.0.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/pascalbaljetmedia/laravel-ffmpeg.git",
                 "url": "https://github.com/pascalbaljetmedia/laravel-ffmpeg.git",
-                "reference": "8310f4be71611230abc92388368173205fe6415c"
+                "reference": "6b7ac695b56c3847a736de614d4533245541cb6c"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/pascalbaljetmedia/laravel-ffmpeg/zipball/8310f4be71611230abc92388368173205fe6415c",
-                "reference": "8310f4be71611230abc92388368173205fe6415c",
+                "url": "https://api.github.com/repos/pascalbaljetmedia/laravel-ffmpeg/zipball/6b7ac695b56c3847a736de614d4533245541cb6c",
+                "reference": "6b7ac695b56c3847a736de614d4533245541cb6c",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "illuminate/config": "5.7.*",
-                "illuminate/filesystem": "5.7.*",
-                "illuminate/log": "5.7.*",
+                "illuminate/config": "5.8.*",
+                "illuminate/filesystem": "5.8.*",
+                "illuminate/log": "5.8.*",
+                "illuminate/support": "5.8.*",
                 "league/flysystem": "~1.0",
                 "league/flysystem": "~1.0",
-                "php": "^7.1",
-                "php-ffmpeg/php-ffmpeg": "dev-master#c11b79ab5b0174aa1a56c54c67491169e78a4c17",
+                "php": "^7.1.3",
+                "php-ffmpeg/php-ffmpeg": "^0.13",
                 "symfony/process": "~4.0"
                 "symfony/process": "~4.0"
             },
             },
             "require-dev": {
             "require-dev": {
                 "mockery/mockery": "^1.0",
                 "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^7.0"
+                "phpunit/phpunit": "7.5"
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
@@ -3028,26 +2870,26 @@
                 "laravel-ffmpeg",
                 "laravel-ffmpeg",
                 "pbmedia"
                 "pbmedia"
             ],
             ],
-            "time": "2018-09-06T11:52:45+00:00"
+            "time": "2019-02-27T17:33:08+00:00"
         },
         },
         {
         {
             "name": "php-ffmpeg/php-ffmpeg",
             "name": "php-ffmpeg/php-ffmpeg",
-            "version": "dev-master",
+            "version": "v0.13",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
                 "url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
-                "reference": "f9cc3d0c429fade5a144db674272eb8b48db8dce"
+                "reference": "c11b79ab5b0174aa1a56c54c67491169e78a4c17"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/f9cc3d0c429fade5a144db674272eb8b48db8dce",
-                "reference": "f9cc3d0c429fade5a144db674272eb8b48db8dce",
+                "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/c11b79ab5b0174aa1a56c54c67491169e78a4c17",
+                "reference": "c11b79ab5b0174aa1a56c54c67491169e78a4c17",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "alchemy/binary-driver": "^1.5 || ~2.0.0 || ^5.0",
+                "alchemy/binary-driver": "^1.5 || ~2.0.0",
                 "doctrine/cache": "^1.0",
                 "doctrine/cache": "^1.0",
-                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "evenement/evenement": "^2.0 || ^1.0",
                 "neutron/temporary-filesystem": "^2.1.1",
                 "neutron/temporary-filesystem": "^2.1.1",
                 "php": "^5.3.9 || ^7.0"
                 "php": "^5.3.9 || ^7.0"
             },
             },
@@ -3112,173 +2954,57 @@
                 "video",
                 "video",
                 "video processing"
                 "video processing"
             ],
             ],
-            "time": "2019-02-26T17:04:09+00:00"
+            "time": "2018-08-06T20:02:43+00:00"
         },
         },
         {
         {
-            "name": "php-http/guzzle6-adapter",
-            "version": "v1.1.1",
+            "name": "phpoption/phpoption",
+            "version": "1.5.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
-                "url": "https://github.com/php-http/guzzle6-adapter.git",
-                "reference": "a56941f9dc6110409cfcddc91546ee97039277ab"
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/a56941f9dc6110409cfcddc91546ee97039277ab",
-                "reference": "a56941f9dc6110409cfcddc91546ee97039277ab",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed",
+                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "guzzlehttp/guzzle": "^6.0",
-                "php": ">=5.5.0",
-                "php-http/httplug": "^1.0"
-            },
-            "provide": {
-                "php-http/async-client-implementation": "1.0",
-                "php-http/client-implementation": "1.0"
-            },
-            "require-dev": {
-                "ext-curl": "*",
-                "php-http/adapter-integration-tests": "^0.4"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.2-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Http\\Adapter\\Guzzle6\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Márk Sági-Kazár",
-                    "email": "mark.sagikazar@gmail.com"
-                },
-                {
-                    "name": "David de Boer",
-                    "email": "david@ddeboer.nl"
-                }
-            ],
-            "description": "Guzzle 6 HTTP Adapter",
-            "homepage": "http://httplug.io",
-            "keywords": [
-                "Guzzle",
-                "http"
-            ],
-            "time": "2016-05-10T06:13:32+00:00"
-        },
-        {
-            "name": "php-http/httplug",
-            "version": "v1.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-http/httplug.git",
-                "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018",
-                "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.4",
-                "php-http/promise": "^1.0",
-                "psr/http-message": "^1.0"
-            },
-            "require-dev": {
-                "henrikbjorn/phpspec-code-coverage": "^1.0",
-                "phpspec/phpspec": "^2.4"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.1-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Http\\Client\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Eric GELOEN",
-                    "email": "geloen.eric@gmail.com"
-                },
-                {
-                    "name": "Márk Sági-Kazár",
-                    "email": "mark.sagikazar@gmail.com"
-                }
-            ],
-            "description": "HTTPlug, the HTTP client abstraction for PHP",
-            "homepage": "http://httplug.io",
-            "keywords": [
-                "client",
-                "http"
-            ],
-            "time": "2016-08-31T08:30:17+00:00"
-        },
-        {
-            "name": "php-http/promise",
-            "version": "v1.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/php-http/promise.git",
-                "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980",
-                "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980",
-                "shasum": ""
+                "php": ">=5.3.0"
             },
             },
             "require-dev": {
             "require-dev": {
-                "henrikbjorn/phpspec-code-coverage": "^1.0",
-                "phpspec/phpspec": "^2.4"
+                "phpunit/phpunit": "4.7.*"
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
                 "branch-alias": {
                 "branch-alias": {
-                    "dev-master": "1.1-dev"
+                    "dev-master": "1.3-dev"
                 }
                 }
             },
             },
             "autoload": {
             "autoload": {
-                "psr-4": {
-                    "Http\\Promise\\": "src/"
+                "psr-0": {
+                    "PhpOption\\": "src/"
                 }
                 }
             },
             },
             "notification-url": "https://packagist.org/downloads/",
             "notification-url": "https://packagist.org/downloads/",
             "license": [
             "license": [
-                "MIT"
+                "Apache2"
             ],
             ],
             "authors": [
             "authors": [
                 {
                 {
-                    "name": "Márk Sági-Kazár",
-                    "email": "mark.sagikazar@gmail.com"
-                },
-                {
-                    "name": "Joel Wurtz",
-                    "email": "joel.wurtz@gmail.com"
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com"
                 }
                 }
             ],
             ],
-            "description": "Promise used for asynchronous HTTP requests",
-            "homepage": "http://httplug.io",
+            "description": "Option Type for PHP",
             "keywords": [
             "keywords": [
-                "promise"
+                "language",
+                "option",
+                "php",
+                "type"
             ],
             ],
-            "time": "2016-01-26T13:27:02+00:00"
+            "time": "2015-07-25T16:39:46+00:00"
         },
         },
         {
         {
             "name": "phpseclib/phpseclib",
             "name": "phpseclib/phpseclib",
@@ -4223,38 +3949,39 @@
         },
         },
         {
         {
             "name": "spatie/laravel-backup",
             "name": "spatie/laravel-backup",
-            "version": "5.12.0",
+            "version": "6.1.1",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/spatie/laravel-backup.git",
                 "url": "https://github.com/spatie/laravel-backup.git",
-                "reference": "9f6466ad6b51dedba0ceccc65aff8a613d5f4521"
+                "reference": "aea866d7a66b503365a3bdf07fbeea3babb87707"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/9f6466ad6b51dedba0ceccc65aff8a613d5f4521",
-                "reference": "9f6466ad6b51dedba0ceccc65aff8a613d5f4521",
+                "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/aea866d7a66b503365a3bdf07fbeea3babb87707",
+                "reference": "aea866d7a66b503365a3bdf07fbeea3babb87707",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "illuminate/console": "~5.5.0|~5.6.0|~5.7.0",
-                "illuminate/contracts": "~5.5.0|~5.6.0|~5.7.0",
-                "illuminate/events": "~5.5.0|~5.6.0|~5.7.0",
-                "illuminate/filesystem": "~5.5.0|~5.6.0|~5.7.0",
-                "illuminate/notifications": "~5.5.0|~5.6.0|~5.7.0",
-                "illuminate/support": "~5.5.0|~5.6.0|~5.7.0",
-                "league/flysystem": "^1.0.27",
-                "php": "^7.1",
-                "spatie/db-dumper": "^2.11.1",
+                "illuminate/console": "~5.8.0",
+                "illuminate/contracts": "~5.8.0",
+                "illuminate/events": "~5.8.0",
+                "illuminate/filesystem": "~5.8.0",
+                "illuminate/notifications": "~5.8.0",
+                "illuminate/support": "~5.8.0",
+                "league/flysystem": "^1.0.49",
+                "php": "^7.2",
+                "spatie/db-dumper": "^2.12",
                 "spatie/temporary-directory": "^1.1",
                 "spatie/temporary-directory": "^1.1",
-                "symfony/finder": "^3.3|^4.0"
+                "symfony/finder": "^4.2"
             },
             },
             "require-dev": {
             "require-dev": {
+                "laravel/slack-notification-channel": "^1.0",
                 "mockery/mockery": "^1.0",
                 "mockery/mockery": "^1.0",
-                "orchestra/testbench": "~3.5.0|~3.6.0|~3.7.0",
-                "phpunit/phpunit": "^7.3"
+                "orchestra/testbench": "~3.8.0",
+                "phpunit/phpunit": "^8.0"
             },
             },
             "suggest": {
             "suggest": {
-                "guzzlehttp/guzzle": "Allows notifications to be sent via Slack"
+                "laravel/slack-notification-channel": "Required for sending notifications via Slack"
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
@@ -4284,7 +4011,7 @@
                     "role": "Developer"
                     "role": "Developer"
                 }
                 }
             ],
             ],
-            "description": "A Laravel 5 package to backup your application",
+            "description": "A Laravel package to backup your application",
             "homepage": "https://github.com/spatie/laravel-backup",
             "homepage": "https://github.com/spatie/laravel-backup",
             "keywords": [
             "keywords": [
                 "backup",
                 "backup",
@@ -4292,30 +4019,30 @@
                 "laravel-backup",
                 "laravel-backup",
                 "spatie"
                 "spatie"
             ],
             ],
-            "time": "2018-12-28T07:57:48+00:00"
+            "time": "2019-02-27T15:34:30+00:00"
         },
         },
         {
         {
             "name": "spatie/laravel-image-optimizer",
             "name": "spatie/laravel-image-optimizer",
-            "version": "1.3.2",
+            "version": "1.4.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/spatie/laravel-image-optimizer.git",
                 "url": "https://github.com/spatie/laravel-image-optimizer.git",
-                "reference": "d4c8f831d60f3a68dc5323910a6670d9819a4e3e"
+                "reference": "ab2f78c9ce6ddff6aad9b8e377f7e84a5906d12d"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/spatie/laravel-image-optimizer/zipball/d4c8f831d60f3a68dc5323910a6670d9819a4e3e",
-                "reference": "d4c8f831d60f3a68dc5323910a6670d9819a4e3e",
+                "url": "https://api.github.com/repos/spatie/laravel-image-optimizer/zipball/ab2f78c9ce6ddff6aad9b8e377f7e84a5906d12d",
+                "reference": "ab2f78c9ce6ddff6aad9b8e377f7e84a5906d12d",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "laravel/framework": "~5.5.0|~5.6.0|~5.7.0",
-                "php": "^7.0",
+                "laravel/framework": "~5.7.0|~5.8.0",
+                "php": "^7.2",
                 "spatie/image-optimizer": "^1.1.0"
                 "spatie/image-optimizer": "^1.1.0"
             },
             },
             "require-dev": {
             "require-dev": {
-                "orchestra/testbench": "~3.5.0|~3.6.0|~3.7.0",
-                "phpunit/phpunit": "^6.3|^7.0"
+                "orchestra/testbench": "~3.7.0|~3.8.0",
+                "phpunit/phpunit": "^8.0"
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
@@ -4351,7 +4078,7 @@
                 "laravel-image-optimizer",
                 "laravel-image-optimizer",
                 "spatie"
                 "spatie"
             ],
             ],
-            "time": "2018-08-27T14:44:11+00:00"
+            "time": "2019-02-27T10:14:49+00:00"
         },
         },
         {
         {
             "name": "spatie/temporary-directory",
             "name": "spatie/temporary-directory",
@@ -5691,16 +5418,16 @@
         },
         },
         {
         {
             "name": "tightenco/collect",
             "name": "tightenco/collect",
-            "version": "v5.7.27",
+            "version": "v5.8.2",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/tightenco/collect.git",
                 "url": "https://github.com/tightenco/collect.git",
-                "reference": "c1a36a2a8a0aa731c1acdcd83f57724ffe630d00"
+                "reference": "9e431d3ba84ff02183bbb6da07410d59ef769f9b"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/tightenco/collect/zipball/c1a36a2a8a0aa731c1acdcd83f57724ffe630d00",
-                "reference": "c1a36a2a8a0aa731c1acdcd83f57724ffe630d00",
+                "url": "https://api.github.com/repos/tightenco/collect/zipball/9e431d3ba84ff02183bbb6da07410d59ef769f9b",
+                "reference": "9e431d3ba84ff02183bbb6da07410d59ef769f9b",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -5737,7 +5464,7 @@
                 "collection",
                 "collection",
                 "laravel"
                 "laravel"
             ],
             ],
-            "time": "2019-02-13T19:40:13+00:00"
+            "time": "2019-02-26T18:45:33+00:00"
         },
         },
         {
         {
             "name": "tijsverkoyen/css-to-inline-styles",
             "name": "tijsverkoyen/css-to-inline-styles",
@@ -5788,29 +5515,30 @@
         },
         },
         {
         {
             "name": "vlucas/phpdotenv",
             "name": "vlucas/phpdotenv",
-            "version": "v2.6.1",
+            "version": "v3.3.2",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/vlucas/phpdotenv.git",
                 "url": "https://github.com/vlucas/phpdotenv.git",
-                "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5"
+                "reference": "1ee9369cfbf26cfcf1f2515d98f15fab54e9647a"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
-                "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1ee9369cfbf26cfcf1f2515d98f15fab54e9647a",
+                "reference": "1ee9369cfbf26cfcf1f2515d98f15fab54e9647a",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
-                "php": ">=5.3.9",
+                "php": "^5.4 || ^7.0",
+                "phpoption/phpoption": "^1.5",
                 "symfony/polyfill-ctype": "^1.9"
                 "symfony/polyfill-ctype": "^1.9"
             },
             },
             "require-dev": {
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.0"
+                "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0"
             },
             },
             "type": "library",
             "type": "library",
             "extra": {
             "extra": {
                 "branch-alias": {
                 "branch-alias": {
-                    "dev-master": "2.6-dev"
+                    "dev-master": "3.3-dev"
                 }
                 }
             },
             },
             "autoload": {
             "autoload": {
@@ -5835,7 +5563,7 @@
                 "env",
                 "env",
                 "environment"
                 "environment"
             ],
             ],
-            "time": "2019-01-29T11:11:52+00:00"
+            "time": "2019-01-30T10:43:17+00:00"
         },
         },
         {
         {
             "name": "zendframework/zend-diactoros",
             "name": "zendframework/zend-diactoros",

+ 437 - 0
resources/assets/js/components/ComposeModal.vue

@@ -0,0 +1,437 @@
+<template>
+	<div>
+		<input type="file" name="media" class="d-none file-input" multiple="">
+		<div class="timeline">
+			<div class="card status-card card-md-rounded-0">
+
+				<div class="card-header d-inline-flex align-items-center bg-white">
+					<img v-bind:src="profile.avatar" width="32px" height="32px" style="border-radius: 32px;" class="box-shadow">
+					<a class="username font-weight-bold pl-2 text-dark" v-bind:href="profile.url">
+						{{profile.username}}
+					</a>
+					<div class="text-right" style="flex-grow:1;">
+						<div class="dropdown">
+							<button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
+								<span class="fas fa-ellipsis-v fa-lg text-muted"></span>
+							</button>
+							<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
+								<div class="dropdown-item small font-weight-bold" v-on:click="mediaDrawer = !mediaDrawer">Show Media Toolbar</div>
+								<div class="dropdown-divider"></div>
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<div class="postPresenterContainer">
+
+
+					<div v-if="ids.length == 0" class="w-100 h-100 bg-light py-5 cursor-pointer" style="border-bottom: 1px solid #f1f1f1" v-on:click="addMedia()">
+						<p class="text-center mb-0 font-weight-bold p-5">Click here to add photos.</p>
+					</div>
+					<div v-if="ids.length > 0">
+						
+						<b-carousel id="p-carousel"
+							style="text-shadow: 1px 1px 2px #333;"
+							controls
+							indicators
+							background="#ffffff"
+							:interval="0"
+							v-model="carouselCursor"
+						>
+							<b-carousel-slide  v-if="ids.length > 0" v-for="(preview, index) in media" :key="'preview_media_'+index">
+								<div slot="img" :class="[media[index].filter_class?media[index].filter_class + ' cursor-pointer':' cursor-pointer']" v-on:click="addMedia()">
+									<img class="d-block img-fluid w-100" :src="preview.url" :alt="preview.description" :title="preview.description">
+								</div>
+							</b-carousel-slide>
+						</b-carousel>
+					</div>
+
+					<div v-if="mediaDrawer" class="bg-lighter p-2 row">
+						<div class="col-4">
+							<select class="form-control form-control-sm" id="filterSelectDropdown" v-on:change="toggleFilter($event)">
+								<option value="none">No filter</option>
+								<option v-for="(filter, index) in filters" :value="filter[1]" :selected="filter[1]==media[carouselCursor].filter_class?'selected':''">{{filter[0]}}</option>
+							</select>
+						</div>
+						<div class="col-5">
+							<button class="btn btn-outline-primary btn-sm mr-1" v-on:click="mediaAltText()">Alt Text</button>
+							<button class="btn btn-outline-primary btn-sm mr-1" v-on:click="mediaLicense()">License</button>
+						</div>
+						<div class="col-3 text-right">
+							<button class="btn btn-outline-danger btn-sm font-weight-bold mr-1" v-on:click="deleteMedia()"><i class="fas fa-trash"></i></button>
+							<button class="btn btn-outline-secondary btn-sm font-weight-bold" v-on:click="updateMedia()"><i class="fas fa-times"></i></button>
+						</div>
+					</div>
+				</div>
+
+				<div :class="[mediaDrawer?'glass card-body disabled':'card-body']">
+					<div class="reactions my-1">
+						<h3 class="far fa-heart pr-3 m-0 text-lighter" title="Like"></h3>
+						<h3 class="far fa-comment pr-3 m-0 text-lighter" title="Comment"></h3>
+					</div>
+
+					<div class="likes font-weight-bold">
+						<span class="like-count">0</span> likes
+					</div>
+					<div class="caption">
+						<p class="mb-2 read-more" style="overflow: hidden;">
+							<span class="username font-weight-bold d-inline-block clearfix">
+								<bdi><a class="text-dark" :href="profile.url">{{profile.username}}</a></bdi>
+							</span>
+							<span contenteditable="" style="outline:none;" v-on:keyup="textWatcher"></span>
+						</p>
+					</div>
+					<div class="comments">
+					</div>
+					<div class="timestamp pt-1">
+						<p class="small text-uppercase mb-0">
+							<span class="text-muted">
+								Draft
+							</span>
+						</p>
+					</div>
+				</div>
+
+				<div :class="[mediaDrawer?'glass card-footer':'card-footer']">
+					<div class="d-flex justify-content-between align-items-center">
+						<div>
+							<div class="custom-control custom-switch d-inline mr-3">
+								<input type="checkbox" class="custom-control-input" id="nsfwToggle" v-model="nsfw">
+								<label class="custom-control-label small font-weight-bold text-muted pt-1" for="nsfwToggle">NSFW</label>
+							</div>
+							<div class="dropdown d-inline">
+								<button class="btn btn-outline-secondary btn-sm py-0 dropdown-toggle" type="button" id="visibility" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+									Public
+								</button>
+								<div class="dropdown-menu" aria-labelledby="visibility" style="width: 200px;">
+									<a class="dropdown-item active" href="#" data-id="public" data-title="Public">
+										<div class="row">
+											<div class="col-12 col-sm-2 px-0 text-center">
+												<i class="fas fa-globe"></i>
+											</div> 
+											<div class="col-12 col-sm-10 pl-2">
+												<p class="font-weight-bold mb-0">Public</p>
+												<p class="small mb-0">Anyone can see</p>
+											</div> 
+										</div>
+									</a>
+									<a class="dropdown-item" href="#" data-id="private" data-title="Followers Only">
+										<div class="row">
+											<div class="col-12 col-sm-2 px-0 text-center">
+												<i class="fas fa-lock"></i>
+											</div> 
+											<div class="col-12 col-sm-10 pl-2">
+												<p class="font-weight-bold mb-0">Followers Only</p>
+												<p class="small mb-0">Only followers can see</p>
+											</div> 
+										</div>
+									</a>
+
+									<a class="dropdown-item" href="#" data-id="circle" data-title="Circle">
+										<div class="row">
+											<div class="col-12 col-sm-2 px-0 text-center">
+												<i class="far fa-circle"></i>
+											</div> 
+											<div class="col-12 col-sm-10 pl-2">
+												<p class="font-weight-bold mb-0">Circle</p>
+												<p class="small mb-0">Select a circle</p>
+											</div> 
+										</div>
+									</a>
+									<a class="dropdown-item" href="#" data-id="direct" data-title="Direct Message">
+										<div class="row">
+											<div class="col-12 col-sm-2 px-0 text-center">
+												<i class="fas fa-envelope"></i>
+											</div> 
+											<div class="col-12 col-sm-10 pl-2">
+												<p class="font-weight-bold mb-0">Direct Message</p>
+												<p class="small mb-0">Recipients only</p>
+											</div> 
+										</div>
+									</a>
+								</div>
+							</div>
+						</div>
+						<div class="small text-muted font-weight-bold">
+							{{composeTextLength}} / 500
+						</div>
+						<div class="pl-md-5">
+							<div class="btn-group">
+								<button type="button" class="btn btn-primary btn-sm font-weight-bold" v-on:click="compose()">{{composeState[0].toUpperCase() + composeState.slice(1)}}</button>
+								<button type="button" class="btn btn-primary btn-sm dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+									<span class="sr-only">Toggle Dropdown</span>
+								</button>
+								<div class="dropdown-menu dropdown-menu-right">
+									<a :class="[composeState == 'publish' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'publish'">Publish now</a>
+									<a :class="[composeState == 'draft' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'draft'">Save as draft</a>
+									<a :class="[composeState == 'schedule' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'schedule'">Schedule for later</a>
+									<div class="dropdown-divider"></div>
+									<a :class="[composeState == 'delete' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'delete'">Delete</a>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+
+			</div>
+		</div>
+	</div>
+</template>
+
+<style type="text/css" scoped>
+.glass {
+	-webkit-filter: blur(2px);
+	-moz-filter: blur(2px);
+	-o-filter: blur(2px);
+	-ms-filter: blur(2px);
+	filter: blur(2px);
+	width: 100%;
+	height: 100%;
+}
+</style>
+<script type="text/javascript">
+export default {
+	data() {
+		return {
+			profile: {},
+			create: {
+				hasGeneratedSelect: false, 
+				selectedFilter: false, 
+				currentFilterName: false, 
+				currentFilterClass: false
+			},
+			composeText: '',
+			composeTextLength: 27,
+			nsfw: false,
+			filters: [],
+			ids: [],
+			media: [],
+			meta: {
+				'id': false,
+				'cursor': false,
+				'cw': false,
+				'alt': null,
+				'filter': null,
+				'license': null,
+				'preserve_exif': false,
+			},
+			cursor: 1,
+			carouselCursor: 0,
+			visibility: 'public',
+			cropmode: false,
+			croppie: false,
+			limit: pixelfed.settings.maxAlbumLength,
+			acceptedMimes: pixelfed.settings.acceptedMimes,
+			mediaDrawer: false,
+			composeState: 'publish',
+			filter: {
+				name: null,
+				class: null
+			}
+		}
+	},
+
+	beforeMount() {
+		this.fetchProfile();
+	},
+
+	mounted() {
+		this.mediaWatcher();
+		this.filters = [
+			['1977','filter-1977'], 
+			['Aden','filter-aden'], 
+			['Amaro','filter-amaro'], 
+			['Ashby','filter-ashby'], 
+			['Brannan','filter-brannan'], 
+			['Brooklyn','filter-brooklyn'], 
+			['Charmes','filter-charmes'], 
+			['Clarendon','filter-clarendon'], 
+			['Crema','filter-crema'], 
+			['Dogpatch','filter-dogpatch'], 
+			['Earlybird','filter-earlybird'], 
+			['Gingham','filter-gingham'], 
+			['Ginza','filter-ginza'], 
+			['Hefe','filter-hefe'], 
+			['Helena','filter-helena'], 
+			['Hudson','filter-hudson'], 
+			['Inkwell','filter-inkwell'], 
+			['Kelvin','filter-kelvin'], 
+			['Kuno','filter-juno'], 
+			['Lark','filter-lark'], 
+			['Lo-Fi','filter-lofi'], 
+			['Ludwig','filter-ludwig'], 
+			['Maven','filter-maven'], 
+			['Mayfair','filter-mayfair'], 
+			['Moon','filter-moon'], 
+			['Nashville','filter-nashville'], 
+			['Perpetua','filter-perpetua'], 
+			['Poprocket','filter-poprocket'], 
+			['Reyes','filter-reyes'], 
+			['Rise','filter-rise'], 
+			['Sierra','filter-sierra'], 
+			['Skyline','filter-skyline'], 
+			['Slumber','filter-slumber'], 
+			['Stinson','filter-stinson'], 
+			['Sutro','filter-sutro'], 
+			['Toaster','filter-toaster'], 
+			['Valencia','filter-valencia'], 
+			['Vesper','filter-vesper'], 
+			['Walden','filter-walden'], 
+			['Willow','filter-willow'], 
+			['X-Pro II','filter-xpro-ii']
+		];
+	},
+
+	watch: {
+		composeText: function (newComposeText, oldComposeText) {
+			this.debouncedTextWatcher();
+		}
+	},
+
+	created: function() {
+		this.debouncedTextWatcher = _.debounce(this.textWatcher, 300)
+	},
+
+	methods: {
+
+		fetchProfile() {
+			axios.get('/api/v1/accounts/verify_credentials').then(res => {
+				this.profile = res.data;
+			}).catch(err => {
+				console.log(err)
+			});
+		},
+
+		addMedia() {
+			let el = $(event.target);
+			el.attr('disabled', '');
+			let fi = $('.file-input[name="media"]');
+			fi.trigger('click');
+			el.blur();
+			el.removeAttr('disabled');
+		},
+
+		textWatcher() {
+			this.composeText = event.target.innerText;
+			this.composeTextLength = event.target.innerText.length;
+		},
+
+		mediaWatcher() {
+			let self = this;
+			$(document).on('change', '.file-input', function(e) {
+				let io = document.querySelector('.file-input');
+				Array.prototype.forEach.call(io.files, function(io, i) {
+					if(self.media && self.media.length + i >= self.limit) {
+						return;
+					}
+					let type = io.type;
+					let acceptedMimes = pixelfed.settings.acceptedMimes.split(',');
+					let validated = $.inArray(type, acceptedMimes);
+					if(validated == -1) {
+						swal('Invalid File Type', 'The file you are trying to add is not a valid mime type. Please upload a '+pixelfed.uploader.acceptedMimes+' only.', 'error');
+						return;
+					}
+
+					let form = new FormData();
+					form.append('file', io);
+
+					let config = {
+						onUploadProgress: function(e) {
+							let progress = Math.round( (e.loaded * 100) / e.total );
+						}
+					};
+
+					axios.post('/api/v1/media', form, config)
+					.then(function(e) {
+						self.ids.push(e.data.id);
+						self.media.push(e.data);
+						self.mediaDrawer = true;
+					}).catch(function(e) {
+						swal('Oops, something went wrong!', 'An unexpected error occurred.', 'error');
+					});
+					io.value = null;
+				});
+			});
+		},
+
+		toggleFilter(e) {
+			this.media[this.carouselCursor].filter_class = e.target.value;
+
+			// todo: deprecate
+			this.create.selectedFilter = true;
+			this.create.filterName = val;
+			this.create.filterClass = val;
+			this.create.currentFilterName = val;
+			this.create.currentFilterClass = val;
+			this.filter.class = val;
+		},
+
+		updateMedia() {
+			this.mediaDrawer = false;
+		},
+
+		deleteMedia() {
+			let id = this.media[this.carouselCursor].id;
+			axios.delete('/api/v1/media', {
+				params: {
+					id: id
+				}
+			}).then(res => {
+				if(this.media.length == 0) {
+					this.mediaDrawer = false;
+				}
+				this.ids.splice(this.carouselCursor, 1);
+				this.media.splice(this.carouselCursor, 1);
+			}).catch(err => {
+				swal('Whoops!', 'An error occured when attempting to delete this, please try again', 'error');
+			});
+		},
+
+		mediaAltText() {
+			swal({
+				text: 'Add a media description',
+				content: "input"
+			}).then(val => {
+				let media = this.media[this.carouselCursor];
+				media.alt = val;
+			});
+
+		},
+
+		mediaLicense() {
+			swal({
+				text: 'Add a media license',
+				content: "input",
+				button: {
+					text: "Update",
+					closeModal: true,
+				},
+			}).then(val => {
+				let media = this.media[this.carouselCursor];
+				media.license = val;
+			});
+
+		},
+
+		compose() {
+			if(this.media.length == 0) {
+				swal('Whoops!', 'You need to add media before you can save this!', 'warning');
+				return;
+			}
+			let data = {
+				media: this.media,
+				caption: this.composeText,
+				visibility: this.visibility,
+				cw: this.nsfw
+			};
+			axios.post('/api/local/status/compose', data)
+			.then(res => {
+				let data = res.data;
+				window.location.href = data;
+			}).catch(err => {
+				swal('Oops, something went wrong!', 'An unexpected error occurred.', 'error');
+			});
+		}
+	}
+}
+</script>

+ 120 - 1
resources/assets/js/components/SearchResults.vue

@@ -1,6 +1,77 @@
 <template>
 <template>
 <div class="container">
 <div class="container">
-	<p class="h1">search results here</p>
+	<div v-if="loading" class="pt-5 text-center">
+		<div class="spinner-border" role="status">
+			<span class="sr-only">Loading...</span>
+		</div>
+	</div>
+	<div v-if="networkError" class="pt-5 text-center">
+		<p class="lead font-weight-lighter">An error occured, results could not be loaded.<br> Please try again later.</p>
+	</div>
+
+	<div v-if="!loading && !networkError" class="mt-5 row">
+		
+		<div class="col-12 col-md-3">
+			<div>
+				<p class="font-weight-bold">Filters</p>
+				<div class="custom-control custom-checkbox">
+					<input type="checkbox" class="custom-control-input" id="filter1" v-model="filters.hashtags">
+					<label class="custom-control-label text-muted" for="filter1">Show Hashtags</label>
+				</div>
+				<div class="custom-control custom-checkbox">
+					<input type="checkbox" class="custom-control-input" id="filter2" v-model="filters.profiles">
+					<label class="custom-control-label text-muted" for="filter2">Show Profiles</label>
+				</div>
+				<div class="custom-control custom-checkbox">
+					<input type="checkbox" class="custom-control-input" id="filter3" v-model="filters.statuses">
+					<label class="custom-control-label text-muted" for="filter3">Show Statuses</label>
+				</div>
+			</div>
+		</div>
+		<div class="col-12 col-md-9">
+			<p class="h3 font-weight-lighter">Showing results for <i>{{query}}</i></p>
+			<hr>
+			
+			<div v-if="filters.hashtags && results.hashtags.length" class="row mb-4">
+				<p class="col-12 font-weight-bold text-muted">Hashtags</p>
+				<a v-for="(hashtag, index) in results.hashtags" class="col-12 col-md-4" style="text-decoration: none;" :href="hashtag.url">
+					<div class="card card-body text-center">
+						<p class="lead mb-0 text-truncate text-dark">
+							#{{hashtag.value}}
+						</p>
+						<p class="lead mb-0 small font-weight-bold text-dark">
+							{{hashtag.count}} posts
+						</p>
+					</div>
+				</a>
+			</div>
+
+			<div v-if="filters.profiles && results.profiles.length" class="row mb-4">
+				<p class="col-12 font-weight-bold text-muted">Profiles</p>
+				<a v-for="(profile, index) in results.profiles" class="col-12 col-md-4" style="text-decoration: none;" :href="profile.url">
+					<div class="card card-body text-center border-left-primary">
+						<p class="lead mb-0 text-truncate text-dark">
+							{{profile.value}}
+						</p>
+					</div>
+				</a>
+			</div>
+
+			<div v-if="filters.statuses && results.statuses.length" class="row mb-4">
+				<p class="col-12 font-weight-bold text-muted">Statuses</p>
+				<a v-for="(status, index) in results.statuses" class="col-12 col-md-4" style="text-decoration: none;" :href="status.url">
+					<div class="card card-body text-center border-left-primary">
+						<p class="lead mb-0 text-truncate text-dark">
+							{{status.value}}
+						</p>
+					</div>
+				</a>
+			</div>
+
+		</div>
+
+	</div>
+
 </div>
 </div>
 </template>
 </template>
 
 
@@ -10,6 +81,54 @@
 
 
 <script type="text/javascript">
 <script type="text/javascript">
 export default {
 export default {
+	props: ['query'],
+
+	data() {
+		return {
+			loading: true,
+			networkError: false,
+			results: {
+				hashtags: [],
+				profiles: [],
+				statuses: []
+			},
+			filters: {
+				hashtags: true,
+				profiles: true,
+				statuses: true
+			}
+		}
+	},
+	beforeMount() {
+		this.fetchSearchResults();
+	},
+	mounted() {
+		$('.search-form input').val(this.query);
+	},
+	updated() {
+
+	},
+	methods: {
+		fetchSearchResults() {
+			axios.get('/api/search/' + this.query)
+				.then(res => {
+					let results = res.data;
+					this.results.hashtags = results.filter(i => {
+						return i.type == 'hashtag';
+					});
+					this.results.profiles = results.filter(i => {
+						return i.type == 'profile';
+					});
+					this.results.statuses = results.filter(i => {
+						return i.type == 'status';
+					});
+					this.loading = false;
+				}).catch(err => {
+					this.loading = false;
+					this.networkError = true;
+				})
+		},
+	}
 
 
 }
 }
 </script>
 </script>

+ 6 - 2
resources/assets/js/components/presenter/MixedAlbumPresenter.vue

@@ -18,7 +18,9 @@
 						<source :src="media.url" :type="media.mime">
 						<source :src="media.url" :type="media.mime">
 					</video>
 					</video>
 
 
-					<img v-else-if="media.type == 'Image'" slot="img" class="d-block img-fluid w-100" :src="media.url" :alt="media.description" :title="media.description">
+					<div v-else-if="media.type == 'Image'" slot="img" :class="media.filter_class">
+						<img class="d-block img-fluid w-100" :src="media.url" :alt="media.description" :title="media.description">
+					</div>
 
 
 					<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
 					<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
 
 
@@ -40,7 +42,9 @@
 					<source :src="media.url" :type="media.mime">
 					<source :src="media.url" :type="media.mime">
 				</video>
 				</video>
 
 
-				<img v-else-if="media.type == 'Image'" slot="img" class="d-block img-fluid w-100" :src="media.url" :alt="media.description" :title="media.description">
+				<div v-else-if="media.type == 'Image'" slot="img" :class="media.filter_class">
+					<img class="d-block img-fluid w-100" :src="media.url" :alt="media.description" :title="media.description">
+				</div>
 
 
 				<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
 				<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
 
 

+ 6 - 2
resources/assets/js/components/presenter/PhotoAlbumPresenter.vue

@@ -13,7 +13,9 @@
 				:interval="0"
 				:interval="0"
 			>
 			>
 				<b-carousel-slide v-for="(img, index) in status.media_attachments" :key="img.id">
 				<b-carousel-slide v-for="(img, index) in status.media_attachments" :key="img.id">
-					<img slot="img" class="d-block img-fluid w-100" :src="img.url" :alt="img.description" :title="img.description">
+					<div slot="img" :class="img.filter_class">
+						<img class="d-block img-fluid w-100" :src="img.url" :alt="img.description" :title="img.description">
+					</div>
 				</b-carousel-slide>
 				</b-carousel-slide>
 			</b-carousel>
 			</b-carousel>
 		</details>
 		</details>
@@ -27,7 +29,9 @@
 			:interval="0"
 			:interval="0"
 		>
 		>
 			<b-carousel-slide v-for="(img, index) in status.media_attachments" :key="img.id">
 			<b-carousel-slide v-for="(img, index) in status.media_attachments" :key="img.id">
-				<img slot="img" class="d-block img-fluid w-100" :src="img.url" :alt="img.description" :title="img.description">
+				<div slot="img" :class="img.filter_class">
+					<img class="d-block img-fluid w-100" :src="img.url" :alt="img.description" :title="img.description">
+				</div>
 			</b-carousel-slide>
 			</b-carousel-slide>
 		</b-carousel>
 		</b-carousel>
 	</div>
 	</div>

+ 7 - 5
resources/views/site/language.blade.php

@@ -3,14 +3,16 @@
 @section('section')
 @section('section')
 
 
   <div class="title">
   <div class="title">
-    <h3 class="font-weight-bold">Language</h3>
+    <h3 class="font-weight-bold">{{__('site.language')}}</h3>
   </div>
   </div>
   <hr>
   <hr>
-  <div class="alert alert-info font-weight-bold">We’re still working on localization support!</div>
-  <p class="lead">Current Locale: <span class="font-weight-bold">{{App::getLocale()}}</span></p>
-  <p class="lead">Select from one of the supported languages:</p>
+  <div class="alert alert-info font-weight-bold">{{__('site.l10nWip')}}!</div>
+  <p class="font-weight-light">{{__('site.currentLocale')}}: <span class="font-weight-bold">{{App::getLocale()}}</span></p>
+  <p class="font-weight-light">{{__('site.selectLocale')}}:</p>
   <ul class="list-group">
   <ul class="list-group">
-    <a class="list-group-item font-weight-bold" href="/i/lang/en">English</a>
+    @foreach(App\Util\Localization\Localization::languages() as $lang)
+    <a class="list-group-item font-weight-bold" href="/i/lang/{{$lang}}">{{locale_get_display_language($lang)}}</a>
+    @endforeach
   </ul>
   </ul>
 @endsection
 @endsection
 
 

+ 0 - 55
tests/Unit/ActivityPub/AcceptVerbTest.php

@@ -1,55 +0,0 @@
-<?php
-
-namespace Tests\Unit;
-
-use Tests\TestCase;
-use Illuminate\Foundation\Testing\WithFaker;
-use Illuminate\Foundation\Testing\RefreshDatabase;
-use App\Util\ActivityPub\Validator\Accept;
-
-class AcceptVerbTest extends TestCase
-{
-	protected $validAccept;
-	protected $invalidAccept;
-
-	public function setUp()
-	{
-		parent::setUp();
-		$this->validAccept = [
-			'@context' => 'https://www.w3.org/ns/activitystreams',
-			'id' => 'https://example.org/og/b3e4a40b-0b26-4c5a-9079-094bd633fab7',
-			'type' => 'Accept',
-			'actor' => 'https://example.org/u/alice',
-			'object' => [
-				'id' => 'https://example.net/u/bob#follows/bb27f601-ddb9-4567-8f16-023d90605ca9',
-				'type' => 'Follow',
-				'actor' => 'https://example.net/u/bob',
-				'object' => 'https://example.org/u/alice'
-			]
-		];
-		$this->invalidAccept = [
-			'@context' => 'https://www.w3.org/ns/activitystreams',
-			'id' => 'https://example.org/og/b3e4a40b-0b26-4c5a-9079-094bd633fab7',
-			'type' => 'Accept2',
-			'actor' => 'https://example.org/u/alice',
-			'object' => [
-				'id' => 'https://example.net/u/bob#follows/bb27f601-ddb9-4567-8f16-023d90605ca9',
-				'type' => 'Follow',
-				'actor' => 'https://example.net/u/bob',
-				'object' => 'https://example.org/u/alice'
-			]
-		];
-	}
-
-	/** @test */
-	public function basic_accept()
-	{
-		$this->assertTrue(Accept::validate($this->validAccept));
-	}
-
-	/** @test */
-	public function invalid_accept()
-	{
-		$this->assertFalse(Accept::validate($this->invalidAccept));
-	}
-}

+ 1 - 1
tests/Unit/ActivityPub/AudienceScopeTest.php

@@ -9,7 +9,7 @@ use App\Util\ActivityPub\Helpers;
 
 
 class AudienceScopeTest extends TestCase
 class AudienceScopeTest extends TestCase
 {
 {
-	public function setUp()
+	public function setUp(): void
 	{
 	{
 		parent::setUp();
 		parent::setUp();
 
 

文件差異過大導致無法顯示
+ 1 - 1
tests/Unit/ActivityPub/NoteAttachmentTest.php


+ 1 - 1
tests/Unit/ActivityPub/Verb/AnnounceTest.php

@@ -10,7 +10,7 @@ use App\Util\ActivityPub\Validator\Announce;
 class AnnounceTest extends TestCase
 class AnnounceTest extends TestCase
 {
 {
 
 
-	public function setUp()
+	public function setUp(): void
 	{
 	{
 		parent::setUp();
 		parent::setUp();
 
 

+ 0 - 20
tests/Unit/HttpSignatures/HeaderListTest.php

@@ -1,20 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use App\Util\HttpSignatures\HeaderList;
-
-class HeaderListTest extends \PHPUnit\Framework\TestCase
-{
-    public function testToString()
-    {
-        $hl = new HeaderList(['(request-target)', 'Date']);
-        $this->assertEquals('(request-target) date', $hl->string());
-    }
-
-    public function testFromStringRoundTripNormalized()
-    {
-        $hl = HeaderList::fromString('(request-target) Accept');
-        $this->assertEquals('(request-target) accept', $hl->string());
-    }
-}

+ 0 - 190
tests/Unit/HttpSignatures/HmacContextTest.php

@@ -1,190 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use GuzzleHttp\Psr7\Request;
-use App\Util\HttpSignatures\Context;
-
-class HmacContextTest extends \PHPUnit\Framework\TestCase
-{
-    private $context;
-
-    public function setUp()
-    {
-        $this->noDigestContext = new Context([
-            'keys' => ['pda' => 'secret'],
-            'algorithm' => 'hmac-sha256',
-            'headers' => ['(request-target)', 'date'],
-        ]);
-        $this->withDigestContext = new Context([
-            'keys' => ['pda' => 'secret'],
-            'algorithm' => 'hmac-sha256',
-            'headers' => ['(request-target)', 'date', 'digest'],
-        ]);
-    }
-
-    public function testSignerNoDigestAction()
-    {
-        $message = new Request('GET', '/path?query=123', ['date' => 'today', 'accept' => 'llamas']);
-        $message = $this->noDigestContext->signer()->sign($message);
-
-        $expectedString = implode(',', [
-            'keyId="pda"',
-            'algorithm="hmac-sha256"',
-            'headers="(request-target) date"',
-            'signature="SFlytCGpsqb/9qYaKCQklGDvwgmrwfIERFnwt+yqPJw="',
-        ]);
-
-        $this->assertEquals(
-            $expectedString,
-            $message->getHeader('Signature')[0]
-        );
-
-        $this->assertEquals(
-            'Signature '.$expectedString,
-            $message->getHeader('Authorization')[0]
-        );
-    }
-
-    public function testSignerAddDigestToHeadersList()
-    {
-        $message = new Request(
-            'POST', '/path/to/things?query=123',
-            ['date' => 'today', 'accept' => 'llamas'],
-            'Thing to POST');
-        $message = $this->noDigestContext->signer()->signWithDigest($message);
-
-        $expectedString = implode(',', [
-            'keyId="pda"',
-            'algorithm="hmac-sha256"',
-            'headers="(request-target) date digest"',
-            'signature="HH6R3OJmJbKUFqqL0tGVIIb7xi1WbbSh/HBXHUtLkUs="', ]);
-        $expectedDigestHeader =
-          'SHA-256=rEcNhYZoBKiR29D30w1JcgArNlF8rXIXf5MnIL/4kcc=';
-
-        $this->assertEquals(
-            $expectedString,
-            $message->getHeader('Signature')[0]
-        );
-
-        $this->assertEquals(
-            $expectedDigestHeader,
-            $message->getHeader('Digest')[0]
-        );
-
-        $this->assertEquals(
-            'Signature '.$expectedString,
-            $message->getHeader('Authorization')[0]
-        );
-    }
-
-    public function testSignerReplaceDigest()
-    {
-        $message = new Request(
-            'PUT', '/things/thething?query=123',
-              ['date' => 'today',
-              'accept' => 'llamas',
-              'Digest' => 'SHA-256=E/P+4y4x6EySO9qNAjCtQKxVwE1xKsNI/k+cjK+vtLU=', ],
-            'Thing to PUT at /things/thething please...');
-        $message = $this->noDigestContext->signer()->signWithDigest($message);
-
-        $expectedString = implode(',', [
-            'keyId="pda"',
-            'algorithm="hmac-sha256"',
-            'headers="(request-target) date digest"',
-            'signature="Hyatt1lSR/4XLI9Gcx8XOEKiG8LVktH7Lfr+0tmhwRU="', ]);
-        $expectedDigestHeader =
-          'SHA-256=mulOx+77mQU1EbPET50SCGA4P/4bYxVCJA1pTwJsaMw=';
-
-        $this->assertEquals(
-            $expectedString,
-            $message->getHeader('Signature')[0]
-        );
-
-        $this->assertEquals(
-            $expectedDigestHeader,
-            $message->getHeader('Digest')[0]
-        );
-
-        $this->assertEquals(
-            'Signature '.$expectedString,
-            $message->getHeader('Authorization')[0]
-        );
-    }
-
-    public function testSignerNewDigestIsInHeaderList()
-    {
-        $message = new Request(
-            'POST', '/path?query=123',
-              ['date' => 'today',
-              'accept' => 'llamas', ],
-            'Stuff that belongs in /path');
-        $message = $this->withDigestContext->signer()->signWithDigest($message);
-
-        $expectedString = implode(',', [
-            'keyId="pda"',
-            'algorithm="hmac-sha256"',
-            'headers="(request-target) date digest"',
-            'signature="p8gQHs59X2WzQLUecfmxm1YO0OBTCNKldRZZBQsepfk="', ]);
-        $expectedDigestHeader =
-          'SHA-256=jnSMEfBSum4Rh2k6/IVFyvLuQLmGYwMAGBS9WybyDqQ=';
-
-        $this->assertEquals(
-            $expectedString,
-            $message->getHeader('Signature')[0]
-        );
-
-        $this->assertEquals(
-            $expectedDigestHeader,
-            $message->getHeader('Digest')[0]
-        );
-
-        $this->assertEquals(
-            'Signature '.$expectedString,
-            $message->getHeader('Authorization')[0]
-        );
-    }
-
-    public function testSignerNewDigestWithoutBody()
-    {
-        $message = new Request(
-            'GET', '/path?query=123',
-              ['date' => 'today',
-              'accept' => 'llamas', ]);
-        $message = $this->withDigestContext->signer()->signWithDigest($message);
-
-        $expectedString = implode(',', [
-            'keyId="pda"',
-            'algorithm="hmac-sha256"',
-            'headers="(request-target) date digest"',
-            'signature="7iFqqryI6I9opV/Zp3eEg6PDY1tKw/3GqioOM7ACHHA="', ]);
-        $zeroLengthStringDigest =
-          'SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=';
-
-        $this->assertEquals(
-            $expectedString,
-            $message->getHeader('Signature')[0]
-        );
-
-        $this->assertEquals(
-            $zeroLengthStringDigest,
-            $message->getHeader('Digest')[0]
-        );
-
-        $this->assertEquals(
-            'Signature '.$expectedString,
-            $message->getHeader('Authorization')[0]
-        );
-    }
-
-    public function testVerifier()
-    {
-        $message = $this->noDigestContext->signer()->sign(new Request('GET', '/path?query=123', [
-            'Signature' => 'keyId="pda",algorithm="hmac-sha1",headers="date",signature="x"',
-            'Date' => 'x',
-        ]));
-
-        // assert it works without errors; correctness of results tested elsewhere.
-        $this->assertTrue(is_bool($this->noDigestContext->verifier()->isValid($message)));
-    }
-}

+ 0 - 16
tests/Unit/HttpSignatures/KeyStoreHmacTest.php

@@ -1,16 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use App\Util\HttpSignatures\KeyStore;
-
-class KeyStoreHmacTest extends \PHPUnit\Framework\TestCase
-{
-    public function testFetchHmacSuccess()
-    {
-        $ks = new KeyStore(['hmacsecret' => 'ThisIsASecretKey']);
-        $key = $ks->fetch('hmacsecret');
-        $this->assertEquals(['hmacsecret', 'ThisIsASecretKey', 'ThisIsASecretKey', 'secret'], [
-          $key->getId(), $key->getVerifyingKey(), $key->getSigningKey(), $key->getType(), ]);
-    }
-}

+ 0 - 114
tests/Unit/HttpSignatures/KeyStoreRsaTest.php

@@ -1,114 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use App\Util\HttpSignatures\KeyStore;
-use App\Util\HttpSignatures\Key;
-use Tests\Unit\HttpSignatures\TestKeys;
-
-class KeyStoreRsaTest extends \PHPUnit\Framework\TestCase
-{
-    public function setUp()
-    {
-        openssl_pkey_export(
-            openssl_pkey_get_private(TestKeys::rsaPrivateKey),
-            $this->testRsaPrivateKeyPEM
-        );
-        $this->testRsaPublicKeyPEM = openssl_pkey_get_details(
-            openssl_get_publickey(TestKeys::rsaPublicKey)
-        )['key'];
-        $this->testRsaCert = TestKeys::rsaCert;
-    }
-
-    public function testParseX509inObject()
-    {
-        $keySpec = ['rsaCert' => [TestKeys::rsaCert]];
-        $this->assertTrue(Key::hasX509Certificate($keySpec));
-
-        $ks = new KeyStore($keySpec);
-        $publicKey = $ks->fetch('rsaCert')->getVerifyingKey();
-        $this->assertEquals('asymmetric', $ks->fetch('rsaCert')->getType());
-        $this->assertEquals(TestKeys::rsaPublicKey, $publicKey);
-    }
-
-    public function testParseRsaPublicKeyinObject()
-    {
-        $keySpec = ['rsaPubKey' => [TestKeys::rsaPublicKey]];
-        $this->assertTrue(Key::hasPublicKey($keySpec));
-
-        $ks = new KeyStore($keySpec);
-        $publicKey = $ks->fetch('rsaPubKey')->getVerifyingKey();
-        $this->assertEquals('asymmetric', $ks->fetch('rsaPubKey')->getType());
-        $this->assertEquals(TestKeys::rsaPublicKey, $publicKey);
-    }
-
-    public function testParsePrivateKeyinObject()
-    {
-        $keySpec = ['rsaPrivKey' => [TestKeys::rsaPrivateKey]];
-        $this->assertTrue(Key::hasPrivateKey($keySpec));
-
-        $ks = new KeyStore($keySpec);
-        $publicKey = $ks->fetch('rsaPrivKey')->getSigningKey();
-        $this->assertEquals('asymmetric', $ks->fetch('rsaPrivKey')->getType());
-        $this->assertEquals($this->testRsaPrivateKeyPEM, $publicKey);
-    }
-
-    public function testFetchRsaSigningKeySuccess()
-    {
-        $ks = new KeyStore(['rsakey' => TestKeys::rsaPrivateKey]);
-        $key = $ks->fetch('rsakey');
-        openssl_pkey_export($key->getSigningKey(), $keyStoreSigningKey);
-        $this->assertEquals(['rsakey', $this->testRsaPrivateKeyPEM, null, 'asymmetric'], [
-          $key->getId(), $keyStoreSigningKey, $key->getVerifyingKey(), $key->getType(), ]);
-    }
-
-    public function testFetchRsaVerifyingKeyFromCertificateSuccess()
-    {
-        $ks = new KeyStore(['rsacert' => TestKeys::rsaCert]);
-        $key = $ks->fetch('rsacert');
-        $keyStoreVerifyingKey = $key->getVerifyingKey();
-        $this->assertEquals(['rsacert', null, $this->testRsaPublicKeyPEM, 'asymmetric'], [
-          $key->getId(), $key->getSigningKey(), $keyStoreVerifyingKey, $key->getType(), ]);
-    }
-
-    public function testFetchRsaVerifyingKeyFromPublicKeySuccess()
-    {
-        $ks = new KeyStore(['rsapubkey' => TestKeys::rsaPublicKey]);
-        $key = $ks->fetch('rsapubkey');
-        $keyStoreVerifyingKey = $key->getVerifyingKey();
-        $this->assertEquals(['rsapubkey', null, $this->testRsaPublicKeyPEM, 'asymmetric'], [
-          $key->getId(), $key->getSigningKey(), $keyStoreVerifyingKey, $key->getType(), ]);
-    }
-
-    public function testFetchRsaBothSuccess()
-    {
-        $ks = new KeyStore(['rsaboth' => [TestKeys::rsaCert, TestKeys::rsaPrivateKey]]);
-        $key = $ks->fetch('rsaboth');
-        $keyStoreVerifyingKey = $key->getVerifyingKey();
-        $keyStoreSigningKey = $key->getSigningKey();
-        $this->assertEquals(['rsaboth', $this->testRsaPrivateKeyPEM, $this->testRsaPublicKeyPEM, 'asymmetric'], [
-          $key->getId(), $keyStoreSigningKey, $keyStoreVerifyingKey, $key->getType(), ]);
-    }
-
-    public function testFetchRsaBothSuccessSwitched()
-    {
-        $ks = new KeyStore(['rsabothswitch' => [TestKeys::rsaPrivateKey, TestKeys::rsaCert]]);
-        $key = $ks->fetch('rsabothswitch');
-        $keyStoreVerifyingKey = $key->getVerifyingKey();
-        $keyStoreSigningKey = $key->getSigningKey();
-        $this->assertEquals(['rsabothswitch', $this->testRsaPrivateKeyPEM, $this->testRsaPublicKeyPEM, 'asymmetric'], [
-          $key->getId(), $keyStoreSigningKey, $keyStoreVerifyingKey, $key->getType(), ]);
-    }
-
-    /**
-     * @expectedException \App\Util\HttpSignatures\KeyException
-     */
-    public function testRsaMismatch()
-    {
-        $privateKey = openssl_pkey_new([
-          'private_key_type' => 'OPENSSL_KEYTYPE_RSA',
-          'private_key_bits' => 1024, ]
-        );
-        $ks = new Key('badpki', [TestKeys::rsaCert, $privateKey]);
-    }
-}

+ 0 - 17
tests/Unit/HttpSignatures/KeyStoreTest.php

@@ -1,17 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use App\Util\HttpSignatures\KeyStore;
-
-class KeyStoreTest extends \PHPUnit\Framework\TestCase
-{
-    /**
-     * @expectedException App\Util\HttpSignatures\Exception
-     */
-    public function testFetchFail()
-    {
-        $ks = new KeyStore(['id' => 'secret']);
-        $key = $ks->fetch('nope');
-    }
-}

+ 0 - 84
tests/Unit/HttpSignatures/RsaContextTest.php

@@ -1,84 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use GuzzleHttp\Psr7\Request;
-use App\Util\HttpSignatures\Context;
-use Tests\Unit\HttpSignatures\TestKeys;
-
-class RsaContextTest extends \PHPUnit\Framework\TestCase
-{
-    private $context;
-
-    public function setUp()
-    {
-        $this->sha1context = new Context([
-            'keys' => ['rsa1' => TestKeys::rsaPrivateKey],
-            'algorithm' => 'rsa-sha1',
-            'headers' => ['(request-target)', 'date'],
-        ]);
-        $this->sha256context = new Context([
-            'keys' => ['rsa1' => TestKeys::rsaPrivateKey],
-            'algorithm' => 'rsa-sha256',
-            'headers' => ['(request-target)', 'date'],
-        ]);
-    }
-
-    public function testSha1Signer()
-    {
-        $message = new Request('GET', '/path?query=123', ['date' => 'today', 'accept' => 'llamas']);
-
-        $message = $this->sha1context->signer()->sign($message);
-        $expectedSha1String = implode(',', [
-            'keyId="rsa1"',
-            'algorithm="rsa-sha1"',
-            'headers="(request-target) date"',
-            'signature="YIR3DteE3Jmz1VAnUMTgjTn3vTKfQuZl1CJhMBvGOZpnzwKeYBXA'.
-              'H108FojnbSeVG/AXq9pcrA6AFK0peg0aueqxpaFlo+4L/q5XzJ+QoryY3dlSr'.
-              'xwVnE5s5M19xmFm/6YkZR/KPeANCsG4SPL82Um/PCEMU0tmKd6sSx+IIzAYbX'.
-              'G/VrFMDeQAdXqpU1EhgxopKEAapN8rChb49+1JfR/RxlSKiLukJJ6auurm2zM'.
-              'n2D40fR1d2umA5LAO7vRt2iQwVbtwiFkVlRqkMvGftCNZByu8jJ6StI5H7Efu'.
-              'ANSHAZXKXWNH8yxpBUW/QCHCZjPd0ugM0QJJIc7i8JbGlA=="',
-        ]);
-
-        $this->assertEquals(
-            $expectedSha1String,
-            $message->getHeader('Signature')[0]
-        );
-    }
-
-    public function testSha256Signer()
-    {
-        $message = new Request('GET', '/path?query=123', ['date' => 'today', 'accept' => 'llamas']);
-
-        $message = $this->sha256context->signer()->sign($message);
-        $expectedSha256String = implode(',', [
-            'keyId="rsa1"',
-            'algorithm="rsa-sha256"',
-            'headers="(request-target) date"',
-            'signature="WGIegQCC3GEwxbkuXtq67CAqeDhkwblxAH2uoDx5kfWurhLRA5WB'.
-            'FDA/aktsZAjuUoimG1w4CGxSecziER1ez44PBlHP2fCW4ArLgnQgcjkdN2cOf/g'.
-            'j0OVL8s2usG4o4tud/+jjF3nxTxLl3HC+erBKsJakwXbw9kt4Cr028BToVfNXsW'.
-            'oMFpv0IjcgBH2V41AVlX/mYBMMJAihBCIcpgAcGrrxmG2gkfvSn09wtTttkGHft'.
-            'PIp3VpB53zbemlJS9Yw3tmmHr6cvWSXqQy/bTsEOoQJ2REfn5eiyzsJu3GiOpiI'.
-            'LK67i/WH9moltJtlfV57TV72cgYtjWa6yqhtFg=="',
-        ]);
-
-        $this->assertEquals(
-            $expectedSha256String,
-            $message->getHeader('Signature')[0]
-        );
-    }
-
-    /**
-     * @expectedException     App\Util\HttpSignatures\AlgorithmException
-     */
-    public function testRsaBadalgorithm()
-    {
-        $sha224context = new Context([
-              'keys' => ['rsa1' => TestKeys::rsaPrivateKey],
-              'algorithm' => 'rsa-sha224',
-              'headers' => ['(request-target)', 'date'],
-          ]);
-    }
-}

+ 0 - 156
tests/Unit/HttpSignatures/RsaVerifierTest.php

@@ -1,156 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use GuzzleHttp\Psr7\Request;
-use App\Util\HttpSignatures\KeyStore;
-use App\Util\HttpSignatures\Verifier;
-use Tests\Unit\HttpSignatures\TestKeys;
-
-class VerifierRsaTest extends \PHPUnit\Framework\TestCase
-{
-    const DATE = 'Fri, 01 Aug 2014 13:44:32 -0700';
-    const DATE_DIFFERENT = 'Fri, 01 Aug 2014 13:44:33 -0700';
-
-    /**
-     * @var Verifier
-     */
-    private $verifier;
-
-    /**
-     * @var Request
-     */
-    private $message;
-
-    public function setUp()
-    {
-        $this->setUpRsaVerifier();
-
-        $sha1SignatureHeader =
-        'keyId="rsa1",algorithm="rsa-sha1",headers="(request-target) date",'.
-        'signature="YIR3DteE3Jmz1VAnUMTgjTn3vTKfQuZl1CJhMBvGOZpnzwKeYBXAH10'.
-        '8FojnbSeVG/AXq9pcrA6AFK0peg0aueqxpaFlo+4L/q5XzJ+QoryY3dlSrxwVnE5s5'.
-        'M19xmFm/6YkZR/KPeANCsG4SPL82Um/PCEMU0tmKd6sSx+IIzAYbXG/VrFMDeQAdXq'.
-        'pU1EhgxopKEAapN8rChb49+1JfR/RxlSKiLukJJ6auurm2zMn2D40fR1d2umA5LAO7'.
-        'vRt2iQwVbtwiFkVlRqkMvGftCNZByu8jJ6StI5H7EfuANSHAZXKXWNH8yxpBUW/QCH'.
-        'CZjPd0ugM0QJJIc7i8JbGlA=="';
-
-        $this->sha1Message = new Request('GET', '/path?query=123', [
-            'Date' => 'today',
-            'Signature' => $sha1SignatureHeader,
-        ]);
-
-        $sha256SignatureHeader =
-        'keyId="rsa1",algorithm="rsa-sha256",headers="(request-target) date",'.
-        'signature="WGIegQCC3GEwxbkuXtq67CAqeDhkwblxAH2uoDx5kfWurhLRA5WBFDA/a'.
-        'ktsZAjuUoimG1w4CGxSecziER1ez44PBlHP2fCW4ArLgnQgcjkdN2cOf/gj0OVL8s2us'.
-        'G4o4tud/+jjF3nxTxLl3HC+erBKsJakwXbw9kt4Cr028BToVfNXsWoMFpv0IjcgBH2V4'.
-        '1AVlX/mYBMMJAihBCIcpgAcGrrxmG2gkfvSn09wtTttkGHftPIp3VpB53zbemlJS9Yw3'.
-        'tmmHr6cvWSXqQy/bTsEOoQJ2REfn5eiyzsJu3GiOpiILK67i/WH9moltJtlfV57TV72c'.
-        'gYtjWa6yqhtFg=="';
-
-        $this->sha256Message = new Request('GET', '/path?query=123', [
-            'Date' => 'today',
-            'Signature' => $sha256SignatureHeader,
-        ]);
-    }
-
-    private function setUpRsaVerifier()
-    {
-        $keyStore = new KeyStore(['rsa1' => TestKeys::rsaPublicKey]);
-        $this->verifier = new Verifier($keyStore);
-    }
-
-    public function testVerifyValidRsaMessage()
-    {
-        $this->assertTrue($this->verifier->isValid($this->sha1Message));
-        $this->assertTrue($this->verifier->isValid($this->sha256Message));
-    }
-
-    public function testVerifyValidRsaMessageAuthorizationHeader()
-    {
-        $message = $this->sha1Message->withHeader(
-          'Authorization',
-          "Signature {$this->sha1Message->getHeader('Signature')[0]}");
-        $message = $this->sha1Message->withoutHeader('Signature');
-
-        $this->assertTrue($this->verifier->isValid($this->sha1Message));
-
-        $message = $this->sha256Message->withHeader(
-          'Authorization',
-          "Signature {$this->sha256Message->getHeader('Signature')[0]}");
-        $message = $this->sha256Message->withoutHeader('Signature');
-
-        $this->assertTrue($this->verifier->isValid($this->sha256Message));
-    }
-
-    public function testRejectTamperedRsaRequestMethod()
-    {
-        $message = $this->sha1Message->withMethod('POST');
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withMethod('POST');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectTamperedRsaDate()
-    {
-        $message = $this->sha1Message->withHeader('Date', self::DATE_DIFFERENT);
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withHeader('Date', self::DATE_DIFFERENT);
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectTamperedRsaSignature()
-    {
-        $message = $this->sha1Message->withHeader(
-            'Signature',
-            preg_replace('/signature="/', 'signature="x', $this->sha1Message->getHeader('Signature')[0])
-        );
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withHeader(
-            'Signature',
-            preg_replace('/signature="/', 'signature="x', $this->sha256Message->getHeader('Signature')[0])
-        );
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectRsaMessageWithoutSignatureHeader()
-    {
-        $message = $this->sha1Message->withoutHeader('Signature');
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withoutHeader('Signature');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectRsaMessageWithGarbageSignatureHeader()
-    {
-        $message = $this->sha1Message->withHeader('Signature', 'not="a",valid="signature"');
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withHeader('Signature', 'not="a",valid="signature"');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectRsaMessageWithPartialSignatureHeader()
-    {
-        $message = $this->sha1Message->withHeader('Signature', 'keyId="aa",algorithm="bb"');
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withHeader('Signature', 'keyId="aa",algorithm="bb"');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectsRsaMessageWithUnknownKeyId()
-    {
-        $keyStore = new KeyStore(['nope' => 'secret']);
-        $verifier = new Verifier($keyStore);
-        $this->assertFalse($verifier->isValid($this->sha1Message));
-        $this->assertFalse($verifier->isValid($this->sha256Message));
-    }
-
-    public function testRejectsRsaMessageMissingSignedHeaders()
-    {
-        $message = $this->sha1Message->withoutHeader('Date');
-        $this->assertFalse($this->verifier->isValid($message));
-        $message = $this->sha256Message->withoutHeader('Date');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-}

+ 0 - 44
tests/Unit/HttpSignatures/SignatureParametersParserTest.php

@@ -1,44 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use App\Util\HttpSignatures\SignatureParametersParser;
-
-class SignatureParametersParserTest extends \PHPUnit\Framework\TestCase
-{
-    public function testParseReturnsExpectedAssociativeArray()
-    {
-        $parser = new SignatureParametersParser(
-            'keyId="example",algorithm="hmac-sha1",headers="(request-target) date",signature="b64"'
-        );
-        $this->assertEquals(
-            [
-                'keyId' => 'example',
-                'algorithm' => 'hmac-sha1',
-                'headers' => '(request-target) date',
-                'signature' => 'b64',
-            ],
-            $parser->parse()
-        );
-    }
-
-    /**
-     * @expectedException App\Util\HttpSignatures\SignatureParseException
-     */
-    public function testParseThrowsTypedException()
-    {
-        $parser = new SignatureParametersParser('nope');
-        $parser->parse();
-    }
-
-    /**
-     * @expectedException App\Util\HttpSignatures\SignatureParseException
-     */
-    public function testParseExceptionForMissingComponents()
-    {
-        $parser = new SignatureParametersParser(
-            'keyId="example",algorithm="hmac-sha1",headers="(request-target) date"'
-        );
-        $parser->parse();
-    }
-}

+ 0 - 58
tests/Unit/HttpSignatures/SignatureParametersTest.php

@@ -1,58 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use App\Util\HttpSignatures\HeaderList;
-use App\Util\HttpSignatures\HmacAlgorithm;
-use App\Util\HttpSignatures\RsaAlgorithm;
-use App\Util\HttpSignatures\Key;
-use App\Util\HttpSignatures\SignatureParameters;
-
-class SignatureParametersTest extends \PHPUnit\Framework\TestCase
-{
-    public function testHmacToString()
-    {
-        $key = new Key('pda', 'secret');
-        $algorithm = new HmacAlgorithm('sha256');
-        $headerList = new HeaderList(['(request-target)', 'date']);
-
-        $signature = $this->getMockBuilder('HttpSignatures\Signature')
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $signature
-            ->expects($this->any())
-            ->method('string')
-            ->will($this->returnValue('thesignature'));
-
-        $sp = new SignatureParameters($key, $algorithm, $headerList, $signature);
-
-        $this->assertEquals(
-            'keyId="pda",algorithm="hmac-sha256",headers="(request-target) date",signature="dGhlc2lnbmF0dXJl"',
-            $sp->string()
-        );
-    }
-
-    public function testRsaToString()
-    {
-        $key = new Key('pda', TestKeys::rsaPrivateKey);
-        $algorithm = new RsaAlgorithm('sha256');
-        $headerList = new HeaderList(['(request-target)', 'date']);
-
-        $signature = $this->getMockBuilder('HttpSignatures\Signature')
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $signature
-            ->expects($this->any())
-            ->method('string')
-            ->will($this->returnValue('thesignature'));
-
-        $sp = new SignatureParameters($key, $algorithm, $headerList, $signature);
-
-        $this->assertEquals(
-            'keyId="pda",algorithm="rsa-sha256",headers="(request-target) date",signature="dGhlc2lnbmF0dXJl"',
-            $sp->string()
-        );
-    }
-}

+ 0 - 93
tests/Unit/HttpSignatures/SigningStringTest.php

@@ -1,93 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use GuzzleHttp\Psr7\Request;
-use App\Util\HttpSignatures\HeaderList;
-use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
-use App\Util\HttpSignatures\SigningString;
-use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
-
-class SigningStringTest extends \PHPUnit\Framework\TestCase
-{
-    public function testWithoutQueryString()
-    {
-        $headerList = new HeaderList(['(request-target)']);
-        $ss = new SigningString($headerList, $this->message('/path'));
-
-        $this->assertEquals(
-            '(request-target): get /path',
-            $ss->string()
-        );
-    }
-
-    public function testSigningStringWithOrderedQueryParameters()
-    {
-        $headerList = new HeaderList(['(request-target)', 'date']);
-        $ss = new SigningString($headerList, $this->message('/path?a=antelope&z=zebra'));
-
-        $this->assertEquals(
-            "(request-target): get /path?a=antelope&z=zebra\ndate: Mon, 28 Jul 2014 15:39:13 -0700",
-            $ss->string()
-        );
-    }
-
-    public function testSigningStringWithUnorderedQueryParameters()
-    {
-        $headerList = new HeaderList(['(request-target)', 'date']);
-        $ss = new SigningString($headerList, $this->message('/path?z=zebra&a=antelope'));
-
-        $this->assertEquals(
-            "(request-target): get /path?z=zebra&a=antelope\ndate: Mon, 28 Jul 2014 15:39:13 -0700",
-            $ss->string()
-        );
-    }
-
-    public function testSigningStringWithOrderedQueryParametersSymfonyRequest()
-    {
-        $headerList = new HeaderList(['(request-target)', 'date']);
-        $ss = new SigningString($headerList, $this->symfonyMessage('/path?a=antelope&z=zebra'));
-
-        $this->assertEquals(
-            "(request-target): get /path?a=antelope&z=zebra\ndate: Mon, 28 Jul 2014 15:39:13 -0700",
-            $ss->string()
-        );
-    }
-
-    public function testSigningStringWithUnorderedQueryParametersSymfonyRequest()
-    {
-        $headerList = new HeaderList(['(request-target)', 'date']);
-        $ss = new SigningString($headerList, $this->symfonyMessage('/path?z=zebra&a=antelope'));
-
-        $this->assertEquals(
-            "(request-target): get /path?z=zebra&a=antelope\ndate: Mon, 28 Jul 2014 15:39:13 -0700",
-            $ss->string()
-        );
-    }
-
-    /**
-     * @expectedException App\Util\HttpSignatures\Exception
-     */
-    public function testSigningStringErrorForMissingHeader()
-    {
-        $headerList = new HeaderList(['nope']);
-        $ss = new SigningString($headerList, $this->message('/'));
-        $ss->string();
-    }
-
-    private function message($path)
-    {
-        return new Request('GET', $path, ['date' => 'Mon, 28 Jul 2014 15:39:13 -0700']);
-    }
-
-    private function symfonyMessage($path)
-    {
-        $symfonyRequest = SymfonyRequest::create($path, 'GET');
-        $symfonyRequest->headers->replace(['date' => 'Mon, 28 Jul 2014 15:39:13 -0700']);
-
-        $psr7Factory = new DiactorosFactory();
-        $psrRequest = $psr7Factory->createRequest($symfonyRequest)->withRequestTarget($symfonyRequest->getRequestUri());
-
-        return $psrRequest;
-    }
-}

+ 0 - 64
tests/Unit/HttpSignatures/TestKeys.php

@@ -1,64 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-abstract class TestKeys
-{
-    const rsaPrivateKey =
-'-----BEGIN RSA PRIVATE KEY-----
-MIIEpAIBAAKCAQEAxyfPtnW/CakdLpZVh90sR+hpRwiBBdS/za6wal0pFddl2xcL
-sHY/IA1u348xOc4LEY2D0807o1IXSEaqrt9eUkAh+yLGKgDCWLGzW026HBMgojhn
-Y702pmRI2CGqP0xtzrynCSmSMvhdKM9IMc7DGf7Wdo4Y0En0KPat+IyCZpbf7jUX
-cGL/tjxMKS3fXNgthGWVDU2TBSQRFM+fgeH9egODd4DfpzfaUoUDcoB/CZ1FEa4+
-wNfXR5CZ4WjIaeVbCQ09maup07J+KaGe8SQ9LjcUuityyuNBjy5nvQkbmDfAh082
-2lZQYR62FM5EAlNptThODg6q4wkIQ+spLv1dSwIDAQABAoIBAQCXYQyCvVd7qV80
-JTNYNWbONbuoMa+Y1hEA77LK9osfPf3/HbJV7FupKmzHY5lgPdyt9+pnWQ3m46Qs
-3QIqMEEKthLeSJ1mGfOf5VrWoOtBIczhYYw9BPsAWSQBnP1CZf7lcQJqdX3aXmy5
-c22F5oroPIuZzALSeBQt+utcDLml7dr4D2TbFkmh3ocGGK1SDUATGBVQG+MAVGFQ
-J5BYLFPUp8QC2o1672/aqNkx1XO08vIoANtWz5Rjf2hCRrdw1liAN30nnZ+l9h3x
-mbof0sUQMPxxmZ/u+HGPShkN+Y/DLfIyjHlpXI127WBwFLr5P/iL6+DDbsn4Iav3
-L57kSxhhAoGBAO6v/ojuZFE0h3mKHy+SdWggcRqe8Re2C5QTODTy7hRwdUv791lk
-jFnONqlcn51H/hDQwP8nnG8HFxJYl4IoCCdcWfeFlQ82oerIWmsX6H4BFBfF7I9Y
-3Muo1BWj4vif/mwwsL0NOAn5XCO/Gi1nF7YZBd/or+X/x4XLSWKnBISRAoGBANWZ
-xzhpUltGPfKWgfE+ETbZC++Nqsd1MHAdZOhOaeBY615DdmfVv7ryaTWJ1kRbRmq0
-9eKmopEYqfCwyfKZ/8+2dudhndnnEqmiJPu2WFyEiR9sb8NMPcOOjKPhUMgP0ZNx
-Ynz/oTvBOylvU2MfC0hpLghq50JeEJSiGxzE9kIbAoGAejcpeMnAGghwmd4Ma9pt
-PXznDP93aXGwagiRTiNZnqOam+aPV3lxmAZL3NptbCZRxCBvwfZxVjRmLuGn6mA/
-FJBoDKKcmWaa79HY4l8ij2pT9HxGzXttyuZOeiopbK7XomQoCxU6rXi+IhuW9sqD
-zJzxch39+yHF8w8NK3Njj9ECgYEAj8ZXu5fhEIECV5SJWKmvipykFRXleyZdeUm/
-z0Jgr9sKasO8In5U9PAQczIZYJ+TkWXHEE2bpVDVqqZE+KBB+T1XYb1qM+7+t+Hl
-ROzjIzsu1VD3FZzvAf+kmPajmlZTegxa/8pNa9xQBz7hARo3TQFHM/FJQnnwbSuE
-VmQZYjsCgYA9ADxvlgGQmo3uHup6u54S7MgwvzIK7WiXKkuoI5rp0B0mwTr3loVt
-3r3tZBH4+z17fVhmoQ4a4kYT8ixn0XpaL0LOv8s02b36XCNlrfPlafOwhHfOHmlz
-zQnzviLiUOgXyD8FwZlYx+hTM09CYPcdJWSPl6JVF7uxm2fX/HdS3w==
------END RSA PRIVATE KEY-----';
-    const rsaCert =
-'-----BEGIN CERTIFICATE-----
-MIICmjCCAYICCQDIxrpvPCnqRjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0
-ZXN0MB4XDTE4MDgxODEyMjMzMloXDTI4MDgxNTEyMjMzMlowDzENMAsGA1UEAwwE
-dGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcnz7Z1vwmpHS6W
-VYfdLEfoaUcIgQXUv82usGpdKRXXZdsXC7B2PyANbt+PMTnOCxGNg9PNO6NSF0hG
-qq7fXlJAIfsixioAwlixs1tNuhwTIKI4Z2O9NqZkSNghqj9Mbc68pwkpkjL4XSjP
-SDHOwxn+1naOGNBJ9Cj2rfiMgmaW3+41F3Bi/7Y8TCkt31zYLYRllQ1NkwUkERTP
-n4Hh/XoDg3eA36c32lKFA3KAfwmdRRGuPsDX10eQmeFoyGnlWwkNPZmrqdOyfimh
-nvEkPS43FLorcsrjQY8uZ70JG5g3wIdPNtpWUGEethTORAJTabU4Tg4OquMJCEPr
-KS79XUsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAgnvP8UDFND0G/R5ptKeaAUvF
-xmDWnyBOv8/GWb8i9zesBfgjmjoKfjIgbYS/z0ZqMHApuv6td6NovsCVgpfEpLAv
-zxtljLtOWEeQ/25bespBiTiOVp1w8BzEZ2IhNX6M0LxXQkUXgeyOC2wnH6SH9rTW
-USM0aZhhDcdOZ4q+OkpAN6uux3r0QNJLdU8vInBGoyE3s+7MjEun30HQy24HSgEA
-p/Ee+dkqU2Jp7wr5omMzurGrEwre0KjNLbrDvcb/0u8r7RA5sghHiE7MUe8acGqR
-GyMYMn7AX97SD2yxYgwt7i/v65wkAC5oxXA2Yg1TTJZrLD6obGv+wELnePhKgw==
------END CERTIFICATE-----
-';
-    const rsaPublicKey =
-'-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxyfPtnW/CakdLpZVh90s
-R+hpRwiBBdS/za6wal0pFddl2xcLsHY/IA1u348xOc4LEY2D0807o1IXSEaqrt9e
-UkAh+yLGKgDCWLGzW026HBMgojhnY702pmRI2CGqP0xtzrynCSmSMvhdKM9IMc7D
-Gf7Wdo4Y0En0KPat+IyCZpbf7jUXcGL/tjxMKS3fXNgthGWVDU2TBSQRFM+fgeH9
-egODd4DfpzfaUoUDcoB/CZ1FEa4+wNfXR5CZ4WjIaeVbCQ09maup07J+KaGe8SQ9
-LjcUuityyuNBjy5nvQkbmDfAh0822lZQYR62FM5EAlNptThODg6q4wkIQ+spLv1d
-SwIDAQAB
------END PUBLIC KEY-----
-';
-}

+ 0 - 116
tests/Unit/HttpSignatures/VerifierHmacTest.php

@@ -1,116 +0,0 @@
-<?php
-
-namespace Tests\Unit\HttpSignatures;
-
-use GuzzleHttp\Psr7\Request;
-use App\Util\HttpSignatures\KeyStore;
-use App\Util\HttpSignatures\Verifier;
-
-class VerifierHmacTest extends \PHPUnit\Framework\TestCase
-{
-    const DATE = 'Fri, 01 Aug 2014 13:44:32 -0700';
-    const DATE_DIFFERENT = 'Fri, 01 Aug 2014 13:44:33 -0700';
-
-    /**
-     * @var Verifier
-     */
-    private $verifier;
-
-    /**
-     * @var Request
-     */
-    private $message;
-
-    public function setUp()
-    {
-        $this->setUpHmacVerifier();
-        $this->setUpValidHmacMessage();
-    }
-
-    private function setUpHmacVerifier()
-    {
-        $keyStore = new KeyStore(['secret1' => 'secret']);
-        $this->verifier = new Verifier($keyStore);
-    }
-
-    private function setUpValidHmacMessage()
-    {
-        $signatureHeader = sprintf(
-            'keyId="%s",algorithm="%s",headers="%s",signature="%s"',
-            'secret1',
-            'hmac-sha256',
-            '(request-target) date',
-            'cS2VvndvReuTLy52Ggi4j6UaDqGm9hMb4z0xJZ6adqU='
-        );
-
-        $this->message = new Request('GET', '/path?query=123', [
-            'Date' => self::DATE,
-            'Signature' => $signatureHeader,
-        ]);
-    }
-
-    public function testVerifyValidHmacMessage()
-    {
-        $this->assertTrue($this->verifier->isValid($this->message));
-    }
-
-    public function testVerifyValidHmacMessageAuthorizationHeader()
-    {
-        $message = $this->message->withHeader('Authorization', "Signature {$this->message->getHeader('Signature')[0]}");
-        $message = $message->withoutHeader('Signature');
-
-        $this->assertTrue($this->verifier->isValid($this->message));
-    }
-
-    public function testRejectTamperedHmacRequestMethod()
-    {
-        $message = $this->message->withMethod('POST');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectTamperedHmacDate()
-    {
-        $message = $this->message->withHeader('Date', self::DATE_DIFFERENT);
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectTamperedHmacSignature()
-    {
-        $message = $this->message->withHeader(
-            'Signature',
-            preg_replace('/signature="/', 'signature="x', $this->message->getHeader('Signature')[0])
-        );
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectHmacMessageWithoutSignatureHeader()
-    {
-        $message = $this->message->withoutHeader('Signature');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectHmacMessageWithGarbageSignatureHeader()
-    {
-        $message = $this->message->withHeader('Signature', 'not="a",valid="signature"');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectHmacMessageWithPartialSignatureHeader()
-    {
-        $message = $this->message->withHeader('Signature', 'keyId="aa",algorithm="bb"');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-
-    public function testRejectsHmacMessageWithUnknownKeyId()
-    {
-        $keyStore = new KeyStore(['nope' => 'secret']);
-        $verifier = new Verifier($keyStore);
-        $this->assertFalse($verifier->isValid($this->message));
-    }
-
-    public function testRejectsHmacMessageMissingSignedHeaders()
-    {
-        $message = $this->message->withoutHeader('Date');
-        $this->assertFalse($this->verifier->isValid($message));
-    }
-}

+ 1 - 2
tests/Unit/Lexer/StatusLexerTest.php

@@ -14,7 +14,7 @@ class StatusLexerTest extends TestCase
     public $entities;
     public $entities;
 	public $autolink;
 	public $autolink;
 
 
-    public function setUp()
+    public function setUp(): void
     {
     {
         parent::setUp();
         parent::setUp();
     	$this->status = "@pixelfed hi, really like the website! #píxelfed";
     	$this->status = "@pixelfed hi, really like the website! #píxelfed";
@@ -60,7 +60,6 @@ class StatusLexerTest extends TestCase
     public function testAutolink()
     public function testAutolink()
     {
     {
         $expected = '@<a class="u-url mention" href="https://pixelfed.dev/pixelfed" rel="external nofollow noopener" target="_blank">pixelfed</a> hi, really like the website! <a href="https://pixelfed.dev/discover/tags/píxelfed?src=hash" title="#píxelfed" class="u-url hashtag" rel="external nofollow noopener">#píxelfed</a>';
         $expected = '@<a class="u-url mention" href="https://pixelfed.dev/pixelfed" rel="external nofollow noopener" target="_blank">pixelfed</a> hi, really like the website! <a href="https://pixelfed.dev/discover/tags/píxelfed?src=hash" title="#píxelfed" class="u-url hashtag" rel="external nofollow noopener">#píxelfed</a>';
-
         $this->assertEquals($this->autolink, $expected);
         $this->assertEquals($this->autolink, $expected);
     }
     }
 }
 }

部分文件因文件數量過多而無法顯示