Dan Brown

Page Attachments - Improved UI, Now initially complete

Closes #62
...@@ -30,7 +30,7 @@ class File extends Ownable ...@@ -30,7 +30,7 @@ class File extends Ownable
30 */ 30 */
31 public function getUrl() 31 public function getUrl()
32 { 32 {
33 - return '/files/' . $this->id; 33 + return baseUrl('/files/' . $this->id);
34 } 34 }
35 35
36 } 36 }
......
...@@ -3,13 +3,11 @@ ...@@ -3,13 +3,11 @@
3 namespace BookStack\Http\Controllers; 3 namespace BookStack\Http\Controllers;
4 4
5 use BookStack\Ownable; 5 use BookStack\Ownable;
6 -use HttpRequestException;
7 use Illuminate\Foundation\Bus\DispatchesJobs; 6 use Illuminate\Foundation\Bus\DispatchesJobs;
8 use Illuminate\Http\Exception\HttpResponseException; 7 use Illuminate\Http\Exception\HttpResponseException;
8 +use Illuminate\Http\Request;
9 use Illuminate\Routing\Controller as BaseController; 9 use Illuminate\Routing\Controller as BaseController;
10 use Illuminate\Foundation\Validation\ValidatesRequests; 10 use Illuminate\Foundation\Validation\ValidatesRequests;
11 -use Illuminate\Support\Facades\Auth;
12 -use Illuminate\Support\Facades\Session;
13 use BookStack\User; 11 use BookStack\User;
14 12
15 abstract class Controller extends BaseController 13 abstract class Controller extends BaseController
...@@ -130,4 +128,22 @@ abstract class Controller extends BaseController ...@@ -130,4 +128,22 @@ abstract class Controller extends BaseController
130 return response()->json(['message' => $messageText], $statusCode); 128 return response()->json(['message' => $messageText], $statusCode);
131 } 129 }
132 130
131 + /**
132 + * Create the response for when a request fails validation.
133 + *
134 + * @param \Illuminate\Http\Request $request
135 + * @param array $errors
136 + * @return \Symfony\Component\HttpFoundation\Response
137 + */
138 + protected function buildFailedValidationResponse(Request $request, array $errors)
139 + {
140 + if ($request->expectsJson()) {
141 + return response()->json(['validation' => $errors], 422);
142 + }
143 +
144 + return redirect()->to($this->getRedirectUrl())
145 + ->withInput($request->input())
146 + ->withErrors($errors, $this->errorBag());
147 + }
148 +
133 } 149 }
......
...@@ -101,8 +101,8 @@ class FileController extends Controller ...@@ -101,8 +101,8 @@ class FileController extends Controller
101 { 101 {
102 $this->validate($request, [ 102 $this->validate($request, [
103 'uploaded_to' => 'required|integer|exists:pages,id', 103 'uploaded_to' => 'required|integer|exists:pages,id',
104 - 'name' => 'string|max:255', 104 + 'name' => 'required|string|min:1|max:255',
105 - 'link' => 'url' 105 + 'link' => 'url|min:1|max:255'
106 ]); 106 ]);
107 107
108 $pageId = $request->get('uploaded_to'); 108 $pageId = $request->get('uploaded_to');
...@@ -129,8 +129,8 @@ class FileController extends Controller ...@@ -129,8 +129,8 @@ class FileController extends Controller
129 { 129 {
130 $this->validate($request, [ 130 $this->validate($request, [
131 'uploaded_to' => 'required|integer|exists:pages,id', 131 'uploaded_to' => 'required|integer|exists:pages,id',
132 - 'name' => 'string|max:255', 132 + 'name' => 'required|string|min:1|max:255',
133 - 'link' => 'url|max:255' 133 + 'link' => 'required|url|min:1|max:255'
134 ]); 134 ]);
135 135
136 $pageId = $request->get('uploaded_to'); 136 $pageId = $request->get('uploaded_to');
......
...@@ -538,6 +538,10 @@ module.exports = function (ngApp, events) { ...@@ -538,6 +538,10 @@ module.exports = function (ngApp, events) {
538 $scope.files = []; 538 $scope.files = [];
539 $scope.editFile = false; 539 $scope.editFile = false;
540 $scope.file = getCleanFile(); 540 $scope.file = getCleanFile();
541 + $scope.errors = {
542 + link: {},
543 + edit: {}
544 + };
541 545
542 function getCleanFile() { 546 function getCleanFile() {
543 return { 547 return {
...@@ -567,7 +571,7 @@ module.exports = function (ngApp, events) { ...@@ -567,7 +571,7 @@ module.exports = function (ngApp, events) {
567 currentOrder = newOrder; 571 currentOrder = newOrder;
568 $http.put(`/files/sort/page/${pageId}`, {files: $scope.files}).then(resp => { 572 $http.put(`/files/sort/page/${pageId}`, {files: $scope.files}).then(resp => {
569 events.emit('success', resp.data.message); 573 events.emit('success', resp.data.message);
570 - }, checkError); 574 + }, checkError('sort'));
571 } 575 }
572 576
573 /** 577 /**
...@@ -587,7 +591,7 @@ module.exports = function (ngApp, events) { ...@@ -587,7 +591,7 @@ module.exports = function (ngApp, events) {
587 $http.get(url).then(resp => { 591 $http.get(url).then(resp => {
588 $scope.files = resp.data; 592 $scope.files = resp.data;
589 currentOrder = resp.data.map(file => {return file.id}).join(':'); 593 currentOrder = resp.data.map(file => {return file.id}).join(':');
590 - }, checkError); 594 + }, checkError('get'));
591 } 595 }
592 getFiles(); 596 getFiles();
593 597
...@@ -599,7 +603,7 @@ module.exports = function (ngApp, events) { ...@@ -599,7 +603,7 @@ module.exports = function (ngApp, events) {
599 */ 603 */
600 $scope.uploadSuccess = function (file, data) { 604 $scope.uploadSuccess = function (file, data) {
601 $scope.$apply(() => { 605 $scope.$apply(() => {
602 - $scope.files.unshift(data); 606 + $scope.files.push(data);
603 }); 607 });
604 events.emit('success', 'File uploaded'); 608 events.emit('success', 'File uploaded');
605 }; 609 };
...@@ -612,10 +616,10 @@ module.exports = function (ngApp, events) { ...@@ -612,10 +616,10 @@ module.exports = function (ngApp, events) {
612 $scope.uploadSuccessUpdate = function (file, data) { 616 $scope.uploadSuccessUpdate = function (file, data) {
613 $scope.$apply(() => { 617 $scope.$apply(() => {
614 let search = filesIndexOf(data); 618 let search = filesIndexOf(data);
615 - if (search !== -1) $scope.files[search] = file; 619 + if (search !== -1) $scope.files[search] = data;
616 620
617 if ($scope.editFile) { 621 if ($scope.editFile) {
618 - $scope.editFile = data; 622 + $scope.editFile = angular.copy(data);
619 data.link = ''; 623 data.link = '';
620 } 624 }
621 }); 625 });
...@@ -627,10 +631,14 @@ module.exports = function (ngApp, events) { ...@@ -627,10 +631,14 @@ module.exports = function (ngApp, events) {
627 * @param file 631 * @param file
628 */ 632 */
629 $scope.deleteFile = function(file) { 633 $scope.deleteFile = function(file) {
634 + if (!file.deleting) {
635 + file.deleting = true;
636 + return;
637 + }
630 $http.delete(`/files/${file.id}`).then(resp => { 638 $http.delete(`/files/${file.id}`).then(resp => {
631 events.emit('success', resp.data.message); 639 events.emit('success', resp.data.message);
632 $scope.files.splice($scope.files.indexOf(file), 1); 640 $scope.files.splice($scope.files.indexOf(file), 1);
633 - }, checkError); 641 + }, checkError('delete'));
634 }; 642 };
635 643
636 /** 644 /**
...@@ -641,10 +649,10 @@ module.exports = function (ngApp, events) { ...@@ -641,10 +649,10 @@ module.exports = function (ngApp, events) {
641 $scope.attachLinkSubmit = function(file) { 649 $scope.attachLinkSubmit = function(file) {
642 file.uploaded_to = pageId; 650 file.uploaded_to = pageId;
643 $http.post('/files/link', file).then(resp => { 651 $http.post('/files/link', file).then(resp => {
644 - $scope.files.unshift(resp.data); 652 + $scope.files.push(resp.data);
645 events.emit('success', 'Link attached'); 653 events.emit('success', 'Link attached');
646 $scope.file = getCleanFile(); 654 $scope.file = getCleanFile();
647 - }, checkError); 655 + }, checkError('link'));
648 }; 656 };
649 657
650 /** 658 /**
...@@ -652,8 +660,9 @@ module.exports = function (ngApp, events) { ...@@ -652,8 +660,9 @@ module.exports = function (ngApp, events) {
652 * @param fileId 660 * @param fileId
653 */ 661 */
654 $scope.startEdit = function(file) { 662 $scope.startEdit = function(file) {
663 + console.log(file);
655 $scope.editFile = angular.copy(file); 664 $scope.editFile = angular.copy(file);
656 - if (!file.external) $scope.editFile.link = ''; 665 + $scope.editFile.link = (file.external) ? file.path : '';
657 }; 666 };
658 667
659 /** 668 /**
...@@ -670,17 +679,24 @@ module.exports = function (ngApp, events) { ...@@ -670,17 +679,24 @@ module.exports = function (ngApp, events) {
670 $scope.updateFile = function(file) { 679 $scope.updateFile = function(file) {
671 $http.put(`/files/${file.id}`, file).then(resp => { 680 $http.put(`/files/${file.id}`, file).then(resp => {
672 let search = filesIndexOf(resp.data); 681 let search = filesIndexOf(resp.data);
673 - if (search !== -1) $scope.files[search] = file; 682 + if (search !== -1) $scope.files[search] = resp.data;
674 683
675 if ($scope.editFile && !file.external) { 684 if ($scope.editFile && !file.external) {
676 $scope.editFile.link = ''; 685 $scope.editFile.link = '';
677 } 686 }
678 $scope.editFile = false; 687 $scope.editFile = false;
679 events.emit('success', 'Attachment details updated'); 688 events.emit('success', 'Attachment details updated');
680 - }); 689 + }, checkError('edit'));
681 }; 690 };
682 691
683 /** 692 /**
693 + * Get the url of a file.
694 + */
695 + $scope.getFileUrl = function(file) {
696 + return window.baseUrl('/files/' + file.id);
697 + }
698 +
699 + /**
684 * Search the local files via another file object. 700 * Search the local files via another file object.
685 * Used to search via object copies. 701 * Used to search via object copies.
686 * @param file 702 * @param file
...@@ -697,9 +713,16 @@ module.exports = function (ngApp, events) { ...@@ -697,9 +713,16 @@ module.exports = function (ngApp, events) {
697 * Check for an error response in a ajax request. 713 * Check for an error response in a ajax request.
698 * @param response 714 * @param response
699 */ 715 */
700 - function checkError(response) { 716 + function checkError(errorGroupName) {
701 - if (typeof response.data !== 'undefined' && typeof response.data.error !== 'undefined') { 717 + $scope.errors[errorGroupName] = {};
702 - events.emit('error', response.data.error); 718 + return function(response) {
719 + if (typeof response.data !== 'undefined' && typeof response.data.error !== 'undefined') {
720 + events.emit('error', response.data.error);
721 + }
722 + if (typeof response.data !== 'undefined' && typeof response.data.validation !== 'undefined') {
723 + $scope.errors[errorGroupName] = response.data.validation;
724 + console.log($scope.errors[errorGroupName])
725 + }
703 } 726 }
704 } 727 }
705 728
......
...@@ -33,6 +33,59 @@ module.exports = function (ngApp, events) { ...@@ -33,6 +33,59 @@ module.exports = function (ngApp, events) {
33 }; 33 };
34 }); 34 });
35 35
36 + /**
37 + * Common tab controls using simple jQuery functions.
38 + */
39 + ngApp.directive('tabContainer', function() {
40 + return {
41 + restrict: 'A',
42 + link: function (scope, element, attrs) {
43 + const $content = element.find('[tab-content]');
44 + const $buttons = element.find('[tab-button]');
45 +
46 + if (attrs.tabContainer) {
47 + let initial = attrs.tabContainer;
48 + $buttons.filter(`[tab-button="${initial}"]`).addClass('selected');
49 + $content.hide().filter(`[tab-content="${initial}"]`).show();
50 + } else {
51 + $content.hide().first().show();
52 + $buttons.first().addClass('selected');
53 + }
54 +
55 + $buttons.click(function() {
56 + let clickedTab = $(this);
57 + $buttons.removeClass('selected');
58 + $content.hide();
59 + let name = clickedTab.addClass('selected').attr('tab-button');
60 + $content.filter(`[tab-content="${name}"]`).show();
61 + });
62 + }
63 + };
64 + });
65 +
66 + /**
67 + * Sub form component to allow inner-form sections to act like thier own forms.
68 + */
69 + ngApp.directive('subForm', function() {
70 + return {
71 + restrict: 'A',
72 + link: function (scope, element, attrs) {
73 + element.on('keypress', e => {
74 + if (e.keyCode === 13) {
75 + submitEvent(e);
76 + }
77 + });
78 +
79 + element.find('button[type="submit"]').click(submitEvent);
80 +
81 + function submitEvent(e) {
82 + e.preventDefault()
83 + if (attrs.subForm) scope.$eval(attrs.subForm);
84 + }
85 + }
86 + };
87 + });
88 +
36 89
37 /** 90 /**
38 * Image Picker 91 * Image Picker
...@@ -489,8 +542,8 @@ module.exports = function (ngApp, events) { ...@@ -489,8 +542,8 @@ module.exports = function (ngApp, events) {
489 link: function (scope, elem, attrs) { 542 link: function (scope, elem, attrs) {
490 543
491 // Get common elements 544 // Get common elements
492 - const $buttons = elem.find('[tab-button]'); 545 + const $buttons = elem.find('[toolbox-tab-button]');
493 - const $content = elem.find('[tab-content]'); 546 + const $content = elem.find('[toolbox-tab-content]');
494 const $toggle = elem.find('[toolbox-toggle]'); 547 const $toggle = elem.find('[toolbox-toggle]');
495 548
496 // Handle toolbox toggle click 549 // Handle toolbox toggle click
...@@ -502,17 +555,17 @@ module.exports = function (ngApp, events) { ...@@ -502,17 +555,17 @@ module.exports = function (ngApp, events) {
502 function setActive(tabName, openToolbox) { 555 function setActive(tabName, openToolbox) {
503 $buttons.removeClass('active'); 556 $buttons.removeClass('active');
504 $content.hide(); 557 $content.hide();
505 - $buttons.filter(`[tab-button="${tabName}"]`).addClass('active'); 558 + $buttons.filter(`[toolbox-tab-button="${tabName}"]`).addClass('active');
506 - $content.filter(`[tab-content="${tabName}"]`).show(); 559 + $content.filter(`[toolbox-tab-content="${tabName}"]`).show();
507 if (openToolbox) elem.addClass('open'); 560 if (openToolbox) elem.addClass('open');
508 } 561 }
509 562
510 // Set the first tab content active on load 563 // Set the first tab content active on load
511 - setActive($content.first().attr('tab-content'), false); 564 + setActive($content.first().attr('toolbox-tab-content'), false);
512 565
513 // Handle tab button click 566 // Handle tab button click
514 $buttons.click(function (e) { 567 $buttons.click(function (e) {
515 - let name = $(this).attr('tab-button'); 568 + let name = $(this).attr('toolbox-tab-button');
516 setActive(name, true); 569 setActive(name, true);
517 }); 570 });
518 } 571 }
......
...@@ -452,3 +452,17 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { ...@@ -452,3 +452,17 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
452 border-right: 6px solid transparent; 452 border-right: 6px solid transparent;
453 border-bottom: 6px solid $negative; 453 border-bottom: 6px solid $negative;
454 } 454 }
455 +
456 +
457 +[tab-container] .nav-tabs {
458 + text-align: left;
459 + border-bottom: 1px solid #DDD;
460 + margin-bottom: $-m;
461 + .tab-item {
462 + padding: $-s;
463 + color: #666;
464 + &.selected {
465 + border-bottom-width: 3px;
466 + }
467 + }
468 +}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -150,7 +150,6 @@ ...@@ -150,7 +150,6 @@
150 background-color: #FFF; 150 background-color: #FFF;
151 border: 1px solid #DDD; 151 border: 1px solid #DDD;
152 right: $-xl*2; 152 right: $-xl*2;
153 - z-index: 99;
154 width: 48px; 153 width: 48px;
155 overflow: hidden; 154 overflow: hidden;
156 align-items: stretch; 155 align-items: stretch;
...@@ -201,7 +200,7 @@ ...@@ -201,7 +200,7 @@
201 color: #444; 200 color: #444;
202 background-color: rgba(0, 0, 0, 0.1); 201 background-color: rgba(0, 0, 0, 0.1);
203 } 202 }
204 - div[tab-content] { 203 + div[toolbox-tab-content] {
205 padding-bottom: 45px; 204 padding-bottom: 45px;
206 display: flex; 205 display: flex;
207 flex: 1; 206 flex: 1;
...@@ -209,7 +208,7 @@ ...@@ -209,7 +208,7 @@
209 min-height: 0px; 208 min-height: 0px;
210 overflow-y: scroll; 209 overflow-y: scroll;
211 } 210 }
212 - div[tab-content] .padded { 211 + div[toolbox-tab-content] .padded {
213 flex: 1; 212 flex: 1;
214 padding-top: 0; 213 padding-top: 0;
215 } 214 }
...@@ -241,7 +240,7 @@ ...@@ -241,7 +240,7 @@
241 } 240 }
242 } 241 }
243 242
244 -[tab-content] { 243 +[toolbox-tab-content] {
245 display: none; 244 display: none;
246 } 245 }
247 246
......
...@@ -51,4 +51,14 @@ table.list-table { ...@@ -51,4 +51,14 @@ table.list-table {
51 vertical-align: middle; 51 vertical-align: middle;
52 padding: $-xs; 52 padding: $-xs;
53 } 53 }
54 +}
55 +
56 +table.file-table {
57 + @extend .no-style;
58 + td {
59 + padding: $-xs;
60 + }
61 + .ui-sortable-helper {
62 + display: table;
63 + }
54 } 64 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
3 3
4 <div class="tabs primary-background-light"> 4 <div class="tabs primary-background-light">
5 <span toolbox-toggle><i class="zmdi zmdi-caret-left-circle"></i></span> 5 <span toolbox-toggle><i class="zmdi zmdi-caret-left-circle"></i></span>
6 - <span tab-button="tags" title="Page Tags" class="active"><i class="zmdi zmdi-tag"></i></span> 6 + <span toolbox-tab-button="tags" title="Page Tags" class="active"><i class="zmdi zmdi-tag"></i></span>
7 @if(userCan('file-create-all')) 7 @if(userCan('file-create-all'))
8 - <span tab-button="files" title="Attachments"><i class="zmdi zmdi-attachment"></i></span> 8 + <span toolbox-tab-button="files" title="Attachments"><i class="zmdi zmdi-attachment"></i></span>
9 @endif 9 @endif
10 </div> 10 </div>
11 11
12 - <div tab-content="tags" ng-controller="PageTagController" page-id="{{ $page->id or 0 }}"> 12 + <div toolbox-tab-content="tags" ng-controller="PageTagController" page-id="{{ $page->id or 0 }}">
13 <h4>Page Tags</h4> 13 <h4>Page Tags</h4>
14 <div class="padded tags"> 14 <div class="padded tags">
15 <p class="muted small">Add some tags to better categorise your content. <br> You can assign a value to a tag for more in-depth organisation.</p> 15 <p class="muted small">Add some tags to better categorise your content. <br> You can assign a value to a tag for more in-depth organisation.</p>
...@@ -38,55 +38,93 @@ ...@@ -38,55 +38,93 @@
38 </div> 38 </div>
39 39
40 @if(userCan('file-create-all')) 40 @if(userCan('file-create-all'))
41 - <div tab-content="files" ng-controller="PageAttachmentController" page-id="{{ $page->id or 0 }}"> 41 + <div toolbox-tab-content="files" ng-controller="PageAttachmentController" page-id="{{ $page->id or 0 }}">
42 - <h4>Attached Files</h4> 42 + <h4>Attachments</h4>
43 <div class="padded files"> 43 <div class="padded files">
44 44
45 <div id="file-list" ng-show="!editFile"> 45 <div id="file-list" ng-show="!editFile">
46 - <p class="muted small">Upload some files to display on your page. This are visible in the page sidebar.</p> 46 + <p class="muted small">Upload some files or attach some link to display on your page. This are visible in the page sidebar.</p>
47 - <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
48 47
49 - <hr class="even"> 48 + <div tab-container>
49 + <div class="nav-tabs">
50 + <div tab-button="list" class="tab-item">File List</div>
51 + <div tab-button="file" class="tab-item">Upload File</div>
52 + <div tab-button="link" class="tab-item">Attach Link</div>
53 + </div>
54 + <div tab-content="list">
55 + <table class="file-table" style="width: 100%;">
56 + <tbody ui-sortable="sortOptions" ng-model="files" >
57 + <tr ng-repeat="file in files track by $index">
58 + <td width="20" ><i class="handle zmdi zmdi-menu"></i></td>
59 + <td>
60 + <a ng-href="@{{getFileUrl(file)}}" target="_blank" ng-bind="file.name"></a>
61 + <div ng-if="file.deleting">
62 + <span class="neg small">Click delete again to confirm you want to delete this attachment.</span>
63 + <br>
64 + <span class="text-primary small" ng-click="file.deleting=false;">Cancel</span>
65 + </div>
66 + </td>
67 + <td width="10" ng-click="startEdit(file)" class="text-center text-primary" style="padding: 0;"><i class="zmdi zmdi-edit"></i></td>
68 + <td width="5"></td>
69 + <td width="10" ng-click="deleteFile(file)" class="text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-close"></i></td>
70 + </tr>
71 + </tbody>
72 + </table>
73 + <p class="small muted" ng-if="files.length == 0">
74 + No files have been uploaded.
75 + </p>
76 + </div>
77 + <div tab-content="file">
78 + <drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
79 + </div>
80 + <div tab-content="link" sub-form="attachLinkSubmit(file)">
81 + <p class="muted small">You can attach a link if you'd prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.</p>
82 + <div class="form-group">
83 + <label for="attachment-via-link">Link Name</label>
84 + <input type="text" placeholder="Link name" ng-model="file.name">
85 + <p class="small neg" ng-repeat="error in errors.link.name" ng-bind="error"></p>
86 + </div>
87 + <div class="form-group">
88 + <label for="attachment-via-link">Link to file</label>
89 + <input type="text" placeholder="Url of site or file" ng-model="file.link">
90 + <p class="small neg" ng-repeat="error in errors.link.link" ng-bind="error"></p>
91 + </div>
92 + <button type="submit" class="button pos">Attach</button>
50 93
51 - <div class="form-group"> 94 + </div>
52 - <label for="attachment-via-link">File Name</label>
53 - <input type="text" placeholder="File name" ng-model="file.name">
54 - </div>
55 - <div class="form-group">
56 - <label for="attachment-via-link">Link to file</label>
57 - <input type="text" placeholder="File url" ng-model="file.link">
58 </div> 95 </div>
59 - <button type="button" ng-click="attachLinkSubmit(file)" class="button pos">Attach</button>
60 -
61 96
62 - <table class="no-style" tag-autosuggestions style="width: 100%;">
63 - <tbody ui-sortable="sortOptions" ng-model="files" >
64 - <tr ng-repeat="file in files track by $index">
65 - <td width="20" ><i class="handle zmdi zmdi-menu"></i></td>
66 - <td ng-bind="file.name"></td>
67 - <td width="10" ng-click="deleteFile(file)" class="text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-close"></i></td>
68 - <td width="10" ng-click="startEdit(file)" class="text-center text-neg" style="padding: 0;"><i class="zmdi zmdi-edit"></i></td>
69 - </tr>
70 - </tbody>
71 - </table>
72 </div> 97 </div>
73 98
74 - <div id="file-edit" ng-if="editFile"> 99 + <div id="file-edit" ng-if="editFile" sub-form="updateFile(editFile)">
75 <h5>Edit File</h5> 100 <h5>Edit File</h5>
101 +
76 <div class="form-group"> 102 <div class="form-group">
77 <label for="attachment-name-edit">File Name</label> 103 <label for="attachment-name-edit">File Name</label>
78 <input type="text" id="attachment-name-edit" placeholder="File name" ng-model="editFile.name"> 104 <input type="text" id="attachment-name-edit" placeholder="File name" ng-model="editFile.name">
105 + <p class="small neg" ng-repeat="error in errors.edit.name" ng-bind="error"></p>
79 </div> 106 </div>
80 - <hr class="even"> 107 +
81 - <drop-zone upload-url="@{{getUploadUrl(editFile)}}" uploaded-to="@{{uploadedTo}}" placeholder="Drop files or click here to upload and overwrite" event-success="uploadSuccessUpdate"></drop-zone> 108 + <div tab-container="@{{ editFile.external ? 'link' : 'file' }}">
82 - <hr class="even"> 109 + <div class="nav-tabs">
83 - <div class="form-group"> 110 + <div tab-button="file" class="tab-item">Upload File</div>
84 - <label for="attachment-link-edit">Link to file</label> 111 + <div tab-button="link" class="tab-item">Set Link</div>
85 - <input type="text" id="attachment-link-edit" placeholder="File url" ng-model="editFile.link"> 112 + </div>
113 + <div tab-content="file">
114 + <drop-zone upload-url="@{{getUploadUrl(editFile)}}" uploaded-to="@{{uploadedTo}}" placeholder="Drop files or click here to upload and overwrite" event-success="uploadSuccessUpdate"></drop-zone>
115 + <br>
116 + </div>
117 + <div tab-content="link">
118 + <div class="form-group">
119 + <label for="attachment-link-edit">Link to file</label>
120 + <input type="text" id="attachment-link-edit" placeholder="Attachment link" ng-model="editFile.link">
121 + <p class="small neg" ng-repeat="error in errors.edit.link" ng-bind="error"></p>
122 + </div>
123 + </div>
86 </div> 124 </div>
87 125
88 <button type="button" class="button" ng-click="cancelEdit()">Back</button> 126 <button type="button" class="button" ng-click="cancelEdit()">Back</button>
89 - <button type="button" class="button pos" ng-click="updateFile(editFile)">Save</button> 127 + <button type="submit" class="button pos">Save</button>
90 </div> 128 </div>
91 129
92 </div> 130 </div>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 <h6 class="text-muted">Attachments</h6> 5 <h6 class="text-muted">Attachments</h6>
6 @foreach($page->files as $file) 6 @foreach($page->files as $file)
7 <div class="attachment"> 7 <div class="attachment">
8 - <a href="{{ $file->getUrl() }}" @if($file->external) target="_blank" @endif><i class="zmdi zmdi-file"></i> {{ $file->name }}</a> 8 + <a href="{{ $file->getUrl() }}" @if($file->external) target="_blank" @endif><i class="zmdi zmdi-{{ $file->external ? 'open-in-new' : 'file' }}"></i> {{ $file->name }}</a>
9 </div> 9 </div>
10 @endforeach 10 @endforeach
11 @endif 11 @endif
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
14 .nav-tabs a.selected, .nav-tabs .tab-item.selected { 14 .nav-tabs a.selected, .nav-tabs .tab-item.selected {
15 border-bottom-color: {{ setting('app-color') }}; 15 border-bottom-color: {{ setting('app-color') }};
16 } 16 }
17 - p.primary:hover, p .primary:hover, span.primary:hover, .text-primary:hover, a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus { 17 + .text-primary, p.primary, p .primary, span.primary:hover, .text-primary:hover, a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus {
18 color: {{ setting('app-color') }}; 18 color: {{ setting('app-color') }};
19 } 19 }
20 </style> 20 </style>
...\ No newline at end of file ...\ No newline at end of file
......