Completed
Push — master ( 187618...8c43e0 )
by Will
12s
created

CommentForm   C

Complexity

Total Complexity 33

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 0
Metric Value
wmc 33
lcom 1
cbo 20
dl 0
loc 255
rs 6.0823
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
D __construct() 0 126 14
A doPreviewComment() 0 6 1
F doPostComment() 0 102 18
1
<?php
2
3
namespace SilverStripe\Comments\Forms;
4
5
use SilverStripe\Forms\CompositeField;
6
use SilverStripe\Forms\EmailField;
7
use SilverStripe\Forms\FieldList;
8
use SilverStripe\Forms\Form;
9
use SilverStripe\Forms\FormAction;
10
use SilverStripe\Forms\HiddenField;
11
use SilverStripe\Forms\ReadonlyField;
12
use SilverStripe\Forms\RequiredFields;
13
use SilverStripe\Forms\TextareaField;
14
use SilverStripe\Forms\TextField;
15
use SilverStripe\Security\Member;
16
use SilverStripe\Control\Cookie;
17
use SilverStripe\Core\Convert;
18
use SilverStripe\Security\Security;
19
use SilverStripe\Comments\Model\Comment;
20
use SilverStripe\Control\Controller;
21
use SilverStripe\Comments\Controllers\CommentingController;
22
use SilverStripe\Core\Config\Config;
23
24
class CommentForm extends Form
25
{
26
    /**
27
     * @param string $name
28
     * @param CommentingController $controller
29
     */
30
    public function __construct($name, CommentingController $controller)
31
    {
32
        $usePreview = $controller->getOption('use_preview');
33
        $nameRequired = _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name');
34
        $emailRequired = _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address');
35
        $emailInvalid = _t('CommentInterface.EMAILADDRESS_MESSAGE_EMAIL', 'Please enter a valid email address');
36
        $urlInvalid = _t('CommentInterface.COMMENT_MESSAGE_URL', 'Please enter a valid URL');
37
        $commentRequired = _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment');
38
39
        $fields = FieldList::create(
40
            $dataFields = CompositeField::create(
41
                // Name
42
                $a = TextField::create('Name', _t('CommentInterface.YOURNAME', 'Your name'))
43
                    ->setCustomValidationMessage($nameRequired)
44
                    ->setAttribute('data-msg-required', $nameRequired),
45
                // Email
46
                EmailField::create(
47
                    'Email',
48
                    _t('SilverStripe\\Comments\\Controllers\\CommentingController.EMAILADDRESS', 'Your email address (will not be published)')
49
                )
50
                    ->setCustomValidationMessage($emailRequired)
51
                    ->setAttribute('data-msg-required', $emailRequired)
52
                    ->setAttribute('data-msg-email', $emailInvalid)
53
                    ->setAttribute('data-rule-email', true),
0 ignored issues
show
Documentation introduced by Damian Mooyman
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
54
                // Url
55
                TextField::create('URL', _t('SilverStripe\\Comments\\Controllers\\CommentingController.WEBSITEURL', 'Your website URL'))
56
                    ->setAttribute('data-msg-url', $urlInvalid)
57
                    ->setAttribute('data-rule-url', true),
0 ignored issues
show
Documentation introduced by Damian Mooyman
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
58
                // Comment
59
                TextareaField::create('Comment', _t('SilverStripe\\Comments\\Controllers\\CommentingController.COMMENTS', 'Comments'))
60
                    ->setCustomValidationMessage($commentRequired)
61
                    ->setAttribute('data-msg-required', $commentRequired)
62
            ),
63
            HiddenField::create('ParentID'),
64
            HiddenField::create('ParentClassName'),
65
            HiddenField::create('ReturnURL'),
66
            HiddenField::create('ParentCommentID')
67
        );
68
69
        // Preview formatted comment. Makes most sense when shortcodes or
70
        // limited HTML is allowed. Populated by JS/Ajax.
71
        if ($usePreview) {
72
            $fields->insertAfter(
73
                ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview'))
74
                    ->setAttribute('style', 'display: none'), // enable through JS
75
                'Comment'
0 ignored issues
show
Documentation introduced by Damian Mooyman
'Comment' is of type string, but the function expects a object<SilverStripe\Forms\FormField>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
76
            );
77
        }
78
79
        $dataFields->addExtraClass('data-fields');
80
81
        // save actions
82
        $actions = FieldList::create(
83
            $postAction = new FormAction('doPostComment', _t('CommentInterface.POST', 'Post'))
84
        );
85
86
        if ($usePreview) {
87
            $actions->push(
88
                FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview'))
89
                    ->addExtraClass('action-minor')
90
                    ->setAttribute('style', 'display: none') // enable through JS
91
            );
92
        }
93
94
        $required = new RequiredFields(
95
            $controller->config()->required_fields
96
        );
97
98
        parent::__construct($controller, $name, $fields, $actions, $required);
99
100
101
        // if the record exists load the extra required data
102
        if ($record = $controller->getOwnerRecord()) {
103
            // Load member data
104
            $member = Member::currentUser();
0 ignored issues
show
Deprecated Code introduced by Damian Mooyman
The method SilverStripe\Security\Member::currentUser() has been deprecated with message: 5.0.0 use Security::getCurrentUser()

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
105
            if (($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) {
106
                $fields = $this->Fields();
107
108
                $fields->removeByName('Name');
109
                $fields->removeByName('Email');
110
                $fields->insertBefore(
111
                    new ReadonlyField(
112
                        'NameView',
113
                        _t('CommentInterface.YOURNAME', 'Your name'),
114
                        $member->getName()
115
                    ),
116
                    'URL'
0 ignored issues
show
Documentation introduced by Robbie Averill
'URL' is of type string, but the function expects a object<SilverStripe\Forms\FormField>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
117
                );
118
                $fields->push(new HiddenField('Name', '', $member->getName()));
119
                $fields->push(new HiddenField('Email', '', $member->Email));
120
            }
121
122
            // we do not want to read a new URL when the form has already been submitted
123
            // which in here, it hasn't been.
124
            $this->loadDataFrom(array(
125
                'ParentID'        => $record->ID,
126
                'ReturnURL'       => $controller->getRequest()->getURL(),
127
                'ParentClassName' => $controller->getParentClass()
128
            ));
129
130
            if ($holder = $record->getCommentHolderID()) {
131
                $this->setHTMLID($holder);
132
            }
133
        }
134
135
        // Set it so the user gets redirected back down to the form upon form fail
136
        $this->setRedirectToFormOnValidationError(true);
137
138
        // load any data from the cookies
139
        if ($data = Cookie::get('CommentsForm_UserData')) {
140
            $data = Convert::json2array($data);
141
142
            $this->loadDataFrom(array(
143
                'Name'  => isset($data['Name']) ? $data['Name'] : '',
144
                'URL'   => isset($data['URL']) ? $data['URL'] : '',
145
                'Email' => isset($data['Email']) ? $data['Email'] : ''
146
            ));
147
148
            // allow previous value to fill if comment not stored in cookie (i.e. validation error)
149
            $prevComment = Cookie::get('CommentsForm_Comment');
150
151
            if ($prevComment && $prevComment != '') {
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The expression $prevComment of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
152
                $this->loadDataFrom(array('Comment' => $prevComment));
153
            }
154
        }
155
    }
156
157
    /**
158
     * @param  array $data
159
     * @param  Form $form
160
     * @return HTTPResponse
161
     */
162
    public function doPreviewComment($data, $form)
163
    {
164
        $data['IsPreview'] = 1;
165
166
        return $this->doPostComment($data, $form);
167
    }
168
169
    /**
170
     * Process which creates a {@link Comment} once a user submits a comment from this form.
171
     *
172
     * @param  array $data
173
     * @param  Form $form
174
     * @return HTTPResponse
175
     */
176
    public function doPostComment($data, $form)
177
    {
178
        // Load class and parent from data
179
        if (isset($data['ParentClassName'])) {
180
            $this->controller->setParentClass($data['ParentClassName']);
181
        }
182
        if (isset($data['ParentID']) && ($class = $this->controller->getParentClass())) {
183
            $this->controller->setOwnerRecord($class::get()->byID($data['ParentID']));
184
        }
185
        if (!$this->controller->getOwnerRecord()) {
186
            return $this->httpError(404);
0 ignored issues
show
Documentation Bug introduced by Will Rossiter
The method httpError does not exist on object<SilverStripe\Comments\Forms\CommentForm>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
187
        }
188
189
        // cache users data
190
        Cookie::set('CommentsForm_UserData', Convert::raw2json($data));
191
        Cookie::set('CommentsForm_Comment', $data['Comment']);
192
193
        // extend hook to allow extensions. Also see onAfterPostComment
194
        $this->controller->extend('onBeforePostComment', $form);
195
196
        // If commenting can only be done by logged in users, make sure the user is logged in
197
        if (!$this->controller->getOwnerRecord()->canPostComment()) {
198
            return Security::permissionFailure(
199
                $this,
0 ignored issues
show
Documentation introduced by Will Rossiter
$this is of type this<SilverStripe\Comments\Forms\CommentForm>, but the function expects a object<SilverStripe\Control\Controller>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
200
                _t(
201
                    'SilverStripe\\Comments\\Controllers\\CommentingController.PERMISSIONFAILURE',
202
                    "You're not able to post comments to this page. Please ensure you are logged in and have an "
203
                    . 'appropriate permission level.'
204
                )
205
            );
206
        }
207
208
        if ($member = Security::getCurrentUser()) {
209
            $form->Fields()->push(new HiddenField('AuthorID', 'Author ID', $member->ID));
210
        }
211
212
        // What kind of moderation is required?
213
        switch ($this->controller->getOwnerRecord()->ModerationRequired) {
214
            case 'Required':
215
                $requireModeration = true;
216
                break;
217
            case 'NonMembersOnly':
218
                $requireModeration = empty($member);
219
                break;
220
            case 'None':
221
            default:
222
                $requireModeration = false;
223
                break;
224
        }
225
226
        $comment = Comment::create();
227
        $form->saveInto($comment);
228
229
        $comment->ParentID = $data['ParentID'];
230
        $comment->ParentClass = $data['ParentClassName'];
0 ignored issues
show
Documentation introduced by Robbie Averill
The property ParentClass does not exist on object<SilverStripe\Comments\Model\Comment>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
231
232
        $comment->AllowHtml = $this->controller->getOption('html_allowed');
233
        $comment->Moderated = !$requireModeration;
234
235
        // Save into DB, or call pre-save hooks to give accurate preview
236
        $usePreview = $this->controller->getOption('use_preview');
237
        $isPreview = $usePreview && !empty($data['IsPreview']);
238
        if ($isPreview) {
239
            $comment->extend('onBeforeWrite');
240
        } else {
241
            $comment->write();
242
243
            // extend hook to allow extensions. Also see onBeforePostComment
244
            $this->controller->extend('onAfterPostComment', $comment);
245
        }
246
247
        // we want to show a notification if comments are moderated
248
        if ($requireModeration && !$comment->IsSpam) {
249
            Session::set('CommentsModerated', 1);
250
        }
251
252
        // clear the users comment since it passed validation
253
        Cookie::set('CommentsForm_Comment', false);
254
255
        // Find parent link
256
        if (!empty($data['ReturnURL'])) {
257
            $url = $data['ReturnURL'];
258
        } elseif ($parent = $comment->Parent()) {
259
            $url = $parent->Link();
260
        } else {
261
            return $this->controller->redirectBack();
262
        }
263
264
        // Given a redirect page exists, attempt to link to the correct anchor
265
        if ($comment->IsSpam) {
266
            // Link to the form with the error message contained
267
            $hash = $form->FormName();
268
        } elseif (!$comment->Moderated) {
269
            // Display the "awaiting moderation" text
270
            $hash = 'moderated';
271
        } else {
272
            // Link to the moderated, non-spam comment
273
            $hash = $comment->Permalink();
274
        }
275
276
        return $this->controller->redirect(Controller::join_links($url, "#{$hash}"));
277
    }
278
}
279