mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Merge branch 'master' into update_japanese_translation
This commit is contained in:
commit
03ee3d21ba
84
.github/CODE_OF_CONDUCT.md
vendored
Normal file
84
.github/CODE_OF_CONDUCT.md
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||||
|
education, socio-economic status, nationality, personal appearance, race,
|
||||||
|
religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
### Project Maintainer Standards
|
||||||
|
|
||||||
|
Project maintainers should generally follow these additional standards:
|
||||||
|
|
||||||
|
* Avoid using a negative or harsh tone in communication, Even if the other party
|
||||||
|
is being negative themselves.
|
||||||
|
* When providing criticism, try to make it constructive to lead the other person
|
||||||
|
down the correct path.
|
||||||
|
* Keep the [project definition](https://github.com/BookStackApp/BookStack#project-definition)
|
||||||
|
in mind when deciding what's in scope of the Project.
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior. In addition, Project
|
||||||
|
maintainers are responsible for following the standards themselves.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at the email address shown on [the profile here](https://github.com/ssddanbrown). All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
3
LICENSE
3
LICENSE
@ -1,6 +1,7 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016 Dan Brown
|
Copyright (c) 2018 Dan Brown and the BookStack Project contributors
|
||||||
|
https://github.com/BookStackApp/BookStack/graphs/contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
class Book extends Entity
|
class Book extends Entity
|
||||||
{
|
{
|
||||||
|
public $searchFactor = 2;
|
||||||
|
|
||||||
protected $fillable = ['name', 'description', 'image_id'];
|
protected $fillable = ['name', 'description', 'image_id'];
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
class Chapter extends Entity
|
class Chapter extends Entity
|
||||||
{
|
{
|
||||||
|
public $searchFactor = 1.3;
|
||||||
|
|
||||||
protected $fillable = ['name', 'description', 'priority', 'book_id'];
|
protected $fillable = ['name', 'description', 'priority', 'book_id'];
|
||||||
|
|
||||||
protected $with = ['book'];
|
protected $with = ['book'];
|
||||||
|
@ -5,8 +5,16 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|||||||
class Entity extends Ownable
|
class Entity extends Ownable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string - Name of property where the main text content is found
|
||||||
|
*/
|
||||||
public $textField = 'description';
|
public $textField = 'description';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var float - Multiplier for search indexing.
|
||||||
|
*/
|
||||||
|
public $searchFactor = 1.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares this entity to another given entity.
|
* Compares this entity to another given entity.
|
||||||
* Matches by comparing class and id.
|
* Matches by comparing class and id.
|
||||||
|
@ -120,7 +120,7 @@ class ImageController extends Controller
|
|||||||
{
|
{
|
||||||
$this->checkPermission('image-create-all');
|
$this->checkPermission('image-create-all');
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'file' => 'required|is_image'
|
'file' => 'is_image'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$this->imageRepo->isValidType($type)) {
|
if (!$this->imageRepo->isValidType($type)) {
|
||||||
@ -136,6 +136,7 @@ class ImageController extends Controller
|
|||||||
return response($e->getMessage(), 500);
|
return response($e->getMessage(), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return response()->json($image);
|
return response()->json($image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,13 +40,12 @@ class SearchController extends Controller
|
|||||||
$nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
|
$nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
|
||||||
|
|
||||||
$results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
|
$results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
|
||||||
$hasNextPage = $this->searchService->searchEntities($searchTerm, 'all', $page+1, 20)['count'] > 0;
|
|
||||||
|
|
||||||
return view('search/all', [
|
return view('search/all', [
|
||||||
'entities' => $results['results'],
|
'entities' => $results['results'],
|
||||||
'totalResults' => $results['total'],
|
'totalResults' => $results['total'],
|
||||||
'searchTerm' => $searchTerm,
|
'searchTerm' => $searchTerm,
|
||||||
'hasNextPage' => $hasNextPage,
|
'hasNextPage' => $results['has_more'],
|
||||||
'nextPageLink' => $nextPageLink
|
'nextPageLink' => $nextPageLink
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -774,7 +774,9 @@ class EntityRepo
|
|||||||
$scriptSearchRegex = '/<script.*?>.*?<\/script>/ms';
|
$scriptSearchRegex = '/<script.*?>.*?<\/script>/ms';
|
||||||
$matches = [];
|
$matches = [];
|
||||||
preg_match_all($scriptSearchRegex, $html, $matches);
|
preg_match_all($scriptSearchRegex, $html, $matches);
|
||||||
if (count($matches) === 0) return $html;
|
if (count($matches) === 0) {
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($matches[0] as $match) {
|
foreach ($matches[0] as $match) {
|
||||||
$html = str_replace($match, htmlentities($match), $html);
|
$html = str_replace($match, htmlentities($match), $html);
|
||||||
|
@ -225,7 +225,6 @@ class ImageRepo
|
|||||||
try {
|
try {
|
||||||
return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
|
return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
dd($exception);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,6 @@ class AttachmentService extends UploadService
|
|||||||
*/
|
*/
|
||||||
protected function getStorage()
|
protected function getStorage()
|
||||||
{
|
{
|
||||||
if ($this->storageInstance !== null) {
|
|
||||||
return $this->storageInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
$storageType = config('filesystems.default');
|
$storageType = config('filesystems.default');
|
||||||
|
|
||||||
// Override default location if set to local public to ensure not visible.
|
// Override default location if set to local public to ensure not visible.
|
||||||
@ -25,9 +21,7 @@ class AttachmentService extends UploadService
|
|||||||
$storageType = 'local_secure';
|
$storageType = 'local_secure';
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->storageInstance = $this->fileSystem->disk($storageType);
|
return $this->fileSystem->disk($storageType);
|
||||||
|
|
||||||
return $this->storageInstance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,23 @@ class ImageService extends UploadService
|
|||||||
parent::__construct($fileSystem);
|
parent::__construct($fileSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the storage that will be used for storing images.
|
||||||
|
* @param string $type
|
||||||
|
* @return \Illuminate\Contracts\Filesystem\Filesystem
|
||||||
|
*/
|
||||||
|
protected function getStorage($type = '')
|
||||||
|
{
|
||||||
|
$storageType = config('filesystems.default');
|
||||||
|
|
||||||
|
// Override default location if set to local public to ensure not visible.
|
||||||
|
if ($type === 'system' && $storageType === 'local_secure') {
|
||||||
|
$storageType = 'local';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fileSystem->disk($storageType);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a new image from an upload.
|
* Saves a new image from an upload.
|
||||||
* @param UploadedFile $uploadedFile
|
* @param UploadedFile $uploadedFile
|
||||||
@ -119,7 +136,7 @@ class ImageService extends UploadService
|
|||||||
*/
|
*/
|
||||||
private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
|
private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
|
||||||
{
|
{
|
||||||
$storage = $this->getStorage();
|
$storage = $this->getStorage($type);
|
||||||
$secureUploads = setting('app-secure-images');
|
$secureUploads = setting('app-secure-images');
|
||||||
$imageName = str_replace(' ', '-', $imageName);
|
$imageName = str_replace(' ', '-', $imageName);
|
||||||
|
|
||||||
@ -170,6 +187,16 @@ class ImageService extends UploadService
|
|||||||
return $image->path;
|
return $image->path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the image is a gif. Returns true if it is, else false.
|
||||||
|
* @param Image $image
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function isGif(Image $image)
|
||||||
|
{
|
||||||
|
return strtolower(pathinfo($this->getPath($image), PATHINFO_EXTENSION)) === 'gif';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the thumbnail for an image.
|
* Get the thumbnail for an image.
|
||||||
* If $keepRatio is true only the width will be used.
|
* If $keepRatio is true only the width will be used.
|
||||||
@ -184,6 +211,10 @@ class ImageService extends UploadService
|
|||||||
*/
|
*/
|
||||||
public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
|
public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
|
||||||
{
|
{
|
||||||
|
if ($keepRatio && $this->isGif($image)) {
|
||||||
|
return $this->getPublicUrl($this->getPath($image));
|
||||||
|
}
|
||||||
|
|
||||||
$thumbDirName = '/' . ($keepRatio ? 'scaled-' : 'thumbs-') . $width . '-' . $height . '/';
|
$thumbDirName = '/' . ($keepRatio ? 'scaled-' : 'thumbs-') . $width . '-' . $height . '/';
|
||||||
$imagePath = $this->getPath($image);
|
$imagePath = $this->getPath($image);
|
||||||
$thumbFilePath = dirname($imagePath) . $thumbDirName . basename($imagePath);
|
$thumbFilePath = dirname($imagePath) . $thumbDirName . basename($imagePath);
|
||||||
@ -192,7 +223,7 @@ class ImageService extends UploadService
|
|||||||
return $this->getPublicUrl($thumbFilePath);
|
return $this->getPublicUrl($thumbFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
$storage = $this->getStorage();
|
$storage = $this->getStorage($image->type);
|
||||||
if ($storage->exists($thumbFilePath)) {
|
if ($storage->exists($thumbFilePath)) {
|
||||||
return $this->getPublicUrl($thumbFilePath);
|
return $this->getPublicUrl($thumbFilePath);
|
||||||
}
|
}
|
||||||
@ -274,8 +305,9 @@ class ImageService extends UploadService
|
|||||||
/**
|
/**
|
||||||
* Save a gravatar image and set a the profile image for a user.
|
* Save a gravatar image and set a the profile image for a user.
|
||||||
* @param User $user
|
* @param User $user
|
||||||
* @param int $size
|
* @param int $size
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function saveUserGravatar(User $user, $size = 500)
|
public function saveUserGravatar(User $user, $size = 500)
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ class SearchService
|
|||||||
* @param string $searchString
|
* @param string $searchString
|
||||||
* @param string $entityType
|
* @param string $entityType
|
||||||
* @param int $page
|
* @param int $page
|
||||||
* @param int $count
|
* @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed.
|
||||||
* @return array[int, Collection];
|
* @return array[int, Collection];
|
||||||
*/
|
*/
|
||||||
public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20)
|
public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20)
|
||||||
@ -72,7 +72,6 @@ class SearchService
|
|||||||
$terms = $this->parseSearchString($searchString);
|
$terms = $this->parseSearchString($searchString);
|
||||||
$entityTypes = array_keys($this->entities);
|
$entityTypes = array_keys($this->entities);
|
||||||
$entityTypesToSearch = $entityTypes;
|
$entityTypesToSearch = $entityTypes;
|
||||||
$results = collect();
|
|
||||||
|
|
||||||
if ($entityType !== 'all') {
|
if ($entityType !== 'all') {
|
||||||
$entityTypesToSearch = $entityType;
|
$entityTypesToSearch = $entityType;
|
||||||
@ -80,20 +79,27 @@ class SearchService
|
|||||||
$entityTypesToSearch = explode('|', $terms['filters']['type']);
|
$entityTypesToSearch = explode('|', $terms['filters']['type']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$results = collect();
|
||||||
$total = 0;
|
$total = 0;
|
||||||
|
$hasMore = false;
|
||||||
|
|
||||||
foreach ($entityTypesToSearch as $entityType) {
|
foreach ($entityTypesToSearch as $entityType) {
|
||||||
if (!in_array($entityType, $entityTypes)) {
|
if (!in_array($entityType, $entityTypes)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$search = $this->searchEntityTable($terms, $entityType, $page, $count);
|
$search = $this->searchEntityTable($terms, $entityType, $page, $count);
|
||||||
$total += $this->searchEntityTable($terms, $entityType, $page, $count, true);
|
$entityTotal = $this->searchEntityTable($terms, $entityType, $page, $count, true);
|
||||||
|
if ($entityTotal > $page * $count) {
|
||||||
|
$hasMore = true;
|
||||||
|
}
|
||||||
|
$total += $entityTotal;
|
||||||
$results = $results->merge($search);
|
$results = $results->merge($search);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'count' => count($results),
|
'count' => count($results),
|
||||||
|
'has_more' => $hasMore,
|
||||||
'results' => $results->sortByDesc('score')->values()
|
'results' => $results->sortByDesc('score')->values()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -322,8 +328,8 @@ class SearchService
|
|||||||
public function indexEntity(Entity $entity)
|
public function indexEntity(Entity $entity)
|
||||||
{
|
{
|
||||||
$this->deleteEntityTerms($entity);
|
$this->deleteEntityTerms($entity);
|
||||||
$nameTerms = $this->generateTermArrayFromText($entity->name, 5);
|
$nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
|
||||||
$bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1);
|
$bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
|
||||||
$terms = array_merge($nameTerms, $bodyTerms);
|
$terms = array_merge($nameTerms, $bodyTerms);
|
||||||
foreach ($terms as $index => $term) {
|
foreach ($terms as $index => $term) {
|
||||||
$terms[$index]['entity_type'] = $entity->getMorphClass();
|
$terms[$index]['entity_type'] = $entity->getMorphClass();
|
||||||
@ -340,8 +346,8 @@ class SearchService
|
|||||||
{
|
{
|
||||||
$terms = [];
|
$terms = [];
|
||||||
foreach ($entities as $entity) {
|
foreach ($entities as $entity) {
|
||||||
$nameTerms = $this->generateTermArrayFromText($entity->name, 5);
|
$nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
|
||||||
$bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1);
|
$bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
|
||||||
foreach (array_merge($nameTerms, $bodyTerms) as $term) {
|
foreach (array_merge($nameTerms, $bodyTerms) as $term) {
|
||||||
$term['entity_id'] = $entity->id;
|
$term['entity_id'] = $entity->id;
|
||||||
$term['entity_type'] = $entity->getMorphClass();
|
$term['entity_type'] = $entity->getMorphClass();
|
||||||
|
@ -11,11 +11,6 @@ class UploadService
|
|||||||
*/
|
*/
|
||||||
protected $fileSystem;
|
protected $fileSystem;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var FileSystemInstance
|
|
||||||
*/
|
|
||||||
protected $storageInstance;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FileService constructor.
|
* FileService constructor.
|
||||||
@ -32,14 +27,8 @@ class UploadService
|
|||||||
*/
|
*/
|
||||||
protected function getStorage()
|
protected function getStorage()
|
||||||
{
|
{
|
||||||
if ($this->storageInstance !== null) {
|
|
||||||
return $this->storageInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
$storageType = config('filesystems.default');
|
$storageType = config('filesystems.default');
|
||||||
$this->storageInstance = $this->fileSystem->disk($storageType);
|
return $this->fileSystem->disk($storageType);
|
||||||
|
|
||||||
return $this->storageInstance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,13 +42,4 @@ class UploadService
|
|||||||
$folders = $this->getStorage()->directories($path);
|
$folders = $this->getStorage()->directories($path);
|
||||||
return (count($files) === 0 && count($folders) === 0);
|
return (count($files) === 0 && count($folders) === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if using a local filesystem.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function isLocal()
|
|
||||||
{
|
|
||||||
return strtolower(config('filesystems.default')) === 'local';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
28
readme.md
28
readme.md
@ -17,7 +17,9 @@ A platform for storing and organising information and documentation. General inf
|
|||||||
|
|
||||||
BookStack is an opinionated wiki system that provides a pleasant and simple out of the box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
|
BookStack is an opinionated wiki system that provides a pleasant and simple out of the box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
|
||||||
|
|
||||||
BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above.
|
BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above.
|
||||||
|
|
||||||
|
In regards to development philosophy, BookStack has a relaxed, open & positive approach. Put simply, At the end of the day this is free software developed and maintained by people donating their own free time.
|
||||||
|
|
||||||
## Development & Testing
|
## Development & Testing
|
||||||
|
|
||||||
@ -25,17 +27,17 @@ All development on BookStack is currently done on the master branch. When it's t
|
|||||||
|
|
||||||
* [Node.js](https://nodejs.org/en/) v6.9+
|
* [Node.js](https://nodejs.org/en/) v6.9+
|
||||||
|
|
||||||
SASS is used to help the CSS development and the JavaScript is run through browserify/babel to allow for writing ES6 code. Both of these are done using gulp. To run the build task you can use the following commands:
|
SASS is used to help the CSS development and the JavaScript is run through babel to allow for writing ES6 code. This is done using webpack. To run the build task you can use the following commands:
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
# Build assets for development
|
# Build assets for development
|
||||||
npm run-script build
|
npm run build
|
||||||
|
|
||||||
# Build and minify assets for production
|
# Build and minify assets for production
|
||||||
npm run-script production
|
npm run production
|
||||||
|
|
||||||
# Build for dev (With sourcemaps) and watch for changes
|
# Build for dev (With sourcemaps) and watch for changes
|
||||||
npm run-script dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. To use you will need PHPUnit installed and accessible via command line. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the following database name, user name and password defined as `bookstack-test`. You will have to create that database and credentials before testing.
|
BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. To use you will need PHPUnit installed and accessible via command line. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the following database name, user name and password defined as `bookstack-test`. You will have to create that database and credentials before testing.
|
||||||
@ -65,14 +67,16 @@ php resources/lang/check.php <lang>
|
|||||||
php resources/lang/check.php fr
|
php resources/lang/check.php fr
|
||||||
php resources/lang/check.php pt_BR
|
php resources/lang/check.php pt_BR
|
||||||
```
|
```
|
||||||
|
|
||||||
Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
|
Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
|
||||||
|
|
||||||
## Contributing
|
## Contributing & Maintenence
|
||||||
|
|
||||||
Feel free to create issues to request new features or to report bugs and problems. Just please follow the template given when creating the issue.
|
Feel free to create issues to request new features or to report bugs and problems. Just please follow the template given when creating the issue.
|
||||||
|
|
||||||
### Standards
|
The project's code of conduct [can be found here](https://github.com/BookStackApp/BookStack/blob/master/.github/CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
### Code Standards
|
||||||
|
|
||||||
PHP code within BookStack is generally to [PSR-2](http://www.php-fig.org/psr/psr-2/) standards. From the BookStack root folder you can run `./vendor/bin/phpcs` to check code is formatted correctly and `./vendor/bin/phpcbf` to auto-fix non-PSR-2 code.
|
PHP code within BookStack is generally to [PSR-2](http://www.php-fig.org/psr/psr-2/) standards. From the BookStack root folder you can run `./vendor/bin/phpcs` to check code is formatted correctly and `./vendor/bin/phpcbf` to auto-fix non-PSR-2 code.
|
||||||
|
|
||||||
@ -82,9 +86,9 @@ Pull requests are very welcome. If the scope of your pull request is large it ma
|
|||||||
|
|
||||||
Pull requests should be created from the `master` branch and should be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases.
|
Pull requests should be created from the `master` branch and should be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases.
|
||||||
|
|
||||||
If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
|
If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
|
||||||
|
|
||||||
## Website, Docs & Blog
|
## Website, Docs & Blog
|
||||||
|
|
||||||
The website project docs & Blog can be found in the [BookStackApp/website](https://github.com/BookStackApp/website) repo.
|
The website project docs & Blog can be found in the [BookStackApp/website](https://github.com/BookStackApp/website) repo.
|
||||||
|
|
||||||
@ -94,6 +98,8 @@ The BookStack source is provided under the MIT License.
|
|||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
|
The great people that have worked to build and improve BookStack can [be seen here](https://github.com/BookStackApp/BookStack/graphs/contributors).
|
||||||
|
|
||||||
These are the great open-source projects used to help build BookStack:
|
These are the great open-source projects used to help build BookStack:
|
||||||
|
|
||||||
* [Laravel](http://laravel.com/)
|
* [Laravel](http://laravel.com/)
|
||||||
|
@ -12,7 +12,9 @@ const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
|
|||||||
function mounted() {
|
function mounted() {
|
||||||
let container = this.$el;
|
let container = this.$el;
|
||||||
let _this = this;
|
let _this = this;
|
||||||
new DropZone(container, {
|
this._dz = new DropZone(container, {
|
||||||
|
addRemoveLinks: true,
|
||||||
|
dictRemoveFile: trans('components.image_upload_remove'),
|
||||||
url: function() {
|
url: function() {
|
||||||
return _this.uploadUrl;
|
return _this.uploadUrl;
|
||||||
},
|
},
|
||||||
@ -35,26 +37,33 @@ function mounted() {
|
|||||||
|
|
||||||
dz.on('error', function (file, errorMessage, xhr) {
|
dz.on('error', function (file, errorMessage, xhr) {
|
||||||
_this.$emit('error', {file, errorMessage, xhr});
|
_this.$emit('error', {file, errorMessage, xhr});
|
||||||
console.log(errorMessage);
|
|
||||||
console.log(xhr);
|
|
||||||
function setMessage(message) {
|
function setMessage(message) {
|
||||||
$(file.previewElement).find('[data-dz-errormessage]').text(message);
|
$(file.previewElement).find('[data-dz-errormessage]').text(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
|
if (xhr && xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
|
||||||
if (errorMessage.file) setMessage(errorMessage.file[0]);
|
else if (errorMessage.file) setMessage(errorMessage.file);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function data() {
|
function data() {
|
||||||
return {}
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const methods = {
|
||||||
|
onClose: function () {
|
||||||
|
this._dz.removeAllFiles(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template,
|
template,
|
||||||
props,
|
props,
|
||||||
mounted,
|
mounted,
|
||||||
data,
|
data,
|
||||||
};
|
methods
|
||||||
|
};
|
||||||
|
@ -43,6 +43,8 @@ const methods = {
|
|||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.showing = false;
|
this.showing = false;
|
||||||
|
this.selectedImage = false;
|
||||||
|
this.$refs.dropzone.onClose();
|
||||||
this.$el.children[0].components.overlay.hide();
|
this.$el.children[0].components.overlay.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -175,4 +177,4 @@ module.exports = {
|
|||||||
data,
|
data,
|
||||||
computed,
|
computed,
|
||||||
components: {dropzone},
|
components: {dropzone},
|
||||||
};
|
};
|
||||||
|
@ -224,15 +224,15 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
width: 28px;
|
width: 28px;
|
||||||
padding-left: $-xs;
|
padding-left: $-xs;
|
||||||
padding-right: $-xs;
|
padding-right: $-xs;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #EEE;
|
background-color: #EEE;
|
||||||
}
|
}
|
||||||
i {
|
.svg-icon {
|
||||||
flex: 1;
|
margin-right: 0px;
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> div .outline input {
|
> div .outline input {
|
||||||
@ -258,4 +258,4 @@
|
|||||||
background-color: #F8F8F8;
|
background-color: #F8F8F8;
|
||||||
padding: $-m;
|
padding: $-m;
|
||||||
border: 1px solid #DDD;
|
border: 1px solid #DDD;
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,7 @@ $button-border-radius: 2px;
|
|||||||
left: $-m;
|
left: $-m;
|
||||||
top: $-s - 2px;
|
top: $-s - 2px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
}
|
}
|
||||||
padding: $-s $-m;
|
padding: $-s $-m;
|
||||||
padding-bottom: $-s - 2px;
|
padding-bottom: $-s - 2px;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
svg {
|
svg {
|
||||||
fill: #EEEEEE;
|
fill: #EEEEEE;
|
||||||
width: 4em;
|
width: 4em;
|
||||||
|
height: 4em;
|
||||||
padding-right: $-m;
|
padding-right: $-m;
|
||||||
}
|
}
|
||||||
span {
|
span {
|
||||||
@ -211,6 +212,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
padding: $-m $-l;
|
padding: $-m $-l;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
border-left: 1px solid #DDD;
|
border-left: 1px solid #DDD;
|
||||||
.dropzone-container {
|
.dropzone-container {
|
||||||
margin-top: $-m;
|
margin-top: $-m;
|
||||||
@ -315,8 +317,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
|
|
||||||
.dz-preview.dz-file-preview .dz-image {
|
.dz-preview.dz-file-preview .dz-image {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: #999;
|
background: #e9e9e9;
|
||||||
background: linear-gradient(to bottom, #eee, #ddd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview.dz-file-preview .dz-details {
|
.dz-preview.dz-file-preview .dz-details {
|
||||||
@ -332,11 +333,12 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview .dz-remove {
|
.dz-preview .dz-remove {
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
border: none;
|
||||||
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview .dz-remove:hover {
|
.dz-preview .dz-remove:hover {
|
||||||
@ -385,7 +387,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview .dz-details .dz-filename span, .dz-preview .dz-details .dz-size span {
|
.dz-preview .dz-details .dz-filename span {
|
||||||
background-color: rgba(255, 255, 255, 0.4);
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
padding: 0 0.4em;
|
padding: 0 0.4em;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@ -421,13 +423,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
.dz-preview .dz-success-mark, .dz-preview .dz-error-mark {
|
.dz-preview .dz-success-mark, .dz-preview .dz-error-mark {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
z-index: 500;
|
z-index: 1001;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: block;
|
display: block;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin-left: -27px;
|
margin-left: -27px;
|
||||||
margin-top: -27px;
|
margin-top: -35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview .dz-success-mark svg, .dz-preview .dz-error-mark svg {
|
.dz-preview .dz-success-mark svg, .dz-preview .dz-error-mark svg {
|
||||||
@ -482,9 +484,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview.dz-error:hover .dz-error-message {
|
.dz-preview.dz-error {
|
||||||
opacity: 1;
|
.dz-image, .dz-details {
|
||||||
pointer-events: auto;
|
&:hover ~ .dz-error-message {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dz-preview .dz-error-message {
|
.dz-preview .dz-error-message {
|
||||||
@ -496,7 +502,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 11.5px;
|
font-size: 12px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
top: 88px;
|
top: 88px;
|
||||||
left: -26px;
|
left: -26px;
|
||||||
@ -597,4 +603,4 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||||||
.text-muted {
|
.text-muted {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,9 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
div[toolbox-tab-content] .padded.files {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin: $-m 0 0 0;
|
margin: $-m 0 0 0;
|
||||||
|
@ -424,6 +424,7 @@ i {
|
|||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: -0.105em;
|
bottom: -0.105em;
|
||||||
|
@ -118,6 +118,7 @@ $btt-size: 40px;
|
|||||||
fill: #FFF;
|
fill: #FFF;
|
||||||
svg {
|
svg {
|
||||||
width: $btt-size / 1.5;
|
width: $btt-size / 1.5;
|
||||||
|
height: $btt-size / 1.5;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
width: $btt-size;
|
width: $btt-size;
|
||||||
|
@ -21,6 +21,7 @@ return [
|
|||||||
'image_upload_success' => 'Image uploaded successfully',
|
'image_upload_success' => 'Image uploaded successfully',
|
||||||
'image_update_success' => 'Image details successfully updated',
|
'image_update_success' => 'Image details successfully updated',
|
||||||
'image_delete_success' => 'Image successfully deleted',
|
'image_delete_success' => 'Image successfully deleted',
|
||||||
|
'image_upload_remove' => 'Remove',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Code editor
|
* Code editor
|
||||||
@ -29,4 +30,4 @@ return [
|
|||||||
'code_language' => 'Code Language',
|
'code_language' => 'Code Language',
|
||||||
'code_content' => 'Code Content',
|
'code_content' => 'Code Content',
|
||||||
'code_save' => 'Save Code',
|
'code_save' => 'Save Code',
|
||||||
];
|
];
|
||||||
|
@ -35,6 +35,7 @@ return [
|
|||||||
'cannot_get_image_from_url' => 'Cannot get image from :url',
|
'cannot_get_image_from_url' => 'Cannot get image from :url',
|
||||||
'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
|
'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
|
||||||
'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
|
'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
|
||||||
|
'uploaded' => 'The server does not allow uploads of this size. Please try a smaller file size.',
|
||||||
'image_upload_error' => 'An error occurred uploading the image',
|
'image_upload_error' => 'An error occurred uploading the image',
|
||||||
'image_upload_type_error' => 'The image type being uploaded is invalid',
|
'image_upload_type_error' => 'The image type being uploaded is invalid',
|
||||||
|
|
||||||
@ -78,4 +79,4 @@ return [
|
|||||||
'error_occurred' => 'An Error Occurred',
|
'error_occurred' => 'An Error Occurred',
|
||||||
'app_down' => ':appName is down right now',
|
'app_down' => ':appName is down right now',
|
||||||
'back_soon' => 'It will be back up soon.',
|
'back_soon' => 'It will be back up soon.',
|
||||||
];
|
];
|
||||||
|
@ -37,4 +37,7 @@ return [
|
|||||||
'book_sort' => 'a réordonné le livre',
|
'book_sort' => 'a réordonné le livre',
|
||||||
'book_sort_notification' => 'Livre réordonné avec succès',
|
'book_sort_notification' => 'Livre réordonné avec succès',
|
||||||
|
|
||||||
|
// Other
|
||||||
|
'commented_on' => 'a commenté'
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -18,6 +18,8 @@ return [
|
|||||||
*/
|
*/
|
||||||
'sign_up' => "S'inscrire",
|
'sign_up' => "S'inscrire",
|
||||||
'log_in' => 'Se connecter',
|
'log_in' => 'Se connecter',
|
||||||
|
'log_in_with' => 'Se connecter avec :socialDriver',
|
||||||
|
'sign_up_with' => 'S\'inscrire avec :socialDriver',
|
||||||
'logout' => 'Se déconnecter',
|
'logout' => 'Se déconnecter',
|
||||||
|
|
||||||
'name' => 'Nom',
|
'name' => 'Nom',
|
||||||
|
@ -10,6 +10,7 @@ return [
|
|||||||
'save' => 'Enregistrer',
|
'save' => 'Enregistrer',
|
||||||
'continue' => 'Continuer',
|
'continue' => 'Continuer',
|
||||||
'select' => 'Sélectionner',
|
'select' => 'Sélectionner',
|
||||||
|
'more' => 'Montrer plus',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form Labels
|
* Form Labels
|
||||||
@ -29,11 +30,13 @@ return [
|
|||||||
'edit' => 'Editer',
|
'edit' => 'Editer',
|
||||||
'sort' => 'Trier',
|
'sort' => 'Trier',
|
||||||
'move' => 'Déplacer',
|
'move' => 'Déplacer',
|
||||||
|
'reply' => 'Répondre',
|
||||||
'delete' => 'Supprimer',
|
'delete' => 'Supprimer',
|
||||||
'search' => 'Chercher',
|
'search' => 'Chercher',
|
||||||
'search_clear' => 'Réinitialiser la recherche',
|
'search_clear' => 'Réinitialiser la recherche',
|
||||||
'reset' => 'Réinitialiser',
|
'reset' => 'Réinitialiser',
|
||||||
'remove' => 'Enlever',
|
'remove' => 'Enlever',
|
||||||
|
'add' => 'Ajouter',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,6 +48,9 @@ return [
|
|||||||
'back_to_top' => 'Retour en haut',
|
'back_to_top' => 'Retour en haut',
|
||||||
'toggle_details' => 'Afficher les détails',
|
'toggle_details' => 'Afficher les détails',
|
||||||
'toggle_thumbnails' => 'Afficher les vignettes',
|
'toggle_thumbnails' => 'Afficher les vignettes',
|
||||||
|
'details' => 'Détails',
|
||||||
|
'grid_view' => 'Vue en grille',
|
||||||
|
'list_view' => 'Vue en liste',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Header
|
* Header
|
||||||
|
@ -20,5 +20,13 @@ return [
|
|||||||
'image_preview' => 'Prévisualiser l\'image',
|
'image_preview' => 'Prévisualiser l\'image',
|
||||||
'image_upload_success' => 'Image ajoutée avec succès',
|
'image_upload_success' => 'Image ajoutée avec succès',
|
||||||
'image_update_success' => 'Détails de l\'image mis à jour',
|
'image_update_success' => 'Détails de l\'image mis à jour',
|
||||||
'image_delete_success' => 'Image supprimée avec succès'
|
'image_delete_success' => 'Image supprimée avec succès',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code editor
|
||||||
|
*/
|
||||||
|
'code_editor' => 'Editer le code',
|
||||||
|
'code_language' => 'Language du code',
|
||||||
|
'code_content' => 'Contenu du code',
|
||||||
|
'code_save' => 'Enregistrer le code',
|
||||||
];
|
];
|
||||||
|
@ -14,6 +14,7 @@ return [
|
|||||||
'recent_activity' => 'Activité récente',
|
'recent_activity' => 'Activité récente',
|
||||||
'create_now' => 'En créer un maintenant',
|
'create_now' => 'En créer un maintenant',
|
||||||
'revisions' => 'Révisions',
|
'revisions' => 'Révisions',
|
||||||
|
'meta_revision' => 'Révision #:revisionCount',
|
||||||
'meta_created' => 'Créé :timeLength',
|
'meta_created' => 'Créé :timeLength',
|
||||||
'meta_created_name' => 'Créé :timeLength par :user',
|
'meta_created_name' => 'Créé :timeLength par :user',
|
||||||
'meta_updated' => 'Mis à jour :timeLength',
|
'meta_updated' => 'Mis à jour :timeLength',
|
||||||
@ -43,19 +44,39 @@ return [
|
|||||||
* Search
|
* Search
|
||||||
*/
|
*/
|
||||||
'search_results' => 'Résultats de recherche',
|
'search_results' => 'Résultats de recherche',
|
||||||
|
'search_total_results_found' => ':count résultats trouvés|:count résultats trouvés au total',
|
||||||
'search_clear' => 'Réinitialiser la recherche',
|
'search_clear' => 'Réinitialiser la recherche',
|
||||||
'search_no_pages' => 'Aucune page correspondant à cette recherche',
|
'search_no_pages' => 'Aucune page correspondant à cette recherche',
|
||||||
'search_for_term' => 'recherche pour :term',
|
'search_for_term' => 'recherche pour :term',
|
||||||
|
'search_more' => 'Plus de résultats',
|
||||||
|
'search_filters' => 'Filtres de recherche',
|
||||||
|
'search_content_type' => 'Type de contenu',
|
||||||
|
'search_exact_matches' => 'Correspondances exactes',
|
||||||
|
'search_tags' => 'Recherche par tags',
|
||||||
|
'search_viewed_by_me' => 'Vu par moi',
|
||||||
|
'search_not_viewed_by_me' => 'Non vu par moi',
|
||||||
|
'search_permissions_set' => 'Ensemble d\'autorisations',
|
||||||
|
'search_created_by_me' => 'Créé par moi',
|
||||||
|
'search_updated_by_me' => 'Mis à jour par moi',
|
||||||
|
'search_updated_before' => 'Mis à jour avant',
|
||||||
|
'search_updated_after' => 'Mis à jour après',
|
||||||
|
'search_created_before' => 'Créé avant',
|
||||||
|
'search_created_after' => 'Créé après',
|
||||||
|
'search_set_date' => 'Choisir la date',
|
||||||
|
'search_update' => 'Actualiser la recherche',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Books
|
* Books
|
||||||
*/
|
*/
|
||||||
'book' => 'Livre',
|
'book' => 'Livre',
|
||||||
'books' => 'Livres',
|
'books' => 'Livres',
|
||||||
|
'x_books' => ':count livre|:count livres',
|
||||||
'books_empty' => 'Aucun livre n\'a été créé',
|
'books_empty' => 'Aucun livre n\'a été créé',
|
||||||
'books_popular' => 'Livres populaires',
|
'books_popular' => 'Livres populaires',
|
||||||
'books_recent' => 'Livres récents',
|
'books_recent' => 'Livres récents',
|
||||||
|
'books_new' => 'Nouveaux livres',
|
||||||
'books_popular_empty' => 'Les livres les plus populaires apparaîtront ici.',
|
'books_popular_empty' => 'Les livres les plus populaires apparaîtront ici.',
|
||||||
|
'books_new_empty' => 'Les livres les plus récents apparaitront ici.',
|
||||||
'books_create' => 'Créer un nouveau livre',
|
'books_create' => 'Créer un nouveau livre',
|
||||||
'books_delete' => 'Supprimer un livre',
|
'books_delete' => 'Supprimer un livre',
|
||||||
'books_delete_named' => 'Supprimer le livre :bookName',
|
'books_delete_named' => 'Supprimer le livre :bookName',
|
||||||
@ -85,6 +106,7 @@ return [
|
|||||||
*/
|
*/
|
||||||
'chapter' => 'Chapitre',
|
'chapter' => 'Chapitre',
|
||||||
'chapters' => 'Chapitres',
|
'chapters' => 'Chapitres',
|
||||||
|
'x_chapters' => ':count chapitre|:count chapitres',
|
||||||
'chapters_popular' => 'Chapitres populaires',
|
'chapters_popular' => 'Chapitres populaires',
|
||||||
'chapters_new' => 'Nouveau chapitre',
|
'chapters_new' => 'Nouveau chapitre',
|
||||||
'chapters_create' => 'Créer un nouveau chapitre',
|
'chapters_create' => 'Créer un nouveau chapitre',
|
||||||
@ -102,6 +124,7 @@ return [
|
|||||||
'chapters_empty' => 'Il n\'y a pas de page dans ce chapitre actuellement.',
|
'chapters_empty' => 'Il n\'y a pas de page dans ce chapitre actuellement.',
|
||||||
'chapters_permissions_active' => 'Permissions du chapitre activées',
|
'chapters_permissions_active' => 'Permissions du chapitre activées',
|
||||||
'chapters_permissions_success' => 'Permissions du chapitre mises à jour',
|
'chapters_permissions_success' => 'Permissions du chapitre mises à jour',
|
||||||
|
'chapters_search_this' => 'Rechercher dans ce chapitre',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pages
|
* Pages
|
||||||
@ -139,16 +162,19 @@ return [
|
|||||||
'pages_md_preview' => 'Prévisualisation',
|
'pages_md_preview' => 'Prévisualisation',
|
||||||
'pages_md_insert_image' => 'Insérer une image',
|
'pages_md_insert_image' => 'Insérer une image',
|
||||||
'pages_md_insert_link' => 'Insérer un lien',
|
'pages_md_insert_link' => 'Insérer un lien',
|
||||||
|
'pages_md_insert_drawing' => 'Insérer un dessin',
|
||||||
'pages_not_in_chapter' => 'La page n\'est pas dans un chapitre',
|
'pages_not_in_chapter' => 'La page n\'est pas dans un chapitre',
|
||||||
'pages_move' => 'Déplacer la page',
|
'pages_move' => 'Déplacer la page',
|
||||||
'pages_move_success' => 'Page déplacée à ":parentName"',
|
'pages_move_success' => 'Page déplacée à ":parentName"',
|
||||||
'pages_permissions' => 'Permissions de la page',
|
'pages_permissions' => 'Permissions de la page',
|
||||||
'pages_permissions_success' => 'Permissions de la page mises à jour',
|
'pages_permissions_success' => 'Permissions de la page mises à jour',
|
||||||
|
'pages_revision' => 'Révision',
|
||||||
'pages_revisions' => 'Révisions de la page',
|
'pages_revisions' => 'Révisions de la page',
|
||||||
'pages_revisions_named' => 'Révisions pour :pageName',
|
'pages_revisions_named' => 'Révisions pour :pageName',
|
||||||
'pages_revision_named' => 'Révision pour :pageName',
|
'pages_revision_named' => 'Révision pour :pageName',
|
||||||
'pages_revisions_created_by' => 'Créé par',
|
'pages_revisions_created_by' => 'Créé par',
|
||||||
'pages_revisions_date' => 'Date de révision',
|
'pages_revisions_date' => 'Date de révision',
|
||||||
|
'pages_revisions_number' => '#',
|
||||||
'pages_revisions_changelog' => 'Journal des changements',
|
'pages_revisions_changelog' => 'Journal des changements',
|
||||||
'pages_revisions_changes' => 'Changements',
|
'pages_revisions_changes' => 'Changements',
|
||||||
'pages_revisions_current' => 'Version courante',
|
'pages_revisions_current' => 'Version courante',
|
||||||
@ -219,6 +245,18 @@ return [
|
|||||||
*/
|
*/
|
||||||
'comment' => 'Commentaire',
|
'comment' => 'Commentaire',
|
||||||
'comments' => 'Commentaires',
|
'comments' => 'Commentaires',
|
||||||
|
'comment_add' => 'Ajouter un commentaire',
|
||||||
'comment_placeholder' => 'Entrez vos commentaires ici',
|
'comment_placeholder' => 'Entrez vos commentaires ici',
|
||||||
|
'comment_count' => '{0} Pas de commentaires|{1} 1 Commentaire|[2,*] :count Commentaires',
|
||||||
'comment_save' => 'Enregistrer le commentaire',
|
'comment_save' => 'Enregistrer le commentaire',
|
||||||
|
'comment_saving' => 'Enregistrement du commentaire...',
|
||||||
|
'comment_deleting' => 'Suppression du commentaire...',
|
||||||
|
'comment_new' => 'Nouveau commentaire',
|
||||||
|
'comment_created' => 'commenté :createDiff',
|
||||||
|
'comment_updated' => 'Mis à jour :updateDiff par :username',
|
||||||
|
'comment_deleted_success' => 'Commentaire supprimé',
|
||||||
|
'comment_created_success' => 'Commentaire ajouté',
|
||||||
|
'comment_updated_success' => 'Commentaire mis à jour',
|
||||||
|
'comment_delete_confirm' => 'Etes-vous sûr de vouloir supprimer ce commentaire?',
|
||||||
|
'comment_in_reply_to' => 'En réponse à :commentId',
|
||||||
];
|
];
|
||||||
|
@ -31,6 +31,9 @@ return [
|
|||||||
'app_logo_desc' => 'Cette image doit faire 43px de hauteur. <br>Les images plus larges seront réduites.',
|
'app_logo_desc' => 'Cette image doit faire 43px de hauteur. <br>Les images plus larges seront réduites.',
|
||||||
'app_primary_color' => 'Couleur principale de l\'application',
|
'app_primary_color' => 'Couleur principale de l\'application',
|
||||||
'app_primary_color_desc' => 'Cela devrait être une valeur hexadécimale. <br>Laisser vide pour rétablir la couleur par défaut.',
|
'app_primary_color_desc' => 'Cela devrait être une valeur hexadécimale. <br>Laisser vide pour rétablir la couleur par défaut.',
|
||||||
|
'app_homepage' => 'Page d\'accueil de l\'application',
|
||||||
|
'app_homepage_desc' => 'Choisissez une page à afficher sur la page d\'accueil au lieu de la vue par défaut. Les permissions sont ignorées pour les pages sélectionnées.',
|
||||||
|
'app_homepage_default' => 'Page d\'accueil par défaut sélectionnée',
|
||||||
'app_disable_comments' => 'Désactiver les commentaires',
|
'app_disable_comments' => 'Désactiver les commentaires',
|
||||||
'app_disable_comments_desc' => 'Désactive les commentaires sur toutes les pages de l\'application. Les commentaires existants ne sont pas affichés.',
|
'app_disable_comments_desc' => 'Désactive les commentaires sur toutes les pages de l\'application. Les commentaires existants ne sont pas affichés.',
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="{{ baseUrl('/books') }}" class="text-button">@icon('book'){{ trans('entities.books') }}</a>
|
<a href="{{ baseUrl('/books') }}" class="text-button">@icon('book'){{ trans('entities.books') }}</a>
|
||||||
<span class="sep">»</span>
|
<span class="sep">»</span>
|
||||||
<a href="{{ baseUrl('/books/create') }}" class="text-button">@icon('add'){{ trans('entities.books_create') }}</a>
|
<a href="{{ baseUrl('/create-book') }}" class="text-button">@icon('add'){{ trans('entities.books_create') }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@stop
|
@stop
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<div class="col-xs-6 faded">
|
<div class="col-xs-6 faded">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@if($currentUser->can('book-create-all'))
|
@if($currentUser->can('book-create-all'))
|
||||||
<a href="{{ baseUrl("/books/create") }}" class="text-pos text-button">@icon('add'){{ trans('entities.books_create') }}</a>
|
<a href="{{ baseUrl("/create-book") }}" class="text-pos text-button">@icon('add'){{ trans('entities.books_create') }}</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -78,7 +78,7 @@
|
|||||||
@else
|
@else
|
||||||
<p class="text-muted">{{ trans('entities.books_empty') }}</p>
|
<p class="text-muted">{{ trans('entities.books_empty') }}</p>
|
||||||
@if(userCan('books-create-all'))
|
@if(userCan('books-create-all'))
|
||||||
<a href="{{ baseUrl("/books/create") }}" class="text-pos">@icon('edit'){{ trans('entities.create_one_now') }}</a>
|
<a href="{{ baseUrl("/create-book") }}" class="text-pos">@icon('edit'){{ trans('entities.create_one_now') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<div class="container" ng-non-bindable>
|
<div class="container" ng-non-bindable>
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3><@icon('lock') {{ trans('entities.books_permissions') }}</h3>
|
<h3>@icon('lock') {{ trans('entities.books_permissions') }}</h3>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
@include('form/restriction-form', ['model' => $book])
|
@include('form/restriction-form', ['model' => $book])
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
@if(userCan('page-create', $book))
|
@if(userCan('page-create', $book))
|
||||||
<a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button">@icon('add'){{ trans('entities.pages_new') }}</a>
|
<a href="{{ $book->getUrl('/create-page') }}" class="text-pos text-button">@icon('add'){{ trans('entities.pages_new') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('chapter-create', $book))
|
@if(userCan('chapter-create', $book))
|
||||||
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button">@icon('add'){{ trans('entities.chapters_new') }}</a>
|
<a href="{{ $book->getUrl('/create-chapter') }}" class="text-pos text-button">@icon('add'){{ trans('entities.chapters_new') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book))
|
@if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book))
|
||||||
<div dropdown class="dropdown-container">
|
<div dropdown class="dropdown-container">
|
||||||
@ -111,13 +111,13 @@
|
|||||||
<div class="well">
|
<div class="well">
|
||||||
<p class="text-muted italic">{{ trans('entities.books_empty_contents') }}</p>
|
<p class="text-muted italic">{{ trans('entities.books_empty_contents') }}</p>
|
||||||
@if(userCan('page-create', $book))
|
@if(userCan('page-create', $book))
|
||||||
<a href="{{ $book->getUrl('/page/create') }}" class="button outline page">@icon('page'){{ trans('entities.books_empty_create_page') }}</a>
|
<a href="{{ $book->getUrl('/create-page') }}" class="button outline page">@icon('page'){{ trans('entities.books_empty_create_page') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('page-create', $book) && userCan('chapter-create', $book))
|
@if(userCan('page-create', $book) && userCan('chapter-create', $book))
|
||||||
<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>
|
<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('chapter-create', $book))
|
@if(userCan('chapter-create', $book))
|
||||||
<a href="{{ $book->getUrl('/chapter/create') }}" class="button outline chapter">@icon('chapter'){{ trans('entities.books_empty_add_chapter') }}</a>
|
<a href="{{ $book->getUrl('/create-chapter') }}" class="button outline chapter">@icon('chapter'){{ trans('entities.books_empty_add_chapter') }}</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
@section('toolbar')
|
@section('toolbar')
|
||||||
<div class="col-sm-12 faded">
|
<div class="col-sm-12 faded">
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<a href="{{$book->getUrl()}}" class="text-book text-button">@icon('book'){{ $book->getShortName() }}</a>
|
<a href="{{ $book->getUrl() }}" class="text-book text-button">@icon('book'){{ $book->getShortName() }}</a>
|
||||||
<span class="sep">»</span>
|
<span class="sep">»</span>
|
||||||
<a href="{{ baseUrl('/books/chapter/create') }}" class="text-button">@icon('add'){{ trans('entities.chapters_create') }}</a>
|
<a href="{{ $book->getUrl('/create-chapter')}}" class="text-button">@icon('add'){{ trans('entities.chapters_create') }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@stop
|
@stop
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>@icon('add') {{ trans('entities.chapters_create') }}</h3>
|
<h3>@icon('add') {{ trans('entities.chapters_create') }}</h3>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
|
<form action="{{ $book->getUrl('/create-chapter') }}" method="POST">
|
||||||
@include('chapters/form')
|
@include('chapters/form')
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group text-right">
|
<div class="form-group text-right">
|
||||||
<a href="{{ back()->getTargetUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
|
<a href="{{ isset($chapter) ? $chapter->getUrl() : $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
|
||||||
<button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
|
<button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
|
<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
|
||||||
<div overlay v-cloak>
|
<div overlay v-cloak @click="hide()">
|
||||||
<div class="popup-body" @click.stop="">
|
<div class="popup-body" @click.stop="">
|
||||||
|
|
||||||
<div class="popup-header primary-background">
|
<div class="popup-header primary-background">
|
||||||
<div class="popup-title">{{ trans('components.image_select') }}</div>
|
<div class="popup-title">{{ trans('components.image_select') }}</div>
|
||||||
<button class="overlay-close neg corner-button button">x</button>
|
<button class="overlay-close neg corner-button button" @click="hide()">x</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-fill image-manager-body">
|
<div class="flex-fill image-manager-body">
|
||||||
@ -79,7 +79,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<dropzone placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
|
<dropzone ref="dropzone" placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="container small" ng-non-bindable>
|
<div class="container small" ng-non-bindable>
|
||||||
<h1>{{ trans('entities.pages_new') }}</h1>
|
<h1>{{ trans('entities.pages_new') }}</h1>
|
||||||
<form action="{{ $parent->getUrl('/page/create/guest') }}" method="POST">
|
<form action="{{ $parent->getUrl('/create-guest-page') }}" method="POST">
|
||||||
|
|
||||||
{!! csrf_field() !!}
|
{!! csrf_field() !!}
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
<link rel="stylesheet" media="print" href="{{ versioned_asset('dist/print-styles.css') }}">
|
<link rel="stylesheet" media="print" href="{{ versioned_asset('dist/print-styles.css') }}">
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="{{ baseUrl("/libs/jquery/jquery.min.js?version=2.1.4") }}"></script>
|
|
||||||
@include('partials/custom-styles')
|
@include('partials/custom-styles')
|
||||||
|
|
||||||
<!-- Custom user content -->
|
<!-- Custom user content -->
|
||||||
|
@ -193,7 +193,7 @@
|
|||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
|
|
||||||
<div class="container small">
|
<div class="container small" v-pre>
|
||||||
<input type="hidden" name="searchTerm" value="{{$searchTerm}}">
|
<input type="hidden" name="searchTerm" value="{{$searchTerm}}">
|
||||||
|
|
||||||
<h1>{{ trans('entities.search_results') }}</h1>
|
<h1>{{ trans('entities.search_results') }}</h1>
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
@foreach($activeSocialDrivers as $driver => $enabled)
|
@foreach($activeSocialDrivers as $driver => $enabled)
|
||||||
<div class="col-sm-4 col-xs-6 text-center">
|
<div class="col-sm-4 col-xs-6 text-center">
|
||||||
<div>@icon('auth/'. $driver, ['style' => 'width: 56px;'])</div>
|
<div>@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])</div>
|
||||||
<div>
|
<div>
|
||||||
@if($user->hasSocialAccount($driver))
|
@if($user->hasSocialAccount($driver))
|
||||||
<a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
|
<a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
|
||||||
|
@ -14,11 +14,11 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
|
Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::get('/create-book', 'BookController@create');
|
||||||
Route::group(['prefix' => 'books'], function () {
|
Route::group(['prefix' => 'books'], function () {
|
||||||
|
|
||||||
// Books
|
// Books
|
||||||
Route::get('/', 'BookController@index');
|
Route::get('/', 'BookController@index');
|
||||||
Route::get('/create', 'BookController@create');
|
|
||||||
Route::post('/', 'BookController@store');
|
Route::post('/', 'BookController@store');
|
||||||
Route::get('/{slug}/edit', 'BookController@edit');
|
Route::get('/{slug}/edit', 'BookController@edit');
|
||||||
Route::put('/{slug}', 'BookController@update');
|
Route::put('/{slug}', 'BookController@update');
|
||||||
@ -35,8 +35,8 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::get('/{bookSlug}/export/plaintext', 'BookController@exportPlainText');
|
Route::get('/{bookSlug}/export/plaintext', 'BookController@exportPlainText');
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
Route::get('/{bookSlug}/page/create', 'PageController@create');
|
Route::get('/{bookSlug}/create-page', 'PageController@create');
|
||||||
Route::post('/{bookSlug}/page/create/guest', 'PageController@createAsGuest');
|
Route::post('/{bookSlug}/create-guest-page', 'PageController@createAsGuest');
|
||||||
Route::get('/{bookSlug}/draft/{pageId}', 'PageController@editDraft');
|
Route::get('/{bookSlug}/draft/{pageId}', 'PageController@editDraft');
|
||||||
Route::post('/{bookSlug}/draft/{pageId}', 'PageController@store');
|
Route::post('/{bookSlug}/draft/{pageId}', 'PageController@store');
|
||||||
Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
|
Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
|
||||||
@ -62,9 +62,9 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
|
|
||||||
// Chapters
|
// Chapters
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create');
|
||||||
Route::post('/{bookSlug}/chapter/{chapterSlug}/page/create/guest', 'PageController@createAsGuest');
|
Route::post('/{bookSlug}/chapter/{chapterSlug}/create-guest-page', 'PageController@createAsGuest');
|
||||||
Route::get('/{bookSlug}/chapter/create', 'ChapterController@create');
|
Route::get('/{bookSlug}/create-chapter', 'ChapterController@create');
|
||||||
Route::post('/{bookSlug}/chapter/create', 'ChapterController@store');
|
Route::post('/{bookSlug}/create-chapter', 'ChapterController@store');
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show');
|
||||||
Route::put('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@update');
|
Route::put('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@update');
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@showMove');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@showMove');
|
||||||
|
@ -139,7 +139,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
// Navigate to chapter create page
|
// Navigate to chapter create page
|
||||||
->visit($book->getUrl())
|
->visit($book->getUrl())
|
||||||
->click('New Chapter')
|
->click('New Chapter')
|
||||||
->seePageIs($book->getUrl() . '/chapter/create')
|
->seePageIs($book->getUrl() . '/create-chapter')
|
||||||
// Fill out form
|
// Fill out form
|
||||||
->type($chapter->name, '#name')
|
->type($chapter->name, '#name')
|
||||||
->type($chapter->description, '#description')
|
->type($chapter->description, '#description')
|
||||||
@ -161,7 +161,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
->visit('/books')
|
->visit('/books')
|
||||||
// Choose to create a book
|
// Choose to create a book
|
||||||
->click('Create New Book')
|
->click('Create New Book')
|
||||||
->seePageIs('/books/create')
|
->seePageIs('/create-book')
|
||||||
// Fill out form & save
|
// Fill out form & save
|
||||||
->type($book->name, '#name')
|
->type($book->name, '#name')
|
||||||
->type($book->description, '#description')
|
->type($book->description, '#description')
|
||||||
@ -172,7 +172,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
|
|
||||||
// Ensure duplicate names are given different slugs
|
// Ensure duplicate names are given different slugs
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
->visit('/books/create')
|
->visit('/create-book')
|
||||||
->type($book->name, '#name')
|
->type($book->name, '#name')
|
||||||
->type($book->description, '#description')
|
->type($book->description, '#description')
|
||||||
->press('Save Book');
|
->press('Save Book');
|
||||||
|
@ -70,7 +70,7 @@ class PageDraftTest extends BrowserKitTest
|
|||||||
$book = \BookStack\Book::first();
|
$book = \BookStack\Book::first();
|
||||||
$this->asAdmin()->visit('/')
|
$this->asAdmin()->visit('/')
|
||||||
->dontSeeInElement('#recent-drafts', 'New Page')
|
->dontSeeInElement('#recent-drafts', 'New Page')
|
||||||
->visit($book->getUrl() . '/page/create')
|
->visit($book->getUrl() . '/create-page')
|
||||||
->visit('/')
|
->visit('/')
|
||||||
->seeInElement('#recent-drafts', 'New Page');
|
->seeInElement('#recent-drafts', 'New Page');
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ class PageDraftTest extends BrowserKitTest
|
|||||||
$newUser = $this->getEditor();
|
$newUser = $this->getEditor();
|
||||||
|
|
||||||
$this->actingAs($newUser)->visit('/')
|
$this->actingAs($newUser)->visit('/')
|
||||||
->visit($book->getUrl() . '/page/create')
|
->visit($book->getUrl() . '/create-page')
|
||||||
->visit($chapter->getUrl() . '/create-page')
|
->visit($chapter->getUrl() . '/create-page')
|
||||||
->visit($book->getUrl())
|
->visit($book->getUrl())
|
||||||
->seeInElement('.page-list', 'New Page');
|
->seeInElement('.page-list', 'New Page');
|
||||||
|
@ -21,7 +21,7 @@ class ImageTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
protected function getTestImage($fileName)
|
protected function getTestImage($fileName)
|
||||||
{
|
{
|
||||||
return new \Illuminate\Http\UploadedFile($this->getTestImageFilePath(), $fileName, 'image/jpeg', 5238);
|
return new \Illuminate\Http\UploadedFile($this->getTestImageFilePath(), $fileName, 'image/png', 5238);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,7 +86,42 @@ class ImageTest extends TestCase
|
|||||||
'updated_by' => $admin->id,
|
'updated_by' => $admin->id,
|
||||||
'name' => $imageName
|
'name' => $imageName
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_secure_images_uploads_to_correct_place()
|
||||||
|
{
|
||||||
|
config()->set('filesystems.default', 'local_secure');
|
||||||
|
$this->asEditor();
|
||||||
|
$galleryFile = $this->getTestImage('my-secure-test-upload');
|
||||||
|
$page = Page::first();
|
||||||
|
$expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m-M') . '/my-secure-test-upload');
|
||||||
|
|
||||||
|
$upload = $this->call('POST', '/images/gallery/upload', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
|
||||||
|
$upload->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertTrue(file_exists($expectedPath), 'Uploaded image not found at path: '. $expectedPath);
|
||||||
|
|
||||||
|
if (file_exists($expectedPath)) {
|
||||||
|
unlink($expectedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_system_images_remain_public()
|
||||||
|
{
|
||||||
|
config()->set('filesystems.default', 'local_secure');
|
||||||
|
$this->asEditor();
|
||||||
|
$galleryFile = $this->getTestImage('my-system-test-upload');
|
||||||
|
$page = Page::first();
|
||||||
|
$expectedPath = public_path('uploads/images/system/' . Date('Y-m-M') . '/my-system-test-upload');
|
||||||
|
|
||||||
|
$upload = $this->call('POST', '/images/system/upload', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
|
||||||
|
$upload->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertTrue(file_exists($expectedPath), 'Uploaded image not found at path: '. $expectedPath);
|
||||||
|
|
||||||
|
if (file_exists($expectedPath)) {
|
||||||
|
unlink($expectedPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_image_delete()
|
public function test_image_delete()
|
||||||
|
@ -109,21 +109,21 @@ class RestrictionsTest extends BrowserKitTest
|
|||||||
|
|
||||||
$this->setEntityRestrictions($book, ['view', 'delete', 'update']);
|
$this->setEntityRestrictions($book, ['view', 'delete', 'update']);
|
||||||
|
|
||||||
$this->forceVisit($bookUrl . '/chapter/create')
|
$this->forceVisit($bookUrl . '/create-chapter')
|
||||||
->see('You do not have permission')->seePageIs('/');
|
->see('You do not have permission')->seePageIs('/');
|
||||||
$this->forceVisit($bookUrl . '/page/create')
|
$this->forceVisit($bookUrl . '/create-page')
|
||||||
->see('You do not have permission')->seePageIs('/');
|
->see('You do not have permission')->seePageIs('/');
|
||||||
$this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
|
$this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
|
||||||
->dontSeeInElement('.action-buttons', 'New Chapter');
|
->dontSeeInElement('.action-buttons', 'New Chapter');
|
||||||
|
|
||||||
$this->setEntityRestrictions($book, ['view', 'create']);
|
$this->setEntityRestrictions($book, ['view', 'create']);
|
||||||
|
|
||||||
$this->visit($bookUrl . '/chapter/create')
|
$this->visit($bookUrl . '/create-chapter')
|
||||||
->type('test chapter', 'name')
|
->type('test chapter', 'name')
|
||||||
->type('test description for chapter', 'description')
|
->type('test description for chapter', 'description')
|
||||||
->press('Save Chapter')
|
->press('Save Chapter')
|
||||||
->seePageIs($bookUrl . '/chapter/test-chapter');
|
->seePageIs($bookUrl . '/chapter/test-chapter');
|
||||||
$this->visit($bookUrl . '/page/create')
|
$this->visit($bookUrl . '/create-page')
|
||||||
->type('test page', 'name')
|
->type('test page', 'name')
|
||||||
->type('test content', 'html')
|
->type('test content', 'html')
|
||||||
->press('Save Page')
|
->press('Save Page')
|
||||||
@ -454,21 +454,21 @@ class RestrictionsTest extends BrowserKitTest
|
|||||||
|
|
||||||
$this->setEntityRestrictions($book, ['view', 'delete', 'update']);
|
$this->setEntityRestrictions($book, ['view', 'delete', 'update']);
|
||||||
|
|
||||||
$this->forceVisit($bookUrl . '/chapter/create')
|
$this->forceVisit($bookUrl . '/create-chapter')
|
||||||
->see('You do not have permission')->seePageIs('/');
|
->see('You do not have permission')->seePageIs('/');
|
||||||
$this->forceVisit($bookUrl . '/page/create')
|
$this->forceVisit($bookUrl . '/create-page')
|
||||||
->see('You do not have permission')->seePageIs('/');
|
->see('You do not have permission')->seePageIs('/');
|
||||||
$this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
|
$this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
|
||||||
->dontSeeInElement('.action-buttons', 'New Chapter');
|
->dontSeeInElement('.action-buttons', 'New Chapter');
|
||||||
|
|
||||||
$this->setEntityRestrictions($book, ['view', 'create']);
|
$this->setEntityRestrictions($book, ['view', 'create']);
|
||||||
|
|
||||||
$this->visit($bookUrl . '/chapter/create')
|
$this->visit($bookUrl . '/create-chapter')
|
||||||
->type('test chapter', 'name')
|
->type('test chapter', 'name')
|
||||||
->type('test description for chapter', 'description')
|
->type('test description for chapter', 'description')
|
||||||
->press('Save Chapter')
|
->press('Save Chapter')
|
||||||
->seePageIs($bookUrl . '/chapter/test-chapter');
|
->seePageIs($bookUrl . '/chapter/test-chapter');
|
||||||
$this->visit($bookUrl . '/page/create')
|
$this->visit($bookUrl . '/create-page')
|
||||||
->type('test page', 'name')
|
->type('test page', 'name')
|
||||||
->type('test content', 'html')
|
->type('test content', 'html')
|
||||||
->press('Save Page')
|
->press('Save Page')
|
||||||
|
@ -214,12 +214,12 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_books_create_all_permissions()
|
public function test_books_create_all_permissions()
|
||||||
{
|
{
|
||||||
$this->checkAccessPermission('book-create-all', [
|
$this->checkAccessPermission('book-create-all', [
|
||||||
'/books/create'
|
'/create-book'
|
||||||
], [
|
], [
|
||||||
'/books' => 'Create New Book'
|
'/books' => 'Create New Book'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->visit('/books/create')
|
$this->visit('/create-book')
|
||||||
->type('test book', 'name')
|
->type('test book', 'name')
|
||||||
->type('book desc', 'description')
|
->type('book desc', 'description')
|
||||||
->press('Save Book')
|
->press('Save Book')
|
||||||
@ -293,40 +293,38 @@ class RolesTest extends BrowserKitTest
|
|||||||
{
|
{
|
||||||
$book = \BookStack\Book::take(1)->get()->first();
|
$book = \BookStack\Book::take(1)->get()->first();
|
||||||
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
||||||
$baseUrl = $ownBook->getUrl() . '/chapter';
|
|
||||||
$this->checkAccessPermission('chapter-create-own', [
|
$this->checkAccessPermission('chapter-create-own', [
|
||||||
$baseUrl . '/create'
|
$ownBook->getUrl('/create-chapter')
|
||||||
], [
|
], [
|
||||||
$ownBook->getUrl() => 'New Chapter'
|
$ownBook->getUrl() => 'New Chapter'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->visit($baseUrl . '/create')
|
$this->visit($ownBook->getUrl('/create-chapter'))
|
||||||
->type('test chapter', 'name')
|
->type('test chapter', 'name')
|
||||||
->type('chapter desc', 'description')
|
->type('chapter desc', 'description')
|
||||||
->press('Save Chapter')
|
->press('Save Chapter')
|
||||||
->seePageIs($baseUrl . '/test-chapter');
|
->seePageIs($ownBook->getUrl('/chapter/test-chapter'));
|
||||||
|
|
||||||
$this->visit($book->getUrl())
|
$this->visit($book->getUrl())
|
||||||
->dontSeeInElement('.action-buttons', 'New Chapter')
|
->dontSeeInElement('.action-buttons', 'New Chapter')
|
||||||
->visit($book->getUrl() . '/chapter/create')
|
->visit($book->getUrl('/create-chapter'))
|
||||||
->seePageIs('/');
|
->seePageIs('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_chapter_create_all_permissions()
|
public function test_chapter_create_all_permissions()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Book::take(1)->get()->first();
|
$book = \BookStack\Book::take(1)->get()->first();
|
||||||
$baseUrl = $book->getUrl() . '/chapter';
|
|
||||||
$this->checkAccessPermission('chapter-create-all', [
|
$this->checkAccessPermission('chapter-create-all', [
|
||||||
$baseUrl . '/create'
|
$book->getUrl('/create-chapter')
|
||||||
], [
|
], [
|
||||||
$book->getUrl() => 'New Chapter'
|
$book->getUrl() => 'New Chapter'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->visit($baseUrl . '/create')
|
$this->visit($book->getUrl('/create-chapter'))
|
||||||
->type('test chapter', 'name')
|
->type('test chapter', 'name')
|
||||||
->type('chapter desc', 'description')
|
->type('chapter desc', 'description')
|
||||||
->press('Save Chapter')
|
->press('Save Chapter')
|
||||||
->seePageIs($baseUrl . '/test-chapter');
|
->seePageIs($book->getUrl('/chapter/test-chapter'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_chapter_edit_own_permission()
|
public function test_chapter_edit_own_permission()
|
||||||
@ -403,10 +401,8 @@ class RolesTest extends BrowserKitTest
|
|||||||
$ownBook = $entities['book'];
|
$ownBook = $entities['book'];
|
||||||
$ownChapter = $entities['chapter'];
|
$ownChapter = $entities['chapter'];
|
||||||
|
|
||||||
$baseUrl = $ownBook->getUrl() . '/page';
|
$createUrl = $ownBook->getUrl('/create-page');
|
||||||
|
$createUrlChapter = $ownChapter->getUrl('/create-page');
|
||||||
$createUrl = $baseUrl . '/create';
|
|
||||||
$createUrlChapter = $ownChapter->getUrl() . '/create-page';
|
|
||||||
$accessUrls = [$createUrl, $createUrlChapter];
|
$accessUrls = [$createUrl, $createUrlChapter];
|
||||||
|
|
||||||
foreach ($accessUrls as $url) {
|
foreach ($accessUrls as $url) {
|
||||||
@ -427,15 +423,15 @@ class RolesTest extends BrowserKitTest
|
|||||||
$this->seePageIs($expectedUrl);
|
$this->seePageIs($expectedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->visit($baseUrl . '/create')
|
$this->visit($createUrl)
|
||||||
->type('test page', 'name')
|
->type('test page', 'name')
|
||||||
->type('page desc', 'html')
|
->type('page desc', 'html')
|
||||||
->press('Save Page')
|
->press('Save Page')
|
||||||
->seePageIs($baseUrl . '/test-page');
|
->seePageIs($ownBook->getUrl('/page/test-page'));
|
||||||
|
|
||||||
$this->visit($book->getUrl())
|
$this->visit($book->getUrl())
|
||||||
->dontSeeInElement('.action-buttons', 'New Page')
|
->dontSeeInElement('.action-buttons', 'New Page')
|
||||||
->visit($book->getUrl() . '/page/create')
|
->visit($book->getUrl() . '/create-page')
|
||||||
->seePageIs('/');
|
->seePageIs('/');
|
||||||
$this->visit($chapter->getUrl())
|
$this->visit($chapter->getUrl())
|
||||||
->dontSeeInElement('.action-buttons', 'New Page')
|
->dontSeeInElement('.action-buttons', 'New Page')
|
||||||
@ -448,9 +444,9 @@ class RolesTest extends BrowserKitTest
|
|||||||
$book = \BookStack\Book::take(1)->get()->first();
|
$book = \BookStack\Book::take(1)->get()->first();
|
||||||
$chapter = \BookStack\Chapter::take(1)->get()->first();
|
$chapter = \BookStack\Chapter::take(1)->get()->first();
|
||||||
$baseUrl = $book->getUrl() . '/page';
|
$baseUrl = $book->getUrl() . '/page';
|
||||||
$createUrl = $baseUrl . '/create';
|
$createUrl = $book->getUrl('/create-page');
|
||||||
|
|
||||||
$createUrlChapter = $chapter->getUrl() . '/create-page';
|
$createUrlChapter = $chapter->getUrl('/create-page');
|
||||||
$accessUrls = [$createUrl, $createUrlChapter];
|
$accessUrls = [$createUrl, $createUrlChapter];
|
||||||
|
|
||||||
foreach ($accessUrls as $url) {
|
foreach ($accessUrls as $url) {
|
||||||
@ -471,17 +467,17 @@ class RolesTest extends BrowserKitTest
|
|||||||
$this->seePageIs($expectedUrl);
|
$this->seePageIs($expectedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->visit($baseUrl . '/create')
|
$this->visit($createUrl)
|
||||||
->type('test page', 'name')
|
->type('test page', 'name')
|
||||||
->type('page desc', 'html')
|
->type('page desc', 'html')
|
||||||
->press('Save Page')
|
->press('Save Page')
|
||||||
->seePageIs($baseUrl . '/test-page');
|
->seePageIs($book->getUrl('/page/test-page'));
|
||||||
|
|
||||||
$this->visit($chapter->getUrl() . '/create-page')
|
$this->visit($chapter->getUrl('/create-page'))
|
||||||
->type('new test page', 'name')
|
->type('new test page', 'name')
|
||||||
->type('page desc', 'html')
|
->type('page desc', 'html')
|
||||||
->press('Save Page')
|
->press('Save Page')
|
||||||
->seePageIs($baseUrl . '/new-test-page');
|
->seePageIs($book->getUrl('/page/new-test-page'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_page_edit_own_permission()
|
public function test_page_edit_own_permission()
|
||||||
|
Loading…
Reference in New Issue
Block a user