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-05-07 14:29:43 +0100
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Commit
fcfb9470c96c9bff054dbc28a7dea1d7b87ccb91
fcfb9470
1 parent
c99653f0
Added further attribute endpoints and added tests
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
280 additions
and
25 deletions
app/Entity.php
app/Http/Controllers/AttributeController.php
app/Http/Controllers/Controller.php
app/Http/routes.php
app/Repos/AttributeRepo.php
app/Repos/PageRepo.php
database/factories/ModelFactory.php
tests/Auth/AuthTest.php
tests/Entity/AttributeTests.php
tests/Entity/EntityTest.php
tests/Entity/PageDraftTest.php
tests/Permissions/RestrictionsTest.php
tests/TestCase.php
tests/UserProfileTest.php
app/Entity.php
View file @
fcfb947
<?php
namespace
BookStack
;
abstract
class
Entity
extends
Ownable
class
Entity
extends
Ownable
{
/**
...
...
@@ -201,10 +201,4 @@ abstract class Entity extends Ownable
return
$search
->
orderBy
(
'title_relevance'
,
'desc'
);
}
/**
* Get the url for this item.
* @return string
*/
abstract
public
function
getUrl
();
}
...
...
app/Http/Controllers/AttributeController.php
View file @
fcfb947
...
...
@@ -2,7 +2,6 @@
use
BookStack\Repos\AttributeRepo
;
use
Illuminate\Http\Request
;
use
BookStack\Http\Requests
;
class
AttributeController
extends
Controller
...
...
@@ -19,7 +18,6 @@ class AttributeController extends Controller
$this
->
attributeRepo
=
$attributeRepo
;
}
/**
* Get all the Attributes for a particular entity
* @param $entityType
...
...
@@ -27,6 +25,43 @@ class AttributeController extends Controller
*/
public
function
getForEntity
(
$entityType
,
$entityId
)
{
$attributes
=
$this
->
attributeRepo
->
getForEntity
(
$entityType
,
$entityId
);
return
response
()
->
json
(
$attributes
);
}
/**
* Update the attributes for a particular entity.
* @param $entityType
* @param $entityId
* @param Request $request
* @return mixed
*/
public
function
updateForEntity
(
$entityType
,
$entityId
,
Request
$request
)
{
$this
->
validate
(
$request
,
[
'attributes.*.name'
=>
'required|min:3|max:250'
,
'attributes.*.value'
=>
'max:250'
]);
$entity
=
$this
->
attributeRepo
->
getEntity
(
$entityType
,
$entityId
,
'update'
);
if
(
$entity
===
null
)
return
$this
->
jsonError
(
"Entity not found"
,
404
);
$inputAttributes
=
$request
->
input
(
'attributes'
);
$attributes
=
$this
->
attributeRepo
->
saveAttributesToEntity
(
$entity
,
$inputAttributes
);
return
response
()
->
json
(
$attributes
);
}
/**
* Get attribute name suggestions from a given search term.
* @param Request $request
*/
public
function
getNameSuggestions
(
Request
$request
)
{
$searchTerm
=
$request
->
get
(
'search'
);
$suggestions
=
$this
->
attributeRepo
->
getNameSuggestions
(
$searchTerm
);
return
response
()
->
json
(
$suggestions
);
}
}
...
...
app/Http/Controllers/Controller.php
View file @
fcfb947
...
...
@@ -110,4 +110,15 @@ abstract class Controller extends BaseController
return
true
;
}
/**
* Send back a json error message.
* @param string $messageText
* @param int $statusCode
* @return mixed
*/
protected
function
jsonError
(
$messageText
=
""
,
$statusCode
=
500
)
{
return
response
()
->
json
([
'message'
=>
$messageText
],
$statusCode
);
}
}
...
...
app/Http/routes.php
View file @
fcfb947
...
...
@@ -88,6 +88,8 @@ Route::group(['middleware' => 'auth'], function () {
// Attribute routes (AJAX)
Route
::
group
([
'prefix'
=>
'ajax/attributes'
],
function
()
{
Route
::
get
(
'/get/{entityType}/{entityId}'
,
'AttributeController@getForEntity'
);
Route
::
get
(
'/suggest'
,
'AttributeController@getNameSuggestions'
);
Route
::
post
(
'/update/{entityType}/{entityId}'
,
'AttributeController@updateForEntity'
);
});
// Links
...
...
app/Repos/AttributeRepo.php
View file @
fcfb947
...
...
@@ -28,5 +28,76 @@ class AttributeRepo
$this
->
permissionService
=
$ps
;
}
/**
* Get an entity instance of its particular type.
* @param $entityType
* @param $entityId
* @param string $action
*/
public
function
getEntity
(
$entityType
,
$entityId
,
$action
=
'view'
)
{
$entityInstance
=
$this
->
entity
->
getEntityInstance
(
$entityType
);
$searchQuery
=
$entityInstance
->
where
(
'id'
,
'='
,
$entityId
)
->
with
(
'attributes'
);
$searchQuery
=
$this
->
permissionService
->
enforceEntityRestrictions
(
$searchQuery
,
$action
);
return
$searchQuery
->
first
();
}
/**
* Get all attributes for a particular entity.
* @param string $entityType
* @param int $entityId
* @return mixed
*/
public
function
getForEntity
(
$entityType
,
$entityId
)
{
$entity
=
$this
->
getEntity
(
$entityType
,
$entityId
);
if
(
$entity
===
null
)
return
collect
();
return
$entity
->
attributes
;
}
/**
* Get attribute name suggestions from scanning existing attribute names.
* @param $searchTerm
* @return array
*/
public
function
getNameSuggestions
(
$searchTerm
)
{
if
(
$searchTerm
===
''
)
return
[];
$query
=
$this
->
attribute
->
where
(
'name'
,
'LIKE'
,
$searchTerm
.
'%'
)
->
groupBy
(
'name'
)
->
orderBy
(
'name'
,
'desc'
);
$query
=
$this
->
permissionService
->
filterRestrictedEntityRelations
(
$query
,
'attributes'
,
'entity_id'
,
'entity_type'
);
return
$query
->
get
([
'name'
])
->
pluck
(
'name'
);
}
/**
* Save an array of attributes to an entity
* @param Entity $entity
* @param array $attributes
* @return array|\Illuminate\Database\Eloquent\Collection
*/
public
function
saveAttributesToEntity
(
Entity
$entity
,
$attributes
=
[])
{
$entity
->
attributes
()
->
delete
();
$newAttributes
=
[];
foreach
(
$attributes
as
$attribute
)
{
$newAttributes
[]
=
$this
->
newInstanceFromInput
(
$attribute
);
}
return
$entity
->
attributes
()
->
saveMany
(
$newAttributes
);
}
/**
* Create a new Attribute instance from user input.
* @param $input
* @return static
*/
protected
function
newInstanceFromInput
(
$input
)
{
$name
=
trim
(
$input
[
'name'
]);
$value
=
isset
(
$input
[
'value'
])
?
trim
(
$input
[
'value'
])
:
''
;
// Any other modification or cleanup required can go here
$values
=
[
'name'
=>
$name
,
'value'
=>
$value
];
return
$this
->
attribute
->
newInstance
(
$values
);
}
}
\ No newline at end of file
...
...
app/Repos/PageRepo.php
View file @
fcfb947
...
...
@@ -582,6 +582,7 @@ class PageRepo extends EntityRepo
{
Activity
::
removeEntity
(
$page
);
$page
->
views
()
->
delete
();
$page
->
attributes
()
->
delete
();
$page
->
revisions
()
->
delete
();
$page
->
permissions
()
->
delete
();
$this
->
permissionService
->
deleteJointPermissionsForEntity
(
$page
);
...
...
database/factories/ModelFactory.php
View file @
fcfb947
...
...
@@ -53,3 +53,10 @@ $factory->define(BookStack\Role::class, function ($faker) {
'description'
=>
$faker
->
sentence
(
10
)
];
});
$factory
->
define
(
BookStack\Attribute
::
class
,
function
(
$faker
)
{
return
[
'name'
=>
$faker
->
city
,
'value'
=>
$faker
->
sentence
(
3
)
];
});
\ No newline at end of file
...
...
tests/Auth/AuthTest.php
View file @
fcfb947
...
...
@@ -181,7 +181,7 @@ class AuthTest extends TestCase
public
function
test_user_deletion
()
{
$userDetails
=
factory
(
\BookStack\User
::
class
)
->
make
();
$user
=
$this
->
get
NewUse
r
(
$userDetails
->
toArray
());
$user
=
$this
->
get
Edito
r
(
$userDetails
->
toArray
());
$this
->
asAdmin
()
->
visit
(
'/settings/users/'
.
$user
->
id
)
...
...
tests/Entity/AttributeTests.php
0 → 100644
View file @
fcfb947
<?php
namespace
Entity
;
use
BookStack\Attribute
;
use
BookStack\Page
;
use
BookStack\Services\PermissionService
;
class
AttributeTests
extends
\TestCase
{
protected
$defaultAttrCount
=
20
;
/**
* Get an instance of a page that has many attributes.
* @param Attribute[]|bool $attributes
* @return mixed
*/
protected
function
getPageWithAttributes
(
$attributes
=
false
)
{
$page
=
Page
::
first
();
if
(
!
$attributes
)
{
$attributes
=
factory
(
Attribute
::
class
,
$this
->
defaultAttrCount
)
->
make
();
}
$page
->
attributes
()
->
saveMany
(
$attributes
);
return
$page
;
}
public
function
test_get_page_attributes
()
{
$page
=
$this
->
getPageWithAttributes
();
// Add some other attributes to check they don't interfere
factory
(
Attribute
::
class
,
$this
->
defaultAttrCount
)
->
create
();
$this
->
asAdmin
()
->
get
(
"/ajax/attributes/get/page/"
.
$page
->
id
)
->
shouldReturnJson
();
$json
=
json_decode
(
$this
->
response
->
getContent
());
$this
->
assertTrue
(
count
(
$json
)
===
$this
->
defaultAttrCount
,
"Returned JSON item count is not as expected"
);
}
public
function
test_attribute_name_suggestions
()
{
// Create some attributes with similar names to test with
$attrs
=
collect
();
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'country'
]));
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'color'
]));
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'city'
]));
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'county'
]));
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'planet'
]));
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'plans'
]));
$page
=
$this
->
getPageWithAttributes
(
$attrs
);
$this
->
asAdmin
()
->
get
(
'/ajax/attributes/suggest?search=dog'
)
->
seeJsonEquals
([]);
$this
->
get
(
'/ajax/attributes/suggest?search=co'
)
->
seeJsonEquals
([
'color'
,
'country'
,
'county'
]);
$this
->
get
(
'/ajax/attributes/suggest?search=cou'
)
->
seeJsonEquals
([
'country'
,
'county'
]);
$this
->
get
(
'/ajax/attributes/suggest?search=pla'
)
->
seeJsonEquals
([
'planet'
,
'plans'
]);
}
public
function
test_entity_permissions_effect_attribute_suggestions
()
{
$permissionService
=
$this
->
app
->
make
(
PermissionService
::
class
);
// Create some attributes with similar names to test with and save to a page
$attrs
=
collect
();
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'country'
]));
$attrs
=
$attrs
->
merge
(
factory
(
Attribute
::
class
,
5
)
->
make
([
'name'
=>
'color'
]));
$page
=
$this
->
getPageWithAttributes
(
$attrs
);
$this
->
asAdmin
()
->
get
(
'/ajax/attributes/suggest?search=co'
)
->
seeJsonEquals
([
'color'
,
'country'
]);
$this
->
asEditor
()
->
get
(
'/ajax/attributes/suggest?search=co'
)
->
seeJsonEquals
([
'color'
,
'country'
]);
// Set restricted permission the page
$page
->
restricted
=
true
;
$page
->
save
();
$permissionService
->
buildJointPermissionsForEntity
(
$page
);
$this
->
asAdmin
()
->
get
(
'/ajax/attributes/suggest?search=co'
)
->
seeJsonEquals
([
'color'
,
'country'
]);
$this
->
asEditor
()
->
get
(
'/ajax/attributes/suggest?search=co'
)
->
seeJsonEquals
([]);
}
public
function
test_entity_attribute_updating
()
{
$page
=
$this
->
getPageWithAttributes
();
$testJsonData
=
[
[
'name'
=>
'color'
,
'value'
=>
'red'
],
[
'name'
=>
'color'
,
'value'
=>
' blue '
],
[
'name'
=>
'city'
,
'value'
=>
'London '
],
[
'name'
=>
'country'
,
'value'
=>
' England'
],
];
$testResponseJsonData
=
[
[
'name'
=>
'color'
,
'value'
=>
'red'
],
[
'name'
=>
'color'
,
'value'
=>
'blue'
],
[
'name'
=>
'city'
,
'value'
=>
'London'
],
[
'name'
=>
'country'
,
'value'
=>
'England'
],
];
$this
->
asAdmin
()
->
json
(
"POST"
,
"/ajax/attributes/update/page/"
.
$page
->
id
,
[
'attributes'
=>
$testJsonData
]);
$this
->
asAdmin
()
->
get
(
"/ajax/attributes/get/page/"
.
$page
->
id
);
$jsonData
=
json_decode
(
$this
->
response
->
getContent
());
// Check counts
$this
->
assertTrue
(
count
(
$jsonData
)
===
count
(
$testJsonData
),
"The received attribute count is incorrect"
);
// Check data is correct
$testDataCorrect
=
true
;
foreach
(
$jsonData
as
$data
)
{
$testItem
=
[
'name'
=>
$data
->
name
,
'value'
=>
$data
->
value
];
if
(
!
in_array
(
$testItem
,
$testResponseJsonData
))
$testDataCorrect
=
false
;
}
$testMessage
=
"Expected data was not found in the response.
\n
Expected Data: %s
\n
Recieved Data: %s"
;
$this
->
assertTrue
(
$testDataCorrect
,
sprintf
(
$testMessage
,
json_encode
(
$testResponseJsonData
),
json_encode
(
$jsonData
)));
}
}
tests/Entity/EntityTest.php
View file @
fcfb947
...
...
@@ -161,8 +161,8 @@ class EntityTest extends TestCase
public
function
test_entities_viewable_after_creator_deletion
()
{
// Create required assets and revisions
$creator
=
$this
->
get
NewUse
r
();
$updater
=
$this
->
get
NewUse
r
();
$creator
=
$this
->
get
Edito
r
();
$updater
=
$this
->
get
Edito
r
();
$entities
=
$this
->
createEntityChainBelongingToUser
(
$creator
,
$updater
);
$this
->
actingAs
(
$creator
);
app
(
'BookStack\Repos\UserRepo'
)
->
destroy
(
$creator
);
...
...
@@ -174,8 +174,8 @@ class EntityTest extends TestCase
public
function
test_entities_viewable_after_updater_deletion
()
{
// Create required assets and revisions
$creator
=
$this
->
get
NewUse
r
();
$updater
=
$this
->
get
NewUse
r
();
$creator
=
$this
->
get
Edito
r
();
$updater
=
$this
->
get
Edito
r
();
$entities
=
$this
->
createEntityChainBelongingToUser
(
$creator
,
$updater
);
$this
->
actingAs
(
$updater
);
app
(
'BookStack\Repos\UserRepo'
)
->
destroy
(
$updater
);
...
...
@@ -198,7 +198,7 @@ class EntityTest extends TestCase
public
function
test_recently_created_pages_view
()
{
$user
=
$this
->
get
NewUse
r
();
$user
=
$this
->
get
Edito
r
();
$content
=
$this
->
createEntityChainBelongingToUser
(
$user
);
$this
->
asAdmin
()
->
visit
(
'/pages/recently-created'
)
...
...
@@ -207,7 +207,7 @@ class EntityTest extends TestCase
public
function
test_recently_updated_pages_view
()
{
$user
=
$this
->
get
NewUse
r
();
$user
=
$this
->
get
Edito
r
();
$content
=
$this
->
createEntityChainBelongingToUser
(
$user
);
$this
->
asAdmin
()
->
visit
(
'/pages/recently-updated'
)
...
...
@@ -241,7 +241,7 @@ class EntityTest extends TestCase
public
function
test_recently_created_pages_on_home
()
{
$entityChain
=
$this
->
createEntityChainBelongingToUser
(
$this
->
get
NewUse
r
());
$entityChain
=
$this
->
createEntityChainBelongingToUser
(
$this
->
get
Edito
r
());
$this
->
asAdmin
()
->
visit
(
'/'
)
->
seeInElement
(
'#recently-created-pages'
,
$entityChain
[
'page'
]
->
name
);
}
...
...
tests/Entity/PageDraftTest.php
View file @
fcfb947
...
...
@@ -32,7 +32,7 @@ class PageDraftTest extends TestCase
->
dontSeeInField
(
'html'
,
$addedContent
);
$newContent
=
$this
->
page
->
html
.
$addedContent
;
$newUser
=
$this
->
get
NewUse
r
();
$newUser
=
$this
->
get
Edito
r
();
$this
->
pageRepo
->
saveUpdateDraft
(
$this
->
page
,
[
'html'
=>
$newContent
]);
$this
->
actingAs
(
$newUser
)
->
visit
(
$this
->
page
->
getUrl
()
.
'/edit'
)
->
dontSeeInField
(
'html'
,
$newContent
);
...
...
@@ -54,7 +54,7 @@ class PageDraftTest extends TestCase
->
dontSeeInField
(
'html'
,
$addedContent
);
$newContent
=
$this
->
page
->
html
.
$addedContent
;
$newUser
=
$this
->
get
NewUse
r
();
$newUser
=
$this
->
get
Edito
r
();
$this
->
pageRepo
->
saveUpdateDraft
(
$this
->
page
,
[
'html'
=>
$newContent
]);
$this
->
actingAs
(
$newUser
)
...
...
@@ -79,7 +79,7 @@ class PageDraftTest extends TestCase
{
$book
=
\BookStack\Book
::
first
();
$chapter
=
$book
->
chapters
->
first
();
$newUser
=
$this
->
get
NewUse
r
();
$newUser
=
$this
->
get
Edito
r
();
$this
->
actingAs
(
$newUser
)
->
visit
(
'/'
)
->
visit
(
$book
->
getUrl
()
.
'/page/create'
)
...
...
tests/Permissions/RestrictionsTest.php
View file @
fcfb947
...
...
@@ -9,7 +9,7 @@ class RestrictionsTest extends TestCase
public
function
setUp
()
{
parent
::
setUp
();
$this
->
user
=
$this
->
get
NewUse
r
();
$this
->
user
=
$this
->
get
Edito
r
();
$this
->
viewer
=
$this
->
getViewer
();
$this
->
restrictionService
=
$this
->
app
[
\BookStack\Services\PermissionService
::
class
];
}
...
...
tests/TestCase.php
View file @
fcfb947
...
...
@@ -14,7 +14,10 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
* @var string
*/
protected
$baseUrl
=
'http://localhost'
;
// Local user instances
private
$admin
;
private
$editor
;
/**
* Creates the application.
...
...
@@ -30,6 +33,10 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
return
$app
;
}
/**
* Set the current user context to be an admin.
* @return $this
*/
public
function
asAdmin
()
{
if
(
$this
->
admin
===
null
)
{
...
...
@@ -40,6 +47,18 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
}
/**
* Set the current editor context to be an editor.
* @return $this
*/
public
function
asEditor
()
{
if
(
$this
->
editor
===
null
)
{
$this
->
editor
=
$this
->
getEditor
();
}
return
$this
->
actingAs
(
$this
->
editor
);
}
/**
* Quickly sets an array of settings.
* @param $settingsArray
*/
...
...
@@ -79,7 +98,7 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
* @param array $attributes
* @return mixed
*/
protected
function
get
NewUse
r
(
$attributes
=
[])
protected
function
get
Edito
r
(
$attributes
=
[])
{
$user
=
factory
(
\BookStack\User
::
class
)
->
create
(
$attributes
);
$role
=
\BookStack\Role
::
getRole
(
'editor'
);
...
...
tests/UserProfileTest.php
View file @
fcfb947
...
...
@@ -33,7 +33,7 @@ class UserProfileTest extends TestCase
public
function
test_profile_page_shows_created_content_counts
()
{
$newUser
=
$this
->
get
NewUse
r
();
$newUser
=
$this
->
get
Edito
r
();
$this
->
asAdmin
()
->
visit
(
'/user/'
.
$newUser
->
id
)
->
see
(
$newUser
->
name
)
...
...
@@ -52,7 +52,7 @@ class UserProfileTest extends TestCase
public
function
test_profile_page_shows_recent_activity
()
{
$newUser
=
$this
->
get
NewUse
r
();
$newUser
=
$this
->
get
Edito
r
();
$this
->
actingAs
(
$newUser
);
$entities
=
$this
->
createEntityChainBelongingToUser
(
$newUser
,
$newUser
);
Activity
::
add
(
$entities
[
'book'
],
'book_update'
,
$entities
[
'book'
]
->
id
);
...
...
@@ -66,7 +66,7 @@ class UserProfileTest extends TestCase
public
function
test_clicking_user_name_in_activity_leads_to_profile_page
()
{
$newUser
=
$this
->
get
NewUse
r
();
$newUser
=
$this
->
get
Edito
r
();
$this
->
actingAs
(
$newUser
);
$entities
=
$this
->
createEntityChainBelongingToUser
(
$newUser
,
$newUser
);
Activity
::
add
(
$entities
[
'book'
],
'book_update'
,
$entities
[
'book'
]
->
id
);
...
...
Please
register
or
sign in
to post a comment