Passed
Push — master ( 621302...15a517 )
by Angel Fernando Quiroz
16:26 queued 07:49
created

EmbedRegistryPlugin::get_name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Course;
5
use Chamilo\CoreBundle\Entity\Session;
6
use Chamilo\CoreBundle\Entity\TrackEAccess;
7
use Chamilo\CourseBundle\Entity\CTool;
8
use Chamilo\PluginBundle\EmbedRegistry\Entity\Embed;
9
use Doctrine\ORM\Tools\SchemaTool;
10
11
/**
12
 * Class EmbedRegistryPlugin.
13
 */
14
class EmbedRegistryPlugin extends Plugin
15
{
16
    public const SETTING_ENABLED = 'tool_enabled';
17
    public const SETTING_TITLE = 'tool_title';
18
    public const SETTING_EXTERNAL_URL = 'external_url';
19
    public const TBL_EMBED = 'plugin_embed_registry_embed';
20
21
    /**
22
     * EmbedRegistryPlugin constructor.
23
     */
24
    protected function __construct()
25
    {
26
        $authors = [
27
            'Angel Fernando Quiroz Campos',
28
        ];
29
30
        parent::__construct(
31
            '1.0',
32
            implode(', ', $authors),
33
            [
34
                self::SETTING_ENABLED => 'boolean',
35
                self::SETTING_TITLE => 'text',
36
                self::SETTING_EXTERNAL_URL => 'text',
37
            ]
38
        );
39
    }
40
41
    /**
42
     * @return string
43
     */
44
    public function getToolTitle()
45
    {
46
        $title = $this->get(self::SETTING_TITLE);
47
48
        if (!empty($title)) {
49
            return $title;
50
        }
51
52
        return $this->get_title();
53
    }
54
55
    /**
56
     * @return EmbedRegistryPlugin|null
57
     */
58
    public static function create()
59
    {
60
        static $result = null;
61
62
        return $result ? $result : $result = new self();
63
    }
64
65
    /**
66
     * Create DB schema for this plugin if not present.
67
     *
68
     * @throws \Doctrine\ORM\Tools\ToolsException
69
     * @throws \Doctrine\DBAL\Exception
70
     */
71
    public function install()
72
    {
73
        $em = Database::getManager();
74
75
        if ($em->getConnection()->createSchemaManager()->tablesExist([self::TBL_EMBED])) {
76
            return;
77
        }
78
79
        $schemaTool = new SchemaTool($em);
80
        $schemaTool->createSchema([$em->getClassMetadata(Embed::class)]);
81
    }
82
83
    /**
84
     * Drop DB schema if present.
85
     *
86
     * @throws \Doctrine\DBAL\Exception
87
     */
88
    public function uninstall()
89
    {
90
        $em = Database::getManager();
91
92
        if (!$em->getConnection()->createSchemaManager()->tablesExist([self::TBL_EMBED])) {
93
            return;
94
        }
95
96
        $schemaTool = new SchemaTool($em);
97
        $schemaTool->dropSchema([$em->getClassMetadata(Embed::class)]);
98
    }
99
100
    /**
101
     * After (re)configuring the plugin, (re)create tool links if enabled.
102
     *
103
     * @return EmbedRegistryPlugin
104
     */
105
    public function performActionsAfterConfigure()
106
    {
107
        $em = Database::getManager();
108
109
        $this->deleteCourseToolLinks();
110
111
        if ('true' === $this->get(self::SETTING_ENABLED)) {
112
            // Use FQCN instead of "ChamiloCoreBundle:Course".
113
            $courses = $em->createQuery('SELECT c.id FROM '.Course::class.' c')->getResult();
114
115
            foreach ($courses as $course) {
116
                $this->createLinkToCourseTool($this->getToolTitle(), $course['id']);
117
            }
118
        }
119
120
        return $this;
121
    }
122
123
    /**
124
     * Hook called when a course is deleted.
125
     * We only have the course ID here, but e.course is a relation,
126
     * so we must compare using IDENTITY(e.course) = :courseId.
127
     *
128
     * @param int $courseId
129
     */
130
    public function doWhenDeletingCourse($courseId)
131
    {
132
        $em = Database::getManager();
133
134
        // IMPORTANT: Use FQCN and IDENTITY() for relation-to-id comparison.
135
        $em->createQuery(
136
            'DELETE FROM '.Embed::class.' e WHERE IDENTITY(e.course) = :courseId'
137
        )
138
            ->setParameter('courseId', (int) $courseId)
139
            ->execute();
140
    }
141
142
    /**
143
     * Hook called when a session is deleted.
144
     * We only have the session ID here, but e.session is a relation,
145
     * so we must compare using IDENTITY(e.session) = :sessionId.
146
     *
147
     * @param int $sessionId
148
     */
149
    public function doWhenDeletingSession($sessionId)
150
    {
151
        $em = Database::getManager();
152
153
        // IMPORTANT: Use FQCN and IDENTITY() for relation-to-id comparison.
154
        $em->createQuery(
155
            'DELETE FROM '.Embed::class.' e WHERE IDENTITY(e.session) = :sessionId'
156
        )
157
            ->setParameter('sessionId', (int) $sessionId)
158
            ->execute();
159
    }
160
161
    /**
162
     * Get the currently active embed (by date range) for a course and optional session.
163
     * DO NOT compare an entity relation to a scalar id in DQL; bind the entity itself.
164
     *
165
     * @throws \Doctrine\ORM\NonUniqueResultException
166
     *
167
     * @return Embed|null
168
     */
169
    public function getCurrentEmbed(Course $course, Session $session = null)
170
    {
171
        $em = Database::getManager();
172
        $repo = $em->getRepository(Embed::class);
173
        $qb = $repo->createQueryBuilder('e');
174
175
        $qb
176
            ->where('e.displayStartDate <= :now')
177
            ->andWhere('e.displayEndDate >= :now')
178
            ->andWhere('e.course = :course')
179
            ->setParameter('now', new \DateTimeImmutable('now', new \DateTimeZone('UTC')))
180
            ->setParameter('course', $course);
181
182
        if ($session) {
183
            $qb->andWhere('e.session = :session')
184
                ->setParameter('session', $session);
185
        } else {
186
            $qb->andWhere('e.session IS NULL');
187
        }
188
189
        return $qb
190
            ->orderBy('e.displayStartDate', 'DESC')
191
            ->setMaxResults(1)
192
            ->getQuery()
193
            ->getOneOrNullResult();
194
    }
195
196
    /**
197
     * Human-readable date range for an embed.
198
     *
199
     * @return string
200
     */
201
    public function formatDisplayDate(Embed $embed)
202
    {
203
        $startDate = sprintf(
204
            '<time datetime="%s">%s</time>',
205
            $embed->getDisplayStartDate()->format(\DateTimeInterface::W3C),
206
            api_convert_and_format_date($embed->getDisplayStartDate())
207
        );
208
        $endDate = sprintf(
209
            '<time datetime="%s">%s</time>',
210
            $embed->getDisplayEndDate()->format(\DateTimeInterface::W3C),
211
            api_convert_and_format_date($embed->getDisplayEndDate())
212
        );
213
214
        return sprintf(get_lang('From %s to %s'), $startDate, $endDate);
215
    }
216
217
    /**
218
     * URL to view a single embed in the plugin.
219
     *
220
     * @return string
221
     */
222
    public function getViewUrl(Embed $embed)
223
    {
224
        return api_get_path(WEB_PLUGIN_PATH).'EmbedRegistry/view.php?id='.$embed->getId().'&'.api_get_cidreq();
225
    }
226
227
    /**
228
     * Count distinct users who accessed this plugin within the embed date window.
229
     * NOTE: TrackEAccess uses scalar fields (cId, accessSessionId), so pass integers.
230
     *
231
     * @throws \Doctrine\ORM\Query\QueryException
232
     *
233
     * @return int
234
     */
235
    public function getMembersCount(Embed $embed)
236
    {
237
        $dql = 'SELECT COUNT(DISTINCT tea.accessUserId) FROM '.TrackEAccess::class.' tea
238
                WHERE
239
                    tea.accessTool = :tool AND
240
                    (tea.accessDate >= :start_date AND tea.accessDate <= :end_date) AND
241
                    tea.cId = :courseId';
242
243
        $params = [
244
            'tool' => 'plugin_'.$this->get_name(),
245
            'start_date' => $embed->getDisplayStartDate(),
246
            'end_date' => $embed->getDisplayEndDate(),
247
            // IMPORTANT: cId is an integer field, not a relation.
248
            'courseId' => $embed->getCourse()->getId(),
249
        ];
250
251
        if ($embed->getSession()) {
252
            $dql .= ' AND tea.accessSessionId = :sessionId ';
253
            $params['sessionId'] = $embed->getSession()->getId();
254
        }
255
256
        return (int) Database::getManager()
257
            ->createQuery($dql)
258
            ->setParameters($params)
259
            ->getSingleScalarResult();
260
    }
261
262
    /**
263
     * Track a plugin access event (raw SQL-level insert is fine here).
264
     */
265
    public function saveEventAccessTool()
266
    {
267
        $tableAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
268
        $params = [
269
            'access_user_id' => api_get_user_id(),
270
            'c_id' => api_get_course_int_id(),
271
            'access_tool' => 'plugin_'.$this->get_name(),
272
            'access_date' => api_get_utc_datetime(),
273
            'access_session_id' => api_get_session_id(),
274
            'user_ip' => api_get_real_ip(),
275
        ];
276
        Database::insert($tableAccess, $params);
277
    }
278
279
    /**
280
     * Remove tool links created for this plugin in course tools.
281
     * Use FQCN (CTool::class) instead of "ChamiloCourseBundle:CTool".
282
     */
283
    private function deleteCourseToolLinks()
284
    {
285
        Database::getManager()
286
            ->createQuery(
287
                'DELETE FROM '.CTool::class.' t WHERE t.category = :category AND t.link LIKE :link'
288
            )
289
            ->setParameters(['category' => 'plugin', 'link' => 'EmbedRegistry/start.php%'])
290
            ->execute();
291
    }
292
}
293