From 1c656d65569b2d50af44f49ade81b04c1c918801 Mon Sep 17 00:00:00 2001
From: Sebastian Reese <reese@dz-online.de>
Date: Thu, 2 Feb 2017 00:43:24 +0100
Subject: [PATCH 01/27] Updated and improved german translation

---
 resources/lang/de/auth.php       |  51 ++++++-
 resources/lang/de/common.php     |  58 ++++++++
 resources/lang/de/components.php |  24 ++++
 resources/lang/de/entities.php   | 225 +++++++++++++++++++++++++++++++
 resources/lang/de/errors.php     |  60 ++++++++-
 resources/lang/de/passwords.php  |   2 +-
 resources/lang/de/settings.php   |  80 ++++++++++-
 7 files changed, 493 insertions(+), 7 deletions(-)
 create mode 100644 resources/lang/de/common.php
 create mode 100644 resources/lang/de/components.php
 create mode 100644 resources/lang/de/entities.php

diff --git a/resources/lang/de/auth.php b/resources/lang/de/auth.php
index c58d9d974..f253cdfa1 100644
--- a/resources/lang/de/auth.php
+++ b/resources/lang/de/auth.php
@@ -14,7 +14,50 @@ return [
     'throttle' => 'Zu viele Anmeldeversuche. Bitte versuchen sie es in :seconds Sekunden erneut.',
 
     /**
-     * Email Confirmation Text
+     * Login & Register
+     */
+    'sign_up' => 'Registrieren',
+    'log_in' => 'Anmelden',
+    'logout' => 'Abmelden',
+
+    'name' => 'Name',
+    'username' => 'Benutzername',
+    'email' => 'E-Mail',
+    'password' => 'Passwort',
+    'password_confirm' => 'Passwort best&auml;tigen',
+    'password_hint' => 'Mindestlänge: 5 Zeichen',
+    'forgot_password' => 'Passwort vergessen?',
+    'remember_me' => 'Angemeldet bleiben',
+    'ldap_email_hint' => 'Bitte geben Sie eine E-Mail-Adresse ein, um diese mit dem Account zu nutzen.',
+    'create_account' => 'Account anlegen',
+    'social_login' => 'Social Login',
+    'social_registration' => 'Social Registrierung',
+    'social_registration_text' => 'Mit einem dieser Möglichkeiten registrieren oder anmelden.',
+
+
+    'register_thanks' => 'Vielen Dank für Ihre Registrierung!',
+    'register_confirm' => 'Bitte prüfen Sie Ihren E-Mail Eingang und klicken auf den Verifizieren-Button, um :appName nutzen zu können.',
+    'registrations_disabled' => 'Die Registrierung ist momentan nicht möglich',
+    'registration_email_domain_invalid' => 'Diese E-Mail-Domain ist für die Benutzer der Applikation nicht freigeschaltet.',
+    'register_success' => 'Vielen Dank für Ihre Registrierung! Die Daten sind gespeichert und Sie sind angemeldet.',
+
+
+    /**
+     * Password Reset
+     */
+    'reset_password' => 'Passwort vergessen',
+    'reset_password_send_instructions' => 'Bitte geben Sie unten Ihre E-Mail-Adresse ein und Sie erhalten eine E-Mail, um Ihr Passwort zurück zu setzen.',
+    'reset_password_send_button' => 'Passwort zurücksetzen',
+    'reset_password_sent_success' => 'Eine E-Mail mit den Instruktionen, um Ihr Passwort zurückzusetzen wurde an :email gesendet.',
+    'reset_password_success' => 'Ihr Passwort wurde erfolgreich zurück gesetzt.',
+
+    'email_reset_subject' => 'Passwort zurücksetzen für :appName',
+    'email_reset_text' => 'Sie erhalten diese E-Mail, weil eine Passwort-Rücksetzung für Ihren Account beantragt wurde.',
+    'email_reset_not_requested' => 'Wenn Sie die Passwort-Rücksetzung nicht ausgelöst haben, ist kein weiteres Handeln notwendig.',
+
+
+    /**
+     * Email Confirmation
      */
     'email_confirm_subject' => 'Best&auml;tigen sie ihre E-Mail Adresse bei :appName',
     'email_confirm_greeting' => 'Danke, dass sie :appName beigetreten sind!',
@@ -23,4 +66,10 @@ return [
     'email_confirm_send_error' => 'Best&auml;tigungs-E-Mail ben&ouml;tigt, aber das System konnte die E-Mail nicht versenden. Kontaktieren sie den Administrator, um sicherzustellen, dass das Sytsem korrekt eingerichtet ist.',
     'email_confirm_success' => 'Ihre E-Mail Adresse wurde best&auml;tigt!',
     'email_confirm_resent' => 'Best&auml;tigungs-E-Mail wurde erneut versendet, bitte &uuml;berpr&uuml;fen sie ihren Posteingang.',
+
+    'email_not_confirmed' => 'E-Mail-Adresse ist nicht bestätigt',
+    'email_not_confirmed_text' => 'Ihre E-Mail-Adresse ist bisher nicht bestätigt.',
+    'email_not_confirmed_click_link' => 'Bitte klicken Sie auf den Link in der E-Mail, die Sie nach der Registrierung erhalten haben.',
+    'email_not_confirmed_resend' => 'Wenn Sie die E-Mail nicht erhalten haben, können Sie die Nachricht erneut anfordern. Füllen Sie hierzu bitte das folgende Formular aus:',
+    'email_not_confirmed_resend_button' => 'Bestätigungs E-Mail erneut senden',
 ];
diff --git a/resources/lang/de/common.php b/resources/lang/de/common.php
new file mode 100644
index 000000000..7ad1743a0
--- /dev/null
+++ b/resources/lang/de/common.php
@@ -0,0 +1,58 @@
+<?php
+return [
+
+    /**
+     * Buttons
+     */
+    'cancel' => 'Abbrechen',
+    'confirm' => 'Bestätigen',
+    'back' => 'Zurück',
+    'save' => 'Speichern',
+    'continue' => 'Weiter',
+    'select' => 'Auswählen',
+
+    /**
+     * Form Labels
+     */
+    'name' => 'Name',
+    'description' => 'Beschreibung',
+    'role' => 'Rolle',
+
+    /**
+     * Actions
+     */
+    'actions' => 'Aktionen',
+    'view' => 'Anzeigen',
+    'create' => 'Anlegen',
+    'update' => 'Aktualisieren',
+    'edit' => 'Bearbeiten',
+    'sort' => 'Sortieren',
+    'move' => 'Verschieben',
+    'delete' => 'L&ouml;schen',
+    'search' => 'Suchen',
+    'search_clear' => 'Suche l&ouml;schen',
+    'reset' => 'Zurücksetzen',
+    'remove' => 'Entfernen',
+
+
+    /**
+     * Misc
+     */
+    'deleted_user' => 'Gel&ouml;schte Benutzer',
+    'no_activity' => 'Keine Aktivit&auml;ten zum Anzeigen',
+    'no_items' => 'Keine Eintr&auml;ge gefunden.',
+    'back_to_top' => 'nach oben',
+    'toggle_details' => 'Details zeigen/verstecken',
+
+    /**
+     * Header
+     */
+    'view_profile' => 'Profil ansehen',
+    'edit_profile' => 'Profil bearbeiten',
+
+    /**
+     * Email Content
+     */
+    'email_action_help' => 'Sollte es beim Anklicken des ":actionText" Buttons Probleme geben, kopieren Sie folgende URL und fügen diese in Ihrem Webbrowser ein:',
+    'email_rights' => 'Alle Rechte vorbehalten',
+];
\ No newline at end of file
diff --git a/resources/lang/de/components.php b/resources/lang/de/components.php
new file mode 100644
index 000000000..a8538c465
--- /dev/null
+++ b/resources/lang/de/components.php
@@ -0,0 +1,24 @@
+<?php
+return [
+
+    /**
+     * Image Manager
+     */
+    'image_select' => 'Bild auswählen',
+    'image_all' => 'Alle',
+    'image_all_title' => 'Alle Bilder anzeigen',
+    'image_book_title' => 'Zeige alle Bilder, die in dieses Buch hochgeladen wurden',
+    'image_page_title' => 'Zeige alle Bilder, die auf diese Seite hochgeladen wurden',
+    'image_search_hint' => 'Nach Bildnamen suchen',
+    'image_uploaded' => 'Hochgeladen am :uploadedDate',
+    'image_load_more' => 'Mehr',
+    'image_image_name' => 'Bildname',
+    'image_delete_confirm' => 'Dieses Bild wird auf den folgenden Seiten benutzt. Bitte klicken Sie erneut auf löschen, wenn Sie dieses Bild tatsächlich entfernen möchten.',
+    'image_select_image' => 'Bild auswählen',
+    'image_dropzone' => 'Ziehen Sie Bilder hier hinein oder klicken Sie hier, um ein Bild auszuwählen',
+    'images_deleted' => 'Bilder gelöscht',
+    'image_preview' => 'Bildvorschau',
+    'image_upload_success' => 'Bild erfolgreich hochgeladen',
+    'image_update_success' => 'Bilddetails erfolgreich aktualisiert',
+    'image_delete_success' => 'Bild erfolgreich gelöscht'
+];
\ No newline at end of file
diff --git a/resources/lang/de/entities.php b/resources/lang/de/entities.php
new file mode 100644
index 000000000..ff590bb88
--- /dev/null
+++ b/resources/lang/de/entities.php
@@ -0,0 +1,225 @@
+<?php
+return [
+
+    /**
+     * Shared
+     */
+    'recently_created' => 'K&uuml;rzlich angelegt',
+    'recently_created_pages' => 'K&uuml;rzlich angelegte Seiten',
+    'recently_updated_pages' => 'K&uuml;rzlich aktualisierte Seiten',
+    'recently_created_chapters' => 'K&uuml;rzlich angelegte Kapitel',
+    'recently_created_books' => 'K&uuml;rzlich angelegte B&uuml;cher',
+    'recently_update' => 'K&uuml;rzlich aktualisiert',
+    'recently_viewed' => 'K&uuml;rzlich angesehen',
+    'recent_activity' => 'K&uuml;rzliche Aktivit&auml;t',
+    'create_now' => 'Jetzt anlegen',
+    'revisions' => 'Revisionen',
+    'meta_created' => 'Angelegt am :timeLength',
+    'meta_created_name' => 'Angelegt am :timeLength durch :user',
+    'meta_updated' => 'Aktualisiert am :timeLength',
+    'meta_updated_name' => 'Aktualisiert am :timeLength durch :user',
+    'x_pages' => ':count Seiten',
+    'entity_select' => 'Eintrag ausw&auml;hlen',
+    'images' => 'Bilder',
+    'my_recent_drafts' => 'Meine k&uuml;rzlichen Entw&uuml;rfe',
+    'my_recently_viewed' => 'K&uuml;rzlich von mir angesehen',
+    'no_pages_viewed' => 'Sie haben bisher keine Seiten angesehen.',
+    'no_pages_recently_created' => 'Sie haben bisher keine Seiten angelegt.',
+    'no_pages_recently_updated' => 'Sie haben bisher keine Seiten aktualisiert.',
+
+    /**
+     * Permissions and restrictions
+     */
+    'permissions' => 'Berechtigungen',
+    'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, &uuml;berschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
+    'permissions_enable' => 'Individuelle Berechtigungen aktivieren',
+    'permissions_save' => 'Berechtigungen speichern',
+
+    /**
+     * Search
+     */
+    'search_results' => 'Suchergebnisse',
+    'search_results_page' => 'Seiten-Suchergebnisse',
+    'search_results_chapter' => 'Kapitel-Suchergebnisse',
+    'search_results_book' => 'Buch-Suchergebnisse',
+    'search_clear' => 'Suche zur&uuml;cksetzen',
+    'search_view_pages' => 'Zeige alle passenden Seiten',
+    'search_view_chapters' => 'Zeige alle passenden Kapitel',
+    'search_view_books' => 'Zeige alle passenden B&uuml;cher',
+    'search_no_pages' => 'Es wurden keine passenden Suchergebnisse gefunden',
+    'search_for_term' => 'Suche nach :term',
+    'search_page_for_term' => 'Suche nach :term in Seiten',
+    'search_chapter_for_term' => 'Suche nach :term in Kapiteln',
+    'search_book_for_term' => 'Suche nach :term in B&uuml;chern',
+
+    /**
+     * Books
+     */
+    'book' => 'Buch',
+    'books' => 'B&uuml;cher',
+    'books_empty' => 'Es wurden keine B&uuml;cher angelegt',
+    'books_popular' => 'Popul&auml;re B&uuml;cher',
+    'books_recent' => 'K&uuml;rzlich genutzte B&uuml;cher',
+    'books_popular_empty' => 'Die popul&auml;rsten B&uuml;cher werden hier angezeigt.',
+    'books_create' => 'Neues Buch anlegen',
+    'books_delete' => 'Buch l&ouml;schen',
+    'books_delete_named' => 'Buch :bookName l&ouml;schen',
+    'books_delete_explain' => 'Sie m&ouml;chten das Buch \':bookName\' l&ouml;schen und alle Seiten und Kapitel entfernen.',
+    'books_delete_confirmation' => 'Sind Sie sicher, dass Sie dieses Buch l&ouml;schen m&ouml;chten?',
+    'books_edit' => 'Buch bearbeiten',
+    'books_edit_named' => 'Buch :bookName bearbeiten',
+    'books_form_book_name' => 'Buchname',
+    'books_save' => 'Buch speichern',
+    'books_permissions' => 'Buch-Berechtigungen',
+    'books_permissions_updated' => 'Buch-Berechtigungen aktualisiert',
+    'books_empty_contents' => 'Es sind noch keine Seiten oder Kapitel f&uuml;r dieses Buch angelegt.',
+    'books_empty_create_page' => 'Neue Seite anlegen',
+    'books_empty_or' => 'oder',
+    'books_empty_sort_current_book' => 'Aktuelles Buch sortieren',
+    'books_empty_add_chapter' => 'Neues Kapitel hinzuf&uuml;gen',
+    'books_permissions_active' => 'Buch-Berechtigungen aktiv',
+    'books_search_this' => 'Dieses Buch durchsuchen',
+    'books_navigation' => 'Buch-Navigation',
+    'books_sort' => 'Buchinhalte sortieren',
+    'books_sort_named' => 'Buch :bookName sortieren',
+    'books_sort_show_other' => 'Andere B&uuml;cher zeigen',
+    'books_sort_save' => 'Neue Reihenfolge speichern',
+
+    /**
+     * Chapters
+     */
+    'chapter' => 'Kapitel',
+    'chapters' => 'Kapitel',
+    'chapters_popular' => 'Popul&auml;re Kapitel',
+    'chapters_new' => 'Neues Kapitel',
+    'chapters_create' => 'Neues Kapitel anlegen',
+    'chapters_delete' => 'Kapitel entfernen',
+    'chapters_delete_named' => 'Kapitel :chapterName entfernen',
+    'chapters_delete_explain' => 'Sie m&ouml;chten das Kapitel \':chapterName\' l&ouml;schen und alle Seiten dem direkten Eltern-Buch hinzugef&uuml;gen.',
+    'chapters_delete_confirm' => 'Sind Sie sicher, dass Sie dieses Kapitel l&ouml;schen m&ouml;chten?',
+    'chapters_edit' => 'Kapitel bearbeiten',
+    'chapters_edit_named' => 'Kapitel :chapterName bearbeiten',
+    'chapters_save' => 'Kapitel speichern',
+    'chapters_move' => 'Kapitel verschieben',
+    'chapters_move_named' => 'Kapitel :chapterName verschieben',
+    'chapter_move_success' => 'Kapitel in das Buch :bookName verschoben.',
+    'chapters_permissions' => 'Kapitel-Berechtigungen',
+    'chapters_empty' => 'Aktuell sind keine Kapitel in diesem Buch angelegt.',
+    'chapters_permissions_active' => 'Kapitel-Berechtigungen aktiv',
+    'chapters_permissions_success' => 'Kapitel-Berechtigungenen aktualisisert',
+
+    /**
+     * Pages
+     */
+    'page' => 'Seite',
+    'pages' => 'Seiten',
+    'pages_popular' => 'Popul&auml;re Seiten',
+    'pages_new' => 'Neue Seite',
+    'pages_attachments' => 'Anh&auml;nge',
+    'pages_navigation' => 'Seitennavigation',
+    'pages_delete' => 'Seite l&ouml;schen',
+    'pages_delete_named' => 'Seite :pageName l&ouml;schen',
+    'pages_delete_draft_named' => 'Seitenentwurf von :pageName l&ouml;schen',
+    'pages_delete_draft' => 'Seitenentwurf l&ouml;schen',
+    'pages_delete_success' => 'Seite gel&ouml;scht',
+    'pages_delete_draft_success' => 'Seitenentwurf gel&ouml;scht',
+    'pages_delete_confirm' => 'Sind Sie sicher, dass Sie diese Seite l&ouml;schen m&ouml;chen?',
+    'pages_delete_draft_confirm' => 'Sind Sie sicher, dass Sie diesen Seitenentwurf l&ouml;schen m&ouml;chten?',
+    'pages_editing_named' => 'Seite :pageName bearbeiten',
+    'pages_edit_toggle_header' => 'Toggle header',
+    'pages_edit_save_draft' => 'Entwurf speichern',
+    'pages_edit_draft' => 'Seitenentwurf bearbeiten',
+    'pages_editing_draft' => 'Seitenentwurf bearbeiten',
+    'pages_editing_page' => 'Seite bearbeiten',
+    'pages_edit_draft_save_at' => 'Entwurf gespeichert um ',
+    'pages_edit_delete_draft' => 'Entwurf l&ouml;schen',
+    'pages_edit_discard_draft' => 'Entwurf verwerfen',
+    'pages_edit_set_changelog' => 'Ver&auml;nderungshinweis setzen',
+    'pages_edit_enter_changelog_desc' => 'Bitte geben Sie eine kurze Zusammenfassung Ihrer &Auml;nderungen ein',
+    'pages_edit_enter_changelog' => 'Ver&auml;nderungshinweis eingeben',
+    'pages_save' => 'Seite speichern',
+    'pages_title' => 'Seitentitel',
+    'pages_name' => 'Seitenname',
+    'pages_md_editor' => 'Redakteur',
+    'pages_md_preview' => 'Vorschau',
+    'pages_md_insert_image' => 'Bild einf&uuml;gen',
+    'pages_md_insert_link' => 'Link zu einem Objekt einf&uuml;gen',
+    'pages_not_in_chapter' => 'Seite ist in keinem Kapitel',
+    'pages_move' => 'Seite verschieben',
+    'pages_move_success' => 'Seite nach ":parentName" verschoben',
+    'pages_permissions' => 'Seiten Berechtigungen',
+    'pages_permissions_success' => 'Seiten Berechtigungen aktualisiert',
+    'pages_revisions' => 'Seitenversionen',
+    'pages_revisions_named' => 'Seitenversionen von :pageName',
+    'pages_revision_named' => 'Seitenversion von :pageName',
+    'pages_revisions_created_by' => 'Angelegt von',
+    'pages_revisions_date' => 'Versionsdatum',
+    'pages_revisions_changelog' => 'Ver&auml;nderungshinweise',
+    'pages_revisions_changes' => 'Ver&auml;nderungen',
+    'pages_revisions_current' => 'Aktuelle Version',
+    'pages_revisions_preview' => 'Vorschau',
+    'pages_revisions_restore' => 'Zur&uuml;ck sichern',
+    'pages_revisions_none' => 'Diese Seite hat keine &auml;lteren Versionen.',
+    'pages_export' => 'Exportieren',
+    'pages_export_html' => 'HTML-Datei',
+    'pages_export_pdf' => 'PDF-Datei',
+    'pages_export_text' => 'Text-Datei',
+    'pages_copy_link' => 'Link kopieren',
+    'pages_permissions_active' => 'Seiten-Berechtigungen aktiv',
+    'pages_initial_revision' => 'Erste Ver&ouml;ffentlichung',
+    'pages_initial_name' => 'Neue Seite',
+    'pages_editing_draft_notification' => 'Sie bearbeiten momenten einen Entwurf, der zuletzt um :timeDiff gespeichert wurde.',
+    'pages_draft_edited_notification' => 'Diese Seite wurde seit diesem Zeitpunkt ver&auml;ndert. Wir empfehlen Ihnen, diesen Entwurf zu verwerfen.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count Benutzer haben die Bearbeitung dieser Seite begonnen.',
+        'start_b' => ':userName hat die Bearbeitung dieser Seite begonnen.',
+        'time_a' => 'seit die Seiten zuletzt aktualisiert wurden.',
+        'time_b' => 'in den letzten :minCount Minuten',
+        'message' => ':start :time. Achten Sie darauf keine Aktualisierungen von anderen Benutzern zu &uuml;berschreiben!',
+    ],
+    'pages_draft_discarded' => 'Entwurf verworfen. Der aktuelle Seiteninhalt wurde geladen.',
+
+    /**
+     * Editor sidebar
+     */
+    'page_tags' => 'Seiten-Schlagw&ouml;rter',
+    'tag' => 'Schlagwort',
+    'tags' =>  'Schlagworte',
+    'tag_value' => 'Schlagwortinhalt (Optional)',
+    'tags_explain' => "F&uuml;gen Sie Schlagworte hinzu, um Ihren Inhalt zu kategorisieren. \n Sie k&ouml;nnen einen erkl&auml;renden Inhalt hinzuf&uuml;gen, um eine genauere Unterteilung vorzunehmen.",
+    'tags_add' => 'Weiteres Schlagwort hinzuf&uuml;gen',
+    'attachments' => 'Anh&auml;nge',
+    'attachments_explain' => 'Sie k&ouml;nnen auf Ihrer Seite Dateien hochladen oder Links anf&uuml;gen. Diese werden in der seitlich angezeigt.',
+    'attachments_explain_instant_save' => '&Auml;nderungen werden direkt gespeichert.',
+    'attachments_items' => 'Angef&uuml;gte Elemente',
+    'attachments_upload' => 'Datei hochladen',
+    'attachments_link' => 'Link anf&uuml;gen',
+    'attachments_set_link' => 'Link setzen',
+    'attachments_delete_confirm' => 'Klicken Sie erneut auf l&ouml;schen, um diesen Anhang zu entfernen.',
+    'attachments_dropzone' => 'Ziehen Sie Dateien hier hinein oder klicken Sie hier, um eine Datei auszuw&auml;hlen',
+    'attachments_no_files' => 'Es wurden bisher keine Dateien hochgeladen.',
+    'attachments_explain_link' => 'Wenn Sie keine Datei hochladen m&ouml;chten, k&ouml;nnen Sie stattdessen einen Link anf&uuml;gen. Dieser Link kann auf eine andere Seite oder zu einer Datei in der Cloud weisen.',
+    'attachments_link_name' => 'Link-Name',
+    'attachment_link' => 'Link zum Anhang',
+    'attachments_link_url' => 'Link zu einer Datei',
+    'attachments_link_url_hint' => 'URL einer Seite oder Datei',
+    'attach' => 'anf&uuml;gen',
+    'attachments_edit_file' => 'Datei bearbeiten',
+    'attachments_edit_file_name' => 'Dateiname',
+    'attachments_edit_drop_upload' => 'Ziehen Sie Dateien hier hinein, um diese hochzuladen und zu &uuml;berschreiben',
+    'attachments_order_updated' => 'Reihenfolge der Anh&auml;nge aktualisiert',
+    'attachments_updated_success' => 'Anhang-Details aktualisiert',
+    'attachments_deleted' => 'Anhang gel&ouml;scht',
+    'attachments_file_uploaded' => 'Datei erfolgrecich hochgeladen',
+    'attachments_file_updated' => 'Datei erfolgreich aktualisisert',
+    'attachments_link_attached' => 'Link erfolgreich der Seite hinzugef&uuml;gt',
+
+    /**
+     * Profile View
+     */
+    'profile_user_for_x' => 'Benutzer seit :time',
+    'profile_created_content' => 'Angelegte Inhalte',
+    'profile_not_created_pages' => ':userName hat bisher keine Seiten angelegt.',
+    'profile_not_created_chapters' => ':userName hat bisher keine Kapitel angelegt.',
+    'profile_not_created_books' => ':userName hat bisher keine B&uuml;cher angelegt.',
+];
\ No newline at end of file
diff --git a/resources/lang/de/errors.php b/resources/lang/de/errors.php
index 697952086..e085d9915 100644
--- a/resources/lang/de/errors.php
+++ b/resources/lang/de/errors.php
@@ -8,5 +8,63 @@ return [
 
     // Pages
     'permission' => 'Sie haben keine Berechtigung auf diese Seite zuzugreifen.',
-    'permissionJson' => 'Sie haben keine Berechtigung die angeforderte Aktion auszuf&uuml;hren.'
+    'permissionJson' => 'Sie haben keine Berechtigung die angeforderte Aktion auszuf&uuml;hren.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Ein Benutzer mit der E-Mail-Adresse :email ist bereits mit anderen Anmeldedaten angelegt.',
+    'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits best&auml;tigt. Bitte melden Sie sich an.',
+    'email_confirmation_invalid' => 'Der Best&auml;tigungs-Token ist nicht g&uuml;ltig oder wurde bereits verwendet. Bitte registrieren Sie sich erneut.',
+    'email_confirmation_expired' => 'Der Best&auml;tigungs-Token ist abgelaufen. Es wurde eine neue Best&auml;tigungs-E-Mail gesendet.',
+    'ldap_fail_anonymous' => 'Anonymer LDAP Zugriff ist fehlgeschlafgen',
+    'ldap_fail_authed' => 'LDAP Zugriff mit DN & Passwort ist fehlgeschlagen',
+    'ldap_extension_not_installed' => 'LDAP PHP Erweiterung ist nicht installiert.',
+    'ldap_cannot_connect' => 'Die Verbindung zu LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
+    'social_no_action_defined' => 'Es ist keine Aktion definiert',
+    'social_account_in_use' => 'Dieses :socialAccount Konto wird bereits verwendet. Bitte melden Sie sich mit dem :socialAccount Konto an.',
+    'social_account_email_in_use' => 'Die E-Mail-Adresse :email ist bereits registriert. Wenn Sie bereits registriert sind, k&ouml;nnen Sie Ihr :socialAccount Konto in Ihren Profil-Einstellungen verkn&uuml;pfen.',
+    'social_account_existing' => 'Dieses :socialAccount Konto ist bereits mit Ihrem Profil verkn&uuml;pft.',
+    'social_account_already_used_existing' => 'Dieses :socialAccount Konto wird bereits durch einen anderen Benutzer verwendet.',
+    'social_account_not_used' => 'Dieses :socialAccount Konto ist bisher keinem Benutzer zugeordnet. Bitte verkn&uuml;pfen Sie deses in Ihrem Profil-Einstellungen.',
+    'social_account_register_instructions' => 'Wenn Sie bisher keinen Social-Media Konto besitzen k&ouml;nnen Sie ein solches Konto mit der :socialAccount Option anlegen.',
+    'social_driver_not_found' => 'Social-Media Konto Treiber nicht gefunden',
+    'social_driver_not_configured' => 'Ihr :socialAccount Konto ist nicht korrekt konfiguriert.',
+
+    // System
+    'path_not_writable' => 'Die Datei kann nicht in den angegebenen Pfad :filePath hochgeladen werden. Stellen Sie sicher, dass dieser Ordner auf dem Server beschreibbar ist.',
+    'cannot_get_image_from_url' => 'Bild konnte nicht von der URL :url geladen werden.',
+    'cannot_create_thumbs' => 'Der Server kann keine Vorschau-Bilder erzeugen. Bitte pr&uuml;fen Sie, ob Sie die GD PHP Erweiterung installiert haben.',
+    'server_upload_limit' => 'Der Server verbietet das Hochladen von Dateien mit dieser Dateigr&ouml;&szlig;e. Bitte versuchen Sie es mit einer kleineren Datei.',
+    'image_upload_error' => 'Beim Hochladen des Bildes trat ein Fehler auf.',
+
+    // Attachments
+    'attachment_page_mismatch' => 'Die Seite stimmt nach dem Hochladen des Anhangs nicht &uuml;berein.',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Fehler beim Speichern des Entwurfs. Stellen Sie sicher, dass Sie mit dem Internet verbunden sind, bevor Sie den Entwurf dieser Seite speichern.',
+
+    // Entities
+    'entity_not_found' => 'Eintrag nicht gefunden',
+    'book_not_found' => 'Buch nicht gefunden',
+    'page_not_found' => 'Seite nicht gefunden',
+    'chapter_not_found' => 'Kapitel nicht gefunden',
+    'selected_book_not_found' => 'Das gew&auml;hlte Buch wurde nicht gefunden.',
+    'selected_book_chapter_not_found' => 'Das gew&auml;hlte Buch oder Kapitel wurde nicht gefunden.',
+    'guests_cannot_save_drafts' => 'G&auml;ste k&ouml;nnen keine Entw&uuml;rfe speichern',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Sie k&ouml;nnen den einzigen Administrator nicht l&ouml;schen.',
+    'users_cannot_delete_guest' => 'Sie k&ouml;nnen den Gast-Benutzer nicht l&ouml;schen',
+
+    // Roles
+    'role_cannot_be_edited' => 'Diese Rolle kann nicht bearbeitet werden.',
+    'role_system_cannot_be_deleted' => 'Dies ist eine Systemrolle und kann nicht gel&ouml;scht werden',
+    'role_registration_default_cannot_delete' => 'Diese Rolle kann nicht gel&ouml;scht werden solange sie als Standardrolle f&uuml;r neue Registrierungen gesetzt ist',
+
+    // Error pages
+    '404_page_not_found' => 'Seite nicht gefunden',
+    'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Sie angefordert haben wurde nicht gefunden.',
+    'return_home' => 'Zur&uuml;ck zur Startseite',
+    'error_occurred' => 'Es ist ein Fehler aufgetreten',
+    'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
+    'back_soon' => 'Wir werden so schnell wie m&ouml;glich wieder online sein.',
 ];
diff --git a/resources/lang/de/passwords.php b/resources/lang/de/passwords.php
index f71358055..c44b49baa 100644
--- a/resources/lang/de/passwords.php
+++ b/resources/lang/de/passwords.php
@@ -16,7 +16,7 @@ return [
     'password' => 'Pass&ouml;rter m&uuml;ssen mindestens sechs Zeichen enthalten und die Wiederholung muss identisch sein.',
     'user' => "Wir k&ouml;nnen keinen Benutzer mit dieser E-Mail Adresse finden.",
     'token' => 'Dieser Passwort-Reset-Token ist ung&uuml;ltig.',
-    'sent' => 'Wir haben ihnen eine E-Mail mit einem Link zum Zurücksetzen des Passworts zugesendet!',
+    'sent' => 'Wir haben Ihnen eine E-Mail mit einem Link zum Zur&uuml;cksetzen des Passworts zugesendet!',
     'reset' => 'Ihr Passwort wurde zur&uuml;ckgesetzt!',
 
 ];
diff --git a/resources/lang/de/settings.php b/resources/lang/de/settings.php
index 0017acd1d..668eecf33 100644
--- a/resources/lang/de/settings.php
+++ b/resources/lang/de/settings.php
@@ -10,14 +10,19 @@ return [
 
     'settings' => 'Einstellungen',
     'settings_save' => 'Einstellungen speichern',
+    'settings_save_success' => 'Einstellungen gespeichert',
+
+    /**
+     * App settings
+     */
 
     'app_settings' => 'Anwendungseinstellungen',
     'app_name' => 'Anwendungsname',
-    'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.',
+    'app_name_desc' => 'Dieser Name wird im Header und in E-Mails angezeigt.',
     'app_name_header' => 'Anwendungsname im Header anzeigen?',
     'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?',
     'app_secure_images' => 'Erh&ouml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
-    'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.',
+    'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugriff zu verhindern.',
     'app_editor' => 'Seiteneditor',
     'app_editor_desc' => 'W&auml;hlen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
     'app_custom_html' => 'Benutzerdefinierter HTML <head> Inhalt',
@@ -25,15 +30,82 @@ return [
     'app_logo' => 'Anwendungslogo',
     'app_logo_desc' => 'Dieses Bild sollte 43px hoch sein. <br>Gr&ouml;&szlig;ere Bilder werden verkleinert.',
     'app_primary_color' => 'Prim&auml;re Anwendungsfarbe',
-    'app_primary_color_desc' => 'Dies sollte ein HEX Wert sein. <br>Leer lassen des Feldes setzt auf die Standard-Anwendungsfarbe zur&uuml;ck.',
+    'app_primary_color_desc' => 'Dies sollte ein HEX Wert sein. <br>Wenn Sie nicht eingeben, wird die Anwendung auf die Standardfarbe zur&uuml;ckgesetzt.',
+
+    /**
+     * Registration settings
+     */
 
     'reg_settings' => 'Registrierungseinstellungen',
     'reg_allow' => 'Registrierung erlauben?',
     'reg_default_role' => 'Standard-Benutzerrolle nach Registrierung',
     'reg_confirm_email' => 'Best&auml;tigung per E-Mail erforderlich?',
-    'reg_confirm_email_desc' => 'Falls die Einschr&auml;nkung f&uumlr; Domains genutzt wird, ist die Best&auml;tigung per E-Mail zwingend erforderlich und der untenstehende Wert wird ignoriert.',
+    'reg_confirm_email_desc' => 'Falls die Einschr&auml;nkung f&uuml;r Domains genutzt wird, ist die Best&auml;tigung per E-Mail zwingend erforderlich und der untenstehende Wert wird ignoriert.',
     'reg_confirm_restrict_domain' => 'Registrierung auf bestimmte Domains einschr&auml;nken',
     'reg_confirm_restrict_domain_desc' => 'F&uuml;gen sie eine, durch Komma getrennte, Liste von E-Mail Domains hinzu, auf die die Registrierung eingeschr&auml;nkt werden soll. Benutzern wird eine E-Mail gesendet, um ihre E-Mail Adresse zu best&auml;tigen, bevor sie diese Anwendung nutzen k&ouml;nnen. <br> Hinweis: Benutzer k&ouml;nnen ihre E-Mail Adresse nach erfolgreicher Registrierung &auml;ndern.',
     'reg_confirm_restrict_domain_placeholder' => 'Keine Einschr&auml;nkung gesetzt',
 
+    /**
+     * Role settings
+     */
+
+    'roles' => 'Rollen',
+    'role_user_roles' => 'Benutzer-Rollen',
+    'role_create' => 'Neue Rolle anlegen',
+    'role_create_success' => 'Rolle erfolgreich angelegt',
+    'role_delete' => 'Rolle l&ouml;schen',
+    'role_delete_confirm' => 'Sie m&ouml;chten die Rolle \':roleName\' l&ouml;schen.',
+    'role_delete_users_assigned' => 'Diese Rolle ist :userCount Benutzern zugeordnet. Sie k&ouml;nnen unten eine neue Rolle ausw&auml;hlen, die Sie diesen Benutzern zuordnen m&ouml;chten.',
+    'role_delete_no_migration' => "Den Benutzern keine andere Rolle zuordnen",
+    'role_delete_sure' => 'Sind Sie sicher, dass Sie diese Rolle l&ouml;schen m&ouml;chten?',
+    'role_delete_success' => 'Rolle erfolgreich gel&ouml;scht',
+    'role_edit' => 'Rolle bearbeiten',
+    'role_details' => 'Rollen-Details',
+    'role_name' => 'Rollenname',
+    'role_desc' => 'Kurzbeschreibung der Rolle',
+    'role_system' => 'System-Berechtigungen',
+    'role_manage_users' => 'Benutzer verwalten',
+    'role_manage_roles' => 'Rollen & Rollen-Berechtigungen verwalten',
+    'role_manage_entity_permissions' => 'Alle Buch-, Kapitel und Seiten-Berechtigungen verwalten',
+    'role_manage_own_entity_permissions' => 'Nur Berechtigungen eigener B&uuml;cher, Kapitel und Seiten verwalten',
+    'role_manage_settings' => 'Globaleinstellungen verwalrten',
+    'role_asset' => 'Berechtigungen',
+    'role_asset_desc' => 'Diese Berechtigungen gelten f&uuml;r den Standard-Zugriff innerhalb des Systems. Berechtigungen f&uuml;r B&uuml;cher, Kapitel und Seiten &uuml;berschreiben diese Berechtigungenen.',
+    'role_all' => 'Alle',
+    'role_own' => 'Eigene',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Rolle speichern',
+    'role_update_success' => 'Rolle erfolgreich gespeichert',
+    'role_users' => 'Dieser Rolle zugeordnete Benutzer',
+    'role_users_none' => 'Bisher sind dieser Rolle keiner Benutzer zugeordnet,',
+
+    /**
+     * Users
+     */
+
+    'users' => 'Benutzer',
+    'user_profile' => 'Benutzerprofil',
+    'users_add_new' => 'Benutzer hinzuf&uuml;gen',
+    'users_search' => 'Benutzer suchen',
+    'users_role' => 'Benutzerrollen',
+    'users_external_auth_id' => 'Externe Authentifizierungs-ID',
+    'users_password_warning' => 'F&uuml;llen Sie die folgenden Felder nur aus, wenn Sie Ihr Passwort &auml;ndern m&ouml;chten:',
+    'users_system_public' => 'Dieser Benutzer repr&auml;sentiert alle Gast-Benutzer, die diese Seite betrachten. Er kann nicht zum Anmelden benutzt werden, sondern wird automatisch zugeordnet.',
+    'users_delete' => 'Benutzer l&ouml;schen',
+    'users_delete_named' => 'Benutzer :userName l&ouml;schen',
+    'users_delete_warning' => 'Sie m&ouml;chten den Benutzer \':userName\' g&auml;nzlich aus dem System l&ouml;schen.',
+    'users_delete_confirm' => 'Sind Sie sicher, dass Sie diesen Benutzer l&ouml;schen m&ouml;chten?',
+    'users_delete_success' => 'Benutzer erfolgreich gel&ouml;scht.',
+    'users_edit' => 'Benutzer bearbeiten',
+    'users_edit_profile' => 'Profil bearbeiten',
+    'users_edit_success' => 'Benutzer erfolgreich aktualisisert',
+    'users_avatar' => 'Benutzer-Bild',
+    'users_avatar_desc' => 'Dieses Bild sollte einen Durchmesser von ca. 256px haben.',
+    'users_preferred_language' => 'Bevorzugte Sprache',
+    'users_social_accounts' => 'Social-Media Konten',
+    'users_social_accounts_info' => 'Hier k&ouml;nnen Sie andere Social-Media Konten f&uuml;r eine schnellere und einfachere Anmeldung verkn&uuml;pfen. Wenn Sie ein Social-Media Konto hier l&ouml;sen, bleibt der Zugriff erhalteb. Entfernen Sie in diesem Falle die Berechtigung in Ihren Profil-Einstellungen des verkn&uuml;pften Social-Media Kontos.',
+    'users_social_connect' => 'Social-Media Konto verkn&uuml;pfen',
+    'users_social_disconnect' => 'Social-Media Kontoverkn&uuml;pfung l&ouml;sen',
+    'users_social_connected' => ':socialAccount Konto wurde erfolgreich mit dem Profil verkn&uuml;pft.',
+    'users_social_disconnected' => ':socialAccount Konto wurde erfolgreich vom Profil gel&ouml;st.',
 ];

From 33a2999a57854495fcea8c1acf1b75b04a28a0bf Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 4 Feb 2017 11:58:42 +0000
Subject: [PATCH 02/27] Namespaced tests to align with new laravel default

---
 composer.json                          |  7 +++---
 tests/ActivityTrackingTest.php         |  2 +-
 tests/AttachmentTest.php               |  3 +--
 tests/Auth/AuthTest.php                |  7 +++---
 tests/Auth/LdapTest.php                | 10 ++++----
 tests/Auth/SocialAuthTest.php          | 18 +++++++-------
 tests/BrowserKitTest.php               | 21 ++++++++--------
 tests/CreatesApplication.php           | 18 ++++++++++++++
 tests/Entity/EntitySearchTest.php      |  2 +-
 tests/Entity/EntityTest.php            |  2 +-
 tests/Entity/MarkdownTest.php          |  2 +-
 tests/Entity/PageContentTest.php       |  2 +-
 tests/Entity/PageDraftTest.php         |  2 +-
 tests/Entity/SortTest.php              |  2 +-
 tests/Entity/TagTest.php               |  2 +-
 tests/ImageTest.php                    |  2 +-
 tests/Permissions/RestrictionsTest.php |  2 +-
 tests/Permissions/RolesTest.php        |  2 +-
 tests/PublicActionTest.php             |  2 +-
 tests/TestCase.php                     | 33 ++++----------------------
 tests/UserProfileTest.php              | 10 ++++----
 21 files changed, 71 insertions(+), 80 deletions(-)
 create mode 100644 tests/CreatesApplication.php

diff --git a/composer.json b/composer.json
index 959bdbdc2..5755afffe 100644
--- a/composer.json
+++ b/composer.json
@@ -36,10 +36,9 @@
         }
     },
     "autoload-dev": {
-        "classmap": [
-            "tests/TestCase.php",
-            "tests/BrowserKitTest.php"
-        ]
+        "psr-4": {
+            "Tests\\": "tests/"
+        }
     },
     "scripts": {
         "post-root-package-install": [
diff --git a/tests/ActivityTrackingTest.php b/tests/ActivityTrackingTest.php
index 137317996..bb65fbcf4 100644
--- a/tests/ActivityTrackingTest.php
+++ b/tests/ActivityTrackingTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 
 class ActivityTrackingTest extends BrowserKitTest
diff --git a/tests/AttachmentTest.php b/tests/AttachmentTest.php
index f99051a72..a17f003db 100644
--- a/tests/AttachmentTest.php
+++ b/tests/AttachmentTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class AttachmentTest extends BrowserKitTest
 {
@@ -75,7 +75,6 @@ class AttachmentTest extends BrowserKitTest
     {
         $page = \BookStack\Page::first();
         $this->asAdmin();
-        $admin = $this->getAdmin();
         $fileName = 'upload_test_file.txt';
 
         $this->uploadFile($fileName, $page->id);
diff --git a/tests/Auth/AuthTest.php b/tests/Auth/AuthTest.php
index f75958838..4de35faff 100644
--- a/tests/Auth/AuthTest.php
+++ b/tests/Auth/AuthTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 use BookStack\Notifications\ConfirmEmail;
 use Illuminate\Support\Facades\Notification;
@@ -88,7 +88,7 @@ class AuthTest extends BrowserKitTest
             ->press('Resend Confirmation Email');
 
         // Get confirmation and confirm notification matches
-        $emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
+        $emailConfirmation = \DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
         Notification::assertSentTo($dbUser, ConfirmEmail::class, function($notification, $channels) use ($emailConfirmation) {
             return $notification->token === $emailConfirmation->token;
         });
@@ -177,7 +177,7 @@ class AuthTest extends BrowserKitTest
             ->seePageIs('/settings/users');
 
             $userPassword = \BookStack\User::find($user->id)->password;
-            $this->assertTrue(Hash::check('newpassword', $userPassword));
+            $this->assertTrue(\Hash::check('newpassword', $userPassword));
     }
 
     public function test_user_deletion()
@@ -238,7 +238,6 @@ class AuthTest extends BrowserKitTest
         Notification::assertSentTo($user, \BookStack\Notifications\ResetPassword::class);
         $n = Notification::sent($user, \BookStack\Notifications\ResetPassword::class);
 
-        $reset = DB::table('password_resets')->where('email', '=', 'admin@admin.com')->first();
         $this->visit('/password/reset/' . $n->first()->token)
             ->see('Reset Password')
             ->submitForm('Reset Password', [
diff --git a/tests/Auth/LdapTest.php b/tests/Auth/LdapTest.php
index 80d0c9fe2..681ead91c 100644
--- a/tests/Auth/LdapTest.php
+++ b/tests/Auth/LdapTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 use BookStack\User;
 
 class LdapTest extends BrowserKitTest
@@ -12,7 +12,7 @@ class LdapTest extends BrowserKitTest
     {
         parent::setUp();
         app('config')->set(['auth.method' => 'ldap', 'services.ldap.base_dn' => 'dc=ldap,dc=local', 'auth.providers.users.driver' => 'ldap']);
-        $this->mockLdap = Mockery::mock(BookStack\Services\Ldap::class);
+        $this->mockLdap = \Mockery::mock(\BookStack\Services\Ldap::class);
         $this->app['BookStack\Services\Ldap'] = $this->mockLdap;
         $this->mockUser = factory(User::class)->make();
     }
@@ -22,7 +22,7 @@ class LdapTest extends BrowserKitTest
         $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
         $this->mockLdap->shouldReceive('setVersion')->once();
         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4)
-            ->with($this->resourceId, config('services.ldap.base_dn'), Mockery::type('string'), Mockery::type('array'))
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
@@ -50,7 +50,7 @@ class LdapTest extends BrowserKitTest
         $this->mockLdap->shouldReceive('setVersion')->once();
         $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
-            ->with($this->resourceId, config('services.ldap.base_dn'), Mockery::type('string'), Mockery::type('array'))
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'cn' => [$this->mockUser->name],
                 'dn' => $ldapDn,
@@ -73,7 +73,7 @@ class LdapTest extends BrowserKitTest
         $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
         $this->mockLdap->shouldReceive('setVersion')->once();
         $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
-            ->with($this->resourceId, config('services.ldap.base_dn'), Mockery::type('string'), Mockery::type('array'))
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 1, 0 => [
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
diff --git a/tests/Auth/SocialAuthTest.php b/tests/Auth/SocialAuthTest.php
index aff86a81e..e3494d073 100644
--- a/tests/Auth/SocialAuthTest.php
+++ b/tests/Auth/SocialAuthTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class SocialAuthTest extends BrowserKitTest
 {
@@ -11,10 +11,10 @@ class SocialAuthTest extends BrowserKitTest
         $this->setSettings(['registration-enabled' => 'true']);
         config(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
 
-        $mockSocialite = Mockery::mock('Laravel\Socialite\Contracts\Factory');
+        $mockSocialite = \Mockery::mock('Laravel\Socialite\Contracts\Factory');
         $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
-        $mockSocialDriver = Mockery::mock('Laravel\Socialite\Contracts\Provider');
-        $mockSocialUser = Mockery::mock('\Laravel\Socialite\Contracts\User');
+        $mockSocialDriver = \Mockery::mock('Laravel\Socialite\Contracts\Provider');
+        $mockSocialUser = \Mockery::mock('\Laravel\Socialite\Contracts\User');
 
         $mockSocialite->shouldReceive('driver')->twice()->with('google')->andReturn($mockSocialDriver);
         $mockSocialDriver->shouldReceive('redirect')->once()->andReturn(redirect('/'));
@@ -34,18 +34,16 @@ class SocialAuthTest extends BrowserKitTest
 
     public function test_social_login()
     {
-        $user = factory(\BookStack\User::class)->make();
-
         config([
             'GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc',
             'GITHUB_APP_ID' => 'abc123', 'GITHUB_APP_SECRET' => '123abc',
             'APP_URL' => 'http://localhost'
         ]);
 
-        $mockSocialite = Mockery::mock('Laravel\Socialite\Contracts\Factory');
+        $mockSocialite = \Mockery::mock('Laravel\Socialite\Contracts\Factory');
         $this->app['Laravel\Socialite\Contracts\Factory'] = $mockSocialite;
-        $mockSocialDriver = Mockery::mock('Laravel\Socialite\Contracts\Provider');
-        $mockSocialUser = Mockery::mock('\Laravel\Socialite\Contracts\User');
+        $mockSocialDriver = \Mockery::mock('Laravel\Socialite\Contracts\Provider');
+        $mockSocialUser = \Mockery::mock('\Laravel\Socialite\Contracts\User');
 
         $mockSocialUser->shouldReceive('getId')->twice()->andReturn('logintest123');
 
@@ -68,7 +66,7 @@ class SocialAuthTest extends BrowserKitTest
         ->seePageIs('/login');
 
         // Test social callback with matching social account
-        DB::table('social_accounts')->insert([
+        \DB::table('social_accounts')->insert([
             'user_id' => $this->getAdmin()->id,
             'driver' => 'github',
             'driver_id' => 'logintest123'
diff --git a/tests/BrowserKitTest.php b/tests/BrowserKitTest.php
index 0a97c1292..674aef984 100644
--- a/tests/BrowserKitTest.php
+++ b/tests/BrowserKitTest.php
@@ -1,9 +1,12 @@
-<?php
+<?php namespace Tests;
 
+use BookStack\Role;
+use Illuminate\Contracts\Console\Kernel;
 use Illuminate\Foundation\Testing\DatabaseTransactions;
+use Laravel\BrowserKitTesting\TestCase;
 use Symfony\Component\DomCrawler\Crawler;
 
-abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
+abstract class BrowserKitTest extends TestCase
 {
 
     use DatabaseTransactions;
@@ -28,7 +31,7 @@ abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
     {
         $app = require __DIR__.'/../bootstrap/app.php';
 
-        $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
+        $app->make(Kernel::class)->bootstrap();
 
         return $app;
     }
@@ -48,7 +51,7 @@ abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
      */
     public function getAdmin() {
         if($this->admin === null) {
-            $adminRole = \BookStack\Role::getRole('admin');
+            $adminRole = Role::getRole('admin');
             $this->admin = $adminRole->users->first();
         }
         return $this->admin;
@@ -95,9 +98,9 @@ abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
     protected function createEntityChainBelongingToUser($creatorUser, $updaterUser = false)
     {
         if ($updaterUser === false) $updaterUser = $creatorUser;
-        $book = factory(BookStack\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
-        $chapter = factory(BookStack\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
-        $page = factory(BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
+        $book = factory(\BookStack\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
+        $chapter = factory(\BookStack\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
+        $page = factory(\BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
         $book->chapters()->saveMany([$chapter]);
         $chapter->pages()->saveMany([$page]);
         $restrictionService = $this->app[\BookStack\Services\PermissionService::class];
@@ -117,7 +120,7 @@ abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
     protected function getEditor($attributes = [])
     {
         $user = factory(\BookStack\User::class)->create($attributes);
-        $role = \BookStack\Role::getRole('editor');
+        $role = Role::getRole('editor');
         $user->attachRole($role);;
         return $user;
     }
@@ -211,7 +214,6 @@ abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
     /**
      * Check if the page contains the given element.
      * @param  string  $selector
-     * @return bool
      */
     protected function pageHasElement($selector)
     {
@@ -223,7 +225,6 @@ abstract class BrowserKitTest extends \Laravel\BrowserKitTesting\TestCase
     /**
      * Check if the page contains the given element.
      * @param  string  $selector
-     * @return bool
      */
     protected function pageNotHasElement($selector)
     {
diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php
new file mode 100644
index 000000000..42a5da2d1
--- /dev/null
+++ b/tests/CreatesApplication.php
@@ -0,0 +1,18 @@
+<?php namespace Tests;
+
+use Illuminate\Contracts\Console\Kernel;
+
+trait CreatesApplication
+{
+    /**
+     * Creates the application.
+     *
+     * @return \Illuminate\Foundation\Application
+     */
+    public function createApplication()
+    {
+        $app = require __DIR__.'/../bootstrap/app.php';
+        $app->make(Kernel::class)->bootstrap();
+        return $app;
+    }
+}
\ No newline at end of file
diff --git a/tests/Entity/EntitySearchTest.php b/tests/Entity/EntitySearchTest.php
index 23351e546..4ef8d46fb 100644
--- a/tests/Entity/EntitySearchTest.php
+++ b/tests/Entity/EntitySearchTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class EntitySearchTest extends BrowserKitTest
 {
diff --git a/tests/Entity/EntityTest.php b/tests/Entity/EntityTest.php
index 1b115ad65..5fa331737 100644
--- a/tests/Entity/EntityTest.php
+++ b/tests/Entity/EntityTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class EntityTest extends BrowserKitTest
 {
diff --git a/tests/Entity/MarkdownTest.php b/tests/Entity/MarkdownTest.php
index 03f3aa12f..b85e92565 100644
--- a/tests/Entity/MarkdownTest.php
+++ b/tests/Entity/MarkdownTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class MarkdownTest extends BrowserKitTest
 {
diff --git a/tests/Entity/PageContentTest.php b/tests/Entity/PageContentTest.php
index 3a005bc67..4f65d3406 100644
--- a/tests/Entity/PageContentTest.php
+++ b/tests/Entity/PageContentTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class PageContentTest extends BrowserKitTest
 {
diff --git a/tests/Entity/PageDraftTest.php b/tests/Entity/PageDraftTest.php
index de875c3f3..b8ac9d3ae 100644
--- a/tests/Entity/PageDraftTest.php
+++ b/tests/Entity/PageDraftTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 
 class PageDraftTest extends BrowserKitTest
diff --git a/tests/Entity/SortTest.php b/tests/Entity/SortTest.php
index 3c6400d58..d61b3f56f 100644
--- a/tests/Entity/SortTest.php
+++ b/tests/Entity/SortTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class SortTest extends BrowserKitTest
 {
diff --git a/tests/Entity/TagTest.php b/tests/Entity/TagTest.php
index f4ce581e3..257c20789 100644
--- a/tests/Entity/TagTest.php
+++ b/tests/Entity/TagTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 use BookStack\Tag;
 use BookStack\Page;
diff --git a/tests/ImageTest.php b/tests/ImageTest.php
index debde848c..3bb41138b 100644
--- a/tests/ImageTest.php
+++ b/tests/ImageTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class ImageTest extends BrowserKitTest
 {
diff --git a/tests/Permissions/RestrictionsTest.php b/tests/Permissions/RestrictionsTest.php
index e874fccae..7007985e4 100644
--- a/tests/Permissions/RestrictionsTest.php
+++ b/tests/Permissions/RestrictionsTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class RestrictionsTest extends BrowserKitTest
 {
diff --git a/tests/Permissions/RolesTest.php b/tests/Permissions/RolesTest.php
index a4b502915..24b8ae0f5 100644
--- a/tests/Permissions/RolesTest.php
+++ b/tests/Permissions/RolesTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class RolesTest extends BrowserKitTest
 {
diff --git a/tests/PublicActionTest.php b/tests/PublicActionTest.php
index 6127e1cd4..422511dbf 100644
--- a/tests/PublicActionTest.php
+++ b/tests/PublicActionTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class PublicActionTest extends BrowserKitTest
 {
diff --git a/tests/TestCase.php b/tests/TestCase.php
index e0455f447..6b97682e8 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -1,31 +1,8 @@
-<?php
+<?php namespace Tests;
 
-use Illuminate\Foundation\Testing\DatabaseTransactions;
-use Symfony\Component\DomCrawler\Crawler;
+use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 
-class TestCase extends Illuminate\Foundation\Testing\TestCase
+abstract class TestCase extends BaseTestCase
 {
-
-    use DatabaseTransactions;
-
-    /**
-     * The base URL of the application.
-     *
-     * @var string
-     */
-    public $baseUrl = 'http://localhost';
-
-    /**
-     * Creates the application.
-     *
-     * @return \Illuminate\Foundation\Application
-     */
-    public function createApplication()
-    {
-        $app = require __DIR__.'/../bootstrap/app.php';
-
-        $app->make(Kernel::class)->bootstrap();
-
-        return $app;
-    }
-}
+    use CreatesApplication;
+}
\ No newline at end of file
diff --git a/tests/UserProfileTest.php b/tests/UserProfileTest.php
index a448e3a9f..12f88483f 100644
--- a/tests/UserProfileTest.php
+++ b/tests/UserProfileTest.php
@@ -1,4 +1,4 @@
-<?php
+<?php namespace Tests;
 
 class UserProfileTest extends BrowserKitTest
 {
@@ -55,8 +55,8 @@ class UserProfileTest extends BrowserKitTest
         $newUser = $this->getEditor();
         $this->actingAs($newUser);
         $entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
-        Activity::add($entities['book'], 'book_update', $entities['book']->id);
-        Activity::add($entities['page'], 'page_create', $entities['book']->id);
+        \Activity::add($entities['book'], 'book_update', $entities['book']->id);
+        \Activity::add($entities['page'], 'page_create', $entities['book']->id);
 
         $this->asAdmin()->visit('/user/' . $newUser->id)
             ->seeInElement('#recent-activity', 'updated book')
@@ -69,8 +69,8 @@ class UserProfileTest extends BrowserKitTest
         $newUser = $this->getEditor();
         $this->actingAs($newUser);
         $entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
-        Activity::add($entities['book'], 'book_update', $entities['book']->id);
-        Activity::add($entities['page'], 'page_create', $entities['book']->id);
+        \Activity::add($entities['book'], 'book_update', $entities['book']->id);
+        \Activity::add($entities['page'], 'page_create', $entities['book']->id);
 
         $this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name)
             ->seePageIs('/user/' . $newUser->id)

From 7c9937e9240b9656fe865de85a89c97b734656ab Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Feb 2017 14:20:59 +0000
Subject: [PATCH 03/27] Converted sort tests to non browserkit testing

Added testing to cover book sort endpoint.
Closes #283
---
 resources/views/books/show.blade.php |  4 +-
 tests/BrowserKitTest.php             |  2 +-
 tests/Entity/SortTest.php            | 99 +++++++++++++++++++++-------
 tests/TestCase.php                   | 46 +++++++++++++
 4 files changed, 126 insertions(+), 25 deletions(-)

diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php
index 6a18302bc..6b4e7f88a 100644
--- a/resources/views/books/show.blade.php
+++ b/resources/views/books/show.blade.php
@@ -5,10 +5,10 @@
     <div class="faded-small toolbar">
         <div class="container">
             <div class="row">
-                <div class="col-md-6 faded">
+                <div class="col-sm-6 faded">
                     @include('books._breadcrumbs', ['book' => $book])
                 </div>
-                <div class="col-md-6">
+                <div class="col-sm-6">
                     <div class="action-buttons faded">
                         @if(userCan('page-create', $book))
                             <a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
diff --git a/tests/BrowserKitTest.php b/tests/BrowserKitTest.php
index 674aef984..8e02cb5f1 100644
--- a/tests/BrowserKitTest.php
+++ b/tests/BrowserKitTest.php
@@ -51,7 +51,7 @@ abstract class BrowserKitTest extends TestCase
      */
     public function getAdmin() {
         if($this->admin === null) {
-            $adminRole = Role::getRole('admin');
+            $adminRole = Role::getSystemRole('admin');
             $this->admin = $adminRole->users->first();
         }
         return $this->admin;
diff --git a/tests/Entity/SortTest.php b/tests/Entity/SortTest.php
index d61b3f56f..3b0831029 100644
--- a/tests/Entity/SortTest.php
+++ b/tests/Entity/SortTest.php
@@ -1,6 +1,10 @@
 <?php namespace Tests;
 
-class SortTest extends BrowserKitTest
+use BookStack\Book;
+use BookStack\Page;
+use BookStack\Repos\EntityRepo;
+
+class SortTest extends TestCase
 {
     protected $book;
 
@@ -13,13 +17,14 @@ class SortTest extends BrowserKitTest
     public function test_drafts_do_not_show_up()
     {
         $this->asAdmin();
-        $entityRepo = app('\BookStack\Repos\EntityRepo');
+        $entityRepo = app(EntityRepo::class);
         $draft = $entityRepo->getDraftPage($this->book);
 
-        $this->visit($this->book->getUrl())
-            ->see($draft->name)
-            ->visit($this->book->getUrl() . '/sort')
-            ->dontSee($draft->name);
+        $resp = $this->get($this->book->getUrl());
+        $resp->assertSee($draft->name);
+
+        $resp = $this->get($this->book->getUrl() . '/sort');
+        $resp->assertDontSee($draft->name);
     }
 
     public function test_page_move()
@@ -27,17 +32,21 @@ class SortTest extends BrowserKitTest
         $page = \BookStack\Page::first();
         $currentBook = $page->book;
         $newBook = \BookStack\Book::where('id', '!=', $currentBook->id)->first();
-        $this->asAdmin()->visit($page->getUrl() . '/move')
-            ->see('Move Page')
-            ->type('book:' . $newBook->id, 'entity_selection')->press('Move Page');
 
+        $resp = $this->asAdmin()->get($page->getUrl() . '/move');
+        $resp->assertSee('Move Page');
+
+        $movePageResp = $this->put($page->getUrl() . '/move', [
+            'entity_selection' => 'book:' . $newBook->id
+        ]);
         $page = \BookStack\Page::find($page->id);
-        $this->seePageIs($page->getUrl());
+
+        $movePageResp->assertRedirect($page->getUrl());
         $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book');
 
-        $this->visit($newBook->getUrl())
-            ->seeInNthElement('.activity-list-item', 0, 'moved page')
-            ->seeInNthElement('.activity-list-item', 0, $page->name);
+        $newBookResp = $this->get($newBook->getUrl());
+        $newBookResp->assertSee('moved page');
+        $newBookResp->assertSee($page->name);
     }
 
     public function test_chapter_move()
@@ -47,22 +56,68 @@ class SortTest extends BrowserKitTest
         $pageToCheck = $chapter->pages->first();
         $newBook = \BookStack\Book::where('id', '!=', $currentBook->id)->first();
 
-        $this->asAdmin()->visit($chapter->getUrl() . '/move')
-            ->see('Move Chapter')
-            ->type('book:' . $newBook->id, 'entity_selection')->press('Move Chapter');
+        $chapterMoveResp = $this->asAdmin()->get($chapter->getUrl() . '/move');
+        $chapterMoveResp->assertSee('Move Chapter');
+
+        $moveChapterResp = $this->put($chapter->getUrl() . '/move', [
+            'entity_selection' => 'book:' . $newBook->id
+        ]);
 
         $chapter = \BookStack\Chapter::find($chapter->id);
-        $this->seePageIs($chapter->getUrl());
+        $moveChapterResp->assertRedirect($chapter->getUrl());
         $this->assertTrue($chapter->book->id === $newBook->id, 'Chapter Book is now the new book');
 
-        $this->visit($newBook->getUrl())
-            ->seeInNthElement('.activity-list-item', 0, 'moved chapter')
-            ->seeInNthElement('.activity-list-item', 0, $chapter->name);
+        $newBookResp = $this->get($newBook->getUrl());
+        $newBookResp->assertSee('moved chapter');
+        $newBookResp->assertSee($chapter->name);
 
         $pageToCheck = \BookStack\Page::find($pageToCheck->id);
         $this->assertTrue($pageToCheck->book_id === $newBook->id, 'Chapter child page\'s book id has changed to the new book');
-        $this->visit($pageToCheck->getUrl())
-            ->see($newBook->name);
+        $pageCheckResp = $this->get($pageToCheck->getUrl());
+        $pageCheckResp->assertSee($newBook->name);
+    }
+
+    public function test_book_sort()
+    {
+        $oldBook = Book::query()->first();
+        $chapterToMove = $this->newChapter(['name' => 'chapter to move'], $oldBook);
+        $newBook = $this->newBook(['name' => 'New sort book']);
+        $pagesToMove = Page::query()->take(5)->get();
+
+        // Create request data
+        $reqData = [
+            [
+                'id' => $chapterToMove->id,
+                'sort' => 0,
+                'parentChapter' => false,
+                'type' => 'chapter',
+                'book' => $newBook->id
+            ]
+        ];
+        foreach ($pagesToMove as $index => $page) {
+            $reqData[] = [
+                'id' => $page->id,
+                'sort' => $index,
+                'parentChapter' => $index === count($pagesToMove) - 1 ? $chapterToMove->id : false,
+                'type' => 'page',
+                'book' => $newBook->id
+            ];
+        }
+
+        $sortResp = $this->asAdmin()->put($newBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)]);
+        $sortResp->assertRedirect($newBook->getUrl());
+        $sortResp->assertStatus(302);
+        $this->assertDatabaseHas('chapters', [
+            'id' => $chapterToMove->id,
+            'book_id' => $newBook->id,
+            'priority' => 0
+        ]);
+        $this->assertTrue($newBook->chapters()->count() === 1);
+        $this->assertTrue($newBook->chapters()->first()->pages()->count() === 1);
+
+        $checkPage = $pagesToMove[1];
+        $checkResp = $this->get(Page::find($checkPage->id)->getUrl());
+        $checkResp->assertSee($newBook->name);
     }
 
 }
\ No newline at end of file
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 6b97682e8..d52e991e3 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -1,8 +1,54 @@
 <?php namespace Tests;
 
+use BookStack\Book;
+use BookStack\Chapter;
+use BookStack\Repos\EntityRepo;
+use BookStack\Role;
 use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 
 abstract class TestCase extends BaseTestCase
 {
     use CreatesApplication;
+
+    protected $admin;
+
+    /**
+     * Set the current user context to be an admin.
+     * @return $this
+     */
+    public function asAdmin()
+    {
+        return $this->actingAs($this->getAdmin());
+    }
+
+    /**
+     * Get the current admin user.
+     * @return mixed
+     */
+    public function getAdmin() {
+        if($this->admin === null) {
+            $adminRole = Role::getSystemRole('admin');
+            $this->admin = $adminRole->users->first();
+        }
+        return $this->admin;
+    }
+
+    /**
+     * Create and return a new book.
+     * @param array $input
+     * @return Book
+     */
+    public function newBook($input = ['name' => 'test book', 'description' => 'My new test book']) {
+        return $this->app[EntityRepo::class]->createFromInput('book', $input, false);
+    }
+
+    /**
+     * Create and return a new test chapter
+     * @param array $input
+     * @param Book $book
+     * @return Chapter
+     */
+    public function newChapter($input = ['name' => 'test chapter', 'description' => 'My new test chapter'], Book $book) {
+        return $this->app[EntityRepo::class]->createFromInput('chapter', $input, $book);
+    }
 }
\ No newline at end of file

From d369d315a7e43320ed85e2e4feadd66601a3b06d Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Feb 2017 14:37:50 +0000
Subject: [PATCH 04/27] Fixed non-browserkit testcase and seeder issues

---
 composer.lock                         | 98 +++++++++++++--------------
 database/seeds/DummyContentSeeder.php |  6 +-
 tests/TestCase.php                    |  2 +
 3 files changed, 54 insertions(+), 52 deletions(-)

diff --git a/composer.lock b/composer.lock
index 586213e57..df7b80289 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,21 +4,21 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "27dd30e92f700ea9a8c2a0a2327d4f9f",
+    "hash": "625ae196ac4c457c3aaff34235acbc4d",
     "content-hash": "e851e9fd06efac8362604c39b0a17542",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.21.4",
+            "version": "3.21.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "6408a4904a04eca44461a65ba4a0fae53f80417b"
+                "reference": "b51512a4ad4aa080ab963942a1e234265771fcde"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6408a4904a04eca44461a65ba4a0fae53f80417b",
-                "reference": "6408a4904a04eca44461a65ba4a0fae53f80417b",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b51512a4ad4aa080ab963942a1e234265771fcde",
+                "reference": "b51512a4ad4aa080ab963942a1e234265771fcde",
                 "shasum": ""
             },
             "require": {
@@ -85,7 +85,7 @@
                 "s3",
                 "sdk"
             ],
-            "time": "2017-01-25 00:43:08"
+            "time": "2017-01-27 00:34:55"
         },
         {
             "name": "barryvdh/laravel-debugbar",
@@ -799,16 +799,16 @@
         },
         {
             "name": "intervention/image",
-            "version": "2.3.9",
+            "version": "2.3.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Intervention/image.git",
-                "reference": "2bce9a59c43b868300b02a7d31a1e4aa67a200ae"
+                "reference": "e8881fd99b9804b29e02d6d1c2c15ee459335cf1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Intervention/image/zipball/2bce9a59c43b868300b02a7d31a1e4aa67a200ae",
-                "reference": "2bce9a59c43b868300b02a7d31a1e4aa67a200ae",
+                "url": "https://api.github.com/repos/Intervention/image/zipball/e8881fd99b9804b29e02d6d1c2c15ee459335cf1",
+                "reference": "e8881fd99b9804b29e02d6d1c2c15ee459335cf1",
                 "shasum": ""
             },
             "require": {
@@ -857,7 +857,7 @@
                 "thumbnail",
                 "watermark"
             ],
-            "time": "2017-01-10 14:15:56"
+            "time": "2017-02-04 10:37:19"
         },
         {
             "name": "knplabs/knp-snappy",
@@ -973,16 +973,16 @@
         },
         {
             "name": "laravel/framework",
-            "version": "v5.4.3",
+            "version": "v5.4.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "9fa94bf77272ea7f920292c6f016b0f6e0853f98"
+                "reference": "600330ae1d218919b3b307e0578461a2df248663"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/9fa94bf77272ea7f920292c6f016b0f6e0853f98",
-                "reference": "9fa94bf77272ea7f920292c6f016b0f6e0853f98",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/600330ae1d218919b3b307e0578461a2df248663",
+                "reference": "600330ae1d218919b3b307e0578461a2df248663",
                 "shasum": ""
             },
             "require": {
@@ -1098,20 +1098,20 @@
                 "framework",
                 "laravel"
             ],
-            "time": "2017-01-25 16:40:49"
+            "time": "2017-02-03 19:47:35"
         },
         {
             "name": "laravel/socialite",
-            "version": "v3.0.2",
+            "version": "v3.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/socialite.git",
-                "reference": "d3aaffa5e122395e54eb2c26062fde3a848c40fd"
+                "reference": "01588748beef55ad5dd4f172d235548d3a6be79a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/d3aaffa5e122395e54eb2c26062fde3a848c40fd",
-                "reference": "d3aaffa5e122395e54eb2c26062fde3a848c40fd",
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/01588748beef55ad5dd4f172d235548d3a6be79a",
+                "reference": "01588748beef55ad5dd4f172d235548d3a6be79a",
                 "shasum": ""
             },
             "require": {
@@ -1152,20 +1152,20 @@
                 "laravel",
                 "oauth"
             ],
-            "time": "2017-01-25 17:58:13"
+            "time": "2017-02-01 13:43:56"
         },
         {
             "name": "league/flysystem",
-            "version": "1.0.33",
+            "version": "1.0.34",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "5c7f98498b12d47f9de90ec9186a90000125777c"
+                "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5c7f98498b12d47f9de90ec9186a90000125777c",
-                "reference": "5c7f98498b12d47f9de90ec9186a90000125777c",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/469ad53c13ea19a0e54e3e5d70f61227ddcc0299",
+                "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299",
                 "shasum": ""
             },
             "require": {
@@ -1235,7 +1235,7 @@
                 "sftp",
                 "storage"
             ],
-            "time": "2017-01-23 10:32:09"
+            "time": "2017-01-30 17:41:17"
         },
         {
             "name": "league/flysystem-aws-s3-v3",
@@ -2237,7 +2237,7 @@
         },
         {
             "name": "symfony/css-selector",
-            "version": "v3.1.9",
+            "version": "v3.1.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
@@ -2347,16 +2347,16 @@
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v3.1.9",
+            "version": "v3.1.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "a950260ebc947578fba82a3222e2085d90682376"
+                "reference": "7eede2a901a19928494194f7d1815a77b9a473a0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/a950260ebc947578fba82a3222e2085d90682376",
-                "reference": "a950260ebc947578fba82a3222e2085d90682376",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7eede2a901a19928494194f7d1815a77b9a473a0",
+                "reference": "7eede2a901a19928494194f7d1815a77b9a473a0",
                 "shasum": ""
             },
             "require": {
@@ -2399,7 +2399,7 @@
             ],
             "description": "Symfony DomCrawler Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02 20:31:54"
+            "time": "2017-01-21 17:13:55"
         },
         {
             "name": "symfony/event-dispatcher",
@@ -3268,16 +3268,16 @@
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.5.5",
+            "version": "1.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108"
+                "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108",
-                "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe",
+                "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe",
                 "shasum": ""
             },
             "require": {
@@ -3306,7 +3306,7 @@
                 "object",
                 "object graph"
             ],
-            "time": "2016-10-31 17:19:45"
+            "time": "2017-01-26 22:05:40"
         },
         {
             "name": "phpdocumentor/reflection-common",
@@ -3763,16 +3763,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "5.7.6",
+            "version": "5.7.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "caf8141b89691498d91aaac6c82e9cd5f685ae86"
+                "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/caf8141b89691498d91aaac6c82e9cd5f685ae86",
-                "reference": "caf8141b89691498d91aaac6c82e9cd5f685ae86",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bf0804199f516fe80ffcc48ac6d4741c49baeb6e",
+                "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e",
                 "shasum": ""
             },
             "require": {
@@ -3789,11 +3789,11 @@
                 "phpunit/php-text-template": "~1.2",
                 "phpunit/php-timer": "^1.0.6",
                 "phpunit/phpunit-mock-objects": "^3.2",
-                "sebastian/comparator": "~1.2.2",
+                "sebastian/comparator": "^1.2.4",
                 "sebastian/diff": "~1.2",
                 "sebastian/environment": "^1.3.4 || ^2.0",
                 "sebastian/exporter": "~2.0",
-                "sebastian/global-state": "^1.0 || ^2.0",
+                "sebastian/global-state": "^1.1",
                 "sebastian/object-enumerator": "~2.0",
                 "sebastian/resource-operations": "~1.0",
                 "sebastian/version": "~1.0|~2.0",
@@ -3841,7 +3841,7 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2017-01-22 08:39:59"
+            "time": "2017-02-04 09:03:53"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
@@ -3949,16 +3949,16 @@
         },
         {
             "name": "sebastian/comparator",
-            "version": "1.2.2",
+            "version": "1.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/comparator.git",
-                "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f"
+                "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f",
-                "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
+                "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
                 "shasum": ""
             },
             "require": {
@@ -4009,7 +4009,7 @@
                 "compare",
                 "equality"
             ],
-            "time": "2016-11-19 09:18:40"
+            "time": "2017-01-29 09:50:25"
         },
         {
             "name": "sebastian/diff",
diff --git a/database/seeds/DummyContentSeeder.php b/database/seeds/DummyContentSeeder.php
index c64ca2a8c..efcda4220 100644
--- a/database/seeds/DummyContentSeeder.php
+++ b/database/seeds/DummyContentSeeder.php
@@ -11,14 +11,14 @@ class DummyContentSeeder extends Seeder
      */
     public function run()
     {
-        $user = factory(BookStack\User::class, 1)->create();
+        $user = factory(\BookStack\User::class)->create();
         $role = \BookStack\Role::getRole('editor');
         $user->attachRole($role);
 
 
-        $books = factory(BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id])
+        $books = factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id])
             ->each(function($book) use ($user) {
-                $chapters = factory(BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
+                $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
                     ->each(function($chapter) use ($user, $book){
                        $pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]);
                         $chapter->pages()->saveMany($pages);
diff --git a/tests/TestCase.php b/tests/TestCase.php
index d52e991e3..d64aef3db 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -4,11 +4,13 @@ use BookStack\Book;
 use BookStack\Chapter;
 use BookStack\Repos\EntityRepo;
 use BookStack\Role;
+use Illuminate\Foundation\Testing\DatabaseTransactions;
 use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 
 abstract class TestCase extends BaseTestCase
 {
     use CreatesApplication;
+    use DatabaseTransactions;
 
     protected $admin;
 

From ee495450cc5bcb86eea8b206ae25aae062169aa7 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Feb 2017 14:47:00 +0000
Subject: [PATCH 05/27] Improved multi-line callout rendering

Closes #300
---
 resources/assets/sass/_blocks.scss | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/resources/assets/sass/_blocks.scss b/resources/assets/sass/_blocks.scss
index a2023aa37..bd3f8ff4e 100644
--- a/resources/assets/sass/_blocks.scss
+++ b/resources/assets/sass/_blocks.scss
@@ -135,11 +135,19 @@
   border-left: 3px solid #BBB;
   background-color: #EEE;
   padding: $-s;
+  padding-left: $-xl;
   display: block;
+  position: relative;
   &:before {
     font-family: 'Material-Design-Iconic-Font';
-    padding-right: $-s;
+    left: $-xs + 4px;
+    top: 50%;
+    margin-top: -9px;
+    //top: $-xs + 5px;
     display: inline-block;
+    position: absolute;
+    font-size: 1.222em;
+    line-height: 1;
   }
   &.success {
     border-left-color: $positive;

From 86625a7642e437d3f0b68543747036e25ff94912 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Feb 2017 15:28:53 +0000
Subject: [PATCH 06/27] Neatened up social login/register buttons

---
 app/Services/SocialAuthService.php      | 16 +++++++++++++---
 config/services.php                     |  5 +++++
 resources/assets/sass/_buttons.scss     | 19 +++++++++++++++++++
 resources/assets/sass/_grid.scss        | 14 --------------
 resources/assets/sass/styles.scss       | 22 ++++++++++++++++++----
 resources/lang/en/auth.php              |  2 ++
 resources/views/auth/login.blade.php    |  9 +++++----
 resources/views/auth/register.blade.php | 10 +++++-----
 8 files changed, 67 insertions(+), 30 deletions(-)

diff --git a/app/Services/SocialAuthService.php b/app/Services/SocialAuthService.php
index 710f95696..2c15e73ce 100644
--- a/app/Services/SocialAuthService.php
+++ b/app/Services/SocialAuthService.php
@@ -181,14 +181,24 @@ class SocialAuthService
     public function getActiveDrivers()
     {
         $activeDrivers = [];
-        foreach ($this->validSocialDrivers as $driverName) {
-            if ($this->checkDriverConfigured($driverName)) {
-                $activeDrivers[$driverName] = true;
+        foreach ($this->validSocialDrivers as $driverKey) {
+            if ($this->checkDriverConfigured($driverKey)) {
+                $activeDrivers[$driverKey] = $this->getDriverName($driverKey);
             }
         }
         return $activeDrivers;
     }
 
+    /**
+     * Get the presentational name for a driver.
+     * @param $driver
+     * @return mixed
+     */
+    public function getDriverName($driver)
+    {
+        return config('services.' . strtolower($driver) . '.name');
+    }
+
     /**
      * @param string                            $socialDriver
      * @param \Laravel\Socialite\Contracts\User $socialUser
diff --git a/config/services.php b/config/services.php
index 8c17897e4..fe58cbb9a 100644
--- a/config/services.php
+++ b/config/services.php
@@ -41,30 +41,35 @@ return [
         'client_id'     => env('GITHUB_APP_ID', false),
         'client_secret' => env('GITHUB_APP_SECRET', false),
         'redirect'      => env('APP_URL') . '/login/service/github/callback',
+        'name'          => 'GitHub',
     ],
 
     'google'   => [
         'client_id'     => env('GOOGLE_APP_ID', false),
         'client_secret' => env('GOOGLE_APP_SECRET', false),
         'redirect'      => env('APP_URL') . '/login/service/google/callback',
+        'name'          => 'Google',
     ],
 
     'slack'   => [
         'client_id'     => env('SLACK_APP_ID', false),
         'client_secret' => env('SLACK_APP_SECRET', false),
         'redirect'      => env('APP_URL') . '/login/service/slack/callback',
+        'name'          => 'Slack',
     ],
 
     'facebook'   => [
         'client_id'     => env('FACEBOOK_APP_ID', false),
         'client_secret' => env('FACEBOOK_APP_SECRET', false),
         'redirect'      => env('APP_URL') . '/login/service/facebook/callback',
+        'name'          => 'Facebook',
     ],
 
     'twitter'   => [
         'client_id'     => env('TWITTER_APP_ID', false),
         'client_secret' => env('TWITTER_APP_SECRET', false),
         'redirect'      => env('APP_URL') . '/login/service/twitter/callback',
+        'name'          => 'Twitter',
     ],
 
     'ldap' => [
diff --git a/resources/assets/sass/_buttons.scss b/resources/assets/sass/_buttons.scss
index 791a5bb72..6e03c9217 100644
--- a/resources/assets/sass/_buttons.scss
+++ b/resources/assets/sass/_buttons.scss
@@ -54,6 +54,9 @@ $button-border-radius: 2px;
   &.muted {
     @include generate-button-colors(#EEE, #888);
   }
+  &.muted-light {
+    @include generate-button-colors(#666, #e4e4e4);
+  }
 }
 
 .text-button {
@@ -92,6 +95,9 @@ $button-border-radius: 2px;
   width: 100%;
   text-align: center;
   display: block;
+  &.text-left {
+    text-align: left;
+  }
 }
 
 .button.icon {
@@ -100,6 +106,19 @@ $button-border-radius: 2px;
   }
 }
 
+.button.svg {
+  svg {
+    display: inline-block;
+    position: absolute;
+    left: $-m;
+    top: $-s - 2px;
+    width: 24px;
+  }
+  padding: $-s $-m;
+  padding-bottom: $-s - 2px;
+  padding-left: $-m*2 + 24px;
+}
+
 .button[disabled] {
   background-color: #BBB;
   cursor: default;
diff --git a/resources/assets/sass/_grid.scss b/resources/assets/sass/_grid.scss
index 231c12d4d..b32dafd38 100644
--- a/resources/assets/sass/_grid.scss
+++ b/resources/assets/sass/_grid.scss
@@ -55,20 +55,6 @@ div[class^="col-"] img {
   }
 }
 
-.center-box {
-  margin: $-xl auto 0 auto;
-  padding: $-m $-xxl $-xl*2 $-xxl;
-  max-width: 346px;
-  display: inline-block;
-  text-align: left;
-  vertical-align: top;
-  &.login {
-    background-color: #EEE;
-    box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
-    border: 1px solid #DDD;
-  }
-}
-
 .row {
   margin-left: -$-m;
   margin-right: -$-m;
diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss
index 7d33bd0a6..967aba76b 100644
--- a/resources/assets/sass/styles.scss
+++ b/resources/assets/sass/styles.scss
@@ -251,10 +251,24 @@ $btt-size: 40px;
   }
 }
 
-
-
-
-
+.center-box {
+  margin: $-xl auto 0 auto;
+  padding: $-m $-xxl $-xl $-xxl;
+  width: 420px;
+  max-width: 100%;
+  display: inline-block;
+  text-align: left;
+  vertical-align: top;
+  //border: 1px solid #DDD;
+  input {
+    width: 100%;
+  }
+  &.login {
+    background-color: #EEE;
+    box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
+    border: 1px solid #DDD;
+  }
+}
 
 
 
diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php
index b734828fc..a1232efc6 100644
--- a/resources/lang/en/auth.php
+++ b/resources/lang/en/auth.php
@@ -18,6 +18,8 @@ return [
      */
     'sign_up' => 'Sign up',
     'log_in' => 'Log in',
+    'log_in_with' => 'Login with :socialDriver',
+    'sign_up_with' => 'Sign up with :socialDriver',
     'logout' => 'Logout',
 
     'name' => 'Name',
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php
index 6c8b1720c..706747b8b 100644
--- a/resources/views/auth/login.blade.php
+++ b/resources/views/auth/login.blade.php
@@ -32,10 +32,11 @@
 
             @if(count($socialDrivers) > 0)
                 <hr class="margin-top">
-                <h3 class="text-muted">{{ trans('auth.social_login') }}</h3>
-                @foreach($socialDrivers as $driver => $enabled)
-                    <a id="social-login-{{$driver}}" href="{{ baseUrl("/login/service/" . $driver) }}">@icon($driver, ['width' => 56])</a>
-                        &nbsp;
+                @foreach($socialDrivers as $driver => $name)
+                    <a id="social-login-{{$driver}}" class="button block muted-light svg text-left" href="{{ baseUrl("/login/service/" . $driver) }}">
+                        @icon($driver)
+                        {{ trans('auth.log_in_with', ['socialDriver' => $name]) }}
+                    </a>
                 @endforeach
             @endif
         </div>
diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php
index 860508df0..d5db4afa8 100644
--- a/resources/views/auth/register.blade.php
+++ b/resources/views/auth/register.blade.php
@@ -35,11 +35,11 @@
 
             @if(count($socialDrivers) > 0)
                 <hr class="margin-top">
-                <h3 class="text-muted">{{ trans('auth.social_registration') }}</h3>
-                <p class="text-small">{{ trans('auth.social_registration_text') }}</p>
-                @foreach($socialDrivers as $driver => $enabled)
-                    <a href="{{ baseUrl("/register/service/" . $driver) }}">@icon($driver, ['width' => 56])</a>
-                    &nbsp;
+                @foreach($socialDrivers as $driver => $name)
+                    <a id="social-register-{{$driver}}" class="button block muted-light svg text-left" href="{{ baseUrl("/register/service/" . $driver) }}">
+                        @icon($driver)
+                        {{ trans('auth.sign_up_with', ['socialDriver' => $name]) }}
+                    </a>
                 @endforeach
             @endif
         </div>

From 65899a3e91911d7e7199eae8e33cb5f4fa6ce929 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Feb 2017 18:57:57 +0000
Subject: [PATCH 07/27] Prevented settings being overfetched from db/cache

---
 app/Providers/AppServiceProvider.php |  6 +++++-
 app/Services/SettingService.php      | 12 ++++++++----
 app/helpers.php                      |  2 +-
 3 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 118271f51..49cc15dd6 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -1,5 +1,7 @@
 <?php namespace BookStack\Providers;
 
+use BookStack\Services\SettingService;
+use BookStack\Setting;
 use Illuminate\Support\ServiceProvider;
 use Validator;
 
@@ -30,6 +32,8 @@ class AppServiceProvider extends ServiceProvider
      */
     public function register()
     {
-        //
+        $this->app->singleton(SettingService::class, function($app) {
+            return new SettingService($app->make(Setting::class), $app->make('Illuminate\Contracts\Cache\Repository'));
+        });
     }
 }
diff --git a/app/Services/SettingService.php b/app/Services/SettingService.php
index 40094a513..18a7c0d1b 100644
--- a/app/Services/SettingService.php
+++ b/app/Services/SettingService.php
@@ -16,6 +16,7 @@ class SettingService
 
     protected $setting;
     protected $cache;
+    protected $localCache = [];
 
     protected $cachePrefix = 'setting-';
 
@@ -40,8 +41,12 @@ class SettingService
     public function get($key, $default = false)
     {
         if ($default === false) $default = config('setting-defaults.' . $key, false);
+        if (isset($this->localCache[$key])) return $this->localCache[$key];
+
         $value = $this->getValueFromStore($key, $default);
-        return $this->formatValue($value, $default);
+        $formatted = $this->formatValue($value, $default);
+        $this->localCache[$key] = $formatted;
+        return $formatted;
     }
 
     /**
@@ -71,9 +76,8 @@ class SettingService
 
         // Check the cache
         $cacheKey = $this->cachePrefix . $key;
-        if ($this->cache->has($cacheKey)) {
-            return $this->cache->get($cacheKey);
-        }
+        $cacheVal = $this->cache->get($cacheKey, null);
+        if ($cacheVal !== null) return $cacheVal;
 
         // Check the database
         $settingObject = $this->getSettingObjectByKey($key);
diff --git a/app/helpers.php b/app/helpers.php
index 8103ad1ff..f69f2c174 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -64,7 +64,7 @@ function userCan($permission, Ownable $ownable = null)
  */
 function setting($key = null, $default = false)
 {
-    $settingService = app(\BookStack\Services\SettingService::class);
+    $settingService = resolve(\BookStack\Services\SettingService::class);
     if (is_null($key)) return $settingService;
     return $settingService->get($key, $default);
 }

From 6638ee47d3b62015323f2be3a0c383c9cdcdd4f5 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Feb 2017 21:19:29 +0000
Subject: [PATCH 08/27] Fixed entities wrongly visible on 404

Also ensured header state as expected on 404.
In reference to BookStackApp/website#9
---
 app/Http/Kernel.php                  |  4 ++--
 app/helpers.php                      |  9 +++++++++
 resources/views/base.blade.php       |  8 ++++----
 resources/views/errors/404.blade.php | 30 +++++++++++++++-------------
 tests/PublicActionTest.php           | 10 ++++++++++
 5 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index c55cc9ab8..839590c95 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -13,6 +13,8 @@ class Kernel extends HttpKernel
      */
     protected $middleware = [
         \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
+        \Illuminate\Session\Middleware\StartSession::class,
+        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
     ];
 
     /**
@@ -24,8 +26,6 @@ class Kernel extends HttpKernel
         'web' => [
             \BookStack\Http\Middleware\EncryptCookies::class,
             \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
-            \Illuminate\Session\Middleware\StartSession::class,
-            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
             \BookStack\Http\Middleware\VerifyCsrfToken::class,
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
             \BookStack\Http\Middleware\Localization::class
diff --git a/app/helpers.php b/app/helpers.php
index 6decb08e9..e68e29d13 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -37,6 +37,15 @@ function user()
     return auth()->user() ?: \BookStack\User::getDefault();
 }
 
+/**
+ * Check if current user is a signed in user.
+ * @return bool
+ */
+function signedInUser()
+{
+    return auth()->user() && !auth()->user()->isDefault();
+}
+
 /**
  * Check if the current user has a permission.
  * If an ownable element is passed in the jointPermissions are checked against
diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php
index 43f22d89a..a98a37131 100644
--- a/resources/views/base.blade.php
+++ b/resources/views/base.blade.php
@@ -55,15 +55,15 @@
                     <div class="float right">
                         <div class="links text-center">
                             <a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
-                            @if(isset($currentUser) && userCan('settings-manage'))
+                            @if(signedInUser() && userCan('settings-manage'))
                                 <a href="{{ baseUrl('/settings') }}"><i class="zmdi zmdi-settings"></i>{{ trans('settings.settings') }}</a>
                             @endif
-                            @if(!isset($signedIn) || !$signedIn)
+                            @if(!signedInUser())
                                 <a href="{{ baseUrl('/login') }}"><i class="zmdi zmdi-sign-in"></i>{{ trans('auth.log_in') }}</a>
                             @endif
                         </div>
-                        @if(isset($signedIn) && $signedIn)
-                            @include('partials._header-dropdown', ['currentUser' => $currentUser])
+                        @if(signedInUser())
+                            @include('partials._header-dropdown', ['currentUser' => user()])
                         @endif
 
                     </div>
diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php
index c9e600ceb..a0e34e83d 100644
--- a/resources/views/errors/404.blade.php
+++ b/resources/views/errors/404.blade.php
@@ -10,22 +10,24 @@
     <p>{{ trans('errors.sorry_page_not_found') }}</p>
     <p><a href="{{ baseUrl('/') }}" class="button">{{ trans('errors.return_home') }}</a></p>
 
-    <hr>
+    @if (setting('app-public') || !user()->isDefault())
+        <hr>
 
-    <div class="row">
-        <div class="col-md-4">
-            <h3 class="text-muted">{{ trans('entities.pages_popular') }}</h3>
-            @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Page::class]), 'style' => 'compact'])
+        <div class="row">
+            <div class="col-md-4">
+                <h3 class="text-muted">{{ trans('entities.pages_popular') }}</h3>
+                @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Page::class]), 'style' => 'compact'])
+            </div>
+            <div class="col-md-4">
+                <h3 class="text-muted">{{ trans('entities.books_popular') }}</h3>
+                @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
+            </div>
+            <div class="col-md-4">
+                <h3 class="text-muted">{{ trans('entities.chapters_popular') }}</h3>
+                @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Chapter::class]), 'style' => 'compact'])
+            </div>
         </div>
-        <div class="col-md-4">
-            <h3 class="text-muted">{{ trans('entities.books_popular') }}</h3>
-            @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
-        </div>
-        <div class="col-md-4">
-            <h3 class="text-muted">{{ trans('entities.chapters_popular') }}</h3>
-            @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Chapter::class]), 'style' => 'compact'])
-        </div>
-    </div>
+    @endif
 </div>
 
 @stop
\ No newline at end of file
diff --git a/tests/PublicActionTest.php b/tests/PublicActionTest.php
index 2ea5fbfed..4e242fc7d 100644
--- a/tests/PublicActionTest.php
+++ b/tests/PublicActionTest.php
@@ -80,4 +80,14 @@ class PublicActionTest extends TestCase
         ]);
     }
 
+    public function test_content_not_listed_on_404_for_public_users()
+    {
+        $page = \BookStack\Page::first();
+        $this->asAdmin()->visit($page->getUrl());
+        Auth::logout();
+        view()->share('pageTitle', '');
+        $this->forceVisit('/cats/dogs/hippos');
+        $this->dontSee($page->name);
+    }
+
 }
\ No newline at end of file

From b2b64fb853510fdcdace5e719e9c076b6ac1a4e3 Mon Sep 17 00:00:00 2001
From: Arie Timmerman <arietimmerman@Aries-MacBook-Pro.local>
Date: Fri, 10 Feb 2017 22:10:41 +0100
Subject: [PATCH 09/27] Started with Dutch translation

---
 .gitignore                       |   9 +-
 resources/lang/nl/activities.php |  40 ++++++
 resources/lang/nl/auth.php       |  76 +++++++++++
 resources/lang/nl/common.php     |  58 ++++++++
 resources/lang/nl/components.php |  24 ++++
 resources/lang/nl/entities.php   | 226 +++++++++++++++++++++++++++++++
 resources/lang/nl/errors.php     |  70 ++++++++++
 resources/lang/nl/pagination.php |  19 +++
 resources/lang/nl/passwords.php  |  22 +++
 resources/lang/nl/settings.php   | 123 +++++++++++++++++
 resources/lang/nl/validation.php | 108 +++++++++++++++
 11 files changed, 774 insertions(+), 1 deletion(-)
 create mode 100644 resources/lang/nl/activities.php
 create mode 100644 resources/lang/nl/auth.php
 create mode 100644 resources/lang/nl/common.php
 create mode 100644 resources/lang/nl/components.php
 create mode 100644 resources/lang/nl/entities.php
 create mode 100644 resources/lang/nl/errors.php
 create mode 100644 resources/lang/nl/pagination.php
 create mode 100644 resources/lang/nl/passwords.php
 create mode 100644 resources/lang/nl/settings.php
 create mode 100644 resources/lang/nl/validation.php

diff --git a/.gitignore b/.gitignore
index 83b754c04..5f41a864e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,11 @@ _ide_helper.php
 /storage/debugbar
 .phpstorm.meta.php
 yarn.lock
-/bin
\ No newline at end of file
+/bin
+.buildpath
+
+.project
+
+.settings/org.eclipse.wst.common.project.facet.core.xml
+
+.settings/org.eclipse.php.core.prefs
diff --git a/resources/lang/nl/activities.php b/resources/lang/nl/activities.php
new file mode 100644
index 000000000..815be4898
--- /dev/null
+++ b/resources/lang/nl/activities.php
@@ -0,0 +1,40 @@
+<?php
+
+return [
+
+    /**
+     * Activity text strings.
+     * Is used for all the text within activity logs & notifications.
+     */
+
+    // Pages
+    'page_create'                 => 'aangemaakte pagina',
+    'page_create_notification'    => 'Pagina Succesvol Aangemaakt',
+    'page_update'                 => 'bijgewerkte pagina',
+    'page_update_notification'    => 'Pagina Succesvol Bijgewerkt',
+    'page_delete'                 => 'verwijderde pagina',
+    'page_delete_notification'    => 'Pagina Succesvol Verwijderd',
+    'page_restore'                => 'restored page',
+    'page_restore_notification'   => 'Pagina Succesvol Hersteld',
+    'page_move'                   => 'verplaatste pagina',
+
+    // Chapters
+    'chapter_create'              => 'aangemaakt hoofdstuk',
+    'chapter_create_notification' => 'Hoofdstuk Succesvol Aangemaakt',
+    'chapter_update'              => 'bijgewerkt hoofdstuk',
+    'chapter_update_notification' => 'Hoofdstuk Succesvol Bijgewerkt',
+    'chapter_delete'              => 'verwijderd hoofdstuk',
+    'chapter_delete_notification' => 'Hoofdstuk Succesvol Verwijderd',
+    'chapter_move'                => 'verplaatst hoofdstuk',
+
+    // Books
+    'book_create'                 => 'aangemaakt boek',
+    'book_create_notification'    => 'Boek Succesvol Aangemaakt',
+    'book_update'                 => 'bijgewerkt boek',
+    'book_update_notification'    => 'Boek Succesvol Bijgewerkt',
+    'book_delete'                 => 'verwijderd boek',
+    'book_delete_notification'    => 'Boek Succesvol Verwijderd',
+    'book_sort'                   => 'gesorteerd book',
+    'book_sort_notification'      => 'Boek Succesvol Gesorteerd',
+
+];
diff --git a/resources/lang/nl/auth.php b/resources/lang/nl/auth.php
new file mode 100644
index 000000000..82dcf7020
--- /dev/null
+++ b/resources/lang/nl/auth.php
@@ -0,0 +1,76 @@
+<?php
+return [
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used during authentication for various
+    | messages that we need to display to the user. You are free to modify
+    | these language lines according to your application's requirements.
+    |
+    */
+    'failed' => 'Deze inloggegevens zijn niet bij ons bekend.',
+    'throttle' => 'Te veel loginpogingen! Probeer het opnieuw na :seconds seconden.',
+
+    /**
+     * Login & Register
+     */
+    'sign_up' => 'Registreren',
+    'log_in' => 'Log in',
+    'log_in_with' => 'Login met :socialDriver',
+    'sign_up_with' => 'Registreer met :socialDriver',
+    'logout' => 'Uitloggen',
+
+    'name' => 'Naam',
+    'username' => 'Gebruikersnaam',
+    'email' => 'Email',
+    'password' => 'Wachtwoord',
+    'password_confirm' => 'Wachtwoord Bevestigen',
+    'password_hint' => 'Minimaal 5 tekens',
+    'forgot_password' => 'Wachtwoord vergeten?',
+    'remember_me' => 'Mij onthouden',
+    'ldap_email_hint' => 'Geef een email op waarmee je dit account wilt gebruiken.',
+    'create_account' => 'Account Aanmaken',
+    'social_login' => 'Social Login',
+    'social_registration' => 'Social Registratie',
+    'social_registration_text' => 'Registreer en log in met een andere dienst.',
+
+    'register_thanks' => 'Bedankt voor het registreren!',
+    'register_confirm' => 'Controleer je e-mail en bevestig je registratie om in te loggen op :appName.',
+    'registrations_disabled' => 'Registratie is momenteel niet mogelijk',
+    'registration_email_domain_invalid' => 'Dit e-maildomein is niet toegestaan',
+    'register_success' => 'Bedankt voor het inloggen. Je bent ook geregistreerd.',
+
+
+    /**
+     * Password Reset
+     */
+    'reset_password' => 'Wachtwoord Herstellen',
+    'reset_password_send_instructions' => 'Geef je e-mail en we sturen je een link om je wachtwoord te herstellen',
+    'reset_password_send_button' => 'Link Sturen',
+    'reset_password_sent_success' => 'A password reset link has been sent to :email.',
+    'reset_password_success' => 'Your password has been successfully reset.',
+
+    'email_reset_subject' => 'Reset your :appName password',
+    'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
+    'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
+
+
+    /**
+     * Email Confirmation
+     */
+    'email_confirm_subject' => 'Confirm your email on :appName',
+    'email_confirm_greeting' => 'Thanks for joining :appName!',
+    'email_confirm_text' => 'Please confirm your email address by clicking the button below:',
+    'email_confirm_action' => 'Confirm Email',
+    'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.',
+    'email_confirm_success' => 'Your email has been confirmed!',
+    'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
+
+    'email_not_confirmed' => 'Email Address Not Confirmed',
+    'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
+    'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
+    'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
+    'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
+];
\ No newline at end of file
diff --git a/resources/lang/nl/common.php b/resources/lang/nl/common.php
new file mode 100644
index 000000000..bdde9eb95
--- /dev/null
+++ b/resources/lang/nl/common.php
@@ -0,0 +1,58 @@
+<?php
+return [
+
+    /**
+     * Buttons
+     */
+    'cancel' => 'Annuleren',
+    'confirm' => 'Bevestigen',
+    'back' => 'Terug',
+    'save' => 'Opslaan',
+    'continue' => 'Doorgaan',
+    'select' => 'Kies',
+
+    /**
+     * Form Labels
+     */
+    'name' => 'Naam',
+    'description' => 'Beschrijving',
+    'role' => 'Rol',
+
+    /**
+     * Actions
+     */
+    'actions' => 'Acties',
+    'view' => 'Bekijk',
+    'create' => 'Aanmaken',
+    'update' => 'Update',
+    'edit' => 'Bewerk',
+    'sort' => 'Sorteer',
+    'move' => 'Verplaats',
+    'delete' => 'Verwijder',
+    'search' => 'Zoek',
+    'search_clear' => 'Zoekopdracht wissen',
+    'reset' => 'Reset',
+    'remove' => 'Verwijderen',
+
+
+    /**
+     * Misc
+     */
+    'deleted_user' => 'Verwijderde gebruiker',
+    'no_activity' => 'Geen activiteiten',
+    'no_items' => 'Geen items beschikbaar',
+    'back_to_top' => 'Terug naar boven',
+    'toggle_details' => 'Details Weergeven',
+
+    /**
+     * Header
+     */
+    'view_profile' => 'Profiel Weergeven',
+    'edit_profile' => 'Profiel Bewerken',
+
+    /**
+     * Email Content
+     */
+    'email_action_help' => 'Als je de knop ":actionText" niet werkt, kopieer en plak de onderstaande URL in je web browser:',
+    'email_rights' => 'Alle rechten voorbehouden',
+];
\ No newline at end of file
diff --git a/resources/lang/nl/components.php b/resources/lang/nl/components.php
new file mode 100644
index 000000000..3fc82c04b
--- /dev/null
+++ b/resources/lang/nl/components.php
@@ -0,0 +1,24 @@
+<?php
+return [
+
+    /**
+     * Image Manager
+     */
+    'image_select' => 'Selecteer Afbeelding',
+    'image_all' => 'Alles',
+    'image_all_title' => 'Alle afbeeldingen weergeven',
+    'image_book_title' => 'Afbeeldingen van dit boek weergeven',
+    'image_page_title' => 'Afbeeldingen van deze pagina weergeven',
+    'image_search_hint' => 'Zoek op afbeeldingsnaam',
+    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_load_more' => 'Meer Laden',
+    'image_image_name' => 'Afbeeldingsnaam',
+    'image_delete_confirm' => 'Deze afbeeldingen is op onderstaande pagina\'s in gebruik, Klik opnieuw op verwijderen om de afbeelding echt te verwijderen.',
+    'image_select_image' => 'Kies Afbeelding',
+    'image_dropzone' => 'Sleep afbeeldingen hier of klik hier om te uploaden',
+    'images_deleted' => 'Verwijderde Afbeeldingen',
+    'image_preview' => 'Afbeelding Voorbeeld',
+    'image_upload_success' => 'Afbeelding succesvol geüpload',
+    'image_update_success' => 'Afbeeldingsdetails succesvol verwijderd',
+    'image_delete_success' => 'Afbeelding succesvol verwijderd'
+];
\ No newline at end of file
diff --git a/resources/lang/nl/entities.php b/resources/lang/nl/entities.php
new file mode 100644
index 000000000..9e60e4735
--- /dev/null
+++ b/resources/lang/nl/entities.php
@@ -0,0 +1,226 @@
+<?php
+return [
+
+    /**
+     * Shared
+     */
+    'recently_created' => 'Recent Aangemaakt',
+    'recently_created_pages' => 'Recent Aangemaakte Pagina\'s',
+    'recently_updated_pages' => 'Recent Bijgewerkte Pagina\'s',
+    'recently_created_chapters' => 'Recent Aangemaakte Hoofdstukken',
+    'recently_created_books' => 'Recent Aangemaakte Boeken',
+    'recently_update' => 'Recent Bijgewerkt',
+    'recently_viewed' => 'Recent Bekeken',
+    'recent_activity' => 'Recente Activiteit',
+    'create_now' => 'Maak er zelf één',
+    'revisions' => 'Revisies',
+    'meta_created' => 'Aangemaakt :timeLength',
+    'meta_created_name' => 'Aangemaakt :timeLength by :user',
+    'meta_updated' => ':timeLength Aangepast',
+    'meta_updated_name' => ':timeLength Aangepast door :user',
+    'x_pages' => ':count Pagina\'s',
+    'entity_select' => 'Entity Select',
+    'images' => 'Afbeeldingen',
+    'my_recent_drafts' => 'Mijn Concepten',
+    'my_recently_viewed' => 'Mijn Recent Bekeken',
+    'no_pages_viewed' => 'Je hebt nog niets bekeken',
+    'no_pages_recently_created' => 'Er zijn geen recent aangemaakte pagina\'s',
+    'no_pages_recently_updated' => 'Er zijn geen recente wijzigingen',
+
+    /**
+     * Permissions and restrictions
+     */
+    'permissions' => 'Permissies',
+    'permissions_intro' => 'Als je dit aanzet, dan gelden rol-permissies niet meer voor deze pagina.',
+    'permissions_enable' => 'Custom Permissies Aanzetten',
+    'permissions_save' => 'Permissies Opslaan',
+
+    /**
+     * Search
+     */
+    'search_results' => 'Zoekresultaten',
+    'search_results_page' => 'Pagina Zoekresultaten',
+    'search_results_chapter' => 'Hoofdstuk Zoekresultaten',
+    'search_results_book' => 'Boek Zoekresultaten',
+    'search_clear' => 'Zoekopdracht wissen',
+    'search_view_pages' => 'Bekijk alle gevonden pagina\'s',
+    'search_view_chapters' => 'Bekijk alle gevonden hoofdstukken',
+    'search_view_books' => 'Bekijk alle gevonden boeken',
+    'search_no_pages' => 'Er zijn geen pagina\'s gevonden',
+    'search_for_term' => 'Zoeken op :term',
+    'search_page_for_term' => 'Pagina doorzoeken op :term',
+    'search_chapter_for_term' => 'Hoofdstuk doorzoeken op :term',
+    'search_book_for_term' => 'Boeken doorzoeken op :term',
+
+    /**
+     * Books
+     */
+    'book' => 'Boek',
+    'books' => 'Boeken',
+    'books_empty' => 'Er zijn geen boeken aangemaakt',
+    'books_popular' => 'Populaire Boeken',
+    'books_recent' => 'Recente Boeken',
+    'books_popular_empty' => 'De meest populaire boeken worden hier weergegeven.',
+    'books_create' => 'Nieuw Boek Aanmaken',
+    'books_delete' => 'Boek Verwijderen',
+    'books_delete_named' => 'Verwijder Boek :bookName',
+    'books_delete_explain' => 'Deze actie verwijdert het boek \':bookName\', Alle pagina\'s en hoofdstukken worden verwijderd.',
+    'books_delete_confirmation' => 'Weet je zeker dat je dit boek wilt verwijderen?',
+    'books_edit' => 'Boek Bewerken',
+    'books_edit_named' => 'Bewerkt Boek :bookName',
+    'books_form_book_name' => 'Boek Naam',
+    'books_save' => 'Boek Opslaan',
+    'books_permissions' => 'Boek Permissies',
+    'books_permissions_updated' => 'Boek Permissies Opgeslagen',
+    'books_empty_contents' => 'Er zijn nog een hoofdstukken en pagina\'s voor dit boek gemaakt.',
+    'books_empty_create_page' => 'Pagina Toevoegen',
+    'books_empty_or' => 'of',
+    'books_empty_sort_current_book' => 'Boek sorteren',
+    'books_empty_add_chapter' => 'Hoofdstuk Toevoegen',
+    'books_permissions_active' => 'Boek Permissies Actief',
+    'books_search_this' => 'Zoeken in dit boek',
+    'books_navigation' => 'Boek Navigatie',
+    'books_sort' => 'Inhoud van het boek sorteren',
+    'books_sort_named' => 'Sorteer Boek :bookName',
+    'books_sort_show_other' => 'Bekijk Andere Boeken',
+    'books_sort_save' => 'Nieuwe Order Opslaan',
+
+    /**
+     * Chapters
+     */
+    'chapter' => 'Hoofdstuk',
+    'chapters' => 'Hoofdstukken',
+    'chapters_popular' => 'Populaire Hoofdstukken',
+    'chapters_new' => 'Nieuw Hoofdstuk',
+    'chapters_create' => 'Hoofdstuk Toevoegen',
+    'chapters_delete' => 'Hoofdstuk Verwijderen',
+    'chapters_delete_named' => 'Verwijder Hoofdstuk :chapterName',
+    'chapters_delete_explain' => 'Dit verwijdert het hoofdstuk \':chapterName\', Alle pagina\'s zullen verwijdert worden.
+        en toegevoegd worden aan het bijbehorende boek.',
+    'chapters_delete_confirm' => 'Weet je zeker dat je dit boek wilt verwijderen?',
+    'chapters_edit' => 'Hoofdstuk Aanpassen',
+    'chapters_edit_named' => 'Hoofdstuk :chapterName Aanpassen',
+    'chapters_save' => 'Hoofdstuk Opslaan',
+    'chapters_move' => 'Hoofdstuk Verplaatsen',
+    'chapters_move_named' => 'Verplaatst Hoofdstuk :chapterName',
+    'chapter_move_success' => 'Hoofdstuk Verplaatst Naar :bookName',
+    'chapters_permissions' => 'Hoofdstuk Permissies',
+    'chapters_empty' => 'Er zijn geen pagina\'s in dit hoofdstuk aangemaakt.',
+    'chapters_permissions_active' => 'Hoofdstuk Permissies Actief',
+    'chapters_permissions_success' => 'Hoofdstuk Permissies Bijgewerkt',
+
+    /**
+     * Pages
+     */
+    'page' => 'Pagina',
+    'pages' => 'Pagina\'s',
+    'pages_popular' => 'Populaire Pagina\'s',
+    'pages_new' => 'Nieuwe Pagina',
+    'pages_attachments' => 'Bijlages',
+    'pages_navigation' => 'Pagina Navigatie',
+    'pages_delete' => 'Pagina Verwijderen',
+    'pages_delete_named' => 'Verwijderde Pagina :pageName',
+    'pages_delete_draft_named' => 'Verwijderde Conceptpagina :pageName',
+    'pages_delete_draft' => 'Verwijder Conceptpagina',
+    'pages_delete_success' => 'Pagina verwijderd',
+    'pages_delete_draft_success' => 'Concept verwijderd',
+    'pages_delete_confirm' => 'Weet je zeker dat je deze pagina wilt verwijderen?',
+    'pages_delete_draft_confirm' => 'Weet je zeker dat je dit concept wilt verwijderen?',
+    'pages_editing_named' => 'Pagina :pageName Bewerken',
+    'pages_edit_toggle_header' => 'Toggle header',
+    'pages_edit_save_draft' => 'Concept opslaan',
+    'pages_edit_draft' => 'Paginaconcept Bewerken',
+    'pages_editing_draft' => 'Concept Bewerken',
+    'pages_editing_page' => 'Concept Bewerken',
+    'pages_edit_draft_save_at' => 'Concept opgeslagen op ',
+    'pages_edit_delete_draft' => 'Concept Verwijderen',
+    'pages_edit_discard_draft' => 'Concept Verwijderen',
+    'pages_edit_set_changelog' => 'Changelog',
+    'pages_edit_enter_changelog_desc' => 'Geef een korte omschrijving van de wijzingen die je gemaakt hebt.',
+    'pages_edit_enter_changelog' => 'Enter Changelog',
+    'pages_save' => 'Pagina Opslaan',
+    'pages_title' => 'Pagina Titel',
+    'pages_name' => 'Pagina Naam',
+    'pages_md_editor' => 'Bewerker',
+    'pages_md_preview' => 'Preview',
+    'pages_md_insert_image' => 'Afbeelding Invoegen',
+    'pages_md_insert_link' => 'Entity Link Invoegen',
+    'pages_not_in_chapter' => 'Deze pagina staat niet in een hoofdstuk',
+    'pages_move' => 'Pagina Verplaatsten',
+    'pages_move_success' => 'Pagina verplaatst naar ":parentName"',
+    'pages_permissions' => 'Pagina Permissies',
+    'pages_permissions_success' => 'Pagina Permissies bijgwerkt',
+    'pages_revisions' => 'Pagina Revisies',
+    'pages_revisions_named' => 'Pagina Revisies voor :pageName',
+    'pages_revision_named' => 'Pagina Revisie voor :pageName',
+    'pages_revisions_created_by' => 'Aangemaakt door',
+    'pages_revisions_date' => 'Revisiedatum',
+    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_changes' => 'Wijzigingen',
+    'pages_revisions_current' => 'Huidige Versie',
+    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_restore' => 'Herstellen',
+    'pages_revisions_none' => 'Deze pagina heeft geen revisies',
+    'pages_export' => 'Exporteren',
+    'pages_export_html' => 'Contained Web File',
+    'pages_export_pdf' => 'PDF File',
+    'pages_export_text' => 'Plain Text File',
+    'pages_copy_link' => 'Link Kopiëren',
+    'pages_permissions_active' => 'Pagina Permissies Actief',
+    'pages_initial_revision' => 'Eerste publicatie',
+    'pages_initial_name' => 'Nieuwe Pagina',
+    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
+    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count users have started editing this page',
+        'start_b' => ':userName has started editing this page',
+        'time_a' => 'since the pages was last updated',
+        'time_b' => 'in the last :minCount minutes',
+        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+    ],
+    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
+
+    /**
+     * Editor sidebar
+     */
+    'page_tags' => 'Page Tags',
+    'tag' => 'Tag',
+    'tags' =>  '',
+    'tag_value' => 'Tag Value (Optional)',
+    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
+    'tags_add' => 'Add another tag',
+    'attachments' => 'Attachments',
+    'attachments_explain' => 'Upload some files or attach some link to display on your page. These are visible in the page sidebar.',
+    'attachments_explain_instant_save' => 'Changes here are saved instantly.',
+    'attachments_items' => 'Attached Items',
+    'attachments_upload' => 'Upload File',
+    'attachments_link' => 'Attach Link',
+    'attachments_set_link' => 'Set Link',
+    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_dropzone' => 'Drop files or click here to attach a file',
+    'attachments_no_files' => 'No files have been uploaded',
+    'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
+    'attachments_link_name' => 'Link Name',
+    'attachment_link' => 'Attachment link',
+    'attachments_link_url' => 'Link to file',
+    'attachments_link_url_hint' => 'Url of site or file',
+    'attach' => 'Attach',
+    'attachments_edit_file' => 'Edit File',
+    'attachments_edit_file_name' => 'File Name',
+    'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
+    'attachments_order_updated' => 'Attachment order updated',
+    'attachments_updated_success' => 'Attachment details updated',
+    'attachments_deleted' => 'Attachment deleted',
+    'attachments_file_uploaded' => 'File successfully uploaded',
+    'attachments_file_updated' => 'File successfully updated',
+    'attachments_link_attached' => 'Link successfully attached to page',
+
+    /**
+     * Profile View
+     */
+    'profile_user_for_x' => 'User for :time',
+    'profile_created_content' => 'Created Content',
+    'profile_not_created_pages' => ':userName has not created any pages',
+    'profile_not_created_chapters' => ':userName has not created any chapters',
+    'profile_not_created_books' => ':userName has not created any books',
+];
\ No newline at end of file
diff --git a/resources/lang/nl/errors.php b/resources/lang/nl/errors.php
new file mode 100644
index 000000000..c4578a37a
--- /dev/null
+++ b/resources/lang/nl/errors.php
@@ -0,0 +1,70 @@
+<?php
+
+return [
+
+    /**
+     * Error text strings.
+     */
+
+    // Permissions
+    'permission' => 'You do not have permission to access the requested page.',
+    'permissionJson' => 'You do not have permission to perform the requested action.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
+    'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
+    'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
+    'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
+    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
+    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'social_no_action_defined' => 'No action defined',
+    'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
+    'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.',
+    'social_account_existing' => 'This :socialAccount is already attached to your profile.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
+    'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+
+    // Attachments
+    'attachment_page_mismatch' => 'Page mismatch during attachment update',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+
+    // Error pages
+    '404_page_not_found' => 'Page Not Found',
+    'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
+    'return_home' => 'Return to home',
+    'error_occurred' => 'An Error Occurred',
+    'app_down' => ':appName is down right now',
+    'back_soon' => 'It will be back up soon.',
+];
\ No newline at end of file
diff --git a/resources/lang/nl/pagination.php b/resources/lang/nl/pagination.php
new file mode 100644
index 000000000..9a2a9677a
--- /dev/null
+++ b/resources/lang/nl/pagination.php
@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Pagination Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used by the paginator library to build
+    | the simple pagination links. You are free to change them to anything
+    | you want to customize your views to better match your application.
+    |
+    */
+
+    'previous' => '&laquo; Vorige',
+    'next'     => 'Volgende &raquo;',
+
+];
diff --git a/resources/lang/nl/passwords.php b/resources/lang/nl/passwords.php
new file mode 100644
index 000000000..f89830804
--- /dev/null
+++ b/resources/lang/nl/passwords.php
@@ -0,0 +1,22 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reminder Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are the default lines which match reasons
+    | that are given by the password broker for a password update attempt
+    | has failed, such as for an invalid token or invalid new password.
+    |
+    */
+
+    'password' => 'Wachtwoorden moeten overeenkomen en minimaal zes tekens lang zijn.',
+    'user' => "We kunnen niemand vinden met dat e-mailadres.",
+    'token' => 'De token om het wachtwoord te herstellen is ongeldig.',
+    'sent' => 'We hebben je een link gestuurd om je wachtwoord te herstellen!',
+    'reset' => 'Je wachtwoord is hersteld!',
+
+];
diff --git a/resources/lang/nl/settings.php b/resources/lang/nl/settings.php
new file mode 100644
index 000000000..338f29e43
--- /dev/null
+++ b/resources/lang/nl/settings.php
@@ -0,0 +1,123 @@
+<?php
+
+return [
+
+    /**
+     * Settings text strings
+     * Contains all text strings used in the general settings sections of BookStack
+     * including users and roles.
+     */
+
+    'settings' => 'Instellingen',
+    'settings_save' => 'Instellingen Opslaan',
+    'settings_save_success' => 'Instellingen Opgeslagen',
+
+    /**
+     * App settings
+     */
+
+    'app_settings' => 'App Instellingen',
+    'app_name' => 'Applicatienaam',
+    'app_name_desc' => 'De applicatienaam wordt in e-mails in in de header weergegeven.',
+    'app_name_header' => 'Applicatienaam in de header weergeven?',
+    'app_public_viewing' => 'Publieke bewerkingen toestaan?',
+    'app_secure_images' => 'Beter beveiligide afbeeldingen gebruiken?',
+    'app_secure_images_desc' => 'Omwille van de performance zijn alle afbeeldingen publiek toegankelijk. Zorg ervoor dat je de \'directory index\' niet hebt ingeschakeld.',
+    'app_editor' => 'Pagina Bewerker',
+    'app_editor_desc' => 'Selecteer welke editor je wilt gebruiken.',
+    'app_custom_html' => 'Speciale HTML in de head',
+    'app_custom_html_desc' => 'Alles wat je hier toevoegd wordt in de <head> sectie van elke pagina meengenomen. Dit kun je bijvoorbeeld voor analytics gebruiken.',
+    'app_logo' => 'Applicatielogo',
+    'app_logo_desc' => 'De afbeelding moet 43px hoog zijn. <br>Grotere afbeeldingen worden geschaald.',
+    'app_primary_color' => 'Applicatie hoofdkleur',
+    'app_primary_color_desc' => 'Geef een hexadecimale waarde. <br>Als je niks invult wordt de standaardkleur gebruikt.',
+
+    /**
+     * Registration settings
+     */
+
+    'reg_settings' => 'Registratieinstellingen',
+    'reg_allow' => 'Registratie toestaan?',
+    'reg_default_role' => 'Standaard rol na registratie',
+    'reg_confirm_email' => 'E-mailbevesting vereist?',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and the below value will be ignored.',
+    'reg_confirm_restrict_domain' => 'Restrict registration to domain',
+    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+
+    /**
+     * Role settings
+     */
+
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Edit Role',
+    'role_details' => 'Role Details',
+    'role_name' => 'Role Name',
+    'role_desc' => 'Short Description of Role',
+    'role_system' => 'System Permissions',
+    'role_manage_users' => 'Manage users',
+    'role_manage_roles' => 'Manage roles & role permissions',
+    'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
+    'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
+    'role_manage_settings' => 'Manage app settings',
+    'role_asset' => 'Asset Permissions',
+    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
+    'role_all' => 'All',
+    'role_own' => 'Own',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    /**
+     * Users
+     */
+
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_role' => 'User Roles',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_password_warning' => 'Only fill the below if you would like to change your password:',
+    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+    'users_delete' => 'Delete User',
+    'users_delete_named' => 'Delete user :userName',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_delete_success' => 'Users successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => 'This image should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
+    'users_social_accounts' => 'Social Accounts',
+    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+
+    // Since these labels are already localized this array does not need to be
+    // translated in the language-specific files.
+    // DELETE BELOW IF COPIED FROM EN
+    ///////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'de' => 'Deutsch',
+        'fr' => 'Français',
+        'pt_BR' => 'Português do Brasil'
+    ]
+    ///////////////////////////////////
+];
diff --git a/resources/lang/nl/validation.php b/resources/lang/nl/validation.php
new file mode 100644
index 000000000..b75af7485
--- /dev/null
+++ b/resources/lang/nl/validation.php
@@ -0,0 +1,108 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines contain the default error messages used by
+    | the validator class. Some of these rules have multiple versions such
+    | as the size rules. Feel free to tweak each of these messages here.
+    |
+    */
+
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, and dashes.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'filled'               => 'The :attribute field is required.',
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'string'  => 'The :attribute must be at least :min characters.',
+        'array'   => 'The :attribute must have at least :min items.',
+    ],
+    'not_in'               => 'The selected :attribute is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify custom validation messages for attributes using the
+    | convention "attribute.rule" to name the lines. This makes it quick to
+    | specify a specific custom language line for a given attribute rule.
+    |
+    */
+
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Attributes
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used to swap attribute place-holders
+    | with something more reader friendly such as E-Mail Address instead
+    | of "email". This simply helps us make messages a little cleaner.
+    |
+    */
+
+    'attributes' => [],
+
+];

From 4214fcd2faef7a32d55f663a64f47494c9c94dc7 Mon Sep 17 00:00:00 2001
From: Arie Timmerman <arietimmerman@Aries-MacBook-Pro.local>
Date: Sat, 11 Feb 2017 11:58:45 +0100
Subject: [PATCH 10/27] Updated Dutch language files

---
 resources/lang/nl/activities.php |  22 +++---
 resources/lang/nl/auth.php       |  34 ++++----
 resources/lang/nl/entities.php   |  76 +++++++++---------
 resources/lang/nl/errors.php     |  86 ++++++++++----------
 resources/lang/nl/settings.php   | 130 ++++++++++++++-----------------
 5 files changed, 168 insertions(+), 180 deletions(-)

diff --git a/resources/lang/nl/activities.php b/resources/lang/nl/activities.php
index 815be4898..66a320456 100644
--- a/resources/lang/nl/activities.php
+++ b/resources/lang/nl/activities.php
@@ -8,33 +8,33 @@ return [
      */
 
     // Pages
-    'page_create'                 => 'aangemaakte pagina',
+    'page_create'                 => 'maakte pagina',
     'page_create_notification'    => 'Pagina Succesvol Aangemaakt',
-    'page_update'                 => 'bijgewerkte pagina',
+    'page_update'                 => 'veranderde pagina',
     'page_update_notification'    => 'Pagina Succesvol Bijgewerkt',
     'page_delete'                 => 'verwijderde pagina',
     'page_delete_notification'    => 'Pagina Succesvol Verwijderd',
-    'page_restore'                => 'restored page',
+    'page_restore'                => 'herstelde pagina',
     'page_restore_notification'   => 'Pagina Succesvol Hersteld',
     'page_move'                   => 'verplaatste pagina',
 
     // Chapters
-    'chapter_create'              => 'aangemaakt hoofdstuk',
+    'chapter_create'              => 'maakte hoofdstuk',
     'chapter_create_notification' => 'Hoofdstuk Succesvol Aangemaakt',
-    'chapter_update'              => 'bijgewerkt hoofdstuk',
+    'chapter_update'              => 'veranderde hoofdstuk',
     'chapter_update_notification' => 'Hoofdstuk Succesvol Bijgewerkt',
-    'chapter_delete'              => 'verwijderd hoofdstuk',
+    'chapter_delete'              => 'verwijderde hoofdstuk',
     'chapter_delete_notification' => 'Hoofdstuk Succesvol Verwijderd',
-    'chapter_move'                => 'verplaatst hoofdstuk',
+    'chapter_move'                => 'verplaatste hoofdstuk',
 
     // Books
-    'book_create'                 => 'aangemaakt boek',
+    'book_create'                 => 'maakte boek',
     'book_create_notification'    => 'Boek Succesvol Aangemaakt',
-    'book_update'                 => 'bijgewerkt boek',
+    'book_update'                 => 'veranderde boek',
     'book_update_notification'    => 'Boek Succesvol Bijgewerkt',
-    'book_delete'                 => 'verwijderd boek',
+    'book_delete'                 => 'verwijderde boek',
     'book_delete_notification'    => 'Boek Succesvol Verwijderd',
-    'book_sort'                   => 'gesorteerd book',
+    'book_sort'                   => 'sorteerde boek',
     'book_sort_notification'      => 'Boek Succesvol Gesorteerd',
 
 ];
diff --git a/resources/lang/nl/auth.php b/resources/lang/nl/auth.php
index 82dcf7020..d8813f07b 100644
--- a/resources/lang/nl/auth.php
+++ b/resources/lang/nl/auth.php
@@ -49,28 +49,28 @@ return [
     'reset_password' => 'Wachtwoord Herstellen',
     'reset_password_send_instructions' => 'Geef je e-mail en we sturen je een link om je wachtwoord te herstellen',
     'reset_password_send_button' => 'Link Sturen',
-    'reset_password_sent_success' => 'A password reset link has been sent to :email.',
-    'reset_password_success' => 'Your password has been successfully reset.',
+    'reset_password_sent_success' => 'Een link om je wachtwoord te herstellen is verstuurd naar :email.',
+    'reset_password_success' => 'Je wachtwoord is succesvol hersteld.',
 
-    'email_reset_subject' => 'Reset your :appName password',
-    'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.',
-    'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.',
+    'email_reset_subject' => 'Herstel je wachtwoord van :appName',
+    'email_reset_text' => 'Je ontvangt deze e-mail zodat je je wachtwoord kunt herstellen.',
+    'email_reset_not_requested' => 'Als je jouw wachtwoord niet wilt wijzigen, doe dan niets.',
 
 
     /**
      * Email Confirmation
      */
-    'email_confirm_subject' => 'Confirm your email on :appName',
-    'email_confirm_greeting' => 'Thanks for joining :appName!',
-    'email_confirm_text' => 'Please confirm your email address by clicking the button below:',
-    'email_confirm_action' => 'Confirm Email',
-    'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.',
-    'email_confirm_success' => 'Your email has been confirmed!',
-    'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.',
+    'email_confirm_subject' => 'Bevestig je e-mailadres op :appName',
+    'email_confirm_greeting' => 'Bedankt voor je aanmelding op :appName!',
+    'email_confirm_text' => 'Bevestig je registratie door op onderstaande knop te drukken:',
+    'email_confirm_action' => 'Bevestig je e-mail',
+    'email_confirm_send_error' => 'E-mail bevestiging is vereisd maar het systeem kon geen mail verzenden. Neem contact op met de beheerder.',
+    'email_confirm_success' => 'Je e-mailadres is bevestigt!',
+    'email_confirm_resent' => 'De bevestigingse-mails is opnieuw verzonden. Controleer je inbox.',
 
-    'email_not_confirmed' => 'Email Address Not Confirmed',
-    'email_not_confirmed_text' => 'Your email address has not yet been confirmed.',
-    'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.',
-    'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.',
-    'email_not_confirmed_resend_button' => 'Resend Confirmation Email',
+    'email_not_confirmed' => 'E-mail nog niet bevestigd',
+    'email_not_confirmed_text' => 'Je e-mailadres is nog niet bevestigd.',
+    'email_not_confirmed_click_link' => 'Klik op de link in de e-mail die vlak na je registratie is verstuurd.',
+    'email_not_confirmed_resend' => 'Als je deze e-mail niet kunt vinden kun je deze met onderstaande formulier opnieuw verzenden.',
+    'email_not_confirmed_resend_button' => 'Bevestigingsmail Opnieuw Verzenden',
 ];
\ No newline at end of file
diff --git a/resources/lang/nl/entities.php b/resources/lang/nl/entities.php
index 9e60e4735..46e69549b 100644
--- a/resources/lang/nl/entities.php
+++ b/resources/lang/nl/entities.php
@@ -15,11 +15,11 @@ return [
     'create_now' => 'Maak er zelf één',
     'revisions' => 'Revisies',
     'meta_created' => 'Aangemaakt :timeLength',
-    'meta_created_name' => 'Aangemaakt :timeLength by :user',
+    'meta_created_name' => 'Aangemaakt: :timeLength door :user',
     'meta_updated' => ':timeLength Aangepast',
-    'meta_updated_name' => ':timeLength Aangepast door :user',
+    'meta_updated_name' => 'Aangepast: :timeLength door :user',
     'x_pages' => ':count Pagina\'s',
-    'entity_select' => 'Entity Select',
+    'entity_select' => 'Entiteit Selecteren',
     'images' => 'Afbeeldingen',
     'my_recent_drafts' => 'Mijn Concepten',
     'my_recently_viewed' => 'Mijn Recent Bekeken',
@@ -183,44 +183,44 @@ return [
     /**
      * Editor sidebar
      */
-    'page_tags' => 'Page Tags',
-    'tag' => 'Tag',
+    'page_tags' => 'Pagina Labels',
+    'tag' => 'Label',
     'tags' =>  '',
-    'tag_value' => 'Tag Value (Optional)',
-    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
-    'tags_add' => 'Add another tag',
-    'attachments' => 'Attachments',
-    'attachments_explain' => 'Upload some files or attach some link to display on your page. These are visible in the page sidebar.',
-    'attachments_explain_instant_save' => 'Changes here are saved instantly.',
-    'attachments_items' => 'Attached Items',
-    'attachments_upload' => 'Upload File',
-    'attachments_link' => 'Attach Link',
-    'attachments_set_link' => 'Set Link',
-    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
-    'attachments_dropzone' => 'Drop files or click here to attach a file',
-    'attachments_no_files' => 'No files have been uploaded',
-    'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
-    'attachments_link_name' => 'Link Name',
-    'attachment_link' => 'Attachment link',
-    'attachments_link_url' => 'Link to file',
-    'attachments_link_url_hint' => 'Url of site or file',
-    'attach' => 'Attach',
-    'attachments_edit_file' => 'Edit File',
-    'attachments_edit_file_name' => 'File Name',
-    'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
-    'attachments_order_updated' => 'Attachment order updated',
-    'attachments_updated_success' => 'Attachment details updated',
-    'attachments_deleted' => 'Attachment deleted',
-    'attachments_file_uploaded' => 'File successfully uploaded',
-    'attachments_file_updated' => 'File successfully updated',
-    'attachments_link_attached' => 'Link successfully attached to page',
+    'tag_value' => 'Label Waarde (Optioneel)',
+    'tags_explain' => "Voeg labels toe om de inhoud te categoriseren. \n Je kunt meerdere labels toevoegen.",
+    'tags_add' => 'Voeg een extra label toe',
+    'attachments' => 'Bijlages',
+    'attachments_explain' => 'Upload bijlages of voeg een link toe. Deze worden zichtbaar in het navigatiepaneel.',
+    'attachments_explain_instant_save' => 'Wijzigingen worden meteen opgeslagen.',
+    'attachments_items' => 'Bijlages',
+    'attachments_upload' => 'Bestand Uploaden',
+    'attachments_link' => 'Link Toevoegen',
+    'attachments_set_link' => 'Zet Link',
+    'attachments_delete_confirm' => 'Klik opnieuw op \'verwijderen\' om de bijlage definitief te verwijderen.',
+    'attachments_dropzone' => 'Sleep hier een bestand of klik hier om een bestand toe te voegen',
+    'attachments_no_files' => 'Er zijn geen bestanden geüpload',
+    'attachments_explain_link' => 'Je kunt een link toevoegen als je geen bestanden wilt uploaden. Dit kan een link naar een andere pagina op deze website zijn, maar ook een link naar een andere website.',
+    'attachments_link_name' => 'Link Naam',
+    'attachment_link' => 'Bijlage link',
+    'attachments_link_url' => 'Link naar bestand',
+    'attachments_link_url_hint' => 'Url, site of bestand',
+    'attach' => 'Koppelen',
+    'attachments_edit_file' => 'Bestand Bewerken',
+    'attachments_edit_file_name' => 'Bestandsnaam',
+    'attachments_edit_drop_upload' => 'Sleep een bestand of klik hier om te uploaden en te overschrijven',
+    'attachments_order_updated' => 'De volgorde van de bijlages is bijgewerkt',
+    'attachments_updated_success' => 'Bijlage details bijgewerkt',
+    'attachments_deleted' => 'Bijlage verwijderd',
+    'attachments_file_uploaded' => 'Bestand succesvol geüpload',
+    'attachments_file_updated' => 'Bestand succesvol bijgewerkt',
+    'attachments_link_attached' => 'Link successfully gekoppeld aan de pagina',
 
     /**
      * Profile View
      */
-    'profile_user_for_x' => 'User for :time',
-    'profile_created_content' => 'Created Content',
-    'profile_not_created_pages' => ':userName has not created any pages',
-    'profile_not_created_chapters' => ':userName has not created any chapters',
-    'profile_not_created_books' => ':userName has not created any books',
+    'profile_user_for_x' => 'Lid sinds :time',
+    'profile_created_content' => 'Aangemaakte Inhoud',
+    'profile_not_created_pages' => ':userName heeft geen pagina\'s gemaakt',
+    'profile_not_created_chapters' => ':userName heeft geen hoofdstukken gemaakt',
+    'profile_not_created_books' => ':userName heeft geen boeken gemaakt',
 ];
\ No newline at end of file
diff --git a/resources/lang/nl/errors.php b/resources/lang/nl/errors.php
index c4578a37a..f8b635bce 100644
--- a/resources/lang/nl/errors.php
+++ b/resources/lang/nl/errors.php
@@ -7,64 +7,64 @@ return [
      */
 
     // Permissions
-    'permission' => 'You do not have permission to access the requested page.',
-    'permissionJson' => 'You do not have permission to perform the requested action.',
+    'permission' => 'Je hebt onvoldoende rechten om deze pagina te zien.',
+    'permissionJson' => 'Je hebt onvoldoende rechten voor deze actie.',
 
     // Auth
-    'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
-    'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
-    'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
-    'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
-    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
-    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'error_user_exists_different_creds' => 'Een gebruiker met het e-mailadres :email bestaat al.',
+    'email_already_confirmed' => 'Het e-mailadres is al bevestigd. Probeer in te loggen.',
+    'email_confirmation_invalid' => 'Deze bevestigingstoken is ongeldig, Probeer opnieuw te registreren.',
+    'email_confirmation_expired' => 'De bevestigingstoken is verlopen, Een nieuwe bevestigingsmail is verzonden.',
+    'ldap_fail_anonymous' => 'LDAP toegang kon geen \'anonymous bind\' uitvoeren',
+    'ldap_fail_authed' => 'LDAP toegang was niet mogelijk met de opgegeven dn & wachtwoord',
     'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
-    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
-    'social_no_action_defined' => 'No action defined',
-    'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
-    'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.',
-    'social_account_existing' => 'This :socialAccount is already attached to your profile.',
-    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
-    'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
-    'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
-    'social_driver_not_found' => 'Social driver not found',
-    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+    'ldap_cannot_connect' => 'Kon niet met de LDAP server verbinden',
+    'social_no_action_defined' => 'Geen actie gedefineerd',
+    'social_account_in_use' => 'Dit :socialAccount account is al in gebruik, Probeer in te loggen met de :socialAccount optie.',
+    'social_account_email_in_use' => 'Het e-mailadres :email is al in gebruik. Als je al een account hebt kun je een :socialAccount account verbinden met je profielinstellingen.',
+    'social_account_existing' => 'Dit :socialAccount is al gekoppeld aan een profiel.',
+    'social_account_already_used_existing' => 'Dit :socialAccount account is ingebruik door een andere gebruiker.',
+    'social_account_not_used' => 'Dit :socialAccount account is niet gekopeld aan een gebruiker. Koppel het via je profielinstellingen. ',
+    'social_account_register_instructions' => 'Als je nog geen account hebt kun je je registreren met de :socialAccount optie.',
+    'social_driver_not_found' => 'Social driver niet gevonden',
+    'social_driver_not_configured' => 'Je :socialAccount instellingen zijn correct geconfigureerd.',
 
     // System
-    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
-    'cannot_get_image_from_url' => 'Cannot get image from :url',
-    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
-    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
-    'image_upload_error' => 'An error occurred uploading the image',
+    'path_not_writable' => 'Bestand :filePath kon niet geupload worden. Zorg dat je schrijfrechten op de server hebt.',
+    'cannot_get_image_from_url' => 'Kon geen afbeelding genereren van :url',
+    'cannot_create_thumbs' => 'De server kon geen thumbnails maken. Controleer of je de GD PHP extensie geïnstalleerd hebt.',
+    'server_upload_limit' => 'Het afbeeldingsformaat is te groot. Probeer een kleinere bestandsgrootte.',
+    'image_upload_error' => 'Er ging iets fout bij het uploaden van de afbeelding',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
+    'attachment_page_mismatch' => 'Bij het bijwerken van de bijlage bleek de pagina onjuist',
 
     // Pages
-    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+    'page_draft_autosave_fail' => 'Kon het concept niet opslaan. Zorg ervoor dat je een werkende internetverbinding hebt.',
 
     // Entities
-    'entity_not_found' => 'Entity not found',
-    'book_not_found' => 'Book not found',
-    'page_not_found' => 'Page not found',
-    'chapter_not_found' => 'Chapter not found',
-    'selected_book_not_found' => 'The selected book was not found',
-    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
-    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+    'entity_not_found' => 'Entiteit niet gevonden',
+    'book_not_found' => 'Boek niet gevonden',
+    'page_not_found' => 'Pagina niet gevonden',
+    'chapter_not_found' => 'Hoofdstuk niet gevonden',
+    'selected_book_not_found' => 'Het geselecteerde boek is niet gevonden',
+    'selected_book_chapter_not_found' => 'Het geselecteerde boek of hoofdstuk is niet gevonden',
+    'guests_cannot_save_drafts' => 'Gasten kunnen geen concepten opslaan',
 
     // Users
-    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
-    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+    'users_cannot_delete_only_admin' => 'Je kunt niet het enige admin account verwijderen',
+    'users_cannot_delete_guest' => 'Je kunt het gastaccount niet verwijderen',
 
     // Roles
-    'role_cannot_be_edited' => 'This role cannot be edited',
-    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
-    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+    'role_cannot_be_edited' => 'Deze rol kan niet bewerkt worden',
+    'role_system_cannot_be_deleted' => 'Dit is een systeemrol en kan niet verwijderd worden',
+    'role_registration_default_cannot_delete' => 'Deze rol kan niet verwijerd worden zolang dit de standaardrol na registratie is.',
 
     // Error pages
-    '404_page_not_found' => 'Page Not Found',
-    'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
-    'return_home' => 'Return to home',
-    'error_occurred' => 'An Error Occurred',
-    'app_down' => ':appName is down right now',
-    'back_soon' => 'It will be back up soon.',
+    '404_page_not_found' => 'Pagina Niet Gevonden',
+    'sorry_page_not_found' => 'Sorry, de pagina die je zocht is niet beschikbaar.',
+    'return_home' => 'Terug naar home',
+    'error_occurred' => 'Er Ging Iets Fout',
+    'app_down' => ':appName is nu niet beschikbaar',
+    'back_soon' => 'Komt snel weer online.',
 ];
\ No newline at end of file
diff --git a/resources/lang/nl/settings.php b/resources/lang/nl/settings.php
index 338f29e43..7408a2dc3 100644
--- a/resources/lang/nl/settings.php
+++ b/resources/lang/nl/settings.php
@@ -23,9 +23,9 @@ return [
     'app_public_viewing' => 'Publieke bewerkingen toestaan?',
     'app_secure_images' => 'Beter beveiligide afbeeldingen gebruiken?',
     'app_secure_images_desc' => 'Omwille van de performance zijn alle afbeeldingen publiek toegankelijk. Zorg ervoor dat je de \'directory index\' niet hebt ingeschakeld.',
-    'app_editor' => 'Pagina Bewerker',
-    'app_editor_desc' => 'Selecteer welke editor je wilt gebruiken.',
-    'app_custom_html' => 'Speciale HTML in de head',
+    'app_editor' => 'Pagina Bewerken',
+    'app_editor_desc' => 'Selecteer welke tekstverwerker je wilt gebruiken.',
+    'app_custom_html' => 'Speciale HTML toevoegen',
     'app_custom_html_desc' => 'Alles wat je hier toevoegd wordt in de <head> sectie van elke pagina meengenomen. Dit kun je bijvoorbeeld voor analytics gebruiken.',
     'app_logo' => 'Applicatielogo',
     'app_logo_desc' => 'De afbeelding moet 43px hoog zijn. <br>Grotere afbeeldingen worden geschaald.',
@@ -40,84 +40,72 @@ return [
     'reg_allow' => 'Registratie toestaan?',
     'reg_default_role' => 'Standaard rol na registratie',
     'reg_confirm_email' => 'E-mailbevesting vereist?',
-    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and the below value will be ignored.',
-    'reg_confirm_restrict_domain' => 'Restrict registration to domain',
-    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
-    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+    'reg_confirm_email_desc' => 'Als domeinrestricties aan staan dan is altijd e-maibevestiging nodig. Onderstaande instelling wordt dan genegeerd.',
+    'reg_confirm_restrict_domain' => 'Beperk registratie tot een maildomein',
+    'reg_confirm_restrict_domain_desc' => 'Geen een komma-gescheiden lijst van domeinnamen die gebruikt mogen worden bij registratie. <br> Let op: na registratie kunnen gebruikers hun e-mailadres nog steeds wijzigen.',
+    'reg_confirm_restrict_domain_placeholder' => 'Geen beperkingen ingesteld',
 
     /**
      * Role settings
      */
 
-    'roles' => 'Roles',
-    'role_user_roles' => 'User Roles',
-    'role_create' => 'Create New Role',
-    'role_create_success' => 'Role successfully created',
-    'role_delete' => 'Delete Role',
-    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
-    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
-    'role_delete_no_migration' => "Don't migrate users",
-    'role_delete_sure' => 'Are you sure you want to delete this role?',
-    'role_delete_success' => 'Role successfully deleted',
-    'role_edit' => 'Edit Role',
-    'role_details' => 'Role Details',
-    'role_name' => 'Role Name',
-    'role_desc' => 'Short Description of Role',
-    'role_system' => 'System Permissions',
-    'role_manage_users' => 'Manage users',
-    'role_manage_roles' => 'Manage roles & role permissions',
-    'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
-    'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
-    'role_manage_settings' => 'Manage app settings',
-    'role_asset' => 'Asset Permissions',
-    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
-    'role_all' => 'All',
-    'role_own' => 'Own',
-    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
-    'role_save' => 'Save Role',
-    'role_update_success' => 'Role successfully updated',
-    'role_users' => 'Users in this role',
-    'role_users_none' => 'No users are currently assigned to this role',
+    'roles' => 'Rollen',
+    'role_user_roles' => 'Gebruikrollen',
+    'role_create' => 'Nieuwe Rol Maken',
+    'role_create_success' => 'Rol succesvol aangemaakt',
+    'role_delete' => 'Rol Verwijderen',
+    'role_delete_confirm' => 'Dit verwijdert de rol \':roleName\'.',
+    'role_delete_users_assigned' => 'Er zijn :userCount gebruikers met deze rol. Selecteer hieronder een nieuwe rol als je deze gebruikers een andere rol wilt geven.',
+    'role_delete_no_migration' => "Geen gebruikers migreren",
+    'role_delete_sure' => 'Weet je zeker dat je deze rol wilt verwijderen?',
+    'role_delete_success' => 'Rol succesvol verwijderd',
+    'role_edit' => 'Rol Bewerken',
+    'role_details' => 'Rol Details',
+    'role_name' => 'Rolnaam',
+    'role_desc' => 'Korte beschrijving van de rol',
+    'role_system' => 'Systeem Permissies',
+    'role_manage_users' => 'Gebruikers beheren',
+    'role_manage_roles' => 'Rollen en rechten beheren',
+    'role_manage_entity_permissions' => 'Beheer alle boeken-, hoofdstukken- en paginaresitrcties',
+    'role_manage_own_entity_permissions' => 'Beheer restricties van je eigen boeken, hoofdstukken en pagina\'s',
+    'role_manage_settings' => 'Beheer app instellingen',
+    'role_asset' => 'Asset Permissies',
+    'role_asset_desc' => 'Deze permissies bepalen de standaardtoegangsrechten. Permissies op boeken, hoofdstukken en pagina\'s overschrijven deze instelling.',
+    'role_all' => 'Alles',
+    'role_own' => 'Eigen',
+    'role_controlled_by_asset' => 'Gecontroleerd door de asset waar deze is geüpload',
+    'role_save' => 'Rol Opslaan',
+    'role_update_success' => 'Rol succesvol bijgewerkt',
+    'role_users' => 'Gebruikers in deze rol',
+    'role_users_none' => 'Geen enkele gebruiker heeft deze rol',
 
     /**
      * Users
      */
 
-    'users' => 'Users',
-    'user_profile' => 'User Profile',
-    'users_add_new' => 'Add New User',
-    'users_search' => 'Search Users',
-    'users_role' => 'User Roles',
+    'users' => 'Gebruikers',
+    'user_profile' => 'Gebruikersprofiel',
+    'users_add_new' => 'Gebruiker toevoegen',
+    'users_search' => 'Gebruiker zoeken',
+    'users_role' => 'Gebruikersrollen',
     'users_external_auth_id' => 'External Authentication ID',
-    'users_password_warning' => 'Only fill the below if you would like to change your password:',
-    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
-    'users_delete' => 'Delete User',
-    'users_delete_named' => 'Delete user :userName',
-    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
-    'users_delete_confirm' => 'Are you sure you want to delete this user?',
-    'users_delete_success' => 'Users successfully removed',
-    'users_edit' => 'Edit User',
-    'users_edit_profile' => 'Edit Profile',
-    'users_edit_success' => 'User successfully updated',
-    'users_avatar' => 'User Avatar',
-    'users_avatar_desc' => 'This image should be approx 256px square.',
-    'users_preferred_language' => 'Preferred Language',
+    'users_password_warning' => 'Vul onderstaande formulier alleen in als je het wachtwoord wilt aanpassen:',
+    'users_system_public' => 'De eigenschappen van deze gebruiker worden voor elke gastbezoeker gebruikt. Er kan niet mee ingelogd worden en wordt automatisch toegewezen.',
+    'users_delete' => 'Verwijder gebruiker',
+    'users_delete_named' => 'Verwijder gebruiker :userName',
+    'users_delete_warning' => 'Dit zal de gebruiker \':userName\' volledig uit het systeem verwijderen.',
+    'users_delete_confirm' => 'Weet je zeker dat je deze gebruiker wilt verwijderen?',
+    'users_delete_success' => 'Gebruiker succesvol verwijderd',
+    'users_edit' => 'Bewerk Gebruiker',
+    'users_edit_profile' => 'Bewerk Profiel',
+    'users_edit_success' => 'Gebruiker succesvol bijgewerkt',
+    'users_avatar' => 'Avatar',
+    'users_avatar_desc' => 'De afbeelding moet vierkant zijn en ongeveer 256px breed.',
+    'users_preferred_language' => 'Voorkeurstaal',
     'users_social_accounts' => 'Social Accounts',
-    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
-    'users_social_connect' => 'Connect Account',
-    'users_social_disconnect' => 'Disconnect Account',
-    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
-    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
-
-    // Since these labels are already localized this array does not need to be
-    // translated in the language-specific files.
-    // DELETE BELOW IF COPIED FROM EN
-    ///////////////////////////////////
-    'language_select' => [
-        'en' => 'English',
-        'de' => 'Deutsch',
-        'fr' => 'Français',
-        'pt_BR' => 'Português do Brasil'
-    ]
-    ///////////////////////////////////
+    'users_social_accounts_info' => 'Hier kun je accounts verbinden om makkelijker in te loggen. Via je profiel kun je ook weer rechten intrekken die bij deze social accountsh horen.',
+    'users_social_connect' => 'Account Verbinden',
+    'users_social_disconnect' => 'Account Ontkoppelen',
+    'users_social_connected' => ':socialAccount account is succesvol aan je profiel gekoppeld.',
+    'users_social_disconnected' => ':socialAccount account is succesvol ontkoppeld van je profiel.',
 ];

From 387047f26207e97ab05c315e65bcc961ea71880a Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 25 Feb 2017 12:29:01 +0000
Subject: [PATCH 11/27] Fixed inaccessible revisions, added regression tests

Fixes #309
---
 app/Http/Controllers/PageController.php | 12 +++++--
 app/Repos/EntityRepo.php                |  3 +-
 tests/Entity/PageContentTest.php        | 45 +++++++++++++++++++------
 tests/PublicActionTest.php              |  2 +-
 tests/TestCase.php                      | 23 +++++++++++++
 5 files changed, 68 insertions(+), 17 deletions(-)

diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index 4ed10d61e..623cb9c4d 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -369,10 +369,13 @@ class PageController extends Controller
     public function showRevision($bookSlug, $pageSlug, $revisionId)
     {
         $page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
-        $revision = $this->entityRepo->getById('page_revision', $revisionId, false);
+        $revision = $page->revisions()->where('id', '=', $revisionId)->first();
+        if ($revision === null) {
+            abort(404);
+        }
 
         $page->fill($revision->toArray());
-        $this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
+        $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
         
         return view('pages/revision', [
             'page' => $page,
@@ -390,7 +393,10 @@ class PageController extends Controller
     public function showRevisionChanges($bookSlug, $pageSlug, $revisionId)
     {
         $page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
-        $revision = $this->entityRepo->getById('page_revision', $revisionId);
+        $revision = $page->revisions()->where('id', '=', $revisionId)->first();
+        if ($revision === null) {
+            abort(404);
+        }
 
         $prev = $revision->getPrevious();
         $prevContent = ($prev === null) ? '' : $prev->html;
diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php
index 8a8740d76..7b262c3de 100644
--- a/app/Repos/EntityRepo.php
+++ b/app/Repos/EntityRepo.php
@@ -86,8 +86,7 @@ class EntityRepo
         $this->entities = [
             'page' => $this->page,
             'chapter' => $this->chapter,
-            'book' => $this->book,
-            'page_revision' => $this->pageRevision
+            'book' => $this->book
         ];
         $this->viewService = $viewService;
         $this->permissionService = $permissionService;
diff --git a/tests/Entity/PageContentTest.php b/tests/Entity/PageContentTest.php
index 4f65d3406..6f07b9626 100644
--- a/tests/Entity/PageContentTest.php
+++ b/tests/Entity/PageContentTest.php
@@ -1,33 +1,56 @@
 <?php namespace Tests;
 
-class PageContentTest extends BrowserKitTest
+use BookStack\Page;
+use BookStack\Repos\EntityRepo;
+
+class PageContentTest extends TestCase
 {
 
     public function test_page_includes()
     {
-        $page = \BookStack\Page::first();
-        $secondPage = \BookStack\Page::all()->get(2);
+        $page = Page::first();
+        $secondPage = Page::all()->get(2);
 
         $secondPage->html = "<p id='section1'>Hello, This is a test</p><p id='section2'>This is a second block of content</p>";
         $secondPage->save();
 
-        $this->asAdmin()->visit($page->getUrl())
-            ->dontSee('Hello, This is a test');
+        $this->asEditor();
+
+        $pageContent = $this->get($page->getUrl());
+        $pageContent->assertDontSee('Hello, This is a test');
 
         $originalHtml = $page->html;
         $page->html .= "{{@{$secondPage->id}}}";
         $page->save();
 
-        $this->asAdmin()->visit($page->getUrl())
-            ->see('Hello, This is a test')
-            ->see('This is a second block of content');
+        $pageContent = $this->get($page->getUrl());
+        $pageContent->assertSee('Hello, This is a test');
+        $pageContent->assertSee('This is a second block of content');
 
         $page->html = $originalHtml . " Well {{@{$secondPage->id}#section2}}";
         $page->save();
 
-        $this->asAdmin()->visit($page->getUrl())
-            ->dontSee('Hello, This is a test')
-            ->see('Well This is a second block of content');
+        $pageContent = $this->get($page->getUrl());
+        $pageContent->assertDontSee('Hello, This is a test');
+        $pageContent->assertSee('Well This is a second block of content');
+    }
+
+    public function test_page_revision_views_viewable()
+    {
+        $this->asEditor();
+
+        $entityRepo = $this->app[EntityRepo::class];
+        $page = Page::first();
+        $entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
+        $pageRevision = $page->revisions->last();
+
+        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
+        $revisionView->assertStatus(200);
+        $revisionView->assertSee('new content');
+
+        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id . '/changes');
+        $revisionView->assertStatus(200);
+        $revisionView->assertSee('new content');
     }
 
 }
diff --git a/tests/PublicActionTest.php b/tests/PublicActionTest.php
index 62a321b5e..6f8590d4b 100644
--- a/tests/PublicActionTest.php
+++ b/tests/PublicActionTest.php
@@ -84,7 +84,7 @@ class PublicActionTest extends BrowserKitTest
     {
         $page = \BookStack\Page::first();
         $this->asAdmin()->visit($page->getUrl());
-        Auth::logout();
+        \Auth::logout();
         view()->share('pageTitle', '');
         $this->forceVisit('/cats/dogs/hippos');
         $this->dontSee($page->name);
diff --git a/tests/TestCase.php b/tests/TestCase.php
index d64aef3db..f3f36ca1c 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -13,6 +13,7 @@ abstract class TestCase extends BaseTestCase
     use DatabaseTransactions;
 
     protected $admin;
+    protected $editor;
 
     /**
      * Set the current user context to be an admin.
@@ -35,6 +36,28 @@ abstract class TestCase extends BaseTestCase
         return $this->admin;
     }
 
+    /**
+     * Set the current user context to be an editor.
+     * @return $this
+     */
+    public function asEditor()
+    {
+        return $this->actingAs($this->getEditor());
+    }
+
+
+    /**
+     * Get a editor user.
+     * @return mixed
+     */
+    public function getEditor() {
+        if($this->editor === null) {
+            $editorRole = Role::getRole('editor');
+            $this->editor = $editorRole->users->first();
+        }
+        return $this->editor;
+    }
+
     /**
      * Create and return a new book.
      * @param array $input

From af3c0e43a59483511ce0271bdbd43b5845d3d1fa Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 25 Feb 2017 12:41:32 +0000
Subject: [PATCH 12/27] Prevented custom HTML being inserted on settings page

Gives option for fixing if badly formatted HTML is inserted.
Closes #310
---
 resources/views/base.blade.php | 5 +++--
 routes/web.php                 | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php
index a98a37131..bb00ce19e 100644
--- a/resources/views/base.blade.php
+++ b/resources/views/base.blade.php
@@ -23,9 +23,10 @@
 
     @include('partials/custom-styles')
 
-    <!-- Custom user content -->
-    @if(setting('app-custom-head'))
+    @if(setting('app-custom-head') && \Route::currentRouteName() !== 'settings')
+        <!-- Custom user content -->
         {!! setting('app-custom-head') !!}
+        <!-- End custom user content -->
     @endif
 </head>
 <body class="@yield('body-class')" ng-app="bookStack">
diff --git a/routes/web.php b/routes/web.php
index 076ffb94f..8d166f1d6 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -129,7 +129,7 @@ Route::group(['middleware' => 'auth'], function () {
 
     // Settings
     Route::group(['prefix' => 'settings'], function() {
-        Route::get('/', 'SettingController@index');
+        Route::get('/', 'SettingController@index')->name('settings');
         Route::post('/', 'SettingController@update');
 
         // Users

From b0e849f4137772a424be55d022324cfd7761d3ff Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 25 Feb 2017 13:16:26 +0000
Subject: [PATCH 13/27] Added checkbox sytax parsing to markdown lists

Closes #319
---
 resources/assets/js/directives.js | 17 +++++++++++++++--
 resources/assets/sass/_text.scss  |  8 ++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/resources/assets/js/directives.js b/resources/assets/js/directives.js
index ef8bcd85c..10458e753 100644
--- a/resources/assets/js/directives.js
+++ b/resources/assets/js/directives.js
@@ -214,6 +214,19 @@ export default function (ngApp, events) {
         }
     }]);
 
+    let renderer = new markdown.Renderer();
+    // Custom markdown checkbox list item
+    // Attribution: https://github.com/chjj/marked/issues/107#issuecomment-44542001
+    renderer.listitem = function(text) {
+        if (/^\s*\[[x ]\]\s*/.test(text)) {
+            text = text
+                .replace(/^\s*\[ \]\s*/, '<input type="checkbox"/>')
+                .replace(/^\s*\[x\]\s*/, '<input type="checkbox" checked/>');
+            return `<li class="checkbox-item">${text}</li>`;
+        }
+        return `<li>${text}</li>`;
+    };
+
     /**
      * Markdown input
      * Handles the logic for just the editor input field.
@@ -231,13 +244,13 @@ export default function (ngApp, events) {
                 element = element.find('textarea').first();
                 let content = element.val();
                 scope.mdModel = content;
-                scope.mdChange(markdown(content));
+                scope.mdChange(markdown(content, {renderer: renderer}));
 
                 element.on('change input', (event) => {
                     content = element.val();
                     $timeout(() => {
                         scope.mdModel = content;
-                        scope.mdChange(markdown(content));
+                        scope.mdChange(markdown(content, {renderer: renderer}));
                     });
                 });
 
diff --git a/resources/assets/sass/_text.scss b/resources/assets/sass/_text.scss
index 79d3c3a92..a74a81647 100644
--- a/resources/assets/sass/_text.scss
+++ b/resources/assets/sass/_text.scss
@@ -281,6 +281,14 @@ ol {
   overflow: hidden;
 }
 
+li.checkbox-item {
+  list-style: none;
+  margin-left: - ($-m * 1.3);
+  input[type="checkbox"] {
+    margin-right: $-xs;
+  }
+}
+
 /*
  * Generic text styling classes
  */

From 22077d4181e6dfb84628f076f28b0cdce08d489c Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 25 Feb 2017 14:59:56 +0000
Subject: [PATCH 14/27] Updated DOMPDF to latest version

---
 app/Services/ExportService.php               |   1 -
 composer.json                                |   2 +-
 composer.lock                                | 364 +++++++++++--------
 config/dompdf.php                            |   8 +-
 resources/assets/sass/export-styles.scss     |   2 +-
 resources/views/pages/page-display.blade.php |   2 +-
 resources/views/pages/pdf.blade.php          |   6 -
 7 files changed, 213 insertions(+), 172 deletions(-)

diff --git a/app/Services/ExportService.php b/app/Services/ExportService.php
index 880bc54ad..e51577a22 100644
--- a/app/Services/ExportService.php
+++ b/app/Services/ExportService.php
@@ -39,7 +39,6 @@ class ExportService
     {
         $cssContent = file_get_contents(public_path('/css/export-styles.css'));
         $pageHtml = view('pages/pdf', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render();
-//        return $pageHtml;
         $useWKHTML = config('snappy.pdf.binary') !== false;
         $containedHtml = $this->containHtml($pageHtml);
         if ($useWKHTML) {
diff --git a/composer.json b/composer.json
index 5755afffe..7d820b3f8 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,7 @@
         "barryvdh/laravel-ide-helper": "^2.2.3",
         "barryvdh/laravel-debugbar": "^2.3.2",
         "league/flysystem-aws-s3-v3": "^1.0",
-        "barryvdh/laravel-dompdf": "^0.7",
+        "barryvdh/laravel-dompdf": "^0.8",
         "predis/predis": "^1.1",
         "gathercontent/htmldiff": "^0.2.1",
         "barryvdh/laravel-snappy": "^0.3.1",
diff --git a/composer.lock b/composer.lock
index df7b80289..54218ee48 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,21 +4,21 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "625ae196ac4c457c3aaff34235acbc4d",
-    "content-hash": "e851e9fd06efac8362604c39b0a17542",
+    "hash": "e6d32752d02dae662bedc69fa5856feb",
+    "content-hash": "5f0f4e912f1207e761caf9344f2308a0",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.21.6",
+            "version": "3.22.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "b51512a4ad4aa080ab963942a1e234265771fcde"
+                "reference": "277939bd789204b314f3aaca06976cf3ddf78f07"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b51512a4ad4aa080ab963942a1e234265771fcde",
-                "reference": "b51512a4ad4aa080ab963942a1e234265771fcde",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/277939bd789204b314f3aaca06976cf3ddf78f07",
+                "reference": "277939bd789204b314f3aaca06976cf3ddf78f07",
                 "shasum": ""
             },
             "require": {
@@ -85,7 +85,7 @@
                 "s3",
                 "sdk"
             ],
-            "time": "2017-01-27 00:34:55"
+            "time": "2017-02-24 21:47:48"
         },
         {
             "name": "barryvdh/laravel-debugbar",
@@ -143,27 +143,27 @@
         },
         {
             "name": "barryvdh/laravel-dompdf",
-            "version": "v0.7.1",
+            "version": "v0.8.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-dompdf.git",
-                "reference": "6cb20d3f397d739163d2d743a4600009e52cd453"
+                "reference": "22ee9cb8e0ac0d5f11633d1194280ab40b2bba1f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/6cb20d3f397d739163d2d743a4600009e52cd453",
-                "reference": "6cb20d3f397d739163d2d743a4600009e52cd453",
+                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/22ee9cb8e0ac0d5f11633d1194280ab40b2bba1f",
+                "reference": "22ee9cb8e0ac0d5f11633d1194280ab40b2bba1f",
                 "shasum": ""
             },
             "require": {
-                "dompdf/dompdf": "^0.7",
+                "dompdf/dompdf": "^0.8",
                 "illuminate/support": "5.1.x|5.2.x|5.3.x|5.4.x",
                 "php": ">=5.5.9"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "0.7-dev"
+                    "dev-master": "0.8-dev"
                 }
             },
             "autoload": {
@@ -187,20 +187,20 @@
                 "laravel",
                 "pdf"
             ],
-            "time": "2017-01-24 18:12:54"
+            "time": "2017-02-19 06:45:54"
         },
         {
             "name": "barryvdh/laravel-ide-helper",
-            "version": "v2.2.3",
+            "version": "v2.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-ide-helper.git",
-                "reference": "a7fc2ec489aada6062d3a63ddc915004a21e38af"
+                "reference": "e82de98cef0d6597b1b686be0b5813a3a4bb53c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/a7fc2ec489aada6062d3a63ddc915004a21e38af",
-                "reference": "a7fc2ec489aada6062d3a63ddc915004a21e38af",
+                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/e82de98cef0d6597b1b686be0b5813a3a4bb53c5",
+                "reference": "e82de98cef0d6597b1b686be0b5813a3a4bb53c5",
                 "shasum": ""
             },
             "require": {
@@ -223,7 +223,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2-dev"
+                    "dev-master": "2.3-dev"
                 }
             },
             "autoload": {
@@ -253,20 +253,20 @@
                 "phpstorm",
                 "sublime"
             ],
-            "time": "2017-01-05 21:20:42"
+            "time": "2017-02-22 12:27:33"
         },
         {
             "name": "barryvdh/laravel-snappy",
-            "version": "v0.3.2",
+            "version": "v0.3.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-snappy.git",
-                "reference": "864470e81952f8e568c93754d9d0d2c05145f773"
+                "reference": "dea728426bd867652c2f3a307a9cbea3f1ed7dff"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/864470e81952f8e568c93754d9d0d2c05145f773",
-                "reference": "864470e81952f8e568c93754d9d0d2c05145f773",
+                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/dea728426bd867652c2f3a307a9cbea3f1ed7dff",
+                "reference": "dea728426bd867652c2f3a307a9cbea3f1ed7dff",
                 "shasum": ""
             },
             "require": {
@@ -305,7 +305,7 @@
                 "wkhtmltoimage",
                 "wkhtmltopdf"
             ],
-            "time": "2017-01-20 06:21:34"
+            "time": "2017-02-09 23:18:54"
         },
         {
             "name": "barryvdh/reflection-docblock",
@@ -476,28 +476,29 @@
         },
         {
             "name": "dompdf/dompdf",
-            "version": "v0.7.0",
+            "version": "v0.8.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/dompdf/dompdf.git",
-                "reference": "5c98652b1a5beb7e3cc8ec35419b2828dd63ab14"
+                "reference": "0f418c6b58fdeafc2a0e80eb1fa5e644e185089c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/dompdf/dompdf/zipball/5c98652b1a5beb7e3cc8ec35419b2828dd63ab14",
-                "reference": "5c98652b1a5beb7e3cc8ec35419b2828dd63ab14",
+                "url": "https://api.github.com/repos/dompdf/dompdf/zipball/0f418c6b58fdeafc2a0e80eb1fa5e644e185089c",
+                "reference": "0f418c6b58fdeafc2a0e80eb1fa5e644e185089c",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-gd": "*",
                 "ext-mbstring": "*",
-                "phenx/php-font-lib": "0.4.*",
-                "phenx/php-svg-lib": "0.1.*",
+                "phenx/php-font-lib": "0.5.*",
+                "phenx/php-svg-lib": "0.2.*",
                 "php": ">=5.3.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "3.7.*"
+                "phpunit/phpunit": "4.8.*",
+                "squizlabs/php_codesniffer": "2.*"
             },
             "type": "library",
             "extra": {
@@ -533,7 +534,7 @@
             ],
             "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
             "homepage": "https://github.com/dompdf/dompdf",
-            "time": "2016-05-11 00:36:29"
+            "time": "2017-02-16 02:40:40"
         },
         {
             "name": "erusev/parsedown",
@@ -926,22 +927,22 @@
         },
         {
             "name": "laravel/browser-kit-testing",
-            "version": "v1.0.2",
+            "version": "v1.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/browser-kit-testing.git",
-                "reference": "60e038e3dcfef2977347f9610c14b48721802278"
+                "reference": "0adfb725147815bff5516d157577f375a6e66ebd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/60e038e3dcfef2977347f9610c14b48721802278",
-                "reference": "60e038e3dcfef2977347f9610c14b48721802278",
+                "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/0adfb725147815bff5516d157577f375a6e66ebd",
+                "reference": "0adfb725147815bff5516d157577f375a6e66ebd",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.5.9",
-                "symfony/css-selector": "3.1.*",
-                "symfony/dom-crawler": "3.1.*"
+                "symfony/css-selector": "~3.1",
+                "symfony/dom-crawler": "~3.1"
             },
             "type": "library",
             "extra": {
@@ -969,20 +970,20 @@
                 "laravel",
                 "testing"
             ],
-            "time": "2017-01-25 13:07:25"
+            "time": "2017-02-08 22:32:37"
         },
         {
             "name": "laravel/framework",
-            "version": "v5.4.9",
+            "version": "v5.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "600330ae1d218919b3b307e0578461a2df248663"
+                "reference": "3eebfaa759156e06144892b00bb95304aa5a71c1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/600330ae1d218919b3b307e0578461a2df248663",
-                "reference": "600330ae1d218919b3b307e0578461a2df248663",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/3eebfaa759156e06144892b00bb95304aa5a71c1",
+                "reference": "3eebfaa759156e06144892b00bb95304aa5a71c1",
                 "shasum": ""
             },
             "require": {
@@ -1098,7 +1099,7 @@
                 "framework",
                 "laravel"
             ],
-            "time": "2017-02-03 19:47:35"
+            "time": "2017-02-22 16:07:04"
         },
         {
             "name": "laravel/socialite",
@@ -1156,16 +1157,16 @@
         },
         {
             "name": "league/flysystem",
-            "version": "1.0.34",
+            "version": "1.0.35",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299"
+                "reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/469ad53c13ea19a0e54e3e5d70f61227ddcc0299",
-                "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/dda7f3ab94158a002d9846a97dc18ebfb7acc062",
+                "reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062",
                 "shasum": ""
             },
             "require": {
@@ -1235,7 +1236,7 @@
                 "sftp",
                 "storage"
             ],
-            "time": "2017-01-30 17:41:17"
+            "time": "2017-02-09 11:33:58"
         },
         {
             "name": "league/flysystem-aws-s3-v3",
@@ -1688,22 +1689,25 @@
         },
         {
             "name": "phenx/php-font-lib",
-            "version": "0.4",
+            "version": "0.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PhenX/php-font-lib.git",
-                "reference": "b8af0cacdc3cbf1e41a586fcb78f506f4121a088"
+                "reference": "19ad2bebc35be028fcc0221025fcbf3d436a3962"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/b8af0cacdc3cbf1e41a586fcb78f506f4121a088",
-                "reference": "b8af0cacdc3cbf1e41a586fcb78f506f4121a088",
+                "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/19ad2bebc35be028fcc0221025fcbf3d436a3962",
+                "reference": "19ad2bebc35be028fcc0221025fcbf3d436a3962",
                 "shasum": ""
             },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8"
+            },
             "type": "library",
             "autoload": {
-                "psr-0": {
-                    "FontLib\\": "src/"
+                "psr-4": {
+                    "FontLib\\": "src/FontLib"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -1718,22 +1722,25 @@
             ],
             "description": "A library to read, parse, export and make subsets of different types of font files.",
             "homepage": "https://github.com/PhenX/php-font-lib",
-            "time": "2015-05-06 20:02:39"
+            "time": "2017-02-11 10:58:43"
         },
         {
             "name": "phenx/php-svg-lib",
-            "version": "0.1",
+            "version": "v0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PhenX/php-svg-lib.git",
-                "reference": "b419766515b3426c6da74b0e29e93d71c4f17099"
+                "reference": "de291bec8449b89acfe85691b5c71434797959dc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/b419766515b3426c6da74b0e29e93d71c4f17099",
-                "reference": "b419766515b3426c6da74b0e29e93d71c4f17099",
+                "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/de291bec8449b89acfe85691b5c71434797959dc",
+                "reference": "de291bec8449b89acfe85691b5c71434797959dc",
                 "shasum": ""
             },
+            "require": {
+                "sabberworm/php-css-parser": "6.0.*"
+            },
             "type": "library",
             "autoload": {
                 "psr-0": {
@@ -1752,7 +1759,7 @@
             ],
             "description": "A library to read, parse and export to PDF SVG files.",
             "homepage": "https://github.com/PhenX/php-svg-lib",
-            "time": "2015-05-06 18:49:49"
+            "time": "2016-12-13 20:25:45"
         },
         {
             "name": "predis/predis",
@@ -1984,17 +1991,58 @@
             "time": "2016-11-22 19:21:44"
         },
         {
-            "name": "socialiteproviders/manager",
-            "version": "v3.0.2",
+            "name": "sabberworm/php-css-parser",
+            "version": "6.0.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/SocialiteProviders/Manager.git",
-                "reference": "3bf2b405b6bfd4bec66f706f5390323f51033eb1"
+                "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
+                "reference": "9ea4b00c569b19f731d0c2e0e802055877ff40c2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/3bf2b405b6bfd4bec66f706f5390323f51033eb1",
-                "reference": "3bf2b405b6bfd4bec66f706f5390323f51033eb1",
+                "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/9ea4b00c569b19f731d0c2e0e802055877ff40c2",
+                "reference": "9ea4b00c569b19f731d0c2e0e802055877ff40c2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Sabberworm\\CSS": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Raphael Schweikert"
+                }
+            ],
+            "description": "Parser for CSS Files written in PHP",
+            "homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
+            "keywords": [
+                "css",
+                "parser",
+                "stylesheet"
+            ],
+            "time": "2015-08-24 08:48:52"
+        },
+        {
+            "name": "socialiteproviders/manager",
+            "version": "v3.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/SocialiteProviders/Manager.git",
+                "reference": "f57877e96c46f0f9157fcd8fa68a547f6d344ed5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/f57877e96c46f0f9157fcd8fa68a547f6d344ed5",
+                "reference": "f57877e96c46f0f9157fcd8fa68a547f6d344ed5",
                 "shasum": ""
             },
             "require": {
@@ -2023,20 +2071,20 @@
                 }
             ],
             "description": "Easily add new or override built-in providers in Laravel Socialite.",
-            "time": "2017-01-27 08:35:03"
+            "time": "2017-02-07 07:26:42"
         },
         {
             "name": "socialiteproviders/slack",
-            "version": "v3.0.0",
+            "version": "v3.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Slack.git",
-                "reference": "a0d676a07bb8293547df6678f1da0258ac40bfec"
+                "reference": "6f8f0599cd80ecea4958ad37ed729805c4100c8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/a0d676a07bb8293547df6678f1da0258ac40bfec",
-                "reference": "a0d676a07bb8293547df6678f1da0258ac40bfec",
+                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/6f8f0599cd80ecea4958ad37ed729805c4100c8c",
+                "reference": "6f8f0599cd80ecea4958ad37ed729805c4100c8c",
                 "shasum": ""
             },
             "require": {
@@ -2060,20 +2108,20 @@
                 }
             ],
             "description": "Slack OAuth2 Provider for Laravel Socialite",
-            "time": "2017-01-25 09:48:29"
+            "time": "2017-02-20 04:42:11"
         },
         {
             "name": "swiftmailer/swiftmailer",
-            "version": "v5.4.5",
+            "version": "v5.4.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/swiftmailer/swiftmailer.git",
-                "reference": "cd142238a339459b10da3d8234220963f392540c"
+                "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c",
-                "reference": "cd142238a339459b10da3d8234220963f392540c",
+                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e",
+                "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e",
                 "shasum": ""
             },
             "require": {
@@ -2114,20 +2162,20 @@
                 "mail",
                 "mailer"
             ],
-            "time": "2016-12-29 10:02:40"
+            "time": "2017-02-13 07:52:53"
         },
         {
             "name": "symfony/class-loader",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/class-loader.git",
-                "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6"
+                "reference": "2847d56f518ad5721bf85aa9174b3aa3fd12aa03"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/class-loader/zipball/0152f7a47acd564ca62c652975c2b32ac6d613a6",
-                "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6",
+                "url": "https://api.github.com/repos/symfony/class-loader/zipball/2847d56f518ad5721bf85aa9174b3aa3fd12aa03",
+                "reference": "2847d56f518ad5721bf85aa9174b3aa3fd12aa03",
                 "shasum": ""
             },
             "require": {
@@ -2170,20 +2218,20 @@
             ],
             "description": "Symfony ClassLoader Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-10 14:14:38"
+            "time": "2017-01-21 17:06:35"
         },
         {
             "name": "symfony/console",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd"
+                "reference": "0e5e6899f82230fcb1153bcaf0e106ffaa44b870"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/4f9e449e76996adf310498a8ca955c6deebe29dd",
-                "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd",
+                "url": "https://api.github.com/repos/symfony/console/zipball/0e5e6899f82230fcb1153bcaf0e106ffaa44b870",
+                "reference": "0e5e6899f82230fcb1153bcaf0e106ffaa44b870",
                 "shasum": ""
             },
             "require": {
@@ -2233,7 +2281,7 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-08 20:47:33"
+            "time": "2017-02-16 14:07:22"
         },
         {
             "name": "symfony/css-selector",
@@ -2290,16 +2338,16 @@
         },
         {
             "name": "symfony/debug",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05"
+                "reference": "9b98854cb45bc59d100b7d4cc4cf9e05f21026b9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/810ba5c1c5352a4ddb15d4719e8936751dff0b05",
-                "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/9b98854cb45bc59d100b7d4cc4cf9e05f21026b9",
+                "reference": "9b98854cb45bc59d100b7d4cc4cf9e05f21026b9",
                 "shasum": ""
             },
             "require": {
@@ -2343,7 +2391,7 @@
             ],
             "description": "Symfony Debug Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02 20:32:22"
+            "time": "2017-02-16 16:34:18"
         },
         {
             "name": "symfony/dom-crawler",
@@ -2403,7 +2451,7 @@
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
@@ -2463,7 +2511,7 @@
         },
         {
             "name": "symfony/finder",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
@@ -2512,16 +2560,16 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "33eb76bf1d833c705433e5361a646c164696394b"
+                "reference": "a90da6dd679605d88c9803a57a6fc1fb7a19a6e0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/33eb76bf1d833c705433e5361a646c164696394b",
-                "reference": "33eb76bf1d833c705433e5361a646c164696394b",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a90da6dd679605d88c9803a57a6fc1fb7a19a6e0",
+                "reference": "a90da6dd679605d88c9803a57a6fc1fb7a19a6e0",
                 "shasum": ""
             },
             "require": {
@@ -2561,20 +2609,20 @@
             ],
             "description": "Symfony HttpFoundation Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-08 20:47:33"
+            "time": "2017-02-16 22:46:52"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "8a898e340a89022246645b1288d295f49c9381e4"
+                "reference": "4cd0d4bc31819095c6ef13573069f621eb321081"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8a898e340a89022246645b1288d295f49c9381e4",
-                "reference": "8a898e340a89022246645b1288d295f49c9381e4",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4cd0d4bc31819095c6ef13573069f621eb321081",
+                "reference": "4cd0d4bc31819095c6ef13573069f621eb321081",
                 "shasum": ""
             },
             "require": {
@@ -2643,7 +2691,7 @@
             ],
             "description": "Symfony HttpKernel Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-12 21:36:33"
+            "time": "2017-02-16 23:59:56"
         },
         {
             "name": "symfony/polyfill-mbstring",
@@ -2706,16 +2754,16 @@
         },
         {
             "name": "symfony/process",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893"
+                "reference": "0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/350e810019fc52dd06ae844b6a6d382f8a0e8893",
-                "reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893",
+                "url": "https://api.github.com/repos/symfony/process/zipball/0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856",
+                "reference": "0ab87c1e7570b3534a6e51eb4ca8e9f6d7327856",
                 "shasum": ""
             },
             "require": {
@@ -2751,20 +2799,20 @@
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02 20:32:22"
+            "time": "2017-02-16 14:07:22"
         },
         {
             "name": "symfony/routing",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "fda2c67d47ec801726ca888c95d701d31b27b444"
+                "reference": "af464432c177dbcdbb32295113b7627500331f2d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/fda2c67d47ec801726ca888c95d701d31b27b444",
-                "reference": "fda2c67d47ec801726ca888c95d701d31b27b444",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/af464432c177dbcdbb32295113b7627500331f2d",
+                "reference": "af464432c177dbcdbb32295113b7627500331f2d",
                 "shasum": ""
             },
             "require": {
@@ -2826,20 +2874,20 @@
                 "uri",
                 "url"
             ],
-            "time": "2017-01-02 20:32:22"
+            "time": "2017-01-28 02:37:08"
         },
         {
             "name": "symfony/translation",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "6520f3d4cce604d9dd1e86cac7af954984dd9bda"
+                "reference": "d6825c6bb2f1da13f564678f9f236fe8242c0029"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/6520f3d4cce604d9dd1e86cac7af954984dd9bda",
-                "reference": "6520f3d4cce604d9dd1e86cac7af954984dd9bda",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/d6825c6bb2f1da13f564678f9f236fe8242c0029",
+                "reference": "d6825c6bb2f1da13f564678f9f236fe8242c0029",
                 "shasum": ""
             },
             "require": {
@@ -2890,20 +2938,20 @@
             ],
             "description": "Symfony Translation Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-02 20:32:22"
+            "time": "2017-02-16 22:46:52"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "b54b23f9a19b465e76fdaac0f6732410467c83b2"
+                "reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b54b23f9a19b465e76fdaac0f6732410467c83b2",
-                "reference": "b54b23f9a19b465e76fdaac0f6732410467c83b2",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cb50260b674ee1c2d4ab49f2395a42e0b4681e20",
+                "reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20",
                 "shasum": ""
             },
             "require": {
@@ -2953,7 +3001,7 @@
                 "debug",
                 "dump"
             ],
-            "time": "2017-01-03 08:53:57"
+            "time": "2017-02-16 22:46:52"
         },
         {
             "name": "tijsverkoyen/css-to-inline-styles",
@@ -3203,16 +3251,16 @@
         },
         {
             "name": "mockery/mockery",
-            "version": "0.9.7",
+            "version": "0.9.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/padraic/mockery.git",
-                "reference": "4de7969f4664da3cef1ccd83866c9f59378c3371"
+                "reference": "1e5e2ffdc4d71d7358ed58a6fdd30a4a0c506855"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/padraic/mockery/zipball/4de7969f4664da3cef1ccd83866c9f59378c3371",
-                "reference": "4de7969f4664da3cef1ccd83866c9f59378c3371",
+                "url": "https://api.github.com/repos/padraic/mockery/zipball/1e5e2ffdc4d71d7358ed58a6fdd30a4a0c506855",
+                "reference": "1e5e2ffdc4d71d7358ed58a6fdd30a4a0c506855",
                 "shasum": ""
             },
             "require": {
@@ -3264,7 +3312,7 @@
                 "test double",
                 "testing"
             ],
-            "time": "2016-12-19 14:50:55"
+            "time": "2017-02-09 13:29:38"
         },
         {
             "name": "myclabs/deep-copy",
@@ -3519,23 +3567,23 @@
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "4.0.5",
+            "version": "4.0.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971"
+                "reference": "ca060f645beeddebedb1885c97bf163e93264c35"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971",
-                "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca060f645beeddebedb1885c97bf163e93264c35",
+                "reference": "ca060f645beeddebedb1885c97bf163e93264c35",
                 "shasum": ""
             },
             "require": {
                 "php": "^5.6 || ^7.0",
                 "phpunit/php-file-iterator": "~1.3",
                 "phpunit/php-text-template": "~1.2",
-                "phpunit/php-token-stream": "^1.4.2",
+                "phpunit/php-token-stream": "^1.4.2 || ^2.0",
                 "sebastian/code-unit-reverse-lookup": "~1.0",
                 "sebastian/environment": "^1.3.2 || ^2.0",
                 "sebastian/version": "~1.0|~2.0"
@@ -3578,7 +3626,7 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2017-01-20 15:06:43"
+            "time": "2017-02-23 07:38:02"
         },
         {
             "name": "phpunit/php-file-iterator",
@@ -3714,16 +3762,16 @@
         },
         {
             "name": "phpunit/php-token-stream",
-            "version": "1.4.9",
+            "version": "1.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b"
+                "reference": "284fb0679dd25fb5ffb56dad92c72860c0a22f1b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b",
-                "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/284fb0679dd25fb5ffb56dad92c72860c0a22f1b",
+                "reference": "284fb0679dd25fb5ffb56dad92c72860c0a22f1b",
                 "shasum": ""
             },
             "require": {
@@ -3759,20 +3807,20 @@
             "keywords": [
                 "tokenizer"
             ],
-            "time": "2016-11-15 14:06:22"
+            "time": "2017-02-23 06:14:45"
         },
         {
             "name": "phpunit/phpunit",
-            "version": "5.7.10",
+            "version": "5.7.14",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e"
+                "reference": "4906b8faf23e42612182fd212eb6f4c0f2954b57"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bf0804199f516fe80ffcc48ac6d4741c49baeb6e",
-                "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4906b8faf23e42612182fd212eb6f4c0f2954b57",
+                "reference": "4906b8faf23e42612182fd212eb6f4c0f2954b57",
                 "shasum": ""
             },
             "require": {
@@ -3796,7 +3844,7 @@
                 "sebastian/global-state": "^1.1",
                 "sebastian/object-enumerator": "~2.0",
                 "sebastian/resource-operations": "~1.0",
-                "sebastian/version": "~1.0|~2.0",
+                "sebastian/version": "~1.0.3|~2.0",
                 "symfony/yaml": "~2.1|~3.0"
             },
             "conflict": {
@@ -3841,7 +3889,7 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2017-02-04 09:03:53"
+            "time": "2017-02-19 07:22:16"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
@@ -4233,16 +4281,16 @@
         },
         {
             "name": "sebastian/object-enumerator",
-            "version": "2.0.0",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/object-enumerator.git",
-                "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35"
+                "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35",
-                "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
+                "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
                 "shasum": ""
             },
             "require": {
@@ -4275,7 +4323,7 @@
             ],
             "description": "Traverses array structures and object graphs to enumerate all referenced objects",
             "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
-            "time": "2016-11-19 07:35:10"
+            "time": "2017-02-18 15:18:39"
         },
         {
             "name": "sebastian/recursion-context",
@@ -4417,16 +4465,16 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v3.2.2",
+            "version": "v3.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "50eadbd7926e31842893c957eca362b21592a97d"
+                "reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d",
-                "reference": "50eadbd7926e31842893c957eca362b21592a97d",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/9724c684646fcb5387d579b4bfaa63ee0b0c64c8",
+                "reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8",
                 "shasum": ""
             },
             "require": {
@@ -4468,7 +4516,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2017-01-03 13:51:32"
+            "time": "2017-02-16 22:46:52"
         },
         {
             "name": "webmozart/assert",
diff --git a/config/dompdf.php b/config/dompdf.php
index 924bdb9cd..1eb1d9782 100644
--- a/config/dompdf.php
+++ b/config/dompdf.php
@@ -1,6 +1,6 @@
 <?php
 
-return array(
+return [
 
     /*
     |--------------------------------------------------------------------------
@@ -13,7 +13,7 @@ return array(
     */
     'show_warnings' => false,   // Throw an Exception on warnings from dompdf
     'orientation' => 'portrait',
-    'defines' => array(
+    'defines' => [
         /**
          * The location of the DOMPDF font directory
          *
@@ -260,7 +260,7 @@ return array(
         "DOMPDF_ENABLE_HTML5PARSER" => true,
 
 
-    ),
+    ],
 
 
-);
+];
diff --git a/resources/assets/sass/export-styles.scss b/resources/assets/sass/export-styles.scss
index 7e1ab4e9e..60450f3e2 100644
--- a/resources/assets/sass/export-styles.scss
+++ b/resources/assets/sass/export-styles.scss
@@ -1,4 +1,4 @@
-@import "reset";
+//@import "reset";
 @import "variables";
 @import "mixins";
 @import "html";
diff --git a/resources/views/pages/page-display.blade.php b/resources/views/pages/page-display.blade.php
index 6eb927687..cb7cae445 100644
--- a/resources/views/pages/page-display.blade.php
+++ b/resources/views/pages/page-display.blade.php
@@ -1,6 +1,6 @@
 <div ng-non-bindable>
 
-    <h1 id="bkmrk-page-title" class="float left">{{$page->name}}</h1>
+    <h1 id="bkmrk-page-title">{{$page->name}}</h1>
 
     <div style="clear:left;"></div>
 
diff --git a/resources/views/pages/pdf.blade.php b/resources/views/pages/pdf.blade.php
index 7e43c5e1a..33a009fee 100644
--- a/resources/views/pages/pdf.blade.php
+++ b/resources/views/pages/pdf.blade.php
@@ -30,11 +30,5 @@
             clear: both;
             display: block;
         }
-
-        .tag-display {
-            min-width: 0;
-            max-width: none;
-            display: none;
-        }
     </style>
 @stop
\ No newline at end of file

From 0abed1afe5006173c9a16e369a28b693c6716423 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 26 Feb 2017 09:14:18 +0000
Subject: [PATCH 15/27] Added clear activity/revision commands. Cleaned
 commands.

Added testing to cover each command.
Removed example laravel inspire command.
Standardised command names to be behind 'bookstack' naming.
In reference to #320.
---
 app/Console/Commands/ClearActivity.php        |  47 ++++++++
 app/Console/Commands/ClearRevisions.php       |  50 +++++++++
 .../{ResetViews.php => ClearViews.php}        |   7 +-
 app/Console/Commands/Inspire.php              |  33 ------
 .../Commands/RegeneratePermissions.php        |   3 +-
 app/Console/Kernel.php                        |   8 +-
 tests/CommandsTest.php                        | 102 ++++++++++++++++++
 7 files changed, 209 insertions(+), 41 deletions(-)
 create mode 100644 app/Console/Commands/ClearActivity.php
 create mode 100644 app/Console/Commands/ClearRevisions.php
 rename app/Console/Commands/{ResetViews.php => ClearViews.php} (74%)
 delete mode 100644 app/Console/Commands/Inspire.php
 create mode 100644 tests/CommandsTest.php

diff --git a/app/Console/Commands/ClearActivity.php b/app/Console/Commands/ClearActivity.php
new file mode 100644
index 000000000..66babd9a9
--- /dev/null
+++ b/app/Console/Commands/ClearActivity.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\Activity;
+use Illuminate\Console\Command;
+
+class ClearActivity extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:clear-activity';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Clear user activity from the system';
+
+    protected $activity;
+
+    /**
+     * Create a new command instance.
+     *
+     * @param Activity $activity
+     */
+    public function __construct(Activity $activity)
+    {
+        $this->activity = $activity;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->activity->newQuery()->truncate();
+        $this->comment('System activity cleared');
+    }
+}
diff --git a/app/Console/Commands/ClearRevisions.php b/app/Console/Commands/ClearRevisions.php
new file mode 100644
index 000000000..f0c8a5e85
--- /dev/null
+++ b/app/Console/Commands/ClearRevisions.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\PageRevision;
+use Illuminate\Console\Command;
+
+class ClearRevisions extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:clear-revisions
+                            {--a|all : Include active update drafts in deletion}
+                            ';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Clear page revisions';
+
+    protected $pageRevision;
+
+    /**
+     * Create a new command instance.
+     *
+     * @param PageRevision $pageRevision
+     */
+    public function __construct(PageRevision $pageRevision)
+    {
+        $this->pageRevision = $pageRevision;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $deleteTypes = $this->option('all') ? ['version', 'update_draft'] : ['version'];
+        $this->pageRevision->newQuery()->whereIn('type', $deleteTypes)->delete();
+        $this->comment('Revisions deleted');
+    }
+}
diff --git a/app/Console/Commands/ResetViews.php b/app/Console/Commands/ClearViews.php
similarity index 74%
rename from app/Console/Commands/ResetViews.php
rename to app/Console/Commands/ClearViews.php
index 3a3903ff8..678c64d33 100644
--- a/app/Console/Commands/ResetViews.php
+++ b/app/Console/Commands/ClearViews.php
@@ -4,21 +4,21 @@ namespace BookStack\Console\Commands;
 
 use Illuminate\Console\Command;
 
-class ResetViews extends Command
+class ClearViews extends Command
 {
     /**
      * The name and signature of the console command.
      *
      * @var string
      */
-    protected $signature = 'views:reset';
+    protected $signature = 'bookstack:clear-views';
 
     /**
      * The console command description.
      *
      * @var string
      */
-    protected $description = 'Reset all view-counts for all entities.';
+    protected $description = 'Clear all view-counts for all entities.';
 
     /**
      * Create a new command instance.
@@ -37,5 +37,6 @@ class ResetViews extends Command
     public function handle()
     {
         \Views::resetAll();
+        $this->comment('Views cleared');
     }
 }
diff --git a/app/Console/Commands/Inspire.php b/app/Console/Commands/Inspire.php
deleted file mode 100644
index 4b115cfb9..000000000
--- a/app/Console/Commands/Inspire.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace BookStack\Console\Commands;
-
-use Illuminate\Console\Command;
-use Illuminate\Foundation\Inspiring;
-
-class Inspire extends Command
-{
-    /**
-     * The name and signature of the console command.
-     *
-     * @var string
-     */
-    protected $signature = 'inspire';
-
-    /**
-     * The console command description.
-     *
-     * @var string
-     */
-    protected $description = 'Display an inspiring quote';
-
-    /**
-     * Execute the console command.
-     *
-     * @return mixed
-     */
-    public function handle()
-    {
-        $this->comment(PHP_EOL.Inspiring::quote().PHP_EOL);
-    }
-}
diff --git a/app/Console/Commands/RegeneratePermissions.php b/app/Console/Commands/RegeneratePermissions.php
index 60d5f4e45..966ee4a82 100644
--- a/app/Console/Commands/RegeneratePermissions.php
+++ b/app/Console/Commands/RegeneratePermissions.php
@@ -12,7 +12,7 @@ class RegeneratePermissions extends Command
      *
      * @var string
      */
-    protected $signature = 'permissions:regen';
+    protected $signature = 'bookstack:regenerate-permissions';
 
     /**
      * The console command description.
@@ -47,5 +47,6 @@ class RegeneratePermissions extends Command
     public function handle()
     {
         $this->permissionService->buildJointPermissions();
+        $this->comment('Permissions regenerated');
     }
 }
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index b725c9e21..0112e72ca 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -13,8 +13,9 @@ class Kernel extends ConsoleKernel
      * @var array
      */
     protected $commands = [
-        \BookStack\Console\Commands\Inspire::class,
-        \BookStack\Console\Commands\ResetViews::class,
+        \BookStack\Console\Commands\ClearViews::class,
+        \BookStack\Console\Commands\ClearActivity::class,
+        \BookStack\Console\Commands\ClearRevisions::class,
         \BookStack\Console\Commands\RegeneratePermissions::class,
     ];
 
@@ -26,7 +27,6 @@ class Kernel extends ConsoleKernel
      */
     protected function schedule(Schedule $schedule)
     {
-        $schedule->command('inspire')
-                 ->hourly();
+        //
     }
 }
diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php
new file mode 100644
index 000000000..5df82ee51
--- /dev/null
+++ b/tests/CommandsTest.php
@@ -0,0 +1,102 @@
+<?php namespace Tests;
+
+use BookStack\JointPermission;
+use BookStack\Page;
+use BookStack\Repos\EntityRepo;
+
+class CommandsTest extends TestCase
+{
+
+    public function test_clear_views_command()
+    {
+        $this->asEditor();
+        $page = Page::first();
+
+        $this->get($page->getUrl());
+
+        $this->assertDatabaseHas('views', [
+            'user_id' => $this->getEditor()->id,
+            'viewable_id' => $page->id,
+            'views' => 1
+        ]);
+
+        $exitCode = \Artisan::call('bookstack:clear-views');
+        $this->assertTrue($exitCode === 0, 'Command executed successfully');
+
+        $this->assertDatabaseMissing('views', [
+            'user_id' => $this->getEditor()->id
+        ]);
+    }
+
+    public function test_clear_activity_command()
+    {
+        $this->asEditor();
+        $page = Page::first();
+        \Activity::add($page, 'page_update', $page->book->id);
+
+        $this->assertDatabaseHas('activities', [
+            'key' => 'page_update',
+            'entity_id' => $page->id,
+            'user_id' => $this->getEditor()->id
+        ]);
+
+        $exitCode = \Artisan::call('bookstack:clear-activity');
+        $this->assertTrue($exitCode === 0, 'Command executed successfully');
+
+
+        $this->assertDatabaseMissing('activities', [
+            'key' => 'page_update'
+        ]);
+    }
+
+    public function test_clear_revisions_command()
+    {
+        $this->asEditor();
+        $entityRepo = $this->app[EntityRepo::class];
+        $page = Page::first();
+        $entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
+        $entityRepo->updatePageDraft($page, ['name' => 'updated page', 'html' => '<p>new content in draft</p>', 'summary' => 'page revision testing']);
+
+        $this->assertDatabaseHas('page_revisions', [
+            'page_id' => $page->id,
+            'type' => 'version'
+        ]);
+        $this->assertDatabaseHas('page_revisions', [
+            'page_id' => $page->id,
+            'type' => 'update_draft'
+        ]);
+
+        $exitCode = \Artisan::call('bookstack:clear-revisions');
+        $this->assertTrue($exitCode === 0, 'Command executed successfully');
+
+        $this->assertDatabaseMissing('page_revisions', [
+            'page_id' => $page->id,
+            'type' => 'version'
+        ]);
+        $this->assertDatabaseHas('page_revisions', [
+            'page_id' => $page->id,
+            'type' => 'update_draft'
+        ]);
+
+        $exitCode = \Artisan::call('bookstack:clear-revisions', ['--all' => true]);
+        $this->assertTrue($exitCode === 0, 'Command executed successfully');
+
+        $this->assertDatabaseMissing('page_revisions', [
+            'page_id' => $page->id,
+            'type' => 'update_draft'
+        ]);
+    }
+
+    public function test_regen_permissions_command()
+    {
+        JointPermission::query()->truncate();
+        $page = Page::first();
+
+        $this->assertDatabaseMissing('joint_permissions', ['entity_id' => $page->id]);
+
+        $exitCode = \Artisan::call('bookstack:regenerate-permissions');
+        $this->assertTrue($exitCode === 0, 'Command executed successfully');
+
+        $this->assertDatabaseHas('joint_permissions', ['entity_id' => $page->id]);
+    }
+}

From eded8abdedf71ce6fca552a310e04d0cbe9c3827 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 26 Feb 2017 13:26:51 +0000
Subject: [PATCH 16/27] Added book export and created export tests to cover

In reference to #177
---
 app/Http/Controllers/BookController.php | 51 +++++++++++++++-
 app/Http/Controllers/PageController.php |  1 -
 app/Repos/EntityRepo.php                |  9 ++-
 app/Services/ExportService.php          | 81 +++++++++++++++++++++++--
 app/Services/PermissionService.php      |  8 ++-
 config/dompdf.php                       |  2 +-
 resources/views/books/export.blade.php  | 78 ++++++++++++++++++++++++
 resources/views/books/show.blade.php    |  8 +++
 resources/views/pages/export.blade.php  |  2 +-
 routes/web.php                          |  3 +
 tests/Entity/ExportTest.php             | 78 ++++++++++++++++++++++++
 11 files changed, 306 insertions(+), 15 deletions(-)
 create mode 100644 resources/views/books/export.blade.php
 create mode 100644 tests/Entity/ExportTest.php

diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php
index 408192ff9..fe9ece5b2 100644
--- a/app/Http/Controllers/BookController.php
+++ b/app/Http/Controllers/BookController.php
@@ -3,6 +3,7 @@
 use Activity;
 use BookStack\Repos\EntityRepo;
 use BookStack\Repos\UserRepo;
+use BookStack\Services\ExportService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
 use Views;
@@ -12,16 +13,19 @@ class BookController extends Controller
 
     protected $entityRepo;
     protected $userRepo;
+    protected $exportService;
 
     /**
      * BookController constructor.
      * @param EntityRepo $entityRepo
      * @param UserRepo $userRepo
+     * @param ExportService $exportService
      */
-    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo)
+    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportService $exportService)
     {
         $this->entityRepo = $entityRepo;
         $this->userRepo = $userRepo;
+        $this->exportService = $exportService;
         parent::__construct();
     }
 
@@ -258,4 +262,49 @@ class BookController extends Controller
         session()->flash('success', trans('entities.books_permissions_updated'));
         return redirect($book->getUrl());
     }
+
+    /**
+     * Export a book as a PDF file.
+     * @param string $bookSlug
+     * @return mixed
+     */
+    public function exportPdf($bookSlug)
+    {
+        $book = $this->entityRepo->getBySlug('book', $bookSlug);
+        $pdfContent = $this->exportService->bookToPdf($book);
+        return response()->make($pdfContent, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $bookSlug . '.pdf'
+        ]);
+    }
+
+    /**
+     * Export a book as a contained HTML file.
+     * @param string $bookSlug
+     * @return mixed
+     */
+    public function exportHtml($bookSlug)
+    {
+        $book = $this->entityRepo->getBySlug('book', $bookSlug);
+        $htmlContent = $this->exportService->bookToContainedHtml($book);
+        return response()->make($htmlContent, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $bookSlug . '.html'
+        ]);
+    }
+
+    /**
+     * Export a book as a plain text file.
+     * @param $bookSlug
+     * @return mixed
+     */
+    public function exportPlainText($bookSlug)
+    {
+        $book = $this->entityRepo->getBySlug('book', $bookSlug);
+        $htmlContent = $this->exportService->bookToPlainText($book);
+        return response()->make($htmlContent, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $bookSlug . '.txt'
+        ]);
+    }
 }
diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index 623cb9c4d..4a29c20d6 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -439,7 +439,6 @@ class PageController extends Controller
     {
         $page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
         $pdfContent = $this->exportService->pageToPdf($page);
-//        return $pdfContent;
         return response()->make($pdfContent, 200, [
             'Content-Type'        => 'application/octet-stream',
             'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf'
diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php
index 7b262c3de..4db69137f 100644
--- a/app/Repos/EntityRepo.php
+++ b/app/Repos/EntityRepo.php
@@ -313,11 +313,12 @@ class EntityRepo
      * Loads the book slug onto child elements to prevent access database access for getting the slug.
      * @param Book $book
      * @param bool $filterDrafts
+     * @param bool $renderPages
      * @return mixed
      */
-    public function getBookChildren(Book $book, $filterDrafts = false)
+    public function getBookChildren(Book $book, $filterDrafts = false, $renderPages = false)
     {
-        $q = $this->permissionService->bookChildrenQuery($book->id, $filterDrafts)->get();
+        $q = $this->permissionService->bookChildrenQuery($book->id, $filterDrafts, $renderPages)->get();
         $entities = [];
         $parents = [];
         $tree = [];
@@ -325,6 +326,10 @@ class EntityRepo
         foreach ($q as $index => $rawEntity) {
             if ($rawEntity->entity_type === 'BookStack\\Page') {
                 $entities[$index] = $this->page->newFromBuilder($rawEntity);
+                if ($renderPages) {
+                    $entities[$index]->html = $rawEntity->description;
+                    $entities[$index]->html = $this->renderPage($entities[$index]);
+                };
             } else if ($rawEntity->entity_type === 'BookStack\\Chapter') {
                 $entities[$index] = $this->chapter->newFromBuilder($rawEntity);
                 $key = $entities[$index]->entity_type . ':' . $entities[$index]->id;
diff --git a/app/Services/ExportService.php b/app/Services/ExportService.php
index e51577a22..3ac698718 100644
--- a/app/Services/ExportService.php
+++ b/app/Services/ExportService.php
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Services;
 
+use BookStack\Book;
 use BookStack\Page;
 use BookStack\Repos\EntityRepo;
 
@@ -25,24 +26,69 @@ class ExportService
      */
     public function pageToContainedHtml(Page $page)
     {
-        $cssContent = file_get_contents(public_path('/css/export-styles.css'));
-        $pageHtml = view('pages/export', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render();
+        $pageHtml = view('pages/export', [
+            'page' => $page,
+            'pageContent' => $this->entityRepo->renderPage($page)
+        ])->render();
         return $this->containHtml($pageHtml);
     }
 
     /**
-     * Convert a page to a pdf file.
+     * Convert a book to a self-contained HTML file.
+     * @param Book $book
+     * @return mixed|string
+     */
+    public function bookToContainedHtml(Book $book)
+    {
+        $bookTree = $this->entityRepo->getBookChildren($book, true, true);
+        $html = view('books/export', [
+            'book' => $book,
+            'bookChildren' => $bookTree
+        ])->render();
+        return $this->containHtml($html);
+    }
+
+    /**
+     * Convert a page to a PDF file.
      * @param Page $page
      * @return mixed|string
      */
     public function pageToPdf(Page $page)
     {
-        $cssContent = file_get_contents(public_path('/css/export-styles.css'));
-        $pageHtml = view('pages/pdf', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render();
+        $html = view('pages/pdf', [
+            'page' => $page,
+            'pageContent' => $this->entityRepo->renderPage($page)
+        ])->render();
+        return $this->htmlToPdf($html);
+    }
+
+    /**
+     * Convert a book to a PDF file
+     * @param Book $book
+     * @return string
+     */
+    public function bookToPdf(Book $book)
+    {
+        $bookTree = $this->entityRepo->getBookChildren($book, true, true);
+        $html = view('books/export', [
+            'book' => $book,
+            'bookChildren' => $bookTree
+        ])->render();
+        return $this->htmlToPdf($html);
+    }
+
+    /**
+     * Convert normal webpage HTML to a PDF.
+     * @param $html
+     * @return string
+     */
+    protected function htmlToPdf($html)
+    {
+        $containedHtml = $this->containHtml($html);
         $useWKHTML = config('snappy.pdf.binary') !== false;
-        $containedHtml = $this->containHtml($pageHtml);
         if ($useWKHTML) {
             $pdf = \SnappyPDF::loadHTML($containedHtml);
+            $pdf->setOption('print-media-type', true);
         } else {
             $pdf = \PDF::loadHTML($containedHtml);
         }
@@ -122,6 +168,29 @@ class ExportService
         return $text;
     }
 
+    /**
+     * Convert a book into a plain text string.
+     * @param Book $book
+     * @return string
+     */
+    public function bookToPlainText(Book $book)
+    {
+        $bookTree = $this->entityRepo->getBookChildren($book, true, true);
+        $text = $book->name . "\n\n";
+        foreach ($bookTree as $bookChild) {
+            if ($bookChild->isA('chapter')) {
+                $text .= $bookChild->name . "\n\n";
+                $text .= $bookChild->description . "\n\n";
+                foreach ($bookChild->pages as $page) {
+                    $text .= $this->pageToPlainText($page);
+                }
+            } else {
+                $text .= $this->pageToPlainText($bookChild);
+            }
+        }
+        return $text;
+    }
+
 }
 
 
diff --git a/app/Services/PermissionService.php b/app/Services/PermissionService.php
index 72a810b6b..8b47e1246 100644
--- a/app/Services/PermissionService.php
+++ b/app/Services/PermissionService.php
@@ -474,11 +474,13 @@ class PermissionService
     /**
      * Get the children of a book in an efficient single query, Filtered by the permission system.
      * @param integer $book_id
-     * @param bool    $filterDrafts
+     * @param bool $filterDrafts
+     * @param bool $fetchPageContent
      * @return \Illuminate\Database\Query\Builder
      */
-    public function bookChildrenQuery($book_id, $filterDrafts = false) {
-        $pageSelect = $this->db->table('pages')->selectRaw("'BookStack\\\\Page' as entity_type, id, slug, name, text, '' as description, book_id, priority, chapter_id, draft")->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
+    public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) {
+        $pageContentSelect = $fetchPageContent ? 'html' : "''";
+        $pageSelect = $this->db->table('pages')->selectRaw("'BookStack\\\\Page' as entity_type, id, slug, name, text, {$pageContentSelect} as description, book_id, priority, chapter_id, draft")->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
             $query->where('draft', '=', 0);
             if (!$filterDrafts) {
                 $query->orWhere(function($query) {
diff --git a/config/dompdf.php b/config/dompdf.php
index 1eb1d9782..036e1bb3c 100644
--- a/config/dompdf.php
+++ b/config/dompdf.php
@@ -143,7 +143,7 @@ return [
          * the desired content might be different (e.g. screen or projection view of html file).
          * Therefore allow specification of content here.
          */
-        "DOMPDF_DEFAULT_MEDIA_TYPE" => "screen",
+        "DOMPDF_DEFAULT_MEDIA_TYPE" => "print",
 
         /**
          * The default paper size.
diff --git a/resources/views/books/export.blade.php b/resources/views/books/export.blade.php
new file mode 100644
index 000000000..e5fbada44
--- /dev/null
+++ b/resources/views/books/export.blade.php
@@ -0,0 +1,78 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <title>{{ $book->name }}</title>
+
+    <style>
+        {!! file_get_contents(public_path('/css/export-styles.css')) !!}
+        .page-break {
+            page-break-after: always;
+        }
+        .chapter-hint {
+            color: #888;
+            margin-top: 32px;
+        }
+        .chapter-hint + h1 {
+            margin-top: 0;
+        }
+        ul.contents ul li {
+            list-style: circle;
+        }
+        @media screen {
+            .page-break {
+                border-top: 1px solid #DDD;
+            }
+        }
+    </style>
+    @yield('head')
+</head>
+<body>
+<div class="container">
+    <div class="row">
+        <div class="col-md-8 col-md-offset-2">
+            <div class="page-content">
+
+                <h1 style="font-size: 4.8em">{{$book->name}}</h1>
+
+                <p>{{ $book->description }}</p>
+
+                @if(count($bookChildren) > 0)
+                <ul class="contents">
+                    @foreach($bookChildren as $bookChild)
+                        <li><a href="#{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</a></li>
+                        @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                            <ul>
+                                @foreach($bookChild->pages as $page)
+                                    <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
+                                @endforeach
+                            </ul>
+                        @endif
+                    @endforeach
+                </ul>
+                @endif
+
+                @foreach($bookChildren as $bookChild)
+                    <div class="page-break"></div>
+                    <h1 id="{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</h1>
+                    @if($bookChild->isA('chapter'))
+                        <p>{{ $bookChild->description }}</p>
+                        @if(count($bookChild->pages) > 0)
+                            @foreach($bookChild->pages as $page)
+                                <div class="page-break"></div>
+                                <div class="chapter-hint">{{$bookChild->name}}</div>
+                                <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
+                                {!! $page->html !!}
+                            @endforeach
+                        @endif
+                    @else
+                        {!! $bookChild->html !!}
+                    @endif
+                @endforeach
+
+            </div>
+        </div>
+    </div>
+</div>
+</body>
+</html>
diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php
index 6b4e7f88a..99ffe80e1 100644
--- a/resources/views/books/show.blade.php
+++ b/resources/views/books/show.blade.php
@@ -10,6 +10,14 @@
                 </div>
                 <div class="col-sm-6">
                     <div class="action-buttons faded">
+                        <span dropdown class="dropdown-container">
+                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
+                            <ul class="wide">
+                                <li><a href="{{ $book->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
+                                <li><a href="{{ $book->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                                <li><a href="{{ $book->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                            </ul>
+                        </span>
                         @if(userCan('page-create', $book))
                             <a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
                         @endif
diff --git a/resources/views/pages/export.blade.php b/resources/views/pages/export.blade.php
index 19a635563..e0813e468 100644
--- a/resources/views/pages/export.blade.php
+++ b/resources/views/pages/export.blade.php
@@ -5,7 +5,7 @@
     <title>{{ $page->name }}</title>
 
     <style>
-        {!! $css !!}
+        {!! file_get_contents(public_path('/css/export-styles.css')) !!}
     </style>
     @yield('head')
 </head>
diff --git a/routes/web.php b/routes/web.php
index 8d166f1d6..670439a66 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -26,6 +26,9 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{slug}/delete', 'BookController@showDelete');
         Route::get('/{bookSlug}/sort', 'BookController@sort');
         Route::put('/{bookSlug}/sort', 'BookController@saveSort');
+        Route::get('/{bookSlug}/export/html', 'BookController@exportHtml');
+        Route::get('/{bookSlug}/export/pdf', 'BookController@exportPdf');
+        Route::get('/{bookSlug}/export/plaintext', 'BookController@exportPlainText');
 
         // Pages
         Route::get('/{bookSlug}/page/create', 'PageController@create');
diff --git a/tests/Entity/ExportTest.php b/tests/Entity/ExportTest.php
new file mode 100644
index 000000000..b1dab094f
--- /dev/null
+++ b/tests/Entity/ExportTest.php
@@ -0,0 +1,78 @@
+<?php namespace Tests;
+
+
+use BookStack\Page;
+
+class ExportTest extends TestCase
+{
+
+    public function test_page_text_export()
+    {
+        $page = Page::first();
+        $this->asEditor();
+
+        $resp = $this->get($page->getUrl('/export/plaintext'));
+        $resp->assertStatus(200);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt');
+    }
+
+    public function test_page_pdf_export()
+    {
+        $page = Page::first();
+        $this->asEditor();
+
+        $resp = $this->get($page->getUrl('/export/pdf'));
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf');
+    }
+
+    public function test_page_html_export()
+    {
+        $page = Page::first();
+        $this->asEditor();
+
+        $resp = $this->get($page->getUrl('/export/html'));
+        $resp->assertStatus(200);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html');
+    }
+
+    public function test_book_text_export()
+    {
+        $page = Page::first();
+        $book = $page->book;
+        $this->asEditor();
+
+        $resp = $this->get($book->getUrl('/export/plaintext'));
+        $resp->assertStatus(200);
+        $resp->assertSee($book->name);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt');
+    }
+
+    public function test_book_pdf_export()
+    {
+        $page = Page::first();
+        $book = $page->book;
+        $this->asEditor();
+
+        $resp = $this->get($book->getUrl('/export/pdf'));
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf');
+    }
+
+    public function test_book_html_export()
+    {
+        $page = Page::first();
+        $book = $page->book;
+        $this->asEditor();
+
+        $resp = $this->get($book->getUrl('/export/html'));
+        $resp->assertStatus(200);
+        $resp->assertSee($book->name);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html');
+    }
+
+}
\ No newline at end of file

From 253132afdf54b9200bcf33c95c0414798a3554d5 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 26 Feb 2017 14:25:02 +0000
Subject: [PATCH 17/27] Added chapter export options

Closes #177
---
 app/Http/Controllers/ChapterController.php | 54 +++++++++++++++++++-
 app/Http/Controllers/PageController.php    |  2 +-
 app/Services/ExportService.php             | 58 ++++++++++++++++++++--
 resources/views/chapters/export.blade.php  | 52 +++++++++++++++++++
 resources/views/chapters/show.blade.php    |  8 +++
 routes/web.php                             |  3 ++
 tests/Entity/ExportTest.php                | 37 ++++++++++++++
 7 files changed, 207 insertions(+), 7 deletions(-)
 create mode 100644 resources/views/chapters/export.blade.php

diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php
index 1760ee5c6..ceeb2a3ef 100644
--- a/app/Http/Controllers/ChapterController.php
+++ b/app/Http/Controllers/ChapterController.php
@@ -3,6 +3,7 @@
 use Activity;
 use BookStack\Repos\EntityRepo;
 use BookStack\Repos\UserRepo;
+use BookStack\Services\ExportService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
 use Views;
@@ -12,16 +13,19 @@ class ChapterController extends Controller
 
     protected $userRepo;
     protected $entityRepo;
+    protected $exportService;
 
     /**
      * ChapterController constructor.
      * @param EntityRepo $entityRepo
      * @param UserRepo $userRepo
+     * @param ExportService $exportService
      */
-    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo)
+    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportService $exportService)
     {
         $this->entityRepo = $entityRepo;
         $this->userRepo = $userRepo;
+        $this->exportService = $exportService;
         parent::__construct();
     }
 
@@ -236,4 +240,52 @@ class ChapterController extends Controller
         session()->flash('success', trans('entities.chapters_permissions_success'));
         return redirect($chapter->getUrl());
     }
+
+    /**
+     * Exports a chapter to pdf .
+     * @param string $bookSlug
+     * @param string $chapterSlug
+     * @return \Illuminate\Http\Response
+     */
+    public function exportPdf($bookSlug, $chapterSlug)
+    {
+        $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+        $pdfContent = $this->exportService->chapterToPdf($chapter);
+        return response()->make($pdfContent, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $chapterSlug . '.pdf'
+        ]);
+    }
+
+    /**
+     * Export a chapter to a self-contained HTML file.
+     * @param string $bookSlug
+     * @param string $chapterSlug
+     * @return \Illuminate\Http\Response
+     */
+    public function exportHtml($bookSlug, $chapterSlug)
+    {
+        $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+        $containedHtml = $this->exportService->chapterToContainedHtml($chapter);
+        return response()->make($containedHtml, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $chapterSlug . '.html'
+        ]);
+    }
+
+    /**
+     * Export a chapter to a simple plaintext .txt file.
+     * @param string $bookSlug
+     * @param string $chapterSlug
+     * @return \Illuminate\Http\Response
+     */
+    public function exportPlainText($bookSlug, $chapterSlug)
+    {
+        $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+        $containedHtml = $this->exportService->chapterToPlainText($chapter);
+        return response()->make($containedHtml, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $chapterSlug . '.txt'
+        ]);
+    }
 }
diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index 4a29c20d6..c97597bc4 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -429,7 +429,7 @@ class PageController extends Controller
     }
 
     /**
-     * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
+     * Exports a page to a PDF.
      * https://github.com/barryvdh/laravel-dompdf
      * @param string $bookSlug
      * @param string $pageSlug
diff --git a/app/Services/ExportService.php b/app/Services/ExportService.php
index 3ac698718..78cef41a4 100644
--- a/app/Services/ExportService.php
+++ b/app/Services/ExportService.php
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Services;
 
 use BookStack\Book;
+use BookStack\Chapter;
 use BookStack\Page;
 use BookStack\Repos\EntityRepo;
 
@@ -33,6 +34,24 @@ class ExportService
         return $this->containHtml($pageHtml);
     }
 
+    /**
+     * Convert a chapter to a self-contained HTML file.
+     * @param Chapter $chapter
+     * @return mixed|string
+     */
+    public function chapterToContainedHtml(Chapter $chapter)
+    {
+        $pages = $this->entityRepo->getChapterChildren($chapter);
+        $pages->each(function($page) {
+            $page->html = $this->entityRepo->renderPage($page);
+        });
+        $html = view('chapters/export', [
+            'chapter' => $chapter,
+            'pages' => $pages
+        ])->render();
+        return $this->containHtml($html);
+    }
+
     /**
      * Convert a book to a self-contained HTML file.
      * @param Book $book
@@ -62,6 +81,24 @@ class ExportService
         return $this->htmlToPdf($html);
     }
 
+    /**
+     * Convert a chapter to a PDF file.
+     * @param Chapter $chapter
+     * @return mixed|string
+     */
+    public function chapterToPdf(Chapter $chapter)
+    {
+        $pages = $this->entityRepo->getChapterChildren($chapter);
+        $pages->each(function($page) {
+            $page->html = $this->entityRepo->renderPage($page);
+        });
+        $html = view('chapters/export', [
+            'chapter' => $chapter,
+            'pages' => $pages
+        ])->render();
+        return $this->htmlToPdf($html);
+    }
+
     /**
      * Convert a book to a PDF file
      * @param Book $book
@@ -168,6 +205,21 @@ class ExportService
         return $text;
     }
 
+    /**
+     * Convert a chapter into a plain text string.
+     * @param Chapter $chapter
+     * @return string
+     */
+    public function chapterToPlainText(Chapter $chapter)
+    {
+        $text = $chapter->name . "\n\n";
+        $text .= $chapter->description . "\n\n";
+        foreach ($chapter->pages as $page) {
+            $text .= $this->pageToPlainText($page);
+        }
+        return $text;
+    }
+
     /**
      * Convert a book into a plain text string.
      * @param Book $book
@@ -179,11 +231,7 @@ class ExportService
         $text = $book->name . "\n\n";
         foreach ($bookTree as $bookChild) {
             if ($bookChild->isA('chapter')) {
-                $text .= $bookChild->name . "\n\n";
-                $text .= $bookChild->description . "\n\n";
-                foreach ($bookChild->pages as $page) {
-                    $text .= $this->pageToPlainText($page);
-                }
+                $text .= $this->chapterToPlainText($bookChild);
             } else {
                 $text .= $this->pageToPlainText($bookChild);
             }
diff --git a/resources/views/chapters/export.blade.php b/resources/views/chapters/export.blade.php
new file mode 100644
index 000000000..57fcd1649
--- /dev/null
+++ b/resources/views/chapters/export.blade.php
@@ -0,0 +1,52 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <title>{{ $chapter->name }}</title>
+
+    <style>
+        {!! file_get_contents(public_path('/css/export-styles.css')) !!}
+        .page-break {
+            page-break-after: always;
+        }
+        ul.contents ul li {
+            list-style: circle;
+        }
+        @media screen {
+            .page-break {
+                border-top: 1px solid #DDD;
+            }
+        }
+    </style>
+    @yield('head')
+</head>
+<body>
+<div class="container">
+    <div class="row">
+        <div class="col-md-8 col-md-offset-2">
+            <div class="page-content">
+
+                <h1 style="font-size: 4.8em">{{$chapter->name}}</h1>
+
+                <p>{{ $chapter->description }}</p>
+
+                @if(count($pages) > 0)
+                <ul class="contents">
+                    @foreach($pages as $page)
+                        <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
+                    @endforeach
+                </ul>
+                @endif
+
+                @foreach($pages as $page)
+                    <div class="page-break"></div>
+                    <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
+                    {!! $page->html !!}
+                @endforeach
+
+            </div>
+        </div>
+    </div>
+</div>
+</body>
+</html>
diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php
index 93eee6424..47a1d9ddf 100644
--- a/resources/views/chapters/show.blade.php
+++ b/resources/views/chapters/show.blade.php
@@ -10,6 +10,14 @@
                 </div>
                 <div class="col-sm-4 faded">
                     <div class="action-buttons">
+                        <span dropdown class="dropdown-container">
+                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
+                            <ul class="wide">
+                                <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                            </ul>
+                        </span>
                         @if(userCan('page-create', $chapter))
                             <a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
                         @endif
diff --git a/routes/web.php b/routes/web.php
index 670439a66..4bd2b4a06 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -67,6 +67,9 @@ Route::group(['middleware' => 'auth'], function () {
         Route::put('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@move');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/edit', 'ChapterController@edit');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showRestrict');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterController@exportPdf');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/html', 'ChapterController@exportHtml');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/plaintext', 'ChapterController@exportPlainText');
         Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@restrict');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
         Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy');
diff --git a/tests/Entity/ExportTest.php b/tests/Entity/ExportTest.php
index b1dab094f..7fa485f20 100644
--- a/tests/Entity/ExportTest.php
+++ b/tests/Entity/ExportTest.php
@@ -1,6 +1,7 @@
 <?php namespace Tests;
 
 
+use BookStack\Chapter;
 use BookStack\Page;
 
 class ExportTest extends TestCase
@@ -75,4 +76,40 @@ class ExportTest extends TestCase
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html');
     }
 
+    public function test_chapter_text_export()
+    {
+        $chapter = Chapter::first();
+        $page = $chapter->pages[0];
+        $this->asEditor();
+
+        $resp = $this->get($chapter->getUrl('/export/plaintext'));
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt');
+    }
+
+    public function test_chapter_pdf_export()
+    {
+        $chapter = Chapter::first();
+        $this->asEditor();
+
+        $resp = $this->get($chapter->getUrl('/export/pdf'));
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf');
+    }
+
+    public function test_chapter_html_export()
+    {
+        $chapter = Chapter::first();
+        $page = $chapter->pages[0];
+        $this->asEditor();
+
+        $resp = $this->get($chapter->getUrl('/export/html'));
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html');
+    }
+
 }
\ No newline at end of file

From a90f564980608f8682d65a8558fb7ed5a0d6f43c Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 26 Feb 2017 14:51:49 +0000
Subject: [PATCH 18/27] Made LDAP email attribute configurable via .env

Closes #306
---
 app/Services/LdapService.php | 5 +++--
 config/services.php          | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/app/Services/LdapService.php b/app/Services/LdapService.php
index f8a4b88bb..71dc9c0e1 100644
--- a/app/Services/LdapService.php
+++ b/app/Services/LdapService.php
@@ -41,7 +41,8 @@ class LdapService
         // Find user
         $userFilter = $this->buildFilter($this->config['user_filter'], ['user' => $userName]);
         $baseDn = $this->config['base_dn'];
-        $users = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', 'mail']);
+        $emailAttr = $this->config['email_attribute'];
+        $users = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', $emailAttr]);
         if ($users['count'] === 0) return null;
 
         $user = $users[0];
@@ -49,7 +50,7 @@ class LdapService
             'uid'   => (isset($user['uid'])) ? $user['uid'][0] : $user['dn'],
             'name'  => $user['cn'][0],
             'dn'    => $user['dn'],
-            'email' => (isset($user['mail'])) ? $user['mail'][0] : null
+            'email' => (isset($user[$emailAttr])) ? (is_array($user[$emailAttr]) ? $user[$emailAttr][0] : $user[$emailAttr]) : null
         ];
     }
 
diff --git a/config/services.php b/config/services.php
index fe58cbb9a..99022e5f2 100644
--- a/config/services.php
+++ b/config/services.php
@@ -78,7 +78,8 @@ return [
         'pass' => env('LDAP_PASS', false),
         'base_dn' => env('LDAP_BASE_DN', false),
         'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'),
-        'version' => env('LDAP_VERSION', false)
+        'version' => env('LDAP_VERSION', false),
+        'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'),
     ]
 
 ];

From e3eefba745dda9cfb995ba70090e499c953adddd Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 26 Feb 2017 21:39:15 +0000
Subject: [PATCH 19/27] Fixed export testing and updated travis settings

---
 .travis.yml                               | 15 +++++++--------
 config/database.php                       |  2 +-
 resources/views/books/export.blade.php    |  2 ++
 resources/views/chapters/export.blade.php |  2 ++
 resources/views/pages/export.blade.php    |  2 ++
 5 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 0ad753ced..909e3e1f4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
 dist: trusty
-sudo: required
+sudo: false
 language: php
 php:
   - 7.0
@@ -8,15 +8,11 @@ cache:
   directories:
     - $HOME/.composer/cache
 
-addons:
-  apt:
-    packages:
-    - mysql-server-5.6
-    - mysql-client-core-5.6
-    - mysql-client-5.6
-
 before_script:
   - mysql -u root -e 'create database `bookstack-test`;'
+  - mysql -u root -e "CREATE USER 'bookstack-test'@'localhost' IDENTIFIED BY 'bookstack-test';"
+  - mysql -u root -e "GRANT ALL ON \`bookstack-test\`.* TO 'bookstack-test'@'localhost';"
+  - mysql -u root -e "FLUSH PRIVILEGES;"
   - phpenv config-rm xdebug.ini
   - composer dump-autoload --no-interaction
   - composer install --prefer-dist --no-interaction
@@ -25,5 +21,8 @@ before_script:
   - php artisan migrate --force -n --database=mysql_testing
   - php artisan db:seed --force -n --class=DummyContentSeeder --database=mysql_testing
 
+after_failure:
+  - cat storage/logs/laravel.log
+
 script:
   - phpunit
\ No newline at end of file
diff --git a/config/database.php b/config/database.php
index 832852dc2..92c768245 100644
--- a/config/database.php
+++ b/config/database.php
@@ -82,7 +82,7 @@ return [
 
         'mysql_testing' => [
             'driver'    => 'mysql',
-            'host'      => 'localhost',
+            'host'      => '127.0.0.1',
             'database'  => 'bookstack-test',
             'username'  => env('MYSQL_USER', 'bookstack-test'),
             'password'  => env('MYSQL_PASSWORD', 'bookstack-test'),
diff --git a/resources/views/books/export.blade.php b/resources/views/books/export.blade.php
index e5fbada44..3ea3c9d65 100644
--- a/resources/views/books/export.blade.php
+++ b/resources/views/books/export.blade.php
@@ -5,7 +5,9 @@
     <title>{{ $book->name }}</title>
 
     <style>
+        @if (!app()->environment('testing'))
         {!! file_get_contents(public_path('/css/export-styles.css')) !!}
+        @endif
         .page-break {
             page-break-after: always;
         }
diff --git a/resources/views/chapters/export.blade.php b/resources/views/chapters/export.blade.php
index 57fcd1649..0fc1295df 100644
--- a/resources/views/chapters/export.blade.php
+++ b/resources/views/chapters/export.blade.php
@@ -5,7 +5,9 @@
     <title>{{ $chapter->name }}</title>
 
     <style>
+        @if (!app()->environment('testing'))
         {!! file_get_contents(public_path('/css/export-styles.css')) !!}
+        @endif
         .page-break {
             page-break-after: always;
         }
diff --git a/resources/views/pages/export.blade.php b/resources/views/pages/export.blade.php
index e0813e468..0bb376a9a 100644
--- a/resources/views/pages/export.blade.php
+++ b/resources/views/pages/export.blade.php
@@ -5,7 +5,9 @@
     <title>{{ $page->name }}</title>
 
     <style>
+        @if (!app()->environment('testing'))
         {!! file_get_contents(public_path('/css/export-styles.css')) !!}
+        @endif
     </style>
     @yield('head')
 </head>

From bcafa73faf4a26810cf4f876efe2677b116d5aba Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Mon, 27 Feb 2017 16:48:36 +0000
Subject: [PATCH 20/27] Set composer to clean bootstrap/cache before an update

---
 composer.json | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/composer.json b/composer.json
index 7d820b3f8..396a9babd 100644
--- a/composer.json
+++ b/composer.json
@@ -47,6 +47,14 @@
         "post-create-project-cmd": [
             "php artisan key:generate"
         ],
+        "pre-update-cmd": [
+            "php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
+            "php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
+        ],
+        "pre-install-cmd": [
+            "php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
+            "php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
+        ],
         "post-install-cmd": [
             "Illuminate\\Foundation\\ComposerScripts::postInstall",
             "php artisan optimize",

From f7645824d99d34e12814057e478e026c4c88ba54 Mon Sep 17 00:00:00 2001
From: Diego Jose Sosa Diaz <diego.venezuela@gmail.com>
Date: Fri, 3 Mar 2017 00:21:33 +0100
Subject: [PATCH 21/27] First spanish translation effort

---
 resources/lang/es/activities.php |  40 ++++++
 resources/lang/es/auth.php       |  74 ++++++++++
 resources/lang/es/common.php     |  58 ++++++++
 resources/lang/es/components.php |  24 ++++
 resources/lang/es/entities.php   | 226 +++++++++++++++++++++++++++++++
 resources/lang/es/errors.php     |  70 ++++++++++
 resources/lang/es/pagination.php |  19 +++
 resources/lang/es/passwords.php  |  22 +++
 resources/lang/es/settings.php   | 123 +++++++++++++++++
 resources/lang/es/validation.php | 108 +++++++++++++++
 10 files changed, 764 insertions(+)
 create mode 100644 resources/lang/es/activities.php
 create mode 100644 resources/lang/es/auth.php
 create mode 100644 resources/lang/es/common.php
 create mode 100644 resources/lang/es/components.php
 create mode 100644 resources/lang/es/entities.php
 create mode 100644 resources/lang/es/errors.php
 create mode 100644 resources/lang/es/pagination.php
 create mode 100644 resources/lang/es/passwords.php
 create mode 100644 resources/lang/es/settings.php
 create mode 100644 resources/lang/es/validation.php

diff --git a/resources/lang/es/activities.php b/resources/lang/es/activities.php
new file mode 100644
index 000000000..1556f4530
--- /dev/null
+++ b/resources/lang/es/activities.php
@@ -0,0 +1,40 @@
+<?php
+
+return [
+
+    /**
+     * Activity text strings.
+     * Is used for all the text within activity logs & notifications.
+     */
+
+    // Pages
+    'page_create'                 => 'página creada',
+    'page_create_notification'    => 'Página creada exitosamente',
+    'page_update'                 => 'página actualizada',
+    'page_update_notification'    => 'Página actualizada exitosamente',
+    'page_delete'                 => 'página borrada',
+    'page_delete_notification'    => 'Página borrada exitosamente,
+    'page_restore'                => 'página restaurada',
+    'page_restore_notification'   => 'Página restaurada exitosamente',
+    'page_move'                   => 'página movida',
+
+    // Chapters
+    'chapter_create'              => 'capítulo creado',
+    'chapter_create_notification' => 'Capítulo creado exitosamente',
+    'chapter_update'              => 'capítulo actualizado',
+    'chapter_update_notification' => 'Capítulo actualizado exitosamente',
+    'chapter_delete'              => 'capítulo borrado',
+    'chapter_delete_notification' => 'Capítulo borrado exitosamente',
+    'chapter_move'                => 'capítulo movido',
+
+    // Books
+    'book_create'                 => 'libro creado',
+    'book_create_notification'    => 'Libro creado exitosamente',
+    'book_update'                 => 'libro actualizado',
+    'book_update_notification'    => 'Libro actualizado exitosamente',
+    'book_delete'                 => 'libro borrado',
+    'book_delete_notification'    => 'Libro borrado exitosamente',
+    'book_sort'                   => 'libro ordenado',
+    'book_sort_notification'      => 'Libro re-ordenado exitosamente',
+
+];
diff --git a/resources/lang/es/auth.php b/resources/lang/es/auth.php
new file mode 100644
index 000000000..8837525ae
--- /dev/null
+++ b/resources/lang/es/auth.php
@@ -0,0 +1,74 @@
+<?php
+return [
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used during authentication for various
+    | messages that we need to display to the user. You are free to modify
+    | these language lines according to your application's requirements.
+    |
+    */
+    'failed' => 'Las credenciales no concuerdan con nuestros registros.',
+    'throttle' => 'Demasiados intentos fallidos de conexi�n. Por favor intente nuevamente en :seconds segundos.',
+
+    /**
+     * Login & Register
+     */
+    'sign_up' => 'Inscribete',
+    'log_in' => 'Log in',
+    'logout' => 'Logout',
+
+    'name' => 'Nombre',
+    'username' => 'Username',
+    'email' => 'Email',
+    'password' => 'Password',
+    'password_confirm' => 'Confirmar Password',
+    'password_hint' => 'Debe contener al menos 5 caracteres',
+    'forgot_password' => 'Olvidó Password?',
+    'remember_me' => 'Recordarme',
+    'ldap_email_hint' => 'Por favor introduzca un mail para utilizar con esta cuenta.',
+    'create_account' => 'Crear una cuenta',
+    'social_login' => 'Login Social',
+    'social_registration' => 'Registro Social',
+    'social_registration_text' => 'Registrar y entrar utilizando otro servicio.',
+
+    'register_thanks' => 'Gracias por registrarse!',
+    'register_confirm' => 'Por favor chequee su email y haga clic en el botón de confirmación enviado para acceder a :appName.',
+    'registrations_disabled' => 'Los registros están deshabilitados actualmente',
+    'registration_email_domain_invalid' => 'Este dominio de Email no tiene acceso a esta aplicación',
+    'register_success' => 'Gracias por registrarse! Ahora se encuentra registrado y logueado.',
+
+
+    /**
+     * Password Reset
+     */
+    'reset_password' => 'Reset Password',
+    'reset_password_send_instructions' => 'Introduzca su email a continuación y le será enviado un correo con un link para la restauración',
+    'reset_password_send_button' => 'Enviar Link de Reset',
+    'reset_password_sent_success' => 'Un link para resetear password ha sido enviado a :email.',
+    'reset_password_success' => 'Su password ha sido reiniciado de manera éxitosa.',
+
+    'email_reset_subject' => 'Reset de su password de :appName',
+    'email_reset_text' => 'Ud. esta recibiendo este email debido a que recibimos una solicitud de reset de password de su cuenta.',
+    'email_reset_not_requested' => 'Si ud. no solicitó un reset de password, no es requerida ninguna acción.',
+
+
+    /**
+     * Email Confirmation
+     */
+    'email_confirm_subject' => 'Confirme su email en :appName',
+    'email_confirm_greeting' => 'Gracias por unirse a :appName!',
+    'email_confirm_text' => 'Por favor confirme su dirección de email haciendo click en el siguiente botón:',
+    'email_confirm_action' => 'Confirmar Email',
+    'email_confirm_send_error' => 'Confirmation de email requerida pero el sistema no pudo enviar el mail. Contacte al administrador para asegurarse que el email está seteado correctamente.',
+    'email_confirm_success' => 'Su email hasido confirmado!',
+    'email_confirm_resent' => 'Email de confirmación reenviado, Por favor chequee su Inbox.',
+
+    'email_not_confirmed' => 'Dirección de email no confirmada',
+    'email_not_confirmed_text' => 'Su cuenta de email todavía no ha sido confirmada.',
+    'email_not_confirmed_click_link' => 'Por favor chequee el email con el link de confirmación que ha sido enviado luego de registrarse.',
+    'email_not_confirmed_resend' => 'Si no puede encontrar el email, puede solicitar el renvío del email de confirmación rellenando el formulario a continuación.',
+    'email_not_confirmed_resend_button' => 'Reenviar Email de confirmación',
+];
diff --git a/resources/lang/es/common.php b/resources/lang/es/common.php
new file mode 100644
index 000000000..3a62a2177
--- /dev/null
+++ b/resources/lang/es/common.php
@@ -0,0 +1,58 @@
+<?php
+return [
+
+    /**
+     * Buttons
+     */
+    'cancel' => 'Cancelar',
+    'confirm' => 'Confirmar',
+    'back' => 'Atrás',
+    'save' => 'Guardar',
+    'continue' => 'Continuar',
+    'select' => 'Seleccionar',
+
+    /**
+     * Form Labels
+     */
+    'name' => 'Nombre',
+    'description' => 'Descripción',
+    'role' => 'Rol',
+
+    /**
+     * Actions
+     */
+    'actions' => 'Acciones',
+    'view' => 'Ver',
+    'create' => 'Crear',
+    'update' => 'Actualizar',
+    'edit' => 'Editar',
+    'sort' => 'Ordenar',
+    'move' => 'Mover',
+    'delete' => 'Borrar',
+    'search' => 'Buscar',
+    'search_clear' => 'Limpiar búsqueda',
+    'reset' => 'Reset',
+    'remove' => 'Remover',
+
+
+    /**
+     * Misc
+     */
+    'deleted_user' => 'Usuario borrado',
+    'no_activity' => 'Ninguna actividad para mostrar',
+    'no_items' => 'No hay items disponibles',
+    'back_to_top' => 'Volver arriba',
+    'toggle_details' => 'Alternar detalles',
+
+    /**
+     * Header
+     */
+    'view_profile' => 'Ver Perfil',
+    'edit_profile' => 'Editar Perfil',
+
+    /**
+     * Email Content
+     */
+    'email_action_help' => 'Si está teniendo problemas haga click en el botón ":actionText", copie y pegue la siguiente URL en su navegador web:',
+    'email_rights' => 'Todos los derechos reservados',
+];
diff --git a/resources/lang/es/components.php b/resources/lang/es/components.php
new file mode 100644
index 000000000..fead3d4a3
--- /dev/null
+++ b/resources/lang/es/components.php
@@ -0,0 +1,24 @@
+<?php
+return [
+
+    /**
+     * Image Manager
+     */
+    'image_select' => 'Seleccionar Imagen',
+    'image_all' => 'Todo',
+    'image_all_title' => 'Ver todas las imágenes',
+    'image_book_title' => 'Ver las imágenes subidas a este libro',
+    'image_page_title' => 'Ver las imágenes subidas a esta página',
+    'image_search_hint' => 'Buscar por nombre de imagen',
+    'image_uploaded' => 'Subido el :uploadedDate',
+    'image_load_more' => 'Cargar más',
+    'image_image_name' => 'Nombre de imagen',
+    'image_delete_confirm' => 'Esta imagen esta siendo utilizada en las páginas a continuación, haga click de nuevo para confirmar que quiere borrar esta imagen.',
+    'image_select_image' => 'Seleccionar Imagen',
+    'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir',
+    'images_deleted' => 'Imágenes borradas',
+    'image_preview' => 'Preview de la imagen',
+    'image_upload_success' => 'Imagen subida exitosamente',
+    'image_update_success' => 'Detalles de la imagen actualizados exitosamente',
+    'image_delete_success' => 'Imagen borrada exitosamente'
+];
diff --git a/resources/lang/es/entities.php b/resources/lang/es/entities.php
new file mode 100644
index 000000000..917a25093
--- /dev/null
+++ b/resources/lang/es/entities.php
@@ -0,0 +1,226 @@
+<?php
+return [
+
+    /**
+     * Shared
+     */
+    'recently_created' => 'Recientemente creadod',
+    'recently_created_pages' => 'P�ginas recientemente creadas',
+    'recently_updated_pages' => 'P�ginas recientemente actualizadas',
+    'recently_created_chapters' => 'Cap�tulos recientemente creados',
+    'recently_created_books' => 'Libros recientemente creados',
+    'recently_update' => 'Recientemente actualizado',
+    'recently_viewed' => 'Recientemente visto',
+    'recent_activity' => 'Actividad reciente',
+    'create_now' => 'Crear uno ahora',
+    'revisions' => 'Revisiones',
+    'meta_created' => 'Creado el :timeLength',
+    'meta_created_name' => 'Creado el  :timeLength por :user',
+    'meta_updated' => 'Actualizado el :timeLength',
+    'meta_updated_name' => 'Actualizado el :timeLength por :user',
+    'x_pages' => ':count P�ginas',
+    'entity_select' => 'Seleccione entidad',
+    'images' => 'Im�genes',
+    'my_recent_drafts' => 'Mis borradores recientes',
+    'my_recently_viewed' => 'Mis visualizaciones recientes',
+    'no_pages_viewed' => 'Ud. no ha visto ninguna p�gina',
+    'no_pages_recently_created' => 'Ninguna p�gina ha sido creada recientemente',
+    'no_pages_recently_updated' => 'Ninguna p�gina ha sido actualizada recientemente',
+
+    /**
+     * Permissions and restrictions
+     */
+    'permissions' => 'Permisos',
+    'permissions_intro' => 'una vez habilitado, Estos permisos tendr�n prioridad por encima de cualquier permiso establecido.',
+    'permissions_enable' => 'Habilitar permisos custom',
+    'permissions_save' => 'Guardar permisos',
+
+    /**
+     * Search
+     */
+    'search_results' => 'Buscar resultados',
+    'search_results_page' => 'resultados de b�squeda en p�gina',
+    'search_results_chapter' => 'Resultados de b�squeda en cap�tulo ',
+    'search_results_book' => 'Resultados de b�squeda en libro',
+    'search_clear' => 'Limpiar resultados',
+    'search_view_pages' => 'Ver todas las p�ginas que concuerdan',
+    'search_view_chapters' => 'Ver todos los cap�tulos que concuerdan',
+    'search_view_books' => 'Ver todos los libros que concuerdan',
+    'search_no_pages' => 'Ninguna p�gina encontrada para la b�squeda',
+    'search_for_term' => 'Busqueda por :term',
+    'search_page_for_term' => 'B�squeda de p�gina por :term',
+    'search_chapter_for_term' => 'B�squeda por cap�tulo de :term',
+    'search_book_for_term' => 'B�squeda en libro de :term',
+
+    /**
+     * Books
+     */
+    'book' => 'Libro',
+    'books' => 'Libros',
+    'books_empty' => 'No hay libros creados',
+    'books_popular' => 'Libros populares',
+    'books_recent' => 'Libros recientes',
+    'books_popular_empty' => 'Los libros m�s populares aparecer�n aquí.',
+    'books_create' => 'Crear nuevo libro',
+    'books_delete' => 'Borrar libro',
+    'books_delete_named' => 'Borrar libro :bookName',
+    'books_delete_explain' => 'Esto borrará el libro con el nombre \':bookName\', Todos las páginas y capítulos serán removios.',
+    'books_delete_confirmation' => '�Está seguro de que desea borrar este libro?',
+    'books_edit' => 'Editar Libro',
+    'books_edit_named' => 'Editar Libro :bookName',
+    'books_form_book_name' => 'Nombre de libro',
+    'books_save' => 'Guardar libro',
+    'books_permissions' => 'permisos de libro',
+    'books_permissions_updated' => 'Permisos de libro actualizados',
+    'books_empty_contents' => 'Ninguna p�gina o cap�tulo ha sido creada para este libro.',
+    'books_empty_create_page' => 'Crear una nueva p�gina',
+    'books_empty_or' => 'ó',
+    'books_empty_sort_current_book' => 'Organizar el libro actual',
+    'books_empty_add_chapter' => 'Agregar un cap�tulo',
+    'books_permissions_active' => 'Permisos de libro activados',
+    'books_search_this' => 'Buscar en este libro',
+    'books_navigation' => 'Navegaci�n de libro',
+    'books_sort' => 'Organizar contenido de libro',
+    'books_sort_named' => 'Organizar libro :bookName',
+    'books_sort_show_other' => 'Mostrar otros libros',
+    'books_sort_save' => 'Guardar nuevo orden',
+
+    /**
+     * Chapters
+     */
+    'chapter' => 'Cap�tulo',
+    'chapters' => 'Cap�tulos',
+    'chapters_popular' => 'Cap�tulos populares',
+    'chapters_new' => 'Nuevo cap�tulo',
+    'chapters_create' => 'Crear nuevo cap�tulo',
+    'chapters_delete' => 'Borrar cap�tulo',
+    'chapters_delete_named' => 'Borrar cap�tulo :chapterName',
+    'chapters_delete_explain' => 'Esto borrará el caítulo con el nombre \':chapterName\', todas las p�ginas ser�n removidas
+        y agregadas directamente al libro padre.',
+    'chapters_delete_confirm' => 'Est� ud. seguro de borrar este cap�tulo?',
+    'chapters_edit' => 'Editar cap�tulo',
+    'chapters_edit_named' => 'Editar cap�tulo :chapterName',
+    'chapters_save' => 'Guardar cap�tulo',
+    'chapters_move' => 'Mover cap�tulo',
+    'chapters_move_named' => 'Mover Cap�tulo :chapterName',
+    'chapter_move_success' => 'Cap�tulo movido a :bookName',
+    'chapters_permissions' => 'Permisos de cap�tulo',
+    'chapters_empty' => 'No existen p�ginas en este cap�tulo.',
+    'chapters_permissions_active' => 'Permisos de cap�tulo activado',
+    'chapters_permissions_success' => 'Permisos de cap�tulo actualizados',
+
+    /**
+     * Pages
+     */
+    'page' => 'P�gina',
+    'pages' => 'P�ginas',
+    'pages_popular' => 'P�ginas populares',
+    'pages_new' => 'Nueva p�gina',
+    'pages_attachments' => 'Adjuntos',
+    'pages_navigation' => 'Navegaci�n de p�gina',
+    'pages_delete' => 'Borrar p�gina',
+    'pages_delete_named' => 'Borrar p�gina :pageName',
+    'pages_delete_draft_named' => 'Borrar borrador de p�gina :pageName',
+    'pages_delete_draft' => 'Borrar borrador de p�gina',
+    'pages_delete_success' => 'P�gina borrada',
+    'pages_delete_draft_success' => 'Borrador de p�gina borrado',
+    'pages_delete_confirm' => 'Est� ud. seguro de borrar esta p�gina ?',
+    'pages_delete_draft_confirm' => 'Est� ud. seguro de que desea borrar este borrador de p�gina?',
+    'pages_editing_named' => 'Editando p�gina :pageName',
+    'pages_edit_toggle_header' => 'Toggle T�tulo',
+    'pages_edit_save_draft' => 'Guardar borrador',
+    'pages_edit_draft' => 'Editar borrador de p�gina',
+    'pages_editing_draft' => 'Editando borrador',
+    'pages_editing_page' => 'Editando p�gina',
+    'pages_edit_draft_save_at' => 'Borrador guardado el ',
+    'pages_edit_delete_draft' => 'Borrar borrador',
+    'pages_edit_discard_draft' => 'Descartar borrador',
+    'pages_edit_set_changelog' => 'Set Changelog',
+    'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripci�n de los cambios que ha realizado',
+    'pages_edit_enter_changelog' => 'Entrar en Changelog',
+    'pages_save' => 'Guardar p�gina',
+    'pages_title' => 'T�tulo de p�gina',
+    'pages_name' => 'Nombre de p�gina',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Preview',
+    'pages_md_insert_image' => 'Insertar Imagen',
+    'pages_md_insert_link' => 'Insert Entity Link',
+    'pages_not_in_chapter' => 'Page is not in a chapter',
+    'pages_move' => 'Move Page',
+    'pages_move_success' => 'Page moved to ":parentName"',
+    'pages_permissions' => 'Page Permissions',
+    'pages_permissions_success' => 'Page permissions updated',
+    'pages_revisions' => 'Page Revisions',
+    'pages_revisions_named' => 'Page Revisions for :pageName',
+    'pages_revision_named' => 'Page Revision for :pageName',
+    'pages_revisions_created_by' => 'Created By',
+    'pages_revisions_date' => 'Revision Date',
+    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_changes' => 'Changes',
+    'pages_revisions_current' => 'Current Version',
+    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_restore' => 'Restore',
+    'pages_revisions_none' => 'This page has no revisions',
+    'pages_export' => 'Export',
+    'pages_export_html' => 'Contained Web File',
+    'pages_export_pdf' => 'PDF File',
+    'pages_export_text' => 'Plain Text File',
+    'pages_copy_link' => 'Copy Link',
+    'pages_permissions_active' => 'Page Permissions Active',
+    'pages_initial_revision' => 'Initial publish',
+    'pages_initial_name' => 'New Page',
+    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
+    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count users have started editing this page',
+        'start_b' => ':userName has started editing this page',
+        'time_a' => 'since the pages was last updated',
+        'time_b' => 'in the last :minCount minutes',
+        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+    ],
+    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
+
+    /**
+     * Editor sidebar
+     */
+    'page_tags' => 'Page Tags',
+    'tag' => 'Tag',
+    'tags' =>  '',
+    'tag_value' => 'Tag Value (Optional)',
+    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
+    'tags_add' => 'Add another tag',
+    'attachments' => 'Attachments',
+    'attachments_explain' => 'Upload some files or attach some link to display on your page. These are visible in the page sidebar.',
+    'attachments_explain_instant_save' => 'Changes here are saved instantly.',
+    'attachments_items' => 'Attached Items',
+    'attachments_upload' => 'Upload File',
+    'attachments_link' => 'Attach Link',
+    'attachments_set_link' => 'Set Link',
+    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_dropzone' => 'Drop files or click here to attach a file',
+    'attachments_no_files' => 'No files have been uploaded',
+    'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
+    'attachments_link_name' => 'Link Name',
+    'attachment_link' => 'Attachment link',
+    'attachments_link_url' => 'Link to file',
+    'attachments_link_url_hint' => 'Url of site or file',
+    'attach' => 'Attach',
+    'attachments_edit_file' => 'Edit File',
+    'attachments_edit_file_name' => 'File Name',
+    'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
+    'attachments_order_updated' => 'Attachment order updated',
+    'attachments_updated_success' => 'Attachment details updated',
+    'attachments_deleted' => 'Attachment deleted',
+    'attachments_file_uploaded' => 'File successfully uploaded',
+    'attachments_file_updated' => 'File successfully updated',
+    'attachments_link_attached' => 'Link successfully attached to page',
+
+    /**
+     * Profile View
+     */
+    'profile_user_for_x' => 'User for :time',
+    'profile_created_content' => 'Created Content',
+    'profile_not_created_pages' => ':userName has not created any pages',
+    'profile_not_created_chapters' => ':userName has not created any chapters',
+    'profile_not_created_books' => ':userName has not created any books',
+];
diff --git a/resources/lang/es/errors.php b/resources/lang/es/errors.php
new file mode 100644
index 000000000..08b8b0946
--- /dev/null
+++ b/resources/lang/es/errors.php
@@ -0,0 +1,70 @@
+<?php
+
+return [
+
+    /**
+     * Error text strings.
+     */
+
+    // Permissions
+    'permission' => 'You do not have permission to access the requested page.',
+    'permissionJson' => 'You do not have permission to perform the requested action.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.',
+    'email_already_confirmed' => 'Email has already been confirmed, Try logging in.',
+    'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.',
+    'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.',
+    'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind',
+    'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
+    'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
+    'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'social_no_action_defined' => 'No action defined',
+    'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
+    'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.',
+    'social_account_existing' => 'This :socialAccount is already attached to your profile.',
+    'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+    'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ',
+    'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+
+    // Attachments
+    'attachment_page_mismatch' => 'Page mismatch during attachment update',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+
+    // Error pages
+    '404_page_not_found' => 'Page Not Found',
+    'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
+    'return_home' => 'Return to home',
+    'error_occurred' => 'An Error Occurred',
+    'app_down' => ':appName is down right now',
+    'back_soon' => 'It will be back up soon.',
+];
diff --git a/resources/lang/es/pagination.php b/resources/lang/es/pagination.php
new file mode 100644
index 000000000..325916dc3
--- /dev/null
+++ b/resources/lang/es/pagination.php
@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Pagination Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used by the paginator library to build
+    | the simple pagination links. You are free to change them to anything
+    | you want to customize your views to better match your application.
+    |
+    */
+
+    'previous' => '&laquo; Anterior',
+    'next'     => 'Siguiente &raquo;',
+
+];
diff --git a/resources/lang/es/passwords.php b/resources/lang/es/passwords.php
new file mode 100644
index 000000000..3fa17b066
--- /dev/null
+++ b/resources/lang/es/passwords.php
@@ -0,0 +1,22 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reminder Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are the default lines which match reasons
+    | that are given by the password broker for a password update attempt
+    | has failed, such as for an invalid token or invalid new password.
+    |
+    */
+
+    'password' => 'El Password debe ser como mínimo de seis caracteres y coincidir con la confirmación.',
+    'user' => "No podemos encontrar un usuario con esta dirección de e-mail.",
+    'token' => 'El token de reset del password es inválido.',
+    'sent' => 'Hemos enviado a su cuenta de e-mail un link para restaurar su password!',
+    'reset' => 'Su password ha sido restaurado!',
+
+];
diff --git a/resources/lang/es/settings.php b/resources/lang/es/settings.php
new file mode 100644
index 000000000..ed8a0db43
--- /dev/null
+++ b/resources/lang/es/settings.php
@@ -0,0 +1,123 @@
+<?php
+
+return [
+
+    /**
+     * Settings text strings
+     * Contains all text strings used in the general settings sections of BookStack
+     * including users and roles.
+     */
+
+    'settings' => 'Settings',
+    'settings_save' => 'Save Settings',
+    'settings_save_success' => 'Settings saved',
+
+    /**
+     * App settings
+     */
+
+    'app_settings' => 'App Settings',
+    'app_name' => 'Application name',
+    'app_name_desc' => 'This name is shown in the header and any emails.',
+    'app_name_header' => 'Show Application name in header?',
+    'app_public_viewing' => 'Allow public viewing?',
+    'app_secure_images' => 'Enable higher security image uploads?',
+    'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.',
+    'app_editor' => 'Page editor',
+    'app_editor_desc' => 'Select which editor will be used by all users to edit pages.',
+    'app_custom_html' => 'Custom HTML head content',
+    'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
+    'app_logo' => 'Application logo',
+    'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
+    'app_primary_color' => 'Application primary color',
+    'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
+
+    /**
+     * Registration settings
+     */
+
+    'reg_settings' => 'Registration Settings',
+    'reg_allow' => 'Allow registration?',
+    'reg_default_role' => 'Default user role after registration',
+    'reg_confirm_email' => 'Require email confirmation?',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and the below value will be ignored.',
+    'reg_confirm_restrict_domain' => 'Restrict registration to domain',
+    'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+    'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
+
+    /**
+     * Role settings
+     */
+
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Edit Role',
+    'role_details' => 'Role Details',
+    'role_name' => 'Role Name',
+    'role_desc' => 'Short Description of Role',
+    'role_system' => 'System Permissions',
+    'role_manage_users' => 'Manage users',
+    'role_manage_roles' => 'Manage roles & role permissions',
+    'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions',
+    'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
+    'role_manage_settings' => 'Manage app settings',
+    'role_asset' => 'Asset Permissions',
+    'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
+    'role_all' => 'All',
+    'role_own' => 'Own',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    /**
+     * Users
+     */
+
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_role' => 'User Roles',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_password_warning' => 'Only fill the below if you would like to change your password:',
+    'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+    'users_delete' => 'Delete User',
+    'users_delete_named' => 'Delete user :userName',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_delete_success' => 'Users successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => 'This image should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
+    'users_social_accounts' => 'Social Accounts',
+    'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+
+    // Since these labels are already localized this array does not need to be
+    // translated in the language-specific files.
+    // DELETE BELOW IF COPIED FROM EN
+    ///////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'de' => 'Deutsch',
+        'fr' => 'Français',
+        'pt_BR' => 'Português do Brasil'
+    ]
+    ///////////////////////////////////
+];
diff --git a/resources/lang/es/validation.php b/resources/lang/es/validation.php
new file mode 100644
index 000000000..b75af7485
--- /dev/null
+++ b/resources/lang/es/validation.php
@@ -0,0 +1,108 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines contain the default error messages used by
+    | the validator class. Some of these rules have multiple versions such
+    | as the size rules. Feel free to tweak each of these messages here.
+    |
+    */
+
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, and dashes.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'filled'               => 'The :attribute field is required.',
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        'string'  => 'The :attribute must be at least :min characters.',
+        'array'   => 'The :attribute must have at least :min items.',
+    ],
+    'not_in'               => 'The selected :attribute is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify custom validation messages for attributes using the
+    | convention "attribute.rule" to name the lines. This makes it quick to
+    | specify a specific custom language line for a given attribute rule.
+    |
+    */
+
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Attributes
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used to swap attribute place-holders
+    | with something more reader friendly such as E-Mail Address instead
+    | of "email". This simply helps us make messages a little cleaner.
+    |
+    */
+
+    'attributes' => [],
+
+];

From 36173eb47d1c48ba6225f7c44caef79eabd4b8f0 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Mar 2017 14:10:55 +0000
Subject: [PATCH 22/27] Removed extension from translation script link

Also fixed bug causing EN translation backup to not be passed
to javascript translation system.

Closes #328
---
 app/Http/Controllers/HomeController.php | 13 +++++--------
 resources/views/base.blade.php          |  2 +-
 routes/web.php                          |  2 +-
 3 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
index f4706a5c4..7892fe8ae 100644
--- a/app/Http/Controllers/HomeController.php
+++ b/app/Http/Controllers/HomeController.php
@@ -1,10 +1,7 @@
-<?php
-
-namespace BookStack\Http\Controllers;
+<?php namespace BookStack\Http\Controllers;
 
 use Activity;
 use BookStack\Repos\EntityRepo;
-use BookStack\Http\Requests;
 use Illuminate\Http\Response;
 use Views;
 
@@ -63,10 +60,10 @@ class HomeController extends Controller
             ];
             if ($locale !== 'en') {
                 $enTrans = [
-                    'common' => trans('common', [], null, 'en'),
-                    'components' => trans('components', [], null, 'en'),
-                    'entities' => trans('entities', [], null, 'en'),
-                    'errors' => trans('errors', [], null, 'en')
+                    'common' => trans('common', [], 'en'),
+                    'components' => trans('components', [], 'en'),
+                    'entities' => trans('entities', [], 'en'),
+                    'errors' => trans('errors', [], 'en')
                 ];
                 $translations = array_replace_recursive($enTrans, $translations);
             }
diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php
index bb00ce19e..4287014c2 100644
--- a/resources/views/base.blade.php
+++ b/resources/views/base.blade.php
@@ -17,7 +17,7 @@
     <!-- Scripts -->
     <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script>
     <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script>
-    <script src="{{ baseUrl('/translations.js') }}"></script>
+    <script src="{{ baseUrl('/translations') }}"></script>
 
     @yield('head')
 
diff --git a/routes/web.php b/routes/web.php
index 4bd2b4a06..8259a633b 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,6 +1,6 @@
 <?php
 
-Route::get('/translations.js', 'HomeController@getTranslations');
+Route::get('/translations', 'HomeController@getTranslations');
 
 // Authenticated routes...
 Route::group(['middleware' => 'auth'], function () {

From 9b35aa42a20f3a58fccb4747142f6ad34adbd17d Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Mar 2017 14:43:43 +0000
Subject: [PATCH 23/27] Fixed spanish encoding, Added new lang to settings

---
 resources/lang/en/settings.php |   4 +-
 resources/lang/es/entities.php | 128 ++++++++++++++++-----------------
 resources/lang/es/settings.php |  11 ---
 3 files changed, 67 insertions(+), 76 deletions(-)

diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index ed8a0db43..fa60f99a8 100644
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -116,8 +116,10 @@ return [
     'language_select' => [
         'en' => 'English',
         'de' => 'Deutsch',
+        'es' => 'Español',
         'fr' => 'Français',
-        'pt_BR' => 'Português do Brasil'
+        'nl' => 'Nederlands',
+        'pt_BR' => 'Português do Brasil',
     ]
     ///////////////////////////////////
 ];
diff --git a/resources/lang/es/entities.php b/resources/lang/es/entities.php
index 917a25093..d3a5ec4bd 100644
--- a/resources/lang/es/entities.php
+++ b/resources/lang/es/entities.php
@@ -5,9 +5,9 @@ return [
      * Shared
      */
     'recently_created' => 'Recientemente creadod',
-    'recently_created_pages' => 'P�ginas recientemente creadas',
-    'recently_updated_pages' => 'P�ginas recientemente actualizadas',
-    'recently_created_chapters' => 'Cap�tulos recientemente creados',
+    'recently_created_pages' => 'Páginas recientemente creadas',
+    'recently_updated_pages' => 'Páginas recientemente actualizadas',
+    'recently_created_chapters' => 'Capítulos recientemente creados',
     'recently_created_books' => 'Libros recientemente creados',
     'recently_update' => 'Recientemente actualizado',
     'recently_viewed' => 'Recientemente visto',
@@ -18,20 +18,20 @@ return [
     'meta_created_name' => 'Creado el  :timeLength por :user',
     'meta_updated' => 'Actualizado el :timeLength',
     'meta_updated_name' => 'Actualizado el :timeLength por :user',
-    'x_pages' => ':count P�ginas',
+    'x_pages' => ':count Páginas',
     'entity_select' => 'Seleccione entidad',
-    'images' => 'Im�genes',
+    'images' => 'Imágenes',
     'my_recent_drafts' => 'Mis borradores recientes',
     'my_recently_viewed' => 'Mis visualizaciones recientes',
-    'no_pages_viewed' => 'Ud. no ha visto ninguna p�gina',
-    'no_pages_recently_created' => 'Ninguna p�gina ha sido creada recientemente',
-    'no_pages_recently_updated' => 'Ninguna p�gina ha sido actualizada recientemente',
+    'no_pages_viewed' => 'Ud. no ha visto ninguna página',
+    'no_pages_recently_created' => 'Ninguna página ha sido creada recientemente',
+    'no_pages_recently_updated' => 'Ninguna página ha sido actualizada recientemente',
 
     /**
      * Permissions and restrictions
      */
     'permissions' => 'Permisos',
-    'permissions_intro' => 'una vez habilitado, Estos permisos tendr�n prioridad por encima de cualquier permiso establecido.',
+    'permissions_intro' => 'una vez habilitado, Estos permisos tendrán prioridad por encima de cualquier permiso establecido.',
     'permissions_enable' => 'Habilitar permisos custom',
     'permissions_save' => 'Guardar permisos',
 
@@ -39,18 +39,18 @@ return [
      * Search
      */
     'search_results' => 'Buscar resultados',
-    'search_results_page' => 'resultados de b�squeda en p�gina',
-    'search_results_chapter' => 'Resultados de b�squeda en cap�tulo ',
-    'search_results_book' => 'Resultados de b�squeda en libro',
+    'search_results_page' => 'resultados de búsqueda en página',
+    'search_results_chapter' => 'Resultados de búsqueda en capítulo ',
+    'search_results_book' => 'Resultados de búsqueda en libro',
     'search_clear' => 'Limpiar resultados',
-    'search_view_pages' => 'Ver todas las p�ginas que concuerdan',
-    'search_view_chapters' => 'Ver todos los cap�tulos que concuerdan',
+    'search_view_pages' => 'Ver todas las páginas que concuerdan',
+    'search_view_chapters' => 'Ver todos los capítulos que concuerdan',
     'search_view_books' => 'Ver todos los libros que concuerdan',
-    'search_no_pages' => 'Ninguna p�gina encontrada para la b�squeda',
+    'search_no_pages' => 'Ninguna página encontrada para la búsqueda',
     'search_for_term' => 'Busqueda por :term',
-    'search_page_for_term' => 'B�squeda de p�gina por :term',
-    'search_chapter_for_term' => 'B�squeda por cap�tulo de :term',
-    'search_book_for_term' => 'B�squeda en libro de :term',
+    'search_page_for_term' => 'Búsqueda de página por :term',
+    'search_chapter_for_term' => 'Búsqueda por capítulo de :term',
+    'search_book_for_term' => 'Búsqueda en libro de :term',
 
     /**
      * Books
@@ -60,26 +60,26 @@ return [
     'books_empty' => 'No hay libros creados',
     'books_popular' => 'Libros populares',
     'books_recent' => 'Libros recientes',
-    'books_popular_empty' => 'Los libros m�s populares aparecer�n aquí.',
+    'books_popular_empty' => 'Los libros más populares aparecerán aquí.',
     'books_create' => 'Crear nuevo libro',
     'books_delete' => 'Borrar libro',
     'books_delete_named' => 'Borrar libro :bookName',
     'books_delete_explain' => 'Esto borrará el libro con el nombre \':bookName\', Todos las páginas y capítulos serán removios.',
-    'books_delete_confirmation' => '�Está seguro de que desea borrar este libro?',
+    'books_delete_confirmation' => '¿Está seguro de que desea borrar este libro?',
     'books_edit' => 'Editar Libro',
     'books_edit_named' => 'Editar Libro :bookName',
     'books_form_book_name' => 'Nombre de libro',
     'books_save' => 'Guardar libro',
     'books_permissions' => 'permisos de libro',
     'books_permissions_updated' => 'Permisos de libro actualizados',
-    'books_empty_contents' => 'Ninguna p�gina o cap�tulo ha sido creada para este libro.',
-    'books_empty_create_page' => 'Crear una nueva p�gina',
+    'books_empty_contents' => 'Ninguna página o capítulo ha sido creada para este libro.',
+    'books_empty_create_page' => 'Crear una nueva página',
     'books_empty_or' => 'ó',
     'books_empty_sort_current_book' => 'Organizar el libro actual',
-    'books_empty_add_chapter' => 'Agregar un cap�tulo',
+    'books_empty_add_chapter' => 'Agregar un capítulo',
     'books_permissions_active' => 'Permisos de libro activados',
     'books_search_this' => 'Buscar en este libro',
-    'books_navigation' => 'Navegaci�n de libro',
+    'books_navigation' => 'Navegación de libro',
     'books_sort' => 'Organizar contenido de libro',
     'books_sort_named' => 'Organizar libro :bookName',
     'books_sort_show_other' => 'Mostrar otros libros',
@@ -88,59 +88,59 @@ return [
     /**
      * Chapters
      */
-    'chapter' => 'Cap�tulo',
-    'chapters' => 'Cap�tulos',
-    'chapters_popular' => 'Cap�tulos populares',
-    'chapters_new' => 'Nuevo cap�tulo',
-    'chapters_create' => 'Crear nuevo cap�tulo',
-    'chapters_delete' => 'Borrar cap�tulo',
-    'chapters_delete_named' => 'Borrar cap�tulo :chapterName',
-    'chapters_delete_explain' => 'Esto borrará el caítulo con el nombre \':chapterName\', todas las p�ginas ser�n removidas
+    'chapter' => 'Capítulo',
+    'chapters' => 'Capítulos',
+    'chapters_popular' => 'Capítulos populares',
+    'chapters_new' => 'Nuevo capítulo',
+    'chapters_create' => 'Crear nuevo capítulo',
+    'chapters_delete' => 'Borrar capítulo',
+    'chapters_delete_named' => 'Borrar capítulo :chapterName',
+    'chapters_delete_explain' => 'Esto borrará el caítulo con el nombre \':chapterName\', todas las páginas serán removidas
         y agregadas directamente al libro padre.',
-    'chapters_delete_confirm' => 'Est� ud. seguro de borrar este cap�tulo?',
-    'chapters_edit' => 'Editar cap�tulo',
-    'chapters_edit_named' => 'Editar cap�tulo :chapterName',
-    'chapters_save' => 'Guardar cap�tulo',
-    'chapters_move' => 'Mover cap�tulo',
-    'chapters_move_named' => 'Mover Cap�tulo :chapterName',
-    'chapter_move_success' => 'Cap�tulo movido a :bookName',
-    'chapters_permissions' => 'Permisos de cap�tulo',
-    'chapters_empty' => 'No existen p�ginas en este cap�tulo.',
-    'chapters_permissions_active' => 'Permisos de cap�tulo activado',
-    'chapters_permissions_success' => 'Permisos de cap�tulo actualizados',
+    'chapters_delete_confirm' => 'Está ud. seguro de borrar este capítulo?',
+    'chapters_edit' => 'Editar capítulo',
+    'chapters_edit_named' => 'Editar capítulo :chapterName',
+    'chapters_save' => 'Guardar capítulo',
+    'chapters_move' => 'Mover capítulo',
+    'chapters_move_named' => 'Mover Capítulo :chapterName',
+    'chapter_move_success' => 'Capítulo movido a :bookName',
+    'chapters_permissions' => 'Permisos de capítulo',
+    'chapters_empty' => 'No existen páginas en este capítulo.',
+    'chapters_permissions_active' => 'Permisos de capítulo activado',
+    'chapters_permissions_success' => 'Permisos de capítulo actualizados',
 
     /**
      * Pages
      */
-    'page' => 'P�gina',
-    'pages' => 'P�ginas',
-    'pages_popular' => 'P�ginas populares',
-    'pages_new' => 'Nueva p�gina',
+    'page' => 'Página',
+    'pages' => 'Páginas',
+    'pages_popular' => 'Páginas populares',
+    'pages_new' => 'Nueva página',
     'pages_attachments' => 'Adjuntos',
-    'pages_navigation' => 'Navegaci�n de p�gina',
-    'pages_delete' => 'Borrar p�gina',
-    'pages_delete_named' => 'Borrar p�gina :pageName',
-    'pages_delete_draft_named' => 'Borrar borrador de p�gina :pageName',
-    'pages_delete_draft' => 'Borrar borrador de p�gina',
-    'pages_delete_success' => 'P�gina borrada',
-    'pages_delete_draft_success' => 'Borrador de p�gina borrado',
-    'pages_delete_confirm' => 'Est� ud. seguro de borrar esta p�gina ?',
-    'pages_delete_draft_confirm' => 'Est� ud. seguro de que desea borrar este borrador de p�gina?',
-    'pages_editing_named' => 'Editando p�gina :pageName',
-    'pages_edit_toggle_header' => 'Toggle T�tulo',
+    'pages_navigation' => 'Navegación de página',
+    'pages_delete' => 'Borrar página',
+    'pages_delete_named' => 'Borrar página :pageName',
+    'pages_delete_draft_named' => 'Borrar borrador de página :pageName',
+    'pages_delete_draft' => 'Borrar borrador de página',
+    'pages_delete_success' => 'Página borrada',
+    'pages_delete_draft_success' => 'Borrador de página borrado',
+    'pages_delete_confirm' => 'Está ud. seguro de borrar esta página ?',
+    'pages_delete_draft_confirm' => 'Está ud. seguro de que desea borrar este borrador de página?',
+    'pages_editing_named' => 'Editando página :pageName',
+    'pages_edit_toggle_header' => 'Toggle T�tulo',
     'pages_edit_save_draft' => 'Guardar borrador',
-    'pages_edit_draft' => 'Editar borrador de p�gina',
+    'pages_edit_draft' => 'Editar borrador de página',
     'pages_editing_draft' => 'Editando borrador',
-    'pages_editing_page' => 'Editando p�gina',
+    'pages_editing_page' => 'Editando página',
     'pages_edit_draft_save_at' => 'Borrador guardado el ',
     'pages_edit_delete_draft' => 'Borrar borrador',
     'pages_edit_discard_draft' => 'Descartar borrador',
     'pages_edit_set_changelog' => 'Set Changelog',
-    'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripci�n de los cambios que ha realizado',
+    'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado',
     'pages_edit_enter_changelog' => 'Entrar en Changelog',
-    'pages_save' => 'Guardar p�gina',
-    'pages_title' => 'T�tulo de p�gina',
-    'pages_name' => 'Nombre de p�gina',
+    'pages_save' => 'Guardar página',
+    'pages_title' => 'T�tulo de página',
+    'pages_name' => 'Nombre de página',
     'pages_md_editor' => 'Editor',
     'pages_md_preview' => 'Preview',
     'pages_md_insert_image' => 'Insertar Imagen',
diff --git a/resources/lang/es/settings.php b/resources/lang/es/settings.php
index ed8a0db43..cf926ae52 100644
--- a/resources/lang/es/settings.php
+++ b/resources/lang/es/settings.php
@@ -109,15 +109,4 @@ return [
     'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
     'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
 
-    // Since these labels are already localized this array does not need to be
-    // translated in the language-specific files.
-    // DELETE BELOW IF COPIED FROM EN
-    ///////////////////////////////////
-    'language_select' => [
-        'en' => 'English',
-        'de' => 'Deutsch',
-        'fr' => 'Français',
-        'pt_BR' => 'Português do Brasil'
-    ]
-    ///////////////////////////////////
 ];

From d4e790d3cf32c1ee406795fd188def39f8e83d50 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Mar 2017 15:10:06 +0000
Subject: [PATCH 24/27] Added lang tests and update export text keys

---
 resources/lang/de/entities.php          |  8 ++++----
 resources/lang/en/entities.php          |  8 ++++----
 resources/lang/es/entities.php          | 12 ++++++------
 resources/lang/fr/entities.php          |  8 ++++----
 resources/lang/nl/entities.php          |  8 ++++----
 resources/lang/pt_BR/entities.php       |  8 ++++----
 resources/views/books/show.blade.php    |  8 ++++----
 resources/views/chapters/show.blade.php |  8 ++++----
 resources/views/pages/show.blade.php    |  8 ++++----
 tests/LanguageTest.php                  | 21 +++++++++++++++++++++
 10 files changed, 59 insertions(+), 38 deletions(-)
 create mode 100644 tests/LanguageTest.php

diff --git a/resources/lang/de/entities.php b/resources/lang/de/entities.php
index ff590bb88..2859e4ec5 100644
--- a/resources/lang/de/entities.php
+++ b/resources/lang/de/entities.php
@@ -26,6 +26,10 @@ return [
     'no_pages_viewed' => 'Sie haben bisher keine Seiten angesehen.',
     'no_pages_recently_created' => 'Sie haben bisher keine Seiten angelegt.',
     'no_pages_recently_updated' => 'Sie haben bisher keine Seiten aktualisiert.',
+    'export' => 'Exportieren',
+    'export_html' => 'HTML-Datei',
+    'export_pdf' => 'PDF-Datei',
+    'export_text' => 'Text-Datei',
 
     /**
      * Permissions and restrictions
@@ -160,10 +164,6 @@ return [
     'pages_revisions_preview' => 'Vorschau',
     'pages_revisions_restore' => 'Zur&uuml;ck sichern',
     'pages_revisions_none' => 'Diese Seite hat keine &auml;lteren Versionen.',
-    'pages_export' => 'Exportieren',
-    'pages_export_html' => 'HTML-Datei',
-    'pages_export_pdf' => 'PDF-Datei',
-    'pages_export_text' => 'Text-Datei',
     'pages_copy_link' => 'Link kopieren',
     'pages_permissions_active' => 'Seiten-Berechtigungen aktiv',
     'pages_initial_revision' => 'Erste Ver&ouml;ffentlichung',
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index 109b6ee2a..f54134718 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -26,6 +26,10 @@ return [
     'no_pages_viewed' => 'You have not viewed any pages',
     'no_pages_recently_created' => 'No pages have been recently created',
     'no_pages_recently_updated' => 'No pages have been recently updated',
+    'export' => 'Export',
+    'export_html' => 'Contained Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Plain Text File',
 
     /**
      * Permissions and restrictions
@@ -161,10 +165,6 @@ return [
     'pages_revisions_preview' => 'Preview',
     'pages_revisions_restore' => 'Restore',
     'pages_revisions_none' => 'This page has no revisions',
-    'pages_export' => 'Export',
-    'pages_export_html' => 'Contained Web File',
-    'pages_export_pdf' => 'PDF File',
-    'pages_export_text' => 'Plain Text File',
     'pages_copy_link' => 'Copy Link',
     'pages_permissions_active' => 'Page Permissions Active',
     'pages_initial_revision' => 'Initial publish',
diff --git a/resources/lang/es/entities.php b/resources/lang/es/entities.php
index d3a5ec4bd..14e952f1a 100644
--- a/resources/lang/es/entities.php
+++ b/resources/lang/es/entities.php
@@ -26,6 +26,10 @@ return [
     'no_pages_viewed' => 'Ud. no ha visto ninguna página',
     'no_pages_recently_created' => 'Ninguna página ha sido creada recientemente',
     'no_pages_recently_updated' => 'Ninguna página ha sido actualizada recientemente',
+    'export' => 'Export',
+    'export_html' => 'Contained Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Plain Text File',
 
     /**
      * Permissions and restrictions
@@ -127,7 +131,7 @@ return [
     'pages_delete_confirm' => 'Está ud. seguro de borrar esta página ?',
     'pages_delete_draft_confirm' => 'Está ud. seguro de que desea borrar este borrador de página?',
     'pages_editing_named' => 'Editando página :pageName',
-    'pages_edit_toggle_header' => 'Toggle T�tulo',
+    'pages_edit_toggle_header' => 'Toggle Título',
     'pages_edit_save_draft' => 'Guardar borrador',
     'pages_edit_draft' => 'Editar borrador de página',
     'pages_editing_draft' => 'Editando borrador',
@@ -139,7 +143,7 @@ return [
     'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado',
     'pages_edit_enter_changelog' => 'Entrar en Changelog',
     'pages_save' => 'Guardar página',
-    'pages_title' => 'T�tulo de página',
+    'pages_title' => 'Título de página',
     'pages_name' => 'Nombre de página',
     'pages_md_editor' => 'Editor',
     'pages_md_preview' => 'Preview',
@@ -161,10 +165,6 @@ return [
     'pages_revisions_preview' => 'Preview',
     'pages_revisions_restore' => 'Restore',
     'pages_revisions_none' => 'This page has no revisions',
-    'pages_export' => 'Export',
-    'pages_export_html' => 'Contained Web File',
-    'pages_export_pdf' => 'PDF File',
-    'pages_export_text' => 'Plain Text File',
     'pages_copy_link' => 'Copy Link',
     'pages_permissions_active' => 'Page Permissions Active',
     'pages_initial_revision' => 'Initial publish',
diff --git a/resources/lang/fr/entities.php b/resources/lang/fr/entities.php
index 941259f80..cfd206b91 100644
--- a/resources/lang/fr/entities.php
+++ b/resources/lang/fr/entities.php
@@ -26,6 +26,10 @@ return [
     'no_pages_viewed' => 'Vous n\'avez rien visité récemment',
     'no_pages_recently_created' => 'Aucune page créée récemment',
     'no_pages_recently_updated' => 'Aucune page mise à jour récemment',
+    'export' => 'Exporter',
+    'export_html' => 'Fichiers web',
+    'export_pdf' => 'Fichier PDF',
+    'export_text' => 'Document texte',
 
     /**
      * Permissions and restrictions
@@ -160,10 +164,6 @@ return [
     'pages_revisions_preview' => 'Prévisualisation',
     'pages_revisions_restore' => 'Restaurer',
     'pages_revisions_none' => 'Cette page n\'a aucune révision',
-    'pages_export' => 'Exporter',
-    'pages_export_html' => 'Fichiers web',
-    'pages_export_pdf' => 'Fichier PDF',
-    'pages_export_text' => 'Document texte',
     'pages_copy_link' => 'Copier le lien',
     'pages_permissions_active' => 'Permissions de page actives',
     'pages_initial_revision' => 'Publication initiale',
diff --git a/resources/lang/nl/entities.php b/resources/lang/nl/entities.php
index 46e69549b..610116c8b 100644
--- a/resources/lang/nl/entities.php
+++ b/resources/lang/nl/entities.php
@@ -26,6 +26,10 @@ return [
     'no_pages_viewed' => 'Je hebt nog niets bekeken',
     'no_pages_recently_created' => 'Er zijn geen recent aangemaakte pagina\'s',
     'no_pages_recently_updated' => 'Er zijn geen recente wijzigingen',
+    'export' => 'Exporteren',
+    'export_html' => 'Contained Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Plain Text File',
 
     /**
      * Permissions and restrictions
@@ -161,10 +165,6 @@ return [
     'pages_revisions_preview' => 'Preview',
     'pages_revisions_restore' => 'Herstellen',
     'pages_revisions_none' => 'Deze pagina heeft geen revisies',
-    'pages_export' => 'Exporteren',
-    'pages_export_html' => 'Contained Web File',
-    'pages_export_pdf' => 'PDF File',
-    'pages_export_text' => 'Plain Text File',
     'pages_copy_link' => 'Link Kopiëren',
     'pages_permissions_active' => 'Pagina Permissies Actief',
     'pages_initial_revision' => 'Eerste publicatie',
diff --git a/resources/lang/pt_BR/entities.php b/resources/lang/pt_BR/entities.php
index a6e670353..922342424 100644
--- a/resources/lang/pt_BR/entities.php
+++ b/resources/lang/pt_BR/entities.php
@@ -26,6 +26,10 @@ return [
     'no_pages_viewed' => 'Você não visualizou nenhuma página',
     'no_pages_recently_created' => 'Nenhuma página recentemente criada',
     'no_pages_recently_updated' => 'Nenhuma página recentemente atualizada',
+    'export' => 'Exportar',
+    'export_html' => 'Arquivo Web Contained',
+    'export_pdf' => 'Arquivo PDF',
+    'export_text' => 'Arquivo Texto',
 
     /**
      * Permissions and restrictions
@@ -161,10 +165,6 @@ return [
     'pages_revisions_preview' => 'Preview',
     'pages_revisions_restore' => 'Restaurar',
     'pages_revisions_none' => 'Essa página não tem revisões',
-    'pages_export' => 'Exportar',
-    'pages_export_html' => 'Arquivo Web Contained',
-    'pages_export_pdf' => 'Arquivo PDF',
-    'pages_export_text' => 'Arquivo Texto',
     'pages_copy_link' => 'Copia Link',
     'pages_permissions_active' => 'Permissões de Página Ativas',
     'pages_initial_revision' => 'Publicação Inicial',
diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php
index 99ffe80e1..f5e08b2f6 100644
--- a/resources/views/books/show.blade.php
+++ b/resources/views/books/show.blade.php
@@ -11,11 +11,11 @@
                 <div class="col-sm-6">
                     <div class="action-buttons faded">
                         <span dropdown class="dropdown-container">
-                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
+                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
                             <ul class="wide">
-                                <li><a href="{{ $book->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
-                                <li><a href="{{ $book->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                                <li><a href="{{ $book->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                                <li><a href="{{ $book->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                                <li><a href="{{ $book->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                                <li><a href="{{ $book->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
                             </ul>
                         </span>
                         @if(userCan('page-create', $book))
diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php
index 47a1d9ddf..28c34eef2 100644
--- a/resources/views/chapters/show.blade.php
+++ b/resources/views/chapters/show.blade.php
@@ -11,11 +11,11 @@
                 <div class="col-sm-4 faded">
                     <div class="action-buttons">
                         <span dropdown class="dropdown-container">
-                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
+                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
                             <ul class="wide">
-                                <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
-                                <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                                <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
                             </ul>
                         </span>
                         @if(userCan('page-create', $chapter))
diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php
index fd6cebf41..6462225ea 100644
--- a/resources/views/pages/show.blade.php
+++ b/resources/views/pages/show.blade.php
@@ -11,11 +11,11 @@
                 <div class="col-sm-6 faded">
                     <div class="action-buttons">
                         <span dropdown class="dropdown-container">
-                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
+                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
                             <ul class="wide">
-                                <li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
-                                <li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                                <li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                                <li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                                <li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                                <li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
                             </ul>
                         </span>
                         @if(userCan('page-update', $page))
diff --git a/tests/LanguageTest.php b/tests/LanguageTest.php
new file mode 100644
index 000000000..ed5ee7dee
--- /dev/null
+++ b/tests/LanguageTest.php
@@ -0,0 +1,21 @@
+<?php namespace Tests;
+
+class LanguageTest extends TestCase
+{
+
+    public function test_js_endpoint_for_each_language() {
+
+        $langs = array_diff(scandir(resource_path('lang')), ['..', '.']);
+        $visibleKeys = ['common', 'components', 'entities', 'errors'];
+
+        $this->asEditor();
+        foreach ($langs as $lang) {
+            setting()->putUser($this->getEditor(), 'language', $lang);
+            $transResp = $this->get('/translations');
+            foreach ($visibleKeys as $key) {
+                $transResp->assertSee($key);
+            }
+        }
+    }
+
+}
\ No newline at end of file

From 4499ae84bb129cbf1b041edcc9eaf8038b1d24d0 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 5 Mar 2017 15:34:54 +0000
Subject: [PATCH 25/27] Made fixes to es languge files and users page

Fixed PHP formatting error in ES lang file and added tests to cover.
Made user edit page more responsive on smaller devices.
Fixed 'cancel' button on profile screen when the user does not have
permission to manage users.
---
 resources/lang/es/activities.php              |  2 +-
 resources/views/users/create.blade.php        |  4 ++
 resources/views/users/edit.blade.php          | 62 ++++++++++---------
 resources/views/users/forms/ldap.blade.php    |  7 +--
 .../views/users/forms/standard.blade.php      |  8 +--
 resources/views/users/forms/system.blade.php  |  5 --
 tests/LanguageTest.php                        | 33 +++++++++-
 7 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/resources/lang/es/activities.php b/resources/lang/es/activities.php
index 1556f4530..649b8c7d2 100644
--- a/resources/lang/es/activities.php
+++ b/resources/lang/es/activities.php
@@ -13,7 +13,7 @@ return [
     'page_update'                 => 'página actualizada',
     'page_update_notification'    => 'Página actualizada exitosamente',
     'page_delete'                 => 'página borrada',
-    'page_delete_notification'    => 'Página borrada exitosamente,
+    'page_delete_notification'    => 'Página borrada exitosamente',
     'page_restore'                => 'página restaurada',
     'page_restore_notification'   => 'Página restaurada exitosamente',
     'page_move'                   => 'página movida',
diff --git a/resources/views/users/create.blade.php b/resources/views/users/create.blade.php
index de3a3151f..e0019e618 100644
--- a/resources/views/users/create.blade.php
+++ b/resources/views/users/create.blade.php
@@ -21,6 +21,10 @@
         <form action="{{ baseUrl("/settings/users/create") }}" method="post">
             {!! csrf_field() !!}
             @include('users/forms/' . $authMethod)
+            <div class="form-group">
+                <a href="{{  baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
+                <button class="button pos" type="submit">{{ trans('common.save') }}</button>
+            </div>
         </form>
     </div>
 
diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php
index bf10b829d..ff3475194 100644
--- a/resources/views/users/edit.blade.php
+++ b/resources/views/users/edit.blade.php
@@ -19,38 +19,42 @@
                 </div>
             </div>
             <div class="row">
-            <div class="col-md-6" ng-non-bindable>
-                {!! csrf_field() !!}
-                <input type="hidden" name="_method" value="put">
-                @include('users.forms.' . $authMethod, ['model' => $user])
+                <div class="col-sm-6" ng-non-bindable>
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="put">
+                    @include('users.forms.' . $authMethod, ['model' => $user])
 
-            </div>
-            <div class="col-md-6">
-                <div class="form-group" id="logo-control">
-                    <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
-                    <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
-
-                    @include('components.image-picker', [
-                          'resizeHeight' => '512',
-                          'resizeWidth' => '512',
-                          'showRemove' => false,
-                          'defaultImage' => baseUrl('/user_avatar.png'),
-                          'currentImage' => $user->getAvatar(80),
-                          'currentId' => $user->image_id,
-                          'name' => 'image_id',
-                          'imageClass' => 'avatar large'
-                      ])
                 </div>
-                <div class="form-group">
-                    <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
-                    <select name="setting[language]" id="user-language">
-                        @foreach(trans('settings.language_select') as $lang => $label)
-                            <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
-                        @endforeach
-                    </select>
+                <div class="col-sm-6">
+                    <div class="form-group" id="logo-control">
+                        <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
+                        <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
+
+                        @include('components.image-picker', [
+                              'resizeHeight' => '512',
+                              'resizeWidth' => '512',
+                              'showRemove' => false,
+                              'defaultImage' => baseUrl('/user_avatar.png'),
+                              'currentImage' => $user->getAvatar(80),
+                              'currentId' => $user->image_id,
+                              'name' => 'image_id',
+                              'imageClass' => 'avatar large'
+                          ])
+                    </div>
+                    <div class="form-group">
+                        <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
+                        <select name="setting[language]" id="user-language">
+                            @foreach(trans('settings.language_select') as $lang => $label)
+                                <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
+                            @endforeach
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>
+            <div class="form-group">
+                <a href="{{  baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
+                <button class="button pos" type="submit">{{ trans('common.save') }}</button>
+            </div>
         </form>
 
         <hr class="margin-top large">
@@ -60,7 +64,7 @@
             <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
             <div class="row">
                 @foreach($activeSocialDrivers as $driver => $enabled)
-                    <div class="col-md-3 text-center">
+                    <div class="col-sm-3 col-xs-6 text-center">
                         <div>@icon($driver, ['width' => 56])</div>
                         <div>
                             @if($user->hasSocialAccount($driver))
diff --git a/resources/views/users/forms/ldap.blade.php b/resources/views/users/forms/ldap.blade.php
index 0a6cf79ae..f6e8b4c80 100644
--- a/resources/views/users/forms/ldap.blade.php
+++ b/resources/views/users/forms/ldap.blade.php
@@ -22,9 +22,4 @@
         <label for="external_auth_id">{{ trans('settings.users_external_auth_id') }}</label>
         @include('form.text', ['name' => 'external_auth_id'])
     </div>
-@endif
-
-<div class="form-group">
-    <a href="{{ baseUrl("/settings/users") }}" class="button muted">{{ trans('common.cancel') }}</a>
-    <button class="button pos" type="submit">{{ trans('common.save') }}</button>
-</div>
\ No newline at end of file
+@endif
\ No newline at end of file
diff --git a/resources/views/users/forms/standard.blade.php b/resources/views/users/forms/standard.blade.php
index 39ae4c770..fa712368b 100644
--- a/resources/views/users/forms/standard.blade.php
+++ b/resources/views/users/forms/standard.blade.php
@@ -31,10 +31,4 @@
 <div class="form-group">
     <label for="password-confirm">{{ trans('auth.password_confirm') }}</label>
     @include('form.password', ['name' => 'password-confirm'])
-</div>
-
-<div class="form-group">
-    <a href="{{ baseUrl("/settings/users") }}" class="button muted">{{ trans('common.cancel') }}</a>
-    <button class="button pos" type="submit">{{ trans('common.save') }}</button>
-</div>
-
+</div>
\ No newline at end of file
diff --git a/resources/views/users/forms/system.blade.php b/resources/views/users/forms/system.blade.php
index 868dad854..6243010a4 100644
--- a/resources/views/users/forms/system.blade.php
+++ b/resources/views/users/forms/system.blade.php
@@ -19,8 +19,3 @@
     </div>
 @endif
 
-<div class="form-group">
-    <a href="{{ baseUrl("/settings/users") }}" class="button muted">{{ trans('common.cancel') }}</a>
-    <button class="button pos" type="submit">{{ trans('common.save') }}</button>
-</div>
-
diff --git a/tests/LanguageTest.php b/tests/LanguageTest.php
index ed5ee7dee..911ac3e81 100644
--- a/tests/LanguageTest.php
+++ b/tests/LanguageTest.php
@@ -3,13 +3,24 @@
 class LanguageTest extends TestCase
 {
 
-    public function test_js_endpoint_for_each_language() {
+    protected $langs;
+
+    /**
+     * LanguageTest constructor.
+     */
+    public function setUp()
+    {
+        parent::setUp();
+        $this->langs = array_diff(scandir(resource_path('lang')), ['..', '.']);
+    }
+
+    public function test_js_endpoint_for_each_language()
+    {
 
-        $langs = array_diff(scandir(resource_path('lang')), ['..', '.']);
         $visibleKeys = ['common', 'components', 'entities', 'errors'];
 
         $this->asEditor();
-        foreach ($langs as $lang) {
+        foreach ($this->langs as $lang) {
             setting()->putUser($this->getEditor(), 'language', $lang);
             $transResp = $this->get('/translations');
             foreach ($visibleKeys as $key) {
@@ -18,4 +29,20 @@ class LanguageTest extends TestCase
         }
     }
 
+    public function test_all_lang_files_loadable()
+    {
+        $files = array_diff(scandir(resource_path('lang/en')), ['..', '.']);
+        foreach ($this->langs as $lang) {
+            foreach ($files as $file) {
+                $loadError = false;
+                try {
+                    $translations = trans(str_replace('.php', '', $file), [], $lang);
+                } catch (\Exception $e) {
+                    $loadError = true;
+                }
+                $this->assertFalse($loadError, "Translation file {$lang}/{$file} failed to load");
+            }
+        }
+    }
+
 }
\ No newline at end of file

From 668ce262690d069db84ef269dc6bd1e832cb4ac6 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 19 Mar 2017 08:32:04 +0000
Subject: [PATCH 26/27] Fixed back button behaviour on books edit

As reported in #339
---
 resources/views/books/form.blade.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/resources/views/books/form.blade.php b/resources/views/books/form.blade.php
index 514abf42c..b1484d129 100644
--- a/resources/views/books/form.blade.php
+++ b/resources/views/books/form.blade.php
@@ -11,6 +11,6 @@
 </div>
 
 <div class="form-group">
-    <a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
+    <a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button muted">{{ trans('common.cancel') }}</a>
     <button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
 </div>
\ No newline at end of file

From cc0ce7c6308985e7524c36214aa965757b4c92df Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Thu, 23 Mar 2017 22:19:14 +0000
Subject: [PATCH 27/27] Fixed bug preventing page revision restore

Added regression tests to cover.
Fixes #341
---
 app/Repos/EntityRepo.php         |  2 +-
 tests/Entity/PageContentTest.php | 27 +++++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php
index 4db69137f..8f4b533ff 100644
--- a/app/Repos/EntityRepo.php
+++ b/app/Repos/EntityRepo.php
@@ -1058,7 +1058,7 @@ class EntityRepo
     public function restorePageRevision(Page $page, Book $book, $revisionId)
     {
         $this->savePageRevision($page);
-        $revision = $this->getById('page_revision', $revisionId);
+        $revision = $page->revisions()->where('id', '=', $revisionId)->first();
         $page->fill($revision->toArray());
         $page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
         $page->text = strip_tags($page->html);
diff --git a/tests/Entity/PageContentTest.php b/tests/Entity/PageContentTest.php
index 6f07b9626..6b64c2c64 100644
--- a/tests/Entity/PageContentTest.php
+++ b/tests/Entity/PageContentTest.php
@@ -53,4 +53,31 @@ class PageContentTest extends TestCase
         $revisionView->assertSee('new content');
     }
 
+    public function test_page_revision_restore_updates_content()
+    {
+        $this->asEditor();
+
+        $entityRepo = $this->app[EntityRepo::class];
+        $page = Page::first();
+        $entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'initial page revision testing']);
+        $entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
+        $page =  Page::find($page->id);
+
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertDontSee('abc123');
+        $pageView->assertDontSee('def456');
+
+        $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
+        $restoreReq = $this->get($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
+        $page =  Page::find($page->id);
+
+        $restoreReq->assertStatus(302);
+        $restoreReq->assertRedirect($page->getUrl());
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertSee('abc123');
+        $pageView->assertSee('def456');
+    }
+
 }