Passed
Push — master ( ca275c...7ae7c9 )
by Russell
11:47
created

VerifiableExtension::getExtraByIndex()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 1
dl 0
loc 14
rs 9.2
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\ToggleCompositeField;
17
use PhpTek\JSONText\ORM\FieldType\JSONText;
18
19
/**
20
 * By attaching this extension to any {@link DataObject} subclass and declaring a
21
 * $verifiable_fields array in YML config, all subsequent database writes will
22
 * be passed through here via {@link $this->onBeforeWrite()};
23
 *
24
 * This {@link DataExtension} also provides a single field to which all verified
25
 * and verifiable chainpoint proofs are stored in a queryable JSON-aware field.
26
 *
27
 * @todo Hard-code "Created" and "LastEdited" fields into "verifiable_fields"
28
 * @todo Prevent "Proof" field from ever being configured in verifiable_fields
29
 * @todo Use AsyncPHP to make the initial write call to the backend, wait ~15s and then request a proof in return
30
 * @todo Use crontask module to periodically query backends for a full proof
31
 * @todo WARNING: Tight coupling between: Extension <=> Service <=> Backend (Node "Discovery" is chainpoint-specific)
32
 * @todo Rename x2 fields and prefix with 'v' ??
33
 * @todo Save only the IP octets, no need to save "http(s)?" in the "Extra" field
34
 * @todo Avoid node-discovery in anywhere _other_ than the CMS / admin UI
35
 */
36
class VerifiableExtension extends DataExtension
37
{
38
    /**
39
     * Declares a JSON-aware {@link DBField} where all chainpoint proofs are stored.
40
     *
41
     * @var array
42
     * @config
43
     */
44
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
45
        'Proof' => ChainpointProof::class,
46
        'Extra' => JSONText::class,
47
    ];
48
49
    /**
50
     * The field-values that will be hashed and committed to the current backend.
51
     *
52
     * @var array
53
     * @config
54
     */
55
    private static $verifiable_fields = [];
0 ignored issues
show
introduced by
The private property $verifiable_fields is not used, and could be removed.
Loading history...
56
57
    /**
58
     * Before each write, data from the $verifiable_fields config is compiled
59
     * into a string, hashed and submitted to the current backend.
60
     *
61
     * @return void
62
     */
63
    public function onBeforeWrite()
64
    {
65
        parent::onBeforeWrite();
66
67
        $verifiable = $this->normaliseData();
68
        $owner = $this->getOwner();
69
        // TODO Tight coupling
70
        $nodes = $owner->dbObject('Extra')->getStoreAsArray();
71
        $this->verifiableService->setNodes($nodes);
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...
72
73
        if (count($verifiable) && $proofData = $this->verifiableService->write($verifiable)) {
74
            if (is_array($proofData)) {
75
                $proofData = json_encode($proofData);
76
            }
77
78
            $owner->setField('Proof', $proofData);
79
            $owner->setField('Extra', json_encode($nodes));
80
        }
81
    }
82
83
    /**
84
     * Normalise this model's data so it's suited to being hashed.
85
     *
86
     * @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...
87
     * @return array
88
     */
89
    public function normaliseData($record = null) : array
90
    {
91
        $record = $record ?: $this->getOwner();
92
        $fields = $record->config()->get('verifiable_fields');
93
        $verifiable = [];
94
95
        foreach ($fields as $field) {
96
            $verifiable[] = (string) $record->getField($field);
97
        }
98
99
        return $verifiable;
100
    }
101
102
    /**
103
     * Adds a "Verification" tab to the CMS.
104
     *
105
     * @param  FieldList $fields
106
     * @return void
107
     * @todo Complete a basic CMS UI
108
     */
109
    public function updateCMSFields(FieldList $fields)
110
    {
111
        parent::updateCMSFields($fields);
112
113
        $owner = $this->getOwner();
114
        $list = [];
115
        $versions = $owner->Versions()->sort('Version');
116
        $keyTable = file_get_contents(realpath(__DIR__) . '/../../doc/statuses.html');
117
118
        foreach ($versions as $item) {
119
            $list[$item->Version] = sprintf('Version: %s (Created: %s)', $item->Version, $item->Created);
120
        }
121
122
        $fields->addFieldsToTab('Root.Verify', FieldList::create([
123
            LiteralField::create('Introduction', '<p class="message">Select a version'
124
                    . ' whose data you wish to verify, then select the "Verify"'
125
                    . ' button. After a few seconds, a verification status will be'
126
                    . ' displayed. Please refer to the "Status Key" table below to'
127
                    . ' interpret the result.</p>'),
128
            DropdownField::create('Version', 'Version', $list)
129
                ->setEmptyString('-- Select One --'),
130
                FormAction::create('doVerify', 'Verify'),
131
            ToggleCompositeField::create('KeyTable', 'Status Key', LiteralField::create('Foo', $keyTable)),
132
        ]));
133
    }
134
135
    /**
136
     * Get the contents of this model's "Extra" field by numeric index.
137
     *
138
     * @param  int $num
139
     * @return mixed array | int
140
     */
141
    public function getExtraByIndex(int $num = null)
142
    {
143
        $extra = $this->getOwner()->dbObject('Extra');
144
        $extra->setReturnType('array');
145
146
        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...
147
            return $extra->getStoreAsArray();
148
        }
149
150
        if (!empty($value = $extra->nth($num))) {
151
            return is_array($value) ? $value[0] : $value; // <-- stuuupId. Needs fixing in JSONText
152
        }
153
154
        return [];
155
    }
156
157
}
158