Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Зуев Егор
/
wiki.dev
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Authored by
Dan Brown
2016-08-26 20:20:58 +0100
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Commit
7973412c297720daa94d98980960d2b4ba3c9d17
7973412c
1 parent
f83de5f8
Improved sort efficiency by a factor of 10
Fixes #145
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
152 additions
and
47 deletions
app/Http/Controllers/BookController.php
app/Http/Controllers/ChapterController.php
app/Repos/BookRepo.php
app/Repos/ChapterRepo.php
app/Repos/EntityRepo.php
app/Services/PermissionService.php
resources/views/books/sort.blade.php
app/Http/Controllers/BookController.php
View file @
7973412
...
...
@@ -3,7 +3,6 @@
use
Activity
;
use
BookStack\Repos\UserRepo
;
use
Illuminate\Http\Request
;
use
Illuminate\Support\Facades\Auth
;
use
BookStack\Http\Requests
;
use
BookStack\Repos\BookRepo
;
use
BookStack\Repos\ChapterRepo
;
...
...
@@ -180,21 +179,31 @@ class BookController extends Controller
return
redirect
(
$book
->
getUrl
());
}
$sortedBooks
=
[];
// Sort pages and chapters
$sortedBooks
=
[];
$updatedModels
=
collect
();
$sortMap
=
json_decode
(
$request
->
get
(
'sort-tree'
));
$defaultBookId
=
$book
->
id
;
foreach
(
$sortMap
as
$index
=>
$bookChild
)
{
$id
=
$bookChild
->
id
;
// Loop through contents of provided map and update entities accordingly
foreach
(
$sortMap
as
$bookChild
)
{
$priority
=
$bookChild
->
sort
;
$id
=
intval
(
$bookChild
->
id
);
$isPage
=
$bookChild
->
type
==
'page'
;
$bookId
=
$this
->
bookRepo
->
exists
(
$bookChild
->
book
)
?
$bookChild
->
book
:
$defaultBookId
;
$bookId
=
$this
->
bookRepo
->
exists
(
$bookChild
->
book
)
?
intval
(
$bookChild
->
book
)
:
$defaultBookId
;
$chapterId
=
(
$isPage
&&
$bookChild
->
parentChapter
===
false
)
?
0
:
intval
(
$bookChild
->
parentChapter
);
$model
=
$isPage
?
$this
->
pageRepo
->
getById
(
$id
)
:
$this
->
chapterRepo
->
getById
(
$id
);
$isPage
?
$this
->
pageRepo
->
changeBook
(
$bookId
,
$model
)
:
$this
->
chapterRepo
->
changeBook
(
$bookId
,
$model
);
$model
->
priority
=
$index
;
if
(
$isPage
)
{
$model
->
chapter_id
=
(
$bookChild
->
parentChapter
===
false
)
?
0
:
$bookChild
->
parentChapter
;
// Update models only if there's a change in parent chain or ordering.
if
(
$model
->
priority
!==
$priority
||
$model
->
book_id
!==
$bookId
||
(
$isPage
&&
$model
->
chapter_id
!==
$chapterId
))
{
$isPage
?
$this
->
pageRepo
->
changeBook
(
$bookId
,
$model
)
:
$this
->
chapterRepo
->
changeBook
(
$bookId
,
$model
);
$model
->
priority
=
$priority
;
if
(
$isPage
)
$model
->
chapter_id
=
$chapterId
;
$model
->
save
();
$updatedModels
->
push
(
$model
);
}
$model
->
save
();
// Store involved books to be sorted later
if
(
!
in_array
(
$bookId
,
$sortedBooks
))
{
$sortedBooks
[]
=
$bookId
;
}
...
...
@@ -203,10 +212,12 @@ class BookController extends Controller
// Add activity for books
foreach
(
$sortedBooks
as
$bookId
)
{
$updatedBook
=
$this
->
bookRepo
->
getById
(
$bookId
);
$this
->
bookRepo
->
updateBookPermissions
(
$updatedBook
);
Activity
::
add
(
$updatedBook
,
'book_sort'
,
$updatedBook
->
id
);
}
// Update permissions on changed models
$this
->
bookRepo
->
buildJointPermissions
(
$updatedModels
);
return
redirect
(
$book
->
getUrl
());
}
...
...
app/Http/Controllers/ChapterController.php
View file @
7973412
...
...
@@ -204,7 +204,7 @@ class ChapterController extends Controller
return
redirect
()
->
back
();
}
$this
->
chapterRepo
->
changeBook
(
$parent
->
id
,
$chapter
);
$this
->
chapterRepo
->
changeBook
(
$parent
->
id
,
$chapter
,
true
);
Activity
::
add
(
$chapter
,
'chapter_move'
,
$chapter
->
book
->
id
);
session
()
->
flash
(
'success'
,
sprintf
(
'Chapter moved to "%s"'
,
$parent
->
name
));
...
...
app/Repos/BookRepo.php
View file @
7973412
...
...
@@ -2,6 +2,7 @@
use
Alpha\B
;
use
BookStack\Exceptions\NotFoundException
;
use
Illuminate\Database\Eloquent\Collection
;
use
Illuminate\Support\Str
;
use
BookStack\Book
;
use
Views
;
...
...
@@ -174,15 +175,6 @@ class BookRepo extends EntityRepo
}
/**
* Alias method to update the book jointPermissions in the PermissionService.
* @param Book $book
*/
public
function
updateBookPermissions
(
Book
$book
)
{
$this
->
permissionService
->
buildJointPermissionsForEntity
(
$book
);
}
/**
* Get the next child element priority.
* @param Book $book
* @return int
...
...
app/Repos/ChapterRepo.php
View file @
7973412
...
...
@@ -195,11 +195,12 @@ class ChapterRepo extends EntityRepo
/**
* Changes the book relation of this chapter.
* @param
$bookId
* @param $bookId
* @param Chapter $chapter
* @param bool $rebuildPermissions
* @return Chapter
*/
public
function
changeBook
(
$bookId
,
Chapter
$chapter
)
public
function
changeBook
(
$bookId
,
Chapter
$chapter
,
$rebuildPermissions
=
false
)
{
$chapter
->
book_id
=
$bookId
;
// Update related activity
...
...
@@ -213,9 +214,12 @@ class ChapterRepo extends EntityRepo
foreach
(
$chapter
->
pages
as
$page
)
{
$this
->
pageRepo
->
changeBook
(
$bookId
,
$page
);
}
// Update permissions
$chapter
->
load
(
'book'
);
$this
->
permissionService
->
buildJointPermissionsForEntity
(
$chapter
->
book
);
// Update permissions if applicable
if
(
$rebuildPermissions
)
{
$chapter
->
load
(
'book'
);
$this
->
permissionService
->
buildJointPermissionsForEntity
(
$chapter
->
book
);
}
return
$chapter
;
}
...
...
app/Repos/EntityRepo.php
View file @
7973412
...
...
@@ -6,6 +6,7 @@ use BookStack\Entity;
use
BookStack\Page
;
use
BookStack\Services\PermissionService
;
use
BookStack\User
;
use
Illuminate\Support\Collection
;
use
Illuminate\Support\Facades\Log
;
class
EntityRepo
...
...
@@ -260,6 +261,15 @@ class EntityRepo
return
$query
;
}
/**
* Alias method to update the book jointPermissions in the PermissionService.
* @param Collection $collection collection on entities
*/
public
function
buildJointPermissions
(
Collection
$collection
)
{
$this
->
permissionService
->
buildJointPermissionsForEntities
(
$collection
);
}
}
...
...
app/Services/PermissionService.php
View file @
7973412
...
...
@@ -8,7 +8,7 @@ use BookStack\Ownable;
use
BookStack\Page
;
use
BookStack\Role
;
use
BookStack\User
;
use
Illuminate\
Database\Eloquen
t\Collection
;
use
Illuminate\
Suppor
t\Collection
;
class
PermissionService
{
...
...
@@ -25,6 +25,8 @@ class PermissionService
protected
$jointPermission
;
protected
$role
;
protected
$entityCache
;
/**
* PermissionService constructor.
* @param JointPermission $jointPermission
...
...
@@ -49,6 +51,57 @@ class PermissionService
}
/**
* Prepare the local entity cache and ensure it's empty
*/
protected
function
readyEntityCache
()
{
$this
->
entityCache
=
[
'books'
=>
collect
(),
'chapters'
=>
collect
()
];
}
/**
* Get a book via ID, Checks local cache
* @param $bookId
* @return Book
*/
protected
function
getBook
(
$bookId
)
{
if
(
isset
(
$this
->
entityCache
[
'books'
])
&&
$this
->
entityCache
[
'books'
]
->
has
(
$bookId
))
{
return
$this
->
entityCache
[
'books'
]
->
get
(
$bookId
);
}
$book
=
$this
->
book
->
find
(
$bookId
);
if
(
$book
===
null
)
$book
=
false
;
if
(
isset
(
$this
->
entityCache
[
'books'
]))
{
$this
->
entityCache
[
'books'
]
->
put
(
$bookId
,
$book
);
}
return
$book
;
}
/**
* Get a chapter via ID, Checks local cache
* @param $chapterId
* @return Book
*/
protected
function
getChapter
(
$chapterId
)
{
if
(
isset
(
$this
->
entityCache
[
'chapters'
])
&&
$this
->
entityCache
[
'chapters'
]
->
has
(
$chapterId
))
{
return
$this
->
entityCache
[
'chapters'
]
->
get
(
$chapterId
);
}
$chapter
=
$this
->
chapter
->
find
(
$chapterId
);
if
(
$chapter
===
null
)
$chapter
=
false
;
if
(
isset
(
$this
->
entityCache
[
'chapters'
]))
{
$this
->
entityCache
[
'chapters'
]
->
put
(
$chapterId
,
$chapter
);
}
return
$chapter
;
}
/**
* Get the roles for the current user;
* @return array|bool
*/
...
...
@@ -76,6 +129,7 @@ class PermissionService
public
function
buildJointPermissions
()
{
$this
->
jointPermission
->
truncate
();
$this
->
readyEntityCache
();
// Get all roles (Should be the most limited dimension)
$roles
=
$this
->
role
->
with
(
'permissions'
)
->
get
();
...
...
@@ -97,7 +151,7 @@ class PermissionService
}
/**
*
Create
the entity jointPermissions for a particular entity.
*
Rebuild
the entity jointPermissions for a particular entity.
* @param Entity $entity
*/
public
function
buildJointPermissionsForEntity
(
Entity
$entity
)
...
...
@@ -117,6 +171,17 @@ class PermissionService
}
/**
* Rebuild the entity jointPermissions for a collection of entities.
* @param Collection $entities
*/
public
function
buildJointPermissionsForEntities
(
Collection
$entities
)
{
$roles
=
$this
->
role
->
with
(
'jointPermissions'
)
->
get
();
$this
->
deleteManyJointPermissionsForEntities
(
$entities
);
$this
->
createManyJointPermissions
(
$entities
,
$roles
);
}
/**
* Build the entity jointPermissions for a particular role.
* @param Role $role
*/
...
...
@@ -177,9 +242,14 @@ class PermissionService
*/
protected
function
deleteManyJointPermissionsForEntities
(
$entities
)
{
$query
=
$this
->
jointPermission
->
newQuery
();
foreach
(
$entities
as
$entity
)
{
$entity
->
jointPermissions
()
->
delete
();
$query
->
orWhere
(
function
(
$query
)
use
(
$entity
)
{
$query
->
where
(
'entity_id'
,
'='
,
$entity
->
id
)
->
where
(
'entity_type'
,
'='
,
$entity
->
getMorphClass
());
});
}
$query
->
delete
();
}
/**
...
...
@@ -189,6 +259,7 @@ class PermissionService
*/
protected
function
createManyJointPermissions
(
$entities
,
$roles
)
{
$this
->
readyEntityCache
();
$jointPermissions
=
[];
foreach
(
$entities
as
$entity
)
{
foreach
(
$roles
as
$role
)
{
...
...
@@ -248,8 +319,9 @@ class PermissionService
}
elseif
(
$entity
->
isA
(
'chapter'
))
{
if
(
!
$entity
->
restricted
)
{
$hasExplicitAccessToBook
=
$entity
->
book
->
hasActiveRestriction
(
$role
->
id
,
$restrictionAction
);
$hasPermissiveAccessToBook
=
!
$entity
->
book
->
restricted
;
$book
=
$this
->
getBook
(
$entity
->
book_id
);
$hasExplicitAccessToBook
=
$book
->
hasActiveRestriction
(
$role
->
id
,
$restrictionAction
);
$hasPermissiveAccessToBook
=
!
$book
->
restricted
;
return
$this
->
createJointPermissionDataArray
(
$entity
,
$role
,
$action
,
(
$hasExplicitAccessToBook
||
(
$roleHasPermission
&&
$hasPermissiveAccessToBook
)),
(
$hasExplicitAccessToBook
||
(
$roleHasPermissionOwn
&&
$hasPermissiveAccessToBook
)));
...
...
@@ -261,11 +333,14 @@ class PermissionService
}
elseif
(
$entity
->
isA
(
'page'
))
{
if
(
!
$entity
->
restricted
)
{
$hasExplicitAccessToBook
=
$entity
->
book
->
hasActiveRestriction
(
$role
->
id
,
$restrictionAction
);
$hasPermissiveAccessToBook
=
!
$entity
->
book
->
restricted
;
$hasExplicitAccessToChapter
=
$entity
->
chapter
&&
$entity
->
chapter
->
hasActiveRestriction
(
$role
->
id
,
$restrictionAction
);
$hasPermissiveAccessToChapter
=
$entity
->
chapter
&&
!
$entity
->
chapter
->
restricted
;
$acknowledgeChapter
=
(
$entity
->
chapter
&&
$entity
->
chapter
->
restricted
);
$book
=
$this
->
getBook
(
$entity
->
book_id
);
$hasExplicitAccessToBook
=
$book
->
hasActiveRestriction
(
$role
->
id
,
$restrictionAction
);
$hasPermissiveAccessToBook
=
!
$book
->
restricted
;
$chapter
=
$this
->
getChapter
(
$entity
->
chapter_id
);
$hasExplicitAccessToChapter
=
$chapter
&&
$chapter
->
hasActiveRestriction
(
$role
->
id
,
$restrictionAction
);
$hasPermissiveAccessToChapter
=
$chapter
&&
!
$chapter
->
restricted
;
$acknowledgeChapter
=
(
$chapter
&&
$chapter
->
restricted
);
$hasExplicitAccessToParents
=
$acknowledgeChapter
?
$hasExplicitAccessToChapter
:
$hasExplicitAccessToBook
;
$hasPermissiveAccessToParents
=
$acknowledgeChapter
?
$hasPermissiveAccessToChapter
:
$hasPermissiveAccessToBook
;
...
...
resources/views/books/sort.blade.php
View file @
7973412
...
...
@@ -50,7 +50,7 @@
var
sortableOptions
=
{
group
:
'serialization'
,
onDrop
:
function
(
$item
,
container
,
_super
)
{
var
pageMap
=
build
Page
Map
();
var
pageMap
=
build
Entity
Map
();
$
(
'#sort-tree-input'
).
val
(
JSON
.
stringify
(
pageMap
));
_super
(
$item
,
container
);
},
...
...
@@ -74,29 +74,42 @@
$link
.
remove
();
});
function
buildPageMap
()
{
var
pageMap
=
[];
/**
* Build up a mapping of entities with their ordering and nesting.
* @returns {Array}
*/
function
buildEntityMap
()
{
var
entityMap
=
[];
var
$lists
=
$
(
'.sort-list'
);
$lists
.
each
(
function
(
listIndex
)
{
var
list
=
$
(
this
);
var
bookId
=
list
.
closest
(
'[data-type="book"]'
).
attr
(
'data-id'
);
var
$
childElements
=
list
.
find
(
'[data-type="page"],
[data-type="chapter"]'
);
$
childElements
.
each
(
function
(
c
hildIndex
)
{
var
$
directChildren
=
list
.
find
(
'> [data-type="page"], >
[data-type="chapter"]'
);
$
directChildren
.
each
(
function
(
directC
hildIndex
)
{
var
$childElem
=
$
(
this
);
var
type
=
$childElem
.
attr
(
'data-type'
);
var
parentChapter
=
false
;
if
(
type
===
'page'
&&
$childElem
.
closest
(
'[data-type="chapter"]'
).
length
===
1
)
{
parentChapter
=
$childElem
.
closest
(
'[data-type="chapter"]'
).
attr
(
'data-id'
);
}
pageMap
.
push
({
id
:
$childElem
.
attr
(
'data-id'
),
var
childId
=
$childElem
.
attr
(
'data-id'
);
entityMap
.
push
({
id
:
childId
,
sort
:
directChildIndex
,
parentChapter
:
parentChapter
,
type
:
type
,
book
:
bookId
});
$chapterChildren
=
$childElem
.
find
(
'[data-type="page"]'
).
each
(
function
(
pageIndex
)
{
var
$chapterChild
=
$
(
this
);
entityMap
.
push
({
id
:
$chapterChild
.
attr
(
'data-id'
),
sort
:
pageIndex
,
parentChapter
:
childId
,
type
:
'page'
,
book
:
bookId
});
});
});
});
return
page
Map
;
return
entity
Map
;
}
});
...
...
Please
register
or
sign in
to post a comment