Added uploaded to book/page filters & search in image manager
Also refactored tab styles which affected the settings area. Closes #41
Showing
10 changed files
with
234 additions
and
27 deletions
| 1 | -<?php | 1 | +<?php namespace BookStack\Http\Controllers; |
| 2 | - | ||
| 3 | -namespace BookStack\Http\Controllers; | ||
| 4 | 2 | ||
| 5 | use BookStack\Exceptions\ImageUploadException; | 3 | use BookStack\Exceptions\ImageUploadException; |
| 6 | use BookStack\Repos\ImageRepo; | 4 | use BookStack\Repos\ImageRepo; |
| 7 | use Illuminate\Filesystem\Filesystem as File; | 5 | use Illuminate\Filesystem\Filesystem as File; |
| 8 | use Illuminate\Http\Request; | 6 | use Illuminate\Http\Request; |
| 9 | -use Illuminate\Support\Facades\Auth; | ||
| 10 | -use Intervention\Image\Facades\Image as ImageTool; | ||
| 11 | -use Illuminate\Support\Facades\DB; | ||
| 12 | use BookStack\Image; | 7 | use BookStack\Image; |
| 13 | use BookStack\Repos\PageRepo; | 8 | use BookStack\Repos\PageRepo; |
| 14 | 9 | ||
| ... | @@ -45,6 +40,24 @@ class ImageController extends Controller | ... | @@ -45,6 +40,24 @@ class ImageController extends Controller |
| 45 | } | 40 | } |
| 46 | 41 | ||
| 47 | /** | 42 | /** |
| 43 | + * Search through images within a particular type. | ||
| 44 | + * @param $type | ||
| 45 | + * @param int $page | ||
| 46 | + * @param Request $request | ||
| 47 | + * @return mixed | ||
| 48 | + */ | ||
| 49 | + public function searchByType($type, $page = 0, Request $request) | ||
| 50 | + { | ||
| 51 | + $this->validate($request, [ | ||
| 52 | + 'term' => 'required|string' | ||
| 53 | + ]); | ||
| 54 | + | ||
| 55 | + $searchTerm = $request->get('term'); | ||
| 56 | + $imgData = $this->imageRepo->searchPaginatedByType($type, $page,24, $searchTerm); | ||
| 57 | + return response()->json($imgData); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 48 | * Get all images for a user. | 61 | * Get all images for a user. |
| 49 | * @param int $page | 62 | * @param int $page |
| 50 | * @return \Illuminate\Http\JsonResponse | 63 | * @return \Illuminate\Http\JsonResponse |
| ... | @@ -56,6 +69,27 @@ class ImageController extends Controller | ... | @@ -56,6 +69,27 @@ class ImageController extends Controller |
| 56 | } | 69 | } |
| 57 | 70 | ||
| 58 | /** | 71 | /** |
| 72 | + * Get gallery images with a specific filter such as book or page | ||
| 73 | + * @param $filter | ||
| 74 | + * @param int $page | ||
| 75 | + * @param Request $request | ||
| 76 | + */ | ||
| 77 | + public function getGalleryFiltered($filter, $page = 0, Request $request) | ||
| 78 | + { | ||
| 79 | + $this->validate($request, [ | ||
| 80 | + 'page_id' => 'required|integer' | ||
| 81 | + ]); | ||
| 82 | + | ||
| 83 | + $validFilters = collect(['page', 'book']); | ||
| 84 | + if (!$validFilters->contains($filter)) return response('Invalid filter', 500); | ||
| 85 | + | ||
| 86 | + $pageId = $request->get('page_id'); | ||
| 87 | + $imgData = $this->imageRepo->getGalleryFiltered($page, 24, strtolower($filter), $pageId); | ||
| 88 | + | ||
| 89 | + return response()->json($imgData); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + /** | ||
| 59 | * Handles image uploads for use on pages. | 93 | * Handles image uploads for use on pages. |
| 60 | * @param string $type | 94 | * @param string $type |
| 61 | * @param Request $request | 95 | * @param Request $request | ... | ... |
| ... | @@ -75,6 +75,8 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -75,6 +75,8 @@ Route::group(['middleware' => 'auth'], function () { |
| 75 | Route::post('/{type}/upload', 'ImageController@uploadByType'); | 75 | Route::post('/{type}/upload', 'ImageController@uploadByType'); |
| 76 | Route::get('/{type}/all', 'ImageController@getAllByType'); | 76 | Route::get('/{type}/all', 'ImageController@getAllByType'); |
| 77 | Route::get('/{type}/all/{page}', 'ImageController@getAllByType'); | 77 | Route::get('/{type}/all/{page}', 'ImageController@getAllByType'); |
| 78 | + Route::get('/{type}/search/{page}', 'ImageController@searchByType'); | ||
| 79 | + Route::get('/gallery/{filter}/{page}', 'ImageController@getGalleryFiltered'); | ||
| 78 | Route::delete('/{imageId}', 'ImageController@destroy'); | 80 | Route::delete('/{imageId}', 'ImageController@destroy'); |
| 79 | }); | 81 | }); |
| 80 | 82 | ... | ... |
| ... | @@ -2,6 +2,7 @@ | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | use BookStack\Image; | 4 | use BookStack\Image; |
| 5 | +use BookStack\Page; | ||
| 5 | use BookStack\Services\ImageService; | 6 | use BookStack\Services\ImageService; |
| 6 | use BookStack\Services\RestrictionService; | 7 | use BookStack\Services\RestrictionService; |
| 7 | use Setting; | 8 | use Setting; |
| ... | @@ -13,18 +14,21 @@ class ImageRepo | ... | @@ -13,18 +14,21 @@ class ImageRepo |
| 13 | protected $image; | 14 | protected $image; |
| 14 | protected $imageService; | 15 | protected $imageService; |
| 15 | protected $restictionService; | 16 | protected $restictionService; |
| 17 | + protected $page; | ||
| 16 | 18 | ||
| 17 | /** | 19 | /** |
| 18 | * ImageRepo constructor. | 20 | * ImageRepo constructor. |
| 19 | * @param Image $image | 21 | * @param Image $image |
| 20 | * @param ImageService $imageService | 22 | * @param ImageService $imageService |
| 21 | * @param RestrictionService $restrictionService | 23 | * @param RestrictionService $restrictionService |
| 24 | + * @param Page $page | ||
| 22 | */ | 25 | */ |
| 23 | - public function __construct(Image $image, ImageService $imageService, RestrictionService $restrictionService) | 26 | + public function __construct(Image $image, ImageService $imageService, RestrictionService $restrictionService, Page $page) |
| 24 | { | 27 | { |
| 25 | $this->image = $image; | 28 | $this->image = $image; |
| 26 | $this->imageService = $imageService; | 29 | $this->imageService = $imageService; |
| 27 | $this->restictionService = $restrictionService; | 30 | $this->restictionService = $restrictionService; |
| 31 | + $this->page = $page; | ||
| 28 | } | 32 | } |
| 29 | 33 | ||
| 30 | 34 | ||
| ... | @@ -39,22 +43,16 @@ class ImageRepo | ... | @@ -39,22 +43,16 @@ class ImageRepo |
| 39 | } | 43 | } |
| 40 | 44 | ||
| 41 | /** | 45 | /** |
| 42 | - * Gets a load images paginated, filtered by image type. | 46 | + * Execute a paginated query, returning in a standard format. |
| 43 | - * @param string $type | 47 | + * Also runs the query through the restriction system. |
| 48 | + * @param $query | ||
| 44 | * @param int $page | 49 | * @param int $page |
| 45 | * @param int $pageSize | 50 | * @param int $pageSize |
| 46 | - * @param bool|int $userFilter | ||
| 47 | * @return array | 51 | * @return array |
| 48 | */ | 52 | */ |
| 49 | - public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false) | 53 | + private function returnPaginated($query, $page = 0, $pageSize = 24) |
| 50 | { | 54 | { |
| 51 | - $images = $this->image->where('type', '=', strtolower($type)); | 55 | + $images = $this->restictionService->filterRelatedPages($query, 'images', 'uploaded_to'); |
| 52 | - | ||
| 53 | - if ($userFilter !== false) { | ||
| 54 | - $images = $images->where('created_by', '=', $userFilter); | ||
| 55 | - } | ||
| 56 | - | ||
| 57 | - $images = $this->restictionService->filterRelatedPages($images, 'images', 'uploaded_to'); | ||
| 58 | $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get(); | 56 | $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get(); |
| 59 | $hasMore = count($images) > $pageSize; | 57 | $hasMore = count($images) > $pageSize; |
| 60 | 58 | ||
| ... | @@ -70,6 +68,64 @@ class ImageRepo | ... | @@ -70,6 +68,64 @@ class ImageRepo |
| 70 | } | 68 | } |
| 71 | 69 | ||
| 72 | /** | 70 | /** |
| 71 | + * Gets a load images paginated, filtered by image type. | ||
| 72 | + * @param string $type | ||
| 73 | + * @param int $page | ||
| 74 | + * @param int $pageSize | ||
| 75 | + * @param bool|int $userFilter | ||
| 76 | + * @return array | ||
| 77 | + */ | ||
| 78 | + public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false) | ||
| 79 | + { | ||
| 80 | + $images = $this->image->where('type', '=', strtolower($type)); | ||
| 81 | + | ||
| 82 | + if ($userFilter !== false) { | ||
| 83 | + $images = $images->where('created_by', '=', $userFilter); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + return $this->returnPaginated($images, $page, $pageSize); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * Search for images by query, of a particular type. | ||
| 91 | + * @param string $type | ||
| 92 | + * @param int $page | ||
| 93 | + * @param int $pageSize | ||
| 94 | + * @param string $searchTerm | ||
| 95 | + * @return array | ||
| 96 | + */ | ||
| 97 | + public function searchPaginatedByType($type, $page = 0, $pageSize = 24, $searchTerm) | ||
| 98 | + { | ||
| 99 | + $images = $this->image->where('type', '=', strtolower($type))->where('name', 'LIKE', '%' . $searchTerm . '%'); | ||
| 100 | + return $this->returnPaginated($images, $page, $pageSize); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + /** | ||
| 104 | + * Get gallery images with a particular filter criteria such as | ||
| 105 | + * being within the current book or page. | ||
| 106 | + * @param int $pagination | ||
| 107 | + * @param int $pageSize | ||
| 108 | + * @param $filter | ||
| 109 | + * @param $pageId | ||
| 110 | + * @return array | ||
| 111 | + */ | ||
| 112 | + public function getGalleryFiltered($pagination = 0, $pageSize = 24, $filter, $pageId) | ||
| 113 | + { | ||
| 114 | + $images = $this->image->where('type', '=', 'gallery'); | ||
| 115 | + | ||
| 116 | + $page = $this->page->findOrFail($pageId); | ||
| 117 | + | ||
| 118 | + if ($filter === 'page') { | ||
| 119 | + $images = $images->where('uploaded_to', '=', $page->id); | ||
| 120 | + } elseif ($filter === 'book') { | ||
| 121 | + $validPageIds = $page->book->pages->pluck('id')->toArray(); | ||
| 122 | + $images = $images->whereIn('uploaded_to', $validPageIds); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + return $this->returnPaginated($images, $pagination, $pageSize); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + /** | ||
| 73 | * Save a new image into storage and return the new image. | 129 | * Save a new image into storage and return the new image. |
| 74 | * @param UploadedFile $uploadFile | 130 | * @param UploadedFile $uploadFile |
| 75 | * @param string $type | 131 | * @param string $type | ... | ... |
| ... | @@ -14,14 +14,22 @@ module.exports = function (ngApp, events) { | ... | @@ -14,14 +14,22 @@ module.exports = function (ngApp, events) { |
| 14 | $scope.imageUpdateSuccess = false; | 14 | $scope.imageUpdateSuccess = false; |
| 15 | $scope.imageDeleteSuccess = false; | 15 | $scope.imageDeleteSuccess = false; |
| 16 | $scope.uploadedTo = $attrs.uploadedTo; | 16 | $scope.uploadedTo = $attrs.uploadedTo; |
| 17 | + $scope.view = 'all'; | ||
| 18 | + | ||
| 19 | + $scope.searching = false; | ||
| 20 | + $scope.searchTerm = ''; | ||
| 17 | 21 | ||
| 18 | var page = 0; | 22 | var page = 0; |
| 19 | var previousClickTime = 0; | 23 | var previousClickTime = 0; |
| 24 | + var previousClickImage = 0; | ||
| 20 | var dataLoaded = false; | 25 | var dataLoaded = false; |
| 21 | var callback = false; | 26 | var callback = false; |
| 22 | 27 | ||
| 28 | + var preSearchImages = []; | ||
| 29 | + var preSearchHasMore = false; | ||
| 30 | + | ||
| 23 | /** | 31 | /** |
| 24 | - * Simple returns the appropriate upload url depending on the image type set. | 32 | + * Used by dropzone to get the endpoint to upload to. |
| 25 | * @returns {string} | 33 | * @returns {string} |
| 26 | */ | 34 | */ |
| 27 | $scope.getUploadUrl = function () { | 35 | $scope.getUploadUrl = function () { |
| ... | @@ -29,6 +37,18 @@ module.exports = function (ngApp, events) { | ... | @@ -29,6 +37,18 @@ module.exports = function (ngApp, events) { |
| 29 | }; | 37 | }; |
| 30 | 38 | ||
| 31 | /** | 39 | /** |
| 40 | + * Cancel the current search operation. | ||
| 41 | + */ | ||
| 42 | + function cancelSearch() { | ||
| 43 | + $scope.searching = false; | ||
| 44 | + $scope.searchTerm = ''; | ||
| 45 | + $scope.images = preSearchImages; | ||
| 46 | + $scope.hasMore = preSearchHasMore; | ||
| 47 | + } | ||
| 48 | + $scope.cancelSearch = cancelSearch; | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + /** | ||
| 32 | * Runs on image upload, Adds an image to local list of images | 52 | * Runs on image upload, Adds an image to local list of images |
| 33 | * and shows a success message to the user. | 53 | * and shows a success message to the user. |
| 34 | * @param file | 54 | * @param file |
| ... | @@ -59,7 +79,7 @@ module.exports = function (ngApp, events) { | ... | @@ -59,7 +79,7 @@ module.exports = function (ngApp, events) { |
| 59 | var currentTime = Date.now(); | 79 | var currentTime = Date.now(); |
| 60 | var timeDiff = currentTime - previousClickTime; | 80 | var timeDiff = currentTime - previousClickTime; |
| 61 | 81 | ||
| 62 | - if (timeDiff < dblClickTime) { | 82 | + if (timeDiff < dblClickTime && image.id === previousClickImage) { |
| 63 | // If double click | 83 | // If double click |
| 64 | callbackAndHide(image); | 84 | callbackAndHide(image); |
| 65 | } else { | 85 | } else { |
| ... | @@ -68,6 +88,7 @@ module.exports = function (ngApp, events) { | ... | @@ -68,6 +88,7 @@ module.exports = function (ngApp, events) { |
| 68 | $scope.dependantPages = false; | 88 | $scope.dependantPages = false; |
| 69 | } | 89 | } |
| 70 | previousClickTime = currentTime; | 90 | previousClickTime = currentTime; |
| 91 | + previousClickImage = image.id; | ||
| 71 | }; | 92 | }; |
| 72 | 93 | ||
| 73 | /** | 94 | /** |
| ... | @@ -110,21 +131,70 @@ module.exports = function (ngApp, events) { | ... | @@ -110,21 +131,70 @@ module.exports = function (ngApp, events) { |
| 110 | $scope.showing = false; | 131 | $scope.showing = false; |
| 111 | }; | 132 | }; |
| 112 | 133 | ||
| 134 | + var baseUrl = '/images/' + $scope.imageType + '/all/' | ||
| 135 | + | ||
| 113 | /** | 136 | /** |
| 114 | * Fetch the list image data from the server. | 137 | * Fetch the list image data from the server. |
| 115 | */ | 138 | */ |
| 116 | function fetchData() { | 139 | function fetchData() { |
| 117 | - var url = '/images/' + $scope.imageType + '/all/' + page; | 140 | + var url = baseUrl + page + '?'; |
| 141 | + var components = {}; | ||
| 142 | + if ($scope.uploadedTo) components['page_id'] = $scope.uploadedTo; | ||
| 143 | + if ($scope.searching) components['term'] = $scope.searchTerm; | ||
| 144 | + | ||
| 145 | + | ||
| 146 | + var urlQueryString = Object.keys(components).map((key) => { | ||
| 147 | + return key + '=' + encodeURIComponent(components[key]); | ||
| 148 | + }).join('&'); | ||
| 149 | + url += urlQueryString; | ||
| 150 | + | ||
| 118 | $http.get(url).then((response) => { | 151 | $http.get(url).then((response) => { |
| 119 | $scope.images = $scope.images.concat(response.data.images); | 152 | $scope.images = $scope.images.concat(response.data.images); |
| 120 | $scope.hasMore = response.data.hasMore; | 153 | $scope.hasMore = response.data.hasMore; |
| 121 | page++; | 154 | page++; |
| 122 | }); | 155 | }); |
| 123 | } | 156 | } |
| 124 | - | ||
| 125 | $scope.fetchData = fetchData; | 157 | $scope.fetchData = fetchData; |
| 126 | 158 | ||
| 127 | /** | 159 | /** |
| 160 | + * Start a search operation | ||
| 161 | + * @param searchTerm | ||
| 162 | + */ | ||
| 163 | + $scope.searchImages = function() { | ||
| 164 | + | ||
| 165 | + if ($scope.searchTerm === '') { | ||
| 166 | + cancelSearch(); | ||
| 167 | + return; | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + if (!$scope.searching) { | ||
| 171 | + preSearchImages = $scope.images; | ||
| 172 | + preSearchHasMore = $scope.hasMore; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + $scope.searching = true; | ||
| 176 | + $scope.images = []; | ||
| 177 | + $scope.hasMore = false; | ||
| 178 | + page = 0; | ||
| 179 | + baseUrl = '/images/' + $scope.imageType + '/search/'; | ||
| 180 | + fetchData(); | ||
| 181 | + }; | ||
| 182 | + | ||
| 183 | + /** | ||
| 184 | + * Set the current image listing view. | ||
| 185 | + * @param viewName | ||
| 186 | + */ | ||
| 187 | + $scope.setView = function(viewName) { | ||
| 188 | + cancelSearch(); | ||
| 189 | + $scope.images = []; | ||
| 190 | + $scope.hasMore = false; | ||
| 191 | + page = 0; | ||
| 192 | + $scope.view = viewName; | ||
| 193 | + baseUrl = '/images/' + $scope.imageType + '/' + viewName + '/'; | ||
| 194 | + fetchData(); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + /** | ||
| 128 | * Save the details of an image. | 198 | * Save the details of an image. |
| 129 | * @param event | 199 | * @param event |
| 130 | */ | 200 | */ | ... | ... |
| ... | @@ -189,12 +189,13 @@ form.search-box { | ... | @@ -189,12 +189,13 @@ form.search-box { |
| 189 | } | 189 | } |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | -.setting-nav { | 192 | +.nav-tabs { |
| 193 | text-align: center; | 193 | text-align: center; |
| 194 | - a { | 194 | + a, .tab-item { |
| 195 | padding: $-m; | 195 | padding: $-m; |
| 196 | display: inline-block; | 196 | display: inline-block; |
| 197 | color: #666; | 197 | color: #666; |
| 198 | + cursor: pointer; | ||
| 198 | &.selected { | 199 | &.selected { |
| 199 | border-bottom: 2px solid $primary; | 200 | border-bottom: 2px solid $primary; |
| 200 | } | 201 | } | ... | ... |
| ... | @@ -120,7 +120,6 @@ | ... | @@ -120,7 +120,6 @@ |
| 120 | .image-manager-list { | 120 | .image-manager-list { |
| 121 | overflow-y: scroll; | 121 | overflow-y: scroll; |
| 122 | flex: 1; | 122 | flex: 1; |
| 123 | - border-top: 1px solid #ddd; | ||
| 124 | } | 123 | } |
| 125 | 124 | ||
| 126 | .image-manager-content { | 125 | .image-manager-content { |
| ... | @@ -128,6 +127,12 @@ | ... | @@ -128,6 +127,12 @@ |
| 128 | flex-direction: column; | 127 | flex-direction: column; |
| 129 | height: 100%; | 128 | height: 100%; |
| 130 | flex: 1; | 129 | flex: 1; |
| 130 | + .container { | ||
| 131 | + width: 100%; | ||
| 132 | + } | ||
| 133 | + .full-tab { | ||
| 134 | + text-align: center; | ||
| 135 | + } | ||
| 131 | } | 136 | } |
| 132 | 137 | ||
| 133 | // Dropzone | 138 | // Dropzone | ... | ... |
| ... | @@ -176,4 +176,29 @@ $btt-size: 40px; | ... | @@ -176,4 +176,29 @@ $btt-size: 40px; |
| 176 | position: relative; | 176 | position: relative; |
| 177 | top: -5px; | 177 | top: -5px; |
| 178 | } | 178 | } |
| 179 | +} | ||
| 180 | + | ||
| 181 | +.contained-search-box { | ||
| 182 | + display: flex; | ||
| 183 | + input, button { | ||
| 184 | + border-radius: 0; | ||
| 185 | + border: 1px solid #DDD; | ||
| 186 | + margin-left: -1px; | ||
| 187 | + } | ||
| 188 | + input { | ||
| 189 | + flex: 5; | ||
| 190 | + &:focus, &:active { | ||
| 191 | + outline: 0; | ||
| 192 | + } | ||
| 193 | + } | ||
| 194 | + button { | ||
| 195 | + width: 60px; | ||
| 196 | + } | ||
| 197 | + button i { | ||
| 198 | + padding: 0; | ||
| 199 | + } | ||
| 200 | + button.cancel.active { | ||
| 201 | + background-color: $negative; | ||
| 202 | + color: #EEE; | ||
| 203 | + } | ||
| 179 | } | 204 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -12,7 +12,7 @@ | ... | @@ -12,7 +12,7 @@ |
| 12 | .button-base:hover, .button:hover, input[type="button"]:hover, input[type="submit"]:hover, .button:focus { | 12 | .button-base:hover, .button:hover, input[type="button"]:hover, input[type="submit"]:hover, .button:focus { |
| 13 | background-color: {{ Setting::get('app-color') }}; | 13 | background-color: {{ Setting::get('app-color') }}; |
| 14 | } | 14 | } |
| 15 | - .setting-nav a.selected { | 15 | + .nav-tabs a.selected, .nav-tabs .tab-item.selected { |
| 16 | border-bottom-color: {{ Setting::get('app-color') }}; | 16 | border-bottom-color: {{ Setting::get('app-color') }}; |
| 17 | } | 17 | } |
| 18 | p.primary:hover, p .primary:hover, span.primary:hover, .text-primary:hover, a, a:hover, a:focus { | 18 | p.primary:hover, p .primary:hover, span.primary:hover, .text-primary:hover, a, a:hover, a:focus { | ... | ... |
| ... | @@ -3,6 +3,20 @@ | ... | @@ -3,6 +3,20 @@ |
| 3 | <div class="image-manager-body" ng-click="$event.stopPropagation()"> | 3 | <div class="image-manager-body" ng-click="$event.stopPropagation()"> |
| 4 | 4 | ||
| 5 | <div class="image-manager-content"> | 5 | <div class="image-manager-content"> |
| 6 | + <div ng-if="imageType === 'gallery'" class="container"> | ||
| 7 | + <div class="image-manager-header row faded-small nav-tabs"> | ||
| 8 | + <div class="col-xs-4 tab-item" title="View all images" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> All</div> | ||
| 9 | + <div class="col-xs-4 tab-item" title="View images uploaded to this book" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> Book</div> | ||
| 10 | + <div class="col-xs-4 tab-item" title="View images uploaded to this page" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> Page</div> | ||
| 11 | + </div> | ||
| 12 | + </div> | ||
| 13 | + <div ng-show="view === 'all'" > | ||
| 14 | + <form ng-submit="searchImages()" class="contained-search-box"> | ||
| 15 | + <input type="text" placeholder="Search by image name" ng-model="searchTerm"> | ||
| 16 | + <button ng-class="{active: searching}" title="Clear Search" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button> | ||
| 17 | + <button title="Search" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button> | ||
| 18 | + </form> | ||
| 19 | + </div> | ||
| 6 | <div class="image-manager-list"> | 20 | <div class="image-manager-list"> |
| 7 | <div ng-repeat="image in images"> | 21 | <div ng-repeat="image in images"> |
| 8 | <div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}" | 22 | <div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}" | ... | ... |
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | <div class="faded-small toolbar"> | 2 | <div class="faded-small toolbar"> |
| 3 | <div class="container"> | 3 | <div class="container"> |
| 4 | <div class="row"> | 4 | <div class="row"> |
| 5 | - <div class="col-md-12 setting-nav"> | 5 | + <div class="col-md-12 setting-nav nav-tabs"> |
| 6 | <a href="/settings" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>Settings</a> | 6 | <a href="/settings" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>Settings</a> |
| 7 | <a href="/settings/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a> | 7 | <a href="/settings/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a> |
| 8 | <a href="/settings/roles" @if($selected == 'roles') class="selected text-button" @endif><i class="zmdi zmdi-lock-open"></i>Roles</a> | 8 | <a href="/settings/roles" @if($selected == 'roles') class="selected text-button" @endif><i class="zmdi zmdi-lock-open"></i>Roles</a> | ... | ... |
-
Please register or sign in to post a comment