Passed
Push — 1.11.x ( 5f02d6...ef1e71 )
by Yannick
11:47
created

CcHelpers::processLinkedFiles()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
c 0
b 0
f 0
dl 0
loc 53
rs 8.8177
cc 6
nc 8
nop 6

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
abstract class CcHelpers
5
{
6
    /**
7
     * Checks extension of the supplied filename.
8
     *
9
     * @param string $filename
10
     */
11
    public static function isHtml($filename)
12
    {
13
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
14
15
        return in_array($extension, ['htm', 'html']);
16
    }
17
18
    /**
19
     * Generates unique identifier.
20
     *
21
     * @param string $prefix
22
     * @param string $suffix
23
     *
24
     * @return string
25
     */
26
    public static function uuidgen($prefix = '', $suffix = '', $uppercase = true)
27
    {
28
        $uuid = trim(sprintf('%s%04x%04x%s', $prefix, mt_rand(0, 65535), mt_rand(0, 65535), $suffix));
29
        $result = $uppercase ? strtoupper($uuid) : strtolower($uuid);
30
31
        return $result;
32
    }
33
34
    /**
35
     * Creates new folder with random name.
36
     *
37
     * @param string $where
38
     * @param string $prefix
39
     * @param string $suffix
40
     *
41
     * @return mixed - directory short name or false in case of failure
42
     */
43
    public static function randomdir($where, $prefix = '', $suffix = '')
44
    {
45
        $permDirs = api_get_permissions_for_new_directories();
46
47
        $dirname = false;
48
        $randomname = self::uuidgen($prefix, $suffix, false);
49
        $newdirname = $where.DIRECTORY_SEPARATOR.$randomname;
50
        if (mkdir($newdirname)) {
51
            chmod($newdirname, $permDirs);
52
            $dirname = $randomname;
53
        }
54
55
        return $dirname;
56
    }
57
58
    public static function buildQuery($attributes, $search)
59
    {
60
        $result = '';
61
        foreach ($attributes as $attribute) {
62
            if ($result != '') {
63
                $result .= ' | ';
64
            }
65
            $result .= "//*[starts-with(@{$attribute},'{$search}')]/@{$attribute}";
66
        }
67
68
        return $result;
69
    }
70
71
    public static function processEmbeddedFiles(XMLGenericDocument &$doc, $attributes, $search, $customslash = null)
72
    {
73
        $result = [];
74
        $query = self::buildQuery($attributes, $search);
75
        $list = $doc->nodeList($query);
76
        foreach ($list as $filelink) {
77
            // Prepare the return value of just the filepath from within the course's document folder
78
            $rvalue = str_replace($search, '', $filelink->nodeValue);
79
            if (!empty($customslash)) {
80
                $rvalue = str_replace($customslash, '/', $rvalue);
81
            }
82
            $result[] = rawurldecode($rvalue);
83
        }
84
85
        return $result;
86
    }
87
88
    /**
89
     * Get list of embedded files.
90
     *
91
     * @param string $html
92
     *
93
     * @return multitype:mixed
94
     */
95
    public static function embeddedFiles(string $html, string $courseDir = null)
96
    {
97
        $result = [];
98
        $doc = new XMLGenericDocument();
99
        $doc->doc->validateOnParse = false;
100
        $doc->doc->strictErrorChecking = false;
101
        if (!empty($html) && $doc->loadHTML($html)) {
102
            $attributes = ['src', 'href'];
103
            $result1 = [];
104
            if (!empty($courseDir) && is_dir(api_get_path(SYS_COURSE_PATH).$courseDir)) {
105
                // get a list of files within the course's "document" directory (only those... for now)
106
                $result1 = self::processEmbeddedFiles($doc, $attributes, '/courses/'.$courseDir.'/document/');
107
            }
108
            $result2 = [];
109
            //$result2 = self::processEmbeddedFiles($doc, $attributes, '/app/upload/users/');
110
            $result = array_merge($result1, $result2);
111
        }
112
113
        return $result;
114
    }
115
116
    /**
117
     * Return an array of static media dependencies found in a given document file or a document and its neighbours in the same folder
118
     * @param $packageroot
119
     * @param $contextid
120
     * @param $folder
121
     * @param $docfilepath
122
     * @return array
123
     */
124
    public static function embeddedMapping($packageroot, $contextid = null, $folder = null, $docfilepath = null)
125
    {
126
        if (isset($folder)) {
127
            $files = array_diff(scandir($folder), ['.', '..']);
128
        } else {
129
            $folder = dirname($docfilepath);
130
            $files[] = basename($docfilepath);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$files was never initialized. Although not strictly required by PHP, it is generally a good practice to add $files = array(); before regardless.
Loading history...
131
        }
132
        $basePath = api_get_path(SYS_APP_PATH);
133
134
        $depfiles = [];
135
        foreach ($files as $file) {
136
            $mainfile = 1;
137
            $filename = $file;
138
            $filepath = DIRECTORY_SEPARATOR;
139
            $source = '';
140
            $author = '';
141
            $license = '';
142
            $hashedname = '';
143
            $hashpart = '';
144
145
            $location = $folder.DIRECTORY_SEPARATOR.$file;
146
            $type = mime_content_type($basePath.$location);
147
148
            $depfiles[$filepath.$filename] = [$location,
149
                                                    ($mainfile == 1),
150
                                                    strtolower(str_replace(' ', '_', $filename)),
151
                                                    $type,
152
                                                    $source,
153
                                                    $author,
154
                                                    $license,
155
                                                    strtolower(str_replace(' ', '_', $filepath)), ];
156
        }
157
158
        return $depfiles;
159
    }
160
161
    public static function addFiles(CcIManifest &$manifest, $packageroot, $outdir, $allinone = true, $folder = null, $docfilepath = null)
162
    {
163
        $permDirs = api_get_permissions_for_new_directories();
164
165
        $files = CcHelpers::embeddedMapping($packageroot, null, $folder, $docfilepath);
166
        $basePath = api_get_path(SYS_APP_PATH);
167
168
        $rdir = $allinone ? new CcResourceLocation($outdir) : null;
169
        foreach ($files as $virtual => $values) {
170
            $clean_filename = $values[2];
171
            if (!$allinone) {
172
                $rdir = new CcResourceLocation($outdir);
173
            }
174
            $rtp = $rdir->fullpath().$values[7].$clean_filename;
175
            //Are there any relative virtual directories?
176
            //let us try to recreate them
177
            $justdir = $rdir->fullpath(false).$values[7];
178
            if (!file_exists($justdir)) {
179
                if (!mkdir($justdir, $permDirs, true)) {
180
                    throw new RuntimeException('Unable to create directories!');
181
                }
182
            }
183
184
            $source = $values[0];
185
            if (is_dir($basePath.$source)) {
186
                continue;
187
            }
188
189
            if (!copy($basePath.$source, $rtp)) {
190
                throw new RuntimeException('Unable to copy files from '.$basePath.$source.' to '.$rtp.'!');
191
            }
192
            $resource = new CcResources($rdir->rootdir(),
193
                                        $values[7].$clean_filename,
194
                                        $rdir->dirname(false));
195
196
            $res = $manifest->addResource($resource, null, CcVersion13::WEBCONTENT);
197
198
            PkgStaticResources::instance()->add($virtual,
199
                                                  $res[0],
200
                                                  $rdir->dirname(false).$values[7].$clean_filename,
201
                                                  $values[1],
202
                                                  $resource);
203
        }
204
205
        PkgStaticResources::instance()->finished = true;
206
    }
207
208
    /**
209
     * Excerpt from IMS CC 1.1 overview :
210
     * No spaces in filenames, directory and file references should
211
     * employ all lowercase or all uppercase - no mixed case.
212
     *
213
     * @param string $packageroot
214
     * @param int    $contextid
215
     * @param string $outdir
216
     * @param bool   $allinone
217
     *
218
     * @throws RuntimeException
219
     */
220
    public static function handleStaticContent(CcIManifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true, $folder = null)
221
    {
222
        self::addFiles($manifest, $packageroot, $outdir, $allinone, $folder);
223
224
        return PkgStaticResources::instance()->getValues();
225
    }
226
227
    public static function handleResourceContent(CcIManifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true, $docfilepath = null)
228
    {
229
        $result = [];
230
231
        self::addFiles($manifest, $packageroot, $outdir, $allinone, null, $docfilepath);
232
233
        $files = self::embeddedMapping($packageroot, $contextid, null, $docfilepath);
234
        $rootnode = null;
235
        $rootvals = null;
236
        $depfiles = [];
237
        $depres = [];
238
        $flocation = null;
239
        foreach ($files as $virtual => $values) {
240
            $vals = PkgStaticResources::instance()->getIdentifier($virtual);
241
            $resource = $vals[3];
242
            $identifier = $resource->identifier;
243
            $flocation = $vals[1];
244
            if ($values[1]) {
245
                $rootnode = $resource;
246
                $rootvals = $flocation;
247
                continue;
248
            }
249
            $depres[] = $identifier;
250
            $depfiles[] = $vals[1];
251
            $result[$virtual] = [$identifier, $flocation, false];
252
        }
253
254
        if (!empty($rootnode)) {
255
            $rootnode->files = array_merge($rootnode->files, $depfiles);
256
            $result[$virtual] = [$rootnode->identifier, $rootvals, true];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $virtual seems to be defined by a foreach iteration on line 239. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
257
        }
258
259
        return $result;
260
    }
261
262
    /**
263
     * Detect embedded files in the given HTML string
264
     * @param string      $content The HTML string content
265
     * @param CcIManifest $manifest Manifest object (usually empty at this point) that will be filled
266
     * @param             $packageroot
267
     * @param             $contextid
268
     * @param             $outdir
269
     * @param             $webcontent
270
     * @return array
271
     */
272
    public static function processLinkedFiles(
273
        string $content,
274
        CcIManifest &$manifest,
275
        $packageroot,
276
        $contextid,
277
        $outdir,
278
        $webcontent = false
279
    )
280
    {
281
        // Detect all embedded files
282
        // copy all files in the cc package stripping any spaces and using only lowercase letters
283
        // add those files as resources of the type webcontent to the manifest
284
        // replace the links to the resource using $1Edtech-CC-FILEBASE$ and their new locations
285
        // cc_resource has array of files and array of dependencies
286
        // most likely we would need to add all files as independent resources and than
287
        // attach them all as dependencies to the forum tag.
288
        $courseDir = $internalCourseDocumentsPath = null;
289
        $courseInfo = api_get_course_info();
290
        $replaceprefix = '$1EdTech-CC-FILEBASE$';
291
        $tokenSyntax = api_get_configuration_value('commoncartridge_path_token');
292
        if (!empty($tokenSyntax)) {
293
            $replaceprefix = $tokenSyntax;
294
        }
295
        if (!empty($courseInfo)) {
296
            $courseDir = $courseInfo['directory'];
297
            $internalCourseDocumentsPath = '/courses/'.$courseDir.'/document';
298
        }
299
        $lfiles = self::embeddedFiles($content, $courseDir);
300
        $text = $content;
301
        $deps = [];
302
        if (!empty($lfiles)) {
303
            foreach ($lfiles as $lfile) {
304
                $lfile = DIRECTORY_SEPARATOR.$lfile; // results of handleResourceContent() come prefixed by DIRECTORY_SEPARATOR
305
                $files = self::handleResourceContent(
306
                    $manifest,
307
                    $packageroot,
308
                    $contextid,
309
                    $outdir,
310
                    true,
311
                    $internalCourseDocumentsPath.$lfile
312
                );
313
                if (isset($files[$lfile])) {
314
                    $filename = str_replace('%2F', '/', rawurlencode($lfile));
315
                    $content = str_replace($internalCourseDocumentsPath.$filename,
316
                                           $replaceprefix.'../'.$files[$lfile][1],
317
                                           $content);
318
                    $deps[] = $files[$lfile][0];
319
                }
320
            }
321
            $text = $content;
322
        }
323
324
        return [$text, $deps];
325
    }
326
}
327