Passed
Push — master ( d344a1...da6ddd )
by Russell
02:54
created

VerifiableExtension::source()   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->source();
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
     * Source the data that will end-up hashed and submitted. This method will
76
     * call a custom verify() method on all decorated objects, if one is defined.
77
     * This provides a flexible public API for hashing and verifying pretty much
78
     * anything.
79
     *
80
     * If no such method exists, the default is to take the values of the YML
81
     * config "verifiable_fields" array, then hash and submit the values of those
82
     * fields. If no verifiable_fields are found or configured, we just return
83
     * an empty array and just stop.
84
     *
85
     * @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...
86
     * @return array
87
     */
88
    public function source($record = null) : array
89
    {
90
        $record = $record ?: $this->getOwner();
91
        $verifiable = [];
92
93
        if (method_exists($record, 'verify')) {
94
            $verifiable = (array) $record->verify();
95
        } else {
96
            $fields = $record->config()->get('verifiable_fields');
97
98
            foreach ($fields as $field) {
99
                if ($field === 'Proof') {
100
                    continue;
101
                }
102
103
                $verifiable[] = (string) $record->getField($field);
104
            }
105
        }
106
107
        return $verifiable;
108
    }
109
110
    /**
111
     * Adds a "Verification" tab to the CMS.
112
     *
113
     * @param  FieldList $fields
114
     * @return void
115
     */
116
    public function updateCMSFields(FieldList $fields)
117
    {
118
        parent::updateCMSFields($fields);
119
120
        Requirements::javascript('phptek/verifiable: client/verifiable.js');
121
122
        $owner = $this->getOwner();
123
        $list = [];
124
        $versions = $owner->Versions()->sort('Version');
125
126
        foreach ($versions as $item) {
127
            $list[$item->Version] = sprintf('Version: %s (Created: %s)', $item->Version, $item->Created);
128
        }
129
130
        $fields->addFieldsToTab('Root.Verify', FieldList::create([
131
            LiteralField::create('Introduction', '<p class="message">Select a version'
132
                    . ' whose data you wish to verify, then select the "Verify"'
133
                    . ' button. After a few seconds, a verification status will be'
134
                    . ' displayed.</p>'),
135
            HiddenField::create('Type', null, get_class($owner)),
136
            DropdownField::create('Version', 'Version', $list)
137
                ->setEmptyString('-- Select One --'),
138
            FormAction::create('doVerify', 'Verify')
139
                ->setUseButtonTag(true)
140
                ->addExtraClass('btn action btn-outline-primary ')
141
        ]));
142
    }
143
144
    /**
145
     * Get the contents of this model's "Extra" field by numeric index.
146
     *
147
     * @param  int $num
148
     * @return mixed array | int
149
     */
150
    public function getExtraByIndex(int $num = null)
151
    {
152
        $extra = $this->getOwner()->dbObject('Extra');
153
        $extra->setReturnType('array');
154
155
        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...
156
            return $extra->getStoreAsArray();
157
        }
158
159
        if (!empty($value = $extra->nth($num))) {
160
            return is_array($value) ? $value[0] : $value; // <-- stuuupId. Needs fixing in JSONText
161
        }
162
163
        return [];
164
    }
165
166
}
167