PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.01
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
← All changes | Modules/Course/Model/CourseLesson.php +178 -21 1.0.932.10.01 View file →
@@ -2,16 +2,20 @@
2 2
3 3 namespace FluentCommunity\Modules\Course\Model;
4 4
5 5
6 +use FluentCommunity\App\Functions\Utility;
6 7 use FluentCommunity\App\Models\Model;
7 8 use FluentCommunity\App\Models\Reaction;
8 9 use FluentCommunity\App\Models\Term;
9 10 use FluentCommunity\App\Models\User;
11 +use FluentCommunity\App\Models\Media;
12 +use FluentCommunity\App\Models\Comment;
10 13 use FluentCommunity\App\Services\Helper;
14 +use FluentCommunity\Framework\Support\Arr;
11 15
12 16 /**
13 - * Clourse Lesson Model - DB Model for Individual Clourse Lesson
17 + * Course Lesson Model - DB Model for Individual Course Lesson
14 18 *
15 19 * Database Model
16 20 *
17 21 * @package FluentCrm\App\Models
@@ -16,18 +20,47 @@
16 20 *
17 21 * @package FluentCrm\App\Models
18 22 *
19 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
20 53 */
21 54 class CourseLesson extends Model
22 55 {
23 56 protected $table = 'fcom_posts';
24 57
25 - protected $guarded = ['id'];
58 + protected $guarded = [ 'id' ];
26 59
27 60 protected $casts = [
28 61 'comments_count' => 'int',
29 - 'reactions_count' => 'int'
62 + 'reactions_count' => 'int',
30 63 ];
31 64
32 65 protected $fillable = [
33 66 'user_id',
@@ -40,22 +73,25 @@
40 73 'privacy',
41 74 'status',
42 75 'featured_image',
43 76 'is_sticky',
77 + 'scheduled_at',
44 78 'expired_at',
79 + 'content_type',
45 80 'comments_count',
46 81 'reactions_count',
47 82 'meta',
48 - 'parent_id'
83 + 'priority',
84 + 'parent_id',
49 85 ];
50 86
51 87 protected $searchable = [
52 88 'message',
53 - 'title'
89 + 'title',
54 90 ];
55 91
56 92 public static $publicColumns = [
57 - 'id', 'slug', 'title', 'message_rendered', 'featured_image', 'created_at', 'privacy', 'type', 'status', 'slug', 'space_id', 'user_id', 'meta', 'comments_count', 'reactions_count'
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',
58 94 ];
59 95
60 96 protected static $type = 'course_lesson';
61 97
@@ -70,8 +106,16 @@
70 106 }
71 107
72 108 $model->type = self::$type;
73 109
110 + if (empty($model->content_type)) {
111 + $model->content_type = 'text';
112 + }
113 +
114 + if (empty($model->message)) {
115 + $model->message = '';
116 + }
117 +
74 118 if (empty($model->meta)) {
75 119 $model->meta = self::getDefaultMeta();
76 120 }
77 121 });
@@ -87,24 +131,55 @@
87 131 'media' => [
88 132 'type' => 'oembed',
89 133 'url' => '',
90 134 'content_type' => 'video',
91 - 'html' => ''
135 + 'html' => '',
92 136 ],
93 137 'enable_comments' => 'yes',
94 - 'enable_media' => 'yes'
138 + 'enable_media' => 'yes',
139 + 'document_lists' => [],
95 140 ];
96 141 }
97 142
98 - protected static function generateNewSlug($newModel)
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 = '')
99 162 {
100 - $slug = sanitize_title($newModel->title);
163 + $slug = sanitize_title($slug);
101 164
102 - // check if the slug is available for this type
103 - $exist = self::where('slug', $slug)
104 - ->exists();
165 + if (!$slug) {
166 + $slug = Utility::slugify($fallbackTitle, 'lesson-' . time());
167 + }
105 168
106 - if ($exist) {
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()) {
107 182 $slug = $slug . '-' . time();
108 183 }
109 184
110 185 return $slug;
@@ -109,8 +184,13 @@
109 184
110 185 return $slug;
111 186 }
112 187
188 + protected static function generateNewSlug($newModel)
189 + {
190 + return self::uniqueSlug('', $newModel->space_id, null, $newModel->title);
191 + }
192 +
113 193 public function topic()
114 194 {
115 195 return $this->belongsTo(CourseTopic::class, 'parent_id');
116 196 }
@@ -116,9 +196,9 @@
116 196 }
117 197
118 198 public function course()
119 199 {
120 - return $this->belongsTo(Course::class, 'space_id');
200 + return $this->belongsTo(Course::class, 'space_id', 'id');
121 201 }
122 202
123 203 public function setMetaAttribute($value)
124 204 {
@@ -126,9 +206,9 @@
126 206 }
127 207
128 208 public function getMetaAttribute($value)
129 209 {
130 - $meta = maybe_unserialize($value);
210 + $meta = Utility::safeUnserialize($value);
131 211
132 212 if (!$meta) {
133 213 $meta = self::getDefaultMeta();
134 214 }
@@ -135,8 +215,38 @@
135 215
136 216 return $meta;
137 217 }
138 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 +
139 249 public function scopeSearchBy($query, $search)
140 250 {
141 251 if (!$search) {
142 252 return $query;
@@ -157,22 +267,44 @@
157 267 {
158 268 return $this->belongsTo(User::class, 'user_id', 'ID');
159 269 }
160 270
271 + public function comments()
272 + {
273 + return $this->hasMany(Comment::class, 'post_id', 'id');
274 + }
275 +
161 276 public function reactions()
162 277 {
278 + return $this->hasMany(Reaction::class, 'parent_id', 'id')
279 + ->where('object_type', 'comment');
280 + }
281 +
282 + public function lessonCompleted()
283 + {
163 284 return $this->hasMany(Reaction::class, 'object_id', 'id')
164 - ->where('object_type', 'feed');
285 + ->where('object_type', 'lesson_completed');
165 286 }
166 287
288 + public function media()
289 + {
290 + return $this->hasMany(Media::class, 'feed_id', 'id');
291 + }
292 +
167 293 public function terms()
168 294 {
169 295 return $this->belongsToMany(Term::class, 'fcom_term_feed', 'post_id', 'term_id');
170 296 }
171 297
298 + public function isQuizType()
299 + {
300 + return $this->content_type == 'quiz';
301 + }
302 +
172 303 public function getPermalink()
173 304 {
174 - return Helper::baseUrl() . '/course/' . $this->course ? $this->course->slug : 'undefined' . '/lessons/' . $this->slug . '/view';
305 + $uri = '/course/' . ($this->course ? $this->course->slug : 'undefined') . '/lessons/' . $this->slug . '/view';
306 + return Helper::baseUrl($uri);
175 307 }
176 308
177 309 public function hasUserReact($userId, $type = 'like')
178 310 {
@@ -180,9 +312,9 @@
180 312 return false;
181 313 }
182 314
183 315 return (bool)Reaction::where('object_id', $this->id)
184 - ->select(['id'])
316 + ->select([ 'id' ])
185 317 ->where('object_type', 'feed')
186 318 ->where('user_id', $userId)
187 319 ->where('type', $type)
188 320 ->first();
@@ -197,11 +329,11 @@
197 329 if (!$content) {
198 330 return '';
199 331 }
200 332 // remove all tags
201 - $content = strip_tags($content);
333 + $content = wp_strip_all_tags($content);
202 334 // remove new lines and tabs
203 - $content = str_replace(["\r", "\n", "\t"], ' ', $content);
335 + $content = str_replace([ "\r", "\n", "\t" ], ' ', $content);
204 336 // remove multiple spaces
205 337 $content = preg_replace('/\s+/', ' ', $content);
206 338
207 339 // trim
@@ -213,6 +345,31 @@
213 345 }
214 346
215 347 // return the first $length chars of the content with ... at the end
216 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;
217 374 }
218 375 }