AuditController   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
dl 0
loc 275
rs 9.1199
c 0
b 0
f 0
wmc 41
lcom 1
cbo 9

7 Methods

Rating   Name   Duplication   Size   Complexity  
A options() 0 7 2
B actionCleanup() 0 46 11
B preCleanupSummary() 0 38 9
A cleanupEntry() 0 20 4
B cleanupEntrySolo() 0 29 7
A cleanupPanel() 0 21 4
A actionErrorEmail() 0 43 4

How to fix   Complexity   

Complex Class

Complex classes like AuditController 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 AuditController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace bedezign\yii2\audit\commands;
4
5
use bedezign\yii2\audit\Audit;
6
use bedezign\yii2\audit\components\panels\Panel;
7
use bedezign\yii2\audit\models\AuditEntry;
8
use bedezign\yii2\audit\models\AuditError;
9
use Yii;
10
use yii\base\Exception;
11
use yii\console\Controller;
12
use yii\console\ExitCode;
13
use yii\helpers\Console;
14
use yii\helpers\Html;
15
use yii\helpers\Url;
16
17
/**
18
 * Task runner commands for Audit.
19
 *
20
 * @package bedezign\yii2\audit\commands
21
 */
22
class AuditController extends Controller
23
{
24
25
    /**
26
     * @var bool True to cleanup the AuditEntry.
27
     */
28
    public $entry;
29
30
    /**
31
     * @var bool True to cleanup solo AuditEntry records (no trail/mail/error/javascript).
32
     */
33
    public $entrySolo;
34
35
    /**
36
     * @var string|null Comma separated list of panels to cleanup.
37
     */
38
    public $panels;
39
40
    /**
41
     * @var int|null Max age in days to cleanup, if null then the panel settings are used.
42
     */
43
    public $age;
44
45
    /**
46
     * @inheritdoc
47
     */
48
    public function options($actionID)
49
    {
50
        return array_merge(
51
            parent::options($actionID),
52
            ($actionID == 'cleanup') ? ['entry', 'entrySolo', 'panels', 'age'] : []
53
        );
54
    }
55
56
    /**
57
     * Cleanup the Audit data
58
     *
59
     * @return int
60
     */
61
    public function actionCleanup()
62
    {
63
        /** @var Audit $audit */
64
        $audit = Yii::$app->getModule(Audit::findModuleIdentifier());
65
        if (!$audit) {
66
            $this->stderr('Unable to load the Audit Component. Please make sure it was added to your console configuration?');
67
            return ExitCode::CONFIG;
68
        }
69
70
        if ($this->panels === '') {
71
            $panels = [];
72
        } else {
73
            $panels = !empty($this->panels) ? explode(',', $this->panels) : array_keys($audit->panels);
74
        }
75
76
        // summary
77
        $this->preCleanupSummary($this->entry, $this->entrySolo, $panels, $this->age);
78
79
        // confirm
80
        if ($this->confirm('Cleanup the above data?')) {
81
            // cleanup panels
82
            foreach ($panels as $id) {
83
                if (!$this->cleanupPanel($id, $this->age)) {
84
                    $this->stdout("\nCleanup failed. The rest of the cleanups are canceled.\n", Console::FG_RED);
85
                    return self::EXIT_CODE_ERROR;
0 ignored issues
show
Deprecated Code introduced by cornernote
The constant yii\console\Controller::EXIT_CODE_ERROR has been deprecated with message: since 2.0.13. Use [[ExitCode::UNSPECIFIED_ERROR]] instead.

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
86
                }
87
            }
88
            // cleanup audit_entry
89
            if ($this->entry) {
90
                if (!$this->cleanupEntry($this->age)) {
91
                    $this->stdout("\nCleanup failed.\n", Console::FG_RED);
92
                    return self::EXIT_CODE_ERROR;
0 ignored issues
show
Deprecated Code introduced by cornernote
The constant yii\console\Controller::EXIT_CODE_ERROR has been deprecated with message: since 2.0.13. Use [[ExitCode::UNSPECIFIED_ERROR]] instead.

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
93
                }
94
            }
95
            // cleanup solo audit_entry
96
            if ($this->entrySolo) {
97
                if (!$this->cleanupEntrySolo()) {
98
                    $this->stdout("\nCleanup failed.\n", Console::FG_RED);
99
                    return self::EXIT_CODE_ERROR;
0 ignored issues
show
Deprecated Code introduced by cornernote
The constant yii\console\Controller::EXIT_CODE_ERROR has been deprecated with message: since 2.0.13. Use [[ExitCode::UNSPECIFIED_ERROR]] instead.

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
100
                }
101
            }
102
            // success!
103
            $this->stdout("\nCleanup was successful.\n", Console::FG_GREEN);
104
        }
105
        return ExitCode::OK;
106
    }
107
108
    /**
109
     * Displays a summary of the data and dates to clean
110
     *
111
     * @param bool $entry
112
     * @param bool $entrySolo
113
     * @param array $panels
114
     * @param int|null $maxAge
115
     */
116
    protected function preCleanupSummary($entry, $entrySolo, $panels, $maxAge)
117
    {
118
        $audit = Audit::getInstance();
119
120
        // heading
121
        $n = count($panels);
122
        $this->stdout("Total $n " . ($n === 1 ? 'cleanup' : 'cleanups') . " to be applied:\n", Console::FG_YELLOW);
123
        $this->stdout("\t" . 'DATA                      CLEANUP TO DATETIME' . "\n");
124
        $this->stdout("\t" . '---------------------------------------------' . "\n");
125
126
        // audit panels
127
        foreach ($panels as $id) {
128
            /** @var Panel $panel */
129
            $panel = $audit->getPanel($id);
130
            $age = $maxAge !== null ? $maxAge : $panel->maxAge;
131
            $dots = str_repeat('.', 24 - strlen($id));
132
            if ($age !== null) {
133
                $date = date('Y-m-d 23:59:59', strtotime("-$age days"));
134
                $this->stdout("\t" . $id . ' ' . $dots . ' ' . $date . "\n");
135
            } else {
136
                $this->stdout("\t" . $id . ' ' . $dots . ' no maxAge, skipping' . "\n");
137
            }
138
        }
139
140
        // audit entry
141
        if ($entry) {
142
            $maxAge = $maxAge !== null ? $maxAge : $audit->maxAge;
143
            $date = $maxAge !== null ? date('Y-m-d 23:59:59', strtotime("-$maxAge days")) : 'no maxAge, skipping';
144
            $this->stdout("\t" . 'AuditEntry .............. ' . $date . "\n");
145
        }
146
147
        // audit entry solo
148
        if ($entrySolo) {
149
            $this->stdout("\t" . 'AuditEntry solo ......... ' . date('Y-m-d 23:59:59') . "\n");
150
        }
151
152
        $this->stdout("\n");
153
    }
154
155
    /**
156
     * Cleans the AuditEntry data
157
     *
158
     * @param $maxAge
159
     * @return bool
160
     */
161
    protected function cleanupEntry($maxAge)
162
    {
163
        $maxAge = $maxAge !== null ? $maxAge : Audit::getInstance()->maxAge;
164
        if ($maxAge === null) {
165
            $this->stdout("\n*** skipped AuditEntry\n", Console::FG_PURPLE);
166
            return true;
167
        }
168
        $date = date('Y-m-d 23:59:59', strtotime("-$maxAge days"));
169
        $this->stdout("\n*** cleaning AuditEntry", Console::FG_YELLOW);
170
        $start = microtime(true);
171
        $count = AuditEntry::deleteAll(['<=', 'created', $date]);
172
        if ($count !== false) {
173
            $time = microtime(true) - $start;
174
            $this->stdout("\n*** cleaned AuditEntry (records: " . $count . ",time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_GREEN);
175
            return true;
176
        }
177
        $time = microtime(true) - $start;
178
        $this->stdout("\n*** failed to clean AuditEntry (time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_RED);
179
        return false;
180
    }
181
182
    /**
183
     * Cleans the AuditEntry solo data (no trail/mail/error/javascript)
184
     *
185
     * @return bool
186
     */
187
    protected function cleanupEntrySolo()
188
    {
189
        $this->stdout("\n*** cleaning AuditEntry solo", Console::FG_YELLOW);
190
        $start = microtime(true);
191
        $count = 0;
192
        foreach (AuditEntry::find()->each(100) as $auditEntry) {
193
            /** @var AuditEntry $auditEntry */
194
            /** @var Audit $audit */
195
            $audit = Yii::$app->getModule('audit');
196
            $auditEntryCurrent = $audit->getEntry();
197
            if ($auditEntryCurrent && $auditEntryCurrent->id == $auditEntry->id) {
198
                continue;
199
            }
200
            if (!$auditEntry->hasRelatedData()) {
201
                foreach ($auditEntry->data as $data) {
202
                    $data->delete();
203
                }
204
                try {
205
                    $auditEntry->delete();
206
                    $count++;
207
                    $this->stdout('.', Console::FG_CYAN);
208
                } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by cornernote
Consider adding a comment why this CATCH block is empty.
Loading history...
209
                }
210
            }
211
        }
212
        $time = microtime(true) - $start;
213
        $this->stdout("\n*** cleaned AuditEntry (records: " . $count . ",time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_GREEN);
214
        return true;
215
    }
216
217
    /**
218
     * Cleans the Panel data
219
     *
220
     * @param $id
221
     * @param $maxAge
222
     * @return bool
223
     */
224
    protected function cleanupPanel($id, $maxAge)
225
    {
226
        /** @var Panel $panel */
227
        $panel = Audit::getInstance()->getPanel($id);
228
        $age = $maxAge !== null ? $maxAge : $panel->maxAge;
229
        if ($age === null) {
230
            $this->stdout("\n*** skipped $id\n", Console::FG_PURPLE);
231
            return true;
232
        }
233
        $this->stdout("\n*** cleaning $id", Console::FG_YELLOW);
234
        $start = microtime(true);
235
        $count = $panel->cleanup($maxAge);
236
        if ($count !== false) {
237
            $time = microtime(true) - $start;
238
            $this->stdout("\n*** cleaned $id (records: " . $count . ", time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_GREEN);
239
            return true;
240
        }
241
        $time = microtime(true) - $start;
242
        $this->stdout("\n*** failed to clean $id (time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_RED);
243
        return false;
244
    }
245
246
    /**
247
     * Email errors to support email.
248
     *
249
     * @param string|null $email
250
     * @return int
251
     */
252
    public function actionErrorEmail($email = null)
253
    {
254
        $email = $email ? $email : Yii::$app->params['supportEmail'];
255
256
        // find all errors to email
257
        $batch = AuditError::find()->where(['emailed' => 0])->batch();
258
        foreach ($batch as $auditErrors) {
259
            /** @var AuditError $model */
260
            foreach ($auditErrors as $model) {
261
262
                // define params and message
263
                $url = ['audit/default/view', 'id' => $model->entry_id];
264
                $params = [
265
                    'entry_id' => $model->entry_id,
266
                    'message' => $model->message,
267
                    'file' => $model->file,
268
                    'line' => $model->line,
269
                    'url' => Url::to($url),
270
                    'link' => Html::a(Yii::t('audit', 'view audit entry'), $url),
271
                ];
272
                $message = [
273
                    'subject' => Yii::t('audit', 'Audit Error in Audit Entry #{entry_id}', $params),
274
                    'text' => Yii::t('audit', '{message}' . "\n" . 'in {file} on line {line}.' . "\n" . '-- {url}', $params),
275
                    'html' => Yii::t('audit', '<b>{message}</b><br />in <i>{file}</i> on line <i>{line}</i>.<br/>-- {link}', $params),
276
                ];
277
278
                // send email
279
                Yii::$app->mailer->compose()
280
                    ->setFrom([$email => 'Audit :: ' . Yii::$app->name])
281
                    ->setTo($email)
282
                    ->setSubject($message['subject'])
283
                    ->setTextBody($message['text'])
284
                    ->setHtmlBody($message['html'])
285
                    ->send();
286
287
                // mark as emailed
288
                $model->emailed = 1;
289
                $model->save(false, ['emailed']);
290
291
            }
292
        }
293
        return self::EXIT_CODE_NORMAL;
0 ignored issues
show
Deprecated Code introduced by cornernote
The constant yii\console\Controller::EXIT_CODE_NORMAL has been deprecated with message: since 2.0.13. Use [[ExitCode::OK]] instead.

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
294
    }
295
296
}
297