]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'master' into update_japanese_translation 767/head
authormsaus <redacted>
Wed, 28 Mar 2018 02:58:14 +0000 (11:58 +0900)
committerGitHub <redacted>
Wed, 28 Mar 2018 02:58:14 +0000 (11:58 +0900)
47 files changed:
.github/CODE_OF_CONDUCT.md [new file with mode: 0644]
LICENSE
app/Book.php
app/Chapter.php
app/Entity.php
app/Http/Controllers/ImageController.php
app/Http/Controllers/SearchController.php
app/Repos/EntityRepo.php
app/Repos/ImageRepo.php
app/Services/AttachmentService.php
app/Services/ImageService.php
app/Services/SearchService.php
app/Services/UploadService.php
readme.md
resources/assets/js/vues/components/dropzone.js
resources/assets/js/vues/image-manager.js
resources/assets/sass/_blocks.scss
resources/assets/sass/_buttons.scss
resources/assets/sass/_components.scss
resources/assets/sass/_pages.scss
resources/assets/sass/_text.scss
resources/assets/sass/styles.scss
resources/lang/en/components.php
resources/lang/en/errors.php
resources/lang/fr/activities.php
resources/lang/fr/auth.php
resources/lang/fr/common.php
resources/lang/fr/components.php
resources/lang/fr/entities.php
resources/lang/fr/settings.php
resources/views/books/create.blade.php
resources/views/books/index.blade.php
resources/views/books/restrictions.blade.php
resources/views/books/show.blade.php
resources/views/chapters/create.blade.php
resources/views/chapters/form.blade.php
resources/views/components/image-manager.blade.php
resources/views/pages/guest-create.blade.php
resources/views/public.blade.php
resources/views/search/all.blade.php
resources/views/users/edit.blade.php
routes/web.php
tests/Entity/EntityTest.php
tests/Entity/PageDraftTest.php
tests/ImageTest.php
tests/Permissions/RestrictionsTest.php
tests/Permissions/RolesTest.php

diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644 (file)
index 0000000..2c6317a
--- /dev/null
@@ -0,0 +1,84 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+education, socio-economic status, nationality, personal appearance, race,
+religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+  advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+  address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+### Project Maintainer Standards
+
+Project maintainers should generally follow these additional standards:
+
+* Avoid using a negative or harsh tone in communication, Even if the other party
+is being negative themselves.
+* When providing criticism, try to make it constructive to lead the other person
+down the correct path.
+* Keep the [project definition](https://github.com/BookStackApp/BookStack#project-definition)
+in mind when deciding what's in scope of the Project.
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior. In addition, Project
+maintainers are responsible for following the standards themselves.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at the email address shown on [the profile here](https://github.com/ssddanbrown). All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
diff --git a/LICENSE b/LICENSE
index 281814bb8ce2b7e47c238ecd428e6c88869eb0b7..080c54b3e7a66c66ecdd40ea413d7811ab6d4425 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
 The MIT License (MIT)
 
-Copyright (c) 2016 Dan Brown
+Copyright (c) 2018 Dan Brown and the BookStack Project contributors
+https://github.com/BookStackApp/BookStack/graphs/contributors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
index 457a4c928ac5b6f08bdea84f1e13cace325fe338..51ea226b47cda9b9df027e922db2d4e1dc6995a2 100644 (file)
@@ -2,6 +2,7 @@
 
 class Book extends Entity
 {
+    public $searchFactor = 2;
 
     protected $fillable = ['name', 'description', 'image_id'];
 
index 6dab9dc47c2dda2588d5d38f83a33169d70006a4..3726c57f4e3df340c49cce79f29c8624ec4485c3 100644 (file)
@@ -2,6 +2,8 @@
 
 class Chapter extends Entity
 {
+    public $searchFactor = 1.3;
+
     protected $fillable = ['name', 'description', 'priority', 'book_id'];
 
     protected $with = ['book'];
index 1ea4e8dac57cc56e8b978b11ca94f7850d914142..67edec4e089eefe3d4eb69472da891cde409ed81 100644 (file)
@@ -5,8 +5,16 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
 class Entity extends Ownable
 {
 
+    /**
+     * @var string - Name of property where the main text content is found
+     */
     public $textField = 'description';
 
+    /**
+     * @var float - Multiplier for search indexing.
+     */
+    public $searchFactor = 1.0;
+
     /**
      * Compares this entity to another given entity.
      * Matches by comparing class and id.
index d71e383461bb025474f441b0ba691b55a4d00d47..8437c80d71d1ac58b1565fcc56fc66be45a63af0 100644 (file)
@@ -120,7 +120,7 @@ class ImageController extends Controller
     {
         $this->checkPermission('image-create-all');
         $this->validate($request, [
-            'file' => 'required|is_image'
+            'file' => 'is_image'
         ]);
 
         if (!$this->imageRepo->isValidType($type)) {
@@ -136,6 +136,7 @@ class ImageController extends Controller
             return response($e->getMessage(), 500);
         }
 
+
         return response()->json($image);
     }
 
index 0827eeb7196624fb77e0800cbdcc15c76979ec7d..ddc92f7055cb97570cfca5ce74b0398ac461d18a 100644 (file)
@@ -40,13 +40,12 @@ class SearchController extends Controller
         $nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
 
         $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
-        $hasNextPage = $this->searchService->searchEntities($searchTerm, 'all', $page+1, 20)['count'] > 0;
 
         return view('search/all', [
             'entities'   => $results['results'],
             'totalResults' => $results['total'],
             'searchTerm' => $searchTerm,
-            'hasNextPage' => $hasNextPage,
+            'hasNextPage' => $results['has_more'],
             'nextPageLink' => $nextPageLink
         ]);
     }
index ece9aa3057572fd14f7549181eec0b94b65e7f92..acdda3c8d08a601eee67780bcb6e351a62ff5c01 100644 (file)
@@ -774,7 +774,9 @@ class EntityRepo
         $scriptSearchRegex = '/<script.*?>.*?<\/script>/ms';
         $matches = [];
         preg_match_all($scriptSearchRegex, $html, $matches);
-        if (count($matches) === 0) return $html;
+        if (count($matches) === 0) {
+            return $html;
+        }
 
         foreach ($matches[0] as $match) {
             $html = str_replace($match, htmlentities($match), $html);
index ec2fda1d36fd443e09bbe50264f55d39b9bec700..245c0f27b35e5c8a8af852f7b4c5092300b8dd8b 100644 (file)
@@ -225,7 +225,6 @@ class ImageRepo
         try {
             return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
         } catch (\Exception $exception) {
-            dd($exception);
             return null;
         }
     }
index 381e44749258ff4ef060d97d9580be18517262f6..3a6f95dcc7aa7f84d4520e32ab1e697695c2ce69 100644 (file)
@@ -14,10 +14,6 @@ class AttachmentService extends UploadService
      */
     protected function getStorage()
     {
-        if ($this->storageInstance !== null) {
-            return $this->storageInstance;
-        }
-
         $storageType = config('filesystems.default');
 
         // Override default location if set to local public to ensure not visible.
@@ -25,9 +21,7 @@ class AttachmentService extends UploadService
             $storageType = 'local_secure';
         }
 
-        $this->storageInstance = $this->fileSystem->disk($storageType);
-
-        return $this->storageInstance;
+        return $this->fileSystem->disk($storageType);
     }
 
     /**
index 9755ea3072b0baa652e8d381c55e7dc2264b9635..c2e915e2d91e64353bf303eb40dea4f0c83c3553 100644 (file)
@@ -31,6 +31,23 @@ class ImageService extends UploadService
         parent::__construct($fileSystem);
     }
 
+    /**
+     * Get the storage that will be used for storing images.
+     * @param string $type
+     * @return \Illuminate\Contracts\Filesystem\Filesystem
+     */
+    protected function getStorage($type = '')
+    {
+        $storageType = config('filesystems.default');
+
+        // Override default location if set to local public to ensure not visible.
+        if ($type === 'system' && $storageType === 'local_secure') {
+            $storageType = 'local';
+        }
+
+        return $this->fileSystem->disk($storageType);
+    }
+
     /**
      * Saves a new image from an upload.
      * @param UploadedFile $uploadedFile
@@ -119,7 +136,7 @@ class ImageService extends UploadService
      */
     private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
     {
-        $storage = $this->getStorage();
+        $storage = $this->getStorage($type);
         $secureUploads = setting('app-secure-images');
         $imageName = str_replace(' ', '-', $imageName);
 
@@ -170,6 +187,16 @@ class ImageService extends UploadService
         return $image->path;
     }
 
+    /**
+     * Checks if the image is a gif. Returns true if it is, else false.
+     * @param Image $image
+     * @return boolean
+     */
+    protected function isGif(Image $image)
+    {
+        return strtolower(pathinfo($this->getPath($image), PATHINFO_EXTENSION)) === 'gif';
+    }
+
     /**
      * Get the thumbnail for an image.
      * If $keepRatio is true only the width will be used.
@@ -184,6 +211,10 @@ class ImageService extends UploadService
      */
     public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
     {
+        if ($keepRatio && $this->isGif($image)) {
+            return $this->getPublicUrl($this->getPath($image));
+        }
+
         $thumbDirName = '/' . ($keepRatio ? 'scaled-' : 'thumbs-') . $width . '-' . $height . '/';
         $imagePath = $this->getPath($image);
         $thumbFilePath = dirname($imagePath) . $thumbDirName . basename($imagePath);
@@ -192,7 +223,7 @@ class ImageService extends UploadService
             return $this->getPublicUrl($thumbFilePath);
         }
 
-        $storage = $this->getStorage();
+        $storage = $this->getStorage($image->type);
         if ($storage->exists($thumbFilePath)) {
             return $this->getPublicUrl($thumbFilePath);
         }
@@ -274,8 +305,9 @@ class ImageService extends UploadService
     /**
      * Save a gravatar image and set a the profile image for a user.
      * @param User $user
-     * @param int  $size
+     * @param int $size
      * @return mixed
+     * @throws Exception
      */
     public function saveUserGravatar(User $user, $size = 500)
     {
index 6786c5cf498707a8691cc7292b632cf909f800fd..056e1f0772d3bc79f3bc3f788585875805e20e07 100644 (file)
@@ -64,7 +64,7 @@ class SearchService
      * @param string $searchString
      * @param string $entityType
      * @param int $page
-     * @param int $count
+     * @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed.
      * @return array[int, Collection];
      */
     public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20)
@@ -72,7 +72,6 @@ class SearchService
         $terms = $this->parseSearchString($searchString);
         $entityTypes = array_keys($this->entities);
         $entityTypesToSearch = $entityTypes;
-        $results = collect();
 
         if ($entityType !== 'all') {
             $entityTypesToSearch = $entityType;
@@ -80,20 +79,27 @@ class SearchService
             $entityTypesToSearch = explode('|', $terms['filters']['type']);
         }
 
+        $results = collect();
         $total = 0;
+        $hasMore = false;
 
         foreach ($entityTypesToSearch as $entityType) {
             if (!in_array($entityType, $entityTypes)) {
                 continue;
             }
             $search = $this->searchEntityTable($terms, $entityType, $page, $count);
-            $total += $this->searchEntityTable($terms, $entityType, $page, $count, true);
+            $entityTotal = $this->searchEntityTable($terms, $entityType, $page, $count, true);
+            if ($entityTotal > $page * $count) {
+                $hasMore = true;
+            }
+            $total += $entityTotal;
             $results = $results->merge($search);
         }
 
         return [
             'total' => $total,
             'count' => count($results),
+            'has_more' => $hasMore,
             'results' => $results->sortByDesc('score')->values()
         ];
     }
@@ -322,8 +328,8 @@ class SearchService
     public function indexEntity(Entity $entity)
     {
         $this->deleteEntityTerms($entity);
-        $nameTerms = $this->generateTermArrayFromText($entity->name, 5);
-        $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1);
+        $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+        $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
         $terms = array_merge($nameTerms, $bodyTerms);
         foreach ($terms as $index => $term) {
             $terms[$index]['entity_type'] = $entity->getMorphClass();
@@ -340,8 +346,8 @@ class SearchService
     {
         $terms = [];
         foreach ($entities as $entity) {
-            $nameTerms = $this->generateTermArrayFromText($entity->name, 5);
-            $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1);
+            $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+            $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
             foreach (array_merge($nameTerms, $bodyTerms) as $term) {
                 $term['entity_id'] = $entity->id;
                 $term['entity_type'] = $entity->getMorphClass();
index cd05675599dc1c4696d262022d961671d0067975..df597a40fef5dbcb79bac610caabf8528ae0d525 100644 (file)
@@ -11,11 +11,6 @@ class UploadService
      */
     protected $fileSystem;
 
-    /**
-     * @var FileSystemInstance
-     */
-    protected $storageInstance;
-
 
     /**
      * FileService constructor.
@@ -32,14 +27,8 @@ class UploadService
      */
     protected function getStorage()
     {
-        if ($this->storageInstance !== null) {
-            return $this->storageInstance;
-        }
-
         $storageType = config('filesystems.default');
-        $this->storageInstance = $this->fileSystem->disk($storageType);
-
-        return $this->storageInstance;
+        return $this->fileSystem->disk($storageType);
     }
 
     /**
@@ -53,13 +42,4 @@ class UploadService
         $folders = $this->getStorage()->directories($path);
         return (count($files) === 0 && count($folders) === 0);
     }
-
-    /**
-     * Check if using a local filesystem.
-     * @return bool
-     */
-    protected function isLocal()
-    {
-        return strtolower(config('filesystems.default')) === 'local';
-    }
 }
index a003bcdecce4c272052e83e59ea15b8a4c6ec8c8..acf1cf3d7c39a3f6895aca3359aa8bddf85aa875 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -17,7 +17,9 @@ A platform for storing and organising information and documentation. General inf
 
 BookStack is an opinionated wiki system that provides a pleasant and simple out of the box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
 
-BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above. 
+BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above.
+
+In regards to development philosophy, BookStack has a relaxed, open & positive approach. Put simply, At the end of the day this is free software developed and maintained by people donating their own free time.
 
 ## Development & Testing
 
@@ -25,17 +27,17 @@ All development on BookStack is currently done on the master branch. When it's t
 
 * [Node.js](https://nodejs.org/en/) v6.9+
 
-SASS is used to help the CSS development and the JavaScript is run through browserify/babel to allow for writing ES6 code. Both of these are done using gulp. To run the build task you can use the following commands:
+SASS is used to help the CSS development and the JavaScript is run through babel to allow for writing ES6 code. This is done using webpack. To run the build task you can use the following commands:
 
 ``` bash
 # Build assets for development
-npm run-script build
+npm run build
 
 # Build and minify assets for production
-npm run-script production
+npm run production
 
 # Build for dev (With sourcemaps) and watch for changes
-npm run-script dev
+npm run dev
 ```
 
 BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. To use you will need PHPUnit installed and accessible via command line. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the following database name, user name and password defined as `bookstack-test`. You will have to create that database and credentials before testing.
@@ -65,14 +67,16 @@ php resources/lang/check.php <lang>
 php resources/lang/check.php fr
 php resources/lang/check.php pt_BR
 ```
+
 Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
-## Contributing
+
+## Contributing & Maintenence
 
 Feel free to create issues to request new features or to report bugs and problems. Just please follow the template given when creating the issue.
 
-### Standards
+The project's code of conduct [can be found here](https://github.com/BookStackApp/BookStack/blob/master/.github/CODE_OF_CONDUCT.md).
+
+### Code Standards
 
 PHP code within BookStack is generally to [PSR-2](http://www.php-fig.org/psr/psr-2/) standards. From the BookStack root folder you can run `./vendor/bin/phpcs` to check code is formatted correctly and `./vendor/bin/phpcbf` to auto-fix non-PSR-2 code.
 
@@ -82,9 +86,9 @@ Pull requests are very welcome. If the scope of your pull request is large it ma
 
 Pull requests should be created from the `master` branch and should be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases.
 
-If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly. 
+If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
 
-## Website, Docs & Blog 
+## Website, Docs & Blog
 
 The website project docs & Blog can be found in the [BookStackApp/website](https://github.com/BookStackApp/website) repo.
 
@@ -94,6 +98,8 @@ The BookStack source is provided under the MIT License.
 
 ## Attribution
 
+The great people that have worked to build and improve BookStack can [be seen here](https://github.com/BookStackApp/BookStack/graphs/contributors).
+
 These are the great open-source projects used to help build BookStack:
 
 * [Laravel](http://laravel.com/)
index 0f31bd5793363d4009b77d0fb1e5164671aa4e7d..0e40b20ca17685250a2b486690d2a5e9c70b58d2 100644 (file)
@@ -12,7 +12,9 @@ const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
 function mounted() {
    let container = this.$el;
    let _this = this;
-   new DropZone(container, {
+   this._dz = new DropZone(container, {
+       addRemoveLinks: true,
+       dictRemoveFile: trans('components.image_upload_remove'),
         url: function() {
             return _this.uploadUrl;
         },
@@ -35,26 +37,33 @@ function mounted() {
 
             dz.on('error', function (file, errorMessage, xhr) {
                 _this.$emit('error', {file, errorMessage, xhr});
-                console.log(errorMessage);
-                console.log(xhr);
+
                 function setMessage(message) {
                     $(file.previewElement).find('[data-dz-errormessage]').text(message);
                 }
 
-                if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
-                if (errorMessage.file) setMessage(errorMessage.file[0]);
+                if (xhr && xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
+                else if (errorMessage.file) setMessage(errorMessage.file);
+
             });
         }
    });
 }
 
 function data() {
-    return {}
+    return {};
 }
 
+const methods = {
+    onClose: function () {
+        this._dz.removeAllFiles(true);
+    }
+};
+
 module.exports = {
     template,
     props,
     mounted,
     data,
-};
\ No newline at end of file
+    methods
+};
index 12ccc970d3917a2253b4a1d39513605b9494e5e9..89fe6769e37091a862fc334c5799762cbefac522 100644 (file)
@@ -43,6 +43,8 @@ const methods = {
 
     hide() {
         this.showing = false;
+        this.selectedImage = false;
+        this.$refs.dropzone.onClose();
         this.$el.children[0].components.overlay.hide();
     },
 
@@ -175,4 +177,4 @@ module.exports = {
     data,
     computed,
     components: {dropzone},
-};
\ No newline at end of file
+};
index e3238f4b46b2b9f27c04b484c703a4e212586f71..4cf2397bca8bc0457f76f31f6aefc304d5f3031b 100644 (file)
     padding: 0;
     align-items: center;
     text-align: center;
+    justify-content: center;
     width: 28px;
     padding-left: $-xs;
     padding-right: $-xs;
     &:hover {
       background-color: #EEE;
     }
-    i {
-      flex: 1;
-      padding: 0;
+    .svg-icon {
+      margin-right: 0px;
     }
   }
   > div .outline input {
   background-color: #F8F8F8;
   padding: $-m;
   border: 1px solid #DDD;
-}
\ No newline at end of file
+}
index 41a6e02c86189b059cdb405b9c08d029ac832dec..2c20c3f41a50eba38ef7b4a957cde2d6140d4f7d 100644 (file)
@@ -158,6 +158,7 @@ $button-border-radius: 2px;
     left: $-m;
     top: $-s - 2px;
     width: 24px;
+    height: 24px;
   }
   padding: $-s $-m;
   padding-bottom: $-s - 2px;
index f15528167939ac76b3684aa80aed50c1332a00fa..84eebc89bc91036143be70d53f39dc1e832615c6 100644 (file)
@@ -23,6 +23,7 @@
   svg {
     fill: #EEEEEE;
     width: 4em;
+    height: 4em;
     padding-right: $-m;
   }
   span {
@@ -211,6 +212,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   margin-left: 1px;
   padding: $-m $-l;
   overflow-y: auto;
+  overflow-x: hidden;
   border-left: 1px solid #DDD;
   .dropzone-container {
     margin-top: $-m;
@@ -315,8 +317,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 
 .dz-preview.dz-file-preview .dz-image {
   border-radius: 4px;
-  background: #999;
-  background: linear-gradient(to bottom, #eee, #ddd);
+  background: #e9e9e9;
 }
 
 .dz-preview.dz-file-preview .dz-details {
@@ -332,11 +333,12 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 }
 
 .dz-preview .dz-remove {
-  font-size: 14px;
+  font-size: 13px;
   text-align: center;
   display: block;
   cursor: pointer;
   border: none;
+  margin-top: 3px;
 }
 
 .dz-preview .dz-remove:hover {
@@ -385,7 +387,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   border: 1px solid transparent;
 }
 
-.dz-preview .dz-details .dz-filename span, .dz-preview .dz-details .dz-size span {
+.dz-preview .dz-details .dz-filename span {
   background-color: rgba(255, 255, 255, 0.4);
   padding: 0 0.4em;
   border-radius: 3px;
@@ -421,13 +423,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 .dz-preview .dz-success-mark, .dz-preview .dz-error-mark {
   pointer-events: none;
   opacity: 0;
-  z-index: 500;
+  z-index: 1001;
   position: absolute;
   display: block;
   top: 50%;
   left: 50%;
   margin-left: -27px;
-  margin-top: -27px;
+  margin-top: -35px;
 }
 
 .dz-preview .dz-success-mark svg, .dz-preview .dz-error-mark svg {
@@ -482,9 +484,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   display: block;
 }
 
-.dz-preview.dz-error:hover .dz-error-message {
-  opacity: 1;
-  pointer-events: auto;
+.dz-preview.dz-error {
+  .dz-image, .dz-details {
+    &:hover ~ .dz-error-message {
+      opacity: 1;
+      pointer-events: auto;
+    }
+  }
 }
 
 .dz-preview .dz-error-message {
@@ -496,7 +502,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   opacity: 0;
   transition: opacity 0.3s ease;
   border-radius: 4px;
-  font-size: 11.5px;
+  font-size: 12px;
   line-height: 1.2;
   top: 88px;
   left: -26px;
@@ -597,4 +603,4 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   .text-muted {
     color: #999;
   }
-}
\ No newline at end of file
+}
index 3555111d8bc1a177bc35a90c60b3691e74a7da49..3749b53210d45f18ce7a92fe89c0ce7e6be2a78a 100755 (executable)
     flex: 1;
     padding-top: 0;
   }
+  div[toolbox-tab-content] .padded.files {
+       overflow-x: hidden;
+  }
   h4 {
     font-size: 24px;
     margin: $-m 0 0 0;
index 3504154c2e70bddd6eecf2aff051bf7a84ef2006..d894a00e739d84cd5273bd5c00db1b1f4eaac97e 100644 (file)
@@ -424,6 +424,7 @@ i {
 
 .svg-icon {
   width: 1em;
+  height: 1em;
   display: inline-block;
   position: relative;
   bottom: -0.105em;
index 9f028f6ef715c7505bef115bc1d0515ae89461c3..c7d288ad387f3ced379a5b7e7596a51fbee8c027 100644 (file)
@@ -118,6 +118,7 @@ $btt-size: 40px;
   fill: #FFF;
   svg {
     width: $btt-size / 1.5;
+    height: $btt-size / 1.5;
     margin-right: 4px;
   }
   width: $btt-size;
index 334502d05b84dc0e09ec37b863d02d49d1d88e56..2266fe2b233890fcc5b2539166bdae3ee6d98449 100644 (file)
@@ -21,6 +21,7 @@ return [
     'image_upload_success' => 'Image uploaded successfully',
     'image_update_success' => 'Image details successfully updated',
     'image_delete_success' => 'Image successfully deleted',
+    'image_upload_remove' => 'Remove',
 
     /**
      * Code editor
@@ -29,4 +30,4 @@ return [
     'code_language' => 'Code Language',
     'code_content' => 'Code Content',
     'code_save' => 'Save Code',
-];
\ No newline at end of file
+];
index 3b1d6e8b3f22cfb1b2d10ad9b4207acfeaeb74e3..a86a1cdfc8dadab7d88c704992a7f84e67e45619 100644 (file)
@@ -35,6 +35,7 @@ return [
     '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.',
+    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
     'image_upload_error' => 'An error occurred uploading the image',
     'image_upload_type_error' => 'The image type being uploaded is invalid',
 
@@ -78,4 +79,4 @@ return [
     '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
+];
index 32f225d5d9f6dcd59f3c1157434c6800287d8101..a59c9a54318a44c6474bbfde26bca5c78ef97f8a 100644 (file)
@@ -37,4 +37,7 @@ return [
     'book_sort'                   => 'a réordonné le livre',
     'book_sort_notification'      => 'Livre réordonné avec succès',
 
+    // Other
+    'commented_on'                => 'a commenté'
+
 ];
index 015bfdff03a858eec70e09ec4b0fadd5d6c0a100..154066ae4adbcf932d78965c4022da321e58d283 100644 (file)
@@ -18,6 +18,8 @@ return [
      */
     'sign_up' => "S'inscrire",
     'log_in' => 'Se connecter',
+    'log_in_with' => 'Se connecter avec :socialDriver',
+    'sign_up_with' => 'S\'inscrire avec :socialDriver',
     'logout' => 'Se déconnecter',
 
     'name' => 'Nom',
index 2553d14822c3ac3ba5b7829a1ef1ed0017c20a6c..823c6ee2f057b6abb7bed2e2e0325ffa32af0e77 100644 (file)
@@ -10,6 +10,7 @@ return [
     'save' => 'Enregistrer',
     'continue' => 'Continuer',
     'select' => 'Sélectionner',
+    'more' => 'Montrer plus',
 
     /**
      * Form Labels
@@ -29,11 +30,13 @@ return [
     'edit' => 'Editer',
     'sort' => 'Trier',
     'move' => 'Déplacer',
+    'reply' => 'Répondre',
     'delete' => 'Supprimer',
     'search' => 'Chercher',
     'search_clear' => 'Réinitialiser la recherche',
     'reset' => 'Réinitialiser',
     'remove' => 'Enlever',
+    'add' => 'Ajouter',
 
 
     /**
@@ -45,6 +48,9 @@ return [
     'back_to_top' => 'Retour en haut',
     'toggle_details' => 'Afficher les détails',
     'toggle_thumbnails' => 'Afficher les vignettes',
+    'details' => 'Détails',
+    'grid_view' => 'Vue en grille',
+    'list_view' => 'Vue en liste',
 
     /**
      * Header
index 7c9c4cfc06310d968837a2afd808bf3676ddbb0b..ddfe665d95b89a6172fac1724990ef1366192b14 100644 (file)
@@ -20,5 +20,13 @@ return [
     'image_preview' => 'Prévisualiser l\'image',
     'image_upload_success' => 'Image ajoutée avec succès',
     'image_update_success' => 'Détails de l\'image mis Ã  jour',
-    'image_delete_success' => 'Image supprimée avec succès'
+    'image_delete_success' => 'Image supprimée avec succès',
+
+    /**
+     * Code editor
+     */
+    'code_editor' => 'Editer le code',
+    'code_language' => 'Language du code',
+    'code_content' => 'Contenu du code',
+    'code_save' => 'Enregistrer le code',
 ];
index d4d9d247575ae25eda3ed1ff2325268fe0f9e976..7d0696c2a710e99063eac26901b7a5f29597c2dd 100644 (file)
@@ -14,6 +14,7 @@ return [
     'recent_activity' => 'Activité récente',
     'create_now' => 'En créer un maintenant',
     'revisions' => 'Révisions',
+    'meta_revision' => 'Révision #:revisionCount',
     'meta_created' => 'Créé :timeLength',
     'meta_created_name' => 'Créé :timeLength par :user',
     'meta_updated' => 'Mis Ã  jour :timeLength',
@@ -43,19 +44,39 @@ return [
      * Search
      */
     'search_results' => 'Résultats de recherche',
+    'search_total_results_found' => ':count résultats trouvés|:count résultats trouvés au total',
     'search_clear' => 'Réinitialiser la recherche',
     'search_no_pages' => 'Aucune page correspondant Ã  cette recherche',
     'search_for_term' => 'recherche pour :term',
+    'search_more' => 'Plus de résultats',
+    'search_filters' => 'Filtres de recherche',
+    'search_content_type' => 'Type de contenu',
+    'search_exact_matches' => 'Correspondances exactes',
+    'search_tags' => 'Recherche par tags',
+    'search_viewed_by_me' => 'Vu par moi',
+    'search_not_viewed_by_me' => 'Non vu par moi',
+    'search_permissions_set' => 'Ensemble d\'autorisations',
+    'search_created_by_me' => 'Créé par moi',
+    'search_updated_by_me' => 'Mis Ã  jour par moi',
+    'search_updated_before' => 'Mis Ã  jour avant',
+    'search_updated_after' => 'Mis Ã  jour après',
+    'search_created_before' => 'Créé avant',
+    'search_created_after' => 'Créé après',
+    'search_set_date' => 'Choisir la date',
+    'search_update' => 'Actualiser la recherche',
 
     /**
      * Books
      */
     'book' => 'Livre',
     'books' => 'Livres',
+    'x_books' => ':count livre|:count livres',
     'books_empty' => 'Aucun livre n\'a Ã©té créé',
     'books_popular' => 'Livres populaires',
     'books_recent' => 'Livres récents',
+    'books_new' => 'Nouveaux livres',
     'books_popular_empty' => 'Les livres les plus populaires apparaîtront ici.',
+    'books_new_empty' => 'Les livres les plus récents apparaitront ici.',
     'books_create' => 'Créer un nouveau livre',
     'books_delete' => 'Supprimer un livre',
     'books_delete_named' => 'Supprimer le livre :bookName',
@@ -85,6 +106,7 @@ return [
      */
     'chapter' => 'Chapitre',
     'chapters' => 'Chapitres',
+    'x_chapters' => ':count chapitre|:count chapitres',
     'chapters_popular' => 'Chapitres populaires',
     'chapters_new' => 'Nouveau chapitre',
     'chapters_create' => 'Créer un nouveau chapitre',
@@ -102,6 +124,7 @@ return [
     'chapters_empty' => 'Il n\'y a pas de page dans ce chapitre actuellement.',
     'chapters_permissions_active' => 'Permissions du chapitre activées',
     'chapters_permissions_success' => 'Permissions du chapitre mises Ã  jour',
+    'chapters_search_this' => 'Rechercher dans ce chapitre',
 
     /**
      * Pages
@@ -139,16 +162,19 @@ return [
     'pages_md_preview' => 'Prévisualisation',
     'pages_md_insert_image' => 'Insérer une image',
     'pages_md_insert_link' => 'Insérer un lien',
+    'pages_md_insert_drawing' => 'Insérer un dessin',
     'pages_not_in_chapter' => 'La page n\'est pas dans un chapitre',
     'pages_move' => 'Déplacer la page',
     'pages_move_success' => 'Page déplacée Ã  ":parentName"',
     'pages_permissions' => 'Permissions de la page',
     'pages_permissions_success' => 'Permissions de la page mises Ã  jour',
+    'pages_revision' => 'Révision',
     'pages_revisions' => 'Révisions de la page',
     'pages_revisions_named' => 'Révisions pour :pageName',
     'pages_revision_named' => 'Révision pour :pageName',
     'pages_revisions_created_by' => 'Créé par',
     'pages_revisions_date' => 'Date de révision',
+    'pages_revisions_number' => '#',
     'pages_revisions_changelog' => 'Journal des changements',
     'pages_revisions_changes' => 'Changements',
     'pages_revisions_current' => 'Version courante',
@@ -219,6 +245,18 @@ return [
      */
     'comment' => 'Commentaire',
     'comments' => 'Commentaires',
+    'comment_add' => 'Ajouter un commentaire',
     'comment_placeholder' => 'Entrez vos commentaires ici',
+    'comment_count' => '{0} Pas de commentaires|{1} 1 Commentaire|[2,*] :count Commentaires',
     'comment_save' => 'Enregistrer le commentaire',
+    'comment_saving' => 'Enregistrement du commentaire...',
+    'comment_deleting' => 'Suppression du commentaire...',
+    'comment_new' => 'Nouveau commentaire',
+    'comment_created' => 'commenté :createDiff',
+    'comment_updated' => 'Mis Ã  jour :updateDiff par :username',
+    'comment_deleted_success' => 'Commentaire supprimé',
+    'comment_created_success' => 'Commentaire ajouté',
+    'comment_updated_success' => 'Commentaire mis Ã  jour',
+    'comment_delete_confirm' => 'Etes-vous sûr de vouloir supprimer ce commentaire?',
+    'comment_in_reply_to' => 'En réponse Ã  :commentId',
 ];
index 2f0163368210a9c152b60dbe94d96c9fc77cd093..ea6214d84b0d3f6d486f6cc5fdf812d2ffb0fd51 100644 (file)
@@ -31,6 +31,9 @@ return [
     'app_logo_desc' => 'Cette image doit faire 43px de hauteur. <br>Les images plus larges seront réduites.',
     'app_primary_color' => 'Couleur principale de l\'application',
     'app_primary_color_desc' => 'Cela devrait Ãªtre une valeur hexadécimale. <br>Laisser vide pour rétablir la couleur par défaut.',
+    'app_homepage' => 'Page d\'accueil de l\'application',
+    'app_homepage_desc' => 'Choisissez une page Ã  afficher sur la page d\'accueil au lieu de la vue par défaut. Les permissions sont ignorées pour les pages sélectionnées.',
+    'app_homepage_default' => 'Page d\'accueil par défaut sélectionnée',
     'app_disable_comments' => 'Désactiver les commentaires',
     'app_disable_comments_desc' => 'Désactive les commentaires sur toutes les pages de l\'application. Les commentaires existants ne sont pas affichés.',
     /**
index eb0664ad84271cdf87cd03da98e09b26cdfdb814..a86cb33523a323e0d9f57822b37894af54eeb173 100644 (file)
@@ -5,7 +5,7 @@
         <div class="breadcrumbs">
             <a href="{{ baseUrl('/books') }}" class="text-button">@icon('book'){{ trans('entities.books') }}</a>
             <span class="sep">&raquo;</span>
-            <a href="{{ baseUrl('/books/create') }}" class="text-button">@icon('add'){{ trans('entities.books_create') }}</a>
+            <a href="{{ baseUrl('/create-book') }}" class="text-button">@icon('add'){{ trans('entities.books_create') }}</a>
         </div>
     </div>
 @stop
index d66612f626db530d02c8622a67a44d6cc432eef0..d1435ab662969c5f9128c6dc7d3d6dae9aae311f 100644 (file)
@@ -18,7 +18,7 @@
     <div class="col-xs-6 faded">
         <div class="action-buttons">
             @if($currentUser->can('book-create-all'))
-                <a href="{{ baseUrl("/books/create") }}" class="text-pos text-button">@icon('add'){{ trans('entities.books_create') }}</a>
+                <a href="{{ baseUrl("/create-book") }}" class="text-pos text-button">@icon('add'){{ trans('entities.books_create') }}</a>
             @endif
         </div>
     </div>
@@ -78,7 +78,7 @@
         @else
             <p class="text-muted">{{ trans('entities.books_empty') }}</p>
             @if(userCan('books-create-all'))
-                <a href="{{ baseUrl("/books/create") }}" class="text-pos">@icon('edit'){{ trans('entities.create_one_now') }}</a>
+                <a href="{{ baseUrl("/create-book") }}" class="text-pos">@icon('edit'){{ trans('entities.create_one_now') }}</a>
             @endif
         @endif
     </div>
index b853969331ea2a2a540945606cc4534f964025da..88a3b24193132b76effe913b6a934894ff3c17f0 100644 (file)
@@ -11,7 +11,7 @@
     <div class="container" ng-non-bindable>
         <p>&nbsp;</p>
         <div class="card">
-            <h3><@icon('lock') {{ trans('entities.books_permissions') }}</h3>
+            <h3>@icon('lock') {{ trans('entities.books_permissions') }}</h3>
             <div class="body">
                 @include('form/restriction-form', ['model' => $book])
             </div>
index bb5189187bbfb112fe031f0b3a033b334f27e00d..d3a51cb3ac637a9523dd4c3c1f6bcbede1dded63 100644 (file)
                 </ul>
             </span>
             @if(userCan('page-create', $book))
-                <a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button">@icon('add'){{ trans('entities.pages_new') }}</a>
+                <a href="{{ $book->getUrl('/create-page') }}" class="text-pos text-button">@icon('add'){{ trans('entities.pages_new') }}</a>
             @endif
             @if(userCan('chapter-create', $book))
-                <a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button">@icon('add'){{ trans('entities.chapters_new') }}</a>
+                <a href="{{ $book->getUrl('/create-chapter') }}" class="text-pos text-button">@icon('add'){{ trans('entities.chapters_new') }}</a>
             @endif
             @if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book))
                 <div dropdown class="dropdown-container">
                 <div class="well">
                     <p class="text-muted italic">{{ trans('entities.books_empty_contents') }}</p>
                         @if(userCan('page-create', $book))
-                            <a href="{{ $book->getUrl('/page/create') }}" class="button outline page">@icon('page'){{ trans('entities.books_empty_create_page') }}</a>
+                            <a href="{{ $book->getUrl('/create-page') }}" class="button outline page">@icon('page'){{ trans('entities.books_empty_create_page') }}</a>
                         @endif
                         @if(userCan('page-create', $book) && userCan('chapter-create', $book))
                             &nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
                         @endif
                         @if(userCan('chapter-create', $book))
-                            <a href="{{ $book->getUrl('/chapter/create') }}" class="button outline chapter">@icon('chapter'){{ trans('entities.books_empty_add_chapter') }}</a>
+                            <a href="{{ $book->getUrl('/create-chapter') }}" class="button outline chapter">@icon('chapter'){{ trans('entities.books_empty_add_chapter') }}</a>
                         @endif
                 </div>
             @endif
index 37f4ff63373c47dbabfbb5cea2eac806a1b16586..9930da605a4eaaf9583a6e881abfe8cb9c7c17ce 100644 (file)
@@ -3,9 +3,9 @@
 @section('toolbar')
     <div class="col-sm-12 faded">
         <div class="breadcrumbs">
-            <a href="{{$book->getUrl()}}" class="text-book text-button">@icon('book'){{ $book->getShortName() }}</a>
+            <a href="{{ $book->getUrl() }}" class="text-book text-button">@icon('book'){{ $book->getShortName() }}</a>
             <span class="sep">&raquo;</span>
-            <a href="{{ baseUrl('/books/chapter/create') }}" class="text-button">@icon('add'){{ trans('entities.chapters_create') }}</a>
+            <a href="{{ $book->getUrl('/create-chapter')}}" class="text-button">@icon('add'){{ trans('entities.chapters_create') }}</a>
         </div>
     </div>
 @stop
@@ -16,7 +16,7 @@
         <div class="card">
             <h3>@icon('add') {{ trans('entities.chapters_create') }}</h3>
             <div class="body">
-                <form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
+                <form action="{{ $book->getUrl('/create-chapter') }}" method="POST">
                     @include('chapters/form')
                 </form>
             </div>
index 9e3b8e472685de56fe109a96cf95c10d67ad9aa8..19cf65a61f8fe47ea0ad761898791b1d1f543172 100644 (file)
@@ -12,6 +12,6 @@
 </div>
 
 <div class="form-group text-right">
-    <a href="{{ back()->getTargetUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+    <a href="{{ isset($chapter) ? $chapter->getUrl() : $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
     <button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
 </div>
index b860038b435e26d95526c732ac81af54cf881014..78c6435d6dc10752b8c472af2321915f47de31e6 100644 (file)
@@ -1,10 +1,10 @@
 <div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
-    <div overlay v-cloak>
+    <div overlay v-cloak @click="hide()">
         <div class="popup-body" @click.stop="">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.image_select') }}</div>
-                <button class="overlay-close neg corner-button button">x</button>
+                <button class="overlay-close neg corner-button button" @click="hide()">x</button>
             </div>
 
             <div class="flex-fill image-manager-body">
@@ -79,7 +79,7 @@
 
                         </div>
 
-                        <dropzone placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
+                        <dropzone ref="dropzone" placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
 
                     </div>
                 </div>
index 10e16cb97e3fd80f1aa3f042ae38e4f888a88e0a..c5ad8ce70f475748a6fc3f6399345a2a966344a3 100644 (file)
@@ -4,7 +4,7 @@
 
     <div class="container small" ng-non-bindable>
         <h1>{{ trans('entities.pages_new') }}</h1>
-        <form action="{{  $parent->getUrl('/page/create/guest') }}" method="POST">
+        <form action="{{  $parent->getUrl('/create-guest-page') }}" method="POST">
 
             {!! csrf_field() !!}
 
index c38c76f188baa727595d374822eb86c871c06a97..f6135cd1bce897cff531da4ffdeb339add2e6ea6 100644 (file)
@@ -14,7 +14,6 @@
     <link rel="stylesheet" media="print" href="{{ versioned_asset('dist/print-styles.css') }}">
 
     <!-- Scripts -->
-    <script src="{{ baseUrl("/libs/jquery/jquery.min.js?version=2.1.4") }}"></script>
     @include('partials/custom-styles')
 
     <!-- Custom user content -->
index b4da1a576a772db1f9822fcba95481bf392264fa..f24e83bc2912bdce087afa16959b3e24d7249982 100644 (file)
 
 @section('body')
 
-    <div class="container small">
+    <div class="container small" v-pre>
         <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
 
         <h1>{{ trans('entities.search_results') }}</h1>
index 82e7dabfd7a2b29f43ab239403cbc452161226ea..b6f8f7345fb995a0ebc04823d58a83038d2bb762 100644 (file)
@@ -65,7 +65,7 @@
                         <div class="row">
                             @foreach($activeSocialDrivers as $driver => $enabled)
                                 <div class="col-sm-4 col-xs-6 text-center">
-                                    <div>@icon('auth/'. $driver, ['style' => 'width: 56px;'])</div>
+                                    <div>@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])</div>
                                     <div>
                                         @if($user->hasSocialAccount($driver))
                                             <a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
index da80125b734d0a9b478b0ab47b0d94faf8bf3a76..7a8634ca39ec659e37142aa47df38f58cb25a6ab 100644 (file)
@@ -14,11 +14,11 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
     });
 
+    Route::get('/create-book', 'BookController@create');
     Route::group(['prefix' => 'books'], function () {
 
         // Books
         Route::get('/', 'BookController@index');
-        Route::get('/create', 'BookController@create');
         Route::post('/', 'BookController@store');
         Route::get('/{slug}/edit', 'BookController@edit');
         Route::put('/{slug}', 'BookController@update');
@@ -35,8 +35,8 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/export/plaintext', 'BookController@exportPlainText');
 
         // Pages
-        Route::get('/{bookSlug}/page/create', 'PageController@create');
-        Route::post('/{bookSlug}/page/create/guest', 'PageController@createAsGuest');
+        Route::get('/{bookSlug}/create-page', 'PageController@create');
+        Route::post('/{bookSlug}/create-guest-page', 'PageController@createAsGuest');
         Route::get('/{bookSlug}/draft/{pageId}', 'PageController@editDraft');
         Route::post('/{bookSlug}/draft/{pageId}', 'PageController@store');
         Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
@@ -62,9 +62,9 @@ Route::group(['middleware' => 'auth'], function () {
 
         // Chapters
         Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create');
-        Route::post('/{bookSlug}/chapter/{chapterSlug}/page/create/guest', 'PageController@createAsGuest');
-        Route::get('/{bookSlug}/chapter/create', 'ChapterController@create');
-        Route::post('/{bookSlug}/chapter/create', 'ChapterController@store');
+        Route::post('/{bookSlug}/chapter/{chapterSlug}/create-guest-page', 'PageController@createAsGuest');
+        Route::get('/{bookSlug}/create-chapter', 'ChapterController@create');
+        Route::post('/{bookSlug}/create-chapter', 'ChapterController@store');
         Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show');
         Route::put('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@update');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@showMove');
index 4d4e0e6cd30112721df035f16a3386b577354079..352af1e42a0efa93c97aa6ac3a70e83806881725 100644 (file)
@@ -139,7 +139,7 @@ class EntityTest extends BrowserKitTest
             // Navigate to chapter create page
             ->visit($book->getUrl())
             ->click('New Chapter')
-            ->seePageIs($book->getUrl() . '/chapter/create')
+            ->seePageIs($book->getUrl() . '/create-chapter')
             // Fill out form
             ->type($chapter->name, '#name')
             ->type($chapter->description, '#description')
@@ -161,7 +161,7 @@ class EntityTest extends BrowserKitTest
             ->visit('/books')
             // Choose to create a book
             ->click('Create New Book')
-            ->seePageIs('/books/create')
+            ->seePageIs('/create-book')
             // Fill out form & save
             ->type($book->name, '#name')
             ->type($book->description, '#description')
@@ -172,7 +172,7 @@ class EntityTest extends BrowserKitTest
 
         // Ensure duplicate names are given different slugs
         $this->asAdmin()
-            ->visit('/books/create')
+            ->visit('/create-book')
             ->type($book->name, '#name')
             ->type($book->description, '#description')
             ->press('Save Book');
index b8ac9d3aeef6d37f86022986a8371b725aa1028a..d9341ace67f406d308026edc56235f175634fa09 100644 (file)
@@ -70,7 +70,7 @@ class PageDraftTest extends BrowserKitTest
         $book = \BookStack\Book::first();
         $this->asAdmin()->visit('/')
             ->dontSeeInElement('#recent-drafts', 'New Page')
-            ->visit($book->getUrl() . '/page/create')
+            ->visit($book->getUrl() . '/create-page')
             ->visit('/')
             ->seeInElement('#recent-drafts', 'New Page');
     }
@@ -82,7 +82,7 @@ class PageDraftTest extends BrowserKitTest
         $newUser = $this->getEditor();
 
         $this->actingAs($newUser)->visit('/')
-            ->visit($book->getUrl() . '/page/create')
+            ->visit($book->getUrl() . '/create-page')
             ->visit($chapter->getUrl() . '/create-page')
             ->visit($book->getUrl())
             ->seeInElement('.page-list', 'New Page');
index 881f73b552a93e2b4298774533bbc3672d44914f..8c96ae9251b3ee4d598f5e4c6a9f09d1246279c2 100644 (file)
@@ -21,7 +21,7 @@ class ImageTest extends TestCase
      */
     protected function getTestImage($fileName)
     {
-        return new \Illuminate\Http\UploadedFile($this->getTestImageFilePath(), $fileName, 'image/jpeg', 5238);
+        return new \Illuminate\Http\UploadedFile($this->getTestImageFilePath(), $fileName, 'image/png', 5238);
     }
 
     /**
@@ -86,7 +86,42 @@ class ImageTest extends TestCase
             'updated_by' => $admin->id,
             'name' => $imageName
         ]);
+    }
 
+    public function test_secure_images_uploads_to_correct_place()
+    {
+        config()->set('filesystems.default', 'local_secure');
+        $this->asEditor();
+        $galleryFile = $this->getTestImage('my-secure-test-upload');
+        $page = Page::first();
+        $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m-M') . '/my-secure-test-upload');
+
+        $upload = $this->call('POST', '/images/gallery/upload', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
+        $upload->assertStatus(200);
+
+        $this->assertTrue(file_exists($expectedPath), 'Uploaded image not found at path: '. $expectedPath);
+
+        if (file_exists($expectedPath)) {
+            unlink($expectedPath);
+        }
+    }
+
+    public function test_system_images_remain_public()
+    {
+        config()->set('filesystems.default', 'local_secure');
+        $this->asEditor();
+        $galleryFile = $this->getTestImage('my-system-test-upload');
+        $page = Page::first();
+        $expectedPath = public_path('uploads/images/system/' . Date('Y-m-M') . '/my-system-test-upload');
+
+        $upload = $this->call('POST', '/images/system/upload', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
+        $upload->assertStatus(200);
+
+        $this->assertTrue(file_exists($expectedPath), 'Uploaded image not found at path: '. $expectedPath);
+
+        if (file_exists($expectedPath)) {
+            unlink($expectedPath);
+        }
     }
 
     public function test_image_delete()
index 8f37b2517b9b94e317bd2b3335452db9f47c474f..433ae7ff94b4569995af23a956f686aa204a9907 100644 (file)
@@ -109,21 +109,21 @@ class RestrictionsTest extends BrowserKitTest
 
         $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
 
-        $this->forceVisit($bookUrl . '/chapter/create')
+        $this->forceVisit($bookUrl . '/create-chapter')
             ->see('You do not have permission')->seePageIs('/');
-        $this->forceVisit($bookUrl . '/page/create')
+        $this->forceVisit($bookUrl . '/create-page')
             ->see('You do not have permission')->seePageIs('/');
         $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
             ->dontSeeInElement('.action-buttons', 'New Chapter');
 
         $this->setEntityRestrictions($book, ['view', 'create']);
 
-        $this->visit($bookUrl . '/chapter/create')
+        $this->visit($bookUrl . '/create-chapter')
             ->type('test chapter', 'name')
             ->type('test description for chapter', 'description')
             ->press('Save Chapter')
             ->seePageIs($bookUrl . '/chapter/test-chapter');
-        $this->visit($bookUrl . '/page/create')
+        $this->visit($bookUrl . '/create-page')
             ->type('test page', 'name')
             ->type('test content', 'html')
             ->press('Save Page')
@@ -454,21 +454,21 @@ class RestrictionsTest extends BrowserKitTest
 
         $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
 
-        $this->forceVisit($bookUrl . '/chapter/create')
+        $this->forceVisit($bookUrl . '/create-chapter')
             ->see('You do not have permission')->seePageIs('/');
-        $this->forceVisit($bookUrl . '/page/create')
+        $this->forceVisit($bookUrl . '/create-page')
             ->see('You do not have permission')->seePageIs('/');
         $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
             ->dontSeeInElement('.action-buttons', 'New Chapter');
 
         $this->setEntityRestrictions($book, ['view', 'create']);
 
-        $this->visit($bookUrl . '/chapter/create')
+        $this->visit($bookUrl . '/create-chapter')
             ->type('test chapter', 'name')
             ->type('test description for chapter', 'description')
             ->press('Save Chapter')
             ->seePageIs($bookUrl . '/chapter/test-chapter');
-        $this->visit($bookUrl . '/page/create')
+        $this->visit($bookUrl . '/create-page')
             ->type('test page', 'name')
             ->type('test content', 'html')
             ->press('Save Page')
index bd9e01d457b45f89a756b344622d52089fc73cf6..5bc66986baee15f2d8155970193f79f1592919cc 100644 (file)
@@ -214,12 +214,12 @@ class RolesTest extends BrowserKitTest
     public function test_books_create_all_permissions()
     {
         $this->checkAccessPermission('book-create-all', [
-            '/books/create'
+            '/create-book'
         ], [
             '/books' => 'Create New Book'
         ]);
 
-        $this->visit('/books/create')
+        $this->visit('/create-book')
             ->type('test book', 'name')
             ->type('book desc', 'description')
             ->press('Save Book')
@@ -293,40 +293,38 @@ class RolesTest extends BrowserKitTest
     {
         $book = \BookStack\Book::take(1)->get()->first();
         $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
-        $baseUrl = $ownBook->getUrl() . '/chapter';
         $this->checkAccessPermission('chapter-create-own', [
-            $baseUrl . '/create'
+            $ownBook->getUrl('/create-chapter')
         ], [
             $ownBook->getUrl() => 'New Chapter'
         ]);
 
-        $this->visit($baseUrl . '/create')
+        $this->visit($ownBook->getUrl('/create-chapter'))
             ->type('test chapter', 'name')
             ->type('chapter desc', 'description')
             ->press('Save Chapter')
-            ->seePageIs($baseUrl . '/test-chapter');
+            ->seePageIs($ownBook->getUrl('/chapter/test-chapter'));
 
         $this->visit($book->getUrl())
             ->dontSeeInElement('.action-buttons', 'New Chapter')
-            ->visit($book->getUrl() . '/chapter/create')
+            ->visit($book->getUrl('/create-chapter'))
             ->seePageIs('/');
     }
 
     public function test_chapter_create_all_permissions()
     {
         $book = \BookStack\Book::take(1)->get()->first();
-        $baseUrl = $book->getUrl() . '/chapter';
         $this->checkAccessPermission('chapter-create-all', [
-            $baseUrl . '/create'
+            $book->getUrl('/create-chapter')
         ], [
             $book->getUrl() => 'New Chapter'
         ]);
 
-        $this->visit($baseUrl . '/create')
+        $this->visit($book->getUrl('/create-chapter'))
             ->type('test chapter', 'name')
             ->type('chapter desc', 'description')
             ->press('Save Chapter')
-            ->seePageIs($baseUrl . '/test-chapter');
+            ->seePageIs($book->getUrl('/chapter/test-chapter'));
     }
 
     public function test_chapter_edit_own_permission()
@@ -403,10 +401,8 @@ class RolesTest extends BrowserKitTest
         $ownBook = $entities['book'];
         $ownChapter = $entities['chapter'];
 
-        $baseUrl = $ownBook->getUrl() . '/page';
-
-        $createUrl = $baseUrl . '/create';
-        $createUrlChapter = $ownChapter->getUrl() . '/create-page';
+        $createUrl = $ownBook->getUrl('/create-page');
+        $createUrlChapter = $ownChapter->getUrl('/create-page');
         $accessUrls = [$createUrl, $createUrlChapter];
 
         foreach ($accessUrls as $url) {
@@ -427,15 +423,15 @@ class RolesTest extends BrowserKitTest
             $this->seePageIs($expectedUrl);
         }
 
-        $this->visit($baseUrl . '/create')
+        $this->visit($createUrl)
             ->type('test page', 'name')
             ->type('page desc', 'html')
             ->press('Save Page')
-            ->seePageIs($baseUrl . '/test-page');
+            ->seePageIs($ownBook->getUrl('/page/test-page'));
 
         $this->visit($book->getUrl())
             ->dontSeeInElement('.action-buttons', 'New Page')
-            ->visit($book->getUrl() . '/page/create')
+            ->visit($book->getUrl() . '/create-page')
             ->seePageIs('/');
         $this->visit($chapter->getUrl())
             ->dontSeeInElement('.action-buttons', 'New Page')
@@ -448,9 +444,9 @@ class RolesTest extends BrowserKitTest
         $book = \BookStack\Book::take(1)->get()->first();
         $chapter = \BookStack\Chapter::take(1)->get()->first();
         $baseUrl = $book->getUrl() . '/page';
-        $createUrl = $baseUrl . '/create';
+        $createUrl = $book->getUrl('/create-page');
 
-        $createUrlChapter = $chapter->getUrl() . '/create-page';
+        $createUrlChapter = $chapter->getUrl('/create-page');
         $accessUrls = [$createUrl, $createUrlChapter];
 
         foreach ($accessUrls as $url) {
@@ -471,17 +467,17 @@ class RolesTest extends BrowserKitTest
             $this->seePageIs($expectedUrl);
         }
 
-        $this->visit($baseUrl . '/create')
+        $this->visit($createUrl)
             ->type('test page', 'name')
             ->type('page desc', 'html')
             ->press('Save Page')
-            ->seePageIs($baseUrl . '/test-page');
+            ->seePageIs($book->getUrl('/page/test-page'));
 
-        $this->visit($chapter->getUrl() . '/create-page')
+        $this->visit($chapter->getUrl('/create-page'))
             ->type('new test page', 'name')
             ->type('page desc', 'html')
             ->press('Save Page')
-            ->seePageIs($baseUrl . '/new-test-page');
+            ->seePageIs($book->getUrl('/page/new-test-page'));
     }
 
     public function test_page_edit_own_permission()