]> BookStack Code Mirror - bookstack/blob - app/Entities/Entity.php
Fix Book form (create) returning to the full books list on cancel
[bookstack] / app / Entities / Entity.php
1 <?php namespace BookStack\Entities;
2
3 use BookStack\Actions\Activity;
4 use BookStack\Actions\Comment;
5 use BookStack\Actions\Tag;
6 use BookStack\Actions\View;
7 use BookStack\Auth\Permissions\EntityPermission;
8 use BookStack\Auth\Permissions\JointPermission;
9 use BookStack\Facades\Permissions;
10 use BookStack\Ownable;
11 use Carbon\Carbon;
12 use Illuminate\Database\Eloquent\Relations\MorphMany;
13
14 /**
15  * Class Entity
16  * The base class for book-like items such as pages, chapters & books.
17  * This is not a database model in itself but extended.
18  *
19  * @property int $id
20  * @property string $name
21  * @property string $slug
22  * @property Carbon $created_at
23  * @property Carbon $updated_at
24  * @property int $created_by
25  * @property int $updated_by
26  * @property boolean $restricted
27  *
28  * @package BookStack\Entities
29  */
30 class Entity extends Ownable
31 {
32
33     /**
34      * @var string - Name of property where the main text content is found
35      */
36     public $textField = 'description';
37
38     /**
39      * @var float - Multiplier for search indexing.
40      */
41     public $searchFactor = 1.0;
42
43     /**
44      * Get the morph class for this model.
45      * Set here since, due to folder changes, the namespace used
46      * in the database no longer matches the class namespace.
47      * @return string
48      */
49     public function getMorphClass()
50     {
51         return 'BookStack\\Entity';
52     }
53
54     /**
55      * Compares this entity to another given entity.
56      * Matches by comparing class and id.
57      * @param $entity
58      * @return bool
59      */
60     public function matches($entity)
61     {
62         return [get_class($this), $this->id] === [get_class($entity), $entity->id];
63     }
64
65     /**
66      * Checks if an entity matches or contains another given entity.
67      * @param Entity $entity
68      * @return bool
69      */
70     public function matchesOrContains(Entity $entity)
71     {
72         $matches = [get_class($this), $this->id] === [get_class($entity), $entity->id];
73
74         if ($matches) {
75             return true;
76         }
77
78         if (($entity->isA('chapter') || $entity->isA('page')) && $this->isA('book')) {
79             return $entity->book_id === $this->id;
80         }
81
82         if ($entity->isA('page') && $this->isA('chapter')) {
83             return $entity->chapter_id === $this->id;
84         }
85
86         return false;
87     }
88
89     /**
90      * Gets the activity objects for this entity.
91      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
92      */
93     public function activity()
94     {
95         return $this->morphMany(Activity::class, 'entity')
96             ->orderBy('created_at', 'desc');
97     }
98
99     /**
100      * Get View objects for this entity.
101      */
102     public function views()
103     {
104         return $this->morphMany(View::class, 'viewable');
105     }
106
107     /**
108      * Get the Tag models that have been user assigned to this entity.
109      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
110      */
111     public function tags()
112     {
113         return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc');
114     }
115
116     /**
117      * Get the comments for an entity
118      * @param bool $orderByCreated
119      * @return MorphMany
120      */
121     public function comments($orderByCreated = true)
122     {
123         $query = $this->morphMany(Comment::class, 'entity');
124         return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
125     }
126
127     /**
128      * Get the related search terms.
129      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
130      */
131     public function searchTerms()
132     {
133         return $this->morphMany(SearchTerm::class, 'entity');
134     }
135
136     /**
137      * Get this entities restrictions.
138      */
139     public function permissions()
140     {
141         return $this->morphMany(EntityPermission::class, 'restrictable');
142     }
143
144     /**
145      * Check if this entity has a specific restriction set against it.
146      * @param $role_id
147      * @param $action
148      * @return bool
149      */
150     public function hasRestriction($role_id, $action)
151     {
152         return $this->permissions()->where('role_id', '=', $role_id)
153             ->where('action', '=', $action)->count() > 0;
154     }
155
156     /**
157      * Get the entity jointPermissions this is connected to.
158      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
159      */
160     public function jointPermissions()
161     {
162         return $this->morphMany(JointPermission::class, 'entity');
163     }
164
165     /**
166      * Allows checking of the exact class, Used to check entity type.
167      * Cleaner method for is_a.
168      * @param $type
169      * @return bool
170      */
171     public static function isA($type)
172     {
173         return static::getType() === strtolower($type);
174     }
175
176     /**
177      * Get entity type.
178      * @return mixed
179      */
180     public static function getType()
181     {
182         return strtolower(static::getClassName());
183     }
184
185     /**
186      * Get the type of this entity.
187      */
188     public function type(): string
189     {
190         return static::getType();
191     }
192
193     /**
194      * Get an instance of an entity of the given type.
195      * @param $type
196      * @return Entity
197      */
198     public static function getEntityInstance($type)
199     {
200         $types = ['Page', 'Book', 'Chapter', 'Bookshelf'];
201         $className = str_replace([' ', '-', '_'], '', ucwords($type));
202         if (!in_array($className, $types)) {
203             return null;
204         }
205
206         return app('BookStack\\Entities\\' . $className);
207     }
208
209     /**
210      * Gets a limited-length version of the entities name.
211      * @param int $length
212      * @return string
213      */
214     public function getShortName($length = 25)
215     {
216         if (mb_strlen($this->name) <= $length) {
217             return $this->name;
218         }
219         return mb_substr($this->name, 0, $length - 3) . '...';
220     }
221
222     /**
223      * Get the body text of this entity.
224      * @return mixed
225      */
226     public function getText()
227     {
228         return $this->{$this->textField};
229     }
230
231     /**
232      * Get an excerpt of this entity's descriptive content to the specified length.
233      * @param int $length
234      * @return mixed
235      */
236     public function getExcerpt(int $length = 100)
237     {
238         $text = $this->getText();
239         if (mb_strlen($text) > $length) {
240             $text = mb_substr($text, 0, $length-3) . '...';
241         }
242         return trim($text);
243     }
244
245     /**
246      * Return a generalised, common raw query that can be 'unioned' across entities.
247      * @return string
248      */
249     public function entityRawQuery()
250     {
251         return '';
252     }
253
254     /**
255      * Get the url of this entity
256      * @param $path
257      * @return string
258      */
259     public function getUrl($path = '/')
260     {
261         return $path;
262     }
263
264     /**
265      * Rebuild the permissions for this entity.
266      */
267     public function rebuildPermissions()
268     {
269         /** @noinspection PhpUnhandledExceptionInspection */
270         Permissions::buildJointPermissionsForEntity($this);
271     }
272
273     /**
274      * Generate and set a new URL slug for this model.
275      */
276     public function refreshSlug(): string
277     {
278         $generator = new SlugGenerator($this);
279         $this->slug = $generator->generate();
280         return $this->slug;
281     }
282 }