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

src/Forms/CommentForm.php (2 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\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),
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),
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'
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();
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'
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 != '') {
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
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
$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'];
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