CcBase   F
last analyzed

Complexity

Total Complexity 66

Size/Duplication

Total Lines 374
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 66
eloc 172
dl 0
loc 374
rs 3.12
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getquizns() 0 3 1
A getforumns() 0 3 1
A getresourcens() 0 3 1
A __construct() 0 12 2
A getItemCcType() 0 10 3
A getNodesByCriteria() 0 13 4
A getMetadata() 0 8 2
A convertToToolType() 0 21 5
A isAuth() 0 13 2
A createCourseCode() 0 7 1
A logAction() 0 8 2
B getManifest() 0 25 7
A criticalError() 0 23 1
A newxPath() 0 13 4
A logFile() 0 3 1
B countInstances() 0 18 7
A getModuleVisible() 0 18 5
A getItemHref() 0 10 3
C createInstances() 0 72 14

How to fix   Complexity   

Complex Class

Complex classes like CcBase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CcBase, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
class CcBase
5
{
6
    public const CC_TYPE_FORUM = 'imsdt_xmlv1p3';
7
    public const CC_TYPE_QUIZ = 'imsqti_xmlv1p3/imscc_xmlv1p3/assessment';
8
    public const CC_TYPE_QUESTION_BANK = 'imsqti_xmlv1p3/imscc_xmlv1p3/question-bank';
9
    public const CC_TYPE_WEBLINK = 'imswl_xmlv1p3';
10
    public const CC_TYPE_WEBCONTENT = 'webcontent';
11
    public const CC_TYPE_ASSOCIATED_CONTENT = 'associatedcontent/imscc_xmlv1p3/learning-application-resource';
12
    public const CC_TYPE_EMPTY = '';
13
14
    public static $restypes = ['associatedcontent/imscc_xmlv1p0/learning-application-resource', 'webcontent'];
15
    public static $forumns = ['dt' => 'http://www.imsglobal.org/xsd/imsdt_v1p0'];
16
    public static $quizns = ['xmlns' => 'http://www.imsglobal.org/xsd/ims_qtiasiv1p2'];
17
    public static $resourcens = ['wl' => 'http://www.imsglobal.org/xsd/imswl_v1p0'];
18
19
    public static $instances = [];
20
    public static $manifest;
21
    public static $pathToManifestFolder;
22
23
    public static $namespaces = ['imscc' => 'http://www.imsglobal.org/xsd/imscc/imscp_v1p1',
24
                                      'lomimscc' => 'http://ltsc.ieee.org/xsd/imscc/LOM',
25
                                      'lom' => 'http://ltsc.ieee.org/xsd/LOM',
26
                                      'voc' => 'http://ltsc.ieee.org/xsd/LOM/vocab',
27
                                      'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
28
                                      'cc' => 'http://www.imsglobal.org/xsd/imsccauth_v1p0', ];
29
30
    public function __construct($path_to_manifest)
31
    {
32
        static::$manifest = new DOMDocument();
33
        static::$manifest->validateOnParse = false;
34
35
        static::$pathToManifestFolder = dirname($path_to_manifest);
36
37
        static::logAction('Proccess start');
38
        static::logAction('Load the manifest file: '.$path_to_manifest);
39
40
        if (!static::$manifest->load($path_to_manifest, LIBXML_NONET)) {
41
            static::logAction('Cannot load the manifest file: '.$path_to_manifest, true);
42
        }
43
    }
44
45
    /**
46
     * @return array
47
     */
48
    public static function getquizns()
49
    {
50
        return static::$quizns;
51
    }
52
53
    /**
54
     * @return array
55
     */
56
    public static function getforumns()
57
    {
58
        return static::$forumns;
59
    }
60
61
    /**
62
     * @return array
63
     */
64
    public static function getresourcens()
65
    {
66
        return static::$resourcens;
67
    }
68
69
    /**
70
     * Find the imsmanifest.xml file inside the given folder and return its path.
71
     *
72
     * @param string $folder Full path name of the folder in which we expect to find imsmanifest.xml
73
     *
74
     * @return false|string
75
     */
76
    public static function getManifest(string $folder)
77
    {
78
        if (!is_dir($folder)) {
79
            return false;
80
        }
81
82
        // Before iterate over directories, try to find one manifest at top level
83
        if (file_exists($folder.'/imsmanifest.xml')) {
84
            return $folder.'/imsmanifest.xml';
85
        }
86
87
        $result = false;
88
        try {
89
            $dirIter = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::KEY_AS_PATHNAME);
90
            $recIter = new RecursiveIteratorIterator($dirIter, RecursiveIteratorIterator::CHILD_FIRST);
91
            foreach ($recIter as $info) {
92
                if ($info->isFile() && ($info->getFilename() == 'imsmanifest.xml')) {
93
                    $result = $info->getPathname();
94
                    break;
95
                }
96
            }
97
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
98
        }
99
100
        return $result;
101
    }
102
103
    public function isAuth()
104
    {
105
        $xpath = static::newxPath(static::$manifest, static::$namespaces);
106
107
        $count_auth = $xpath->evaluate('count(/imscc:manifest/cc:authorizations)');
108
109
        if ($count_auth > 0) {
110
            $response = true;
111
        } else {
112
            $response = false;
113
        }
114
115
        return $response;
116
    }
117
118
    public function getNodesByCriteria($key, $value)
119
    {
120
        $response = [];
121
122
        if (array_key_exists('index', static::$instances)) {
123
            foreach (static::$instances['index'] as $item) {
124
                if ($item[$key] == $value) {
125
                    $response[] = $item;
126
                }
127
            }
128
        }
129
130
        return $response;
131
    }
132
133
    public function countInstances($type)
134
    {
135
        $quantity = 0;
136
137
        if (array_key_exists('index', static::$instances)) {
138
            if (static::$instances['index'] && $type) {
139
                foreach (static::$instances['index'] as $instance) {
140
                    if (!empty($instance['tool_type'])) {
141
                        $types[] = $instance['tool_type'];
142
                    }
143
                }
144
145
                $quantityInstances = array_count_values($types);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $types does not seem to be defined for all execution paths leading up to this point.
Loading history...
146
                $quantity = array_key_exists($type, $quantityInstances) ? $quantityInstances[$type] : 0;
147
            }
148
        }
149
150
        return $quantity;
151
    }
152
153
    public function getItemCcType($identifier)
154
    {
155
        $xpath = static::newxPath(static::$manifest, static::$namespaces);
156
157
        $nodes = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$identifier.'"]/@type');
158
159
        if ($nodes && !empty($nodes->item(0)->nodeValue)) {
160
            return $nodes->item(0)->nodeValue;
161
        } else {
162
            return '';
163
        }
164
    }
165
166
    public function getItemHref($identifier)
167
    {
168
        $xpath = static::newxPath(static::$manifest, static::$namespaces);
169
170
        $nodes = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$identifier.'"]/imscc:file/@href');
171
172
        if ($nodes && !empty($nodes->item(0)->nodeValue)) {
173
            return $nodes->item(0)->nodeValue;
174
        } else {
175
            return '';
176
        }
177
    }
178
179
    public static function newxPath(DOMDocument $manifest, $namespaces = '')
180
    {
181
        $xpath = new DOMXPath($manifest);
182
183
        if (!empty($namespaces)) {
184
            foreach ($namespaces as $prefix => $ns) {
185
                if (!$xpath->registerNamespace($prefix, $ns)) {
186
                    static::logAction('Cannot register the namespace: '.$prefix.':'.$ns, true);
187
                }
188
            }
189
        }
190
191
        return $xpath;
192
    }
193
194
    public static function logFile()
195
    {
196
        return static::$pathToManifestFolder.DIRECTORY_SEPARATOR.'cc_import.log';
197
    }
198
199
    public static function logAction($text, $criticalError = false)
200
    {
201
        $full_message = strtoupper(date("j/n/Y g:i:s a"))." - ".$text."\r";
202
203
        file_put_contents(static::logFile(), $full_message, FILE_APPEND);
204
205
        if ($criticalError) {
206
            static::criticalError($text);
207
        }
208
    }
209
210
    public function convertToToolType($ccType)
211
    {
212
        $type = TYPE_UNKNOWN;
213
214
        if ($ccType == static::CC_TYPE_FORUM) {
215
            $type = TOOL_TYPE_FORUM;
216
        }
217
218
        if ($ccType == static::CC_TYPE_QUIZ) {
219
            $type = TOOL_TYPE_QUIZ;
220
        }
221
222
        if ($ccType == static::CC_TYPE_WEBLINK) {
223
            $type = TOOL_TYPE_WEBLINK;
224
        }
225
226
        if ($ccType == static::CC_TYPE_WEBCONTENT) {
227
            $type = TOOL_TYPE_DOCUMENT;
228
        }
229
230
        return $type;
231
    }
232
233
    protected function getMetadata($section, $key)
234
    {
235
        $xpath = static::newxPath(static::$manifest, static::$namespaces);
236
237
        $metadata = $xpath->query('/imscc:manifest/imscc:metadata/lomimscc:lom/lomimscc:'.$section.'/lomimscc:'.$key.'/lomimscc:string');
238
        $value = !empty($metadata->item(0)->nodeValue) ? $metadata->item(0)->nodeValue : '';
239
240
        return $value;
241
    }
242
243
    /**
244
     * Is activity visible or not.
245
     *
246
     * @param string $identifier
247
     *
248
     * @return number
249
     */
250
    protected function getModuleVisible($identifier)
251
    {
252
        //Should item be hidden or not
253
        $mod_visible = 1;
254
        if (!empty($identifier)) {
255
            $xpath = static::newxPath(static::$manifest, static::$namespaces);
256
            $query = '/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$identifier.'"]';
257
            $query .= '//lom:intendedEndUserRole/voc:vocabulary/lom:value';
258
            $intendeduserrole = $xpath->query($query);
259
            if (!empty($intendeduserrole) && ($intendeduserrole->length > 0)) {
260
                $role = trim($intendeduserrole->item(0)->nodeValue);
261
                if (strcasecmp('Instructor', $role) == 0) {
262
                    $mod_visible = 0;
263
                }
264
            }
265
        }
266
267
        return $mod_visible;
268
    }
269
270
    protected function createInstances($items, $level = 0, &$array_index = 0, $index_root = 0)
271
    {
272
        $level++;
273
        $i = 1;
274
275
        if ($items) {
276
            $xpath = self::newxPath(static::$manifest, static::$namespaces);
277
278
            foreach ($items as $item) {
279
                $array_index++;
280
                $title = $path = $tool_type = $identifierref = '';
281
                if ($item->nodeName == 'item') {
282
                    if ($item->hasAttribute('identifierref')) {
283
                        $identifierref = $item->getAttribute('identifierref');
284
                    }
285
286
                    $titles = $xpath->query('imscc:title', $item);
287
                    if ($titles->length > 0) {
288
                        $title = $titles->item(0)->nodeValue;
289
                    }
290
291
                    $ccType = $this->getItemCcType($identifierref);
292
                    $tool_type = $this->convertToToolType($ccType);
293
                    //Fix the label issue - MDL-33523
294
                    if (empty($identifierref) && empty($title)) {
295
                        $tool_type = TYPE_UNKNOWN;
296
                    }
297
                } elseif ($item->nodeName == 'resource') {
298
                    $identifierref = $xpath->query('@identifier', $item);
299
                    $identifierref = !empty($identifierref->item(0)->nodeValue) ? $identifierref->item(0)->nodeValue : '';
300
301
                    $ccType = $this->getItemCcType($identifierref);
302
                    $tool_type = $this->convertToToolType($ccType);
303
                    if (self::CC_TYPE_WEBCONTENT == $ccType) {
304
                        $path = $this->getItemHref($identifierref);
305
                        $title = basename($path);
306
                    } else { // A resource but not a file... we assume it's a quiz bank and its assigned identifier is irrelevant to its name
307
                        $title = 'Quiz Bank '.($this->countInstances($tool_type) + 1);
308
                    }
309
                }
310
311
                if ($level == ROOT_DEEP) {
312
                    $index_root = $array_index;
313
                }
314
315
                static::$instances['index'][$array_index] = [
316
                    'common_cartridge_type' => $ccType,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ccType does not seem to be defined for all execution paths leading up to this point.
Loading history...
317
                    'tool_type' => $tool_type,
318
                    'title' => $title ? $title : '',
319
                    'root_parent' => $index_root,
320
                    'index' => $array_index,
321
                    'deep' => $level,
322
                    'instance' => $this->countInstances($tool_type),
323
                    'resource_identifier' => $identifierref,
324
                ];
325
326
                static::$instances['instances'][$tool_type][] = [
327
                    'title' => $title,
328
                    'instance' => static::$instances['index'][$array_index]['instance'],
329
                    'common_cartridge_type' => $ccType,
330
                    'resource_identifier' => $identifierref,
331
                    'deep' => $level,
332
                    'src' => $path,
333
                ];
334
335
                $more_items = $xpath->query('imscc:item', $item);
336
337
                if ($more_items->length > 0) {
338
                    $this->createInstances($more_items, $level, $array_index, $index_root);
339
                }
340
341
                $i++;
342
            }
343
        }
344
    }
345
346
    protected static function criticalError($text)
347
    {
348
        $path_to_log = static::logFile();
349
350
        echo '
351
352
        <p>
353
        <hr />A critical error has been found!
354
355
        <p>'.$text.'</p>
356
357
358
        <p>
359
        The process has been stopped. Please see the <a href="'.$path_to_log.'">log file</a> for more information.</p>
360
361
        <p>Log: '.$path_to_log.'</p>
362
363
        <hr />
364
365
        </p>
366
        ';
367
368
        exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
369
    }
370
371
    protected function createCourseCode($title)
372
    {
373
        //Making sure that text of the short name does not go over the DB limit.
374
        //and leaving the space to add additional characters by the platform
375
        $code = substr(strtoupper(str_replace(' ', '', trim($title))), 0, 94);
376
377
        return $code;
378
    }
379
}
380