PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.0
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / Modules / Course / Model / CourseLesson.php

CourseLesson.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.10.0, at Modules/Course/Model/CourseLesson.php

376 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Modules\Course\Model;
4
5
6 use FluentCommunity\App\Functions\Utility;
7 use FluentCommunity\App\Models\Model;
8 use FluentCommunity\App\Models\Reaction;
9 use FluentCommunity\App\Models\Term;
10 use FluentCommunity\App\Models\User;
11 use FluentCommunity\App\Models\Media;
12 use FluentCommunity\App\Models\Comment;
13 use FluentCommunity\App\Services\Helper;
14 use FluentCommunity\Framework\Support\Arr;
15
16 /**
17 * Course Lesson Model - DB Model for Individual Course Lesson
18 *
19 * Database Model
20 *
21 * @package FluentCrm\App\Models
22 *
23 * @version 1.1.0
24 *
25 * @property int $id
26 * @property int|null $user_id
27 * @property string|null $title
28 * @property string|null $slug
29 * @property string|null $message
30 * @property string|null $message_rendered
31 * @property string|null $type
32 * @property int|null $space_id
33 * @property string|null $privacy
34 * @property string|null $status
35 * @property string|null $featured_image
36 * @property int|null $is_sticky
37 * @property string|null $scheduled_at
38 * @property string|null $expired_at
39 * @property string|null $content_type
40 * @property int $comments_count
41 * @property int $reactions_count
42 * @property array $meta
43 * @property int|null $priority
44 * @property int|null $parent_id
45 * @property string|null $created_at
46 * @property string|null $updated_at
47 * @property-read array $questions
48 * @property-read array $enabled_questions
49 * @property-read bool $is_enforce_pass
50 * @property-read bool $is_free_preview
51 * @property-read int $passing_score
52 * @property-read Course|null $course
53 */
54 class CourseLesson extends Model
55 {
56 protected $table = 'fcom_posts';
57
58 protected $guarded = [ 'id' ];
59
60 protected $casts = [
61 'comments_count' => 'int',
62 'reactions_count' => 'int',
63 ];
64
65 protected $fillable = [
66 'user_id',
67 'title',
68 'slug',
69 'message',
70 'message_rendered',
71 'type',
72 'space_id',
73 'privacy',
74 'status',
75 'featured_image',
76 'is_sticky',
77 'scheduled_at',
78 'expired_at',
79 'content_type',
80 'comments_count',
81 'reactions_count',
82 'meta',
83 'priority',
84 'parent_id',
85 ];
86
87 protected $searchable = [
88 'message',
89 'title',
90 ];
91
92 public static $publicColumns = [
93 'id', 'slug', 'title', 'message_rendered', 'featured_image', 'created_at', 'privacy', 'type', 'status', 'slug', 'space_id', 'user_id', 'meta', 'content_type', 'comments_count', 'reactions_count',
94 ];
95
96 protected static $type = 'course_lesson';
97
98 public static function boot()
99 {
100 parent::boot();
101
102 static::creating(function ($model) {
103 $model->user_id = get_current_user_id();
104 if (empty($model->slug)) {
105 $model->slug = self::generateNewSlug($model);
106 }
107
108 $model->type = self::$type;
109
110 if (empty($model->content_type)) {
111 $model->content_type = 'text';
112 }
113
114 if (empty($model->message)) {
115 $model->message = '';
116 }
117
118 if (empty($model->meta)) {
119 $model->meta = self::getDefaultMeta();
120 }
121 });
122
123 static::addGlobalScope('type', function ($builder) {
124 $builder->where('type', self::$type);
125 });
126 }
127
128 protected static function getDefaultMeta()
129 {
130 return [
131 'media' => [
132 'type' => 'oembed',
133 'url' => '',
134 'content_type' => 'video',
135 'html' => '',
136 ],
137 'enable_comments' => 'yes',
138 'enable_media' => 'yes',
139 'document_lists' => [],
140 ];
141 }
142
143 /**
144 * A lesson slug only has to be unique among the lessons of one course.
145 *
146 * A lesson is read at course/{courseSlug}/lessons/{lessonSlug} and
147 * CourseController::getLessonBySlug() looks it up with a space_id filter, so the
148 * same slug in two courses never competes. The type global scope keeps this off
149 * the other row types sharing fcom_posts.
150 *
151 * Every write path goes through here - the creating hook below for generated
152 * slugs, CourseAdminController::patchLesson() for author supplied ones. A
153 * collision gets a -{time()} suffix.
154 *
155 * @param string $slug
156 * @param int|null $courseId the owning course, fcom_posts.space_id
157 * @param int|null $ignoreId the lesson being renamed, so it can keep its own slug
158 * @param string $fallbackTitle used when $slug sanitizes down to nothing
159 * @return string
160 */
161 public static function uniqueSlug($slug, $courseId, $ignoreId = null, $fallbackTitle = '')
162 {
163 $slug = sanitize_title($slug);
164
165 if (!$slug) {
166 $slug = Utility::slugify($fallbackTitle, 'lesson-' . time());
167 }
168
169 $query = self::where('slug', $slug);
170
171 if ($courseId) {
172 $query->where('space_id', $courseId);
173 } else {
174 $query->whereNull('space_id');
175 }
176
177 if ($ignoreId) {
178 $query->where('id', '!=', $ignoreId);
179 }
180
181 if ($query->exists()) {
182 $slug = $slug . '-' . time();
183 }
184
185 return $slug;
186 }
187
188 protected static function generateNewSlug($newModel)
189 {
190 return self::uniqueSlug('', $newModel->space_id, null, $newModel->title);
191 }
192
193 public function topic()
194 {
195 return $this->belongsTo(CourseTopic::class, 'parent_id');
196 }
197
198 public function course()
199 {
200 return $this->belongsTo(Course::class, 'space_id', 'id');
201 }
202
203 public function setMetaAttribute($value)
204 {
205 $this->attributes['meta'] = maybe_serialize($value);
206 }
207
208 public function getMetaAttribute($value)
209 {
210 $meta = Utility::safeUnserialize($value);
211
212 if (!$meta) {
213 $meta = self::getDefaultMeta();
214 }
215
216 return $meta;
217 }
218
219 public function getQuestionsAttribute()
220 {
221 return Arr::get($this->meta, 'quiz_questions', []);
222 }
223
224 public function getEnabledQuestionsAttribute()
225 {
226 return array_filter($this->questions, function ($question) {
227 return Arr::isTrue($question, 'enabled');
228 });
229 }
230
231 public function getIsEnforcePassAttribute()
232 {
233 return Arr::isTrue($this->meta, 'enforce_passing_score');
234 }
235
236 public function getIsFreePreviewAttribute()
237 {
238 return Arr::isTrue($this->meta, 'free_preview_lesson');
239 }
240
241 public function getPassingScoreAttribute()
242 {
243 if (Arr::isTrue($this->meta, 'enable_passing_score')) {
244 return Arr::get($this->meta, 'passing_score', 0);
245 }
246 return 0;
247 }
248
249 public function scopeSearchBy($query, $search)
250 {
251 if (!$search) {
252 return $query;
253 }
254
255 $fields = $this->searchable;
256 $query->where(function ($query) use ($fields, $search) {
257 $query->where(array_shift($fields), 'LIKE', "%$search%");
258 foreach ($fields as $field) {
259 $query->orWhere($field, 'LIKE', "$search%");
260 }
261 });
262
263 return $query;
264 }
265
266 public function owner()
267 {
268 return $this->belongsTo(User::class, 'user_id', 'ID');
269 }
270
271 public function comments()
272 {
273 return $this->hasMany(Comment::class, 'post_id', 'id');
274 }
275
276 public function reactions()
277 {
278 return $this->hasMany(Reaction::class, 'parent_id', 'id')
279 ->where('object_type', 'comment');
280 }
281
282 public function lessonCompleted()
283 {
284 return $this->hasMany(Reaction::class, 'object_id', 'id')
285 ->where('object_type', 'lesson_completed');
286 }
287
288 public function media()
289 {
290 return $this->hasMany(Media::class, 'feed_id', 'id');
291 }
292
293 public function terms()
294 {
295 return $this->belongsToMany(Term::class, 'fcom_term_feed', 'post_id', 'term_id');
296 }
297
298 public function isQuizType()
299 {
300 return $this->content_type == 'quiz';
301 }
302
303 public function getPermalink()
304 {
305 $uri = '/course/' . ($this->course ? $this->course->slug : 'undefined') . '/lessons/' . $this->slug . '/view';
306 return Helper::baseUrl($uri);
307 }
308
309 public function hasUserReact($userId, $type = 'like')
310 {
311 if (!$userId) {
312 return false;
313 }
314
315 return (bool)Reaction::where('object_id', $this->id)
316 ->select([ 'id' ])
317 ->where('object_type', 'feed')
318 ->where('user_id', $userId)
319 ->where('type', $type)
320 ->first();
321 }
322
323 public function getHumanExcerpt($length = 40)
324 {
325 $content = $this->title;
326
327 if (!$content) {
328 $content = $this->message;
329 if (!$content) {
330 return '';
331 }
332 // remove all tags
333 $content = wp_strip_all_tags($content);
334 // remove new lines and tabs
335 $content = str_replace([ "\r", "\n", "\t" ], ' ', $content);
336 // remove multiple spaces
337 $content = preg_replace('/\s+/', ' ', $content);
338
339 // trim
340 $content = trim($content);
341 }
342
343 if (!$content) {
344 return '';
345 }
346
347 // return the first $length chars of the content with ... at the end
348 return mb_substr($content, 0, $length) . '...';
349 }
350
351 public function getPublicLessonMeta($canView = true)
352 {
353 $meta = $this->meta;
354
355 if(!empty($meta['document_lists'])) {
356 $docLists = $meta['document_lists'];
357 foreach ($docLists as $index => $docList) {
358 if(!empty($docList['media_key']) && !empty($docList['id'])) {
359 $docLists[$index]['url'] = Helper::baseUrl('?fcom_action=download_document&media_key='.$docList['media_key'].'&media_id='.$docList['id']);
360 }
361 }
362 $meta['document_lists'] = $docLists;
363 }
364
365 if(!$canView) {
366 $meta['document_lists'] = [];
367 $meta['document_ids'] = [];
368 unset($meta['media']);
369 }
370
371 $meta = apply_filters('fluent_community/lesson/get_public_meta', $meta, $this);
372
373 return $meta;
374 }
375 }
376