From 9392e1bec3fd1fa967e1c59dc23890631aaa997d Mon Sep 17 00:00:00 2001
From: Toby Zerner <toby.zerner@gmail.com>
Date: Wed, 29 Nov 2017 12:52:49 +1030
Subject: [PATCH] New design for reset password view

---
 .../Controller/ResetPasswordController.php    |  14 +--
 .../Controller/SavePasswordController.php     |   3 +-
 src/Forum/ForumServiceProvider.php            |   7 ++
 .../Middleware/ShareErrorsFromSession.php     |  62 ++++++++++
 views/layouts/basic.blade.php                 | 106 ++++++++++++++++++
 views/reset-password.blade.php                |  33 ++++++
 views/reset.blade.php                         |  37 ------
 7 files changed, 213 insertions(+), 49 deletions(-)
 create mode 100644 src/Http/Middleware/ShareErrorsFromSession.php
 create mode 100644 views/layouts/basic.blade.php
 create mode 100644 views/reset-password.blade.php
 delete mode 100644 views/reset.blade.php

diff --git a/src/Forum/Controller/ResetPasswordController.php b/src/Forum/Controller/ResetPasswordController.php
index 42a47b288..1e87fdf0c 100644
--- a/src/Forum/Controller/ResetPasswordController.php
+++ b/src/Forum/Controller/ResetPasswordController.php
@@ -26,18 +26,12 @@ class ResetPasswordController extends AbstractHtmlController
      */
     protected $view;
 
-    /**
-     * @var TranslatorInterface
-     */
-    protected $translator;
-
     /**
      * @param Factory $view
      */
-    public function __construct(Factory $view, TranslatorInterface $translator)
+    public function __construct(Factory $view)
     {
         $this->view = $view;
-        $this->translator = $translator;
     }
 
     /**
@@ -55,10 +49,8 @@ class ResetPasswordController extends AbstractHtmlController
             throw new InvalidConfirmationTokenException;
         }
 
-        return $this->view->make('flarum::reset')
-            ->with('translator', $this->translator)
+        return $this->view->make('flarum.forum::reset-password')
             ->with('passwordToken', $token->id)
-            ->with('csrfToken', $request->getAttribute('session')->get('csrf_token'))
-            ->with('error', $request->getAttribute('session')->get('error'));
+            ->with('csrfToken', $request->getAttribute('session')->get('csrf_token'));
     }
 }
diff --git a/src/Forum/Controller/SavePasswordController.php b/src/Forum/Controller/SavePasswordController.php
index 0b36f9779..3a9fc4605 100644
--- a/src/Forum/Controller/SavePasswordController.php
+++ b/src/Forum/Controller/SavePasswordController.php
@@ -75,11 +75,12 @@ class SavePasswordController implements ControllerInterface
             $this->validator->assertValid(compact('password'));
 
             $validator = $this->validatorFactory->make($input, ['password' => 'required|confirmed']);
+
             if ($validator->fails()) {
                 throw new ValidationException($validator);
             }
         } catch (ValidationException $e) {
-            $request->getAttribute('session')->set('error', $e->errors()->first());
+            $request->getAttribute('session')->set('errors', $e->errors());
 
             return new RedirectResponse($this->url->toRoute('resetPassword', ['token' => $token->id]));
         }
diff --git a/src/Forum/ForumServiceProvider.php b/src/Forum/ForumServiceProvider.php
index 43f4c2bed..766cdc618 100644
--- a/src/Forum/ForumServiceProvider.php
+++ b/src/Forum/ForumServiceProvider.php
@@ -18,6 +18,8 @@ use Flarum\Event\SettingWasSet;
 use Flarum\Foundation\AbstractServiceProvider;
 use Flarum\Http\Handler\RouteHandlerFactory;
 use Flarum\Http\RouteCollection;
+use Flarum\Settings\SettingsRepositoryInterface;
+use Symfony\Component\Translation\TranslatorInterface;
 
 class ForumServiceProvider extends AbstractServiceProvider
 {
@@ -44,6 +46,11 @@ class ForumServiceProvider extends AbstractServiceProvider
 
         $this->loadViewsFrom(__DIR__.'/../../views', 'flarum.forum');
 
+        $this->app->make('view')->share([
+            'translator' => $this->app->make(TranslatorInterface::class),
+            'settings' => $this->app->make(SettingsRepositoryInterface::class)
+        ]);
+
         $this->flushWebAppAssetsWhenThemeChanged();
 
         $this->flushWebAppAssetsWhenExtensionsChanged();
diff --git a/src/Http/Middleware/ShareErrorsFromSession.php b/src/Http/Middleware/ShareErrorsFromSession.php
new file mode 100644
index 000000000..4ab83d2f8
--- /dev/null
+++ b/src/Http/Middleware/ShareErrorsFromSession.php
@@ -0,0 +1,62 @@
+<?php
+/*
+ * This file is part of Flarum.
+ *
+ * (c) Toby Zerner <toby.zerner@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Flarum\Http\Middleware;
+
+use Illuminate\Support\ViewErrorBag;
+use Illuminate\Contracts\View\Factory as ViewFactory;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Zend\Stratigility\MiddlewareInterface;
+
+/**
+ * Inspired by Illuminate\View\Middleware\ShareErrorsFromSession
+ *
+ * @author Taylor Otwell
+ */
+class ShareErrorsFromSession implements MiddlewareInterface
+{
+    /**
+     * @var ViewFactory
+     */
+    protected $view;
+
+    /**
+     * @param ViewFactory $view
+     */
+    public function __construct(ViewFactory $view)
+    {
+        $this->view = $view;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __invoke(Request $request, Response $response, callable $out = null)
+    {
+        $session = $request->getAttribute('session');
+
+        // If the current session has an "errors" variable bound to it, we will share
+        // its value with all view instances so the views can easily access errors
+        // without having to bind. An empty bag is set when there aren't errors.
+        $this->view->share(
+            'errors', $session->get('errors', new ViewErrorBag)
+        );
+
+        // Putting the errors in the view for every view allows the developer to just
+        // assume that some errors are always available, which is convenient since
+        // they don't have to continually run checks for the presence of errors.
+
+        $session->remove('errors');
+
+        return $out ? $out($request, $response) : $response;
+    }
+}
+
diff --git a/views/layouts/basic.blade.php b/views/layouts/basic.blade.php
new file mode 100644
index 000000000..7143e24c9
--- /dev/null
+++ b/views/layouts/basic.blade.php
@@ -0,0 +1,106 @@
+{{-- TODO: Change below to @php when Laravel is upgraded --}}
+<?php
+  $primaryColor = $settings->get('theme_primary_color', '#000');
+?>
+
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    {{-- TODO: Change below to @hasSection when Laravel is upgraded --}}
+    <title>@if ($__env->hasSection('title')) @yield('title') - @endif{{ $settings->get('forum_title') }}</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+
+    <style>
+      * {
+       box-sizing: border-box;
+      }
+      body {
+        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+        font-size: 18px;
+        text-align: center;
+        line-height: 1.5;
+        color: #333;
+      }
+      input,
+      button,
+      select,
+      textarea {
+        font-family: inherit;
+        font-size: inherit;
+        line-height: inherit;
+      }
+      a {
+        cursor: pointer;
+        color: {{ $primaryColor }};
+        text-decoration: none;
+      }
+      a:hover {
+        text-decoration: underline;
+      }
+      .container {
+        margin: 100px auto;
+        max-width: 450px;
+        padding: 0 15px;
+      }
+      .button {
+        display: inline-block;
+        padding: 15px 25px;
+        background: {{ $primaryColor }};
+        color: #fff;
+        text-decoration: none;
+        text-align: center;
+        vertical-align: middle;
+        border-radius: 4px;
+        cursor: pointer;
+        white-space: nowrap;
+        font-weight: bold;
+        border: 0;
+      }
+      .button:hover {
+        text-decoration: none;
+      }
+      .button:active,
+      .button.active {
+        box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+      }
+      .form {
+        max-width: 300px;
+        margin: 0 auto;
+      }
+      .form .button {
+        display: block;
+        width: 100%;
+      }
+      .form-control {
+        display: block;
+        width: 100%;
+        text-align: center;
+        padding: 15px 20px;
+        background-color: #fff;
+        border: 2px solid #eee;
+        border-radius: 4px;
+        transition: border-color .15s;
+      }
+      .form-control:focus,
+      .form-control.focus {
+         border-color: {{ $primaryColor }};
+         outline: none;
+      }
+      .errors {
+        color: #d83e3e;
+      }
+      .errors ul {
+        list-style-type: none;
+        margin: 0;
+        padding: 0;
+      }
+    </style>
+  </head>
+
+  <body>
+    <div class="container">
+      @yield('content')
+    </div>
+  </body>
+</html>
diff --git a/views/reset-password.blade.php b/views/reset-password.blade.php
new file mode 100644
index 000000000..11ac7020e
--- /dev/null
+++ b/views/reset-password.blade.php
@@ -0,0 +1,33 @@
+@extends('flarum.forum::layouts.basic')
+@inject('url', 'Flarum\Forum\UrlGenerator')
+
+@section('title', $translator->trans('core.views.reset_password.title'))
+
+@section('content')
+  @if ($errors->any())
+    <div class="errors">
+      <ul>
+        @foreach ($errors->all() as $error)
+          <li>{{ $error }}</li>
+        @endforeach
+      </ul>
+    </div>
+  @endif
+
+  <form class="form" method="POST" action="{{ $url->toRoute('savePassword') }}">
+    <input type="hidden" name="csrfToken" value="{{ $csrfToken }}">
+    <input type="hidden" name="passwordToken" value="{{ $passwordToken }}">
+
+    <p class="form-group">
+      <input type="password" class="form-control" name="password" placeholder="{{ $translator->trans('core.views.reset_password.new_password_label') }}">
+    </p>
+
+    <p class="form-group">
+      <input type="password" class="form-control" name="password_confirmation" placeholder="{{ $translator->trans('core.views.reset_password.confirm_password_label') }}">
+    </p>
+
+    <p class="form-group">
+      <button type="submit" class="button">{{ $translator->trans('core.views.reset_password.submit_button') }}</button>
+    </p>
+  </form>
+@endsection
diff --git a/views/reset.blade.php b/views/reset.blade.php
deleted file mode 100644
index 671157b38..000000000
--- a/views/reset.blade.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <title>Reset Your Password</title>
-    <meta name="description" content="">
-    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
-  </head>
-
-  <body>
-    <h2>{{ $translator->trans('core.views.reset.title') }}</h2>
-
-    @if (! empty($error))
-      <p style="color:red">{{ $error }}</p>
-    @endif
-
-    <form class="form-horizontal" role="form" method="POST" action="{{ app('Flarum\Forum\UrlGenerator')->toRoute('savePassword') }}">
-      <input type="hidden" name="csrfToken" value="{{ $csrfToken }}">
-      <input type="hidden" name="passwordToken" value="{{ $passwordToken }}">
-
-      <p class="form-group">
-        <label class="control-label">{{ $translator->trans('core.views.reset.password_label') }}</label><br>
-        <input type="password" class="form-control" name="password">
-      </p>
-
-      <p class="form-group">
-        <label class="control-label">{{ $translator->trans('core.views.reset.confirm_password_label') }}</label><br>
-        <input type="password" class="form-control" name="password_confirmation">
-      </p>
-
-      <p class="form-group">
-        <button type="submit" class="btn btn-primary">{{ $translator->trans('core.views.reset.submit_button') }}</button>
-      </p>
-    </form>
-  </body>
-</html>