Completed
Push — master ( 9dab44...8bd79e )
by Robbie
02:00
created

src/Forms/CommentForm.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SilverStripe\Comments\Forms;
4
5
use SilverStripe\Control\HTTPResponse;
6
use SilverStripe\Forms\CompositeField;
7
use SilverStripe\Forms\EmailField;
8
use SilverStripe\Forms\FieldList;
9
use SilverStripe\Forms\Form;
10
use SilverStripe\Forms\FormAction;
11
use SilverStripe\Forms\HiddenField;
12
use SilverStripe\Forms\ReadonlyField;
13
use SilverStripe\Forms\RequiredFields;
14
use SilverStripe\Forms\TextareaField;
15
use SilverStripe\Forms\TextField;
16
use SilverStripe\Security\Member;
17
use SilverStripe\Control\Cookie;
18
use SilverStripe\Core\Convert;
19
use SilverStripe\Security\Security;
20
use SilverStripe\Comments\Model\Comment;
21
use SilverStripe\Control\Controller;
22
use SilverStripe\Comments\Controllers\CommentingController;
23
use SilverStripe\Core\Config\Config;
24
25
class CommentForm extends Form
26
{
27
    /**
28
     * @param string $name
29
     * @param CommentingController $controller
30
     */
31
    public function __construct($name, CommentingController $controller)
32
    {
33
        $usePreview = $controller->getOption('use_preview');
34
        $nameRequired = _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name');
35
        $emailRequired = _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address');
36
        $emailInvalid = _t('CommentInterface.EMAILADDRESS_MESSAGE_EMAIL', 'Please enter a valid email address');
37
        $urlInvalid = _t('CommentInterface.COMMENT_MESSAGE_URL', 'Please enter a valid URL');
38
        $commentRequired = _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment');
39
40
        $fields = FieldList::create(
41
            $dataFields = CompositeField::create(
42
                // Name
43
                $a = TextField::create('Name', _t('CommentInterface.YOURNAME', 'Your name'))
44
                    ->setCustomValidationMessage($nameRequired)
45
                    ->setAttribute('data-msg-required', $nameRequired),
46
                // Email
47
                EmailField::create(
48
                    'Email',
49
                    _t('SilverStripe\\Comments\\Controllers\\CommentingController.EMAILADDRESS', 'Your email address (will not be published)')
50
                )
51
                    ->setCustomValidationMessage($emailRequired)
52
                    ->setAttribute('data-msg-required', $emailRequired)
53
                    ->setAttribute('data-msg-email', $emailInvalid)
54
                    ->setAttribute('data-rule-email', true),
0 ignored issues
show
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...
55
                // Url
56
                TextField::create('URL', _t('SilverStripe\\Comments\\Controllers\\CommentingController.WEBSITEURL', 'Your website URL'))
57
                    ->setAttribute('data-msg-url', $urlInvalid)
58
                    ->setAttribute('data-rule-url', true),
0 ignored issues
show
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...
59
                // Comment
60
                TextareaField::create('Comment', _t('SilverStripe\\Comments\\Controllers\\CommentingController.COMMENTS', 'Comments'))
61
                    ->setCustomValidationMessage($commentRequired)
62
                    ->setAttribute('data-msg-required', $commentRequired)
63
            ),
64
            HiddenField::create('ParentID'),
65
            HiddenField::create('ParentClassName'),
66
            HiddenField::create('ReturnURL'),
67
            HiddenField::create('ParentCommentID')
68
        );
69
70
        // Preview formatted comment. Makes most sense when shortcodes or
71
        // limited HTML is allowed. Populated by JS/Ajax.
72
        if ($usePreview) {
73
            $fields->insertAfter(
74
                ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview'))
75
                    ->setAttribute('style', 'display: none'), // enable through JS
76
                'Comment'
0 ignored issues
show
'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...
77
            );
78
        }
79
80
        $dataFields->addExtraClass('data-fields');
81
82
        // save actions
83
        $actions = FieldList::create(
84
            $postAction = new FormAction('doPostComment', _t('CommentInterface.POST', 'Post'))
85
        );
86
87
        if ($usePreview) {
88
            $actions->push(
89
                FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview'))
90
                    ->addExtraClass('action-minor')
91
                    ->setAttribute('style', 'display: none') // enable through JS
92
            );
93
        }
94
95
        $required = new RequiredFields(
96
            $controller->config()->required_fields
97
        );
98
99
        parent::__construct($controller, $name, $fields, $actions, $required);
100
101
102
        // if the record exists load the extra required data
103
        if ($record = $controller->getOwnerRecord()) {
104
            // Load member data
105
            $member = Member::currentUser();
0 ignored issues
show
Deprecated Code introduced by
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...
106
            if (($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) {
107
                $fields = $this->Fields();
108
109
                $fields->removeByName('Name');
110
                $fields->removeByName('Email');
111
                $fields->insertBefore(
112
                    new ReadonlyField(
113
                        'NameView',
114
                        _t('CommentInterface.YOURNAME', 'Your name'),
115
                        $member->getName()
116
                    ),
117
                    'URL'
0 ignored issues
show
'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...
118
                );
119
                $fields->push(new HiddenField('Name', '', $member->getName()));
120
                $fields->push(new HiddenField('Email', '', $member->Email));
121
            }
122
123
            // we do not want to read a new URL when the form has already been submitted
124
            // which in here, it hasn't been.
125
            $this->loadDataFrom(array(
126
                'ParentID'        => $record->ID,
127
                'ReturnURL'       => $controller->getRequest()->getURL(),
128
                'ParentClassName' => $controller->getParentClass()
129
            ));
130
        }
131
132
        // Set it so the user gets redirected back down to the form upon form fail
133
        $this->setRedirectToFormOnValidationError(true);
134
135
        // load any data from the cookies
136
        if ($data = Cookie::get('CommentsForm_UserData')) {
137
            $data = Convert::json2array($data);
138
139
            $this->loadDataFrom(array(
140
                'Name'  => isset($data['Name']) ? $data['Name'] : '',
141
                'URL'   => isset($data['URL']) ? $data['URL'] : '',
142
                'Email' => isset($data['Email']) ? $data['Email'] : ''
143
            ));
144
145
            // allow previous value to fill if comment not stored in cookie (i.e. validation error)
146
            $prevComment = Cookie::get('CommentsForm_Comment');
147
148
            if ($prevComment && $prevComment != '') {
0 ignored issues
show
Bug Best Practice introduced by
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...
149
                $this->loadDataFrom(array('Comment' => $prevComment));
150
            }
151
        }
152
    }
153
154
    /**
155
     * @param  array $data
156
     * @param  Form $form
157
     * @return HTTPResponse
158
     */
159
    public function doPreviewComment($data, $form)
160
    {
161
        $data['IsPreview'] = 1;
162
163
        return $this->doPostComment($data, $form);
164
    }
165
166
    /**
167
     * Process which creates a {@link Comment} once a user submits a comment from this form.
168
     *
169
     * @param  array $data
170
     * @param  Form $form
171
     * @return HTTPResponse
172
     */
173
    public function doPostComment($data, $form)
174
    {
175
        // Load class and parent from data
176
        if (isset($data['ParentClassName'])) {
177
            $this->controller->setParentClass($data['ParentClassName']);
178
        }
179
        if (isset($data['ParentID']) && ($class = $this->controller->getParentClass())) {
180
            $this->controller->setOwnerRecord($class::get()->byID($data['ParentID']));
181
        }
182
        if (!$this->controller->getOwnerRecord()) {
183
            return $this->getRequestHandler()->httpError(404);
184
        }
185
186
        // cache users data
187
        Cookie::set('CommentsForm_UserData', Convert::raw2json($data));
188
        Cookie::set('CommentsForm_Comment', $data['Comment']);
189
190
        // extend hook to allow extensions. Also see onAfterPostComment
191
        $this->controller->extend('onBeforePostComment', $form);
192
193
        // If commenting can only be done by logged in users, make sure the user is logged in
194
        if (!$this->controller->getOwnerRecord()->canPostComment()) {
195
            return Security::permissionFailure(
196
                $this->controller,
0 ignored issues
show
$this->controller is of type object<SilverStripe\Control\RequestHandler>, 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...
197
                _t(
198
                    'SilverStripe\\Comments\\Controllers\\CommentingController.PERMISSIONFAILURE',
199
                    "You're not able to post comments to this page. Please ensure you are logged in and have an "
200
                    . 'appropriate permission level.'
201
                )
202
            );
203
        }
204
205
        if ($member = Security::getCurrentUser()) {
206
            $form->Fields()->push(new HiddenField('AuthorID', 'Author ID', $member->ID));
207
        }
208
209
        // What kind of moderation is required?
210
        switch ($this->controller->getOwnerRecord()->ModerationRequired) {
211
            case 'Required':
212
                $requireModeration = true;
213
                break;
214
            case 'NonMembersOnly':
215
                $requireModeration = empty($member);
216
                break;
217
            case 'None':
218
            default:
219
                $requireModeration = false;
220
                break;
221
        }
222
223
        $comment = Comment::create();
224
        $form->saveInto($comment);
225
226
        $comment->ParentID = $data['ParentID'];
227
        $comment->ParentClass = $data['ParentClassName'];
0 ignored issues
show
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...
228
229
        $comment->AllowHtml = $this->controller->getOption('html_allowed');
230
        $comment->Moderated = !$requireModeration;
231
232
        // Save into DB, or call pre-save hooks to give accurate preview
233
        $usePreview = $this->controller->getOption('use_preview');
234
        $isPreview = $usePreview && !empty($data['IsPreview']);
235
        if ($isPreview) {
236
            $comment->extend('onBeforeWrite');
237
        } else {
238
            $comment->write();
239
240
            // extend hook to allow extensions. Also see onBeforePostComment
241
            $this->controller->extend('onAfterPostComment', $comment);
242
        }
243
244
        // we want to show a notification if comments are moderated
245
        if ($requireModeration && !$comment->IsSpam) {
246
            $this->getRequest()->getSession()->set('CommentsModerated', 1);
247
        }
248
249
        // clear the users comment since it passed validation
250
        Cookie::set('CommentsForm_Comment', false);
251
252
        // Find parent link
253
        if (!empty($data['ReturnURL'])) {
254
            $url = $data['ReturnURL'];
255
        } elseif ($parent = $comment->Parent()) {
256
            $url = $parent->Link();
257
        } else {
258
            return $this->controller->redirectBack();
259
        }
260
261
        // Given a redirect page exists, attempt to link to the correct anchor
262
        if ($comment->IsSpam) {
263
            // Link to the form with the error message contained
264
            $hash = $form->FormName();
265
        } elseif (!$comment->Moderated) {
266
            // Display the "awaiting moderation" text
267
            $hash = 'moderated';
268
        } else {
269
            // Link to the moderated, non-spam comment
270
            $hash = $comment->Permalink();
271
        }
272
273
        return $this->controller->redirect(Controller::join_links($url, "#{$hash}"));
274
    }
275
}
276