Passed
Push — master ( c5b98b...2d3b7c )
by Russell
15:44
created

VerifiableExtension::verify()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 2
nop 1
dl 0
loc 20
rs 9.2888
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @author  Russell Michell 2018 <[email protected]>
5
 * @package silverstripe-verifiable
6
 */
7
8
namespace PhpTek\Verifiable\Verify;
9
10
use SilverStripe\ORM\DataExtension;
11
use PhpTek\Verifiable\ORM\Fieldtype\ChainpointProof;
12
use SilverStripe\Forms\FieldList;
13
use SilverStripe\Forms\FormAction;
14
use SilverStripe\Forms\LiteralField;
15
use SilverStripe\Forms\DropdownField;
16
use SilverStripe\Forms\HiddenField;
17
use PhpTek\JSONText\ORM\FieldType\JSONText;
18
use SilverStripe\View\Requirements;
19
20
/**
21
 * By attaching this extension to any {@link DataObject} subclass and declaring a
22
 * $verifiable_fields array in YML config, all subsequent database writes will
23
 * be passed through here via {@link $this->onBeforeWrite()};
24
 *
25
 * This {@link DataExtension} also provides a single field to which all verified
26
 * and verifiable chainpoint proofs are stored in a queryable JSON-aware field.
27
 */
28
class VerifiableExtension extends DataExtension
29
{
30
    /**
31
     * Declares a JSON-aware {@link DBField} where all chainpoint proofs are stored.
32
     *
33
     * @var array
34
     * @config
35
     */
36
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
37
        'Proof' => ChainpointProof::class,
38
        'Extra' => JSONText::class,
39
    ];
40
41
    /**
42
     * The field-values that will be hashed and committed to the current backend.
43
     *
44
     * @var array
45
     * @config
46
     */
47
    private static $verifiable_fields = [];
0 ignored issues
show
introduced by
The private property $verifiable_fields is not used, and could be removed.
Loading history...
48
49
    /**
50
     * Before each write, data from the $verifiable_fields config is compiled
51
     * into a string, hashed and submitted to the current backend.
52
     *
53
     * @return void
54
     * @todo Implement in onAfterPublish() instead. Minimises HTTP requests to the backend.
55
     */
56
    public function onBeforeWrite()
57
    {
58
        parent::onBeforeWrite();
59
60
        $verifiable = $this->verify();
61
        $owner = $this->getOwner();
62
        $this->verifiableService->setExtra();
0 ignored issues
show
Bug introduced by
The property verifiableService does not exist on PhpTek\Verifiable\Verify\VerifiableExtension. Did you mean verifiable_fields?
Loading history...
63
64
        if (count($verifiable) && $proofData = $this->verifiableService->call('write', $verifiable)) {
65
            if (is_array($proofData)) {
66
                $proofData = json_encode($proofData);
67
            }
68
69
            $owner->setField('Proof', $proofData);
70
            $owner->setField('Extra', json_encode($this->verifiableService->getExtra()));
71
        }
72
    }
73
74
    /**
75
     * Call a custom verify() method on all decorated objects, if one exists.
76
     * This provides a flexible public API for hashing and verifying pretty much
77
     * anything.
78
     *
79
     * If no verify method exists, the default is to take the values of the YML
80
     * config "verifiable_fields" array, then hash and submit the values of those
81
     * fields. If no verifiable_fields are found or configured, we just return
82
     * an empty array and stop.
83
     *
84
     * @param  DataObject $record
0 ignored issues
show
Bug introduced by
The type PhpTek\Verifiable\Verify\DataObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
85
     * @return array
86
     */
87
    public function verify($record = null) : array
88
    {
89
        $record = $record ?: $this->getOwner();
90
        $verifiable = [];
91
92
        if (method_exists($record, 'verify')) {
93
            $verifiable = (array) $record->verify();
94
        } else {
95
            $fields = $record->config()->get('verifiable_fields');
96
97
            foreach ($fields as $field) {
98
                if ($field === 'Proof') {
99
                    continue;
100
                }
101
102
                $verifiable[] = (string) $record->getField($field);
103
            }
104
        }
105
106
        return $verifiable;
107
    }
108
109
    /**
110
     * Adds a "Verification" tab to the CMS.
111
     *
112
     * @param  FieldList $fields
113
     * @return void
114
     */
115
    public function updateCMSFields(FieldList $fields)
116
    {
117
        parent::updateCMSFields($fields);
118
119
        Requirements::javascript('phptek/verifiable: client/verifiable.js');
120
121
        $owner = $this->getOwner();
122
        $list = [];
123
        $versions = $owner->Versions()->sort('Version');
124
125
        foreach ($versions as $item) {
126
            $list[$item->Version] = sprintf('Version: %s (Created: %s)', $item->Version, $item->Created);
127
        }
128
129
        $fields->addFieldsToTab('Root.Verify', FieldList::create([
130
            LiteralField::create('Introduction', '<p class="message">Select a version'
131
                    . ' whose data you wish to verify, then select the "Verify"'
132
                    . ' button. After a few seconds, a verification status will be'
133
                    . ' displayed.</p>'),
134
            HiddenField::create('Type', null, get_class($owner)),
135
            DropdownField::create('Version', 'Version', $list)
136
                ->setEmptyString('-- Select One --'),
137
            FormAction::create('doVerify', 'Verify')
138
                ->setUseButtonTag(true)
139
                ->addExtraClass('btn action btn-outline-primary ')
140
        ]));
141
    }
142
143
    /**
144
     * Get the contents of this model's "Extra" field by numeric index.
145
     *
146
     * @param  int $num
147
     * @return mixed array | int
148
     */
149
    public function getExtraByIndex(int $num = null)
150
    {
151
        $extra = $this->getOwner()->dbObject('Extra');
152
        $extra->setReturnType('array');
153
154
        if (!$num) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $num of type null|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
155
            return $extra->getStoreAsArray();
156
        }
157
158
        if (!empty($value = $extra->nth($num))) {
159
            return is_array($value) ? $value[0] : $value; // <-- stuuupId. Needs fixing in JSONText
160
        }
161
162
        return [];
163
    }
164
165
}
166