Dan Brown

Merge branch 'master' into release

Showing 60 changed files with 1671 additions and 535 deletions
...@@ -14,14 +14,28 @@ CACHE_DRIVER=file ...@@ -14,14 +14,28 @@ CACHE_DRIVER=file
14 SESSION_DRIVER=file 14 SESSION_DRIVER=file
15 QUEUE_DRIVER=sync 15 QUEUE_DRIVER=sync
16 16
17 +# Storage
18 +STORAGE_TYPE=local
19 +# Amazon S3 Config
20 +STORAGE_S3_KEY=false
21 +STORAGE_S3_SECRET=false
22 +STORAGE_S3_REGION=false
23 +STORAGE_S3_BUCKET=false
24 +# Storage URL
25 +# Used to prefix image urls for when using custom domains/cdns
26 +STORAGE_URL=false
27 +
17 # Social Authentication information. Defaults as off. 28 # Social Authentication information. Defaults as off.
18 GITHUB_APP_ID=false 29 GITHUB_APP_ID=false
19 GITHUB_APP_SECRET=false 30 GITHUB_APP_SECRET=false
20 GOOGLE_APP_ID=false 31 GOOGLE_APP_ID=false
21 GOOGLE_APP_SECRET=false 32 GOOGLE_APP_SECRET=false
22 -# URL for social login redirects, NO TRAILING SLASH 33 +# URL used for social login redirects, NO TRAILING SLASH
23 APP_URL=http://bookstack.dev 34 APP_URL=http://bookstack.dev
24 35
36 +# External services
37 +USE_GRAVATAR=true
38 +
25 # Mail settings 39 # Mail settings
26 MAIL_DRIVER=smtp 40 MAIL_DRIVER=smtp
27 MAIL_HOST=localhost 41 MAIL_HOST=localhost
......
...@@ -7,23 +7,7 @@ use Illuminate\Database\Eloquent\Model; ...@@ -7,23 +7,7 @@ use Illuminate\Database\Eloquent\Model;
7 abstract class Entity extends Model 7 abstract class Entity extends Model
8 { 8 {
9 9
10 - /** 10 + use Ownable;
11 - * Relation for the user that created this entity.
12 - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
13 - */
14 - public function createdBy()
15 - {
16 - return $this->belongsTo('BookStack\User', 'created_by');
17 - }
18 -
19 - /**
20 - * Relation for the user that updated this entity.
21 - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
22 - */
23 - public function updatedBy()
24 - {
25 - return $this->belongsTo('BookStack\User', 'updated_by');
26 - }
27 11
28 /** 12 /**
29 * Compares this entity to another given entity. 13 * Compares this entity to another given entity.
...@@ -97,19 +81,30 @@ abstract class Entity extends Model ...@@ -97,19 +81,30 @@ abstract class Entity extends Model
97 */ 81 */
98 public static function isA($type) 82 public static function isA($type)
99 { 83 {
100 - return static::getName() === strtolower($type); 84 + return static::getClassName() === strtolower($type);
101 } 85 }
102 86
103 /** 87 /**
104 * Gets the class name. 88 * Gets the class name.
105 * @return string 89 * @return string
106 */ 90 */
107 - public static function getName() 91 + public static function getClassName()
108 { 92 {
109 return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]); 93 return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]);
110 } 94 }
111 95
112 /** 96 /**
97 + *Gets a limited-length version of the entities name.
98 + * @param int $length
99 + * @return string
100 + */
101 + public function getShortName($length = 25)
102 + {
103 + if(strlen($this->name) <= $length) return $this->name;
104 + return substr($this->name, 0, $length-3) . '...';
105 + }
106 +
107 + /**
113 * Perform a full-text search on this entity. 108 * Perform a full-text search on this entity.
114 * @param string[] $fieldsToSearch 109 * @param string[] $fieldsToSearch
115 * @param string[] $terms 110 * @param string[] $terms
...@@ -123,20 +118,20 @@ abstract class Entity extends Model ...@@ -123,20 +118,20 @@ abstract class Entity extends Model
123 $termString .= $term . '* '; 118 $termString .= $term . '* ';
124 } 119 }
125 $fields = implode(',', $fieldsToSearch); 120 $fields = implode(',', $fieldsToSearch);
126 - $search = static::whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]); 121 + $termStringEscaped = \DB::connection()->getPdo()->quote($termString);
122 + $search = static::addSelect(\DB::raw('*, MATCH(name) AGAINST('.$termStringEscaped.' IN BOOLEAN MODE) AS title_relevance'));
123 + $search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
124 +
125 + // Add additional where terms
127 foreach ($wheres as $whereTerm) { 126 foreach ($wheres as $whereTerm) {
128 $search->where($whereTerm[0], $whereTerm[1], $whereTerm[2]); 127 $search->where($whereTerm[0], $whereTerm[1], $whereTerm[2]);
129 } 128 }
130 129
131 - if (!static::isA('book')) { 130 + // Load in relations
132 - $search = $search->with('book'); 131 + if (!static::isA('book')) $search = $search->with('book');
133 - } 132 + if (static::isA('page')) $search = $search->with('chapter');
134 -
135 - if (static::isA('page')) {
136 - $search = $search->with('chapter');
137 - }
138 133
139 - return $search->get(); 134 + return $search->orderBy('title_relevance', 'desc')->get();
140 } 135 }
141 136
142 /** 137 /**
......
...@@ -42,8 +42,10 @@ class BookController extends Controller ...@@ -42,8 +42,10 @@ class BookController extends Controller
42 public function index() 42 public function index()
43 { 43 {
44 $books = $this->bookRepo->getAllPaginated(10); 44 $books = $this->bookRepo->getAllPaginated(10);
45 - $recents = $this->signedIn ? $this->bookRepo->getRecentlyViewed(10, 0) : false; 45 + $recents = $this->signedIn ? $this->bookRepo->getRecentlyViewed(4, 0) : false;
46 - return view('books/index', ['books' => $books, 'recents' => $recents]); 46 + $popular = $this->bookRepo->getPopular(4, 0);
47 + $this->setPageTitle('Books');
48 + return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular]);
47 } 49 }
48 50
49 /** 51 /**
...@@ -54,6 +56,7 @@ class BookController extends Controller ...@@ -54,6 +56,7 @@ class BookController extends Controller
54 public function create() 56 public function create()
55 { 57 {
56 $this->checkPermission('book-create'); 58 $this->checkPermission('book-create');
59 + $this->setPageTitle('Create New Book');
57 return view('books/create'); 60 return view('books/create');
58 } 61 }
59 62
...@@ -88,8 +91,9 @@ class BookController extends Controller ...@@ -88,8 +91,9 @@ class BookController extends Controller
88 public function show($slug) 91 public function show($slug)
89 { 92 {
90 $book = $this->bookRepo->getBySlug($slug); 93 $book = $this->bookRepo->getBySlug($slug);
91 - Views::add($book);
92 $bookChildren = $this->bookRepo->getChildren($book); 94 $bookChildren = $this->bookRepo->getChildren($book);
95 + Views::add($book);
96 + $this->setPageTitle($book->getShortName());
93 return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]); 97 return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
94 } 98 }
95 99
...@@ -103,6 +107,7 @@ class BookController extends Controller ...@@ -103,6 +107,7 @@ class BookController extends Controller
103 { 107 {
104 $this->checkPermission('book-update'); 108 $this->checkPermission('book-update');
105 $book = $this->bookRepo->getBySlug($slug); 109 $book = $this->bookRepo->getBySlug($slug);
110 + $this->setPageTitle('Edit Book ' . $book->getShortName());
106 return view('books/edit', ['book' => $book, 'current' => $book]); 111 return view('books/edit', ['book' => $book, 'current' => $book]);
107 } 112 }
108 113
...@@ -138,6 +143,7 @@ class BookController extends Controller ...@@ -138,6 +143,7 @@ class BookController extends Controller
138 { 143 {
139 $this->checkPermission('book-delete'); 144 $this->checkPermission('book-delete');
140 $book = $this->bookRepo->getBySlug($bookSlug); 145 $book = $this->bookRepo->getBySlug($bookSlug);
146 + $this->setPageTitle('Delete Book ' . $book->getShortName());
141 return view('books/delete', ['book' => $book, 'current' => $book]); 147 return view('books/delete', ['book' => $book, 'current' => $book]);
142 } 148 }
143 149
...@@ -152,9 +158,16 @@ class BookController extends Controller ...@@ -152,9 +158,16 @@ class BookController extends Controller
152 $book = $this->bookRepo->getBySlug($bookSlug); 158 $book = $this->bookRepo->getBySlug($bookSlug);
153 $bookChildren = $this->bookRepo->getChildren($book); 159 $bookChildren = $this->bookRepo->getChildren($book);
154 $books = $this->bookRepo->getAll(); 160 $books = $this->bookRepo->getAll();
161 + $this->setPageTitle('Sort Book ' . $book->getShortName());
155 return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]); 162 return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]);
156 } 163 }
157 164
165 + /**
166 + * Shows the sort box for a single book.
167 + * Used via AJAX when loading in extra books to a sort.
168 + * @param $bookSlug
169 + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
170 + */
158 public function getSortItem($bookSlug) 171 public function getSortItem($bookSlug)
159 { 172 {
160 $book = $this->bookRepo->getBySlug($bookSlug); 173 $book = $this->bookRepo->getBySlug($bookSlug);
......
...@@ -40,6 +40,7 @@ class ChapterController extends Controller ...@@ -40,6 +40,7 @@ class ChapterController extends Controller
40 { 40 {
41 $this->checkPermission('chapter-create'); 41 $this->checkPermission('chapter-create');
42 $book = $this->bookRepo->getBySlug($bookSlug); 42 $book = $this->bookRepo->getBySlug($bookSlug);
43 + $this->setPageTitle('Create New Chapter');
43 return view('chapters/create', ['book' => $book, 'current' => $book]); 44 return view('chapters/create', ['book' => $book, 'current' => $book]);
44 } 45 }
45 46
...@@ -79,6 +80,7 @@ class ChapterController extends Controller ...@@ -79,6 +80,7 @@ class ChapterController extends Controller
79 $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); 80 $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
80 $sidebarTree = $this->bookRepo->getChildren($book); 81 $sidebarTree = $this->bookRepo->getChildren($book);
81 Views::add($chapter); 82 Views::add($chapter);
83 + $this->setPageTitle($chapter->getShortName());
82 return view('chapters/show', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter, 'sidebarTree' => $sidebarTree]); 84 return view('chapters/show', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter, 'sidebarTree' => $sidebarTree]);
83 } 85 }
84 86
...@@ -93,6 +95,7 @@ class ChapterController extends Controller ...@@ -93,6 +95,7 @@ class ChapterController extends Controller
93 $this->checkPermission('chapter-update'); 95 $this->checkPermission('chapter-update');
94 $book = $this->bookRepo->getBySlug($bookSlug); 96 $book = $this->bookRepo->getBySlug($bookSlug);
95 $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); 97 $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
98 + $this->setPageTitle('Edit Chapter' . $chapter->getShortName());
96 return view('chapters/edit', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]); 99 return view('chapters/edit', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]);
97 } 100 }
98 101
...@@ -127,6 +130,7 @@ class ChapterController extends Controller ...@@ -127,6 +130,7 @@ class ChapterController extends Controller
127 $this->checkPermission('chapter-delete'); 130 $this->checkPermission('chapter-delete');
128 $book = $this->bookRepo->getBySlug($bookSlug); 131 $book = $this->bookRepo->getBySlug($bookSlug);
129 $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); 132 $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
133 + $this->setPageTitle('Delete Chapter' . $chapter->getShortName());
130 return view('chapters/delete', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]); 134 return view('chapters/delete', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]);
131 } 135 }
132 136
......
...@@ -43,6 +43,15 @@ abstract class Controller extends BaseController ...@@ -43,6 +43,15 @@ abstract class Controller extends BaseController
43 } 43 }
44 44
45 /** 45 /**
46 + * Adds the page title into the view.
47 + * @param $title
48 + */
49 + public function setPageTitle($title)
50 + {
51 + view()->share('pageTitle', $title);
52 + }
53 +
54 + /**
46 * Checks for a permission. 55 * Checks for a permission.
47 * 56 *
48 * @param $permissionName 57 * @param $permissionName
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
2 2
3 namespace BookStack\Http\Controllers; 3 namespace BookStack\Http\Controllers;
4 4
5 +use BookStack\Repos\ImageRepo;
5 use Illuminate\Filesystem\Filesystem as File; 6 use Illuminate\Filesystem\Filesystem as File;
6 use Illuminate\Http\Request; 7 use Illuminate\Http\Request;
7 use Illuminate\Support\Facades\Auth; 8 use Illuminate\Support\Facades\Auth;
...@@ -14,125 +15,78 @@ class ImageController extends Controller ...@@ -14,125 +15,78 @@ class ImageController extends Controller
14 { 15 {
15 protected $image; 16 protected $image;
16 protected $file; 17 protected $file;
18 + protected $imageRepo;
17 19
18 /** 20 /**
19 * ImageController constructor. 21 * ImageController constructor.
20 - * @param Image $image 22 + * @param Image $image
21 - * @param File $file 23 + * @param File $file
24 + * @param ImageRepo $imageRepo
22 */ 25 */
23 - public function __construct(Image $image, File $file) 26 + public function __construct(Image $image, File $file, ImageRepo $imageRepo)
24 { 27 {
25 $this->image = $image; 28 $this->image = $image;
26 $this->file = $file; 29 $this->file = $file;
30 + $this->imageRepo = $imageRepo;
27 parent::__construct(); 31 parent::__construct();
28 } 32 }
29 33
30 34
31 /** 35 /**
32 - * Get all images, Paginated 36 + * Get all images for a specific type, Paginated
33 * @param int $page 37 * @param int $page
34 * @return \Illuminate\Http\JsonResponse 38 * @return \Illuminate\Http\JsonResponse
35 */ 39 */
36 - public function getAll($page = 0) 40 + public function getAllByType($type, $page = 0)
37 { 41 {
38 - $pageSize = 30; 42 + $imgData = $this->imageRepo->getPaginatedByType($type, $page);
39 - $images = $this->image->orderBy('created_at', 'desc') 43 + return response()->json($imgData);
40 - ->skip($page * $pageSize)->take($pageSize)->get();
41 - foreach ($images as $image) {
42 - $this->loadSizes($image);
43 - }
44 - $hasMore = $this->image->orderBy('created_at', 'desc')
45 - ->skip(($page + 1) * $pageSize)->take($pageSize)->count() > 0;
46 - return response()->json([
47 - 'images' => $images,
48 - 'hasMore' => $hasMore
49 - ]);
50 } 44 }
51 45
52 /** 46 /**
53 - * Loads the standard thumbnail sizes for an image. 47 + * Get all images for a user.
54 - * @param Image $image 48 + * @param int $page
49 + * @return \Illuminate\Http\JsonResponse
55 */ 50 */
56 - private function loadSizes(Image $image) 51 + public function getAllForUserType($page = 0)
57 { 52 {
58 - $image->thumbnail = $this->getThumbnail($image, 150, 150); 53 + $imgData = $this->imageRepo->getPaginatedByType('user', $page, 24, $this->currentUser->id);
59 - $image->display = $this->getThumbnail($image, 840, 0, true); 54 + return response()->json($imgData);
60 } 55 }
61 56
62 - /**
63 - * Get the thumbnail for an image.
64 - * If $keepRatio is true only the width will be used.
65 - * @param $image
66 - * @param int $width
67 - * @param int $height
68 - * @param bool $keepRatio
69 - * @return string
70 - */
71 - public function getThumbnail($image, $width = 220, $height = 220, $keepRatio = false)
72 - {
73 - $explodedPath = explode('/', $image->url);
74 - $dirPrefix = $keepRatio ? 'scaled-' : 'thumbs-';
75 - array_splice($explodedPath, 4, 0, [$dirPrefix . $width . '-' . $height]);
76 - $thumbPath = implode('/', $explodedPath);
77 - $thumbFilePath = public_path() . $thumbPath;
78 -
79 - // Return the thumbnail url path if already exists
80 - if (file_exists($thumbFilePath)) {
81 - return $thumbPath;
82 - }
83 -
84 - // Otherwise create the thumbnail
85 - $thumb = ImageTool::make(public_path() . $image->url);
86 - if($keepRatio) {
87 - $thumb->resize($width, null, function ($constraint) {
88 - $constraint->aspectRatio();
89 - $constraint->upsize();
90 - });
91 - } else {
92 - $thumb->fit($width, $height);
93 - }
94 -
95 - // Create thumbnail folder if it does not exist
96 - if (!file_exists(dirname($thumbFilePath))) {
97 - mkdir(dirname($thumbFilePath), 0775, true);
98 - }
99 -
100 - //Save Thumbnail
101 - $thumb->save($thumbFilePath);
102 - return $thumbPath;
103 - }
104 57
105 /** 58 /**
106 * Handles image uploads for use on pages. 59 * Handles image uploads for use on pages.
60 + * @param string $type
107 * @param Request $request 61 * @param Request $request
108 * @return \Illuminate\Http\JsonResponse 62 * @return \Illuminate\Http\JsonResponse
109 */ 63 */
110 - public function upload(Request $request) 64 + public function uploadByType($type, Request $request)
111 { 65 {
112 $this->checkPermission('image-create'); 66 $this->checkPermission('image-create');
113 $this->validate($request, [ 67 $this->validate($request, [
114 'file' => 'image|mimes:jpeg,gif,png' 68 'file' => 'image|mimes:jpeg,gif,png'
115 ]); 69 ]);
70 +
116 $imageUpload = $request->file('file'); 71 $imageUpload = $request->file('file');
72 + $image = $this->imageRepo->saveNew($imageUpload, $type);
73 + return response()->json($image);
74 + }
117 75
118 - $name = str_replace(' ', '-', $imageUpload->getClientOriginalName()); 76 + /**
119 - $storageName = substr(sha1(time()), 0, 10) . '-' . $name; 77 + * Generate a sized thumbnail for an image.
120 - $imagePath = '/uploads/images/' . Date('Y-m-M') . '/'; 78 + * @param $id
121 - $storagePath = public_path() . $imagePath; 79 + * @param $width
122 - $fullPath = $storagePath . $storageName; 80 + * @param $height
123 - while (file_exists($fullPath)) { 81 + * @param $crop
124 - $storageName = substr(sha1(rand()), 0, 3) . $storageName; 82 + * @return \Illuminate\Http\JsonResponse
125 - $fullPath = $storagePath . $storageName; 83 + */
126 - } 84 + public function getThumbnail($id, $width, $height, $crop)
127 - $imageUpload->move($storagePath, $storageName); 85 + {
128 - // Create and save image object 86 + $this->checkPermission('image-create');
129 - $this->image->name = $name; 87 + $image = $this->imageRepo->getById($id);
130 - $this->image->url = $imagePath . $storageName; 88 + $thumbnailUrl = $this->imageRepo->getThumbnail($image, $width, $height, $crop == 'false');
131 - $this->image->created_by = auth()->user()->id; 89 + return response()->json(['url' => $thumbnailUrl]);
132 - $this->image->updated_by = auth()->user()->id;
133 - $this->image->save();
134 - $this->loadSizes($this->image);
135 - return response()->json($this->image);
136 } 90 }
137 91
138 /** 92 /**
...@@ -147,13 +101,12 @@ class ImageController extends Controller ...@@ -147,13 +101,12 @@ class ImageController extends Controller
147 $this->validate($request, [ 101 $this->validate($request, [
148 'name' => 'required|min:2|string' 102 'name' => 'required|min:2|string'
149 ]); 103 ]);
150 - $image = $this->image->findOrFail($imageId); 104 + $image = $this->imageRepo->getById($imageId);
151 - $image->fill($request->all()); 105 + $image = $this->imageRepo->updateImageDetails($image, $request->all());
152 - $image->save(); 106 + return response()->json($image);
153 - $this->loadSizes($image);
154 - return response()->json($this->image);
155 } 107 }
156 108
109 +
157 /** 110 /**
158 * Deletes an image and all thumbnail/image files 111 * Deletes an image and all thumbnail/image files
159 * @param PageRepo $pageRepo 112 * @param PageRepo $pageRepo
...@@ -164,41 +117,18 @@ class ImageController extends Controller ...@@ -164,41 +117,18 @@ class ImageController extends Controller
164 public function destroy(PageRepo $pageRepo, Request $request, $id) 117 public function destroy(PageRepo $pageRepo, Request $request, $id)
165 { 118 {
166 $this->checkPermission('image-delete'); 119 $this->checkPermission('image-delete');
167 - $image = $this->image->findOrFail($id); 120 + $image = $this->imageRepo->getById($id);
168 121
169 // Check if this image is used on any pages 122 // Check if this image is used on any pages
170 - $pageSearch = $pageRepo->searchForImage($image->url);
171 $isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true); 123 $isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true);
172 - if ($pageSearch !== false && !$isForced) { 124 + if (!$isForced) {
173 - return response()->json($pageSearch, 400); 125 + $pageSearch = $pageRepo->searchForImage($image->url);
174 - } 126 + if ($pageSearch !== false) {
175 - 127 + return response()->json($pageSearch, 400);
176 - // Delete files
177 - $folder = public_path() . dirname($image->url);
178 - $fileName = basename($image->url);
179 -
180 - // Delete thumbnails
181 - foreach (glob($folder . '/*') as $file) {
182 - if (is_dir($file)) {
183 - $thumbName = $file . '/' . $fileName;
184 - if (file_exists($file)) {
185 - unlink($thumbName);
186 - }
187 - // Remove thumb folder if empty
188 - if (count(glob($file . '/*')) === 0) {
189 - rmdir($file);
190 - }
191 } 128 }
192 } 129 }
193 130
194 - // Delete file and database entry 131 + $this->imageRepo->destroyImage($image);
195 - unlink($folder . '/' . $fileName);
196 - $image->delete();
197 -
198 - // Delete parent folder if empty
199 - if (count(glob($folder . '/*')) === 0) {
200 - rmdir($folder);
201 - }
202 return response()->json('Image Deleted'); 132 return response()->json('Image Deleted');
203 } 133 }
204 134
......
...@@ -46,6 +46,7 @@ class PageController extends Controller ...@@ -46,6 +46,7 @@ class PageController extends Controller
46 $this->checkPermission('page-create'); 46 $this->checkPermission('page-create');
47 $book = $this->bookRepo->getBySlug($bookSlug); 47 $book = $this->bookRepo->getBySlug($bookSlug);
48 $chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : false; 48 $chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : false;
49 + $this->setPageTitle('Create New Page');
49 return view('pages/create', ['book' => $book, 'chapter' => $chapter]); 50 return view('pages/create', ['book' => $book, 'chapter' => $chapter]);
50 } 51 }
51 52
...@@ -89,6 +90,7 @@ class PageController extends Controller ...@@ -89,6 +90,7 @@ class PageController extends Controller
89 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 90 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
90 $sidebarTree = $this->bookRepo->getChildren($book); 91 $sidebarTree = $this->bookRepo->getChildren($book);
91 Views::add($page); 92 Views::add($page);
93 + $this->setPageTitle($page->getShortName());
92 return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]); 94 return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
93 } 95 }
94 96
...@@ -104,6 +106,7 @@ class PageController extends Controller ...@@ -104,6 +106,7 @@ class PageController extends Controller
104 $this->checkPermission('page-update'); 106 $this->checkPermission('page-update');
105 $book = $this->bookRepo->getBySlug($bookSlug); 107 $book = $this->bookRepo->getBySlug($bookSlug);
106 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 108 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
109 + $this->setPageTitle('Editing Page ' . $page->getShortName());
107 return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]); 110 return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
108 } 111 }
109 112
...@@ -148,6 +151,7 @@ class PageController extends Controller ...@@ -148,6 +151,7 @@ class PageController extends Controller
148 $this->checkPermission('page-delete'); 151 $this->checkPermission('page-delete');
149 $book = $this->bookRepo->getBySlug($bookSlug); 152 $book = $this->bookRepo->getBySlug($bookSlug);
150 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 153 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
154 + $this->setPageTitle('Delete Page ' . $page->getShortName());
151 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]); 155 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
152 } 156 }
153 157
...@@ -179,6 +183,7 @@ class PageController extends Controller ...@@ -179,6 +183,7 @@ class PageController extends Controller
179 { 183 {
180 $book = $this->bookRepo->getBySlug($bookSlug); 184 $book = $this->bookRepo->getBySlug($bookSlug);
181 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 185 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
186 + $this->setPageTitle('Revisions For ' . $page->getShortName());
182 return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]); 187 return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
183 } 188 }
184 189
...@@ -195,6 +200,7 @@ class PageController extends Controller ...@@ -195,6 +200,7 @@ class PageController extends Controller
195 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 200 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
196 $revision = $this->pageRepo->getRevisionById($revisionId); 201 $revision = $this->pageRepo->getRevisionById($revisionId);
197 $page->fill($revision->toArray()); 202 $page->fill($revision->toArray());
203 + $this->setPageTitle('Page Revision For ' . $page->getShortName());
198 return view('pages/revision', ['page' => $page, 'book' => $book]); 204 return view('pages/revision', ['page' => $page, 'book' => $book]);
199 } 205 }
200 206
......
...@@ -45,6 +45,7 @@ class SearchController extends Controller ...@@ -45,6 +45,7 @@ class SearchController extends Controller
45 $pages = $this->pageRepo->getBySearch($searchTerm); 45 $pages = $this->pageRepo->getBySearch($searchTerm);
46 $books = $this->bookRepo->getBySearch($searchTerm); 46 $books = $this->bookRepo->getBySearch($searchTerm);
47 $chapters = $this->chapterRepo->getBySearch($searchTerm); 47 $chapters = $this->chapterRepo->getBySearch($searchTerm);
48 + $this->setPageTitle('Search For ' . $searchTerm);
48 return view('search/all', ['pages' => $pages, 'books' => $books, 'chapters' => $chapters, 'searchTerm' => $searchTerm]); 49 return view('search/all', ['pages' => $pages, 'books' => $books, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
49 } 50 }
50 51
......
...@@ -18,6 +18,7 @@ class SettingController extends Controller ...@@ -18,6 +18,7 @@ class SettingController extends Controller
18 public function index() 18 public function index()
19 { 19 {
20 $this->checkPermission('settings-update'); 20 $this->checkPermission('settings-update');
21 + $this->setPageTitle('Settings');
21 return view('settings/index'); 22 return view('settings/index');
22 } 23 }
23 24
......
...@@ -4,7 +4,7 @@ namespace BookStack\Http\Controllers; ...@@ -4,7 +4,7 @@ namespace BookStack\Http\Controllers;
4 4
5 use Illuminate\Http\Request; 5 use Illuminate\Http\Request;
6 6
7 -use Illuminate\Support\Facades\Hash; 7 +use Illuminate\Http\Response;
8 use BookStack\Http\Requests; 8 use BookStack\Http\Requests;
9 use BookStack\Repos\UserRepo; 9 use BookStack\Repos\UserRepo;
10 use BookStack\Services\SocialAuthService; 10 use BookStack\Services\SocialAuthService;
...@@ -18,7 +18,8 @@ class UserController extends Controller ...@@ -18,7 +18,8 @@ class UserController extends Controller
18 18
19 /** 19 /**
20 * UserController constructor. 20 * UserController constructor.
21 - * @param $user 21 + * @param User $user
22 + * @param UserRepo $userRepo
22 */ 23 */
23 public function __construct(User $user, UserRepo $userRepo) 24 public function __construct(User $user, UserRepo $userRepo)
24 { 25 {
...@@ -29,18 +30,17 @@ class UserController extends Controller ...@@ -29,18 +30,17 @@ class UserController extends Controller
29 30
30 /** 31 /**
31 * Display a listing of the users. 32 * Display a listing of the users.
32 - *
33 * @return Response 33 * @return Response
34 */ 34 */
35 public function index() 35 public function index()
36 { 36 {
37 $users = $this->user->all(); 37 $users = $this->user->all();
38 + $this->setPageTitle('Users');
38 return view('users/index', ['users' => $users]); 39 return view('users/index', ['users' => $users]);
39 } 40 }
40 41
41 /** 42 /**
42 * Show the form for creating a new user. 43 * Show the form for creating a new user.
43 - *
44 * @return Response 44 * @return Response
45 */ 45 */
46 public function create() 46 public function create()
...@@ -51,7 +51,6 @@ class UserController extends Controller ...@@ -51,7 +51,6 @@ class UserController extends Controller
51 51
52 /** 52 /**
53 * Store a newly created user in storage. 53 * Store a newly created user in storage.
54 - *
55 * @param Request $request 54 * @param Request $request
56 * @return Response 55 * @return Response
57 */ 56 */
...@@ -60,7 +59,7 @@ class UserController extends Controller ...@@ -60,7 +59,7 @@ class UserController extends Controller
60 $this->checkPermission('user-create'); 59 $this->checkPermission('user-create');
61 $this->validate($request, [ 60 $this->validate($request, [
62 'name' => 'required', 61 'name' => 'required',
63 - 'email' => 'required|email', 62 + 'email' => 'required|email|unique:users,email',
64 'password' => 'required|min:5', 63 'password' => 'required|min:5',
65 'password-confirm' => 'required|same:password', 64 'password-confirm' => 'required|same:password',
66 'role' => 'required|exists:roles,id' 65 'role' => 'required|exists:roles,id'
...@@ -71,13 +70,20 @@ class UserController extends Controller ...@@ -71,13 +70,20 @@ class UserController extends Controller
71 $user->save(); 70 $user->save();
72 71
73 $user->attachRoleId($request->get('role')); 72 $user->attachRoleId($request->get('role'));
73 +
74 + // Get avatar from gravatar and save
75 + if (!env('DISABLE_EXTERNAL_SERVICES', false)) {
76 + $avatar = \Images::saveUserGravatar($user);
77 + $user->avatar()->associate($avatar);
78 + $user->save();
79 + }
80 +
74 return redirect('/users'); 81 return redirect('/users');
75 } 82 }
76 83
77 84
78 /** 85 /**
79 * Show the form for editing the specified user. 86 * Show the form for editing the specified user.
80 - *
81 * @param int $id 87 * @param int $id
82 * @param SocialAuthService $socialAuthService 88 * @param SocialAuthService $socialAuthService
83 * @return Response 89 * @return Response
...@@ -90,12 +96,12 @@ class UserController extends Controller ...@@ -90,12 +96,12 @@ class UserController extends Controller
90 96
91 $user = $this->user->findOrFail($id); 97 $user = $this->user->findOrFail($id);
92 $activeSocialDrivers = $socialAuthService->getActiveDrivers(); 98 $activeSocialDrivers = $socialAuthService->getActiveDrivers();
99 + $this->setPageTitle('User Profile');
93 return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers]); 100 return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers]);
94 } 101 }
95 102
96 /** 103 /**
97 * Update the specified user in storage. 104 * Update the specified user in storage.
98 - *
99 * @param Request $request 105 * @param Request $request
100 * @param int $id 106 * @param int $id
101 * @return Response 107 * @return Response
...@@ -139,12 +145,12 @@ class UserController extends Controller ...@@ -139,12 +145,12 @@ class UserController extends Controller
139 return $this->currentUser->id == $id; 145 return $this->currentUser->id == $id;
140 }); 146 });
141 $user = $this->user->findOrFail($id); 147 $user = $this->user->findOrFail($id);
148 + $this->setPageTitle('Delete User ' . $user->name);
142 return view('users/delete', ['user' => $user]); 149 return view('users/delete', ['user' => $user]);
143 } 150 }
144 151
145 /** 152 /**
146 * Remove the specified user from storage. 153 * Remove the specified user from storage.
147 - *
148 * @param int $id 154 * @param int $id
149 * @return Response 155 * @return Response
150 */ 156 */
...@@ -153,14 +159,14 @@ class UserController extends Controller ...@@ -153,14 +159,14 @@ class UserController extends Controller
153 $this->checkPermissionOr('user-delete', function () use ($id) { 159 $this->checkPermissionOr('user-delete', function () use ($id) {
154 return $this->currentUser->id == $id; 160 return $this->currentUser->id == $id;
155 }); 161 });
162 +
156 $user = $this->userRepo->getById($id); 163 $user = $this->userRepo->getById($id);
157 - // Delete social accounts 164 + if ($this->userRepo->isOnlyAdmin($user)) {
158 - if($this->userRepo->isOnlyAdmin($user)) {
159 session()->flash('error', 'You cannot delete the only admin'); 165 session()->flash('error', 'You cannot delete the only admin');
160 return redirect($user->getEditUrl()); 166 return redirect($user->getEditUrl());
161 } 167 }
162 - $user->socialAccounts()->delete(); 168 + $this->userRepo->destroy($user);
163 - $user->delete(); 169 +
164 return redirect('/users'); 170 return redirect('/users');
165 } 171 }
166 } 172 }
......
...@@ -45,8 +45,6 @@ Route::group(['middleware' => 'auth'], function () { ...@@ -45,8 +45,6 @@ Route::group(['middleware' => 'auth'], function () {
45 45
46 }); 46 });
47 47
48 - // Uploads
49 - Route::post('/upload/image', 'ImageController@upload');
50 48
51 // Users 49 // Users
52 Route::get('/users', 'UserController@index'); 50 Route::get('/users', 'UserController@index');
...@@ -58,10 +56,18 @@ Route::group(['middleware' => 'auth'], function () { ...@@ -58,10 +56,18 @@ Route::group(['middleware' => 'auth'], function () {
58 Route::delete('/users/{id}', 'UserController@destroy'); 56 Route::delete('/users/{id}', 'UserController@destroy');
59 57
60 // Image routes 58 // Image routes
61 - Route::get('/images/all', 'ImageController@getAll'); 59 + Route::group(['prefix' => 'images'], function() {
62 - Route::put('/images/update/{imageId}', 'ImageController@update'); 60 + // Get for user images
63 - Route::delete('/images/{imageId}', 'ImageController@destroy'); 61 + Route::get('/user/all', 'ImageController@getAllForUserType');
64 - Route::get('/images/all/{page}', 'ImageController@getAll'); 62 + Route::get('/user/all/{page}', 'ImageController@getAllForUserType');
63 + // Standard get, update and deletion for all types
64 + Route::get('/thumb/{id}/{width}/{height}/{crop}', 'ImageController@getThumbnail');
65 + Route::put('/update/{imageId}', 'ImageController@update');
66 + Route::post('/{type}/upload', 'ImageController@uploadByType');
67 + Route::get('/{type}/all', 'ImageController@getAllByType');
68 + Route::get('/{type}/all/{page}', 'ImageController@getAllByType');
69 + Route::delete('/{imageId}', 'ImageController@destroy');
70 + });
65 71
66 // Links 72 // Links
67 Route::get('/link/{id}', 'PageController@redirectFromLink'); 73 Route::get('/link/{id}', 'PageController@redirectFromLink');
......
...@@ -3,22 +3,24 @@ ...@@ -3,22 +3,24 @@
3 namespace BookStack; 3 namespace BookStack;
4 4
5 5
6 -class Image extends Entity 6 +use Illuminate\Database\Eloquent\Model;
7 +use Images;
8 +
9 +class Image extends Model
7 { 10 {
11 + use Ownable;
8 12
9 protected $fillable = ['name']; 13 protected $fillable = ['name'];
10 14
11 - public function getFilePath()
12 - {
13 - return storage_path() . $this->url;
14 - }
15 -
16 /** 15 /**
17 - * Get the url for this item. 16 + * Get a thumbnail for this image.
17 + * @param int $width
18 + * @param int $height
19 + * @param bool|false $keepRatio
18 * @return string 20 * @return string
19 */ 21 */
20 - public function getUrl() 22 + public function getThumb($width, $height, $keepRatio = false)
21 { 23 {
22 - return public_path() . $this->url; 24 + return Images::getThumbnail($this, $width, $height, $keepRatio);
23 } 25 }
24 } 26 }
......
1 +<?php namespace BookStack;
2 +
3 +
4 +trait Ownable
5 +{
6 + /**
7 + * Relation for the user that created this entity.
8 + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
9 + */
10 + public function createdBy()
11 + {
12 + return $this->belongsTo('BookStack\User', 'created_by');
13 + }
14 +
15 + /**
16 + * Relation for the user that updated this entity.
17 + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
18 + */
19 + public function updatedBy()
20 + {
21 + return $this->belongsTo('BookStack\User', 'updated_by');
22 + }
23 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -32,7 +32,6 @@ class Page extends Entity ...@@ -32,7 +32,6 @@ class Page extends Entity
32 return $this->chapter()->count() > 0; 32 return $this->chapter()->count() > 0;
33 } 33 }
34 34
35 -
36 public function revisions() 35 public function revisions()
37 { 36 {
38 return $this->hasMany('BookStack\PageRevision')->orderBy('created_at', 'desc'); 37 return $this->hasMany('BookStack\PageRevision')->orderBy('created_at', 'desc');
...@@ -40,7 +39,6 @@ class Page extends Entity ...@@ -40,7 +39,6 @@ class Page extends Entity
40 39
41 public function getUrl() 40 public function getUrl()
42 { 41 {
43 - // TODO - Extract this and share with chapters
44 $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug; 42 $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
45 return '/books/' . $bookSlug . '/page/' . $this->slug; 43 return '/books/' . $bookSlug . '/page/' . $this->slug;
46 } 44 }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
2 2
3 namespace BookStack\Providers; 3 namespace BookStack\Providers;
4 4
5 +use BookStack\Services\ImageService;
5 use BookStack\Services\ViewService; 6 use BookStack\Services\ViewService;
6 use Illuminate\Support\ServiceProvider; 7 use Illuminate\Support\ServiceProvider;
7 use BookStack\Services\ActivityService; 8 use BookStack\Services\ActivityService;
...@@ -40,5 +41,12 @@ class CustomFacadeProvider extends ServiceProvider ...@@ -40,5 +41,12 @@ class CustomFacadeProvider extends ServiceProvider
40 $this->app->make('Illuminate\Contracts\Cache\Repository') 41 $this->app->make('Illuminate\Contracts\Cache\Repository')
41 ); 42 );
42 }); 43 });
44 + $this->app->bind('images', function() {
45 + return new ImageService(
46 + $this->app->make('Intervention\Image\ImageManager'),
47 + $this->app->make('Illuminate\Contracts\Filesystem\Factory'),
48 + $this->app->make('Illuminate\Contracts\Cache\Repository')
49 + );
50 + });
43 } 51 }
44 } 52 }
......
...@@ -78,6 +78,17 @@ class BookRepo ...@@ -78,6 +78,17 @@ class BookRepo
78 } 78 }
79 79
80 /** 80 /**
81 + * Gets the most viewed books.
82 + * @param int $count
83 + * @param int $page
84 + * @return mixed
85 + */
86 + public function getPopular($count = 10, $page = 0)
87 + {
88 + return Views::getPopular($count, $page, $this->book);
89 + }
90 +
91 + /**
81 * Get a book by slug 92 * Get a book by slug
82 * @param $slug 93 * @param $slug
83 * @return mixed 94 * @return mixed
......
1 +<?php namespace BookStack\Repos;
2 +
3 +
4 +use BookStack\Image;
5 +use BookStack\Services\ImageService;
6 +use Setting;
7 +use Symfony\Component\HttpFoundation\File\UploadedFile;
8 +
9 +class ImageRepo
10 +{
11 +
12 + protected $image;
13 + protected $imageService;
14 +
15 + /**
16 + * ImageRepo constructor.
17 + * @param Image $image
18 + * @param ImageService $imageService
19 + */
20 + public function __construct(Image $image, ImageService $imageService)
21 + {
22 + $this->image = $image;
23 + $this->imageService = $imageService;
24 + }
25 +
26 +
27 + /**
28 + * Get an image with the given id.
29 + * @param $id
30 + * @return mixed
31 + */
32 + public function getById($id)
33 + {
34 + return $this->image->findOrFail($id);
35 + }
36 +
37 +
38 + /**
39 + * Gets a load images paginated, filtered by image type.
40 + * @param string $type
41 + * @param int $page
42 + * @param int $pageSize
43 + * @param bool|int $userFilter
44 + * @return array
45 + */
46 + public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false)
47 + {
48 + $images = $this->image->where('type', '=', strtolower($type));
49 +
50 + if ($userFilter !== false) {
51 + $images = $images->where('created_by', '=', $userFilter);
52 + }
53 +
54 + $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get();
55 + $hasMore = count($images) > $pageSize;
56 +
57 + $returnImages = $images->take(24);
58 + $returnImages->each(function ($image) {
59 + $this->loadThumbs($image);
60 + });
61 +
62 + return [
63 + 'images' => $returnImages,
64 + 'hasMore' => $hasMore
65 + ];
66 + }
67 +
68 + /**
69 + * Save a new image into storage and return the new image.
70 + * @param UploadedFile $uploadFile
71 + * @param string $type
72 + * @return Image
73 + */
74 + public function saveNew(UploadedFile $uploadFile, $type)
75 + {
76 + $image = $this->imageService->saveNewFromUpload($uploadFile, $type);
77 + $this->loadThumbs($image);
78 + return $image;
79 + }
80 +
81 + /**
82 + * Update the details of an image via an array of properties.
83 + * @param Image $image
84 + * @param array $updateDetails
85 + * @return Image
86 + */
87 + public function updateImageDetails(Image $image, $updateDetails)
88 + {
89 + $image->fill($updateDetails);
90 + $image->save();
91 + $this->loadThumbs($image);
92 + return $image;
93 + }
94 +
95 +
96 + /**
97 + * Destroys an Image object along with its files and thumbnails.
98 + * @param Image $image
99 + * @return bool
100 + */
101 + public function destroyImage(Image $image)
102 + {
103 + $this->imageService->destroyImage($image);
104 + return true;
105 + }
106 +
107 +
108 + /**
109 + * Load thumbnails onto an image object.
110 + * @param Image $image
111 + */
112 + private function loadThumbs(Image $image)
113 + {
114 + $image->thumbs = [
115 + 'gallery' => $this->getThumbnail($image, 150, 150),
116 + 'display' => $this->getThumbnail($image, 840, 0, true)
117 + ];
118 + }
119 +
120 + /**
121 + * Get the thumbnail for an image.
122 + * If $keepRatio is true only the width will be used.
123 + * Checks the cache then storage to avoid creating / accessing the filesystem on every check.
124 + *
125 + * @param Image $image
126 + * @param int $width
127 + * @param int $height
128 + * @param bool $keepRatio
129 + * @return string
130 + */
131 + public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
132 + {
133 + return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
134 + }
135 +
136 +
137 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -269,7 +269,7 @@ class PageRepo ...@@ -269,7 +269,7 @@ class PageRepo
269 * @param Page $page 269 * @param Page $page
270 * @return $this 270 * @return $this
271 */ 271 */
272 - private function saveRevision(Page $page) 272 + public function saveRevision(Page $page)
273 { 273 {
274 $revision = $this->pageRevision->fill($page->toArray()); 274 $revision = $this->pageRevision->fill($page->toArray());
275 $revision->page_id = $page->id; 275 $revision->page_id = $page->id;
......
...@@ -46,14 +46,19 @@ class UserRepo ...@@ -46,14 +46,19 @@ class UserRepo
46 public function registerNew(array $data) 46 public function registerNew(array $data)
47 { 47 {
48 $user = $this->create($data); 48 $user = $this->create($data);
49 - $roleId = \Setting::get('registration-role'); 49 + $this->attachDefaultRole($user);
50 - 50 + return $user;
51 - if ($roleId === false) { 51 + }
52 - $roleId = $this->role->getDefault()->id;
53 - }
54 52
53 + /**
54 + * Give a user the default role. Used when creating a new user.
55 + * @param $user
56 + */
57 + public function attachDefaultRole($user)
58 + {
59 + $roleId = \Setting::get('registration-role');
60 + if ($roleId === false) $roleId = $this->role->getDefault()->id;
55 $user->attachRoleId($roleId); 61 $user->attachRoleId($roleId);
56 - return $user;
57 } 62 }
58 63
59 /** 64 /**
...@@ -88,4 +93,14 @@ class UserRepo ...@@ -88,4 +93,14 @@ class UserRepo
88 'password' => bcrypt($data['password']) 93 'password' => bcrypt($data['password'])
89 ]); 94 ]);
90 } 95 }
96 +
97 + /**
98 + * Remove the given user from storage, Delete all related content.
99 + * @param User $user
100 + */
101 + public function destroy(User $user)
102 + {
103 + $user->socialAccounts()->delete();
104 + $user->delete();
105 + }
91 } 106 }
...\ No newline at end of file ...\ No newline at end of file
......
1 +<?php namespace BookStack\Services\Facades;
2 +
3 +
4 +use Illuminate\Support\Facades\Facade;
5 +
6 +class Images extends Facade
7 +{
8 + /**
9 + * Get the registered name of the component.
10 + *
11 + * @return string
12 + */
13 + protected static function getFacadeAccessor() { return 'images'; }
14 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<?php namespace BookStack\Services;
2 +
3 +use BookStack\Image;
4 +use BookStack\User;
5 +use Intervention\Image\ImageManager;
6 +use Illuminate\Contracts\Filesystem\Factory as FileSystem;
7 +use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
8 +use Illuminate\Contracts\Cache\Repository as Cache;
9 +use Setting;
10 +use Symfony\Component\HttpFoundation\File\UploadedFile;
11 +
12 +class ImageService
13 +{
14 +
15 + protected $imageTool;
16 + protected $fileSystem;
17 + protected $cache;
18 +
19 + /**
20 + * @var FileSystemInstance
21 + */
22 + protected $storageInstance;
23 + protected $storageUrl;
24 +
25 + /**
26 + * ImageService constructor.
27 + * @param $imageTool
28 + * @param $fileSystem
29 + * @param $cache
30 + */
31 + public function __construct(ImageManager $imageTool, FileSystem $fileSystem, Cache $cache)
32 + {
33 + $this->imageTool = $imageTool;
34 + $this->fileSystem = $fileSystem;
35 + $this->cache = $cache;
36 + }
37 +
38 + /**
39 + * Saves a new image from an upload.
40 + * @param UploadedFile $uploadedFile
41 + * @param string $type
42 + * @return mixed
43 + */
44 + public function saveNewFromUpload(UploadedFile $uploadedFile, $type)
45 + {
46 + $imageName = $uploadedFile->getClientOriginalName();
47 + $imageData = file_get_contents($uploadedFile->getRealPath());
48 + return $this->saveNew($imageName, $imageData, $type);
49 + }
50 +
51 +
52 + /**
53 + * Gets an image from url and saves it to the database.
54 + * @param $url
55 + * @param string $type
56 + * @param bool|string $imageName
57 + * @return mixed
58 + * @throws \Exception
59 + */
60 + private function saveNewFromUrl($url, $type, $imageName = false)
61 + {
62 + $imageName = $imageName ? $imageName : basename($url);
63 + $imageData = file_get_contents($url);
64 + if($imageData === false) throw new \Exception('Cannot get image from ' . $url);
65 + return $this->saveNew($imageName, $imageData, $type);
66 + }
67 +
68 + /**
69 + * Saves a new image
70 + * @param string $imageName
71 + * @param string $imageData
72 + * @param string $type
73 + * @return Image
74 + */
75 + private function saveNew($imageName, $imageData, $type)
76 + {
77 + $storage = $this->getStorage();
78 + $secureUploads = Setting::get('app-secure-images');
79 + $imageName = str_replace(' ', '-', $imageName);
80 +
81 + if ($secureUploads) $imageName = str_random(16) . '-' . $imageName;
82 +
83 + $imagePath = '/uploads/images/' . $type . '/' . Date('Y-m-M') . '/';
84 + while ($storage->exists($imagePath . $imageName)) {
85 + $imageName = str_random(3) . $imageName;
86 + }
87 + $fullPath = $imagePath . $imageName;
88 +
89 + $storage->put($fullPath, $imageData);
90 +
91 + $userId = auth()->user()->id;
92 + $image = Image::forceCreate([
93 + 'name' => $imageName,
94 + 'path' => $fullPath,
95 + 'url' => $this->getPublicUrl($fullPath),
96 + 'type' => $type,
97 + 'created_by' => $userId,
98 + 'updated_by' => $userId
99 + ]);
100 +
101 + return $image;
102 + }
103 +
104 + /**
105 + * Get the thumbnail for an image.
106 + * If $keepRatio is true only the width will be used.
107 + * Checks the cache then storage to avoid creating / accessing the filesystem on every check.
108 + *
109 + * @param Image $image
110 + * @param int $width
111 + * @param int $height
112 + * @param bool $keepRatio
113 + * @return string
114 + */
115 + public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
116 + {
117 + $thumbDirName = '/' . ($keepRatio ? 'scaled-' : 'thumbs-') . $width . '-' . $height . '/';
118 + $thumbFilePath = dirname($image->path) . $thumbDirName . basename($image->path);
119 +
120 + if ($this->cache->has('images-' . $image->id . '-' . $thumbFilePath) && $this->cache->get('images-' . $thumbFilePath)) {
121 + return $this->getPublicUrl($thumbFilePath);
122 + }
123 +
124 + $storage = $this->getStorage();
125 +
126 + if ($storage->exists($thumbFilePath)) {
127 + return $this->getPublicUrl($thumbFilePath);
128 + }
129 +
130 + // Otherwise create the thumbnail
131 + $thumb = $this->imageTool->make($storage->get($image->path));
132 + if ($keepRatio) {
133 + $thumb->resize($width, null, function ($constraint) {
134 + $constraint->aspectRatio();
135 + $constraint->upsize();
136 + });
137 + } else {
138 + $thumb->fit($width, $height);
139 + }
140 +
141 + $thumbData = (string)$thumb->encode();
142 + $storage->put($thumbFilePath, $thumbData);
143 + $this->cache->put('images-' . $image->id . '-' . $thumbFilePath, $thumbFilePath, 60 * 72);
144 +
145 + return $this->getPublicUrl($thumbFilePath);
146 + }
147 +
148 + /**
149 + * Destroys an Image object along with its files and thumbnails.
150 + * @param Image $image
151 + * @return bool
152 + */
153 + public function destroyImage(Image $image)
154 + {
155 + $storage = $this->getStorage();
156 +
157 + $imageFolder = dirname($image->path);
158 + $imageFileName = basename($image->path);
159 + $allImages = collect($storage->allFiles($imageFolder));
160 +
161 + $imagesToDelete = $allImages->filter(function ($imagePath) use ($imageFileName) {
162 + $expectedIndex = strlen($imagePath) - strlen($imageFileName);
163 + return strpos($imagePath, $imageFileName) === $expectedIndex;
164 + });
165 +
166 + $storage->delete($imagesToDelete->all());
167 +
168 + // Cleanup of empty folders
169 + foreach ($storage->directories($imageFolder) as $directory) {
170 + if ($this->isFolderEmpty($directory)) $storage->deleteDirectory($directory);
171 + }
172 + if ($this->isFolderEmpty($imageFolder)) $storage->deleteDirectory($imageFolder);
173 +
174 + $image->delete();
175 + return true;
176 + }
177 +
178 + /**
179 + * Save a gravatar image and set a the profile image for a user.
180 + * @param User $user
181 + * @param int $size
182 + * @return mixed
183 + */
184 + public function saveUserGravatar(User $user, $size = 500)
185 + {
186 + $emailHash = md5(strtolower(trim($user->email)));
187 + $url = 'http://www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon';
188 + $imageName = str_replace(' ', '-', $user->name . '-gravatar.png');
189 + $image = $this->saveNewFromUrl($url, 'user', $imageName);
190 + $image->created_by = $user->id;
191 + $image->save();
192 + return $image;
193 + }
194 +
195 + /**
196 + * Get the storage that will be used for storing images.
197 + * @return FileSystemInstance
198 + */
199 + private function getStorage()
200 + {
201 + if ($this->storageInstance !== null) return $this->storageInstance;
202 +
203 + $storageType = env('STORAGE_TYPE');
204 + $this->storageInstance = $this->fileSystem->disk($storageType);
205 +
206 + return $this->storageInstance;
207 + }
208 +
209 + /**
210 + * Check whether or not a folder is empty.
211 + * @param $path
212 + * @return int
213 + */
214 + private function isFolderEmpty($path)
215 + {
216 + $files = $this->getStorage()->files($path);
217 + $folders = $this->getStorage()->directories($path);
218 + return count($files) === 0 && count($folders) === 0;
219 + }
220 +
221 + /**
222 + * Gets a public facing url for an image by checking relevant environment variables.
223 + * @param $filePath
224 + * @return string
225 + */
226 + private function getPublicUrl($filePath)
227 + {
228 + if ($this->storageUrl === null) {
229 + $storageUrl = env('STORAGE_URL');
230 +
231 + // Get the standard public s3 url if s3 is set as storage type
232 + if ($storageUrl == false && env('STORAGE_TYPE') === 's3') {
233 + $storageDetails = config('filesystems.disks.s3');
234 + $storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
235 + }
236 +
237 + $this->storageUrl = $storageUrl;
238 + }
239 +
240 + return ($this->storageUrl == false ? '' : rtrim($this->storageUrl, '/')) . $filePath;
241 + }
242 +
243 +
244 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -44,6 +44,29 @@ class ViewService ...@@ -44,6 +44,29 @@ class ViewService
44 return 1; 44 return 1;
45 } 45 }
46 46
47 +
48 + /**
49 + * Get the entities with the most views.
50 + * @param int $count
51 + * @param int $page
52 + * @param bool|false $filterModel
53 + */
54 + public function getPopular($count = 10, $page = 0, $filterModel = false)
55 + {
56 + $skipCount = $count * $page;
57 + $query = $this->view->select('id', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count'))
58 + ->groupBy('viewable_id', 'viewable_type')
59 + ->orderBy('view_count', 'desc');
60 +
61 + if($filterModel) $query->where('viewable_type', '=', get_class($filterModel));
62 +
63 + $views = $query->with('viewable')->skip($skipCount)->take($count)->get();
64 + $viewedEntities = $views->map(function ($item) {
65 + return $item->viewable()->getResults();
66 + });
67 + return $viewedEntities;
68 + }
69 +
47 /** 70 /**
48 * Get all recently viewed entities for the current user. 71 * Get all recently viewed entities for the current user.
49 * @param int $count 72 * @param int $count
......
...@@ -24,7 +24,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -24,7 +24,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
24 * 24 *
25 * @var array 25 * @var array
26 */ 26 */
27 - protected $fillable = ['name', 'email', 'password']; 27 + protected $fillable = ['name', 'email', 'password', 'image_id'];
28 28
29 /** 29 /**
30 * The attributes excluded from the model's JSON form. 30 * The attributes excluded from the model's JSON form.
...@@ -145,8 +145,17 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -145,8 +145,17 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
145 */ 145 */
146 public function getAvatar($size = 50) 146 public function getAvatar($size = 50)
147 { 147 {
148 - $emailHash = md5(strtolower(trim($this->email))); 148 + if ($this->image_id === 0 || $this->image_id === '0' || $this->image_id === null) return '/user_avatar.png';
149 - return '//www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon'; 149 + return $this->avatar->getThumb($size, $size, false);
150 + }
151 +
152 + /**
153 + * Get the avatar for the user.
154 + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
155 + */
156 + public function avatar()
157 + {
158 + return $this->belongsTo('BookStack\Image', 'image_id');
150 } 159 }
151 160
152 /** 161 /**
......
1 +<?php
2 +
3 +if (! function_exists('versioned_asset')) {
4 + /**
5 + * Get the path to a versioned file.
6 + *
7 + * @param string $file
8 + * @return string
9 + *
10 + * @throws \InvalidArgumentException
11 + */
12 + function versioned_asset($file)
13 + {
14 + static $manifest = null;
15 +
16 + if (is_null($manifest)) {
17 + $manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);
18 + }
19 +
20 + if (isset($manifest[$file])) {
21 + return '/' . $manifest[$file];
22 + }
23 +
24 + if (file_exists(public_path($file))) {
25 + return '/' . $file;
26 + }
27 +
28 + throw new InvalidArgumentException("File {$file} not defined in asset manifest.");
29 + }
30 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -8,15 +8,16 @@ ...@@ -8,15 +8,16 @@
8 "php": ">=5.5.9", 8 "php": ">=5.5.9",
9 "laravel/framework": "5.1.*", 9 "laravel/framework": "5.1.*",
10 "intervention/image": "^2.3", 10 "intervention/image": "^2.3",
11 - "laravel/socialite": "^2.0" 11 + "laravel/socialite": "^2.0",
12 + "barryvdh/laravel-ide-helper": "^2.1",
13 + "barryvdh/laravel-debugbar": "^2.0",
14 + "league/flysystem-aws-s3-v3": "^1.0"
12 }, 15 },
13 "require-dev": { 16 "require-dev": {
14 "fzaninotto/faker": "~1.4", 17 "fzaninotto/faker": "~1.4",
15 "mockery/mockery": "0.9.*", 18 "mockery/mockery": "0.9.*",
16 "phpunit/phpunit": "~4.0", 19 "phpunit/phpunit": "~4.0",
17 - "phpspec/phpspec": "~2.1", 20 + "phpspec/phpspec": "~2.1"
18 - "barryvdh/laravel-ide-helper": "^2.1",
19 - "barryvdh/laravel-debugbar": "^2.0"
20 }, 21 },
21 "autoload": { 22 "autoload": {
22 "classmap": [ 23 "classmap": [
...@@ -24,7 +25,10 @@ ...@@ -24,7 +25,10 @@
24 ], 25 ],
25 "psr-4": { 26 "psr-4": {
26 "BookStack\\": "app/" 27 "BookStack\\": "app/"
27 - } 28 + },
29 + "files": [
30 + "app/helpers.php"
31 + ]
28 }, 32 },
29 "autoload-dev": { 33 "autoload-dev": {
30 "classmap": [ 34 "classmap": [
......
...@@ -4,10 +4,88 @@ ...@@ -4,10 +4,88 @@
4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 "This file is @generated automatically" 5 "This file is @generated automatically"
6 ], 6 ],
7 - "hash": "c216d0bcb72b4f2008d7fa8067da2f77", 7 + "hash": "19725116631f01881caafa33052eecb9",
8 - "content-hash": "3c421ae5b8e5c11792249142cb96ff25", 8 + "content-hash": "f1dbd776f0ae13ec99e4e6d99510cd8e",
9 "packages": [ 9 "packages": [
10 { 10 {
11 + "name": "aws/aws-sdk-php",
12 + "version": "3.11.4",
13 + "source": {
14 + "type": "git",
15 + "url": "https://github.com/aws/aws-sdk-php.git",
16 + "reference": "2524c78e0fa1ed049719b8b6b0696f0b6dfb1ca2"
17 + },
18 + "dist": {
19 + "type": "zip",
20 + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/2524c78e0fa1ed049719b8b6b0696f0b6dfb1ca2",
21 + "reference": "2524c78e0fa1ed049719b8b6b0696f0b6dfb1ca2",
22 + "shasum": ""
23 + },
24 + "require": {
25 + "guzzlehttp/guzzle": "~5.3|~6.0.1|~6.1",
26 + "guzzlehttp/promises": "~1.0",
27 + "guzzlehttp/psr7": "~1.0",
28 + "mtdowling/jmespath.php": "~2.2",
29 + "php": ">=5.5"
30 + },
31 + "require-dev": {
32 + "andrewsville/php-token-reflection": "^1.4",
33 + "aws/aws-php-sns-message-validator": "~1.0",
34 + "behat/behat": "~3.0",
35 + "doctrine/cache": "~1.4",
36 + "ext-dom": "*",
37 + "ext-json": "*",
38 + "ext-openssl": "*",
39 + "ext-pcre": "*",
40 + "ext-simplexml": "*",
41 + "ext-spl": "*",
42 + "nette/neon": "^2.3",
43 + "phpunit/phpunit": "~4.0"
44 + },
45 + "suggest": {
46 + "doctrine/cache": "To use the DoctrineCacheAdapter",
47 + "ext-curl": "To send requests using cURL",
48 + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages"
49 + },
50 + "type": "library",
51 + "extra": {
52 + "branch-alias": {
53 + "dev-master": "3.0-dev"
54 + }
55 + },
56 + "autoload": {
57 + "psr-4": {
58 + "Aws\\": "src/"
59 + },
60 + "files": [
61 + "src/functions.php"
62 + ]
63 + },
64 + "notification-url": "https://packagist.org/downloads/",
65 + "license": [
66 + "Apache-2.0"
67 + ],
68 + "authors": [
69 + {
70 + "name": "Amazon Web Services",
71 + "homepage": "http://aws.amazon.com"
72 + }
73 + ],
74 + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
75 + "homepage": "http://aws.amazon.com/sdkforphp",
76 + "keywords": [
77 + "amazon",
78 + "aws",
79 + "cloud",
80 + "dynamodb",
81 + "ec2",
82 + "glacier",
83 + "s3",
84 + "sdk"
85 + ],
86 + "time": "2015-12-04 01:19:53"
87 + },
88 + {
11 "name": "barryvdh/laravel-debugbar", 89 "name": "barryvdh/laravel-debugbar",
12 "version": "v2.0.6", 90 "version": "v2.0.6",
13 "source": { 91 "source": {
...@@ -126,29 +204,29 @@ ...@@ -126,29 +204,29 @@
126 }, 204 },
127 { 205 {
128 "name": "classpreloader/classpreloader", 206 "name": "classpreloader/classpreloader",
129 - "version": "2.0.0", 207 + "version": "3.0.0",
130 "source": { 208 "source": {
131 "type": "git", 209 "type": "git",
132 "url": "https://github.com/ClassPreloader/ClassPreloader.git", 210 "url": "https://github.com/ClassPreloader/ClassPreloader.git",
133 - "reference": "8c3c14b10309e3b40bce833913a6c0c0b8c8f962" 211 + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a"
134 }, 212 },
135 "dist": { 213 "dist": {
136 "type": "zip", 214 "type": "zip",
137 - "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/8c3c14b10309e3b40bce833913a6c0c0b8c8f962", 215 + "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/9b10b913c2bdf90c3d2e0d726b454fb7f77c552a",
138 - "reference": "8c3c14b10309e3b40bce833913a6c0c0b8c8f962", 216 + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a",
139 "shasum": "" 217 "shasum": ""
140 }, 218 },
141 "require": { 219 "require": {
142 - "nikic/php-parser": "~1.3", 220 + "nikic/php-parser": "^1.0|^2.0",
143 "php": ">=5.5.9" 221 "php": ">=5.5.9"
144 }, 222 },
145 "require-dev": { 223 "require-dev": {
146 - "phpunit/phpunit": "~4.0" 224 + "phpunit/phpunit": "^4.8|^5.0"
147 }, 225 },
148 "type": "library", 226 "type": "library",
149 "extra": { 227 "extra": {
150 "branch-alias": { 228 "branch-alias": {
151 - "dev-master": "2.0-dev" 229 + "dev-master": "3.0-dev"
152 } 230 }
153 }, 231 },
154 "autoload": { 232 "autoload": {
...@@ -176,7 +254,7 @@ ...@@ -176,7 +254,7 @@
176 "class", 254 "class",
177 "preload" 255 "preload"
178 ], 256 ],
179 - "time": "2015-06-28 21:39:13" 257 + "time": "2015-11-09 22:51:51"
180 }, 258 },
181 { 259 {
182 "name": "danielstjules/stringy", 260 "name": "danielstjules/stringy",
...@@ -269,16 +347,16 @@ ...@@ -269,16 +347,16 @@
269 }, 347 },
270 { 348 {
271 "name": "doctrine/inflector", 349 "name": "doctrine/inflector",
272 - "version": "v1.0.1", 350 + "version": "v1.1.0",
273 "source": { 351 "source": {
274 "type": "git", 352 "type": "git",
275 "url": "https://github.com/doctrine/inflector.git", 353 "url": "https://github.com/doctrine/inflector.git",
276 - "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604" 354 + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae"
277 }, 355 },
278 "dist": { 356 "dist": {
279 "type": "zip", 357 "type": "zip",
280 - "url": "https://api.github.com/repos/doctrine/inflector/zipball/0bcb2e79d8571787f18b7eb036ed3d004908e604", 358 + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae",
281 - "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604", 359 + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae",
282 "shasum": "" 360 "shasum": ""
283 }, 361 },
284 "require": { 362 "require": {
...@@ -290,7 +368,7 @@ ...@@ -290,7 +368,7 @@
290 "type": "library", 368 "type": "library",
291 "extra": { 369 "extra": {
292 "branch-alias": { 370 "branch-alias": {
293 - "dev-master": "1.0.x-dev" 371 + "dev-master": "1.1.x-dev"
294 } 372 }
295 }, 373 },
296 "autoload": { 374 "autoload": {
...@@ -332,7 +410,7 @@ ...@@ -332,7 +410,7 @@
332 "singularize", 410 "singularize",
333 "string" 411 "string"
334 ], 412 ],
335 - "time": "2014-12-20 21:24:13" 413 + "time": "2015-11-06 14:35:42"
336 }, 414 },
337 { 415 {
338 "name": "guzzle/guzzle", 416 "name": "guzzle/guzzle",
...@@ -431,16 +509,16 @@ ...@@ -431,16 +509,16 @@
431 }, 509 },
432 { 510 {
433 "name": "guzzlehttp/guzzle", 511 "name": "guzzlehttp/guzzle",
434 - "version": "6.0.2", 512 + "version": "6.1.1",
435 "source": { 513 "source": {
436 "type": "git", 514 "type": "git",
437 "url": "https://github.com/guzzle/guzzle.git", 515 "url": "https://github.com/guzzle/guzzle.git",
438 - "reference": "a8dfeff00eb84616a17fea7a4d72af35e750410f" 516 + "reference": "c6851d6e48f63b69357cbfa55bca116448140e0c"
439 }, 517 },
440 "dist": { 518 "dist": {
441 "type": "zip", 519 "type": "zip",
442 - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a8dfeff00eb84616a17fea7a4d72af35e750410f", 520 + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c6851d6e48f63b69357cbfa55bca116448140e0c",
443 - "reference": "a8dfeff00eb84616a17fea7a4d72af35e750410f", 521 + "reference": "c6851d6e48f63b69357cbfa55bca116448140e0c",
444 "shasum": "" 522 "shasum": ""
445 }, 523 },
446 "require": { 524 "require": {
...@@ -456,7 +534,7 @@ ...@@ -456,7 +534,7 @@
456 "type": "library", 534 "type": "library",
457 "extra": { 535 "extra": {
458 "branch-alias": { 536 "branch-alias": {
459 - "dev-master": "6.0-dev" 537 + "dev-master": "6.1-dev"
460 } 538 }
461 }, 539 },
462 "autoload": { 540 "autoload": {
...@@ -489,20 +567,20 @@ ...@@ -489,20 +567,20 @@
489 "rest", 567 "rest",
490 "web service" 568 "web service"
491 ], 569 ],
492 - "time": "2015-07-04 20:09:24" 570 + "time": "2015-11-23 00:47:50"
493 }, 571 },
494 { 572 {
495 "name": "guzzlehttp/promises", 573 "name": "guzzlehttp/promises",
496 - "version": "1.0.2", 574 + "version": "1.0.3",
497 "source": { 575 "source": {
498 "type": "git", 576 "type": "git",
499 "url": "https://github.com/guzzle/promises.git", 577 "url": "https://github.com/guzzle/promises.git",
500 - "reference": "97fe7210def29451ec74923b27e552238defd75a" 578 + "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea"
501 }, 579 },
502 "dist": { 580 "dist": {
503 "type": "zip", 581 "type": "zip",
504 - "url": "https://api.github.com/repos/guzzle/promises/zipball/97fe7210def29451ec74923b27e552238defd75a", 582 + "url": "https://api.github.com/repos/guzzle/promises/zipball/b1e1c0d55f8083c71eda2c28c12a228d708294ea",
505 - "reference": "97fe7210def29451ec74923b27e552238defd75a", 583 + "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea",
506 "shasum": "" 584 "shasum": ""
507 }, 585 },
508 "require": { 586 "require": {
...@@ -540,20 +618,20 @@ ...@@ -540,20 +618,20 @@
540 "keywords": [ 618 "keywords": [
541 "promise" 619 "promise"
542 ], 620 ],
543 - "time": "2015-08-15 19:37:21" 621 + "time": "2015-10-15 22:28:00"
544 }, 622 },
545 { 623 {
546 "name": "guzzlehttp/psr7", 624 "name": "guzzlehttp/psr7",
547 - "version": "1.2.0", 625 + "version": "1.2.1",
548 "source": { 626 "source": {
549 "type": "git", 627 "type": "git",
550 "url": "https://github.com/guzzle/psr7.git", 628 "url": "https://github.com/guzzle/psr7.git",
551 - "reference": "4ef919b0cf3b1989523138b60163bbcb7ba1ff7e" 629 + "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982"
552 }, 630 },
553 "dist": { 631 "dist": {
554 "type": "zip", 632 "type": "zip",
555 - "url": "https://api.github.com/repos/guzzle/psr7/zipball/4ef919b0cf3b1989523138b60163bbcb7ba1ff7e", 633 + "url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982",
556 - "reference": "4ef919b0cf3b1989523138b60163bbcb7ba1ff7e", 634 + "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982",
557 "shasum": "" 635 "shasum": ""
558 }, 636 },
559 "require": { 637 "require": {
...@@ -598,20 +676,20 @@ ...@@ -598,20 +676,20 @@
598 "stream", 676 "stream",
599 "uri" 677 "uri"
600 ], 678 ],
601 - "time": "2015-08-15 19:32:36" 679 + "time": "2015-11-03 01:34:55"
602 }, 680 },
603 { 681 {
604 "name": "intervention/image", 682 "name": "intervention/image",
605 - "version": "2.3.1", 683 + "version": "2.3.4",
606 "source": { 684 "source": {
607 "type": "git", 685 "type": "git",
608 "url": "https://github.com/Intervention/image.git", 686 "url": "https://github.com/Intervention/image.git",
609 - "reference": "156f9d6f8a186c68b92f0c50084718f02dae1b5f" 687 + "reference": "a67ee32df0c6820cc6e861ad4144ee0ef9c74aa3"
610 }, 688 },
611 "dist": { 689 "dist": {
612 "type": "zip", 690 "type": "zip",
613 - "url": "https://api.github.com/repos/Intervention/image/zipball/156f9d6f8a186c68b92f0c50084718f02dae1b5f", 691 + "url": "https://api.github.com/repos/Intervention/image/zipball/a67ee32df0c6820cc6e861ad4144ee0ef9c74aa3",
614 - "reference": "156f9d6f8a186c68b92f0c50084718f02dae1b5f", 692 + "reference": "a67ee32df0c6820cc6e861ad4144ee0ef9c74aa3",
615 "shasum": "" 693 "shasum": ""
616 }, 694 },
617 "require": { 695 "require": {
...@@ -660,7 +738,7 @@ ...@@ -660,7 +738,7 @@
660 "thumbnail", 738 "thumbnail",
661 "watermark" 739 "watermark"
662 ], 740 ],
663 - "time": "2015-07-10 15:03:58" 741 + "time": "2015-11-30 17:03:21"
664 }, 742 },
665 { 743 {
666 "name": "jakub-onderka/php-console-color", 744 "name": "jakub-onderka/php-console-color",
...@@ -809,20 +887,20 @@ ...@@ -809,20 +887,20 @@
809 }, 887 },
810 { 888 {
811 "name": "laravel/framework", 889 "name": "laravel/framework",
812 - "version": "v5.1.10", 890 + "version": "v5.1.25",
813 "source": { 891 "source": {
814 "type": "git", 892 "type": "git",
815 "url": "https://github.com/laravel/framework.git", 893 "url": "https://github.com/laravel/framework.git",
816 - "reference": "d47ccc8de10ccb6f328cc90f901ca5e47e077c93" 894 + "reference": "53979acc664debc401bfcb61086c4fc4f196b22d"
817 }, 895 },
818 "dist": { 896 "dist": {
819 "type": "zip", 897 "type": "zip",
820 - "url": "https://api.github.com/repos/laravel/framework/zipball/d47ccc8de10ccb6f328cc90f901ca5e47e077c93", 898 + "url": "https://api.github.com/repos/laravel/framework/zipball/53979acc664debc401bfcb61086c4fc4f196b22d",
821 - "reference": "d47ccc8de10ccb6f328cc90f901ca5e47e077c93", 899 + "reference": "53979acc664debc401bfcb61086c4fc4f196b22d",
822 "shasum": "" 900 "shasum": ""
823 }, 901 },
824 "require": { 902 "require": {
825 - "classpreloader/classpreloader": "~2.0", 903 + "classpreloader/classpreloader": "~2.0|~3.0",
826 "danielstjules/stringy": "~1.8", 904 "danielstjules/stringy": "~1.8",
827 "doctrine/inflector": "~1.0", 905 "doctrine/inflector": "~1.0",
828 "ext-mbstring": "*", 906 "ext-mbstring": "*",
...@@ -832,8 +910,9 @@ ...@@ -832,8 +910,9 @@
832 "monolog/monolog": "~1.11", 910 "monolog/monolog": "~1.11",
833 "mtdowling/cron-expression": "~1.0", 911 "mtdowling/cron-expression": "~1.0",
834 "nesbot/carbon": "~1.19", 912 "nesbot/carbon": "~1.19",
913 + "paragonie/random_compat": "~1.1",
835 "php": ">=5.5.9", 914 "php": ">=5.5.9",
836 - "psy/psysh": "~0.5.1", 915 + "psy/psysh": "0.6.*",
837 "swiftmailer/swiftmailer": "~5.1", 916 "swiftmailer/swiftmailer": "~5.1",
838 "symfony/console": "2.7.*", 917 "symfony/console": "2.7.*",
839 "symfony/css-selector": "2.7.*", 918 "symfony/css-selector": "2.7.*",
...@@ -882,7 +961,7 @@ ...@@ -882,7 +961,7 @@
882 "require-dev": { 961 "require-dev": {
883 "aws/aws-sdk-php": "~3.0", 962 "aws/aws-sdk-php": "~3.0",
884 "iron-io/iron_mq": "~2.0", 963 "iron-io/iron_mq": "~2.0",
885 - "mockery/mockery": "~0.9.1", 964 + "mockery/mockery": "~0.9.2",
886 "pda/pheanstalk": "~3.0", 965 "pda/pheanstalk": "~3.0",
887 "phpunit/phpunit": "~4.0", 966 "phpunit/phpunit": "~4.0",
888 "predis/predis": "~1.0" 967 "predis/predis": "~1.0"
...@@ -933,20 +1012,20 @@ ...@@ -933,20 +1012,20 @@
933 "framework", 1012 "framework",
934 "laravel" 1013 "laravel"
935 ], 1014 ],
936 - "time": "2015-08-12 18:16:08" 1015 + "time": "2015-11-30 19:24:36"
937 }, 1016 },
938 { 1017 {
939 "name": "laravel/socialite", 1018 "name": "laravel/socialite",
940 - "version": "v2.0.12", 1019 + "version": "v2.0.14",
941 "source": { 1020 "source": {
942 "type": "git", 1021 "type": "git",
943 "url": "https://github.com/laravel/socialite.git", 1022 "url": "https://github.com/laravel/socialite.git",
944 - "reference": "0bb08c8666f4c01e55e3b3b0e42f2b5075be6a6e" 1023 + "reference": "b15f4be0ac739405120d74b837af423aa71502d9"
945 }, 1024 },
946 "dist": { 1025 "dist": {
947 "type": "zip", 1026 "type": "zip",
948 - "url": "https://api.github.com/repos/laravel/socialite/zipball/0bb08c8666f4c01e55e3b3b0e42f2b5075be6a6e", 1027 + "url": "https://api.github.com/repos/laravel/socialite/zipball/b15f4be0ac739405120d74b837af423aa71502d9",
949 - "reference": "0bb08c8666f4c01e55e3b3b0e42f2b5075be6a6e", 1028 + "reference": "b15f4be0ac739405120d74b837af423aa71502d9",
950 "shasum": "" 1029 "shasum": ""
951 }, 1030 },
952 "require": { 1031 "require": {
...@@ -987,25 +1066,28 @@ ...@@ -987,25 +1066,28 @@
987 "laravel", 1066 "laravel",
988 "oauth" 1067 "oauth"
989 ], 1068 ],
990 - "time": "2015-08-30 01:12:56" 1069 + "time": "2015-10-16 15:39:46"
991 }, 1070 },
992 { 1071 {
993 "name": "league/flysystem", 1072 "name": "league/flysystem",
994 - "version": "1.0.11", 1073 + "version": "1.0.15",
995 "source": { 1074 "source": {
996 "type": "git", 1075 "type": "git",
997 "url": "https://github.com/thephpleague/flysystem.git", 1076 "url": "https://github.com/thephpleague/flysystem.git",
998 - "reference": "c16222fdc02467eaa12cb6d6d0e65527741f6040" 1077 + "reference": "31525caf9e8772683672fefd8a1ca0c0736020f4"
999 }, 1078 },
1000 "dist": { 1079 "dist": {
1001 "type": "zip", 1080 "type": "zip",
1002 - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c16222fdc02467eaa12cb6d6d0e65527741f6040", 1081 + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/31525caf9e8772683672fefd8a1ca0c0736020f4",
1003 - "reference": "c16222fdc02467eaa12cb6d6d0e65527741f6040", 1082 + "reference": "31525caf9e8772683672fefd8a1ca0c0736020f4",
1004 "shasum": "" 1083 "shasum": ""
1005 }, 1084 },
1006 "require": { 1085 "require": {
1007 "php": ">=5.4.0" 1086 "php": ">=5.4.0"
1008 }, 1087 },
1088 + "conflict": {
1089 + "league/flysystem-sftp": "<1.0.6"
1090 + },
1009 "require-dev": { 1091 "require-dev": {
1010 "ext-fileinfo": "*", 1092 "ext-fileinfo": "*",
1011 "mockery/mockery": "~0.9", 1093 "mockery/mockery": "~0.9",
...@@ -1068,20 +1150,67 @@ ...@@ -1068,20 +1150,67 @@
1068 "sftp", 1150 "sftp",
1069 "storage" 1151 "storage"
1070 ], 1152 ],
1071 - "time": "2015-07-28 20:41:58" 1153 + "time": "2015-09-30 22:26:59"
1154 + },
1155 + {
1156 + "name": "league/flysystem-aws-s3-v3",
1157 + "version": "1.0.9",
1158 + "source": {
1159 + "type": "git",
1160 + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
1161 + "reference": "595e24678bf78f8107ebc9355d8376ae0eb712c6"
1162 + },
1163 + "dist": {
1164 + "type": "zip",
1165 + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/595e24678bf78f8107ebc9355d8376ae0eb712c6",
1166 + "reference": "595e24678bf78f8107ebc9355d8376ae0eb712c6",
1167 + "shasum": ""
1168 + },
1169 + "require": {
1170 + "aws/aws-sdk-php": "^3.0.0",
1171 + "league/flysystem": "~1.0",
1172 + "php": ">=5.5.0"
1173 + },
1174 + "require-dev": {
1175 + "henrikbjorn/phpspec-code-coverage": "~1.0.1",
1176 + "phpspec/phpspec": "^2.0.0"
1177 + },
1178 + "type": "library",
1179 + "extra": {
1180 + "branch-alias": {
1181 + "dev-master": "1.0-dev"
1182 + }
1183 + },
1184 + "autoload": {
1185 + "psr-4": {
1186 + "League\\Flysystem\\AwsS3v3\\": "src/"
1187 + }
1188 + },
1189 + "notification-url": "https://packagist.org/downloads/",
1190 + "license": [
1191 + "MIT"
1192 + ],
1193 + "authors": [
1194 + {
1195 + "name": "Frank de Jonge",
1196 + "email": "info@frenky.net"
1197 + }
1198 + ],
1199 + "description": "Flysystem adapter for the AWS S3 SDK v3.x",
1200 + "time": "2015-11-19 08:44:16"
1072 }, 1201 },
1073 { 1202 {
1074 "name": "league/oauth1-client", 1203 "name": "league/oauth1-client",
1075 - "version": "1.6.0", 1204 + "version": "1.6.1",
1076 "source": { 1205 "source": {
1077 "type": "git", 1206 "type": "git",
1078 "url": "https://github.com/thephpleague/oauth1-client.git", 1207 "url": "https://github.com/thephpleague/oauth1-client.git",
1079 - "reference": "4d4edd9b6014f882e319231a9b3351e3a1dfdc81" 1208 + "reference": "cef3ceda13c78f89c323e4d5e6301c0eb7cea422"
1080 }, 1209 },
1081 "dist": { 1210 "dist": {
1082 "type": "zip", 1211 "type": "zip",
1083 - "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/4d4edd9b6014f882e319231a9b3351e3a1dfdc81", 1212 + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/cef3ceda13c78f89c323e4d5e6301c0eb7cea422",
1084 - "reference": "4d4edd9b6014f882e319231a9b3351e3a1dfdc81", 1213 + "reference": "cef3ceda13c78f89c323e4d5e6301c0eb7cea422",
1085 "shasum": "" 1214 "shasum": ""
1086 }, 1215 },
1087 "require": { 1216 "require": {
...@@ -1131,7 +1260,7 @@ ...@@ -1131,7 +1260,7 @@
1131 "tumblr", 1260 "tumblr",
1132 "twitter" 1261 "twitter"
1133 ], 1262 ],
1134 - "time": "2015-08-22 09:49:14" 1263 + "time": "2015-10-23 04:02:07"
1135 }, 1264 },
1136 { 1265 {
1137 "name": "maximebf/debugbar", 1266 "name": "maximebf/debugbar",
...@@ -1191,16 +1320,16 @@ ...@@ -1191,16 +1320,16 @@
1191 }, 1320 },
1192 { 1321 {
1193 "name": "monolog/monolog", 1322 "name": "monolog/monolog",
1194 - "version": "1.16.0", 1323 + "version": "1.17.2",
1195 "source": { 1324 "source": {
1196 "type": "git", 1325 "type": "git",
1197 "url": "https://github.com/Seldaek/monolog.git", 1326 "url": "https://github.com/Seldaek/monolog.git",
1198 - "reference": "c0c0b4bee3aabce7182876b0d912ef2595563db7" 1327 + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24"
1199 }, 1328 },
1200 "dist": { 1329 "dist": {
1201 "type": "zip", 1330 "type": "zip",
1202 - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c0c0b4bee3aabce7182876b0d912ef2595563db7", 1331 + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24",
1203 - "reference": "c0c0b4bee3aabce7182876b0d912ef2595563db7", 1332 + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24",
1204 "shasum": "" 1333 "shasum": ""
1205 }, 1334 },
1206 "require": { 1335 "require": {
...@@ -1214,10 +1343,11 @@ ...@@ -1214,10 +1343,11 @@
1214 "aws/aws-sdk-php": "^2.4.9", 1343 "aws/aws-sdk-php": "^2.4.9",
1215 "doctrine/couchdb": "~1.0@dev", 1344 "doctrine/couchdb": "~1.0@dev",
1216 "graylog2/gelf-php": "~1.0", 1345 "graylog2/gelf-php": "~1.0",
1346 + "jakub-onderka/php-parallel-lint": "0.9",
1217 "php-console/php-console": "^3.1.3", 1347 "php-console/php-console": "^3.1.3",
1218 "phpunit/phpunit": "~4.5", 1348 "phpunit/phpunit": "~4.5",
1219 "phpunit/phpunit-mock-objects": "2.3.0", 1349 "phpunit/phpunit-mock-objects": "2.3.0",
1220 - "raven/raven": "~0.8", 1350 + "raven/raven": "^0.13",
1221 "ruflin/elastica": ">=0.90 <3.0", 1351 "ruflin/elastica": ">=0.90 <3.0",
1222 "swiftmailer/swiftmailer": "~5.3", 1352 "swiftmailer/swiftmailer": "~5.3",
1223 "videlalvaro/php-amqplib": "~2.4" 1353 "videlalvaro/php-amqplib": "~2.4"
...@@ -1263,7 +1393,7 @@ ...@@ -1263,7 +1393,7 @@
1263 "logging", 1393 "logging",
1264 "psr-3" 1394 "psr-3"
1265 ], 1395 ],
1266 - "time": "2015-08-09 17:44:44" 1396 + "time": "2015-10-14 12:51:02"
1267 }, 1397 },
1268 { 1398 {
1269 "name": "mtdowling/cron-expression", 1399 "name": "mtdowling/cron-expression",
...@@ -1310,17 +1440,72 @@ ...@@ -1310,17 +1440,72 @@
1310 "time": "2015-01-11 23:07:46" 1440 "time": "2015-01-11 23:07:46"
1311 }, 1441 },
1312 { 1442 {
1443 + "name": "mtdowling/jmespath.php",
1444 + "version": "2.2.0",
1445 + "source": {
1446 + "type": "git",
1447 + "url": "https://github.com/jmespath/jmespath.php.git",
1448 + "reference": "a7d99d0c836e69d27b7bfca1d33ca2759fba3289"
1449 + },
1450 + "dist": {
1451 + "type": "zip",
1452 + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a7d99d0c836e69d27b7bfca1d33ca2759fba3289",
1453 + "reference": "a7d99d0c836e69d27b7bfca1d33ca2759fba3289",
1454 + "shasum": ""
1455 + },
1456 + "require": {
1457 + "php": ">=5.4.0"
1458 + },
1459 + "require-dev": {
1460 + "phpunit/phpunit": "~4.0"
1461 + },
1462 + "bin": [
1463 + "bin/jp.php"
1464 + ],
1465 + "type": "library",
1466 + "extra": {
1467 + "branch-alias": {
1468 + "dev-master": "2.0-dev"
1469 + }
1470 + },
1471 + "autoload": {
1472 + "psr-4": {
1473 + "JmesPath\\": "src/"
1474 + },
1475 + "files": [
1476 + "src/JmesPath.php"
1477 + ]
1478 + },
1479 + "notification-url": "https://packagist.org/downloads/",
1480 + "license": [
1481 + "MIT"
1482 + ],
1483 + "authors": [
1484 + {
1485 + "name": "Michael Dowling",
1486 + "email": "mtdowling@gmail.com",
1487 + "homepage": "https://github.com/mtdowling"
1488 + }
1489 + ],
1490 + "description": "Declaratively specify how to extract elements from a JSON document",
1491 + "keywords": [
1492 + "json",
1493 + "jsonpath"
1494 + ],
1495 + "time": "2015-05-27 17:21:31"
1496 + },
1497 + {
1313 "name": "nesbot/carbon", 1498 "name": "nesbot/carbon",
1314 - "version": "1.20.0", 1499 + "version": "1.21.0",
1315 "source": { 1500 "source": {
1316 "type": "git", 1501 "type": "git",
1317 "url": "https://github.com/briannesbitt/Carbon.git", 1502 "url": "https://github.com/briannesbitt/Carbon.git",
1318 - "reference": "bfd3eaba109c9a2405c92174c8e17f20c2b9caf3" 1503 + "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7"
1319 }, 1504 },
1320 "dist": { 1505 "dist": {
1321 "type": "zip", 1506 "type": "zip",
1322 - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bfd3eaba109c9a2405c92174c8e17f20c2b9caf3", 1507 + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7b08ec6f75791e130012f206e3f7b0e76e18e3d7",
1323 - "reference": "bfd3eaba109c9a2405c92174c8e17f20c2b9caf3", 1508 + "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7",
1324 "shasum": "" 1509 "shasum": ""
1325 }, 1510 },
1326 "require": { 1511 "require": {
...@@ -1328,12 +1513,12 @@ ...@@ -1328,12 +1513,12 @@
1328 "symfony/translation": "~2.6|~3.0" 1513 "symfony/translation": "~2.6|~3.0"
1329 }, 1514 },
1330 "require-dev": { 1515 "require-dev": {
1331 - "phpunit/phpunit": "~4.0" 1516 + "phpunit/phpunit": "~4.0|~5.0"
1332 }, 1517 },
1333 "type": "library", 1518 "type": "library",
1334 "autoload": { 1519 "autoload": {
1335 - "psr-0": { 1520 + "psr-4": {
1336 - "Carbon": "src" 1521 + "Carbon\\": "src/Carbon/"
1337 } 1522 }
1338 }, 1523 },
1339 "notification-url": "https://packagist.org/downloads/", 1524 "notification-url": "https://packagist.org/downloads/",
...@@ -1354,20 +1539,20 @@ ...@@ -1354,20 +1539,20 @@
1354 "datetime", 1539 "datetime",
1355 "time" 1540 "time"
1356 ], 1541 ],
1357 - "time": "2015-06-25 04:19:39" 1542 + "time": "2015-11-04 20:07:17"
1358 }, 1543 },
1359 { 1544 {
1360 "name": "nikic/php-parser", 1545 "name": "nikic/php-parser",
1361 - "version": "v1.4.0", 1546 + "version": "v1.4.1",
1362 "source": { 1547 "source": {
1363 "type": "git", 1548 "type": "git",
1364 "url": "https://github.com/nikic/PHP-Parser.git", 1549 "url": "https://github.com/nikic/PHP-Parser.git",
1365 - "reference": "196f177cfefa0f1f7166c0a05d8255889be12418" 1550 + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51"
1366 }, 1551 },
1367 "dist": { 1552 "dist": {
1368 "type": "zip", 1553 "type": "zip",
1369 - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/196f177cfefa0f1f7166c0a05d8255889be12418", 1554 + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51",
1370 - "reference": "196f177cfefa0f1f7166c0a05d8255889be12418", 1555 + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51",
1371 "shasum": "" 1556 "shasum": ""
1372 }, 1557 },
1373 "require": { 1558 "require": {
...@@ -1399,7 +1584,55 @@ ...@@ -1399,7 +1584,55 @@
1399 "parser", 1584 "parser",
1400 "php" 1585 "php"
1401 ], 1586 ],
1402 - "time": "2015-07-14 17:31:05" 1587 + "time": "2015-09-19 14:15:08"
1588 + },
1589 + {
1590 + "name": "paragonie/random_compat",
1591 + "version": "1.1.1",
1592 + "source": {
1593 + "type": "git",
1594 + "url": "https://github.com/paragonie/random_compat.git",
1595 + "reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32"
1596 + },
1597 + "dist": {
1598 + "type": "zip",
1599 + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a208865a5aeffc2dbbef2a5b3409887272d93f32",
1600 + "reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32",
1601 + "shasum": ""
1602 + },
1603 + "require": {
1604 + "php": ">=5.2.0"
1605 + },
1606 + "require-dev": {
1607 + "phpunit/phpunit": "4.*|5.*"
1608 + },
1609 + "suggest": {
1610 + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
1611 + },
1612 + "type": "library",
1613 + "autoload": {
1614 + "files": [
1615 + "lib/random.php"
1616 + ]
1617 + },
1618 + "notification-url": "https://packagist.org/downloads/",
1619 + "license": [
1620 + "MIT"
1621 + ],
1622 + "authors": [
1623 + {
1624 + "name": "Paragon Initiative Enterprises",
1625 + "email": "security@paragonie.com",
1626 + "homepage": "https://paragonie.com"
1627 + }
1628 + ],
1629 + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
1630 + "keywords": [
1631 + "csprng",
1632 + "pseudorandom",
1633 + "random"
1634 + ],
1635 + "time": "2015-12-01 02:52:15"
1403 }, 1636 },
1404 { 1637 {
1405 "name": "phpdocumentor/reflection-docblock", 1638 "name": "phpdocumentor/reflection-docblock",
...@@ -1539,29 +1772,29 @@ ...@@ -1539,29 +1772,29 @@
1539 }, 1772 },
1540 { 1773 {
1541 "name": "psy/psysh", 1774 "name": "psy/psysh",
1542 - "version": "v0.5.2", 1775 + "version": "v0.6.1",
1543 "source": { 1776 "source": {
1544 "type": "git", 1777 "type": "git",
1545 "url": "https://github.com/bobthecow/psysh.git", 1778 "url": "https://github.com/bobthecow/psysh.git",
1546 - "reference": "aaf8772ade08b5f0f6830774a5d5c2f800415975" 1779 + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed"
1547 }, 1780 },
1548 "dist": { 1781 "dist": {
1549 "type": "zip", 1782 "type": "zip",
1550 - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/aaf8772ade08b5f0f6830774a5d5c2f800415975", 1783 + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/0f04df0b23663799a8941fae13cd8e6299bde3ed",
1551 - "reference": "aaf8772ade08b5f0f6830774a5d5c2f800415975", 1784 + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed",
1552 "shasum": "" 1785 "shasum": ""
1553 }, 1786 },
1554 "require": { 1787 "require": {
1555 "dnoegel/php-xdg-base-dir": "0.1", 1788 "dnoegel/php-xdg-base-dir": "0.1",
1556 "jakub-onderka/php-console-highlighter": "0.3.*", 1789 "jakub-onderka/php-console-highlighter": "0.3.*",
1557 - "nikic/php-parser": "^1.2.1", 1790 + "nikic/php-parser": "^1.2.1|~2.0",
1558 "php": ">=5.3.9", 1791 "php": ">=5.3.9",
1559 "symfony/console": "~2.3.10|^2.4.2|~3.0", 1792 "symfony/console": "~2.3.10|^2.4.2|~3.0",
1560 "symfony/var-dumper": "~2.7|~3.0" 1793 "symfony/var-dumper": "~2.7|~3.0"
1561 }, 1794 },
1562 "require-dev": { 1795 "require-dev": {
1563 "fabpot/php-cs-fixer": "~1.5", 1796 "fabpot/php-cs-fixer": "~1.5",
1564 - "phpunit/phpunit": "~3.7|~4.0", 1797 + "phpunit/phpunit": "~3.7|~4.0|~5.0",
1565 "squizlabs/php_codesniffer": "~2.0", 1798 "squizlabs/php_codesniffer": "~2.0",
1566 "symfony/finder": "~2.1|~3.0" 1799 "symfony/finder": "~2.1|~3.0"
1567 }, 1800 },
...@@ -1577,15 +1810,15 @@ ...@@ -1577,15 +1810,15 @@
1577 "type": "library", 1810 "type": "library",
1578 "extra": { 1811 "extra": {
1579 "branch-alias": { 1812 "branch-alias": {
1580 - "dev-develop": "0.6.x-dev" 1813 + "dev-develop": "0.7.x-dev"
1581 } 1814 }
1582 }, 1815 },
1583 "autoload": { 1816 "autoload": {
1584 "files": [ 1817 "files": [
1585 "src/Psy/functions.php" 1818 "src/Psy/functions.php"
1586 ], 1819 ],
1587 - "psr-0": { 1820 + "psr-4": {
1588 - "Psy\\": "src/" 1821 + "Psy\\": "src/Psy/"
1589 } 1822 }
1590 }, 1823 },
1591 "notification-url": "https://packagist.org/downloads/", 1824 "notification-url": "https://packagist.org/downloads/",
...@@ -1607,7 +1840,7 @@ ...@@ -1607,7 +1840,7 @@
1607 "interactive", 1840 "interactive",
1608 "shell" 1841 "shell"
1609 ], 1842 ],
1610 - "time": "2015-07-16 15:26:57" 1843 + "time": "2015-11-12 16:18:56"
1611 }, 1844 },
1612 { 1845 {
1613 "name": "swiftmailer/swiftmailer", 1846 "name": "swiftmailer/swiftmailer",
...@@ -1664,35 +1897,37 @@ ...@@ -1664,35 +1897,37 @@
1664 }, 1897 },
1665 { 1898 {
1666 "name": "symfony/class-loader", 1899 "name": "symfony/class-loader",
1667 - "version": "v2.7.3", 1900 + "version": "v2.8.0",
1668 "source": { 1901 "source": {
1669 "type": "git", 1902 "type": "git",
1670 "url": "https://github.com/symfony/class-loader.git", 1903 "url": "https://github.com/symfony/class-loader.git",
1671 - "reference": "2fccbc544997340808801a7410cdcb96dd12edc4" 1904 + "reference": "51f83451bf0ddfc696e47e4642d6cd10fcfce160"
1672 }, 1905 },
1673 "dist": { 1906 "dist": {
1674 "type": "zip", 1907 "type": "zip",
1675 - "url": "https://api.github.com/repos/symfony/class-loader/zipball/2fccbc544997340808801a7410cdcb96dd12edc4", 1908 + "url": "https://api.github.com/repos/symfony/class-loader/zipball/51f83451bf0ddfc696e47e4642d6cd10fcfce160",
1676 - "reference": "2fccbc544997340808801a7410cdcb96dd12edc4", 1909 + "reference": "51f83451bf0ddfc696e47e4642d6cd10fcfce160",
1677 "shasum": "" 1910 "shasum": ""
1678 }, 1911 },
1679 "require": { 1912 "require": {
1680 "php": ">=5.3.9" 1913 "php": ">=5.3.9"
1681 }, 1914 },
1682 "require-dev": { 1915 "require-dev": {
1683 - "symfony/finder": "~2.0,>=2.0.5", 1916 + "symfony/finder": "~2.0,>=2.0.5|~3.0.0"
1684 - "symfony/phpunit-bridge": "~2.7"
1685 }, 1917 },
1686 "type": "library", 1918 "type": "library",
1687 "extra": { 1919 "extra": {
1688 "branch-alias": { 1920 "branch-alias": {
1689 - "dev-master": "2.7-dev" 1921 + "dev-master": "2.8-dev"
1690 } 1922 }
1691 }, 1923 },
1692 "autoload": { 1924 "autoload": {
1693 "psr-4": { 1925 "psr-4": {
1694 "Symfony\\Component\\ClassLoader\\": "" 1926 "Symfony\\Component\\ClassLoader\\": ""
1695 - } 1927 + },
1928 + "exclude-from-classmap": [
1929 + "/Tests/"
1930 + ]
1696 }, 1931 },
1697 "notification-url": "https://packagist.org/downloads/", 1932 "notification-url": "https://packagist.org/downloads/",
1698 "license": [ 1933 "license": [
...@@ -1710,20 +1945,20 @@ ...@@ -1710,20 +1945,20 @@
1710 ], 1945 ],
1711 "description": "Symfony ClassLoader Component", 1946 "description": "Symfony ClassLoader Component",
1712 "homepage": "https://symfony.com", 1947 "homepage": "https://symfony.com",
1713 - "time": "2015-06-25 12:52:11" 1948 + "time": "2015-11-26 07:00:59"
1714 }, 1949 },
1715 { 1950 {
1716 "name": "symfony/console", 1951 "name": "symfony/console",
1717 - "version": "v2.7.3", 1952 + "version": "v2.7.7",
1718 "source": { 1953 "source": {
1719 "type": "git", 1954 "type": "git",
1720 "url": "https://github.com/symfony/console.git", 1955 "url": "https://github.com/symfony/console.git",
1721 - "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e" 1956 + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750"
1722 }, 1957 },
1723 "dist": { 1958 "dist": {
1724 "type": "zip", 1959 "type": "zip",
1725 - "url": "https://api.github.com/repos/symfony/console/zipball/d6cf02fe73634c96677e428f840704bfbcaec29e", 1960 + "url": "https://api.github.com/repos/symfony/console/zipball/16bb1cb86df43c90931df65f529e7ebd79636750",
1726 - "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e", 1961 + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750",
1727 "shasum": "" 1962 "shasum": ""
1728 }, 1963 },
1729 "require": { 1964 "require": {
...@@ -1732,7 +1967,6 @@ ...@@ -1732,7 +1967,6 @@
1732 "require-dev": { 1967 "require-dev": {
1733 "psr/log": "~1.0", 1968 "psr/log": "~1.0",
1734 "symfony/event-dispatcher": "~2.1", 1969 "symfony/event-dispatcher": "~2.1",
1735 - "symfony/phpunit-bridge": "~2.7",
1736 "symfony/process": "~2.1" 1970 "symfony/process": "~2.1"
1737 }, 1971 },
1738 "suggest": { 1972 "suggest": {
...@@ -1749,7 +1983,10 @@ ...@@ -1749,7 +1983,10 @@
1749 "autoload": { 1983 "autoload": {
1750 "psr-4": { 1984 "psr-4": {
1751 "Symfony\\Component\\Console\\": "" 1985 "Symfony\\Component\\Console\\": ""
1752 - } 1986 + },
1987 + "exclude-from-classmap": [
1988 + "/Tests/"
1989 + ]
1753 }, 1990 },
1754 "notification-url": "https://packagist.org/downloads/", 1991 "notification-url": "https://packagist.org/downloads/",
1755 "license": [ 1992 "license": [
...@@ -1767,28 +2004,25 @@ ...@@ -1767,28 +2004,25 @@
1767 ], 2004 ],
1768 "description": "Symfony Console Component", 2005 "description": "Symfony Console Component",
1769 "homepage": "https://symfony.com", 2006 "homepage": "https://symfony.com",
1770 - "time": "2015-07-28 15:18:12" 2007 + "time": "2015-11-18 09:54:26"
1771 }, 2008 },
1772 { 2009 {
1773 "name": "symfony/css-selector", 2010 "name": "symfony/css-selector",
1774 - "version": "v2.7.3", 2011 + "version": "v2.7.7",
1775 "source": { 2012 "source": {
1776 "type": "git", 2013 "type": "git",
1777 "url": "https://github.com/symfony/css-selector.git", 2014 "url": "https://github.com/symfony/css-selector.git",
1778 - "reference": "0b5c07b516226b7dd32afbbc82fe547a469c5092" 2015 + "reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f"
1779 }, 2016 },
1780 "dist": { 2017 "dist": {
1781 "type": "zip", 2018 "type": "zip",
1782 - "url": "https://api.github.com/repos/symfony/css-selector/zipball/0b5c07b516226b7dd32afbbc82fe547a469c5092", 2019 + "url": "https://api.github.com/repos/symfony/css-selector/zipball/abb47717fb88aebd9437da2fc8bb01a50a36679f",
1783 - "reference": "0b5c07b516226b7dd32afbbc82fe547a469c5092", 2020 + "reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f",
1784 "shasum": "" 2021 "shasum": ""
1785 }, 2022 },
1786 "require": { 2023 "require": {
1787 "php": ">=5.3.9" 2024 "php": ">=5.3.9"
1788 }, 2025 },
1789 - "require-dev": {
1790 - "symfony/phpunit-bridge": "~2.7"
1791 - },
1792 "type": "library", 2026 "type": "library",
1793 "extra": { 2027 "extra": {
1794 "branch-alias": { 2028 "branch-alias": {
...@@ -1798,7 +2032,10 @@ ...@@ -1798,7 +2032,10 @@
1798 "autoload": { 2032 "autoload": {
1799 "psr-4": { 2033 "psr-4": {
1800 "Symfony\\Component\\CssSelector\\": "" 2034 "Symfony\\Component\\CssSelector\\": ""
1801 - } 2035 + },
2036 + "exclude-from-classmap": [
2037 + "/Tests/"
2038 + ]
1802 }, 2039 },
1803 "notification-url": "https://packagist.org/downloads/", 2040 "notification-url": "https://packagist.org/downloads/",
1804 "license": [ 2041 "license": [
...@@ -1820,20 +2057,20 @@ ...@@ -1820,20 +2057,20 @@
1820 ], 2057 ],
1821 "description": "Symfony CssSelector Component", 2058 "description": "Symfony CssSelector Component",
1822 "homepage": "https://symfony.com", 2059 "homepage": "https://symfony.com",
1823 - "time": "2015-05-15 13:33:16" 2060 + "time": "2015-10-30 20:10:21"
1824 }, 2061 },
1825 { 2062 {
1826 "name": "symfony/debug", 2063 "name": "symfony/debug",
1827 - "version": "v2.7.3", 2064 + "version": "v2.7.7",
1828 "source": { 2065 "source": {
1829 "type": "git", 2066 "type": "git",
1830 "url": "https://github.com/symfony/debug.git", 2067 "url": "https://github.com/symfony/debug.git",
1831 - "reference": "9daa1bf9f7e615fa2fba30357e479a90141222e3" 2068 + "reference": "0dbc119596f4afc82d9b2eb2a7e6a4af1ee763fa"
1832 }, 2069 },
1833 "dist": { 2070 "dist": {
1834 "type": "zip", 2071 "type": "zip",
1835 - "url": "https://api.github.com/repos/symfony/debug/zipball/9daa1bf9f7e615fa2fba30357e479a90141222e3", 2072 + "url": "https://api.github.com/repos/symfony/debug/zipball/0dbc119596f4afc82d9b2eb2a7e6a4af1ee763fa",
1836 - "reference": "9daa1bf9f7e615fa2fba30357e479a90141222e3", 2073 + "reference": "0dbc119596f4afc82d9b2eb2a7e6a4af1ee763fa",
1837 "shasum": "" 2074 "shasum": ""
1838 }, 2075 },
1839 "require": { 2076 "require": {
...@@ -1845,13 +2082,7 @@ ...@@ -1845,13 +2082,7 @@
1845 }, 2082 },
1846 "require-dev": { 2083 "require-dev": {
1847 "symfony/class-loader": "~2.2", 2084 "symfony/class-loader": "~2.2",
1848 - "symfony/http-foundation": "~2.1", 2085 + "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2"
1849 - "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2",
1850 - "symfony/phpunit-bridge": "~2.7"
1851 - },
1852 - "suggest": {
1853 - "symfony/http-foundation": "",
1854 - "symfony/http-kernel": ""
1855 }, 2086 },
1856 "type": "library", 2087 "type": "library",
1857 "extra": { 2088 "extra": {
...@@ -1862,7 +2093,10 @@ ...@@ -1862,7 +2093,10 @@
1862 "autoload": { 2093 "autoload": {
1863 "psr-4": { 2094 "psr-4": {
1864 "Symfony\\Component\\Debug\\": "" 2095 "Symfony\\Component\\Debug\\": ""
1865 - } 2096 + },
2097 + "exclude-from-classmap": [
2098 + "/Tests/"
2099 + ]
1866 }, 2100 },
1867 "notification-url": "https://packagist.org/downloads/", 2101 "notification-url": "https://packagist.org/downloads/",
1868 "license": [ 2102 "license": [
...@@ -1880,28 +2114,27 @@ ...@@ -1880,28 +2114,27 @@
1880 ], 2114 ],
1881 "description": "Symfony Debug Component", 2115 "description": "Symfony Debug Component",
1882 "homepage": "https://symfony.com", 2116 "homepage": "https://symfony.com",
1883 - "time": "2015-07-09 16:07:40" 2117 + "time": "2015-10-30 20:10:21"
1884 }, 2118 },
1885 { 2119 {
1886 "name": "symfony/dom-crawler", 2120 "name": "symfony/dom-crawler",
1887 - "version": "v2.7.3", 2121 + "version": "v2.7.7",
1888 "source": { 2122 "source": {
1889 "type": "git", 2123 "type": "git",
1890 "url": "https://github.com/symfony/dom-crawler.git", 2124 "url": "https://github.com/symfony/dom-crawler.git",
1891 - "reference": "9dabece63182e95c42b06967a0d929a5df78bc35" 2125 + "reference": "b33593cbfe1d81b50d48353f338aca76a08658d8"
1892 }, 2126 },
1893 "dist": { 2127 "dist": {
1894 "type": "zip", 2128 "type": "zip",
1895 - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/9dabece63182e95c42b06967a0d929a5df78bc35", 2129 + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b33593cbfe1d81b50d48353f338aca76a08658d8",
1896 - "reference": "9dabece63182e95c42b06967a0d929a5df78bc35", 2130 + "reference": "b33593cbfe1d81b50d48353f338aca76a08658d8",
1897 "shasum": "" 2131 "shasum": ""
1898 }, 2132 },
1899 "require": { 2133 "require": {
1900 "php": ">=5.3.9" 2134 "php": ">=5.3.9"
1901 }, 2135 },
1902 "require-dev": { 2136 "require-dev": {
1903 - "symfony/css-selector": "~2.3", 2137 + "symfony/css-selector": "~2.3"
1904 - "symfony/phpunit-bridge": "~2.7"
1905 }, 2138 },
1906 "suggest": { 2139 "suggest": {
1907 "symfony/css-selector": "" 2140 "symfony/css-selector": ""
...@@ -1915,7 +2148,10 @@ ...@@ -1915,7 +2148,10 @@
1915 "autoload": { 2148 "autoload": {
1916 "psr-4": { 2149 "psr-4": {
1917 "Symfony\\Component\\DomCrawler\\": "" 2150 "Symfony\\Component\\DomCrawler\\": ""
1918 - } 2151 + },
2152 + "exclude-from-classmap": [
2153 + "/Tests/"
2154 + ]
1919 }, 2155 },
1920 "notification-url": "https://packagist.org/downloads/", 2156 "notification-url": "https://packagist.org/downloads/",
1921 "license": [ 2157 "license": [
...@@ -1933,20 +2169,20 @@ ...@@ -1933,20 +2169,20 @@
1933 ], 2169 ],
1934 "description": "Symfony DomCrawler Component", 2170 "description": "Symfony DomCrawler Component",
1935 "homepage": "https://symfony.com", 2171 "homepage": "https://symfony.com",
1936 - "time": "2015-07-09 16:07:40" 2172 + "time": "2015-11-02 20:20:53"
1937 }, 2173 },
1938 { 2174 {
1939 "name": "symfony/event-dispatcher", 2175 "name": "symfony/event-dispatcher",
1940 - "version": "v2.7.3", 2176 + "version": "v2.8.0",
1941 "source": { 2177 "source": {
1942 "type": "git", 2178 "type": "git",
1943 "url": "https://github.com/symfony/event-dispatcher.git", 2179 "url": "https://github.com/symfony/event-dispatcher.git",
1944 - "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3" 2180 + "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc"
1945 }, 2181 },
1946 "dist": { 2182 "dist": {
1947 "type": "zip", 2183 "type": "zip",
1948 - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", 2184 + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc",
1949 - "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", 2185 + "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc",
1950 "shasum": "" 2186 "shasum": ""
1951 }, 2187 },
1952 "require": { 2188 "require": {
...@@ -1954,11 +2190,10 @@ ...@@ -1954,11 +2190,10 @@
1954 }, 2190 },
1955 "require-dev": { 2191 "require-dev": {
1956 "psr/log": "~1.0", 2192 "psr/log": "~1.0",
1957 - "symfony/config": "~2.0,>=2.0.5", 2193 + "symfony/config": "~2.0,>=2.0.5|~3.0.0",
1958 - "symfony/dependency-injection": "~2.6", 2194 + "symfony/dependency-injection": "~2.6|~3.0.0",
1959 - "symfony/expression-language": "~2.6", 2195 + "symfony/expression-language": "~2.6|~3.0.0",
1960 - "symfony/phpunit-bridge": "~2.7", 2196 + "symfony/stopwatch": "~2.3|~3.0.0"
1961 - "symfony/stopwatch": "~2.3"
1962 }, 2197 },
1963 "suggest": { 2198 "suggest": {
1964 "symfony/dependency-injection": "", 2199 "symfony/dependency-injection": "",
...@@ -1967,13 +2202,16 @@ ...@@ -1967,13 +2202,16 @@
1967 "type": "library", 2202 "type": "library",
1968 "extra": { 2203 "extra": {
1969 "branch-alias": { 2204 "branch-alias": {
1970 - "dev-master": "2.7-dev" 2205 + "dev-master": "2.8-dev"
1971 } 2206 }
1972 }, 2207 },
1973 "autoload": { 2208 "autoload": {
1974 "psr-4": { 2209 "psr-4": {
1975 "Symfony\\Component\\EventDispatcher\\": "" 2210 "Symfony\\Component\\EventDispatcher\\": ""
1976 - } 2211 + },
2212 + "exclude-from-classmap": [
2213 + "/Tests/"
2214 + ]
1977 }, 2215 },
1978 "notification-url": "https://packagist.org/downloads/", 2216 "notification-url": "https://packagist.org/downloads/",
1979 "license": [ 2217 "license": [
...@@ -1991,28 +2229,25 @@ ...@@ -1991,28 +2229,25 @@
1991 ], 2229 ],
1992 "description": "Symfony EventDispatcher Component", 2230 "description": "Symfony EventDispatcher Component",
1993 "homepage": "https://symfony.com", 2231 "homepage": "https://symfony.com",
1994 - "time": "2015-06-18 19:21:56" 2232 + "time": "2015-10-30 20:15:42"
1995 }, 2233 },
1996 { 2234 {
1997 "name": "symfony/finder", 2235 "name": "symfony/finder",
1998 - "version": "v2.7.3", 2236 + "version": "v2.7.7",
1999 "source": { 2237 "source": {
2000 "type": "git", 2238 "type": "git",
2001 - "url": "https://github.com/symfony/Finder.git", 2239 + "url": "https://github.com/symfony/finder.git",
2002 - "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4" 2240 + "reference": "a06a0c0ff7db3736a50d530c908cca547bf13da9"
2003 }, 2241 },
2004 "dist": { 2242 "dist": {
2005 "type": "zip", 2243 "type": "zip",
2006 - "url": "https://api.github.com/repos/symfony/Finder/zipball/ae0f363277485094edc04c9f3cbe595b183b78e4", 2244 + "url": "https://api.github.com/repos/symfony/finder/zipball/a06a0c0ff7db3736a50d530c908cca547bf13da9",
2007 - "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4", 2245 + "reference": "a06a0c0ff7db3736a50d530c908cca547bf13da9",
2008 "shasum": "" 2246 "shasum": ""
2009 }, 2247 },
2010 "require": { 2248 "require": {
2011 "php": ">=5.3.9" 2249 "php": ">=5.3.9"
2012 }, 2250 },
2013 - "require-dev": {
2014 - "symfony/phpunit-bridge": "~2.7"
2015 - },
2016 "type": "library", 2251 "type": "library",
2017 "extra": { 2252 "extra": {
2018 "branch-alias": { 2253 "branch-alias": {
...@@ -2022,7 +2257,10 @@ ...@@ -2022,7 +2257,10 @@
2022 "autoload": { 2257 "autoload": {
2023 "psr-4": { 2258 "psr-4": {
2024 "Symfony\\Component\\Finder\\": "" 2259 "Symfony\\Component\\Finder\\": ""
2025 - } 2260 + },
2261 + "exclude-from-classmap": [
2262 + "/Tests/"
2263 + ]
2026 }, 2264 },
2027 "notification-url": "https://packagist.org/downloads/", 2265 "notification-url": "https://packagist.org/downloads/",
2028 "license": [ 2266 "license": [
...@@ -2040,28 +2278,27 @@ ...@@ -2040,28 +2278,27 @@
2040 ], 2278 ],
2041 "description": "Symfony Finder Component", 2279 "description": "Symfony Finder Component",
2042 "homepage": "https://symfony.com", 2280 "homepage": "https://symfony.com",
2043 - "time": "2015-07-09 16:07:40" 2281 + "time": "2015-10-30 20:10:21"
2044 }, 2282 },
2045 { 2283 {
2046 "name": "symfony/http-foundation", 2284 "name": "symfony/http-foundation",
2047 - "version": "v2.7.3", 2285 + "version": "v2.7.7",
2048 "source": { 2286 "source": {
2049 "type": "git", 2287 "type": "git",
2050 "url": "https://github.com/symfony/http-foundation.git", 2288 "url": "https://github.com/symfony/http-foundation.git",
2051 - "reference": "863af6898081b34c65d42100c370b9f3c51b70ca" 2289 + "reference": "e83a3d105ddaf5a113e803c904fdec552d1f1c35"
2052 }, 2290 },
2053 "dist": { 2291 "dist": {
2054 "type": "zip", 2292 "type": "zip",
2055 - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/863af6898081b34c65d42100c370b9f3c51b70ca", 2293 + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e83a3d105ddaf5a113e803c904fdec552d1f1c35",
2056 - "reference": "863af6898081b34c65d42100c370b9f3c51b70ca", 2294 + "reference": "e83a3d105ddaf5a113e803c904fdec552d1f1c35",
2057 "shasum": "" 2295 "shasum": ""
2058 }, 2296 },
2059 "require": { 2297 "require": {
2060 "php": ">=5.3.9" 2298 "php": ">=5.3.9"
2061 }, 2299 },
2062 "require-dev": { 2300 "require-dev": {
2063 - "symfony/expression-language": "~2.4", 2301 + "symfony/expression-language": "~2.4"
2064 - "symfony/phpunit-bridge": "~2.7"
2065 }, 2302 },
2066 "type": "library", 2303 "type": "library",
2067 "extra": { 2304 "extra": {
...@@ -2075,6 +2312,9 @@ ...@@ -2075,6 +2312,9 @@
2075 }, 2312 },
2076 "classmap": [ 2313 "classmap": [
2077 "Resources/stubs" 2314 "Resources/stubs"
2315 + ],
2316 + "exclude-from-classmap": [
2317 + "/Tests/"
2078 ] 2318 ]
2079 }, 2319 },
2080 "notification-url": "https://packagist.org/downloads/", 2320 "notification-url": "https://packagist.org/downloads/",
...@@ -2093,20 +2333,20 @@ ...@@ -2093,20 +2333,20 @@
2093 ], 2333 ],
2094 "description": "Symfony HttpFoundation Component", 2334 "description": "Symfony HttpFoundation Component",
2095 "homepage": "https://symfony.com", 2335 "homepage": "https://symfony.com",
2096 - "time": "2015-07-22 10:11:00" 2336 + "time": "2015-11-20 17:41:18"
2097 }, 2337 },
2098 { 2338 {
2099 "name": "symfony/http-kernel", 2339 "name": "symfony/http-kernel",
2100 - "version": "v2.7.3", 2340 + "version": "v2.7.7",
2101 "source": { 2341 "source": {
2102 "type": "git", 2342 "type": "git",
2103 "url": "https://github.com/symfony/http-kernel.git", 2343 "url": "https://github.com/symfony/http-kernel.git",
2104 - "reference": "405d3e7a59ff7a28ec469441326a0ac79065ea98" 2344 + "reference": "5570de31e8fbc03777a8c61eb24f9b626e5e5941"
2105 }, 2345 },
2106 "dist": { 2346 "dist": {
2107 "type": "zip", 2347 "type": "zip",
2108 - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/405d3e7a59ff7a28ec469441326a0ac79065ea98", 2348 + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5570de31e8fbc03777a8c61eb24f9b626e5e5941",
2109 - "reference": "405d3e7a59ff7a28ec469441326a0ac79065ea98", 2349 + "reference": "5570de31e8fbc03777a8c61eb24f9b626e5e5941",
2110 "shasum": "" 2350 "shasum": ""
2111 }, 2351 },
2112 "require": { 2352 "require": {
...@@ -2129,7 +2369,6 @@ ...@@ -2129,7 +2369,6 @@
2129 "symfony/dom-crawler": "~2.0,>=2.0.5", 2369 "symfony/dom-crawler": "~2.0,>=2.0.5",
2130 "symfony/expression-language": "~2.4", 2370 "symfony/expression-language": "~2.4",
2131 "symfony/finder": "~2.0,>=2.0.5", 2371 "symfony/finder": "~2.0,>=2.0.5",
2132 - "symfony/phpunit-bridge": "~2.7",
2133 "symfony/process": "~2.0,>=2.0.5", 2372 "symfony/process": "~2.0,>=2.0.5",
2134 "symfony/routing": "~2.2", 2373 "symfony/routing": "~2.2",
2135 "symfony/stopwatch": "~2.3", 2374 "symfony/stopwatch": "~2.3",
...@@ -2155,7 +2394,10 @@ ...@@ -2155,7 +2394,10 @@
2155 "autoload": { 2394 "autoload": {
2156 "psr-4": { 2395 "psr-4": {
2157 "Symfony\\Component\\HttpKernel\\": "" 2396 "Symfony\\Component\\HttpKernel\\": ""
2158 - } 2397 + },
2398 + "exclude-from-classmap": [
2399 + "/Tests/"
2400 + ]
2159 }, 2401 },
2160 "notification-url": "https://packagist.org/downloads/", 2402 "notification-url": "https://packagist.org/downloads/",
2161 "license": [ 2403 "license": [
...@@ -2173,28 +2415,25 @@ ...@@ -2173,28 +2415,25 @@
2173 ], 2415 ],
2174 "description": "Symfony HttpKernel Component", 2416 "description": "Symfony HttpKernel Component",
2175 "homepage": "https://symfony.com", 2417 "homepage": "https://symfony.com",
2176 - "time": "2015-07-31 13:24:45" 2418 + "time": "2015-11-23 11:57:49"
2177 }, 2419 },
2178 { 2420 {
2179 "name": "symfony/process", 2421 "name": "symfony/process",
2180 - "version": "v2.7.3", 2422 + "version": "v2.7.7",
2181 "source": { 2423 "source": {
2182 "type": "git", 2424 "type": "git",
2183 - "url": "https://github.com/symfony/Process.git", 2425 + "url": "https://github.com/symfony/process.git",
2184 - "reference": "48aeb0e48600321c272955132d7606ab0a49adb3" 2426 + "reference": "f6290983c8725d0afa29bdc3e5295879de3e58f5"
2185 }, 2427 },
2186 "dist": { 2428 "dist": {
2187 "type": "zip", 2429 "type": "zip",
2188 - "url": "https://api.github.com/repos/symfony/Process/zipball/48aeb0e48600321c272955132d7606ab0a49adb3", 2430 + "url": "https://api.github.com/repos/symfony/process/zipball/f6290983c8725d0afa29bdc3e5295879de3e58f5",
2189 - "reference": "48aeb0e48600321c272955132d7606ab0a49adb3", 2431 + "reference": "f6290983c8725d0afa29bdc3e5295879de3e58f5",
2190 "shasum": "" 2432 "shasum": ""
2191 }, 2433 },
2192 "require": { 2434 "require": {
2193 "php": ">=5.3.9" 2435 "php": ">=5.3.9"
2194 }, 2436 },
2195 - "require-dev": {
2196 - "symfony/phpunit-bridge": "~2.7"
2197 - },
2198 "type": "library", 2437 "type": "library",
2199 "extra": { 2438 "extra": {
2200 "branch-alias": { 2439 "branch-alias": {
...@@ -2204,7 +2443,10 @@ ...@@ -2204,7 +2443,10 @@
2204 "autoload": { 2443 "autoload": {
2205 "psr-4": { 2444 "psr-4": {
2206 "Symfony\\Component\\Process\\": "" 2445 "Symfony\\Component\\Process\\": ""
2207 - } 2446 + },
2447 + "exclude-from-classmap": [
2448 + "/Tests/"
2449 + ]
2208 }, 2450 },
2209 "notification-url": "https://packagist.org/downloads/", 2451 "notification-url": "https://packagist.org/downloads/",
2210 "license": [ 2452 "license": [
...@@ -2222,20 +2464,20 @@ ...@@ -2222,20 +2464,20 @@
2222 ], 2464 ],
2223 "description": "Symfony Process Component", 2465 "description": "Symfony Process Component",
2224 "homepage": "https://symfony.com", 2466 "homepage": "https://symfony.com",
2225 - "time": "2015-07-01 11:25:50" 2467 + "time": "2015-11-19 16:11:24"
2226 }, 2468 },
2227 { 2469 {
2228 "name": "symfony/routing", 2470 "name": "symfony/routing",
2229 - "version": "v2.7.3", 2471 + "version": "v2.7.7",
2230 "source": { 2472 "source": {
2231 "type": "git", 2473 "type": "git",
2232 - "url": "https://github.com/symfony/Routing.git", 2474 + "url": "https://github.com/symfony/routing.git",
2233 - "reference": "ea9134f277162b02e5f80ac058b75a77637b0d26" 2475 + "reference": "7450f6196711b124fb8b04a12286d01a0401ddfe"
2234 }, 2476 },
2235 "dist": { 2477 "dist": {
2236 "type": "zip", 2478 "type": "zip",
2237 - "url": "https://api.github.com/repos/symfony/Routing/zipball/ea9134f277162b02e5f80ac058b75a77637b0d26", 2479 + "url": "https://api.github.com/repos/symfony/routing/zipball/7450f6196711b124fb8b04a12286d01a0401ddfe",
2238 - "reference": "ea9134f277162b02e5f80ac058b75a77637b0d26", 2480 + "reference": "7450f6196711b124fb8b04a12286d01a0401ddfe",
2239 "shasum": "" 2481 "shasum": ""
2240 }, 2482 },
2241 "require": { 2483 "require": {
...@@ -2251,7 +2493,6 @@ ...@@ -2251,7 +2493,6 @@
2251 "symfony/config": "~2.7", 2493 "symfony/config": "~2.7",
2252 "symfony/expression-language": "~2.4", 2494 "symfony/expression-language": "~2.4",
2253 "symfony/http-foundation": "~2.3", 2495 "symfony/http-foundation": "~2.3",
2254 - "symfony/phpunit-bridge": "~2.7",
2255 "symfony/yaml": "~2.0,>=2.0.5" 2496 "symfony/yaml": "~2.0,>=2.0.5"
2256 }, 2497 },
2257 "suggest": { 2498 "suggest": {
...@@ -2269,7 +2510,10 @@ ...@@ -2269,7 +2510,10 @@
2269 "autoload": { 2510 "autoload": {
2270 "psr-4": { 2511 "psr-4": {
2271 "Symfony\\Component\\Routing\\": "" 2512 "Symfony\\Component\\Routing\\": ""
2272 - } 2513 + },
2514 + "exclude-from-classmap": [
2515 + "/Tests/"
2516 + ]
2273 }, 2517 },
2274 "notification-url": "https://packagist.org/downloads/", 2518 "notification-url": "https://packagist.org/downloads/",
2275 "license": [ 2519 "license": [
...@@ -2293,20 +2537,20 @@ ...@@ -2293,20 +2537,20 @@
2293 "uri", 2537 "uri",
2294 "url" 2538 "url"
2295 ], 2539 ],
2296 - "time": "2015-07-09 16:07:40" 2540 + "time": "2015-11-18 13:41:01"
2297 }, 2541 },
2298 { 2542 {
2299 "name": "symfony/translation", 2543 "name": "symfony/translation",
2300 - "version": "v2.7.3", 2544 + "version": "v2.7.7",
2301 "source": { 2545 "source": {
2302 "type": "git", 2546 "type": "git",
2303 - "url": "https://github.com/symfony/Translation.git", 2547 + "url": "https://github.com/symfony/translation.git",
2304 - "reference": "c8dc34cc936152c609cdd722af317e4239d10dd6" 2548 + "reference": "e4ecb9c3ba1304eaf24de15c2d7a428101c1982f"
2305 }, 2549 },
2306 "dist": { 2550 "dist": {
2307 "type": "zip", 2551 "type": "zip",
2308 - "url": "https://api.github.com/repos/symfony/Translation/zipball/c8dc34cc936152c609cdd722af317e4239d10dd6", 2552 + "url": "https://api.github.com/repos/symfony/translation/zipball/e4ecb9c3ba1304eaf24de15c2d7a428101c1982f",
2309 - "reference": "c8dc34cc936152c609cdd722af317e4239d10dd6", 2553 + "reference": "e4ecb9c3ba1304eaf24de15c2d7a428101c1982f",
2310 "shasum": "" 2554 "shasum": ""
2311 }, 2555 },
2312 "require": { 2556 "require": {
...@@ -2318,8 +2562,7 @@ ...@@ -2318,8 +2562,7 @@
2318 "require-dev": { 2562 "require-dev": {
2319 "psr/log": "~1.0", 2563 "psr/log": "~1.0",
2320 "symfony/config": "~2.7", 2564 "symfony/config": "~2.7",
2321 - "symfony/intl": "~2.3", 2565 + "symfony/intl": "~2.4",
2322 - "symfony/phpunit-bridge": "~2.7",
2323 "symfony/yaml": "~2.2" 2566 "symfony/yaml": "~2.2"
2324 }, 2567 },
2325 "suggest": { 2568 "suggest": {
...@@ -2336,7 +2579,10 @@ ...@@ -2336,7 +2579,10 @@
2336 "autoload": { 2579 "autoload": {
2337 "psr-4": { 2580 "psr-4": {
2338 "Symfony\\Component\\Translation\\": "" 2581 "Symfony\\Component\\Translation\\": ""
2339 - } 2582 + },
2583 + "exclude-from-classmap": [
2584 + "/Tests/"
2585 + ]
2340 }, 2586 },
2341 "notification-url": "https://packagist.org/downloads/", 2587 "notification-url": "https://packagist.org/downloads/",
2342 "license": [ 2588 "license": [
...@@ -2354,28 +2600,25 @@ ...@@ -2354,28 +2600,25 @@
2354 ], 2600 ],
2355 "description": "Symfony Translation Component", 2601 "description": "Symfony Translation Component",
2356 "homepage": "https://symfony.com", 2602 "homepage": "https://symfony.com",
2357 - "time": "2015-07-09 16:07:40" 2603 + "time": "2015-11-18 13:41:01"
2358 }, 2604 },
2359 { 2605 {
2360 "name": "symfony/var-dumper", 2606 "name": "symfony/var-dumper",
2361 - "version": "v2.7.3", 2607 + "version": "v2.7.7",
2362 "source": { 2608 "source": {
2363 "type": "git", 2609 "type": "git",
2364 "url": "https://github.com/symfony/var-dumper.git", 2610 "url": "https://github.com/symfony/var-dumper.git",
2365 - "reference": "e8903ebba5eb019f5886ffce739ea9e3b7519579" 2611 + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8"
2366 }, 2612 },
2367 "dist": { 2613 "dist": {
2368 "type": "zip", 2614 "type": "zip",
2369 - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e8903ebba5eb019f5886ffce739ea9e3b7519579", 2615 + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72bcb27411780eaee9469729aace73c0d46fb2b8",
2370 - "reference": "e8903ebba5eb019f5886ffce739ea9e3b7519579", 2616 + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8",
2371 "shasum": "" 2617 "shasum": ""
2372 }, 2618 },
2373 "require": { 2619 "require": {
2374 "php": ">=5.3.9" 2620 "php": ">=5.3.9"
2375 }, 2621 },
2376 - "require-dev": {
2377 - "symfony/phpunit-bridge": "~2.7"
2378 - },
2379 "suggest": { 2622 "suggest": {
2380 "ext-symfony_debug": "" 2623 "ext-symfony_debug": ""
2381 }, 2624 },
...@@ -2391,7 +2634,10 @@ ...@@ -2391,7 +2634,10 @@
2391 ], 2634 ],
2392 "psr-4": { 2635 "psr-4": {
2393 "Symfony\\Component\\VarDumper\\": "" 2636 "Symfony\\Component\\VarDumper\\": ""
2394 - } 2637 + },
2638 + "exclude-from-classmap": [
2639 + "/Tests/"
2640 + ]
2395 }, 2641 },
2396 "notification-url": "https://packagist.org/downloads/", 2642 "notification-url": "https://packagist.org/downloads/",
2397 "license": [ 2643 "license": [
...@@ -2413,7 +2659,7 @@ ...@@ -2413,7 +2659,7 @@
2413 "debug", 2659 "debug",
2414 "dump" 2660 "dump"
2415 ], 2661 ],
2416 - "time": "2015-07-28 15:18:12" 2662 + "time": "2015-11-18 13:41:01"
2417 }, 2663 },
2418 { 2664 {
2419 "name": "vlucas/phpdotenv", 2665 "name": "vlucas/phpdotenv",
...@@ -2715,36 +2961,36 @@ ...@@ -2715,36 +2961,36 @@
2715 }, 2961 },
2716 { 2962 {
2717 "name": "phpspec/phpspec", 2963 "name": "phpspec/phpspec",
2718 - "version": "2.2.1", 2964 + "version": "2.4.0",
2719 "source": { 2965 "source": {
2720 "type": "git", 2966 "type": "git",
2721 "url": "https://github.com/phpspec/phpspec.git", 2967 "url": "https://github.com/phpspec/phpspec.git",
2722 - "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8" 2968 + "reference": "1d3938e6d9ffb1bd4805ea8ddac62ea48767f358"
2723 }, 2969 },
2724 "dist": { 2970 "dist": {
2725 "type": "zip", 2971 "type": "zip",
2726 - "url": "https://api.github.com/repos/phpspec/phpspec/zipball/e9a40577323e67f1de2e214abf32976a0352d8f8", 2972 + "url": "https://api.github.com/repos/phpspec/phpspec/zipball/1d3938e6d9ffb1bd4805ea8ddac62ea48767f358",
2727 - "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8", 2973 + "reference": "1d3938e6d9ffb1bd4805ea8ddac62ea48767f358",
2728 "shasum": "" 2974 "shasum": ""
2729 }, 2975 },
2730 "require": { 2976 "require": {
2731 "doctrine/instantiator": "^1.0.1", 2977 "doctrine/instantiator": "^1.0.1",
2978 + "ext-tokenizer": "*",
2732 "php": ">=5.3.3", 2979 "php": ">=5.3.3",
2733 "phpspec/php-diff": "~1.0.0", 2980 "phpspec/php-diff": "~1.0.0",
2734 "phpspec/prophecy": "~1.4", 2981 "phpspec/prophecy": "~1.4",
2735 "sebastian/exporter": "~1.0", 2982 "sebastian/exporter": "~1.0",
2736 - "symfony/console": "~2.3", 2983 + "symfony/console": "~2.3|~3.0",
2737 - "symfony/event-dispatcher": "~2.1", 2984 + "symfony/event-dispatcher": "~2.1|~3.0",
2738 - "symfony/finder": "~2.1", 2985 + "symfony/finder": "~2.1|~3.0",
2739 - "symfony/process": "~2.1", 2986 + "symfony/process": "^2.6|~3.0",
2740 - "symfony/yaml": "~2.1" 2987 + "symfony/yaml": "~2.1|~3.0"
2741 }, 2988 },
2742 "require-dev": { 2989 "require-dev": {
2743 "behat/behat": "^3.0.11", 2990 "behat/behat": "^3.0.11",
2744 "bossa/phpspec2-expect": "~1.0", 2991 "bossa/phpspec2-expect": "~1.0",
2745 "phpunit/phpunit": "~4.4", 2992 "phpunit/phpunit": "~4.4",
2746 - "symfony/filesystem": "~2.1", 2993 + "symfony/filesystem": "~2.1|~3.0"
2747 - "symfony/process": "~2.1"
2748 }, 2994 },
2749 "suggest": { 2995 "suggest": {
2750 "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" 2996 "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters"
...@@ -2789,7 +3035,7 @@ ...@@ -2789,7 +3035,7 @@
2789 "testing", 3035 "testing",
2790 "tests" 3036 "tests"
2791 ], 3037 ],
2792 - "time": "2015-05-30 15:21:40" 3038 + "time": "2015-11-29 02:03:49"
2793 }, 3039 },
2794 { 3040 {
2795 "name": "phpspec/prophecy", 3041 "name": "phpspec/prophecy",
...@@ -2853,16 +3099,16 @@ ...@@ -2853,16 +3099,16 @@
2853 }, 3099 },
2854 { 3100 {
2855 "name": "phpunit/php-code-coverage", 3101 "name": "phpunit/php-code-coverage",
2856 - "version": "2.2.2", 3102 + "version": "2.2.4",
2857 "source": { 3103 "source": {
2858 "type": "git", 3104 "type": "git",
2859 "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 3105 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
2860 - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c" 3106 + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
2861 }, 3107 },
2862 "dist": { 3108 "dist": {
2863 "type": "zip", 3109 "type": "zip",
2864 - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c", 3110 + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
2865 - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c", 3111 + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
2866 "shasum": "" 3112 "shasum": ""
2867 }, 3113 },
2868 "require": { 3114 "require": {
...@@ -2911,7 +3157,7 @@ ...@@ -2911,7 +3157,7 @@
2911 "testing", 3157 "testing",
2912 "xunit" 3158 "xunit"
2913 ], 3159 ],
2914 - "time": "2015-08-04 03:42:39" 3160 + "time": "2015-10-06 15:47:00"
2915 }, 3161 },
2916 { 3162 {
2917 "name": "phpunit/php-file-iterator", 3163 "name": "phpunit/php-file-iterator",
...@@ -3044,16 +3290,16 @@ ...@@ -3044,16 +3290,16 @@
3044 }, 3290 },
3045 { 3291 {
3046 "name": "phpunit/php-token-stream", 3292 "name": "phpunit/php-token-stream",
3047 - "version": "1.4.6", 3293 + "version": "1.4.8",
3048 "source": { 3294 "source": {
3049 "type": "git", 3295 "type": "git",
3050 "url": "https://github.com/sebastianbergmann/php-token-stream.git", 3296 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
3051 - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b" 3297 + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
3052 }, 3298 },
3053 "dist": { 3299 "dist": {
3054 "type": "zip", 3300 "type": "zip",
3055 - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3ab72c62e550370a6cd5dc873e1a04ab57562f5b", 3301 + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
3056 - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b", 3302 + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
3057 "shasum": "" 3303 "shasum": ""
3058 }, 3304 },
3059 "require": { 3305 "require": {
...@@ -3089,20 +3335,20 @@ ...@@ -3089,20 +3335,20 @@
3089 "keywords": [ 3335 "keywords": [
3090 "tokenizer" 3336 "tokenizer"
3091 ], 3337 ],
3092 - "time": "2015-08-16 08:51:00" 3338 + "time": "2015-09-15 10:49:45"
3093 }, 3339 },
3094 { 3340 {
3095 "name": "phpunit/phpunit", 3341 "name": "phpunit/phpunit",
3096 - "version": "4.8.4", 3342 + "version": "4.8.19",
3097 "source": { 3343 "source": {
3098 "type": "git", 3344 "type": "git",
3099 "url": "https://github.com/sebastianbergmann/phpunit.git", 3345 "url": "https://github.com/sebastianbergmann/phpunit.git",
3100 - "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7" 3346 + "reference": "b2caaf8947aba5e002d42126723e9d69795f32b4"
3101 }, 3347 },
3102 "dist": { 3348 "dist": {
3103 "type": "zip", 3349 "type": "zip",
3104 - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", 3350 + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b2caaf8947aba5e002d42126723e9d69795f32b4",
3105 - "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", 3351 + "reference": "b2caaf8947aba5e002d42126723e9d69795f32b4",
3106 "shasum": "" 3352 "shasum": ""
3107 }, 3353 },
3108 "require": { 3354 "require": {
...@@ -3161,24 +3407,24 @@ ...@@ -3161,24 +3407,24 @@
3161 "testing", 3407 "testing",
3162 "xunit" 3408 "xunit"
3163 ], 3409 ],
3164 - "time": "2015-08-15 04:21:23" 3410 + "time": "2015-11-30 08:18:59"
3165 }, 3411 },
3166 { 3412 {
3167 "name": "phpunit/phpunit-mock-objects", 3413 "name": "phpunit/phpunit-mock-objects",
3168 - "version": "2.3.6", 3414 + "version": "2.3.8",
3169 "source": { 3415 "source": {
3170 "type": "git", 3416 "type": "git",
3171 "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 3417 "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
3172 - "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42" 3418 + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
3173 }, 3419 },
3174 "dist": { 3420 "dist": {
3175 "type": "zip", 3421 "type": "zip",
3176 - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/18dfbcb81d05e2296c0bcddd4db96cade75e6f42", 3422 + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
3177 - "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42", 3423 + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
3178 "shasum": "" 3424 "shasum": ""
3179 }, 3425 },
3180 "require": { 3426 "require": {
3181 - "doctrine/instantiator": "~1.0,>=1.0.2", 3427 + "doctrine/instantiator": "^1.0.2",
3182 "php": ">=5.3.3", 3428 "php": ">=5.3.3",
3183 "phpunit/php-text-template": "~1.2", 3429 "phpunit/php-text-template": "~1.2",
3184 "sebastian/exporter": "~1.2" 3430 "sebastian/exporter": "~1.2"
...@@ -3217,7 +3463,7 @@ ...@@ -3217,7 +3463,7 @@
3217 "mock", 3463 "mock",
3218 "xunit" 3464 "xunit"
3219 ], 3465 ],
3220 - "time": "2015-07-10 06:54:24" 3466 + "time": "2015-10-02 06:51:40"
3221 }, 3467 },
3222 { 3468 {
3223 "name": "sebastian/comparator", 3469 "name": "sebastian/comparator",
...@@ -3337,16 +3583,16 @@ ...@@ -3337,16 +3583,16 @@
3337 }, 3583 },
3338 { 3584 {
3339 "name": "sebastian/environment", 3585 "name": "sebastian/environment",
3340 - "version": "1.3.2", 3586 + "version": "1.3.3",
3341 "source": { 3587 "source": {
3342 "type": "git", 3588 "type": "git",
3343 "url": "https://github.com/sebastianbergmann/environment.git", 3589 "url": "https://github.com/sebastianbergmann/environment.git",
3344 - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" 3590 + "reference": "6e7133793a8e5a5714a551a8324337374be209df"
3345 }, 3591 },
3346 "dist": { 3592 "dist": {
3347 "type": "zip", 3593 "type": "zip",
3348 - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", 3594 + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df",
3349 - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", 3595 + "reference": "6e7133793a8e5a5714a551a8324337374be209df",
3350 "shasum": "" 3596 "shasum": ""
3351 }, 3597 },
3352 "require": { 3598 "require": {
...@@ -3383,7 +3629,7 @@ ...@@ -3383,7 +3629,7 @@
3383 "environment", 3629 "environment",
3384 "hhvm" 3630 "hhvm"
3385 ], 3631 ],
3386 - "time": "2015-08-03 06:14:51" 3632 + "time": "2015-12-02 08:37:27"
3387 }, 3633 },
3388 { 3634 {
3389 "name": "sebastian/exporter", 3635 "name": "sebastian/exporter",
...@@ -3453,16 +3699,16 @@ ...@@ -3453,16 +3699,16 @@
3453 }, 3699 },
3454 { 3700 {
3455 "name": "sebastian/global-state", 3701 "name": "sebastian/global-state",
3456 - "version": "1.0.0", 3702 + "version": "1.1.1",
3457 "source": { 3703 "source": {
3458 "type": "git", 3704 "type": "git",
3459 "url": "https://github.com/sebastianbergmann/global-state.git", 3705 "url": "https://github.com/sebastianbergmann/global-state.git",
3460 - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" 3706 + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
3461 }, 3707 },
3462 "dist": { 3708 "dist": {
3463 "type": "zip", 3709 "type": "zip",
3464 - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", 3710 + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
3465 - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", 3711 + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
3466 "shasum": "" 3712 "shasum": ""
3467 }, 3713 },
3468 "require": { 3714 "require": {
...@@ -3500,7 +3746,7 @@ ...@@ -3500,7 +3746,7 @@
3500 "keywords": [ 3746 "keywords": [
3501 "global state" 3747 "global state"
3502 ], 3748 ],
3503 - "time": "2014-10-06 09:23:50" 3749 + "time": "2015-10-12 03:26:01"
3504 }, 3750 },
3505 { 3751 {
3506 "name": "sebastian/recursion-context", 3752 "name": "sebastian/recursion-context",
...@@ -3592,34 +3838,34 @@ ...@@ -3592,34 +3838,34 @@
3592 }, 3838 },
3593 { 3839 {
3594 "name": "symfony/yaml", 3840 "name": "symfony/yaml",
3595 - "version": "v2.7.3", 3841 + "version": "v3.0.0",
3596 "source": { 3842 "source": {
3597 "type": "git", 3843 "type": "git",
3598 - "url": "https://github.com/symfony/Yaml.git", 3844 + "url": "https://github.com/symfony/yaml.git",
3599 - "reference": "71340e996171474a53f3d29111d046be4ad8a0ff" 3845 + "reference": "177a015cb0e19ff4a49e0e2e2c5fc1c1bee07002"
3600 }, 3846 },
3601 "dist": { 3847 "dist": {
3602 "type": "zip", 3848 "type": "zip",
3603 - "url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff", 3849 + "url": "https://api.github.com/repos/symfony/yaml/zipball/177a015cb0e19ff4a49e0e2e2c5fc1c1bee07002",
3604 - "reference": "71340e996171474a53f3d29111d046be4ad8a0ff", 3850 + "reference": "177a015cb0e19ff4a49e0e2e2c5fc1c1bee07002",
3605 "shasum": "" 3851 "shasum": ""
3606 }, 3852 },
3607 "require": { 3853 "require": {
3608 - "php": ">=5.3.9" 3854 + "php": ">=5.5.9"
3609 - },
3610 - "require-dev": {
3611 - "symfony/phpunit-bridge": "~2.7"
3612 }, 3855 },
3613 "type": "library", 3856 "type": "library",
3614 "extra": { 3857 "extra": {
3615 "branch-alias": { 3858 "branch-alias": {
3616 - "dev-master": "2.7-dev" 3859 + "dev-master": "3.0-dev"
3617 } 3860 }
3618 }, 3861 },
3619 "autoload": { 3862 "autoload": {
3620 "psr-4": { 3863 "psr-4": {
3621 "Symfony\\Component\\Yaml\\": "" 3864 "Symfony\\Component\\Yaml\\": ""
3622 - } 3865 + },
3866 + "exclude-from-classmap": [
3867 + "/Tests/"
3868 + ]
3623 }, 3869 },
3624 "notification-url": "https://packagist.org/downloads/", 3870 "notification-url": "https://packagist.org/downloads/",
3625 "license": [ 3871 "license": [
...@@ -3637,7 +3883,7 @@ ...@@ -3637,7 +3883,7 @@
3637 ], 3883 ],
3638 "description": "Symfony Yaml Component", 3884 "description": "Symfony Yaml Component",
3639 "homepage": "https://symfony.com", 3885 "homepage": "https://symfony.com",
3640 - "time": "2015-07-28 14:07:07" 3886 + "time": "2015-11-30 12:36:17"
3641 } 3887 }
3642 ], 3888 ],
3643 "aliases": [], 3889 "aliases": [],
......
...@@ -13,7 +13,7 @@ return [ ...@@ -13,7 +13,7 @@ return [
13 | 13 |
14 */ 14 */
15 15
16 - 'debug' => env('APP_DEBUG', false), 16 + 'debug' => env('APP_DEBUG', false),
17 17
18 /* 18 /*
19 |-------------------------------------------------------------------------- 19 |--------------------------------------------------------------------------
...@@ -26,7 +26,7 @@ return [ ...@@ -26,7 +26,7 @@ return [
26 | 26 |
27 */ 27 */
28 28
29 - 'url' => env('APP_URL', 'http://localhost'), 29 + 'url' => env('APP_URL', 'http://localhost'),
30 30
31 /* 31 /*
32 |-------------------------------------------------------------------------- 32 |--------------------------------------------------------------------------
...@@ -39,7 +39,7 @@ return [ ...@@ -39,7 +39,7 @@ return [
39 | 39 |
40 */ 40 */
41 41
42 - 'timezone' => 'UTC', 42 + 'timezone' => 'UTC',
43 43
44 /* 44 /*
45 |-------------------------------------------------------------------------- 45 |--------------------------------------------------------------------------
...@@ -52,7 +52,7 @@ return [ ...@@ -52,7 +52,7 @@ return [
52 | 52 |
53 */ 53 */
54 54
55 - 'locale' => 'en', 55 + 'locale' => 'en',
56 56
57 /* 57 /*
58 |-------------------------------------------------------------------------- 58 |--------------------------------------------------------------------------
...@@ -78,9 +78,9 @@ return [ ...@@ -78,9 +78,9 @@ return [
78 | 78 |
79 */ 79 */
80 80
81 - 'key' => env('APP_KEY', 'AbAZchsay4uBTU33RubBzLKw203yqSqr'), 81 + 'key' => env('APP_KEY', 'AbAZchsay4uBTU33RubBzLKw203yqSqr'),
82 82
83 - 'cipher' => 'AES-256-CBC', 83 + 'cipher' => 'AES-256-CBC',
84 84
85 /* 85 /*
86 |-------------------------------------------------------------------------- 86 |--------------------------------------------------------------------------
...@@ -95,7 +95,7 @@ return [ ...@@ -95,7 +95,7 @@ return [
95 | 95 |
96 */ 96 */
97 97
98 - 'log' => 'single', 98 + 'log' => 'single',
99 99
100 /* 100 /*
101 |-------------------------------------------------------------------------- 101 |--------------------------------------------------------------------------
...@@ -108,7 +108,7 @@ return [ ...@@ -108,7 +108,7 @@ return [
108 | 108 |
109 */ 109 */
110 110
111 - 'providers' => [ 111 + 'providers' => [
112 112
113 /* 113 /*
114 * Laravel Framework Service Providers... 114 * Laravel Framework Service Providers...
...@@ -167,7 +167,7 @@ return [ ...@@ -167,7 +167,7 @@ return [
167 | 167 |
168 */ 168 */
169 169
170 - 'aliases' => [ 170 + 'aliases' => [
171 171
172 'App' => Illuminate\Support\Facades\App::class, 172 'App' => Illuminate\Support\Facades\App::class,
173 'Artisan' => Illuminate\Support\Facades\Artisan::class, 173 'Artisan' => Illuminate\Support\Facades\Artisan::class,
...@@ -208,15 +208,16 @@ return [ ...@@ -208,15 +208,16 @@ return [
208 */ 208 */
209 209
210 'ImageTool' => Intervention\Image\Facades\Image::class, 210 'ImageTool' => Intervention\Image\Facades\Image::class,
211 - 'Debugbar' => Barryvdh\Debugbar\Facade::class, 211 + 'Debugbar' => Barryvdh\Debugbar\Facade::class,
212 212
213 /** 213 /**
214 * Custom 214 * Custom
215 */ 215 */
216 216
217 - 'Activity' => BookStack\Services\Facades\Activity::class, 217 + 'Activity' => BookStack\Services\Facades\Activity::class,
218 - 'Setting' => BookStack\Services\Facades\Setting::class, 218 + 'Setting' => BookStack\Services\Facades\Setting::class,
219 - 'Views' => BookStack\Services\Facades\Views::class, 219 + 'Views' => BookStack\Services\Facades\Views::class,
220 + 'Images' => \BookStack\Services\Facades\Images::class,
220 221
221 ], 222 ],
222 223
......
...@@ -45,7 +45,7 @@ return [ ...@@ -45,7 +45,7 @@ return [
45 45
46 'local' => [ 46 'local' => [
47 'driver' => 'local', 47 'driver' => 'local',
48 - 'root' => storage_path('app'), 48 + 'root' => public_path(),
49 ], 49 ],
50 50
51 'ftp' => [ 51 'ftp' => [
...@@ -64,10 +64,10 @@ return [ ...@@ -64,10 +64,10 @@ return [
64 64
65 's3' => [ 65 's3' => [
66 'driver' => 's3', 66 'driver' => 's3',
67 - 'key' => 'your-key', 67 + 'key' => env('STORAGE_S3_KEY', 'your-key'),
68 - 'secret' => 'your-secret', 68 + 'secret' => env('STORAGE_S3_SECRET', 'your-secret'),
69 - 'region' => 'your-region', 69 + 'region' => env('STORAGE_S3_REGION', 'your-region'),
70 - 'bucket' => 'your-bucket', 70 + 'bucket' => env('STORAGE_S3_BUCKET', 'your-bucket'),
71 ], 71 ],
72 72
73 'rackspace' => [ 73 'rackspace' => [
......
1 +<?php
2 +
3 +use Illuminate\Database\Schema\Blueprint;
4 +use Illuminate\Database\Migrations\Migration;
5 +
6 +class FulltextWeighting extends Migration
7 +{
8 + /**
9 + * Run the migrations.
10 + *
11 + * @return void
12 + */
13 + public function up()
14 + {
15 + DB::statement('ALTER TABLE pages ADD FULLTEXT name_search(name)');
16 + DB::statement('ALTER TABLE books ADD FULLTEXT name_search(name)');
17 + DB::statement('ALTER TABLE chapters ADD FULLTEXT name_search(name)');
18 + }
19 +
20 + /**
21 + * Reverse the migrations.
22 + *
23 + * @return void
24 + */
25 + public function down()
26 + {
27 + Schema::table('pages', function(Blueprint $table) {
28 + $table->dropIndex('name_search');
29 + });
30 + Schema::table('books', function(Blueprint $table) {
31 + $table->dropIndex('name_search');
32 + });
33 + Schema::table('chapters', function(Blueprint $table) {
34 + $table->dropIndex('name_search');
35 + });
36 + }
37 +}
1 +<?php
2 +
3 +use BookStack\Image;
4 +use Illuminate\Database\Schema\Blueprint;
5 +use Illuminate\Database\Migrations\Migration;
6 +
7 +class AddImageUploadTypes extends Migration
8 +{
9 + /**
10 + * Run the migrations.
11 + *
12 + * @return void
13 + */
14 + public function up()
15 + {
16 + Schema::table('images', function (Blueprint $table) {
17 + $table->string('path', 400);
18 + $table->string('type')->index();
19 + });
20 +
21 + Image::all()->each(function($image) {
22 + $image->path = $image->url;
23 + $image->type = 'gallery';
24 + $image->save();
25 + });
26 + }
27 +
28 + /**
29 + * Reverse the migrations.
30 + *
31 + * @return void
32 + */
33 + public function down()
34 + {
35 + Schema::table('images', function (Blueprint $table) {
36 + $table->dropColumn('type');
37 + $table->dropColumn('path');
38 + });
39 +
40 + }
41 +}
1 +<?php
2 +
3 +use Illuminate\Database\Schema\Blueprint;
4 +use Illuminate\Database\Migrations\Migration;
5 +
6 +class AddUserAvatars extends Migration
7 +{
8 + /**
9 + * Run the migrations.
10 + *
11 + * @return void
12 + */
13 + public function up()
14 + {
15 + Schema::table('users', function (Blueprint $table) {
16 + $table->integer('image_id')->default(0);
17 + });
18 + }
19 +
20 + /**
21 + * Reverse the migrations.
22 + *
23 + * @return void
24 + */
25 + public function down()
26 + {
27 + Schema::table('users', function (Blueprint $table) {
28 + $table->dropColumn('image_id');
29 + });
30 + }
31 +}
...@@ -23,7 +23,9 @@ class DummyContentSeeder extends Seeder ...@@ -23,7 +23,9 @@ class DummyContentSeeder extends Seeder
23 $pages = factory(\BookStack\Page::class, 10)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]); 23 $pages = factory(\BookStack\Page::class, 10)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]);
24 $chapter->pages()->saveMany($pages); 24 $chapter->pages()->saveMany($pages);
25 }); 25 });
26 + $pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
26 $book->chapters()->saveMany($chapters); 27 $book->chapters()->saveMany($chapters);
28 + $book->pages()->saveMany($pages);
27 }); 29 });
28 } 30 }
29 } 31 }
......
1 var elixir = require('laravel-elixir'); 1 var elixir = require('laravel-elixir');
2 2
3 +// Custom extensions
4 +var gulp = require('gulp');
5 +var Task = elixir.Task;
6 +var fs = require('fs');
7 +
8 +elixir.extend('queryVersion', function(inputFiles) {
9 + new Task('queryVersion', function() {
10 + var manifestObject = {};
11 + var uidString = Date.now().toString(16).slice(4);
12 + for (var i = 0; i < inputFiles.length; i++) {
13 + var file = inputFiles[i];
14 + manifestObject[file] = file + '?version=' + uidString;
15 + }
16 + var fileContents = JSON.stringify(manifestObject, null, 1);
17 + fs.writeFileSync('public/build/manifest.json', fileContents);
18 + }).watch(['./public/css/*.css', './public/js/*.js']);
19 +});
20 +
3 elixir(function(mix) { 21 elixir(function(mix) {
4 mix.sass('styles.scss') 22 mix.sass('styles.scss')
5 .sass('print-styles.scss') 23 .sass('print-styles.scss')
6 .browserify(['jquery-extensions.js', 'global.js'], 'public/js/common.js') 24 .browserify(['jquery-extensions.js', 'global.js'], 'public/js/common.js')
7 - .version(['css/styles.css', 'css/print-styles.css', 'js/common.js']); 25 + .queryVersion(['css/styles.css', 'css/print-styles.css', 'js/common.js']);
8 }); 26 });
......
1 { 1 {
2 "private": true, 2 "private": true,
3 "devDependencies": { 3 "devDependencies": {
4 - "gulp": "^3.8.8", 4 + "gulp": "^3.9.0",
5 "insert-css": "^0.2.0" 5 "insert-css": "^0.2.0"
6 }, 6 },
7 "dependencies": { 7 "dependencies": {
......
...@@ -26,5 +26,6 @@ ...@@ -26,5 +26,6 @@
26 <env name="QUEUE_DRIVER" value="sync"/> 26 <env name="QUEUE_DRIVER" value="sync"/>
27 <env name="DB_CONNECTION" value="mysql_testing"/> 27 <env name="DB_CONNECTION" value="mysql_testing"/>
28 <env name="MAIL_PRETEND" value="true"/> 28 <env name="MAIL_PRETEND" value="true"/>
29 + <env name="DISABLE_EXTERNAL_SERVICES" value="true"/>
29 </php> 30 </php>
30 </phpunit> 31 </phpunit>
......
...@@ -5,7 +5,7 @@ A platform to create documentation/wiki content. General information about BookS ...@@ -5,7 +5,7 @@ A platform to create documentation/wiki content. General information about BookS
5 5
6 ## Requirements 6 ## Requirements
7 7
8 -BookStack has the similar requirements to Laravel. On top of those are some front-end build tools which are only required when developing. 8 +BookStack has similar requirements to Laravel. On top of those are some front-end build tools which are only required when developing.
9 9
10 * PHP >= 5.5.9 10 * PHP >= 5.5.9
11 * OpenSSL PHP Extension 11 * OpenSSL PHP Extension
...@@ -25,11 +25,11 @@ Ensure the requirements are met before installing. ...@@ -25,11 +25,11 @@ Ensure the requirements are met before installing.
25 25
26 This project currently uses the `release` branch of this repository as a stable channel for providing updates. 26 This project currently uses the `release` branch of this repository as a stable channel for providing updates.
27 27
28 -The installation is currently somewhat complicated. Some PHP/Laravel experience will benefit. 28 +The installation is currently somewhat complicated and will be made simpler in future releases. Some PHP/Laravel experience will currently benefit.
29 29
30 1. Clone the release branch of this repository into a folder. 30 1. Clone the release branch of this repository into a folder.
31 31
32 -``` 32 +```
33 git clone https://github.com/ssddanbrown/BookStack.git --branch release --single-branch 33 git clone https://github.com/ssddanbrown/BookStack.git --branch release --single-branch
34 ``` 34 ```
35 35
...@@ -37,7 +37,7 @@ git clone https://github.com/ssddanbrown/BookStack.git --branch release --single ...@@ -37,7 +37,7 @@ git clone https://github.com/ssddanbrown/BookStack.git --branch release --single
37 3. Copy the `.env.example` file to `.env` and fill with your own database and mail details. 37 3. Copy the `.env.example` file to `.env` and fill with your own database and mail details.
38 4. Ensure the `storage` & `bootstrap/cache` folders are writable by the web server. 38 4. Ensure the `storage` & `bootstrap/cache` folders are writable by the web server.
39 5. In the application root, Run `php artisan key:generate` to generate a unique application key. 39 5. In the application root, Run `php artisan key:generate` to generate a unique application key.
40 -6. If not using apache or `.htaccess` files are disable you will have to create some URL rewrite rules as shown below. 40 +6. If not using apache or if `.htaccess` files are disabled you will have to create some URL rewrite rules as shown below.
41 7. Run `php migrate` to update the database. 41 7. Run `php migrate` to update the database.
42 8. Done! You can now login using the default admin details `admin@admin.com` with a password of `password`. It is recommended to change these details directly after first logging in. 42 8. Done! You can now login using the default admin details `admin@admin.com` with a password of `password`. It is recommended to change these details directly after first logging in.
43 43
...@@ -76,3 +76,17 @@ Once done you can run `phpunit` in the application root directory to run all tes ...@@ -76,3 +76,17 @@ Once done you can run `phpunit` in the application root directory to run all tes
76 ## License 76 ## License
77 77
78 BookStack is provided under the MIT License. 78 BookStack is provided under the MIT License.
79 +
80 +## Attribution
81 +
82 +These are the great projects used to help build BookStack:
83 +
84 +* [Laravel](http://laravel.com/)
85 +* [VueJS](http://vuejs.org/)
86 +* [jQuery](https://jquery.com/)
87 +* [TinyMCE](https://www.tinymce.com/)
88 +* [highlight.js](https://highlightjs.org/)
89 +* [jQuery Sortable](https://johnny.github.io/jquery-sortable/)
90 +* [Material Design Iconic Font](http://zavoloklom.github.io/material-design-iconic-font/icons.html)
91 +* [Dropzone.js](http://www.dropzonejs.com/)
92 +* [ZeroClipboard](http://zeroclipboard.org/)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
7 <div v-for="image in images"> 7 <div v-for="image in images">
8 <img class="anim fadeIn" 8 <img class="anim fadeIn"
9 :class="{selected: (image==selectedImage)}" 9 :class="{selected: (image==selectedImage)}"
10 - :src="image.thumbnail" :alt="image.title" :title="image.name" 10 + :src="image.thumbs.gallery" :alt="image.title" :title="image.name"
11 @click="imageClick(image)" 11 @click="imageClick(image)"
12 :style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}"> 12 :style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
13 </div> 13 </div>
...@@ -76,6 +76,13 @@ ...@@ -76,6 +76,13 @@
76 } 76 }
77 }, 77 },
78 78
79 + props: {
80 + imageType: {
81 + type: String,
82 + required: true
83 + }
84 + },
85 +
79 created: function () { 86 created: function () {
80 window.ImageManager = this; 87 window.ImageManager = this;
81 }, 88 },
...@@ -88,7 +95,7 @@ ...@@ -88,7 +95,7 @@
88 methods: { 95 methods: {
89 fetchData: function () { 96 fetchData: function () {
90 var _this = this; 97 var _this = this;
91 - this.$http.get('/images/all/' + _this.page, function (data) { 98 + this.$http.get('/images/' + _this.imageType + '/all/' + _this.page, function (data) {
92 _this.images = _this.images.concat(data.images); 99 _this.images = _this.images.concat(data.images);
93 _this.hasMore = data.hasMore; 100 _this.hasMore = data.hasMore;
94 _this.page++; 101 _this.page++;
...@@ -98,7 +105,7 @@ ...@@ -98,7 +105,7 @@
98 setupDropZone: function () { 105 setupDropZone: function () {
99 var _this = this; 106 var _this = this;
100 var dropZone = new Dropzone(_this.$els.dropZone, { 107 var dropZone = new Dropzone(_this.$els.dropZone, {
101 - url: '/upload/image', 108 + url: '/images/' + _this.imageType + '/upload',
102 init: function () { 109 init: function () {
103 var dz = this; 110 var dz = this;
104 this.on("sending", function (file, xhr, data) { 111 this.on("sending", function (file, xhr, data) {
...@@ -110,8 +117,8 @@ ...@@ -110,8 +117,8 @@
110 dz.removeFile(file); 117 dz.removeFile(file);
111 }); 118 });
112 }); 119 });
113 - this.on('error', function(file, errorMessage, xhr) { 120 + this.on('error', function (file, errorMessage, xhr) {
114 - if(errorMessage.file) { 121 + if (errorMessage.file) {
115 $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]); 122 $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
116 } 123 }
117 console.log(errorMessage); 124 console.log(errorMessage);
...@@ -120,6 +127,10 @@ ...@@ -120,6 +127,10 @@
120 }); 127 });
121 }, 128 },
122 129
130 + returnCallback: function (image) {
131 + this.callback(image);
132 + },
133 +
123 imageClick: function (image) { 134 imageClick: function (image) {
124 var dblClickTime = 380; 135 var dblClickTime = 380;
125 var cTime = (new Date()).getTime(); 136 var cTime = (new Date()).getTime();
...@@ -127,7 +138,7 @@ ...@@ -127,7 +138,7 @@
127 if (this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) { 138 if (this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
128 // DoubleClick 139 // DoubleClick
129 if (this.callback) { 140 if (this.callback) {
130 - this.callback(image); 141 + this.returnCallback(image);
131 } 142 }
132 this.hide(); 143 this.hide();
133 } else { 144 } else {
...@@ -139,7 +150,7 @@ ...@@ -139,7 +150,7 @@
139 150
140 selectButtonClick: function () { 151 selectButtonClick: function () {
141 if (this.callback) { 152 if (this.callback) {
142 - this.callback(this.selectedImage); 153 + this.returnCallback(this.selectedImage);
143 } 154 }
144 this.hide(); 155 this.hide();
145 }, 156 },
......
...@@ -7,31 +7,89 @@ ...@@ -7,31 +7,89 @@
7 </div> 7 </div>
8 <button class="button" type="button" @click="showImageManager">Select Image</button> 8 <button class="button" type="button" @click="showImageManager">Select Image</button>
9 <br> 9 <br>
10 - <button class="text-button" @click="reset" type="button">Reset</button> <span class="sep">|</span> <button class="text-button neg" v-on:click="remove" type="button">Remove</button> 10 + <button class="text-button" @click="reset" type="button">Reset</button> <span v-show="showRemove" class="sep">|</span> <button v-show="showRemove" class="text-button neg" @click="remove" type="button">Remove</button>
11 - <input type="hidden" :name="name" :id="name" v-model="image"> 11 + <input type="hidden" :name="name" :id="name" v-model="value">
12 </div> 12 </div>
13 </template> 13 </template>
14 14
15 <script> 15 <script>
16 module.exports = { 16 module.exports = {
17 - props: ['currentImage', 'name', 'imageClass', 'defaultImage'], 17 + props: {
18 + currentImage: {
19 + required: true,
20 + type: String
21 + },
22 + currentId: {
23 + required: false,
24 + default: 'false',
25 + type: String
26 + },
27 + name: {
28 + required: true,
29 + type: String
30 + },
31 + defaultImage: {
32 + required: true,
33 + type: String
34 + },
35 + imageClass: {
36 + required: true,
37 + type: String
38 + },
39 + resizeWidth: {
40 + type: String
41 + },
42 + resizeHeight: {
43 + type: String
44 + },
45 + resizeCrop: {
46 + type: Boolean
47 + },
48 + showRemove: {
49 + type: Boolean,
50 + default: 'true'
51 + }
52 + },
18 data: function() { 53 data: function() {
19 return { 54 return {
20 - image: this.currentImage 55 + image: this.currentImage,
56 + value: false
21 } 57 }
22 }, 58 },
59 + compiled: function() {
60 + this.value = this.currentId === 'false' ? this.currentImage : this.currentId;
61 + },
23 methods: { 62 methods: {
63 + setCurrentValue: function(imageModel, imageUrl) {
64 + this.image = imageUrl;
65 + this.value = this.currentId === 'false' ? imageUrl : imageModel.id;
66 + },
24 showImageManager: function(e) { 67 showImageManager: function(e) {
25 var _this = this; 68 var _this = this;
26 ImageManager.show(function(image) { 69 ImageManager.show(function(image) {
27 - _this.image = image.url; 70 + _this.updateImageFromModel(image);
28 }); 71 });
29 }, 72 },
30 reset: function() { 73 reset: function() {
31 - this.image = ''; 74 + this.setCurrentValue({id: 0}, this.defaultImage);
32 }, 75 },
33 remove: function() { 76 remove: function() {
34 this.image = 'none'; 77 this.image = 'none';
78 + },
79 + updateImageFromModel: function(model) {
80 + var _this = this;
81 + var isResized = _this.resizeWidth && _this.resizeHeight;
82 +
83 + if (!isResized) {
84 + _this.setCurrentValue(model, model.url);
85 + return;
86 + }
87 +
88 + var cropped = _this.resizeCrop ? 'true' : 'false';
89 + var requestString = '/images/thumb/' + model.id + '/' + _this.resizeWidth + '/' + _this.resizeHeight + '/' + cropped;
90 + _this.$http.get(requestString, function(data) {
91 + _this.setCurrentValue(model, data.url);
92 + });
35 } 93 }
36 } 94 }
37 }; 95 };
......
...@@ -100,7 +100,7 @@ module.exports = { ...@@ -100,7 +100,7 @@ module.exports = {
100 onclick: function() { 100 onclick: function() {
101 ImageManager.show(function(image) { 101 ImageManager.show(function(image) {
102 var html = '<a href="'+image.url+'" target="_blank">'; 102 var html = '<a href="'+image.url+'" target="_blank">';
103 - html += '<img src="'+image.display+'" alt="'+image.name+'">'; 103 + html += '<img src="'+image.thumbs.display+'" alt="'+image.name+'">';
104 html += '</a>'; 104 html += '</a>';
105 editor.execCommand('mceInsertContent', false, html); 105 editor.execCommand('mceInsertContent', false, html);
106 }); 106 });
......
...@@ -118,6 +118,9 @@ ...@@ -118,6 +118,9 @@
118 text-decoration: none; 118 text-decoration: none;
119 } 119 }
120 } 120 }
121 + li a i {
122 + padding-right: $-xs + 2px;
123 + }
121 li, a { 124 li, a {
122 display: block; 125 display: block;
123 } 126 }
...@@ -150,11 +153,11 @@ ...@@ -150,11 +153,11 @@
150 } 153 }
151 .list-item-page { 154 .list-item-page {
152 border-bottom: none; 155 border-bottom: none;
156 + border-left: 5px solid $color-page;
157 + margin: 10px 10px;
153 } 158 }
154 .page { 159 .page {
155 color: $color-page !important; 160 color: $color-page !important;
156 - border-left: 5px solid $color-page;
157 - margin: 10px 10px;
158 border-bottom: none; 161 border-bottom: none;
159 &.selected { 162 &.selected {
160 background-color: rgba($color-page, 0.1); 163 background-color: rgba($color-page, 0.1);
......
...@@ -32,10 +32,16 @@ body.dragging, body.dragging * { ...@@ -32,10 +32,16 @@ body.dragging, body.dragging * {
32 .avatar { 32 .avatar {
33 border-radius: 100%; 33 border-radius: 100%;
34 background-color: #EEE; 34 background-color: #EEE;
35 + width: 30px;
36 + height: 30px;
35 &.med { 37 &.med {
36 width: 40px; 38 width: 40px;
37 height: 40px; 39 height: 40px;
38 } 40 }
41 + &.large {
42 + width: 80px;
43 + height: 80px;
44 + }
39 } 45 }
40 46
41 // System wide notifications 47 // System wide notifications
......
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <html> 2 <html>
3 <head> 3 <head>
4 - <title>BookStack</title> 4 + <title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ Setting::get('app-name', 'BookStack') }}</title>
5 5
6 <!-- Meta --> 6 <!-- Meta -->
7 <meta name="viewport" content="width=device-width"> 7 <meta name="viewport" content="width=device-width">
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
9 <meta charset="utf-8"> 9 <meta charset="utf-8">
10 10
11 <!-- Styles and Fonts --> 11 <!-- Styles and Fonts -->
12 - <link rel="stylesheet" href="{{ elixir('css/styles.css') }}"> 12 + <link rel="stylesheet" href="{{ versioned_asset('css/styles.css') }}">
13 - <link rel="stylesheet" media="print" href="{{ elixir('css/print-styles.css') }}"> 13 + <link rel="stylesheet" media="print" href="{{ versioned_asset('css/print-styles.css') }}">
14 <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'> 14 <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'>
15 <link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css"> 15 <link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css">
16 16
...@@ -79,6 +79,6 @@ ...@@ -79,6 +79,6 @@
79 </section> 79 </section>
80 80
81 @yield('bottom') 81 @yield('bottom')
82 -<script src="{{ elixir('js/common.js') }}"></script> 82 +<script src="{{ versioned_asset('js/common.js') }}"></script>
83 </body> 83 </body>
84 </html> 84 </html>
......
...@@ -34,11 +34,22 @@ ...@@ -34,11 +34,22 @@
34 @endif 34 @endif
35 </div> 35 </div>
36 <div class="col-sm-4 col-sm-offset-1"> 36 <div class="col-sm-4 col-sm-offset-1">
37 + <div id="recents">
38 + @if($recents)
39 + <div class="margin-top large">&nbsp;</div>
40 + <h3>Recently Viewed</h3>
41 + @include('partials/entity-list', ['entities' => $recents])
42 + @endif
43 + </div>
37 <div class="margin-top large">&nbsp;</div> 44 <div class="margin-top large">&nbsp;</div>
38 - @if($recents) 45 + <div id="popular">
39 - <h3>Recently Viewed</h3> 46 + <h3>Popular Books</h3>
40 - @include('partials/entity-list', ['entities' => $recents]) 47 + @if(count($popular) > 0)
41 - @endif 48 + @include('partials/entity-list', ['entities' => $popular])
49 + @else
50 + <p class="text-muted">The most popular books will appear here.</p>
51 + @endif
52 + </div>
42 </div> 53 </div>
43 </div> 54 </div>
44 </div> 55 </div>
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
58 <p class="text-muted small"> 58 <p class="text-muted small">
59 Created {{$book->created_at->diffForHumans()}} @if($book->createdBy) by {{$book->createdBy->name}} @endif 59 Created {{$book->created_at->diffForHumans()}} @if($book->createdBy) by {{$book->createdBy->name}} @endif
60 <br> 60 <br>
61 - Last Updated {{$book->updated_at->diffForHumans()}} @if($book->createdBy) by {{$book->updatedBy->name}} @endif 61 + Last Updated {{$book->updated_at->diffForHumans()}} @if($book->updatedBy) by {{$book->updatedBy->name}} @endif
62 </p> 62 </p>
63 </div> 63 </div>
64 </div> 64 </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 <h3 class="text-book"><i class="zmdi zmdi-book"></i>{{ $book->name }}</h3> 2 <h3 class="text-book"><i class="zmdi zmdi-book"></i>{{ $book->name }}</h3>
3 <ul class="sortable-page-list sort-list"> 3 <ul class="sortable-page-list sort-list">
4 @foreach($bookChildren as $bookChild) 4 @foreach($bookChildren as $bookChild)
5 - <li data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getName() }}" class="text-{{ $bookChild->getName() }}"> 5 + <li data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getClassName() }}" class="text-{{ $bookChild->getClassName() }}">
6 <i class="zmdi {{ $bookChild->isA('chapter') ? 'zmdi-collection-bookmark':'zmdi-file-text'}}"></i>{{ $bookChild->name }} 6 <i class="zmdi {{ $bookChild->isA('chapter') ? 'zmdi-collection-bookmark':'zmdi-file-text'}}"></i>{{ $bookChild->name }}
7 @if($bookChild->isA('chapter')) 7 @if($bookChild->isA('chapter'))
8 <ul> 8 <ul>
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
56 <p class="text-muted small"> 56 <p class="text-muted small">
57 Created {{$chapter->created_at->diffForHumans()}} @if($chapter->createdBy) by {{$chapter->createdBy->name}} @endif 57 Created {{$chapter->created_at->diffForHumans()}} @if($chapter->createdBy) by {{$chapter->createdBy->name}} @endif
58 <br> 58 <br>
59 - Last Updated {{$chapter->updated_at->diffForHumans()}} @if($chapter->createdBy) by {{$chapter->updatedBy->name}} @endif 59 + Last Updated {{$chapter->updated_at->diffForHumans()}} @if($chapter->updatedBy) by {{$chapter->updatedBy->name}} @endif
60 </p> 60 </p>
61 </div> 61 </div>
62 <div class="col-md-3 col-md-offset-1"> 62 <div class="col-md-3 col-md-offset-1">
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 @endif 16 @endif
17 </form> 17 </form>
18 </div> 18 </div>
19 - <image-manager></image-manager> 19 + <image-manager image-type="gallery"></image-manager>
20 @stop 20 @stop
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
14 @include('pages/form', ['model' => $page]) 14 @include('pages/form', ['model' => $page])
15 </form> 15 </form>
16 </div> 16 </div>
17 - <image-manager></image-manager> 17 + <image-manager image-type="gallery"></image-manager>
18 18
19 @stop 19 @stop
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -32,8 +32,12 @@ ...@@ -32,8 +32,12 @@
32 @foreach($page->revisions as $revision) 32 @foreach($page->revisions as $revision)
33 <tr> 33 <tr>
34 <td>{{$revision->name}}</td> 34 <td>{{$revision->name}}</td>
35 - <td style="line-height: 0;"><img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{$revision->createdBy->name}}"></td> 35 + <td style="line-height: 0;">
36 - <td> {{$revision->createdBy->name}}</td> 36 + @if($revision->createdBy)
37 + <img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{$revision->createdBy->name}}">
38 + @endif
39 + </td>
40 + <td> @if($revision->createdBy) {{$revision->createdBy->name}} @else Deleted User @endif</td>
37 <td><small>{{$revision->created_at->format('jS F, Y H:i:s')}} ({{$revision->created_at->diffForHumans()}})</small></td> 41 <td><small>{{$revision->created_at->format('jS F, Y H:i:s')}} ({{$revision->created_at->diffForHumans()}})</small></td>
38 <td> 42 <td>
39 <a href="{{$revision->getUrl()}}" target="_blank">Preview</a> 43 <a href="{{$revision->getUrl()}}" target="_blank">Preview</a>
......
...@@ -7,12 +7,12 @@ ...@@ -7,12 +7,12 @@
7 <div class="row"> 7 <div class="row">
8 <div class="col-sm-6 faded"> 8 <div class="col-sm-6 faded">
9 <div class="breadcrumbs"> 9 <div class="breadcrumbs">
10 - <a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a> 10 + <a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
11 @if($page->hasChapter()) 11 @if($page->hasChapter())
12 <span class="sep">&raquo;</span> 12 <span class="sep">&raquo;</span>
13 <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button"> 13 <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
14 <i class="zmdi zmdi-collection-bookmark"></i> 14 <i class="zmdi zmdi-collection-bookmark"></i>
15 - {{$page->chapter->name}} 15 + {{$page->chapter->getShortName()}}
16 </a> 16 </a>
17 @endif 17 @endif
18 </div> 18 </div>
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
53 <p class="text-muted small"> 53 <p class="text-muted small">
54 Created {{$page->created_at->diffForHumans()}} @if($page->createdBy) by {{$page->createdBy->name}} @endif 54 Created {{$page->created_at->diffForHumans()}} @if($page->createdBy) by {{$page->createdBy->name}} @endif
55 <br> 55 <br>
56 - Last Updated {{$page->updated_at->diffForHumans()}} @if($page->createdBy) by {{$page->updatedBy->name}} @endif 56 + Last Updated {{$page->updated_at->diffForHumans()}} @if($page->updatedBy) by {{$page->updatedBy->name}} @endif
57 </p> 57 </p>
58 58
59 </div> 59 </div>
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
6 6
7 7
8 @foreach($sidebarTree as $bookChild) 8 @foreach($sidebarTree as $bookChild)
9 - <li class="list-item-{{ $bookChild->getName() }} {{ $bookChild->getName() }}"> 9 + <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }}">
10 - <a href="{{$bookChild->getUrl()}}" class="{{ $bookChild->getName() }} {{ $current->matches($bookChild)? 'selected' : '' }}"> 10 + <a href="{{$bookChild->getUrl()}}" class="{{ $bookChild->getClassName() }} {{ $current->matches($bookChild)? 'selected' : '' }}">
11 @if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }} 11 @if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }}
12 </a> 12 </a>
13 13
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
10 <div class="right"> 10 <div class="right">
11 @if($activity->user) 11 @if($activity->user)
12 {{$activity->user->name}} 12 {{$activity->user->name}}
13 + @else
14 + A deleted user
13 @endif 15 @endif
14 16
15 {{ $activity->getText() }} 17 {{ $activity->getText() }}
......
1 1
2 @if(count($entities) > 0) 2 @if(count($entities) > 0)
3 - @foreach($entities as $entity) 3 + @foreach($entities as $index => $entity)
4 @if($entity->isA('page')) 4 @if($entity->isA('page'))
5 @include('pages/list-item', ['page' => $entity]) 5 @include('pages/list-item', ['page' => $entity])
6 @elseif($entity->isA('book')) 6 @elseif($entity->isA('book'))
...@@ -8,7 +8,11 @@ ...@@ -8,7 +8,11 @@
8 @elseif($entity->isA('chapter')) 8 @elseif($entity->isA('chapter'))
9 @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true]) 9 @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true])
10 @endif 10 @endif
11 - <hr> 11 +
12 + @if($index !== count($entities) - 1)
13 + <hr>
14 + @endif
15 +
12 @endforeach 16 @endforeach
13 @else 17 @else
14 <p class="text-muted"> 18 <p class="text-muted">
......
...@@ -23,12 +23,17 @@ ...@@ -23,12 +23,17 @@
23 <label>Allow public viewing?</label> 23 <label>Allow public viewing?</label>
24 <toggle-switch name="setting-app-public" value="{{ Setting::get('app-public') }}"></toggle-switch> 24 <toggle-switch name="setting-app-public" value="{{ Setting::get('app-public') }}"></toggle-switch>
25 </div> 25 </div>
26 + <div class="form-group">
27 + <label>Enable higher security image uploads?</label>
28 + <p class="small">For performance reasons, all images are public by default, This option adds a random, hard-to-guess characters in front of image names. Ensure directory indexes are not enabled to prevent easy access.</p>
29 + <toggle-switch name="setting-app-secure-images" value="{{ Setting::get('app-secure-images') }}"></toggle-switch>
30 + </div>
26 </div> 31 </div>
27 <div class="col-md-6"> 32 <div class="col-md-6">
28 <div class="form-group" id="logo-control"> 33 <div class="form-group" id="logo-control">
29 <label for="setting-app-logo">Application Logo</label> 34 <label for="setting-app-logo">Application Logo</label>
30 - <p class="small">This image should be 43px in height. </p> 35 + <p class="small">This image should be 43px in height. <br>Large images will be scaled down.</p>
31 - <image-picker current-image="{{ Setting::get('app-logo', '') }}" default-image="/logo.png" name="setting-app-logo" image-class="logo-image"></image-picker> 36 + <image-picker resize-height="43" resize-width="200" current-image="{{ Setting::get('app-logo', '') }}" default-image="/logo.png" name="setting-app-logo" image-class="logo-image"></image-picker>
32 </div> 37 </div>
33 </div> 38 </div>
34 </div> 39 </div>
...@@ -57,7 +62,7 @@ ...@@ -57,7 +62,7 @@
57 </select> 62 </select>
58 </div> 63 </div>
59 <div class="form-group"> 64 <div class="form-group">
60 - <label for="setting-registration-confirmation">Require Email Confirmation?</label> 65 + <label for="setting-registration-confirmation">Require email confirmation?</label>
61 <p class="small">If domain restriction is used then email confirmation will be required and the below value will be ignored.</p> 66 <p class="small">If domain restriction is used then email confirmation will be required and the below value will be ignored.</p>
62 <toggle-switch name="setting-registration-confirmation" value="{{ Setting::get('registration-confirmation') }}"></toggle-switch> 67 <toggle-switch name="setting-registration-confirmation" value="{{ Setting::get('registration-confirmation') }}"></toggle-switch>
63 </div> 68 </div>
...@@ -81,6 +86,6 @@ ...@@ -81,6 +86,6 @@
81 86
82 </div> 87 </div>
83 88
84 -<image-manager></image-manager> 89 +<image-manager image-type="system"></image-manager>
85 90
86 @stop 91 @stop
......
...@@ -19,26 +19,25 @@ ...@@ -19,26 +19,25 @@
19 19
20 20
21 <div class="container small"> 21 <div class="container small">
22 - 22 + <form action="/users/{{$user->id}}" method="post">
23 <div class="row"> 23 <div class="row">
24 <div class="col-md-6"> 24 <div class="col-md-6">
25 <h1>Edit {{ $user->id === $currentUser->id ? 'Profile' : 'User' }}</h1> 25 <h1>Edit {{ $user->id === $currentUser->id ? 'Profile' : 'User' }}</h1>
26 - <form action="/users/{{$user->id}}" method="post"> 26 + {!! csrf_field() !!}
27 - {!! csrf_field() !!} 27 + <input type="hidden" name="_method" value="put">
28 - <input type="hidden" name="_method" value="put"> 28 + @include('users/form', ['model' => $user])
29 - @include('users/form', ['model' => $user]) 29 +
30 - </form>
31 </div> 30 </div>
32 <div class="col-md-6"> 31 <div class="col-md-6">
33 <h1>&nbsp;</h1> 32 <h1>&nbsp;</h1>
34 - <div class="shaded padded margin-top"> 33 + <div class="form-group" id="logo-control">
35 - <p> 34 + <label for="user-avatar">User Avatar</label>
36 - <img class="avatar" src="{{ $user->getAvatar(80) }}" alt="{{ $user->name }}"> 35 + <p class="small">This image should be approx 256px square.</p>
37 - </p> 36 + <image-picker resize-height="512" resize-width="512" current-image="{{ $user->getAvatar(80) }}" current-id="{{ $user->image_id }}" default-image="/user_avatar.png" name="image_id" show-remove="false" image-class="avatar large"></image-picker>
38 - <p class="text-muted">You can change your profile picture at <a href="http://en.gravatar.com/">Gravatar</a>.</p>
39 </div> 37 </div>
40 </div> 38 </div>
41 </div> 39 </div>
40 + </form>
42 41
43 <hr class="margin-top large"> 42 <hr class="margin-top large">
44 43
...@@ -80,5 +79,5 @@ ...@@ -80,5 +79,5 @@
80 </div> 79 </div>
81 80
82 <p class="margin-top large"><br></p> 81 <p class="margin-top large"><br></p>
83 - 82 + <image-manager image-type="user"></image-manager>
84 @stop 83 @stop
......
...@@ -36,4 +36,5 @@ ...@@ -36,4 +36,5 @@
36 <div class="form-group"> 36 <div class="form-group">
37 <a href="/users" class="button muted">Cancel</a> 37 <a href="/users" class="button muted">Cancel</a>
38 <button class="button pos" type="submit">Save</button> 38 <button class="button pos" type="submit">Save</button>
39 -</div>
...\ No newline at end of file ...\ No newline at end of file
39 +</div>
40 +
......
1 +<?php
2 +
3 +use Illuminate\Foundation\Testing\WithoutMiddleware;
4 +use Illuminate\Foundation\Testing\DatabaseMigrations;
5 +use Illuminate\Foundation\Testing\DatabaseTransactions;
6 +
7 +class ActivityTrackingTest extends TestCase
8 +{
9 +
10 + public function testRecentlyViewedBooks()
11 + {
12 + $books = \BookStack\Book::all()->take(10);
13 +
14 + $this->asAdmin()->visit('/books')
15 + ->dontSeeInElement('#recents', $books[0]->name)
16 + ->dontSeeInElement('#recents', $books[1]->name)
17 + ->visit($books[0]->getUrl())
18 + ->visit($books[1]->getUrl())
19 + ->visit('/books')
20 + ->seeInElement('#recents', $books[0]->name)
21 + ->seeInElement('#recents', $books[1]->name);
22 + }
23 +
24 + public function testPopularBooks()
25 + {
26 + $books = \BookStack\Book::all()->take(10);
27 +
28 + $this->asAdmin()->visit('/books')
29 + ->dontSeeInElement('#popular', $books[0]->name)
30 + ->dontSeeInElement('#popular', $books[1]->name)
31 + ->visit($books[0]->getUrl())
32 + ->visit($books[1]->getUrl())
33 + ->visit($books[0]->getUrl())
34 + ->visit('/books')
35 + ->seeInNthElement('#popular .book', 0, $books[0]->name)
36 + ->seeInNthElement('#popular .book', 1, $books[1]->name);
37 + }
38 +}
...@@ -171,4 +171,43 @@ class EntityTest extends TestCase ...@@ -171,4 +171,43 @@ class EntityTest extends TestCase
171 } 171 }
172 172
173 173
174 + public function testEntitiesViewableAfterCreatorDeletion()
175 + {
176 + // Create required assets and revisions
177 + $creator = $this->getNewUser();
178 + $updater = $this->getNewUser();
179 + $entities = $this->createEntityChainBelongingToUser($creator, $updater);
180 + $this->actingAs($creator);
181 + app('BookStack\Repos\UserRepo')->destroy($creator);
182 + app('BookStack\Repos\PageRepo')->saveRevision($entities['page']);
183 +
184 + $this->checkEntitiesViewable($entities);
185 + }
186 +
187 + public function testEntitiesViewableAfterUpdaterDeletion()
188 + {
189 + // Create required assets and revisions
190 + $creator = $this->getNewUser();
191 + $updater = $this->getNewUser();
192 + $entities = $this->createEntityChainBelongingToUser($creator, $updater);
193 + $this->actingAs($updater);
194 + app('BookStack\Repos\UserRepo')->destroy($updater);
195 + app('BookStack\Repos\PageRepo')->saveRevision($entities['page']);
196 +
197 + $this->checkEntitiesViewable($entities);
198 + }
199 +
200 + private function checkEntitiesViewable($entities)
201 + {
202 + // Check pages and books are visible.
203 + $this->asAdmin();
204 + $this->visit($entities['book']->getUrl())->seeStatusCode(200)
205 + ->visit($entities['chapter']->getUrl())->seeStatusCode(200)
206 + ->visit($entities['page']->getUrl())->seeStatusCode(200);
207 + // Check revision listing shows no errors.
208 + $this->visit($entities['page']->getUrl())
209 + ->click('Revisions')->seeStatusCode(200);
210 + }
211 +
212 +
174 } 213 }
......
...@@ -48,4 +48,65 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase ...@@ -48,4 +48,65 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
48 $settings->put($key, $value); 48 $settings->put($key, $value);
49 } 49 }
50 } 50 }
51 +
52 + /**
53 + * Create a group of entities that belong to a specific user.
54 + * @param $creatorUser
55 + * @param $updaterUser
56 + * @return array
57 + */
58 + protected function createEntityChainBelongingToUser($creatorUser, $updaterUser = false)
59 + {
60 + if ($updaterUser === false) $updaterUser = $creatorUser;
61 + $book = factory(BookStack\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
62 + $chapter = factory(BookStack\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
63 + $page = factory(BookStack\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
64 + $book->chapters()->saveMany([$chapter]);
65 + $chapter->pages()->saveMany([$page]);
66 + return [
67 + 'book' => $book,
68 + 'chapter' => $chapter,
69 + 'page' => $page
70 + ];
71 + }
72 +
73 + /**
74 + * Quick way to create a new user
75 + * @param array $attributes
76 + * @return mixed
77 + */
78 + protected function getNewUser($attributes = [])
79 + {
80 + $user = factory(\BookStack\User::class)->create($attributes);
81 + $userRepo = app('BookStack\Repos\UserRepo');
82 + $userRepo->attachDefaultRole($user);
83 + return $user;
84 + }
85 +
86 + /**
87 + * Assert that a given string is seen inside an element.
88 + *
89 + * @param bool|string|null $element
90 + * @param integer $position
91 + * @param string $text
92 + * @param bool $negate
93 + * @return $this
94 + */
95 + protected function seeInNthElement($element, $position, $text, $negate = false)
96 + {
97 + $method = $negate ? 'assertNotRegExp' : 'assertRegExp';
98 +
99 + $rawPattern = preg_quote($text, '/');
100 +
101 + $escapedPattern = preg_quote(e($text), '/');
102 +
103 + $content = $this->crawler->filter($element)->eq($position)->html();
104 +
105 + $pattern = $rawPattern == $escapedPattern
106 + ? $rawPattern : "({$rawPattern}|{$escapedPattern})";
107 +
108 + $this->$method("/$pattern/i", $content);
109 +
110 + return $this;
111 + }
51 } 112 }
......