Passed
Push — master ( 6d0cf2...2055ed )
by Julito
08:08 queued 11s
created

CourseArchiver   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 127
c 0
b 0
f 0
dl 0
loc 263
rs 5.04
wmc 57

7 Methods

Rating   Name   Duplication   Size   Complexity  
F createBackup() 0 136 32
A getBackupDir() 0 3 1
A createBackupDir() 0 8 1
B cleanBackupDir() 0 14 8
B getAvailableBackups() 0 35 10
A importUploadedFile() 0 15 4
A readCourse() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like CourseArchiver 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 CourseArchiver, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
namespace Chamilo\CourseBundle\Component\CourseCopy;
6
7
use Chamilo\CourseBundle\Component\CourseCopy\Resources\Asset;
8
use Chamilo\CourseBundle\Component\CourseCopy\Resources\Document;
9
use DateTime;
10
use PclZip;
11
use Symfony\Component\Filesystem\Filesystem;
12
use UnserializeApi;
13
14
/**
15
 * Some functions to write a course-object to a zip-file and to read a course-
16
 * object from such a zip-file.
17
 *
18
 * @author Bart Mollet <[email protected]>
19
 *
20
 * @todo Use archive-folder of Chamilo?
21
 */
22
class CourseArchiver
23
{
24
    /**
25
     * @return string
26
     */
27
    public static function getBackupDir()
28
    {
29
        return api_get_path(SYS_ARCHIVE_PATH).'course_backups/';
30
    }
31
32
    /**
33
     * @return string
34
     */
35
    public static function createBackupDir()
36
    {
37
        $perms = api_get_permissions_for_new_directories();
38
        $dir = self::getBackupDir();
39
        $fs = new Filesystem();
40
        $fs->mkdir($dir, $perms);
41
42
        return $dir;
43
    }
44
45
    /**
46
     * Delete old temp-dirs.
47
     */
48
    public static function cleanBackupDir()
49
    {
50
        $dir = self::getBackupDir();
51
        if (is_dir($dir)) {
52
            if ($handle = @opendir($dir)) {
53
                while (false !== ($file = readdir($handle))) {
54
                    if ('.' != $file && '..' != $file &&
55
                        0 === strpos($file, 'CourseArchiver_') &&
56
                        is_dir($dir.'/'.$file)
57
                    ) {
58
                        rmdirr($dir.'/'.$file);
59
                    }
60
                }
61
                closedir($handle);
62
            }
63
        }
64
    }
65
66
    /**
67
     * Write a course and all its resources to a zip-file.
68
     *
69
     * @return string A pointer to the zip-file
70
     */
71
    public static function createBackup($course)
72
    {
73
        self::cleanBackupDir();
74
        self::createBackupDir();
75
76
        $perm_dirs = api_get_permissions_for_new_directories();
77
        $backupDirectory = self::getBackupDir();
78
79
        // Create a temp directory
80
        $backup_dir = $backupDirectory.'CourseArchiver_'.api_get_unique_id().'/';
81
82
        // All course-information will be stored in course_info.dat
83
        $course_info_file = $backup_dir.'course_info.dat';
84
85
        $user = api_get_user_info();
86
        $date = new DateTime(api_get_local_time());
87
        $zipFileName = $user['user_id'].'_'.$course->code.'_'.$date->format('Ymd-His').'.zip';
88
        $zipFilePath = $backupDirectory.$zipFileName;
89
90
        $php_errormsg = '';
91
        $res = @mkdir($backup_dir, $perm_dirs);
92
        if (false === $res) {
93
            //TODO set and handle an error message telling the user to review the permissions on the archive directory
94
            error_log(__FILE__.' line '.__LINE__.': '.(false != ini_get('track_errors') ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini').' - This error, occuring because your archive directory will not let this script write data into it, will prevent courses backups to be created', 0);
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing ini_get('track_errors') of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
95
        }
96
        // Write the course-object to the file
97
        $fp = @fopen($course_info_file, 'w');
98
        if (false === $fp) {
99
            error_log(__FILE__.' line '.__LINE__.': '.(false != ini_get('track_errors') ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing ini_get('track_errors') of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
100
        }
101
102
        $res = @fwrite($fp, base64_encode(serialize($course)));
103
        if (false === $res) {
104
            error_log(__FILE__.' line '.__LINE__.': '.(false != ini_get('track_errors') ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing ini_get('track_errors') of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
105
        }
106
107
        $res = @fclose($fp);
108
        if (false === $res) {
109
            error_log(__FILE__.' line '.__LINE__.': '.(false != ini_get('track_errors') ? $php_errormsg : 'error not recorded because track_errors is off in your php.ini'), 0);
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing ini_get('track_errors') of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
110
        }
111
112
        // Copy all documents to the temp-dir
113
        if (isset($course->resources[RESOURCE_DOCUMENT]) && is_array($course->resources[RESOURCE_DOCUMENT])) {
114
            $webEditorCss = api_get_path(WEB_CSS_PATH).'editor.css';
115
            /** @var Document $document */
116
            foreach ($course->resources[RESOURCE_DOCUMENT] as $document) {
117
                if ('document' === $document->file_type) {
118
                    $doc_dir = $backup_dir.$document->path;
119
                    @mkdir(dirname($doc_dir), $perm_dirs, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

119
                    /** @scrutinizer ignore-unhandled */ @mkdir(dirname($doc_dir), $perm_dirs, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
120
                    if (file_exists($course->path.$document->path)) {
121
                        copy($course->path.$document->path, $doc_dir);
122
                        // Check if is html or htm
123
                        $extension = pathinfo(basename($document->path), PATHINFO_EXTENSION);
124
                        switch ($extension) {
125
                            case 'html':
126
                            case 'htm':
127
                                $contents = file_get_contents($doc_dir);
128
                                $contents = str_replace(
129
                                    $webEditorCss,
130
                                    '{{css_editor}}',
131
                                    $contents
132
                                );
133
                                file_put_contents($doc_dir, $contents);
134
135
                                break;
136
                        }
137
                    }
138
                } else {
139
                    @mkdir($backup_dir.$document->path, $perm_dirs, true);
140
                }
141
            }
142
        }
143
144
        // Copy all scorm documents to the temp-dir
145
        if (isset($course->resources[RESOURCE_SCORM]) && is_array($course->resources[RESOURCE_SCORM])) {
146
            foreach ($course->resources[RESOURCE_SCORM] as $document) {
147
                copyDirTo($course->path.$document->path, $backup_dir.$document->path, false);
148
            }
149
        }
150
151
        // Copy calendar attachments.
152
        if (isset($course->resources[RESOURCE_EVENT]) && is_array($course->resources[RESOURCE_EVENT])) {
153
            $doc_dir = dirname($backup_dir.'/upload/calendar/');
154
            @mkdir($doc_dir, $perm_dirs, true);
155
            copyDirTo($course->path.'upload/calendar/', $doc_dir, false);
156
        }
157
158
        // Copy Learning path author image.
159
        if (isset($course->resources[RESOURCE_LEARNPATH]) && is_array($course->resources[RESOURCE_LEARNPATH])) {
160
            $doc_dir = dirname($backup_dir.'/upload/learning_path/');
161
            @mkdir($doc_dir, $perm_dirs, true);
162
            copyDirTo($course->path.'upload/learning_path/', $doc_dir, false);
163
        }
164
165
        // Copy announcements attachments.
166
        if (isset($course->resources[RESOURCE_ANNOUNCEMENT]) && is_array($course->resources[RESOURCE_ANNOUNCEMENT])) {
167
            $doc_dir = dirname($backup_dir.'/upload/announcements/');
168
            @mkdir($doc_dir, $perm_dirs, true);
169
            copyDirTo($course->path.'upload/announcements/', $doc_dir, false);
170
        }
171
172
        // Copy work folders (only folders)
173
        if (isset($course->resources[RESOURCE_WORK]) && is_array($course->resources[RESOURCE_WORK])) {
174
            $doc_dir = $backup_dir.'work';
175
            @mkdir($doc_dir, $perm_dirs, true);
176
            copyDirWithoutFilesTo($course->path.'work/', $doc_dir);
0 ignored issues
show
Bug introduced by
The function copyDirWithoutFilesTo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
            /** @scrutinizer ignore-call */ 
177
            copyDirWithoutFilesTo($course->path.'work/', $doc_dir);
Loading history...
177
        }
178
179
        if (isset($course->resources[RESOURCE_ASSET]) && is_array($course->resources[RESOURCE_ASSET])) {
180
            /** @var Asset $asset */
181
            foreach ($course->resources[RESOURCE_ASSET] as $asset) {
182
                $doc_dir = $backup_dir.$asset->path;
183
                @mkdir(dirname($doc_dir), $perm_dirs, true);
184
                $assetPath = $course->path.$asset->path;
185
186
                if (!file_exists($assetPath)) {
187
                    continue;
188
                }
189
190
                if (is_dir($course->path.$asset->path)) {
191
                    copyDirTo($course->path.$asset->path, $doc_dir, false);
192
193
                    continue;
194
                }
195
                copy($course->path.$asset->path, $doc_dir);
196
            }
197
        }
198
199
        // Zip the course-contents
200
        $zip = new PclZip($zipFilePath);
201
        $zip->create($backup_dir, PCLZIP_OPT_REMOVE_PATH, $backup_dir);
0 ignored issues
show
Bug introduced by
The constant Chamilo\CourseBundle\Com...\PCLZIP_OPT_REMOVE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
202
203
        // Remove the temp-dir.
204
        rmdirr($backup_dir);
205
206
        return $zipFileName;
207
    }
208
209
    /**
210
     * @param int $user_id
211
     *
212
     * @return array
213
     */
214
    public static function getAvailableBackups($user_id = null)
215
    {
216
        $backup_files = [];
217
        $dirname = self::getBackupDir();
218
219
        if (!file_exists($dirname)) {
220
            $dirname = self::createBackupDir();
221
        }
222
223
        if ($dir = opendir($dirname)) {
224
            while (false !== ($file = readdir($dir))) {
225
                $file_parts = explode('_', $file);
226
                if (3 == count($file_parts)) {
227
                    $owner_id = $file_parts[0];
228
                    $course_code = $file_parts[1];
229
                    $file_parts = explode('.', $file_parts[2]);
230
                    $date = $file_parts[0];
231
                    $ext = isset($file_parts[1]) ? $file_parts[1] : null;
232
                    if ('zip' == $ext && (null != $user_id && $owner_id == $user_id || null == $user_id)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $user_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing $user_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
233
                        $date =
234
                            substr($date, 0, 4).'-'.substr($date, 4, 2).'-'.
235
                            substr($date, 6, 2).' '.substr($date, 9, 2).':'.
236
                            substr($date, 11, 2).':'.substr($date, 13, 2);
237
                        $backup_files[] = [
238
                            'file' => $file,
239
                            'date' => $date,
240
                            'course_code' => $course_code,
241
                        ];
242
                    }
243
                }
244
            }
245
            closedir($dir);
246
        }
247
248
        return $backup_files;
249
    }
250
251
    /**
252
     * @param array $file
253
     *
254
     * @return bool|string
255
     */
256
    public static function importUploadedFile($file)
257
    {
258
        $new_filename = uniqid('import_file', true).'.zip';
259
        $new_dir = self::getBackupDir();
260
        if (!is_dir($new_dir)) {
261
            $fs = new Filesystem();
262
            $fs->mkdir($new_dir);
263
        }
264
        if (is_dir($new_dir) && is_writable($new_dir)) {
265
            move_uploaded_file($file, $new_dir.$new_filename);
266
267
            return $new_filename;
268
        }
269
270
        return false;
271
    }
272
273
    /**
274
     * Read a course-object from a zip-file.
275
     *
276
     * @param string $filename
277
     * @param bool   $delete   Delete the file after reading the course?
278
     *
279
     * @return Course The course
280
     *
281
     * @todo Check if the archive is a correct Chamilo-export
282
     */
283
    public static function readCourse($filename, $delete = false)
284
    {
285
    }
286
}
287