]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'master' of https://github.com/Abijeet/BookStack
authorAbijeet <redacted>
Tue, 18 Apr 2017 19:53:27 +0000 (01:23 +0530)
committerAbijeet <redacted>
Tue, 18 Apr 2017 19:53:27 +0000 (01:23 +0530)
1  2 
app/Page.php
app/Services/PermissionService.php
resources/assets/js/controllers.js
resources/assets/js/directives.js
resources/assets/sass/styles.scss
resources/lang/en/entities.php
routes/web.php

diff --combined app/Page.php
index 83ef6f3503fad5a678bcda8c4fb0597adc82486c,c9823e7e4ccfcb65dce71328f28fa0f4f04d40a3..4a8d32780388565d668eca10ad249beb929d3c61
@@@ -8,8 -8,7 +8,7 @@@ class Page extends Entit
      protected $simpleAttributes = ['name', 'id', 'slug'];
  
      protected $with = ['book'];
-     protected $fieldsToSearch = ['name', 'text'];
+     public $textField = 'text';
  
      /**
       * Converts this page into a simplified array.
      {
          return $this->belongsTo(Chapter::class);
      }
 +    
 +    /**
 +     * Get the comments in the page.
 +     * @return \Illuminate\Database\Eloquent\Relations\HasMany
 +     */
 +    public function comment()
 +    {
 +        return $this->hasMany(Comment::class);
 +    }
  
      /**
       * Check if this page has a chapter.
          return mb_convert_encoding($text, 'UTF-8');
      }
  
+     /**
+      * Return a generalised, common raw query that can be 'unioned' across entities.
+      * @param bool $withContent
+      * @return string
+      */
+     public function entityRawQuery($withContent = false)
+     {   $htmlQuery = $withContent ? 'html' : "'' as html";
+         return "'BookStack\\\\Page' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, {$htmlQuery}, book_id, priority, chapter_id, draft, created_by, updated_by, updated_at, created_at";
+     }
  }
index 6f27db9b7cd8bd2cb17e280b833a132c682ff7e3,1e75308a07e3734ac6771cf2b78a5c5babe8f3d7..cb0b68026a7de99e8ec16aaf41b2b4a566ad9ecd
@@@ -406,7 -406,7 +406,7 @@@ class PermissionServic
          $action = end($explodedPermission);
          $this->currentAction = $action;
  
 -        $nonJointPermissions = ['restrictions', 'image', 'attachment'];
 +        $nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment'];
  
          // Handle non entity specific jointPermissions
          if (in_array($explodedPermission[0], $nonJointPermissions)) {
       * @return \Illuminate\Database\Query\Builder
       */
      public function bookChildrenQuery($book_id, $filterDrafts = false, $fetchPageContent = false) {
-         $pageContentSelect = $fetchPageContent ? 'html' : "''";
-         $pageSelect = $this->db->table('pages')->selectRaw("'BookStack\\\\Page' as entity_type, id, slug, name, text, {$pageContentSelect} as description, book_id, priority, chapter_id, draft")->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
+         $pageSelect = $this->db->table('pages')->selectRaw($this->page->entityRawQuery($fetchPageContent))->where('book_id', '=', $book_id)->where(function($query) use ($filterDrafts) {
              $query->where('draft', '=', 0);
              if (!$filterDrafts) {
                  $query->orWhere(function($query) {
                  });
              }
          });
-         $chapterSelect = $this->db->table('chapters')->selectRaw("'BookStack\\\\Chapter' as entity_type, id, slug, name, '' as text, description, book_id, priority, 0 as chapter_id, 0 as draft")->where('book_id', '=', $book_id);
+         $chapterSelect = $this->db->table('chapters')->selectRaw($this->chapter->entityRawQuery())->where('book_id', '=', $book_id);
          $query = $this->db->query()->select('*')->from($this->db->raw("({$pageSelect->toSql()} UNION {$chapterSelect->toSql()}) AS U"))
              ->mergeBindings($pageSelect)->mergeBindings($chapterSelect);
  
       * @param string $entityType
       * @param Builder|Entity $query
       * @param string $action
-      * @return mixed
+      * @return Builder
       */
      public function enforceEntityRestrictions($entityType, $query, $action = 'view')
      {
      }
  
      /**
-      * Filter items that have entities set a a polymorphic relation.
+      * Filter items that have entities set as a polymorphic relation.
       * @param $query
       * @param string $tableName
       * @param string $entityIdColumn
index 7df130f3fbaf862d555444ea7de77f7489638ec9,6a88aa81152a6822b81071a77c9f9aa2ccda5870..65dc50e9942ef57fa2b7aa5b054cba5f67a7a004
@@@ -1,12 -1,12 +1,12 @@@
  "use strict";
  
import moment from 'moment';
import 'moment/locale/en-gb';
import editorOptions from "./pages/page-form";
const moment = require('moment');
require('moment/locale/en-gb');
const editorOptions = require("./pages/page-form");
  
  moment.locale('en-gb');
  
export default function (ngApp, events) {
module.exports = function (ngApp, events) {
  
      ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService',
          function ($scope, $attrs, $http, $timeout, imageManagerService) {
  
          }]);
  
-     ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', '$sce', function ($scope, $http, $attrs, $sce) {
-         $scope.searching = false;
-         $scope.searchTerm = '';
-         $scope.searchResults = '';
-         $scope.searchBook = function (e) {
-             e.preventDefault();
-             let term = $scope.searchTerm;
-             if (term.length == 0) return;
-             $scope.searching = true;
-             $scope.searchResults = '';
-             let searchUrl = window.baseUrl('/search/book/' + $attrs.bookId);
-             searchUrl += '?term=' + encodeURIComponent(term);
-             $http.get(searchUrl).then((response) => {
-                 $scope.searchResults = $sce.trustAsHtml(response.data);
-             });
-         };
-         $scope.checkSearchForm = function () {
-             if ($scope.searchTerm.length < 1) {
-                 $scope.searching = false;
-             }
-         };
-         $scope.clearSearch = function () {
-             $scope.searching = false;
-             $scope.searchTerm = '';
-         };
-     }]);
      ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce',
          function ($scope, $http, $attrs, $interval, $timeout, $sce) {
  
          $scope.draftsEnabled = $attrs.draftsEnabled === 'true';
          $scope.isUpdateDraft = Number($attrs.pageUpdateDraft) === 1;
          $scope.isNewPageDraft = Number($attrs.pageNewDraft) === 1;
 +        $scope.commentsLoaded = false;
  
          // Set initial header draft text
          if ($scope.isUpdateDraft || $scope.isNewPageDraft) {
  
          }]);
  
 +    // CommentCrudController
 +    ngApp.controller('CommentAddController', ['$scope', '$http', function ($scope, $http) {
 +        let vm = this;
 +        let comment = {};
 +        $scope.errors = {};
 +        vm.saveComment = function () {
 +            let pageId = $scope.comment.pageId;
 +            let comment = $scope.comment.newComment;
 +            let commentHTML = $scope.getCommentHTML();
 +
 +            $http.post(window.baseUrl(`/ajax/page/${pageId}/comment/`), {
 +                text: comment,
 +                html: commentHTML
 +            }).then(resp => {                
 +                $scope.clearInput();
 +                if (!resp.data || resp.data.status !== 'success') {
 +                     return events.emit('error', trans('error'));
 +                }
 +                events.emit('success', trans(resp.data.message));
 +            }, checkError('add'));
 +                        
 +        };  
 +        
 +        function checkError(errorGroupName) {
 +            $scope.errors[errorGroupName] = {};
 +            return function(response) {
 +                if (typeof response.data !== 'undefined' && typeof response.data.error !== 'undefined') {
 +                    events.emit('error', response.data.error);
 +                }
 +                if (typeof response.data !== 'undefined' && typeof response.data.validation !== 'undefined') {
 +                    $scope.errors[errorGroupName] = response.data.validation;
 +                    console.log($scope.errors[errorGroupName])
 +                }
 +            }
 +        }
 +    }]);
 +
 +
 +    // CommentListController
 +    ngApp.controller('CommentListController', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
 +        let vm = this;
 +        $scope.errors = {};
 +        $scope.defaultAvatar = defaultAvatar;        
 +        vm.totalCommentsStr = 'Loading...';
 +        $scope.editorChange = function (content) {
 +            console.log(content);
 +        }
 +        
 +        $timeout(function() {
 +            console.log($scope.pageId);
 +            $http.get(window.baseUrl(`/ajax/page/${$scope.pageId}/comments/`)).then(resp => {
 +                if (!resp.data || resp.data.success !== true) {
 +                    // TODO : Handle error
 +                    return;
 +                } 
 +                vm.comments = resp.data.comments.data;  
 +                vm.totalComments = resp.data.comments.total;
 +                if (vm.totalComments === 0) {
 +                    vm.totalCommentsStr = 'No comments found.';
 +                } else if (vm.totalComments === 1) {
 +                    vm.totalCommentsStr = '1 Comments';
 +                } else {
 +                    vm.totalCommentsStr = vm.totalComments + ' Comments'
 +                }
 +            }, checkError('app'));
 +        });        
 +        
 +        function checkError(errorGroupName) {
 +            $scope.errors[errorGroupName] = {};
 +            return function(response) {
 +                console.log(resp);
 +            }
 +        }
 +    }]);
 +
  };
index 537a016f5e13a637236301ae7c9f5491c0130353,0bc664200f70a7459859abe1abf38061719a0498..f30a097782200a500d1ccf3c694b1771d9d982d6
@@@ -1,8 -1,9 +1,9 @@@
  "use strict";
- import DropZone from "dropzone";
- import markdown from "marked";
+ const DropZone = require("dropzone");
+ const MarkdownIt = require("markdown-it");
+ const mdTasksLists = require('markdown-it-task-lists');
  
export default function (ngApp, events) {
module.exports = function (ngApp, events) {
  
      /**
       * Common tab controls using simple jQuery functions.
          }
      }]);
  
-     let renderer = new markdown.Renderer();
-     // Custom markdown checkbox list item
-     // Attribution: https://github.com/chjj/marked/issues/107#issuecomment-44542001
-     renderer.listitem = function(text) {
-         if (/^\s*\[[x ]\]\s*/.test(text)) {
-             text = text
-                 .replace(/^\s*\[ \]\s*/, '<input type="checkbox"/>')
-                 .replace(/^\s*\[x\]\s*/, '<input type="checkbox" checked/>');
-             return `<li class="checkbox-item">${text}</li>`;
-         }
-         return `<li>${text}</li>`;
-     };
+     const md = new MarkdownIt();
+     md.use(mdTasksLists, {label: true});
  
      /**
       * Markdown input
                  element = element.find('textarea').first();
                  let content = element.val();
                  scope.mdModel = content;
-                 scope.mdChange(markdown(content, {renderer: renderer}));
+                 scope.mdChange(md.render(content));
  
                  element.on('change input', (event) => {
                      content = element.val();
                      $timeout(() => {
                          scope.mdModel = content;
-                         scope.mdChange(markdown(content, {renderer: renderer}));
+                         scope.mdChange(md.render(content));
                      });
                  });
  
                  scope.$on('markdown-update', (event, value) => {
                      element.val(value);
                      scope.mdModel = value;
-                     scope.mdChange(markdown(value));
+                     scope.mdChange(md.render(value));
                  });
  
              }
              }
          };
      }]);
 +
 +
 +    ngApp.directive('simpleMarkdownInput', ['$timeout', function ($timeout) {
 +        return {
 +            restrict: 'A',
 +            scope: {
 +                smdModel: '=',
 +                smdChange: '=',
 +                smdGetContent: '=',
 +                smdClear: '='
 +            },
 +            link: function (scope, element, attrs) {
 +                // Set initial model content
 +                element = element.find('textarea').first();
 +                let simplemde = new SimpleMDE({
 +                    element: element[0],
 +                    status: []
 +                });
 +                let content = element.val();
 +                simplemde.value(content)
 +                scope.smdModel = content;
 +
 +                simplemde.codemirror.on('change', (event) => {
 +                    content = simplemde.value();
 +                    $timeout(() => {
 +                        scope.smdModel = content;
 +                        if (scope.smdChange) {
 +                            scope.smdChange(element, content);
 +                        }
 +                    });
 +                });
 +                
 +                if ('smdGetContent' in attrs) {
 +                    scope.smdGetContent = function () {
 +                        return simplemde.options.previewRender(simplemde.value());
 +                    };
 +                }
 +                
 +                if ('smdClear' in attrs) {
 +                    scope.smdClear = function () {
 +                        simplemde.value('');
 +                        scope.smdModel = '';                        
 +                    };
 +                }
 +            }
 +        }
 +    }]);
 +
  };
index cd43650ee8c7f8fab22e13f551e1312f6e20a3aa,50c3a50b2b8cce231cfc6fe46cf1d8052ec7f5ee..60071b9efd6da491058204cbe710cf88abc47094
@@@ -7,19 -7,21 +7,23 @@@
  @import "grid";
  @import "blocks";
  @import "buttons";
- @import "forms";
  @import "tables";
+ @import "forms";
  @import "animations";
  @import "tinymce";
  @import "highlightjs";
 +@import "simplemde";
  @import "components";
  @import "header";
  @import "lists";
  @import "pages";
 +@import "comments";
  
- [v-cloak], [v-show] {display: none;}
+ [v-cloak], [v-show] {
+   display: none; opacity: 0;
+   animation-name: none !important;
+ }
  
  [ng\:cloak], [ng-cloak], .ng-cloak {
    display: none !important;
index afe6f7ec11f2c4807ba43bfe511939a44109526a,8644f7a4a839bfa8e7a2b4952a8d6d6e5c056162..ec0e173069a26d986849f0f90fd9047c55e035ba
@@@ -43,18 -43,26 +43,26 @@@ return 
       * Search
       */
      'search_results' => 'Search Results',
-     'search_results_page' => 'Page Search Results',
-     'search_results_chapter' => 'Chapter Search Results',
-     'search_results_book' => 'Book Search Results',
+     'search_total_results_found' => ':count result found|:count total results found',
      'search_clear' => 'Clear Search',
-     'search_view_pages' => 'View all matches pages',
-     'search_view_chapters' => 'View all matches chapters',
-     'search_view_books' => 'View all matches books',
      'search_no_pages' => 'No pages matched this search',
      'search_for_term' => 'Search for :term',
-     'search_page_for_term' => 'Page search for :term',
-     'search_chapter_for_term' => 'Chapter search for :term',
-     'search_book_for_term' => 'Books search for :term',
+     'search_more' => 'More Results',
+     'search_filters' => 'Search Filters',
+     'search_content_type' => 'Content Type',
+     'search_exact_matches' => 'Exact Matches',
+     'search_tags' => 'Tag Searches',
+     'search_viewed_by_me' => 'Viewed by me',
+     'search_not_viewed_by_me' => 'Not viewed by me',
+     'search_permissions_set' => 'Permissions set',
+     'search_created_by_me' => 'Created by me',
+     'search_updated_by_me' => 'Updated by me',
+     'search_updated_before' => 'Updated before',
+     'search_updated_after' => 'Updated after',
+     'search_created_before' => 'Created before',
+     'search_created_after' => 'Created after',
+     'search_set_date' => 'Set Date',
+     'search_update' => 'Update Search',
  
      /**
       * Books
      'chapters_empty' => 'No pages are currently in this chapter.',
      'chapters_permissions_active' => 'Chapter Permissions Active',
      'chapters_permissions_success' => 'Chapter Permissions Updated',
+     'chapters_search_this' => 'Search this chapter',
  
      /**
       * Pages
      'profile_not_created_pages' => ':userName has not created any pages',
      'profile_not_created_chapters' => ':userName has not created any chapters',
      'profile_not_created_books' => ':userName has not created any books',
 +
 +    /**
 +     * Comments
 +     */
 +    'comment' => 'Comment',
 +    'comments' => 'Comments'
  ];
diff --combined routes/web.php
index 4b57138e22e3cb3500d2380c0525df82c1b03726,8ecfd9465ec6c2962375248722031e4a2daa3270..2ac212e620975c58c1152415b8a662141d64e9dd
@@@ -119,22 -119,13 +119,20 @@@ Route::group(['middleware' => 'auth'], 
  
      Route::get('/ajax/search/entities', 'SearchController@searchEntitiesAjax');
  
 +    // Comments
 +    Route::post('/ajax/page/{pageId}/comment/', 'CommentController@save');
 +    Route::put('/ajax/page/{pageId}/comment/{commentId}', 'CommentController@save');
 +    Route::delete('/ajax/comment/{id}', 'CommentController@destroy');
 +    Route::get('/ajax/page/{pageId}/comments/{commentId}/sub-comments', 'CommentController@getComments');
 +    Route::get('/ajax/page/{pageId}/comments/', 'CommentController@getComments');
 +
      // Links
      Route::get('/link/{id}', 'PageController@redirectFromLink');
  
      // Search
-     Route::get('/search/all', 'SearchController@searchAll');
-     Route::get('/search/pages', 'SearchController@searchPages');
-     Route::get('/search/books', 'SearchController@searchBooks');
-     Route::get('/search/chapters', 'SearchController@searchChapters');
+     Route::get('/search', 'SearchController@search');
      Route::get('/search/book/{bookId}', 'SearchController@searchBook');
+     Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
  
      // Other Pages
      Route::get('/', 'HomeController@index');