Dan Brown

Created solution for JS translations

Also tidied up existing components and JS
...@@ -117,7 +117,7 @@ class AttachmentController extends Controller ...@@ -117,7 +117,7 @@ class AttachmentController extends Controller
117 } 117 }
118 118
119 $attachment = $this->attachmentService->updateFile($attachment, $request->all()); 119 $attachment = $this->attachmentService->updateFile($attachment, $request->all());
120 - return $this->jsonSuccess($attachment, trans('entities.attachments_updated_success')); 120 + return response()->json($attachment);
121 } 121 }
122 122
123 /** 123 /**
......
...@@ -118,17 +118,6 @@ abstract class Controller extends BaseController ...@@ -118,17 +118,6 @@ abstract class Controller extends BaseController
118 } 118 }
119 119
120 /** 120 /**
121 - * Send a json respons with a message attached as a header.
122 - * @param $data
123 - * @param string $successMessage
124 - * @return $this
125 - */
126 - protected function jsonSuccess($data, $successMessage = "")
127 - {
128 - return response()->json($data)->header('message-success', $successMessage);
129 - }
130 -
131 - /**
132 * Send back a json error message. 121 * Send back a json error message.
133 * @param string $messageText 122 * @param string $messageText
134 * @param int $statusCode 123 * @param int $statusCode
......
...@@ -43,4 +43,39 @@ class HomeController extends Controller ...@@ -43,4 +43,39 @@ class HomeController extends Controller
43 ]); 43 ]);
44 } 44 }
45 45
46 + /**
47 + * Get a js representation of the current translations
48 + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
49 + */
50 + public function getTranslations() {
51 + $locale = trans()->getLocale();
52 + $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale;
53 + if (cache()->has($cacheKey) && config('app.env') !== 'development') {
54 + $resp = cache($cacheKey);
55 + } else {
56 + $translations = [
57 + // Get only translations which might be used in JS
58 + 'common' => trans('common'),
59 + 'components' => trans('components'),
60 + 'entities' => trans('entities'),
61 + 'errors' => trans('errors')
62 + ];
63 + if ($locale !== 'en') {
64 + $enTrans = [
65 + 'common' => trans('common', [], null, 'en'),
66 + 'components' => trans('components', [], null, 'en'),
67 + 'entities' => trans('entities', [], null, 'en'),
68 + 'errors' => trans('errors', [], null, 'en')
69 + ];
70 + $translations = array_replace_recursive($enTrans, $translations);
71 + }
72 + $resp = 'window.translations = ' . json_encode($translations);
73 + cache()->put($cacheKey, $resp, 120);
74 + }
75 +
76 + return response($resp, 200, [
77 + 'Content-Type' => 'application/javascript'
78 + ]);
79 + }
80 +
46 } 81 }
......
...@@ -64,7 +64,7 @@ export default function (ngApp, events) { ...@@ -64,7 +64,7 @@ export default function (ngApp, events) {
64 $scope.$apply(() => { 64 $scope.$apply(() => {
65 $scope.images.unshift(data); 65 $scope.images.unshift(data);
66 }); 66 });
67 - events.emit('success', 'Image uploaded'); 67 + events.emit('success', trans('components.image_upload_success'));
68 }; 68 };
69 69
70 /** 70 /**
...@@ -151,10 +151,9 @@ export default function (ngApp, events) { ...@@ -151,10 +151,9 @@ export default function (ngApp, events) {
151 if ($scope.searching) components['term'] = $scope.searchTerm; 151 if ($scope.searching) components['term'] = $scope.searchTerm;
152 152
153 153
154 - let urlQueryString = Object.keys(components).map((key) => { 154 + url += Object.keys(components).map((key) => {
155 return key + '=' + encodeURIComponent(components[key]); 155 return key + '=' + encodeURIComponent(components[key]);
156 }).join('&'); 156 }).join('&');
157 - url += urlQueryString;
158 157
159 $http.get(url).then((response) => { 158 $http.get(url).then((response) => {
160 $scope.images = $scope.images.concat(response.data.images); 159 $scope.images = $scope.images.concat(response.data.images);
...@@ -209,7 +208,7 @@ export default function (ngApp, events) { ...@@ -209,7 +208,7 @@ export default function (ngApp, events) {
209 event.preventDefault(); 208 event.preventDefault();
210 let url = window.baseUrl('/images/update/' + $scope.selectedImage.id); 209 let url = window.baseUrl('/images/update/' + $scope.selectedImage.id);
211 $http.put(url, this.selectedImage).then(response => { 210 $http.put(url, this.selectedImage).then(response => {
212 - events.emit('success', 'Image details updated'); 211 + events.emit('success', trans('components.image_update_success'));
213 }, (response) => { 212 }, (response) => {
214 if (response.status === 422) { 213 if (response.status === 422) {
215 let errors = response.data; 214 let errors = response.data;
...@@ -238,7 +237,7 @@ export default function (ngApp, events) { ...@@ -238,7 +237,7 @@ export default function (ngApp, events) {
238 $http.delete(url).then((response) => { 237 $http.delete(url).then((response) => {
239 $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1); 238 $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
240 $scope.selectedImage = false; 239 $scope.selectedImage = false;
241 - events.emit('success', 'Image successfully deleted'); 240 + events.emit('success', trans('components.image_delete_success'));
242 }, (response) => { 241 }, (response) => {
243 // Pages failure 242 // Pages failure
244 if (response.status === 400) { 243 if (response.status === 400) {
...@@ -309,9 +308,9 @@ export default function (ngApp, events) { ...@@ -309,9 +308,9 @@ export default function (ngApp, events) {
309 308
310 // Set initial header draft text 309 // Set initial header draft text
311 if ($scope.isUpdateDraft || $scope.isNewPageDraft) { 310 if ($scope.isUpdateDraft || $scope.isNewPageDraft) {
312 - $scope.draftText = 'Editing Draft' 311 + $scope.draftText = trans('entities.pages_editing_draft');
313 } else { 312 } else {
314 - $scope.draftText = 'Editing Page' 313 + $scope.draftText = trans('entities.pages_editing_page');
315 } 314 }
316 315
317 let autoSave = false; 316 let autoSave = false;
...@@ -388,7 +387,7 @@ export default function (ngApp, events) { ...@@ -388,7 +387,7 @@ export default function (ngApp, events) {
388 lastSave = Date.now(); 387 lastSave = Date.now();
389 }, errorRes => { 388 }, errorRes => {
390 if (draftErroring) return; 389 if (draftErroring) return;
391 - events.emit('error', 'Failed to save draft. Ensure you have internet connection before saving this page.') 390 + events.emit('error', trans('errors.page_draft_autosave_fail'));
392 draftErroring = true; 391 draftErroring = true;
393 }); 392 });
394 } 393 }
...@@ -421,7 +420,7 @@ export default function (ngApp, events) { ...@@ -421,7 +420,7 @@ export default function (ngApp, events) {
421 let url = window.baseUrl('/ajax/page/' + pageId); 420 let url = window.baseUrl('/ajax/page/' + pageId);
422 $http.get(url).then((responseData) => { 421 $http.get(url).then((responseData) => {
423 if (autoSave) $interval.cancel(autoSave); 422 if (autoSave) $interval.cancel(autoSave);
424 - $scope.draftText = 'Editing Page'; 423 + $scope.draftText = trans('entities.pages_editing_page');
425 $scope.isUpdateDraft = false; 424 $scope.isUpdateDraft = false;
426 $scope.$broadcast('html-update', responseData.data.html); 425 $scope.$broadcast('html-update', responseData.data.html);
427 $scope.$broadcast('markdown-update', responseData.data.markdown || responseData.data.html); 426 $scope.$broadcast('markdown-update', responseData.data.markdown || responseData.data.html);
...@@ -429,7 +428,7 @@ export default function (ngApp, events) { ...@@ -429,7 +428,7 @@ export default function (ngApp, events) {
429 $timeout(() => { 428 $timeout(() => {
430 startAutoSave(); 429 startAutoSave();
431 }, 1000); 430 }, 1000);
432 - events.emit('success', 'Draft discarded, The editor has been updated with the current page content'); 431 + events.emit('success', trans('entities.pages_draft_discarded'));
433 }); 432 });
434 }; 433 };
435 434
...@@ -594,7 +593,7 @@ export default function (ngApp, events) { ...@@ -594,7 +593,7 @@ export default function (ngApp, events) {
594 $scope.$apply(() => { 593 $scope.$apply(() => {
595 $scope.files.push(data); 594 $scope.files.push(data);
596 }); 595 });
597 - events.emit('success', 'File uploaded'); 596 + events.emit('success', trans('entities.attachments_file_uploaded'));
598 }; 597 };
599 598
600 /** 599 /**
...@@ -612,7 +611,7 @@ export default function (ngApp, events) { ...@@ -612,7 +611,7 @@ export default function (ngApp, events) {
612 data.link = ''; 611 data.link = '';
613 } 612 }
614 }); 613 });
615 - events.emit('success', 'File updated'); 614 + events.emit('success', trans('entities.attachments_file_updated'));
616 }; 615 };
617 616
618 /** 617 /**
...@@ -638,7 +637,7 @@ export default function (ngApp, events) { ...@@ -638,7 +637,7 @@ export default function (ngApp, events) {
638 file.uploaded_to = pageId; 637 file.uploaded_to = pageId;
639 $http.post(window.baseUrl('/attachments/link'), file).then(resp => { 638 $http.post(window.baseUrl('/attachments/link'), file).then(resp => {
640 $scope.files.push(resp.data); 639 $scope.files.push(resp.data);
641 - events.emit('success', 'Link attached'); 640 + events.emit('success', trans('entities.attachments_link_attached'));
642 $scope.file = getCleanFile(); 641 $scope.file = getCleanFile();
643 }, checkError('link')); 642 }, checkError('link'));
644 }; 643 };
...@@ -672,7 +671,7 @@ export default function (ngApp, events) { ...@@ -672,7 +671,7 @@ export default function (ngApp, events) {
672 $scope.editFile.link = ''; 671 $scope.editFile.link = '';
673 } 672 }
674 $scope.editFile = false; 673 $scope.editFile = false;
675 - events.emit('success', resp.headers('message-success')); 674 + events.emit('success', trans('entities.attachments_updated_success'));
676 }, checkError('edit')); 675 }, checkError('edit'));
677 }; 676 };
678 677
......
...@@ -17,6 +17,12 @@ window.baseUrl = function(path) { ...@@ -17,6 +17,12 @@ window.baseUrl = function(path) {
17 17
18 let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']); 18 let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
19 19
20 +// Translation setup
21 +// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
22 +import Translations from "./translations"
23 +let translator = new Translations(window.translations);
24 +window.trans = translator.get.bind(translator);
25 +
20 // Global Event System 26 // Global Event System
21 class EventManager { 27 class EventManager {
22 constructor() { 28 constructor() {
...@@ -70,150 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) { ...@@ -70,150 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
70 }); 76 });
71 77
72 // Global jQuery Elements 78 // Global jQuery Elements
73 -$(function () { 79 +let notifications = $('.notification');
74 - 80 +let successNotification = notifications.filter('.pos');
75 - let notifications = $('.notification'); 81 +let errorNotification = notifications.filter('.neg');
76 - let successNotification = notifications.filter('.pos'); 82 +let warningNotification = notifications.filter('.warning');
77 - let errorNotification = notifications.filter('.neg'); 83 +// Notification Events
78 - let warningNotification = notifications.filter('.warning'); 84 +window.Events.listen('success', function (text) {
79 - // Notification Events 85 + successNotification.hide();
80 - window.Events.listen('success', function (text) { 86 + successNotification.find('span').text(text);
81 - successNotification.hide(); 87 + setTimeout(() => {
82 - successNotification.find('span').text(text); 88 + successNotification.show();
83 - setTimeout(() => { 89 + }, 1);
84 - successNotification.show(); 90 +});
85 - }, 1); 91 +window.Events.listen('warning', function (text) {
86 - }); 92 + warningNotification.find('span').text(text);
87 - window.Events.listen('warning', function (text) { 93 + warningNotification.show();
88 - warningNotification.find('span').text(text); 94 +});
89 - warningNotification.show(); 95 +window.Events.listen('error', function (text) {
90 - }); 96 + errorNotification.find('span').text(text);
91 - window.Events.listen('error', function (text) { 97 + errorNotification.show();
92 - errorNotification.find('span').text(text); 98 +});
93 - errorNotification.show();
94 - });
95 -
96 - // Notification hiding
97 - notifications.click(function () {
98 - $(this).fadeOut(100);
99 - });
100 -
101 - // Chapter page list toggles
102 - $('.chapter-toggle').click(function (e) {
103 - e.preventDefault();
104 - $(this).toggleClass('open');
105 - $(this).closest('.chapter').find('.inset-list').slideToggle(180);
106 - });
107 -
108 - // Back to top button
109 - $('#back-to-top').click(function() {
110 - $('#header').smoothScrollTo();
111 - });
112 - let scrollTopShowing = false;
113 - let scrollTop = document.getElementById('back-to-top');
114 - let scrollTopBreakpoint = 1200;
115 - window.addEventListener('scroll', function() {
116 - let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
117 - if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
118 - scrollTop.style.display = 'block';
119 - scrollTopShowing = true;
120 - setTimeout(() => {
121 - scrollTop.style.opacity = 0.4;
122 - }, 1);
123 - } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
124 - scrollTop.style.opacity = 0;
125 - scrollTopShowing = false;
126 - setTimeout(() => {
127 - scrollTop.style.display = 'none';
128 - }, 500);
129 - }
130 - });
131 -
132 - // Common jQuery actions
133 - $('[data-action="expand-entity-list-details"]').click(function() {
134 - $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
135 - });
136 -
137 - // Popup close
138 - $('.popup-close').click(function() {
139 - $(this).closest('.overlay').fadeOut(240);
140 - });
141 - $('.overlay').click(function(event) {
142 - if (!$(event.target).hasClass('overlay')) return;
143 - $(this).fadeOut(240);
144 - });
145 -
146 - // Prevent markdown display link click redirect
147 - $('.markdown-display').on('click', 'a', function(event) {
148 - event.preventDefault();
149 - window.open($(this).attr('href'));
150 - });
151 -
152 - // Toggle Switches
153 - let $switches = $('[toggle-switch]');
154 - if ($switches.length > 0) {
155 - $switches.click(event => {
156 - let $switch = $(event.target);
157 - let input = $switch.find('input').first()[0];
158 - let checked = input.value !== 'true';
159 - input.value = checked ? 'true' : 'false';
160 - $switch.toggleClass('active', checked);
161 - });
162 - }
163 -
164 - // Image pickers
165 - $('.image-picker').on('click', 'button', event => {
166 - let button = event.target;
167 - let picker = $(button).closest('.image-picker')[0];
168 - let action = button.getAttribute('data-action');
169 - let resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
170 - let usingIds = picker.getAttribute('data-current-id') !== '';
171 - let resizeCrop = picker.getAttribute('data-resize-crop') !== '';
172 - let imageElem = picker.querySelector('img');
173 - let input = picker.querySelector('input');
174 -
175 - function setImage(image) {
176 -
177 - if (image === 'none') {
178 - imageElem.src = picker.getAttribute('data-default-image');
179 - imageElem.classList.add('none');
180 - input.value = 'none';
181 - return;
182 - }
183 -
184 - imageElem.src = image.url;
185 - input.value = usingIds ? image.id : image.url;
186 - imageElem.classList.remove('none');
187 - }
188 99
189 - if (action === 'show-image-manager') { 100 +// Notification hiding
190 - window.ImageManager.showExternal((image) => { 101 +notifications.click(function () {
191 - if (!resize) { 102 + $(this).fadeOut(100);
192 - setImage(image); 103 +});
193 - return;
194 - }
195 - let requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
196 - $.get(window.baseUrl(requestString), resp => {
197 - image.url = resp.url;
198 - setImage(image);
199 - });
200 - });
201 - } else if (action === 'reset-image') {
202 - setImage({id: 0, url: picker.getAttribute('data-default-image')});
203 - } else if (action === 'remove-image') {
204 - setImage('none');
205 - }
206 104
207 - }); 105 +// Chapter page list toggles
106 +$('.chapter-toggle').click(function (e) {
107 + e.preventDefault();
108 + $(this).toggleClass('open');
109 + $(this).closest('.chapter').find('.inset-list').slideToggle(180);
110 +});
208 111
209 - // Detect IE for css 112 +// Back to top button
210 - if(navigator.userAgent.indexOf('MSIE')!==-1 113 +$('#back-to-top').click(function() {
211 - || navigator.appVersion.indexOf('Trident/') > 0 114 + $('#header').smoothScrollTo();
212 - || navigator.userAgent.indexOf('Safari') !== -1){ 115 +});
213 - $('body').addClass('flexbox-support'); 116 +let scrollTopShowing = false;
117 +let scrollTop = document.getElementById('back-to-top');
118 +let scrollTopBreakpoint = 1200;
119 +window.addEventListener('scroll', function() {
120 + let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
121 + if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
122 + scrollTop.style.display = 'block';
123 + scrollTopShowing = true;
124 + setTimeout(() => {
125 + scrollTop.style.opacity = 0.4;
126 + }, 1);
127 + } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
128 + scrollTop.style.opacity = 0;
129 + scrollTopShowing = false;
130 + setTimeout(() => {
131 + scrollTop.style.display = 'none';
132 + }, 500);
214 } 133 }
134 +});
215 135
136 +// Common jQuery actions
137 +$('[data-action="expand-entity-list-details"]').click(function() {
138 + $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
216 }); 139 });
217 140
141 +// Popup close
142 +$('.popup-close').click(function() {
143 + $(this).closest('.overlay').fadeOut(240);
144 +});
145 +$('.overlay').click(function(event) {
146 + if (!$(event.target).hasClass('overlay')) return;
147 + $(this).fadeOut(240);
148 +});
149 +
150 +// Detect IE for css
151 +if(navigator.userAgent.indexOf('MSIE')!==-1
152 + || navigator.appVersion.indexOf('Trident/') > 0
153 + || navigator.userAgent.indexOf('Safari') !== -1){
154 + $('body').addClass('flexbox-support');
155 +}
156 +
218 // Page specific items 157 // Page specific items
219 import "./pages/page-show"; 158 import "./pages/page-show";
......
1 +/**
2 + * Translation Manager
3 + * Handles the JavaScript side of translating strings
4 + * in a way which fits with Laravel.
5 + */
6 +class Translator {
7 +
8 + /**
9 + * Create an instance, Passing in the required translations
10 + * @param translations
11 + */
12 + constructor(translations) {
13 + this.store = translations;
14 + }
15 +
16 + /**
17 + * Get a translation, Same format as laravel's 'trans' helper
18 + * @param key
19 + * @param replacements
20 + * @returns {*}
21 + */
22 + get(key, replacements) {
23 + let splitKey = key.split('.');
24 + let value = splitKey.reduce((a, b) => {
25 + return a != undefined ? a[b] : a;
26 + }, this.store);
27 +
28 + if (value === undefined) {
29 + console.log(`Translation with key "${key}" does not exist`);
30 + value = key;
31 + }
32 +
33 + if (replacements === undefined) return value;
34 +
35 + let replaceMatches = value.match(/:([\S]+)/g);
36 + if (replaceMatches === null) return value;
37 + replaceMatches.forEach(match => {
38 + let key = match.substring(1);
39 + if (typeof replacements[key] === 'undefined') return;
40 + value = value.replace(match, replacements[key]);
41 + });
42 + return value;
43 + }
44 +
45 +}
46 +
47 +export default Translator
...@@ -35,6 +35,12 @@ table.table { ...@@ -35,6 +35,12 @@ table.table {
35 tr:hover { 35 tr:hover {
36 background-color: #EEE; 36 background-color: #EEE;
37 } 37 }
38 + .text-right {
39 + text-align: right;
40 + }
41 + .text-center {
42 + text-align: center;
43 + }
38 } 44 }
39 45
40 table.no-style { 46 table.no-style {
......
...@@ -16,7 +16,7 @@ return [ ...@@ -16,7 +16,7 @@ return [
16 'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.', 16 'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.',
17 'app_name_header' => 'Anwendungsname im Header anzeigen?', 17 'app_name_header' => 'Anwendungsname im Header anzeigen?',
18 'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?', 18 'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?',
19 - 'app_secure_images' => 'Erh&oml;hte Sicherheit f&uuml;r Bilduploads aktivieren?', 19 + 'app_secure_images' => 'Erh&ouml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
20 'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.', 20 'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.',
21 'app_editor' => 'Seiteneditor', 21 'app_editor' => 'Seiteneditor',
22 'app_editor_desc' => 'W&auml;hlen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.', 22 'app_editor_desc' => 'W&auml;hlen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
......
...@@ -4,18 +4,21 @@ return [ ...@@ -4,18 +4,21 @@ return [
4 /** 4 /**
5 * Image Manager 5 * Image Manager
6 */ 6 */
7 - 'imagem_select' => 'Image Select', 7 + 'image_select' => 'Image Select',
8 - 'imagem_all' => 'All', 8 + 'image_all' => 'All',
9 - 'imagem_all_title' => 'View all images', 9 + 'image_all_title' => 'View all images',
10 - 'imagem_book_title' => 'View images uploaded to this book', 10 + 'image_book_title' => 'View images uploaded to this book',
11 - 'imagem_page_title' => 'View images uploaded to this page', 11 + 'image_page_title' => 'View images uploaded to this page',
12 - 'imagem_search_hint' => 'Search by image name', 12 + 'image_search_hint' => 'Search by image name',
13 - 'imagem_uploaded' => 'Uploaded :uploadedDate', 13 + 'image_uploaded' => 'Uploaded :uploadedDate',
14 - 'imagem_load_more' => 'Load More', 14 + 'image_load_more' => 'Load More',
15 - 'imagem_image_name' => 'Image Name', 15 + 'image_image_name' => 'Image Name',
16 - 'imagem_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.', 16 + 'image_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.',
17 - 'imagem_select_image' => 'Select Image', 17 + 'image_select_image' => 'Select Image',
18 - 'imagem_dropzone' => 'Drop images or click here to upload', 18 + 'image_dropzone' => 'Drop images or click here to upload',
19 'images_deleted' => 'Images Deleted', 19 'images_deleted' => 'Images Deleted',
20 'image_preview' => 'Image Preview', 20 'image_preview' => 'Image Preview',
21 + 'image_upload_success' => 'Image uploaded successfully',
22 + 'image_update_success' => 'Image details successfully updated',
23 + 'image_delete_success' => 'Image successfully deleted'
21 ]; 24 ];
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -129,6 +129,8 @@ return [ ...@@ -129,6 +129,8 @@ return [
129 'pages_edit_toggle_header' => 'Toggle header', 129 'pages_edit_toggle_header' => 'Toggle header',
130 'pages_edit_save_draft' => 'Save Draft', 130 'pages_edit_save_draft' => 'Save Draft',
131 'pages_edit_draft' => 'Edit Page Draft', 131 'pages_edit_draft' => 'Edit Page Draft',
132 + 'pages_editing_draft' => 'Editing Draft',
133 + 'pages_editing_page' => 'Editing Page',
132 'pages_edit_draft_save_at' => 'Draft saved at ', 134 'pages_edit_draft_save_at' => 'Draft saved at ',
133 'pages_edit_delete_draft' => 'Delete Draft', 135 'pages_edit_delete_draft' => 'Delete Draft',
134 'pages_edit_discard_draft' => 'Discard Draft', 136 'pages_edit_discard_draft' => 'Discard Draft',
...@@ -175,6 +177,7 @@ return [ ...@@ -175,6 +177,7 @@ return [
175 'time_b' => 'in the last :minCount minutes', 177 'time_b' => 'in the last :minCount minutes',
176 'message' => ':start :time. Take care not to overwrite each other\'s updates!', 178 'message' => ':start :time. Take care not to overwrite each other\'s updates!',
177 ], 179 ],
180 + 'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
178 181
179 /** 182 /**
180 * Editor sidebar 183 * Editor sidebar
...@@ -207,6 +210,9 @@ return [ ...@@ -207,6 +210,9 @@ return [
207 'attachments_order_updated' => 'Attachment order updated', 210 'attachments_order_updated' => 'Attachment order updated',
208 'attachments_updated_success' => 'Attachment details updated', 211 'attachments_updated_success' => 'Attachment details updated',
209 'attachments_deleted' => 'Attachment deleted', 212 'attachments_deleted' => 'Attachment deleted',
213 + 'attachments_file_uploaded' => 'File successfully uploaded',
214 + 'attachments_file_updated' => 'File successfully updated',
215 + 'attachments_link_attached' => 'Link successfully attached to page',
210 216
211 /** 217 /**
212 * Profile View 218 * Profile View
......
...@@ -33,10 +33,15 @@ return [ ...@@ -33,10 +33,15 @@ return [
33 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', 33 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
34 'cannot_get_image_from_url' => 'Cannot get image from :url', 34 'cannot_get_image_from_url' => 'Cannot get image from :url',
35 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', 35 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
36 + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
37 + 'image_upload_error' => 'An error occurred uploading the image',
36 38
37 // Attachments 39 // Attachments
38 'attachment_page_mismatch' => 'Page mismatch during attachment update', 40 'attachment_page_mismatch' => 'Page mismatch during attachment update',
39 41
42 + // Pages
43 + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
44 +
40 // Entities 45 // Entities
41 'entity_not_found' => 'Entity not found', 46 'entity_not_found' => 'Entity not found',
42 'book_not_found' => 'Book not found', 47 'book_not_found' => 'Book not found',
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 <!-- Scripts --> 17 <!-- Scripts -->
18 <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script> 18 <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script>
19 <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script> 19 <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script>
20 + <script src="{{ baseUrl('/translations.js') }}"></script>
20 21
21 @yield('head') 22 @yield('head')
22 23
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
19 {!! csrf_field() !!} 19 {!! csrf_field() !!}
20 <input type="hidden" name="_method" value="PUT"> 20 <input type="hidden" name="_method" value="PUT">
21 21
22 - @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book']) 22 + @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
23 23
24 <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a> 24 <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
25 <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button> 25 <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 <div class="popup-title">{{ trans('entities.entity_select') }}</div> 5 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
6 <button type="button" class="corner-button neg button popup-close">x</button> 6 <button type="button" class="corner-button neg button popup-close">x</button>
7 </div> 7 </div>
8 - @include('partials/entity-selector', ['name' => 'entity-selector']) 8 + @include('components.entity-selector', ['name' => 'entity-selector'])
9 <div class="popup-footer"> 9 <div class="popup-footer">
10 <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button> 10 <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button>
11 </div> 11 </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}"> 2 <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}">
3 <input type="hidden" entity-selector-input name="{{$name}}" value=""> 3 <input type="hidden" entity-selector-input name="{{$name}}" value="">
4 <input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()"> 4 <input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()">
5 - <div class="text-center loading" ng-show="loading">@include('partials/loading-icon')</div> 5 + <div class="text-center loading" ng-show="loading">@include('partials.loading-icon')</div>
6 <div ng-show="!loading" ng-bind-html="entityResults"></div> 6 <div ng-show="!loading" ng-bind-html="entityResults"></div>
7 </div> 7 </div>
8 </div> 8 </div>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 <div class="popup-body" ng-click="$event.stopPropagation()"> 3 <div class="popup-body" ng-click="$event.stopPropagation()">
4 4
5 <div class="popup-header primary-background"> 5 <div class="popup-header primary-background">
6 - <div class="popup-title">{{ trans('components.imagem_select') }}</div> 6 + <div class="popup-title">{{ trans('components.image_select') }}</div>
7 <button class="popup-close neg corner-button button">x</button> 7 <button class="popup-close neg corner-button button">x</button>
8 </div> 8 </div>
9 9
...@@ -12,14 +12,14 @@ ...@@ -12,14 +12,14 @@
12 <div class="image-manager-content"> 12 <div class="image-manager-content">
13 <div ng-if="imageType === 'gallery'" class="container"> 13 <div ng-if="imageType === 'gallery'" class="container">
14 <div class="image-manager-header row faded-small nav-tabs"> 14 <div class="image-manager-header row faded-small nav-tabs">
15 - <div class="col-xs-4 tab-item" title="{{ trans('components.imagem_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.imagem_all') }}</div> 15 + <div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
16 - <div class="col-xs-4 tab-item" title="{{ trans('components.imagem_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div> 16 + <div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
17 - <div class="col-xs-4 tab-item" title="{{ trans('components.imagem_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div> 17 + <div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
18 </div> 18 </div>
19 </div> 19 </div>
20 <div ng-show="view === 'all'" > 20 <div ng-show="view === 'all'" >
21 <form ng-submit="searchImages()" class="contained-search-box"> 21 <form ng-submit="searchImages()" class="contained-search-box">
22 - <input type="text" placeholder="{{ trans('components.imagem_search_hint') }}" ng-model="searchTerm"> 22 + <input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm">
23 <button ng-class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button> 23 <button ng-class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
24 <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button> 24 <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
25 </form> 25 </form>
...@@ -31,11 +31,11 @@ ...@@ -31,11 +31,11 @@
31 <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}"> 31 <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
32 <div class="image-meta"> 32 <div class="image-meta">
33 <span class="name" ng-bind="image.name"></span> 33 <span class="name" ng-bind="image.name"></span>
34 - <span class="date">{{ trans('components.imagem_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span> 34 + <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
35 </div> 35 </div>
36 </div> 36 </div>
37 </div> 37 </div>
38 - <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.imagem_load_more') }}</div> 38 + <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div>
39 </div> 39 </div>
40 </div> 40 </div>
41 41
...@@ -51,14 +51,14 @@ ...@@ -51,14 +51,14 @@
51 </a> 51 </a>
52 </div> 52 </div>
53 <div class="form-group"> 53 <div class="form-group">
54 - <label for="name">{{ trans('components.imagem_image_name') }}</label> 54 + <label for="name">{{ trans('components.image_image_name') }}</label>
55 <input type="text" id="name" name="name" ng-model="selectedImage.name"> 55 <input type="text" id="name" name="name" ng-model="selectedImage.name">
56 </div> 56 </div>
57 </form> 57 </form>
58 58
59 <div ng-show="dependantPages"> 59 <div ng-show="dependantPages">
60 <p class="text-neg text-small"> 60 <p class="text-neg text-small">
61 - {{ trans('components.imagem_delete_confirm') }} 61 + {{ trans('components.image_delete_confirm') }}
62 </p> 62 </p>
63 <ul class="text-neg"> 63 <ul class="text-neg">
64 <li ng-repeat="page in dependantPages"> 64 <li ng-repeat="page in dependantPages">
...@@ -72,13 +72,13 @@ ...@@ -72,13 +72,13 @@
72 <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button> 72 <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
73 </form> 73 </form>
74 <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()"> 74 <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
75 - <i class="zmdi zmdi-square-right"></i>{{ trans('components.imagem_select_image') }} 75 + <i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
76 </button> 76 </button>
77 </div> 77 </div>
78 78
79 </div> 79 </div>
80 80
81 - <drop-zone message="{{ trans('components.imagem_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone> 81 + <drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
82 82
83 83
84 </div> 84 </div>
......
1 -<div class="image-picker" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}"> 1 +<div class="image-picker" image-picker="{{$name}}" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}">
2 2
3 <div> 3 <div>
4 <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}"> 4 <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
5 </div> 5 </div>
6 6
7 - <button class="button" type="button" data-action="show-image-manager">{{ trans('components.imagem_select_image') }}</button> 7 + <button class="button" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button>
8 <br> 8 <br>
9 <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button> 9 <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
10 10
...@@ -13,5 +13,54 @@ ...@@ -13,5 +13,54 @@
13 <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button> 13 <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
14 @endif 14 @endif
15 15
16 - <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{$currentId or $currentImage or ''}}">
17 -</div>
...\ No newline at end of file ...\ No newline at end of file
16 + <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{ isset($currentId) && ($currentId !== '' && $currentId !== false) ? $currentId : $currentImage}}">
17 +</div>
18 +
19 +<script>
20 + (function(){
21 +
22 + var picker = document.querySelector('[image-picker="{{$name}}"]');
23 + picker.addEventListener('click', function(event) {
24 + if (event.target.nodeName.toLowerCase() !== 'button') return;
25 + var button = event.target;
26 + var action = button.getAttribute('data-action');
27 + var resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
28 + var usingIds = picker.getAttribute('data-current-id') !== '';
29 + var resizeCrop = picker.getAttribute('data-resize-crop') !== '';
30 + var imageElem = picker.querySelector('img');
31 + var input = picker.querySelector('input');
32 +
33 + function setImage(image) {
34 + if (image === 'none') {
35 + imageElem.src = picker.getAttribute('data-default-image');
36 + imageElem.classList.add('none');
37 + input.value = 'none';
38 + return;
39 + }
40 + imageElem.src = image.url;
41 + input.value = usingIds ? image.id : image.url;
42 + imageElem.classList.remove('none');
43 + }
44 +
45 + if (action === 'show-image-manager') {
46 + window.ImageManager.showExternal((image) => {
47 + if (!resize) {
48 + setImage(image);
49 + return;
50 + }
51 + var requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
52 + $.get(window.baseUrl(requestString), resp => {
53 + image.url = resp.url;
54 + setImage(image);
55 + });
56 + });
57 + } else if (action === 'reset-image') {
58 + setImage({id: 0, url: picker.getAttribute('data-default-image')});
59 + } else if (action === 'remove-image') {
60 + setImage('none');
61 + }
62 +
63 + });
64 +
65 + })();
66 +</script>
...\ No newline at end of file ...\ No newline at end of file
......
1 -<div toggle-switch class="toggle-switch @if($value) active @endif"> 1 +<div toggle-switch="{{$name}}" class="toggle-switch @if($value) active @endif">
2 <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/> 2 <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/>
3 <div class="switch-handle"></div> 3 <div class="switch-handle"></div>
4 -</div>
...\ No newline at end of file ...\ No newline at end of file
4 +</div>
5 +<script>
6 + (function() {
7 + var toggle = document.querySelector('[toggle-switch="{{$name}}"]');
8 + var toggleInput = toggle.querySelector('input');
9 + toggle.onclick = function(event) {
10 + var checked = toggleInput.value !== 'true';
11 + toggleInput.value = checked ? 'true' : 'false';
12 + checked ? toggle.classList.add('active') : toggle.classList.remove('active');
13 + };
14 + })()
15 +</script>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
20 20
21 </div> 21 </div>
22 22
23 - @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id]) 23 + @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
24 - @include('partials/entity-selector-popup') 24 + @include('components.entity-selector-popup')
25 25
26 @stop 26 @stop
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
19 {!! csrf_field() !!} 19 {!! csrf_field() !!}
20 <input type="hidden" name="_method" value="PUT"> 20 <input type="hidden" name="_method" value="PUT">
21 21
22 - @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter']) 22 + @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
23 23
24 <a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a> 24 <a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
25 <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button> 25 <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button>
......
1 -<style> 1 +<style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
2 header, #back-to-top, .primary-background { 2 header, #back-to-top, .primary-background {
3 background-color: {{ setting('app-color') }} !important; 3 background-color: {{ setting('app-color') }} !important;
4 } 4 }
......
...@@ -56,7 +56,8 @@ ...@@ -56,7 +56,8 @@
56 'defaultImage' => baseUrl('/logo.png'), 56 'defaultImage' => baseUrl('/logo.png'),
57 'currentImage' => setting('app-logo'), 57 'currentImage' => setting('app-logo'),
58 'name' => 'setting-app-logo', 58 'name' => 'setting-app-logo',
59 - 'imageClass' => 'logo-image' 59 + 'imageClass' => 'logo-image',
60 + 'currentId' => false
60 ]) 61 ])
61 62
62 </div> 63 </div>
...@@ -125,7 +126,7 @@ ...@@ -125,7 +126,7 @@
125 126
126 </div> 127 </div>
127 128
128 -@include('partials/image-manager', ['imageType' => 'system']) 129 +@include('components.image-manager', ['imageType' => 'system'])
129 130
130 @stop 131 @stop
131 132
...@@ -142,10 +143,16 @@ ...@@ -142,10 +143,16 @@
142 var isEmpty = $.trim($elm.val()).length === 0; 143 var isEmpty = $.trim($elm.val()).length === 0;
143 if (!isEmpty) $elm.val(hexVal); 144 if (!isEmpty) $elm.val(hexVal);
144 $('#setting-app-color-light').val(isEmpty ? '' : rgbLightVal); 145 $('#setting-app-color-light').val(isEmpty ? '' : rgbLightVal);
145 - // Set page elements to provide preview 146 +
146 - $('#header, .image-picker .button').attr('style', 'background-color:'+ hexVal+'!important;'); 147 + var customStyles = document.getElementById('custom-styles');
147 - $('.faded-small').css('background-color', rgbLightVal); 148 + var oldColor = customStyles.getAttribute('data-color');
148 - $('.setting-nav a.selected').css('border-bottom-color', hexVal + '!important'); 149 + var oldColorLight = customStyles.getAttribute('data-color-light');
150 +
151 + customStyles.innerHTML = customStyles.innerHTML.split(oldColor).join(hexVal);
152 + customStyles.innerHTML = customStyles.innerHTML.split(oldColorLight).join(rgbLightVal);
153 +
154 + customStyles.setAttribute('data-color', hexVal);
155 + customStyles.setAttribute('data-color-light', rgbLightVal);
149 } 156 }
150 }); 157 });
151 </script> 158 </script>
......
...@@ -20,13 +20,13 @@ ...@@ -20,13 +20,13 @@
20 <tr> 20 <tr>
21 <th>{{ trans('settings.role_name') }}</th> 21 <th>{{ trans('settings.role_name') }}</th>
22 <th></th> 22 <th></th>
23 - <th class="text-right">{{ trans('settings.users') }}</th> 23 + <th class="text-center">{{ trans('settings.users') }}</th>
24 </tr> 24 </tr>
25 @foreach($roles as $role) 25 @foreach($roles as $role)
26 <tr> 26 <tr>
27 <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td> 27 <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td>
28 <td>{{ $role->description }}</td> 28 <td>{{ $role->description }}</td>
29 - <td class="text-right">{{ $role->users->count() }}</td> 29 + <td class="text-center">{{ $role->users->count() }}</td>
30 </tr> 30 </tr>
31 @endforeach 31 @endforeach
32 </table> 32 </table>
......
...@@ -85,5 +85,5 @@ ...@@ -85,5 +85,5 @@
85 </div> 85 </div>
86 86
87 <p class="margin-top large"><br></p> 87 <p class="margin-top large"><br></p>
88 - @include('partials/image-manager', ['imageType' => 'user']) 88 + @include('components.image-manager', ['imageType' => 'user'])
89 @stop 89 @stop
......
1 <?php 1 <?php
2 2
3 +Route::get('/translations.js', 'HomeController@getTranslations');
4 +
3 // Authenticated routes... 5 // Authenticated routes...
4 Route::group(['middleware' => 'auth'], function () { 6 Route::group(['middleware' => 'auth'], function () {
5 7
......