Passed
Push — main ( 1c51a2...82ecb4 )
by MusikAnimal
07:46 queued 04:42
created

AutomatedEditsController::automatedEditsAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Controller;
6
7
use App\Model\AutoEdits;
8
use App\Repository\AutoEditsRepository;
9
use App\Repository\EditRepository;
10
use DateTime;
11
use OpenApi\Annotations as OA;
12
use Symfony\Component\HttpFoundation\JsonResponse;
13
use Symfony\Component\HttpFoundation\RedirectResponse;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\Routing\Annotation\Route;
16
17
/**
18
 * This controller serves the AutomatedEdits tool.
19
 */
20
class AutomatedEditsController extends XtoolsController
21
{
22
    protected AutoEdits $autoEdits;
23
24
    /** @var array Data that is passed to the view. */
25
    private array $output;
26
27
    /**
28
     * @inheritDoc
29
     * @codeCoverageIgnore
30
     */
31
    public function getIndexRoute(): string
32
    {
33
        return 'AutoEdits';
34
    }
35
36
    /**
37
     * This causes the tool to redirect back to the index page, with an error,
38
     * if the user has too high of an edit count.
39
     * @inheritDoc
40
     * @codeCoverageIgnore
41
     */
42
    public function tooHighEditCountRoute(): string
43
    {
44
        return $this->getIndexRoute();
45
    }
46
47
    /**
48
     * Display the search form.
49
     * @Route("/autoedits", name="AutoEdits")
50
     * @Route("/automatededits", name="AutoEditsLong")
51
     * @Route("/autoedits/index.php", name="AutoEditsIndexPhp")
52
     * @Route("/automatededits/index.php", name="AutoEditsLongIndexPhp")
53
     * @Route("/autoedits/{project}", name="AutoEditsProject")
54
     * @return Response
55
     */
56
    public function indexAction(): Response
57
    {
58
        // Redirect if at minimum project and username are provided.
59
        if (isset($this->params['project']) && isset($this->params['username'])) {
60
            // If 'tool' param is given, redirect to corresponding action.
61
            $tool = $this->request->query->get('tool');
62
63
            if ('all' === $tool) {
64
                unset($this->params['tool']);
65
                return $this->redirectToRoute('AutoEditsContributionsResult', $this->params);
66
            } elseif ('' != $tool && 'none' !== $tool) {
67
                $this->params['tool'] = $tool;
68
                return $this->redirectToRoute('AutoEditsContributionsResult', $this->params);
69
            } elseif ('none' === $tool) {
70
                unset($this->params['tool']);
71
            }
72
73
            // Otherwise redirect to the normal result action.
74
            return $this->redirectToRoute('AutoEditsResult', $this->params);
75
        }
76
77
        return $this->render('autoEdits/index.html.twig', array_merge([
78
            'xtPageTitle' => 'tool-autoedits',
79
            'xtSubtitle' => 'tool-autoedits-desc',
80
            'xtPage' => 'AutoEdits',
81
82
            // Defaults that will get overridden if in $this->params.
83
            'username' => '',
84
            'namespace' => 0,
85
            'start' => '',
86
            'end' => '',
87
        ], $this->params, ['project' => $this->project]));
88
    }
89
90
    /**
91
     * Set defaults, and instantiate the AutoEdits model. This is called at the top of every view action.
92
     * @param AutoEditsRepository $autoEditsRepo
93
     * @param EditRepository $editRepo
94
     * @codeCoverageIgnore
95
     */
96
    private function setupAutoEdits(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): void
97
    {
98
        $tool = $this->request->query->get('tool', null);
99
        $useSandbox = (bool)$this->request->query->get('usesandbox', false);
100
101
        if ($useSandbox && !$this->request->getSession()->get('logged_in_user')) {
102
            $this->addFlashMessage('danger', 'auto-edits-logged-out');
103
            $useSandbox = false;
104
        }
105
        $autoEditsRepo->setUseSandbox($useSandbox);
106
107
        $misconfigured = $autoEditsRepo->getInvalidTools($this->project);
108
        $helpLink = "https://w.wiki/ppr";
109
        foreach ($misconfigured as $tool) {
110
            $this->addFlashMessage('warning', 'auto-edits-misconfiguration', [$tool, $helpLink]);
111
        }
112
113
        // Validate tool.
114
        // FIXME: instead of redirecting to index page, show result page listing all tools for that project,
115
        //  clickable to show edits by the user, etc.
116
        if ($tool && !isset($autoEditsRepo->getTools($this->project)[$tool])) {
117
            $this->throwXtoolsException(
118
                $this->getIndexRoute(),
119
                'auto-edits-unknown-tool',
120
                [$tool],
121
                'tool'
122
            );
123
        }
124
125
        $this->autoEdits = new AutoEdits(
126
            $autoEditsRepo,
127
            $editRepo,
128
            $this->pageRepo,
129
            $this->userRepo,
130
            $this->project,
131
            $this->user,
0 ignored issues
show
Bug introduced by
It seems like $this->user can also be of type null; however, parameter $user of App\Model\AutoEdits::__construct() does only seem to accept App\Model\User, maybe add an additional type check? ( Ignorable by Annotation )

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

131
            /** @scrutinizer ignore-type */ $this->user,
Loading history...
132
            $this->namespace,
133
            $this->start,
134
            $this->end,
135
            $tool,
136
            $this->offset,
137
            $this->limit
138
        );
139
140
        $this->output = [
141
            'xtPage' => 'AutoEdits',
142
            'xtTitle' => $this->user->getUsername(),
0 ignored issues
show
Bug introduced by
The method getUsername() does not exist on null. ( Ignorable by Annotation )

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

142
            'xtTitle' => $this->user->/** @scrutinizer ignore-call */ getUsername(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
143
            'ae' => $this->autoEdits,
144
            'is_sub_request' => $this->isSubRequest,
145
        ];
146
147
        if ($useSandbox) {
148
            $this->output['usesandbox'] = 1;
149
        }
150
    }
151
152
    /**
153
     * Display the results.
154
     * @Route(
155
     *     "/autoedits/{project}/{username}/{namespace}/{start}/{end}/{offset}", name="AutoEditsResult",
156
     *     requirements={
157
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
158
     *         "namespace"="|all|\d+",
159
     *         "start"="|\d{4}-\d{2}-\d{2}",
160
     *         "end"="|\d{4}-\d{2}-\d{2}",
161
     *         "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
162
     *     },
163
     *     defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
164
     * )
165
     * @param AutoEditsRepository $autoEditsRepo
166
     * @param EditRepository $editRepo
167
     * @return Response
168
     * @codeCoverageIgnore
169
     */
170
    public function resultAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
171
    {
172
        // Will redirect back to index if the user has too high of an edit count.
173
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
174
175
        if (in_array('bot', $this->user->getUserRights($this->project))) {
176
            $this->addFlashMessage('warning', 'auto-edits-bot');
177
        }
178
179
        return $this->getFormattedResponse('autoEdits/result', $this->output);
180
    }
181
182
    /**
183
     * Get non-automated edits for the given user.
184
     * @Route(
185
     *   "/nonautoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
186
     *   name="NonAutoEditsContributionsResult",
187
     *   requirements={
188
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
189
     *       "namespace"="|all|\d+",
190
     *       "start"="|\d{4}-\d{2}-\d{2}",
191
     *       "end"="|\d{4}-\d{2}-\d{2}",
192
     *       "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
193
     *   },
194
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
195
     * )
196
     * @param AutoEditsRepository $autoEditsRepo
197
     * @param EditRepository $editRepo
198
     * @return Response|RedirectResponse
199
     * @codeCoverageIgnore
200
     */
201
    public function nonAutomatedEditsAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
202
    {
203
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
204
        return $this->getFormattedResponse('autoEdits/nonautomated_edits', $this->output);
205
    }
206
207
    /**
208
     * Get automated edits for the given user using the given tool.
209
     * @Route(
210
     *   "/autoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
211
     *   name="AutoEditsContributionsResult",
212
     *   requirements={
213
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
214
     *       "namespace"="|all|\d+",
215
     *       "start"="|\d{4}-\d{2}-\d{2}",
216
     *       "end"="|\d{4}-\d{2}-\d{2}",
217
     *       "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
218
     *   },
219
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
220
     * )
221
     * @param AutoEditsRepository $autoEditsRepo
222
     * @param EditRepository $editRepo
223
     * @return Response
224
     * @codeCoverageIgnore
225
     */
226
    public function automatedEditsAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
227
    {
228
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
229
230
        return $this->getFormattedResponse('autoEdits/automated_edits', $this->output);
231
    }
232
233
    /************************ API endpoints ************************/
234
235
    /**
236
     * Get a list of the known automated tools for a project along with their regex/tags/etc.
237
     * @Route("/api/project/automated_tools/{project}", name="ProjectApiAutoEditsTools", methods={"GET"})
238
     * @OA\Tag(name="Project API")
239
     * @OA\ExternalDocumentation(url="https://www.mediawiki.org/wiki/XTools/API/Project#Automated_tools")
240
     * @OA\Parameter(ref="#/components/parameters/Project")
241
     * @OA\Response(
242
     *     response=200,
243
     *     description="List of known (semi-)automated tools.",
244
     *     @OA\JsonContent(
245
     *         @OA\Property(property="project", ref="#/components/parameters/Project/schema"),
246
     *         @OA\Property(property="tools", type="object", example={
247
     *             "My tool": {
248
     *                 "regex": "\\(using My tool",
249
     *                 "link": "Project:My tool",
250
     *                 "label": "MyTool",
251
     *                 "namespaces": {0, 2, 4},
252
     *                 "tags": {"mytool"}
253
     *             }
254
     *         }),
255
     *         @OA\Property(property="elapsed_time", ref="#/components/schemas/elapsed_time")
256
     *     )
257
     * )
258
     * @OA\Response(response=404, ref="#/components/responses/404")
259
     * @param AutoEditsRepository $autoEditsRepo
260
     * @return JsonResponse
261
     * @codeCoverageIgnore
262
     */
263
    public function automatedToolsApiAction(AutoEditsRepository $autoEditsRepo): JsonResponse
264
    {
265
        $this->recordApiUsage('user/automated_tools');
266
        return $this->getFormattedApiResponse(
267
            ['tools' => $autoEditsRepo->getTools($this->project)],
268
        );
269
    }
270
271
    /**
272
     * Get the number of automated edits a user has made.
273
     * @Route(
274
     *   "/api/user/automated_editcount/{project}/{username}/{namespace}/{start}/{end}/{tools}",
275
     *   name="UserApiAutoEditsCount",
276
     *   requirements={
277
     *       "username"="(ipr-.+\/\d+[^\/])|([^\/]+)",
278
     *       "namespace"="|all|\d+",
279
     *       "start"="|\d{4}-\d{2}-\d{2}",
280
     *       "end"="|\d{4}-\d{2}-\d{2}"
281
     *   },
282
     *   defaults={"namespace"="all", "start"=false, "end"=false, "tools"=false},
283
     *   methods={"GET"}
284
     * )
285
     * @OA\Tag(name="User API")
286
     * @OA\Get(description="Get the number of edits a user has made using
287
            [known semi-automated tools](https://w.wiki/6oKQ), and optionally how many times each tool was used.")
288
     * @OA\Parameter(ref="#/components/parameters/Project")
289
     * @OA\Parameter(ref="#/components/parameters/UsernameOrIp")
290
     * @OA\Parameter(ref="#/components/parameters/Namespace")
291
     * @OA\Parameter(ref="#/components/parameters/Start")
292
     * @OA\Parameter(ref="#/components/parameters/End")
293
     * @OA\Parameter(ref="#/components/parameters/Tools")
294
     * @OA\Response(
295
     *     response=200,
296
     *     description="Count of edits made using [known (semi-)automated tools](https://w.wiki/6oKQ).",
297
     *     @OA\JsonContent(
298
     *         @OA\Property(property="project", ref="#/components/parameters/Project/schema"),
299
     *         @OA\Property(property="username", ref="#/components/parameters/Username/schema"),
300
     *         @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
301
     *         @OA\Property(property="start", ref="#/components/parameters/Start/schema"),
302
     *         @OA\Property(property="end", ref="#/components/parameters/End/schema"),
303
     *         @OA\Property(property="tools", ref="#/components/parameters/Tools/schema"),
304
     *         @OA\Property(property="total_editcount", type="integer"),
305
     *         @OA\Property(property="automated_editcount", type="integer"),
306
     *         @OA\Property(property="nonautomated_editcount", type="integer"),
307
     *         @OA\Property(property="automated_tools", ref="#/components/schemas/AutomatedTools"),
308
     *         @OA\Property(property="elapsed_time", type="float")
309
     *     )
310
     * )
311
     * @OA\Response(response=404, ref="#/components/responses/404")
312
     * @OA\Response(response=501, ref="#/components/responses/501")
313
     * @OA\Response(response=503, ref="#/components/responses/503")
314
     * @OA\Response(response=504, ref="#/components/responses/504")
315
     * @param AutoEditsRepository $autoEditsRepo
316
     * @param EditRepository $editRepo
317
     * @return JsonResponse
318
     * @codeCoverageIgnore
319
     */
320
    public function automatedEditCountApiAction(
321
        AutoEditsRepository $autoEditsRepo,
322
        EditRepository $editRepo
323
    ): JsonResponse {
324
        $this->recordApiUsage('user/automated_editcount');
325
326
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
327
328
        $ret = [
329
            'total_editcount' => $this->autoEdits->getEditCount(),
330
            'automated_editcount' => $this->autoEdits->getAutomatedCount(),
331
        ];
332
        $ret['nonautomated_editcount'] = $ret['total_editcount'] - $ret['automated_editcount'];
333
334
        if ($this->getBoolVal('tools')) {
335
            $tools = $this->autoEdits->getToolCounts();
336
            $ret['automated_tools'] = $tools;
337
        }
338
339
        return $this->getFormattedApiResponse($ret);
340
    }
341
342
    /**
343
     * Get non-automated contributions for a user.
344
     * @Route(
345
     *   "/api/user/nonautomated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
346
     *   name="UserApiNonAutoEdits",
347
     *   requirements={
348
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
349
     *       "namespace"="|all|\d+",
350
     *       "start"="|\d{4}-\d{2}-\d{2}",
351
     *       "end"="|\d{4}-\d{2}-\d{2}",
352
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}Z?"
353
     *   },
354
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false, "limit"=50},
355
     *   methods={"GET"}
356
     * )
357
     * @OA\Tag(name="User API")
358
     * @OA\Get(description="Get a list of contributions a user has made without the use of any
359
        [known (semi-)automated tools](https://w.wiki/6oKQ). If more results are available than the `limit`, a
360
        `continue` property is returned with the value that can passed as the `offset` in another API request
361
        to paginate through the results.")
362
     * @OA\Parameter(ref="#/components/parameters/Project")
363
     * @OA\Parameter(ref="#/components/parameters/UsernameOrIp")
364
     * @OA\Parameter(ref="#/components/parameters/Namespace")
365
     * @OA\Parameter(ref="#/components/parameters/Start")
366
     * @OA\Parameter(ref="#/components/parameters/End")
367
     * @OA\Parameter(ref="#/components/parameters/Offset")
368
     * @OA\Parameter(ref="#/components/parameters/LimitQuery")
369
     * @OA\Response(
370
     *     response=200,
371
     *     description="List of contributions made without [known (semi-)automated tools](https://w.wiki/6oKQ).",
372
     *     @OA\JsonContent(
373
     *         @OA\Property(property="project", ref="#/components/parameters/Project/schema"),
374
     *         @OA\Property(property="username", ref="#/components/parameters/UsernameOrIp/schema"),
375
     *         @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
376
     *         @OA\Property(property="start", ref="#/components/parameters/Start/schema"),
377
     *         @OA\Property(property="end", ref="#/components/parameters/End/schema"),
378
     *         @OA\Property(property="offset", ref="#/components/parameters/Offset/schema"),
379
     *         @OA\Property(property="limit", ref="#/components/parameters/Limit/schema"),
380
     *         @OA\Property(property="nonautomated_edits", type="array", @OA\Items(ref="#/components/schemas/Edit")),
381
     *         @OA\Property(property="continue", type="date-time", example="2020-01-31T12:59:59Z"),
382
     *         @OA\Property(property="elapsed_time", ref="#/components/schemas/elapsed_time")
383
     *     )
384
     * )
385
     * @OA\Response(response=404, ref="#/components/responses/404")
386
     * @OA\Response(response=501, ref="#/components/responses/501")
387
     * @OA\Response(response=503, ref="#/components/responses/503")
388
     * @OA\Response(response=504, ref="#/components/responses/504")
389
     * @param AutoEditsRepository $autoEditsRepo
390
     * @param EditRepository $editRepo
391
     * @return JsonResponse
392
     * @codeCoverageIgnore
393
     */
394
    public function nonAutomatedEditsApiAction(
395
        AutoEditsRepository $autoEditsRepo,
396
        EditRepository $editRepo
397
    ): JsonResponse {
398
        $this->recordApiUsage('user/nonautomated_edits');
399
400
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
401
402
        $results = $this->autoEdits->getNonAutomatedEdits(true);
403
        $out = $this->addFullPageTitlesAndContinue('nonautomated_edits', [], $results);
404
        if (count($results) === $this->limit) {
405
            $out['continue'] = (new DateTime(end($results)['timestamp']))->format('Y-m-d\TH:i:s');
406
        }
407
408
        $this->addApiWarnings();
409
        return $this->getFormattedApiResponse($out);
410
    }
411
412
    /**
413
     * Get (semi-)automated contributions made by a user.
414
     * @Route(
415
     *   "/api/user/automated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
416
     *   name="UserApiAutoEdits",
417
     *   requirements={
418
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
419
     *       "namespace"="|all|\d+",
420
     *       "start"="|\d{4}-\d{2}-\d{2}",
421
     *       "end"="|\d{4}-\d{2}-\d{2}",
422
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}",
423
     *   },
424
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false, "limit"=50},
425
     *   methods={"GET"}
426
     * )
427
     * @OA\Tag(name="User API")
428
     * @OA\Get(description="Get a list of contributions a user has made using of any of the
429
        [known (semi-)automated tools](https://w.wiki/6oKQ). If more results are available than the `limit`, a
430
        `continue` property is returned with the value that can passed as the `offset` in another API request
431
        to paginate through the results.")
432
     * @OA\Parameter(ref="#/components/parameters/Project")
433
     * @OA\Parameter(ref="#/components/parameters/UsernameOrIp")
434
     * @OA\Parameter(ref="#/components/parameters/Namespace")
435
     * @OA\Parameter(ref="#/components/parameters/Start")
436
     * @OA\Parameter(ref="#/components/parameters/End")
437
     * @OA\Parameter(ref="#/components/parameters/Offset")
438
     * @OA\Parameter(ref="#/components/parameters/LimitQuery")
439
     * @OA\Parameter(name="tool", in="query", description="Get only contributions using this tool.
440
        Use the [automated tools](#/Project%20API/get_ProjectApiAutoEditsTools) endpoint to list available tools.")
441
     * @OA\Response(
442
     *     response=200,
443
     *     description="List of contributions made using [known (semi-)automated tools](https://w.wiki/6oKQ).",
444
     *     @OA\JsonContent(
445
     *         @OA\Property(property="project", ref="#/components/parameters/Project/schema"),
446
     *         @OA\Property(property="username", ref="#/components/parameters/UsernameOrIp/schema"),
447
     *         @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
448
     *         @OA\Property(property="start", ref="#/components/parameters/Start/schema"),
449
     *         @OA\Property(property="end", ref="#/components/parameters/End/schema"),
450
     *         @OA\Property(property="offset", ref="#/components/parameters/Offset/schema"),
451
     *         @OA\Property(property="limit", ref="#/components/parameters/Limit/schema"),
452
     *         @OA\Property(property="tool", type="string", example="Twinkle"),
453
     *         @OA\Property(property="automated_edits", type="array", @OA\Items(ref="#/components/schemas/Edit")),
454
     *         @OA\Property(property="continue", type="date-time", example="2020-01-31T12:59:59Z"),
455
     *         @OA\Property(property="elapsed_time", ref="#/components/schemas/elapsed_time")
456
     *     )
457
     * )
458
     * @OA\Response(response=404, ref="#/components/responses/404")
459
     * @OA\Response(response=501, ref="#/components/responses/501")
460
     * @OA\Response(response=503, ref="#/components/responses/503")
461
     * @OA\Response(response=504, ref="#/components/responses/504")
462
     * @param AutoEditsRepository $autoEditsRepo
463
     * @param EditRepository $editRepo
464
     * @return Response
465
     * @codeCoverageIgnore
466
     */
467
    public function automatedEditsApiAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
468
    {
469
        $this->recordApiUsage('user/automated_edits');
470
471
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
472
473
        $extras = $this->autoEdits->getTool()
474
            ? ['tool' => $this->autoEdits->getTool()]
475
            : [];
476
477
        $results = $this->autoEdits->getAutomatedEdits(true);
478
        $out = $this->addFullPageTitlesAndContinue('automated_edits', $extras, $results);
479
        if (count($results) === $this->limit) {
480
            $out['continue'] = (new DateTime(end($results)['timestamp']))->format('Y-m-d\TH:i:s');
481
        }
482
483
        $this->addApiWarnings();
484
        return $this->getFormattedApiResponse($out);
485
    }
486
487
    private function addApiWarnings(): void
488
    {
489
        $this->addApiWarningAboutDates(['timestamp']);
490
        $this->addApiWarningAboutPageTitles();
491
        $this->addFlash('warning', 'In XTools 3.20, the minor property will return a boolean instead of 0 or 1.');
492
    }
493
}
494