Browse Source

工单防XSS攻击

zhangjiangbin 8 years ago
parent
commit
5dca43b46b
32 changed files with 889 additions and 4 deletions
  1. 1 1
      app/Http/Controllers/TicketController.php
  2. 2 2
      app/Http/Controllers/UserController.php
  3. 1 0
      composer.json
  4. 124 1
      composer.lock
  5. 2 0
      config/app.php
  6. 105 0
      config/purifier.php
  7. 19 0
      resources/views/vendor/mail/html/button.blade.php
  8. 11 0
      resources/views/vendor/mail/html/footer.blade.php
  9. 7 0
      resources/views/vendor/mail/html/header.blade.php
  10. 54 0
      resources/views/vendor/mail/html/layout.blade.php
  11. 27 0
      resources/views/vendor/mail/html/message.blade.php
  12. 13 0
      resources/views/vendor/mail/html/panel.blade.php
  13. 7 0
      resources/views/vendor/mail/html/promotion.blade.php
  14. 13 0
      resources/views/vendor/mail/html/promotion/button.blade.php
  15. 7 0
      resources/views/vendor/mail/html/subcopy.blade.php
  16. 3 0
      resources/views/vendor/mail/html/table.blade.php
  17. 285 0
      resources/views/vendor/mail/html/themes/default.css
  18. 1 0
      resources/views/vendor/mail/markdown/button.blade.php
  19. 1 0
      resources/views/vendor/mail/markdown/footer.blade.php
  20. 1 0
      resources/views/vendor/mail/markdown/header.blade.php
  21. 9 0
      resources/views/vendor/mail/markdown/layout.blade.php
  22. 27 0
      resources/views/vendor/mail/markdown/message.blade.php
  23. 1 0
      resources/views/vendor/mail/markdown/panel.blade.php
  24. 1 0
      resources/views/vendor/mail/markdown/promotion.blade.php
  25. 1 0
      resources/views/vendor/mail/markdown/promotion/button.blade.php
  26. 1 0
      resources/views/vendor/mail/markdown/subcopy.blade.php
  27. 1 0
      resources/views/vendor/mail/markdown/table.blade.php
  28. 58 0
      resources/views/vendor/notifications/email.blade.php
  29. 36 0
      resources/views/vendor/pagination/bootstrap-4.blade.php
  30. 36 0
      resources/views/vendor/pagination/default.blade.php
  31. 17 0
      resources/views/vendor/pagination/simple-bootstrap-4.blade.php
  32. 17 0
      resources/views/vendor/pagination/simple-default.blade.php

+ 1 - 1
app/Http/Controllers/TicketController.php

@@ -37,7 +37,7 @@ class TicketController extends Controller
         $user = $request->session()->get('user');
 
         if ($request->method() == 'POST') {
-            $content = $request->get('content');
+            $content = clean($request->get('content'));
 
             $obj = new TicketReply();
             $obj->ticket_id = $id;

+ 2 - 2
app/Http/Controllers/UserController.php

@@ -298,7 +298,7 @@ class UserController extends Controller
     public function addTicket(Request $request)
     {
         $title = $request->get('title');
-        $content = $request->get('content');
+        $content = clean($request->get('content'));
 
         $user = $request->session()->get('user');
 
@@ -325,7 +325,7 @@ class UserController extends Controller
         $user = $request->session()->get('user');
 
         if ($request->method() == 'POST') {
-            $content = $request->get('content');
+            $content = clean($request->get('content'));
 
             $obj = new TicketReply();
             $obj->ticket_id = $id;

+ 1 - 0
composer.json

@@ -13,6 +13,7 @@
         "laravel/tinker": "~1.0",
         "maatwebsite/excel": "~2.1.0",
         "mews/captcha": "^2.1",
+        "mews/purifier": "^2.0",
         "overtrue/laravel-lang": "~3.0",
         "paypal/rest-api-sdk-php": "*"
     },

+ 124 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "8a222250e9934b48f8d14593fa292b50",
+    "content-hash": "65390c337cfc8249876cc6d926531cf5",
     "packages": [
         {
             "name": "barryvdh/laravel-ide-helper",
@@ -312,6 +312,53 @@
             ],
             "time": "2017-05-14T14:47:48+00:00"
         },
+        {
+            "name": "ezyang/htmlpurifier",
+            "version": "v4.9.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ezyang/htmlpurifier.git",
+                "reference": "95e1bae3182efc0f3422896a3236e991049dac69"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://files.phpcomposer.com/files/ezyang/htmlpurifier/95e1bae3182efc0f3422896a3236e991049dac69.zip",
+                "reference": "95e1bae3182efc0f3422896a3236e991049dac69",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2"
+            },
+            "require-dev": {
+                "simpletest/simpletest": "^1.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "HTMLPurifier": "library/"
+                },
+                "files": [
+                    "library/HTMLPurifier.composer.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL"
+            ],
+            "authors": [
+                {
+                    "name": "Edward Z. Yang",
+                    "email": "[email protected]",
+                    "homepage": "http://ezyang.com"
+                }
+            ],
+            "description": "Standards compliant HTML filter written in PHP",
+            "homepage": "http://htmlpurifier.org/",
+            "keywords": [
+                "html"
+            ],
+            "time": "2017-06-03T02:28:16+00:00"
+        },
         {
             "name": "guzzlehttp/guzzle",
             "version": "6.3.0",
@@ -1244,6 +1291,81 @@
             ],
             "time": "2017-09-11T14:59:20+00:00"
         },
+        {
+            "name": "mews/purifier",
+            "version": "2.0.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/mewebstudio/Purifier.git",
+                "reference": "85af9a2a932583b2c78a0ed762b46cb19399a0a9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://files.phpcomposer.com/files/mewebstudio/Purifier/85af9a2a932583b2c78a0ed762b46cb19399a0a9.zip",
+                "reference": "85af9a2a932583b2c78a0ed762b46cb19399a0a9",
+                "shasum": ""
+            },
+            "require": {
+                "ezyang/htmlpurifier": "4.9.*",
+                "illuminate/config": "5.*",
+                "illuminate/filesystem": "5.*",
+                "illuminate/support": "5.*",
+                "php": ">=5.5.9"
+            },
+            "require-dev": {
+                "graham-campbell/testbench": "^3.2",
+                "mockery/mockery": "0.9.*",
+                "phpunit/phpunit": "^4.8|^5.0",
+                "scrutinizer/ocular": "^1.3"
+            },
+            "suggest": {
+                "laravel/framework": "To test the Laravel bindings",
+                "laravel/lumen-framework": "To test the Lumen bindings"
+            },
+            "type": "package",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Mews\\Purifier\\PurifierServiceProvider"
+                    ],
+                    "aliases": {
+                        "Purifier": "Mews\\Purifier\\Facades\\Purifier"
+                    }
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Mews\\Purifier\\": "src/"
+                },
+                "files": [
+                    "src/helpers.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Muharrem ERİN",
+                    "email": "[email protected]",
+                    "homepage": "https://github.com/mewebstudio",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Laravel 5 HtmlPurifier Package",
+            "homepage": "https://github.com/mewebstudio/purifier",
+            "keywords": [
+                "Purifier",
+                "htmlpurifier",
+                "laravel5 HtmlPurifier",
+                "laravel5 Purifier",
+                "laravel5 Security",
+                "security",
+                "xss"
+            ],
+            "time": "2017-09-11T15:02:51+00:00"
+        },
         {
             "name": "mobiledetect/mobiledetectlib",
             "version": "2.8.26",
@@ -1725,6 +1847,7 @@
                 "xls",
                 "xlsx"
             ],
+            "abandoned": "phpoffice/phpspreadsheet",
             "time": "2015-05-01T07:00:55+00:00"
         },
         {

+ 2 - 0
config/app.php

@@ -186,6 +186,7 @@ return [
         Mews\Captcha\CaptchaServiceProvider::class,
         Jenssegers\Agent\AgentServiceProvider::class,
         Overtrue\LaravelLang\TranslationServiceProvider::class,
+        Mews\Purifier\PurifierServiceProvider::class,
 
     ],
 
@@ -238,6 +239,7 @@ return [
         'Excel' => Maatwebsite\Excel\Facades\Excel::class,
         'Captcha' => Mews\Captcha\Facades\Captcha::class,
         'Agent' => Jenssegers\Agent\Facades\Agent::class,
+        'Purifier' => Mews\Purifier\Facades\Purifier::class,
 
     ],
 

+ 105 - 0
config/purifier.php

@@ -0,0 +1,105 @@
+<?php
+/**
+ * Ok, glad you are here
+ * first we get a config instance, and set the settings
+ * $config = HTMLPurifier_Config::createDefault();
+ * $config->set('Core.Encoding', $this->config->get('purifier.encoding'));
+ * $config->set('Cache.SerializerPath', $this->config->get('purifier.cachePath'));
+ * if ( ! $this->config->get('purifier.finalize')) {
+ *     $config->autoFinalize = false;
+ * }
+ * $config->loadArray($this->getConfig());
+ *
+ * You must NOT delete the default settings
+ * anything in settings should be compacted with params that needed to instance HTMLPurifier_Config.
+ *
+ * @link http://htmlpurifier.org/live/configdoc/plain.html
+ */
+
+return [
+    'encoding'      => 'UTF-8',
+    'finalize'      => true,
+    'cachePath'     => storage_path('app/purifier'),
+    'cacheFileMode' => 0755,
+    'settings'      => [
+        'default' => [
+            'HTML.Doctype'             => 'HTML 4.01 Transitional',
+            'HTML.Allowed'             => 'div,b,strong,i,em,u,a[href|title],ul,ol,li,p[style],br,span[style],img[width|height|alt|src]',
+            'CSS.AllowedProperties'    => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align',
+            'AutoFormat.AutoParagraph' => true,
+            'AutoFormat.RemoveEmpty'   => true,
+        ],
+        'test'    => [
+            'Attr.EnableID' => 'true',
+        ],
+        "youtube" => [
+            "HTML.SafeIframe"      => 'true',
+            "URI.SafeIframeRegexp" => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/)%",
+        ],
+        'custom_definition' => [
+            'id'  => 'html5-definitions',
+            'rev' => 1,
+            'debug' => false,
+            'elements' => [
+                // http://developers.whatwg.org/sections.html
+                ['section', 'Block', 'Flow', 'Common'],
+                ['nav',     'Block', 'Flow', 'Common'],
+                ['article', 'Block', 'Flow', 'Common'],
+                ['aside',   'Block', 'Flow', 'Common'],
+                ['header',  'Block', 'Flow', 'Common'],
+                ['footer',  'Block', 'Flow', 'Common'],
+				
+				// Content model actually excludes several tags, not modelled here
+                ['address', 'Block', 'Flow', 'Common'],
+                ['hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common'],
+				
+				// http://developers.whatwg.org/grouping-content.html
+                ['figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'],
+                ['figcaption', 'Inline', 'Flow', 'Common'],
+				
+				// http://developers.whatwg.org/the-video-element.html#the-video-element
+                ['video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
+                    'src' => 'URI',
+					'type' => 'Text',
+					'width' => 'Length',
+					'height' => 'Length',
+					'poster' => 'URI',
+					'preload' => 'Enum#auto,metadata,none',
+					'controls' => 'Bool',
+                ]],
+                ['source', 'Block', 'Flow', 'Common', [
+					'src' => 'URI',
+					'type' => 'Text',
+                ]],
+
+				// http://developers.whatwg.org/text-level-semantics.html
+                ['s',    'Inline', 'Inline', 'Common'],
+                ['var',  'Inline', 'Inline', 'Common'],
+                ['sub',  'Inline', 'Inline', 'Common'],
+                ['sup',  'Inline', 'Inline', 'Common'],
+                ['mark', 'Inline', 'Inline', 'Common'],
+                ['wbr',  'Inline', 'Empty', 'Core'],
+				
+				// http://developers.whatwg.org/edits.html
+                ['ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']],
+                ['del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']],
+            ],
+            'attributes' => [
+                ['iframe', 'allowfullscreen', 'Bool'],
+                ['table', 'height', 'Text'],
+                ['td', 'border', 'Text'],
+                ['th', 'border', 'Text'],
+                ['tr', 'width', 'Text'],
+                ['tr', 'height', 'Text'],
+                ['tr', 'border', 'Text'],
+            ],
+        ],
+        'custom_attributes' => [
+            ['a', 'target', 'Enum#_blank,_self,_target,_top'],
+        ],
+        'custom_elements' => [
+            ['u', 'Inline', 'Inline', 'Common'],
+        ],
+    ],
+
+];

+ 19 - 0
resources/views/vendor/mail/html/button.blade.php

@@ -0,0 +1,19 @@
+<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0">
+    <tr>
+        <td align="center">
+            <table width="100%" border="0" cellpadding="0" cellspacing="0">
+                <tr>
+                    <td align="center">
+                        <table border="0" cellpadding="0" cellspacing="0">
+                            <tr>
+                                <td>
+                                    <a href="{{ $url }}" class="button button-{{ $color or 'blue' }}" target="_blank">{{ $slot }}</a>
+                                </td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>

+ 11 - 0
resources/views/vendor/mail/html/footer.blade.php

@@ -0,0 +1,11 @@
+<tr>
+    <td>
+        <table class="footer" align="center" width="570" cellpadding="0" cellspacing="0">
+            <tr>
+                <td class="content-cell" align="center">
+                    {{ Illuminate\Mail\Markdown::parse($slot) }}
+                </td>
+            </tr>
+        </table>
+    </td>
+</tr>

+ 7 - 0
resources/views/vendor/mail/html/header.blade.php

@@ -0,0 +1,7 @@
+<tr>
+    <td class="header">
+        <a href="{{ $url }}">
+            {{ $slot }}
+        </a>
+    </td>
+</tr>

+ 54 - 0
resources/views/vendor/mail/html/layout.blade.php

@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+    <style>
+        @media only screen and (max-width: 600px) {
+            .inner-body {
+                width: 100% !important;
+            }
+
+            .footer {
+                width: 100% !important;
+            }
+        }
+
+        @media only screen and (max-width: 500px) {
+            .button {
+                width: 100% !important;
+            }
+        }
+    </style>
+
+    <table class="wrapper" width="100%" cellpadding="0" cellspacing="0">
+        <tr>
+            <td align="center">
+                <table class="content" width="100%" cellpadding="0" cellspacing="0">
+                    {{ $header or '' }}
+
+                    <!-- Email Body -->
+                    <tr>
+                        <td class="body" width="100%" cellpadding="0" cellspacing="0">
+                            <table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0">
+                                <!-- Body content -->
+                                <tr>
+                                    <td class="content-cell">
+                                        {{ Illuminate\Mail\Markdown::parse($slot) }}
+
+                                        {{ $subcopy or '' }}
+                                    </td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+
+                    {{ $footer or '' }}
+                </table>
+            </td>
+        </tr>
+    </table>
+</body>
+</html>

+ 27 - 0
resources/views/vendor/mail/html/message.blade.php

@@ -0,0 +1,27 @@
+@component('mail::layout')
+    {{-- Header --}}
+    @slot('header')
+        @component('mail::header', ['url' => config('app.url')])
+            {{ config('app.name') }}
+        @endcomponent
+    @endslot
+
+    {{-- Body --}}
+    {{ $slot }}
+
+    {{-- Subcopy --}}
+    @isset($subcopy)
+        @slot('subcopy')
+            @component('mail::subcopy')
+                {{ $subcopy }}
+            @endcomponent
+        @endslot
+    @endisset
+
+    {{-- Footer --}}
+    @slot('footer')
+        @component('mail::footer')
+            &copy; {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
+        @endcomponent
+    @endslot
+@endcomponent

+ 13 - 0
resources/views/vendor/mail/html/panel.blade.php

@@ -0,0 +1,13 @@
+<table class="panel" width="100%" cellpadding="0" cellspacing="0">
+    <tr>
+        <td class="panel-content">
+            <table width="100%" cellpadding="0" cellspacing="0">
+                <tr>
+                    <td class="panel-item">
+                        {{ Illuminate\Mail\Markdown::parse($slot) }}
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>

+ 7 - 0
resources/views/vendor/mail/html/promotion.blade.php

@@ -0,0 +1,7 @@
+<table class="promotion" align="center" width="100%" cellpadding="0" cellspacing="0">
+    <tr>
+        <td align="center">
+            {{ Illuminate\Mail\Markdown::parse($slot) }}
+        </td>
+    </tr>
+</table>

+ 13 - 0
resources/views/vendor/mail/html/promotion/button.blade.php

@@ -0,0 +1,13 @@
+<table width="100%" border="0" cellpadding="0" cellspacing="0">
+    <tr>
+        <td align="center">
+            <table border="0" cellpadding="0" cellspacing="0">
+                <tr>
+                    <td>
+                        <a href="{{ $url }}" class="button button-green" target="_blank">{{ $slot }}</a>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>

+ 7 - 0
resources/views/vendor/mail/html/subcopy.blade.php

@@ -0,0 +1,7 @@
+<table class="subcopy" width="100%" cellpadding="0" cellspacing="0">
+    <tr>
+        <td>
+            {{ Illuminate\Mail\Markdown::parse($slot) }}
+        </td>
+    </tr>
+</table>

+ 3 - 0
resources/views/vendor/mail/html/table.blade.php

@@ -0,0 +1,3 @@
+<div class="table">
+{{ Illuminate\Mail\Markdown::parse($slot) }}
+</div>

+ 285 - 0
resources/views/vendor/mail/html/themes/default.css

@@ -0,0 +1,285 @@
+/* Base */
+
+body, body *:not(html):not(style):not(br):not(tr):not(code) {
+    font-family: Avenir, Helvetica, sans-serif;
+    box-sizing: border-box;
+}
+
+body {
+    background-color: #f5f8fa;
+    color: #74787E;
+    height: 100%;
+    hyphens: auto;
+    line-height: 1.4;
+    margin: 0;
+    -moz-hyphens: auto;
+    -ms-word-break: break-all;
+    width: 100% !important;
+    -webkit-hyphens: auto;
+    -webkit-text-size-adjust: none;
+    word-break: break-all;
+    word-break: break-word;
+}
+
+p,
+ul,
+ol,
+blockquote {
+    line-height: 1.4;
+    text-align: left;
+}
+
+a {
+    color: #3869D4;
+}
+
+a img {
+    border: none;
+}
+
+/* Typography */
+
+h1 {
+    color: #2F3133;
+    font-size: 19px;
+    font-weight: bold;
+    margin-top: 0;
+    text-align: left;
+}
+
+h2 {
+    color: #2F3133;
+    font-size: 16px;
+    font-weight: bold;
+    margin-top: 0;
+    text-align: left;
+}
+
+h3 {
+    color: #2F3133;
+    font-size: 14px;
+    font-weight: bold;
+    margin-top: 0;
+    text-align: left;
+}
+
+p {
+    color: #74787E;
+    font-size: 16px;
+    line-height: 1.5em;
+    margin-top: 0;
+    text-align: left;
+}
+
+p.sub {
+    font-size: 12px;
+}
+
+img {
+    max-width: 100%;
+}
+
+/* Layout */
+
+.wrapper {
+    background-color: #f5f8fa;
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 100%;
+}
+
+.content {
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 100%;
+}
+
+/* Header */
+
+.header {
+    padding: 25px 0;
+    text-align: center;
+}
+
+.header a {
+    color: #bbbfc3;
+    font-size: 19px;
+    font-weight: bold;
+    text-decoration: none;
+    text-shadow: 0 1px 0 white;
+}
+
+/* Body */
+
+.body {
+    background-color: #FFFFFF;
+    border-bottom: 1px solid #EDEFF2;
+    border-top: 1px solid #EDEFF2;
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 100%;
+}
+
+.inner-body {
+    background-color: #FFFFFF;
+    margin: 0 auto;
+    padding: 0;
+    width: 570px;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 570px;
+}
+
+/* Subcopy */
+
+.subcopy {
+    border-top: 1px solid #EDEFF2;
+    margin-top: 25px;
+    padding-top: 25px;
+}
+
+.subcopy p {
+    font-size: 12px;
+}
+
+/* Footer */
+
+.footer {
+    margin: 0 auto;
+    padding: 0;
+    text-align: center;
+    width: 570px;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 570px;
+}
+
+.footer p {
+    color: #AEAEAE;
+    font-size: 12px;
+    text-align: center;
+}
+
+/* Tables */
+
+.table table {
+    margin: 30px auto;
+    width: 100%;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 100%;
+}
+
+.table th {
+    border-bottom: 1px solid #EDEFF2;
+    padding-bottom: 8px;
+}
+
+.table td {
+    color: #74787E;
+    font-size: 15px;
+    line-height: 18px;
+    padding: 10px 0;
+}
+
+.content-cell {
+    padding: 35px;
+}
+
+/* Buttons */
+
+.action {
+    margin: 30px auto;
+    padding: 0;
+    text-align: center;
+    width: 100%;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 100%;
+}
+
+.button {
+    border-radius: 3px;
+    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
+    color: #FFF;
+    display: inline-block;
+    text-decoration: none;
+    -webkit-text-size-adjust: none;
+}
+
+.button-blue {
+    background-color: #3097D1;
+    border-top: 10px solid #3097D1;
+    border-right: 18px solid #3097D1;
+    border-bottom: 10px solid #3097D1;
+    border-left: 18px solid #3097D1;
+}
+
+.button-green {
+    background-color: #2ab27b;
+    border-top: 10px solid #2ab27b;
+    border-right: 18px solid #2ab27b;
+    border-bottom: 10px solid #2ab27b;
+    border-left: 18px solid #2ab27b;
+}
+
+.button-red {
+    background-color: #bf5329;
+    border-top: 10px solid #bf5329;
+    border-right: 18px solid #bf5329;
+    border-bottom: 10px solid #bf5329;
+    border-left: 18px solid #bf5329;
+}
+
+/* Panels */
+
+.panel {
+    margin: 0 0 21px;
+}
+
+.panel-content {
+    background-color: #EDEFF2;
+    padding: 16px;
+}
+
+.panel-item {
+    padding: 0;
+}
+
+.panel-item p:last-of-type {
+    margin-bottom: 0;
+    padding-bottom: 0;
+}
+
+/* Promotions */
+
+.promotion {
+    background-color: #FFFFFF;
+    border: 2px dashed #9BA2AB;
+    margin: 0;
+    margin-bottom: 25px;
+    margin-top: 25px;
+    padding: 24px;
+    width: 100%;
+    -premailer-cellpadding: 0;
+    -premailer-cellspacing: 0;
+    -premailer-width: 100%;
+}
+
+.promotion h1 {
+    text-align: center;
+}
+
+.promotion p {
+    font-size: 15px;
+    text-align: center;
+}

+ 1 - 0
resources/views/vendor/mail/markdown/button.blade.php

@@ -0,0 +1 @@
+{{ $slot }}: {{ $url }}

+ 1 - 0
resources/views/vendor/mail/markdown/footer.blade.php

@@ -0,0 +1 @@
+{{ $slot }}

+ 1 - 0
resources/views/vendor/mail/markdown/header.blade.php

@@ -0,0 +1 @@
+[{{ $slot }}]({{ $url }})

+ 9 - 0
resources/views/vendor/mail/markdown/layout.blade.php

@@ -0,0 +1,9 @@
+{!! strip_tags($header) !!}
+
+{!! strip_tags($slot) !!}
+@isset($subcopy)
+
+{!! strip_tags($subcopy) !!}
+@endisset
+
+{!! strip_tags($footer) !!}

+ 27 - 0
resources/views/vendor/mail/markdown/message.blade.php

@@ -0,0 +1,27 @@
+@component('mail::layout')
+    {{-- Header --}}
+    @slot('header')
+        @component('mail::header', ['url' => config('app.url')])
+            {{ config('app.name') }}
+        @endcomponent
+    @endslot
+
+    {{-- Body --}}
+    {{ $slot }}
+
+    {{-- Subcopy --}}
+    @isset($subcopy)
+        @slot('subcopy')
+            @component('mail::subcopy')
+                {{ $subcopy }}
+            @endcomponent
+        @endslot
+    @endisset
+
+    {{-- Footer --}}
+    @slot('footer')
+        @component('mail::footer')
+            © {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
+        @endcomponent
+    @endslot
+@endcomponent

+ 1 - 0
resources/views/vendor/mail/markdown/panel.blade.php

@@ -0,0 +1 @@
+{{ $slot }}

+ 1 - 0
resources/views/vendor/mail/markdown/promotion.blade.php

@@ -0,0 +1 @@
+{{ $slot }}

+ 1 - 0
resources/views/vendor/mail/markdown/promotion/button.blade.php

@@ -0,0 +1 @@
+[{{ $slot }}]({{ $url }})

+ 1 - 0
resources/views/vendor/mail/markdown/subcopy.blade.php

@@ -0,0 +1 @@
+{{ $slot }}

+ 1 - 0
resources/views/vendor/mail/markdown/table.blade.php

@@ -0,0 +1 @@
+{{ $slot }}

+ 58 - 0
resources/views/vendor/notifications/email.blade.php

@@ -0,0 +1,58 @@
+@component('mail::message')
+{{-- Greeting --}}
+@if (! empty($greeting))
+# {{ $greeting }}
+@else
+@if ($level == 'error')
+# Whoops!
+@else
+# Hello!
+@endif
+@endif
+
+{{-- Intro Lines --}}
+@foreach ($introLines as $line)
+{{ $line }}
+
+@endforeach
+
+{{-- Action Button --}}
+@isset($actionText)
+<?php
+    switch ($level) {
+        case 'success':
+            $color = 'green';
+            break;
+        case 'error':
+            $color = 'red';
+            break;
+        default:
+            $color = 'blue';
+    }
+?>
+@component('mail::button', ['url' => $actionUrl, 'color' => $color])
+{{ $actionText }}
+@endcomponent
+@endisset
+
+{{-- Outro Lines --}}
+@foreach ($outroLines as $line)
+{{ $line }}
+
+@endforeach
+
+{{-- Salutation --}}
+@if (! empty($salutation))
+{{ $salutation }}
+@else
+Regards,<br>{{ config('app.name') }}
+@endif
+
+{{-- Subcopy --}}
+@isset($actionText)
+@component('mail::subcopy')
+If you’re having trouble clicking the "{{ $actionText }}" button, copy and paste the URL below
+into your web browser: [{{ $actionUrl }}]({{ $actionUrl }})
+@endcomponent
+@endisset
+@endcomponent

+ 36 - 0
resources/views/vendor/pagination/bootstrap-4.blade.php

@@ -0,0 +1,36 @@
+@if ($paginator->hasPages())
+    <ul class="pagination">
+        {{-- Previous Page Link --}}
+        @if ($paginator->onFirstPage())
+            <li class="page-item disabled"><span class="page-link">&laquo;</span></li>
+        @else
+            <li class="page-item"><a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">&laquo;</a></li>
+        @endif
+
+        {{-- Pagination Elements --}}
+        @foreach ($elements as $element)
+            {{-- "Three Dots" Separator --}}
+            @if (is_string($element))
+                <li class="page-item disabled"><span class="page-link">{{ $element }}</span></li>
+            @endif
+
+            {{-- Array Of Links --}}
+            @if (is_array($element))
+                @foreach ($element as $page => $url)
+                    @if ($page == $paginator->currentPage())
+                        <li class="page-item active"><span class="page-link">{{ $page }}</span></li>
+                    @else
+                        <li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
+                    @endif
+                @endforeach
+            @endif
+        @endforeach
+
+        {{-- Next Page Link --}}
+        @if ($paginator->hasMorePages())
+            <li class="page-item"><a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">&raquo;</a></li>
+        @else
+            <li class="page-item disabled"><span class="page-link">&raquo;</span></li>
+        @endif
+    </ul>
+@endif

+ 36 - 0
resources/views/vendor/pagination/default.blade.php

@@ -0,0 +1,36 @@
+@if ($paginator->hasPages())
+    <ul class="pagination">
+        {{-- Previous Page Link --}}
+        @if ($paginator->onFirstPage())
+            <li class="disabled"><span>&laquo;</span></li>
+        @else
+            <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">&laquo;</a></li>
+        @endif
+
+        {{-- Pagination Elements --}}
+        @foreach ($elements as $element)
+            {{-- "Three Dots" Separator --}}
+            @if (is_string($element))
+                <li class="disabled"><span>{{ $element }}</span></li>
+            @endif
+
+            {{-- Array Of Links --}}
+            @if (is_array($element))
+                @foreach ($element as $page => $url)
+                    @if ($page == $paginator->currentPage())
+                        <li class="active"><span>{{ $page }}</span></li>
+                    @else
+                        <li><a href="{{ $url }}">{{ $page }}</a></li>
+                    @endif
+                @endforeach
+            @endif
+        @endforeach
+
+        {{-- Next Page Link --}}
+        @if ($paginator->hasMorePages())
+            <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">&raquo;</a></li>
+        @else
+            <li class="disabled"><span>&raquo;</span></li>
+        @endif
+    </ul>
+@endif

+ 17 - 0
resources/views/vendor/pagination/simple-bootstrap-4.blade.php

@@ -0,0 +1,17 @@
+@if ($paginator->hasPages())
+    <ul class="pagination">
+        {{-- Previous Page Link --}}
+        @if ($paginator->onFirstPage())
+            <li class="page-item disabled"><span class="page-link">@lang('pagination.previous')</span></li>
+        @else
+            <li class="page-item"><a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
+        @endif
+
+        {{-- Next Page Link --}}
+        @if ($paginator->hasMorePages())
+            <li class="page-item"><a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
+        @else
+            <li class="page-item disabled"><span class="page-link">@lang('pagination.next')</span></li>
+        @endif
+    </ul>
+@endif

+ 17 - 0
resources/views/vendor/pagination/simple-default.blade.php

@@ -0,0 +1,17 @@
+@if ($paginator->hasPages())
+    <ul class="pagination">
+        {{-- Previous Page Link --}}
+        @if ($paginator->onFirstPage())
+            <li class="disabled"><span>@lang('pagination.previous')</span></li>
+        @else
+            <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
+        @endif
+
+        {{-- Next Page Link --}}
+        @if ($paginator->hasMorePages())
+            <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
+        @else
+            <li class="disabled"><span>@lang('pagination.next')</span></li>
+        @endif
+    </ul>
+@endif