diff --git a/wire/modules/Fieldtype/FieldtypeComments/CommentArray.php b/wire/modules/Fieldtype/FieldtypeComments/CommentArray.php index 0c6e0390..03af23b1 100644 --- a/wire/modules/Fieldtype/FieldtypeComments/CommentArray.php +++ b/wire/modules/Fieldtype/FieldtypeComments/CommentArray.php @@ -5,11 +5,11 @@ * * Maintains an array of multiple Comment instances. * Serves as the value referenced when a FieldtypeComment field is reference from a Page. - * - * ProcessWire 2.x - * Copyright (C) 2010 by Ryan Cramer + * + * ProcessWire 2.x + * Copyright (C) 2010 by Ryan Cramer * Licensed under GNU/GPL v2, see LICENSE.TXT - * + * * http://www.processwire.com * http://www.ryancramer.com * @@ -18,10 +18,10 @@ class CommentArray extends PaginatedArray implements WirePaginatable { /** - * Page that owns these comments, required to use the renderForm() or getCommentForm() methods. + * Page that owns these comments, required to use the renderForm() or getCommentForm() methods. * */ - protected $page = null; + protected $page = null; /** * Field object associated with this CommentArray @@ -31,14 +31,14 @@ class CommentArray extends PaginatedArray implements WirePaginatable { /** * Total number of comments, including those here and others that aren't, but may be here in pagination. - * + * * @var int - * + * */ protected $numTotal = 0; /** - * If this CommentArray is a partial representation of a larger set, this will contain the max number + * If this CommentArray is a partial representation of a larger set, this will contain the max number * of comments allowed to be present/loaded in the CommentArray at once. * * May vary from count() when on the last page of a result set. @@ -46,16 +46,16 @@ class CommentArray extends PaginatedArray implements WirePaginatable { * Applicable for paginated result sets. This number is not enforced for adding items to this CommentArray. * * @var int - * + * */ protected $numLimit = 0; /** - * If this CommentArray is a partial representation of a larger set, this will contain the starting result + * If this CommentArray is a partial representation of a larger set, this will contain the starting result * number if previous results preceded it. * * @var int - * + * */ protected $numStart = 0; @@ -65,9 +65,9 @@ class CommentArray extends PaginatedArray implements WirePaginatable { */ public function isValidItem($item) { if($item instanceof Comment) { - if($this->page) $item->setPage($this->page); - if($this->field) $item->setField($this->field); - return true; + if($this->page) $item->setPage($this->page); + if($this->field) $item->setField($this->field); + return true; } else { return false; } @@ -83,13 +83,14 @@ public function isValidItem($item) { */ public function render(array $options = array()) { $defaultOptions = array( + 'useImageField' => ($this->field ? $this->field->useImageField : ''), 'useGravatar' => ($this->field ? $this->field->useGravatar : ''), - 'useVotes' => ($this->field ? $this->field->useVotes : 0), - 'depth' => ($this->field ? (int) $this->field->depth : 0), - 'dateFormat' => 'relative', + 'useVotes' => ($this->field ? $this->field->useVotes : 0), + 'depth' => ($this->field ? (int) $this->field->depth : 0), + 'dateFormat' => 'relative', ); $options = array_merge($defaultOptions, $options); - $commentList = $this->getCommentList($options); + $commentList = $this->getCommentList($options); return $commentList->render(); } @@ -105,20 +106,20 @@ public function renderForm(array $options = array()) { $defaultOptions = array( 'depth' => ($this->field ? (int) $this->field->depth : 0) ); - $options = array_merge($defaultOptions, $options); - $form = $this->getCommentForm($options); + $options = array_merge($defaultOptions, $options); + $form = $this->getCommentForm($options); return $form->render(); } /** * Render all comments and a comments form below it - * + * * @param array $options * @return string - * + * */ public function renderAll(array $options = array()) { - return $this->render($options) . $this->renderForm($options); + return $this->render($options) . $this->renderForm($options); } /** @@ -126,7 +127,7 @@ public function renderAll(array $options = array()) { * */ public function getCommentList(array $options = array()) { - return new CommentList($this, $options); + return new CommentList($this, $options); } /** @@ -135,35 +136,35 @@ public function getCommentList(array $options = array()) { * @param array $options * @return CommentForm * @throws WireException - * + * */ public function getCommentForm(array $options = array()) { - if(!$this->page) throw new WireException("You must set a page to this CommentArray before using it i.e. \$ca->setPage(\$page)"); - return new CommentForm($this->page, $this, $options); + if(!$this->page) throw new WireException("You must set a page to this CommentArray before using it i.e. \$ca->setPage(\$page)"); + return new CommentForm($this->page, $this, $options); } /** - * Set the page that these comments are on + * Set the page that these comments are on * - */ + */ public function setPage(Page $page) { - $this->page = $page; + $this->page = $page; } /** - * Set the Field that these comments are on + * Set the Field that these comments are on * - */ + */ public function setField(Field $field) { - $this->field = $field; + $this->field = $field; } - + /** * Get the page that these comments are on * */ - public function getPage() { - return $this->page; + public function getPage() { + return $this->page; } /** diff --git a/wire/modules/Fieldtype/FieldtypeComments/CommentForm.php b/wire/modules/Fieldtype/FieldtypeComments/CommentForm.php index ccc80930..c1ebbe2c 100644 --- a/wire/modules/Fieldtype/FieldtypeComments/CommentForm.php +++ b/wire/modules/Fieldtype/FieldtypeComments/CommentForm.php @@ -3,15 +3,15 @@ /** * ProcessWire CommentFormInterface and CommentForm * - * Defines the CommentFormInterface and provides a base/example of this interface with the CommentForm class. + * Defines the CommentFormInterface and provides a base/example of this interface with the CommentForm class. * - * Use of this is optional, and it's primarily here for example purposes. - * You can make your own markup/output for the form directly in your own templates. - * - * ProcessWire 2.x - * Copyright (C) 2014 by Ryan Cramer + * Use of this is optional, and it's primarily here for example purposes. + * You can make your own markup/output for the form directly in your own templates. + * + * ProcessWire 2.x + * Copyright (C) 2014 by Ryan Cramer * Licensed under GNU/GPL v2, see LICENSE.TXT - * + * * http://processwire.com * */ @@ -22,13 +22,13 @@ */ interface CommentFormInterface { public function __construct(Page $page, CommentArray $comments, $options = array()); - public function render(); + public function render(); public function processInput(); } /** * Default/example implementation of the CommentFormInterface - * + * * Generates a user input form for comments, processes comment input, and saves to the page * * @see CommentArray::renderForm() @@ -40,13 +40,13 @@ class CommentForm extends Wire implements CommentFormInterface { * Page object where the comment is being submitted * */ - protected $page; + protected $page; /** * Reference to the Field object used by this CommentForm * */ - protected $commentsField; + protected $commentsField; /** * Instance of CommentArray, containing all Comment instances for this Page @@ -62,8 +62,8 @@ class CommentForm extends Wire implements CommentFormInterface { 'cite' => '', 'email' => '', 'text' => '', - 'notify' => '', - ); + 'notify' => '', + ); protected $postedComment = null; @@ -76,11 +76,11 @@ class CommentForm extends Wire implements CommentFormInterface { 'successMessage' => '', // Thank you, your submission has been saved 'pendingMessage' => '', // Your comment has been submitted and will appear once approved by the moderator. 'errorMessage' => '', // Your submission was not saved due to one or more errors. Try again. - 'processInput' => true, - 'encoding' => 'UTF-8', + 'processInput' => true, + 'encoding' => 'UTF-8', 'attrs' => array( - 'id' => 'CommentForm', - 'action' => './', + 'id' => 'CommentForm', + 'action' => './', 'method' => 'post', 'class' => '', 'rows' => 5, @@ -98,25 +98,31 @@ class CommentForm extends Wire implements CommentFormInterface { // note that using presets is NOT cache safe: do not use for non-logged-in users if output caches are active 'presets' => array( 'cite' => null, - 'email' => null, + 'email' => null, 'website' => null, 'text' => null, ), // whether or not eht preset values above will be changeable by the user - // applies only for preset values that are not null. + // applies only for preset values that are not null. 'presetsEditable' => false, // the name of a field that must be set (and have any non-blank value), typically set in Javascript to keep out spammers // to use it, YOU must set this with a field from your own javascript, somewhere in the form - 'requireSecurityField' => '', + 'requireSecurityField' => '', // should a redirect be performed immediately after a comment is successfully posted? 'redirectAfterPost' => null, // null=unset (must be set to true to enable) - + + // whether to use custom fields to create Your Name (cite) value + 'yourNameFields' => null, + + // should user fields (Your Name and Your E-mail) be hidden when logged in + 'hideUserFieldsLoggedIn' => 0, + // use threaded comments? - 'depth' => 0, - + 'depth' => 0, + // When a comment is saved to a page, avoid updating the modified time/user 'quietSave' => false, @@ -134,74 +140,78 @@ class CommentForm extends Wire implements CommentFormInterface { public function __construct(Page $page, CommentArray $comments, $options = array()) { $this->page = $page; - $this->comments = $comments; + $this->comments = $comments; // default messages $h3 = $this->_('h3'); // Headline tag $this->options['headline'] = "<$h3>" . $this->_('Post Comment') . ""; // Form headline - $this->options['successMessage'] = "

" . $this->_('Thank you, your submission has been saved.') . "

"; - $this->options['pendingMessage'] = "

" . $this->_('Your comment has been submitted and will appear once approved by the moderator.') . "

"; - $this->options['errorMessage'] = "

" . $this->_('Your submission was not saved due to one or more errors. Please check that you have completed all fields before submitting again.') . "

"; + $this->options['successMessage'] = "

" . $this->_('Thank you, your submission has been saved.') . "

"; + $this->options['pendingMessage'] = "

" . $this->_('Your comment has been submitted and will appear once approved by the moderator.') . "

"; + $this->options['errorMessage'] = "

" . $this->_('Your submission was not saved due to one or more errors. Please check that you have completed all fields before submitting again.') . "

"; // default labels - $this->options['labels']['cite'] = $this->_('Your Name'); - $this->options['labels']['email'] = $this->_('Your E-Mail'); - $this->options['labels']['website'] = $this->_('Your Website URL'); - $this->options['labels']['text'] = $this->_('Comments'); - $this->options['labels']['submit'] = $this->_('Submit'); + $this->options['labels']['cite'] = $this->_('Your Name'); + $this->options['labels']['email'] = $this->_('Your E-Mail'); + $this->options['labels']['website'] = $this->_('Your Website URL'); + $this->options['labels']['text'] = $this->_('Comments'); + $this->options['labels']['submit'] = $this->_('Submit'); if(isset($options['labels'])) { - $this->options['labels'] = array_merge($this->options['labels'], $options['labels']); - unset($options['labels']); + $this->options['labels'] = array_merge($this->options['labels'], $options['labels']); + unset($options['labels']); } if(isset($options['attrs'])) { - $this->options['attrs'] = array_merge($this->options['attrs'], $options['attrs']); - unset($options['attrs']); + $this->options['attrs'] = array_merge($this->options['attrs'], $options['attrs']); + unset($options['attrs']); } - $this->options = array_merge($this->options, $options); + $this->options = array_merge($this->options, $options); // determine which field on the page is the commentsField and save the Field instance foreach($this->wire('fields') as $field) { - if(!$field->type instanceof FieldtypeComments) continue; - $value = $this->page->get($field->name); + if(!$field->type instanceof FieldtypeComments) continue; + $value = $this->page->get($field->name); if($value === $this->comments) { $this->commentsField = $field; break; } } - // populate the vlaue of redirectAfterPost + // populate the value of redirectAfterPost if($this->commentsField && is_null($this->options['redirectAfterPost'])) { $this->options['redirectAfterPost'] = (bool) $this->commentsField->redirectAfterPost; } if($this->commentsField && $this->commentsField->quietSave) { - $this->options['quietSave'] = true; + $this->options['quietSave'] = true; + } + // populate the value of yourNameFields + if($this->commentsField && is_null($this->options['yourNameFields'])) { + $this->options['yourNameFields'] = $this->commentsField->yourNameFields; } } public function setAttr($attr, $value) { - $this->options['attrs'][$attr] = $value; + $this->options['attrs'][$attr] = $value; } public function setLabel($label, $value) { - $this->options['labels'][$label] = $value; + $this->options['labels'][$label] = $value; } /** * Replaces the output of the render() method when a Comment is posted * * A success message is shown rather than the form. - * + * * @param Comment|null $comment * @return string * */ protected function renderSuccess(Comment $comment = null) { - $pageID = (int) $this->wire('input')->post->page_id; - + $pageID = (int) $this->wire('input')->post->page_id; + if($pageID && $this->options['redirectAfterPost']) { // redirectAfterPost option - $page = $this->wire('pages')->get($pageID); + $page = $this->wire('pages')->get($pageID); if(!$page->viewable() || !$page->id) $page = $this->wire('page'); $url = $page->id ? $page->url : './'; $url .= "?comment_success=1"; @@ -214,15 +224,15 @@ protected function renderSuccess(Comment $comment = null) { $this->wire('session')->redirect($url); return ''; } - + if(!$this->commentsField || $this->commentsField->moderate == FieldtypeComments::moderateAll) { // all comments are moderated $message = $this->options['pendingMessage']; - + } else if($this->commentsField->moderate == FieldtypeComments::moderateNone) { // no moderation in service $message = $this->options['successMessage']; - + } else if($comment && $comment->status > Comment::statusPending) { // comment is approved $message = $this->options['successMessage']; @@ -230,13 +240,13 @@ protected function renderSuccess(Comment $comment = null) { } else if($this->wire('input')->get('comment_approved') == 1) { // comment was approved in previous request $message = $this->options['successMessage']; - + } else { // other/comment still pending $message = $this->options['pendingMessage']; } - - return "
$message
"; + + return "
$message
"; } /** @@ -248,49 +258,58 @@ protected function renderSuccess(Comment $comment = null) { public function render() { if(!$this->commentsField) return "Unable to determine comments field"; - $options = $this->options; + $options = $this->options; $labels = $options['labels']; $attrs = $options['attrs']; $id = $attrs['id']; $submitKey = $id . "_submit"; - $inputValues = array('cite' => '', 'email' => '', 'website' => '', 'text' => '', 'notify' => ''); - $user = wire('user'); + $inputValues = array('cite' => '', 'email' => '', 'website' => '', 'text' => '', 'notify' => ''); + $user = wire('user'); if($user->isLoggedin()) { - $inputValues['cite'] = $user->name; + $citeValue = ''; + if($this->options['yourNameFields']){ + foreach($this->options['yourNameFields'] as $yourNameField) { + $citeValue .= $user->{$this->fields->get($yourNameField)} . ' '; + } + } + else { + $citeValue = $user->name; + } + $inputValues['cite'] = rtrim($citeValue); $inputValues['email'] = $user->email; } - - $input = $this->wire('input'); + + $input = $this->wire('input'); $divClass = 'new'; - $class = trim("CommentForm " . $attrs['class']); + $class = trim("CommentForm " . $attrs['class']); $note = ''; /* - * Removed because this is not cache safe! Converted to JS cookie. - * + * Removed because this is not cache safe! Converted to JS cookie. + * if(is_array($this->session->CommentForm)) { // submission data available in the session $sessionValues = $this->session->CommentForm; foreach($inputValues as $key => $value) { - if($key == 'text') continue; + if($key == 'text') continue; if(!isset($sessionValues[$key])) $sessionValues[$key] = ''; - $inputValues[$key] = htmlentities($sessionValues[$key], ENT_QUOTES, $this->options['encoding']); + $inputValues[$key] = htmlentities($sessionValues[$key], ENT_QUOTES, $this->options['encoding']); } unset($sessionValues); } */ foreach($options['presets'] as $key => $value) { - if(!is_null($value)) $inputValues[$key] = $value; + if(!is_null($value)) $inputValues[$key] = $value; } $out = ''; - $showForm = true; - + $showForm = true; + if($options['processInput'] && $input->post->$submitKey == 1) { - $comment = $this->processInput(); - if($comment) { + $comment = $this->processInput(); + if($comment) { $out .= $this->renderSuccess($comment); // success, return } else { $inputValues = array_merge($inputValues, $this->inputValues); @@ -310,35 +329,37 @@ public function render() { if($this->options['depth'] > 0) { $form = $this->renderFormThread($id, $class, $attrs, $labels, $inputValues); } else { - $form = $this->renderFormNormal($id, $class, $attrs, $labels, $inputValues); + $form = $this->renderFormNormal($id, $class, $attrs, $labels, $inputValues); } if(!$options['presetsEditable']) { foreach($options['presets'] as $key => $value) { - if(!is_null($value)) $form = str_replace(" name='$key'", " name='$key' disabled='disabled'", $form); + if(!is_null($value)) $form = str_replace(" name='$key'", " name='$key' disabled='disabled'", $form); } } } - $out .= - "\n
" . - "\n" . $this->options['headline'] . $note . $form . + $out .= + "\n
" . + "\n" . $this->options['headline'] . $note . $form . "\n
"; - return $out; + return $out; } - + protected function renderFormNormal($id, $class, $attrs, $labels, $inputValues) { - $form = - "\n
" . - "\n\t

" . - "\n\t\t" . - "\n\t\t" . - "\n\t

" . - "\n\t

" . - "\n\t\t" . - "\n\t\t" . - "\n\t

"; + $form = + "\n"; + $hidden = ($this->user->isLoggedin() && $this->commentsField->hideUserFieldsLoggedIn) ? 'style="display:none"' : ''; + $form .= + "\n\t

" . + "\n\t\t" . + "\n\t\t" . + "\n\t

" . + "\n\t

" . + "\n\t\t" . + "\n\t\t" . + "\n\t

"; if($this->commentsField && $this->commentsField->useWebsite && $this->commentsField->schemaVersion > 0) { $form .= @@ -352,41 +373,42 @@ protected function renderFormNormal($id, $class, $attrs, $labels, $inputValues) "\n\t

" . "\n\t\t" . "\n\t\t" . - "\n\t

" . - $this->renderNotifyOptions() . + "\n\t

" . + $this->renderNotifyOptions() . "\n\t

" . "\n\t\t" . "\n\t\t" . "\n\t

" . "\n
"; - - return $form; + + return $form; } - + protected function renderFormThread($id, $class, $attrs, $labels, $inputValues) { - - $form = - "\n
" . - "\n\t

" . - "\n\t\t

" . + "\n\t\t " . "\n\t

" . - "\n\t

" . - "\n\t\t

" . + "\n\t\t" . + "\n\t\t" . "\n\t

"; if($this->commentsField && $this->commentsField->useWebsite && $this->commentsField->schemaVersion > 0) { $form .= "\n\t

" . - "\n\t\t" . "\n\t

"; } @@ -395,25 +417,25 @@ protected function renderFormThread($id, $class, $attrs, $labels, $inputValues) "\n\t\t" . + "\n\t\t" . "\n\t

" . - $this->renderNotifyOptions() . + $this->renderNotifyOptions() . "\n\t

" . "\n\t\t" . "\n\t\t" . "\n\t\t" . "\n\t

" . "\n
"; - + return $form; } - + protected function renderNotifyOptions() { if(!$this->commentsField->useNotify) return ''; $out = ''; - + $options = array(); - + if($this->commentsField->depth > 0) { $options['2'] = $this->_('Replies'); } @@ -422,50 +444,50 @@ protected function renderNotifyOptions() { if($this->commentsField->useNotify == Comment::flagNotifyAll) { $options['4'] = $this->_('All'); } - + if(count($options)) { - $out = - "\n\t

" . - "\n\t\t " . + $out = + "\n\t

" . + "\n\t\t " . "\n\t\t "; - + foreach($options as $value => $label) { - $label = str_replace(' ', ' ', $label); + $label = str_replace(' ', ' ', $label); $out .= "\n\t\t "; } $out .= "\n\t

"; } - - return $out; + + return $out; } /** * Process a submitted CommentForm, insert the Comment, and save the Page - * + * * @return Comment|bool * */ public function processInput() { - $data = $this->wire('input')->post; - if(!count($data)) return false; + $data = $this->wire('input')->post; + if(!count($data)) return false; if($key = $this->options['requireSecurityField']) { - if(empty($data[$key])) return false; + if(empty($data[$key])) return false; } - $comment = new Comment(); - $comment->user_agent = $_SERVER['HTTP_USER_AGENT']; + $comment = new Comment(); + $comment->user_agent = $_SERVER['HTTP_USER_AGENT']; $comment->ip = $this->wire('session')->getIP(); - $comment->created_users_id = $this->user->id; - $comment->sort = count($this->comments)+1; - $comment->parent_id = (int) $data->parent_id; + $comment->created_users_id = $this->user->id; + $comment->sort = count($this->comments)+1; + $comment->parent_id = (int) $data->parent_id; $errors = array(); - // $sessionData = array(); + // $sessionData = array(); foreach(array('cite', 'email', 'website', 'text') as $key) { - if($key == 'website' && (!$this->commentsField || !$this->commentsField->useWebsite)) continue; + if($key == 'website' && (!$this->commentsField || !$this->commentsField->useWebsite)) continue; if($this->options['presetsEditable'] || !isset($this->options['presets'][$key]) || $this->options['presets'][$key] === null) { $comment->$key = $data->$key; // Comment performs sanitization/validation } else { @@ -473,7 +495,7 @@ public function processInput() { } if($key != 'website' && !$comment->$key) $errors[] = $key; $this->inputValues[$key] = $comment->$key; - //if($key != 'text') $sessionData[$key] = $comment->$key; + //if($key != 'text') $sessionData[$key] = $comment->$key; } $flags = 0; @@ -494,15 +516,15 @@ public function processInput() { if(!count($errors)) { if($this->comments->add($comment) && $this->commentsField) { - $outputFormatting = $this->page->outputFormatting; + $outputFormatting = $this->page->outputFormatting; $this->page->setOutputFormatting(false); $saveOptions = array(); - if($this->options['quietSave']) $saveOptions['quiet'] = true; - $this->page->save($this->commentsField->name, $saveOptions); - $this->page->setOutputFormatting($outputFormatting); - $this->postedComment = $comment; + if($this->options['quietSave']) $saveOptions['quiet'] = true; + $this->page->save($this->commentsField->name, $saveOptions); + $this->page->setOutputFormatting($outputFormatting); + $this->postedComment = $comment; // $this->wire('session')->set('CommentForm', $sessionData); - return $comment; + return $comment; } } @@ -514,6 +536,6 @@ public function processInput() { * */ public function getPostedComment() { - return $this->postedComment; + return $this->postedComment; } } diff --git a/wire/modules/Fieldtype/FieldtypeComments/CommentList.php b/wire/modules/Fieldtype/FieldtypeComments/CommentList.php index 2f052f5a..7602db31 100644 --- a/wire/modules/Fieldtype/FieldtypeComments/CommentList.php +++ b/wire/modules/Fieldtype/FieldtypeComments/CommentList.php @@ -4,16 +4,16 @@ * ProcessWire CommentListInterface and CommentList * * CommentListInterface defines an interface for CommentLists. - * CommentList provides the default implementation of this interface. + * CommentList provides the default implementation of this interface. * - * Use of these is not required. These are just here to provide output for a FieldtypeComments field. + * Use of these is not required. These are just here to provide output for a FieldtypeComments field. * Typically you would iterate through the field and generate your own output. But if you just need - * something simple, or are testing, then this may fit your needs. - * - * ProcessWire 2.x - * Copyright (C) 2010 by Ryan Cramer + * something simple, or are testing, then this may fit your needs. + * + * ProcessWire 2.x + * Copyright (C) 2010 by Ryan Cramer * Licensed under GNU/GPL v2, see LICENSE.TXT - * + * * http://www.processwire.com * http://www.ryancramer.com * @@ -24,23 +24,23 @@ * */ interface CommentListInterface { - public function __construct(CommentArray $comments, $options = array()); + public function __construct(CommentArray $comments, $options = array()); public function render(); public function renderItem(Comment $comment); } /** - * CommentList provides the default implementation of the CommentListInterface interface. + * CommentList provides the default implementation of the CommentListInterface interface. * */ class CommentList extends Wire implements CommentListInterface { - + /** * Reference to CommentsArray provided in constructor * */ protected $comments = null; - + protected $page; protected $field; @@ -49,24 +49,25 @@ class CommentList extends Wire implements CommentListInterface { * */ protected $options = array( - 'headline' => '', // '

Comments

', + 'headline' => '', // '

Comments

', 'commentHeader' => '', // 'Posted by {cite} on {created}', - 'dateFormat' => '', // 'm/d/y g:ia', - 'encoding' => 'UTF-8', + 'dateFormat' => '', // 'm/d/y g:ia', + 'encoding' => 'UTF-8', 'admin' => false, // shows unapproved comments if true + 'useImageField' => '', // enable user image field 'useGravatar' => '', // enable gravatar? if so, specify maximum rating: [ g | pg | r | x ] or blank = disable gravatar 'useGravatarImageset' => 'mm', // default gravatar imageset, specify: [ 404 | mm | identicon | monsterid | wavatar ] 'usePermalink' => false, // @todo - 'useVotes' => 0, + 'useVotes' => 0, 'upvoteFormat' => '↑{cnt}', - 'downvoteFormat' => '↓{cnt}', - 'depth' => 0, - ); + 'downvoteFormat' => '↓{cnt}', + 'depth' => 0, + ); /** * Construct the CommentList * - * @param CommentArray $comments + * @param CommentArray $comments * @param array $options Options that may override those provided with the class (see CommentList::$options) * */ @@ -74,7 +75,7 @@ public function __construct(CommentArray $comments, $options = array()) { $h3 = $this->_('h3'); // Headline tag $this->options['headline'] = "<$h3>" . $this->_('Comments') . ""; // Header text - + if(empty($options['commentHeader'])) { if(empty($options['dateFormat'])) { $this->options['dateFormat'] = 'relative'; @@ -85,23 +86,23 @@ public function __construct(CommentArray $comments, $options = array()) { $this->options['dateFormat'] = $this->_('%b %e, %Y %l:%M %p'); // Date format in either PHP strftime() or PHP date() format // Example 1 (strftime): %b %e, %Y %l:%M %p = Feb 27, 2012 1:21 PM. Example 2 (date): m/d/y g:ia = 02/27/12 1:21pm. } } - - $this->comments = $comments; + + $this->comments = $comments; $this->page = $comments->getPage(); $this->field = $comments->getField(); - $this->options = array_merge($this->options, $options); + $this->options = array_merge($this->options, $options); } /** * Get replies to the given comment ID, or 0 for root level comments - * + * * @param int|Comment $commentID * @return array - * + * */ public function getReplies($commentID) { - if(is_object($commentID)) $commentID = $commentID->id; - $commentID = (int) $commentID; + if(is_object($commentID)) $commentID = $commentID->id; + $commentID = (int) $commentID; $admin = $this->options['admin']; $replies = array(); foreach($this->comments as $c) { @@ -109,7 +110,7 @@ public function getReplies($commentID) { if(!$admin && $c->status != Comment::statusApproved) continue; $replies[] = $c; } - return $replies; + return $replies; } /** @@ -120,11 +121,11 @@ public function getReplies($commentID) { * */ public function render() { - $out = $this->renderList(0); - if($out) $out = "\n" . $this->options['headline'] . $out; + $out = $this->renderList(0); + if($out) $out = "\n" . $this->options['headline'] . $out; return $out; } - + protected function renderList($parent_id = 0, $depth = 0) { $out = $parent_id ? '' : $this->renderCheckActions(); $comments = $this->options['depth'] > 0 ? $this->getReplies($parent_id) : $this->comments; @@ -134,62 +135,70 @@ protected function renderList($parent_id = 0, $depth = 0) { $class = "CommentList"; if($this->options['depth'] > 0) $class .= " CommentListThread"; else $class .= " CommentListNormal"; - if($this->options['useGravatar']) $class .= " CommentListHasGravatar"; + if($this->options['useImageField'] || $this->options['useGravatar']) $class .= " CommentListHasGravatar"; if($parent_id) $class .= " CommentListReplies"; $out = ""; - - return $out; + + return $out; } - + /** * Render the comment * * This is the default rendering for development/testing/demonstration purposes * * It may be used for production, but only if it meets your needs already. Typically you'll want to render the comments - * using your own code in your templates. + * using your own code in your templates. * * @see CommentArray::render() - * @return string + * @return string * */ public function renderItem(Comment $comment, $depth = 0) { - $text = $comment->getFormatted('text'); - $cite = $comment->getFormatted('cite'); + $text = $comment->getFormatted('text'); + $cite = $comment->getFormatted('cite'); + + $avatar = ''; + if($this->options['useImageField']) { + $u = $this->users->get("email={$comment->email}"); + $u->of(false); // set to false so that first() will always work when getting the image + $imgUrl = count($u->{$this->options['useImageField']}) ? $u->{$this->options['useImageField']}->first()->url : ''; + $u->of(true); + if($imgUrl) $avatar = "\n\t\t$cite"; + } - $gravatar = ''; - if($this->options['useGravatar']) { - $imgUrl = $comment->gravatar($this->options['useGravatar'], $this->options['useGravatarImageset']); - if($imgUrl) $gravatar = "\n\t\t$cite"; + if($this->options['useGravatar'] && !$imgUrl) { + $imgUrl = $comment->gravatar($this->options['useGravatar'], $this->options['useGravatarImageset']); + if($imgUrl) $avatar = "\n\t\t$cite"; } $website = ''; - if($comment->website) $website = $comment->getFormatted('website'); + if($comment->website) $website = $comment->getFormatted('website'); if($website) $cite = "$cite"; - $created = wireDate($this->options['dateFormat'], $comment->created); - + $created = wireDate($this->options['dateFormat'], $comment->created); + if(empty($this->options['commentHeader'])) { $header = "$cite $created "; - if($this->options['useVotes']) $header .= $this->renderVotes($comment); + if($this->options['useVotes']) $header .= $this->renderVotes($comment); } else { $header = str_replace(array('{cite}', '{created}'), array($cite, $created), $this->options['commentHeader']); - if(strpos($header, '{votes}') !== false) $header = str_replace('{votes}', $this->renderVotes($comment), $header); + if(strpos($header, '{votes}') !== false) $header = str_replace('{votes}', $this->renderVotes($comment), $header); } - + $liClass = ''; - $replies = $this->options['depth'] > 0 ? $this->renderList($comment->id, $depth+1) : ''; + $replies = $this->options['depth'] > 0 ? $this->renderList($comment->id, $depth+1) : ''; if($replies) $liClass .= ' CommentHasReplies'; - if($comment->status == Comment::statusPending) $liClass .= ' CommentStatusPending'; + if($comment->status == Comment::statusPending) $liClass .= ' CommentStatusPending'; else if($comment->status == Comment::statusSpam) $liClass .= ' CommentStatusSpam'; - - $out = - "\n\t
  • " . $gravatar . - "\n\t\t

    $header

    " . - "\n\t\t
    " . - "\n\t\t\t

    $text

    " . + + $out = + "\n\t
  • " . $avatar . + "\n\t\t

    $header

    " . + "\n\t\t
    " . + "\n\t\t\t

    $text

    " . "\n\t\t
    "; - + if($this->options['usePermalink']) { $permalink = $comment->getPage()->httpUrl; $urlSegmentStr = $this->wire('input')->urlSegmentStr; @@ -202,28 +211,28 @@ public function renderItem(Comment $comment, $depth = 0) { if($this->options['depth'] > 0 && $depth < $this->options['depth']) { $out .= - "\n\t\t
    " . + "\n\t\t
    " . "\n\t\t\t

    " . "\n\t\t\t\t" . $this->_('Reply') . " " . - ($permalink ? "\n\t\t\t\t$permalink" : "") . - "\n\t\t\t

    " . + ($permalink ? "\n\t\t\t\t$permalink" : "") . + "\n\t\t\t

    " . "\n\t\t
    "; - + if($replies) $out .= $replies; - + } else { $out .= "\n\t\t
    "; } - + $out .= "\n\t
  • "; - - return $out; + + return $out; } - + public function renderVotes(Comment $comment) { - + if(!$this->options['useVotes']) return ''; - + $upvoteFormat = str_replace('{cnt}', "$comment->upvotes", $this->options['upvoteFormat']); $upvoteURL = "{$this->page->url}?comment_success=upvote&comment_id=$comment->id&field_id={$this->field->id}#Comment$comment->id"; $upvoteLabel = $this->_('Like this comment'); @@ -235,14 +244,14 @@ public function renderVotes(Comment $comment) { // note that data-url attribute stores the href (rather than href) so that we can keep crawlers out of auto-following these links $out = ""; $out .= "$upvoteFormat"; - + if($this->options['useVotes'] == FieldtypeComments::useVotesAll) { $out .= "$downvoteFormat"; } - + $out .= " "; - - return $out; + + return $out; } /** @@ -281,8 +290,8 @@ public function renderCheckActions() { require_once(dirname(__FILE__) . '/CommentNotifications.php'); $no = new CommentNotifications($this->page, $this->field); $info = $no->checkActions(); - if($info['valid']) { - $url = $this->page->url . '?'; + if($info['valid']) { + $url = $this->page->url . '?'; if($info['commentID']) $url .= "comment_id=$info[commentID]&"; $url .= "comment_success=" . ($info['success'] ? '2' : '3'); $this->wire('session')->set('CommentApprovalMessage', $info['message']); diff --git a/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module b/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module index 7a0b8d49..65687590 100644 --- a/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module +++ b/wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module @@ -3,31 +3,31 @@ /** * ProcessWire Comments Fieldtype * - * A field that stores user posted comments for a single Page. + * A field that stores user posted comments for a single Page. * - * For documentation about the fields used in this class, please see: + * For documentation about the fields used in this class, please see: * /wire/core/Fieldtype.php * /wire/core/FieldtypeMulti.php - * - * ProcessWire 2.x - * Copyright (C) 2014 by Ryan Cramer + * + * ProcessWire 2.x + * Copyright (C) 2014 by Ryan Cramer * Licensed under GNU/GPL v2, see LICENSE.TXT - * + * * http://processwire.com * */ -$dirname = dirname(__FILE__); +$dirname = dirname(__FILE__); -require_once($dirname . "/Comment.php"); -require_once($dirname . "/CommentArray.php"); -require_once($dirname . "/CommentList.php"); -require_once($dirname . "/CommentForm.php"); +require_once($dirname . "/Comment.php"); +require_once($dirname . "/CommentArray.php"); +require_once($dirname . "/CommentList.php"); +require_once($dirname . "/CommentForm.php"); /** * ProcessWire Comments Fieldtype * - * A field that stores user posted comments for a single Page. + * A field that stores user posted comments for a single Page. * */ class FieldtypeComments extends FieldtypeMulti { @@ -42,7 +42,7 @@ class FieldtypeComments extends FieldtypeMulti { * Constant that designates that ALL comments require moderation * */ - const moderateAll = 1; + const moderateAll = 1; /** * Constant that designates that all comments require moderation, except those posted by users that have an approved comment @@ -54,11 +54,11 @@ class FieldtypeComments extends FieldtypeMulti { * Time period (in seconds) after which the same IP address may vote again on the same comment * */ - const votesMaxAge = 3600; - + const votesMaxAge = 3600; + const useVotesNo = 0; - const useVotesUp = 1; - const useVotesAll = 2; + const useVotesUp = 1; + const useVotesAll = 2; public static function getModuleInfo() { @@ -69,36 +69,36 @@ class FieldtypeComments extends FieldtypeMulti { 'installs' => array('InputfieldCommentsAdmin'), ); } - + public function __construct() { if($this->wire('config')->ajax) { $this->addHookBefore('Page::render', $this, 'checkVoteAction'); } } - + public function getBlankValue(Page $page, Field $field) { - $commentArray = new CommentArray(); - $commentArray->setPage($page); - $commentArray->setField($field); + $commentArray = new CommentArray(); + $commentArray->setPage($page); + $commentArray->setField($field); $commentArray->setTrackChanges(true); - return $commentArray; + return $commentArray; } public function sanitizeValue(Page $page, Field $field, $value) { - if($value instanceof CommentArray) return $value; - $commentArray = $this->wire('pages')->get($field->name); - if(!$value) return $commentArray; - if($value instanceof Comment) return $commentArray->add($value); + if($value instanceof CommentArray) return $value; + $commentArray = $this->wire('pages')->get($field->name); + if(!$value) return $commentArray; + if($value instanceof Comment) return $commentArray->add($value); if(!is_array($value)) $value = array($value); - foreach($value as $comment) $commentArray->add($comment); - return $commentArray; + foreach($value as $comment) $commentArray->add($comment); + return $commentArray; } public function getInputfield(Page $page, Field $field) { - $inputfield = $this->modules->get('InputfieldCommentsAdmin'); - if(!$inputfield) return null; + $inputfield = $this->modules->get('InputfieldCommentsAdmin'); + if(!$inputfield) return null; $inputfield->class = $this->className(); - return $inputfield; + return $inputfield; } /** @@ -107,12 +107,12 @@ class FieldtypeComments extends FieldtypeMulti { */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { if($subfield == 'text') $subfield = 'data'; - if(empty($subfield) || $subfield === 'data') { + if(empty($subfield) || $subfield === 'data') { $ft = new DatabaseQuerySelectFulltext($query); $ft->match($table, $subfield, $operator, $value); return $query; - } - return parent::getMatchQuery($query, $table, $subfield, $operator, $value); + } + return parent::getMatchQuery($query, $table, $subfield, $operator, $value); } @@ -127,41 +127,41 @@ class FieldtypeComments extends FieldtypeMulti { */ public function ___wakeupValue(Page $page, Field $field, $value) { - if($value instanceof CommentArray) return $value; - $commentArray = $this->getBlankValue($page, $field); - if(empty($value)) return $commentArray; - + if($value instanceof CommentArray) return $value; + $commentArray = $this->getBlankValue($page, $field); + if(empty($value)) return $commentArray; + $editable = $page->editable(); - if(!is_array($value)) $value = array($value); - + if(!is_array($value)) $value = array($value); + foreach($value as $sort => $item) { - - if(!is_array($item)) continue; - + + if(!is_array($item)) continue; + // don't load non-approved comments if the user can't edit them - if(!$editable && $item['status'] < Comment::statusApproved) continue; - + if(!$editable && $item['status'] < Comment::statusApproved) continue; + $comment = new Comment(); $comment->setPage($page); - $comment->setField($field); + $comment->setField($field); foreach($item as $key => $val) { if($key == 'data') $key = 'text'; - $comment->set($key, $val); + $comment->set($key, $val); } - $comment->resetTrackChanges(true); + $comment->resetTrackChanges(true); $commentArray->add($comment); - $comment->setIsLoaded(true); + $comment->setIsLoaded(true); } - - if($field->sortNewest) $commentArray->sort("-created"); - $commentArray->resetTrackChanges(true); - - return $commentArray; + + if($field->sortNewest) $commentArray->sort("-created"); + $commentArray->resetTrackChanges(true); + + return $commentArray; } /** - * Given an 'awake' value, as set by wakeupValue, convert the value back to a basic type for storage in DB. - * + * Given an 'awake' value, as set by wakeupValue, convert the value back to a basic type for storage in DB. + * * @param Page $page * @param Field $field * @param string|int|array|object $value @@ -171,7 +171,7 @@ class FieldtypeComments extends FieldtypeMulti { public function ___sleepValue(Page $page, Field $field, $value) { $sleepValue = array(); - if(!$value instanceof CommentArray) return $sleepValue; + if(!$value instanceof CommentArray) return $sleepValue; $schemaVersion = $field->schemaVersion; foreach($value as $comment) { @@ -181,19 +181,19 @@ class FieldtypeComments extends FieldtypeMulti { } else { $this->checkNewComment($page, $field, $comment); } - + $a = array( - 'id' => $comment->id, - 'status' => $comment->status, - 'data' => $comment->text, - 'cite' => $comment->cite, - 'email' => $comment->email, - 'created' => $comment->created, - 'created_users_id' => $comment->created_users_id, - 'ip' => $comment->ip, - 'user_agent' => $comment->user_agent, - ); - + 'id' => $comment->id, + 'status' => $comment->status, + 'data' => $comment->text, + 'cite' => $comment->cite, + 'email' => $comment->email, + 'created' => $comment->created, + 'created_users_id' => $comment->created_users_id, + 'ip' => $comment->ip, + 'user_agent' => $comment->user_agent, + ); + if($schemaVersion > 0) $a['website'] = $comment->website; if($schemaVersion > 1) { @@ -201,7 +201,7 @@ class FieldtypeComments extends FieldtypeMulti { $a['flags'] = (int) $comment->flags; } if($schemaVersion > 2) { - $a['code'] = $comment->code; + $a['code'] = $comment->code; } if($schemaVersion > 3) { $a['subcode'] = $comment->subcode; @@ -214,7 +214,7 @@ class FieldtypeComments extends FieldtypeMulti { $sleepValue[] = $a; } - + return $sleepValue; } @@ -222,7 +222,7 @@ class FieldtypeComments extends FieldtypeMulti { * Review an existing comment for changes to the status * * If the status was changed, check if Akismet made an error and send it to them if they did - * + * * @param Page $page * @param Field $field * @param Comment $comment @@ -230,39 +230,39 @@ class FieldtypeComments extends FieldtypeMulti { */ protected function checkExistingComment(Page $page, Field $field, Comment $comment) { - $submitSpam = false; - $submitHam = false; + $submitSpam = false; + $submitHam = false; if($comment->prevStatus === Comment::statusSpam && $comment->status === Comment::statusApproved) { $submitHam = true; // identified a false positive - $this->commentApproved($page, $field, $comment, 'Existing comment changed from spam to approved'); + $this->commentApproved($page, $field, $comment, 'Existing comment changed from spam to approved'); } else if($comment->status === Comment::statusSpam && $comment->prevStatus === Comment::statusApproved) { $submitSpam = true; // a missed spam - $this->commentUnapproved($page, $field, $comment); - + $this->commentUnapproved($page, $field, $comment); + } else if($comment->prevStatus === Comment::statusPending && $comment->status === Comment::statusApproved) { - $this->commentApproved($page, $field, $comment, 'Existing comment changed from pending to approved'); - + $this->commentApproved($page, $field, $comment, 'Existing comment changed from pending to approved'); + } else if($comment->status === Comment::statusPending && $comment->prevStatus === Comment::statusApproved) { - $this->commentUnapproved($page, $field, $comment); + $this->commentUnapproved($page, $field, $comment); } if($field->useAkismet && $comment->ip && $comment->user_agent && ($submitHam || $submitSpam)) { - $akismet = $this->modules->get("CommentFilterAkismet"); - $akismet->setComment($comment); + $akismet = $this->modules->get("CommentFilterAkismet"); + $akismet->setComment($comment); if($submitHam) $akismet->submitHam(); else if($submitSpam) $akismet->submitSpam(); } - + $this->checkCommentCodes($comment); } /** * Assign comment code and subcode as needed - * + * * @param Comment $comment - * + * */ protected function checkCommentCodes(Comment $comment) { // assign code and subcode @@ -286,73 +286,73 @@ class FieldtypeComments extends FieldtypeMulti { /** * If comment is new, it sets the status based on whether it's spam, and notifies any people that need to be notified - * + * * @param Page $page * @param Field $field * @param Comment $comment * - */ + */ protected function checkNewComment(Page $page, Field $field, Comment $comment) { if($comment->id) return; - + $this->checkCommentCodes($comment); - + if($field->useAkismet) { - $akismet = $this->modules->get('CommentFilterAkismet'); - $akismet->setComment($comment); + $akismet = $this->modules->get('CommentFilterAkismet'); + $akismet->setComment($comment); $akismet->checkSpam(); // automatically sets status if spam } else { - $comment->status = Comment::statusPending; + $comment->status = Comment::statusPending; } if($comment->status != Comment::statusSpam) { if($field->moderate == self::moderateNone) { - $comment->status = Comment::statusApproved; - $this->commentApproved($page, $field, $comment, 'New comment approved / moderation is off'); + $comment->status = Comment::statusApproved; + $this->commentApproved($page, $field, $comment, 'New comment approved / moderation is off'); } else if($field->moderate == self::moderateNew && $comment->email) { $database = $this->wire('database'); - $table = $database->escapeTable($field->table); - $query = $database->prepare("SELECT count(*) FROM `$table` WHERE status=:status AND email=:email"); + $table = $database->escapeTable($field->table); + $query = $database->prepare("SELECT count(*) FROM `$table` WHERE status=:status AND email=:email"); $query->bindValue(":status", Comment::statusApproved, PDO::PARAM_INT); - $query->bindValue(":email", $comment->email); + $query->bindValue(":email", $comment->email); $query->execute(); $numApproved = (int) $query->fetchColumn(); - + if($numApproved > 0) { $comment->status = Comment::statusApproved; - $cite = $this->wire('sanitizer')->name($comment->cite); - $this->commentApproved($page, $field, $comment, "New comment auto-approved because user '$cite' has other approved comments"); + $cite = $this->wire('sanitizer')->name($comment->cite); + $this->commentApproved($page, $field, $comment, "New comment auto-approved because user '$cite' has other approved comments"); } } } - require_once(dirname(__FILE__) . '/CommentNotifications.php'); - $no = new CommentNotifications($page, $field); - $no->sendAdminNotificationEmail($comment); + require_once(dirname(__FILE__) . '/CommentNotifications.php'); + $no = new CommentNotifications($page, $field); + $no->sendAdminNotificationEmail($comment); $this->commentMaintenance($field); } /** * Delete spam that is older than $field->deleteSpamDays - * + * * @param Field $field * */ protected function commentMaintenance(Field $field) { - + $database = $this->wire('database'); $table = $database->escapeTable($field->table); - + // delete old spam $expiredTime = time() - (86400 * $field->deleteSpamDays); $query = $database->prepare("DELETE FROM `$table` WHERE status=:status AND created < :expiredTime"); $query->bindValue(":status", Comment::statusSpam, PDO::PARAM_INT); - $query->bindValue(":expiredTime", $expiredTime); + $query->bindValue(":expiredTime", $expiredTime); $query->execute(); - + // delete upvote/downvote IP address records $expiredTime = time() - self::votesMaxAge; $query = $database->prepare("DELETE FROM `{$table}_votes` WHERE created < :expiredTime"); @@ -361,7 +361,7 @@ class FieldtypeComments extends FieldtypeMulti { // we use a try/catch here in case the votes table doesn't yet exist $query->execute(); } catch(Exception $e) { - $this->error($e->getMessage(), Notice::log); + $this->error($e->getMessage(), Notice::log); } } @@ -371,7 +371,7 @@ class FieldtypeComments extends FieldtypeMulti { */ public function getDatabaseSchema(Field $field) { - $websiteSchema = "varchar(255) NOT NULL default ''"; + $websiteSchema = "varchar(255) NOT NULL default ''"; $parentSchema = "int unsigned NOT NULL default 0"; $flagSchema = "int unsigned NOT NULL default 0"; $codeSchema = "varchar(128) default NULL"; @@ -380,41 +380,41 @@ class FieldtypeComments extends FieldtypeMulti { $subcodeIndexSchema = "INDEX `subcode` (`subcode`)"; $upvoteSchema = "int unsigned NOT NULL default 0"; $downvoteSchema = "int unsigned NOT NULL default 0"; - + $schemaVersion = (int) $field->schemaVersion; if(!$schemaVersion) { - // add website field for PW 2.3+ + // add website field for PW 2.3+ $database = $this->wire('database'); $table = $database->escapeTable($field->getTable()); - try { + try { $database->query("ALTER TABLE `$table` ADD website $websiteSchema"); $schemaVersion = 1; } catch(Exception $e) { } } - + if($schemaVersion < 2) { // add parent_id and flags columns $database = $this->wire('database'); $table = $database->escapeTable($field->getTable()); - try { + try { $database->query("ALTER TABLE `$table` ADD parent_id $parentSchema"); $database->query("ALTER TABLE `$table` ADD flags $flagSchema"); $schemaVersion = 2; } catch(Exception $e) { } } - + if($schemaVersion < 3) { // add code column (admin code) $database = $this->wire('database'); $table = $database->escapeTable($field->getTable()); try { $database->query("ALTER TABLE `$table` ADD `code` $codeSchema"); - $database->query("ALTER TABLE `$table` ADD $codeIndexSchema"); + $database->query("ALTER TABLE `$table` ADD $codeIndexSchema"); $schemaVersion = 3; } catch(Exception $e) { } } - + if($schemaVersion < 4) { // add subcode column (subscriber code) $database = $this->wire('database'); @@ -425,9 +425,9 @@ class FieldtypeComments extends FieldtypeMulti { $schemaVersion = 4; } catch(Exception $e) { } } - + if($schemaVersion < 5) { - // add upvote/downvote columns + // add upvote/downvote columns $database = $this->wire('database'); $table = $database->escapeTable($field->getTable()); $parentSchema = parent::getDatabaseSchema($field); @@ -435,15 +435,15 @@ class FieldtypeComments extends FieldtypeMulti { $sql = " CREATE TABLE `{$table}_votes` ( `comment_id` int unsigned NOT NULL, - `vote` tinyint NOT NULL, - `created` TIMESTAMP NOT NULL, - `ip` VARCHAR(15) NOT NULL default '', - `user_id` int unsigned NOT NULL default 0, - PRIMARY KEY (`comment_id`, `ip`, `vote`), + `vote` tinyint NOT NULL, + `created` TIMESTAMP NOT NULL, + `ip` VARCHAR(15) NOT NULL default '', + `user_id` int unsigned NOT NULL default 0, + PRIMARY KEY (`comment_id`, `ip`, `vote`), INDEX `created` (`created`) ) " . $parentSchema['xtra']['append']; // engine and charset $database->exec($sql); - $createdVotesTable = true; + $createdVotesTable = true; } catch(Exception $e) { $createdVotesTable = $e->getCode() == '42S01'; // 42S01=table already exists (which we consider success too) if(!$createdVotesTable) $this->error($e->getMessage(), Notice::log); @@ -452,8 +452,8 @@ class FieldtypeComments extends FieldtypeMulti { $database->query("ALTER TABLE `$table` ADD `upvotes` $upvoteSchema"); $database->query("ALTER TABLE `$table` ADD `downvotes` $downvoteSchema"); $schemaVersion = 5; - } catch(Exception $e) { - $this->error($e->getMessage(), Notice::log); + } catch(Exception $e) { + $this->error($e->getMessage(), Notice::log); } } @@ -463,15 +463,15 @@ class FieldtypeComments extends FieldtypeMulti { $field->save(); } - $schema = parent::getDatabaseSchema($field); + $schema = parent::getDatabaseSchema($field); $schema['id'] = "int unsigned NOT NULL auto_increment"; $schema['status'] = "tinyint(3) NOT NULL default '0'"; - $schema['cite'] = "varchar(128) NOT NULL default ''"; + $schema['cite'] = "varchar(128) NOT NULL default ''"; $schema['email'] = "varchar(255) NOT NULL default ''"; - $schema['data'] = "text NOT NULL"; + $schema['data'] = "text NOT NULL"; $schema['sort'] = "int unsigned NOT NULL"; - $schema['created'] = "int unsigned NOT NULL"; + $schema['created'] = "int unsigned NOT NULL"; $schema['created_users_id'] = "int unsigned NOT NULL"; $schema['ip'] = "varchar(15) NOT NULL default ''"; $schema['user_agent'] = "varchar(255) NOT NULL default ''"; @@ -495,10 +495,10 @@ class FieldtypeComments extends FieldtypeMulti { $schema['downvotes'] = $downvoteSchema; } - $schema['keys']['primary'] = "PRIMARY KEY (`id`)"; + $schema['keys']['primary'] = "PRIMARY KEY (`id`)"; $schema['keys']['pages_id_sort'] = "KEY `pages_id_sort` (`pages_id`, `sort`)"; $schema['keys']['status'] = "KEY `status` (`status`, `email`)"; - $schema['keys']['pages_id'] = "KEY `pages_id` (`pages_id`,`status`,`created`)"; + $schema['keys']['pages_id'] = "KEY `pages_id` (`pages_id`,`status`,`created`)"; $schema['keys']['created'] = "KEY `created` (`created`, `status`)"; $schema['keys']['data'] = "FULLTEXT KEY `data` (`data`)"; @@ -522,15 +522,15 @@ class FieldtypeComments extends FieldtypeMulti { $table = $database->escapeTable($field->table); if(!$allItems) return false; - if(!$allItems->isChanged() && !$page->isChanged($field->name)) return true; + if(!$allItems->isChanged() && !$page->isChanged($field->name)) return true; $itemsRemoved = $allItems->getItemsRemoved(); if(count($itemsRemoved)) { foreach($itemsRemoved as $item) { - if(!$item->id) continue; - $this->deleteComment($page, $field, $item, 'deleted from savePageField()'); + if(!$item->id) continue; + $this->deleteComment($page, $field, $item, 'deleted from savePageField()'); /* - $query = $database->prepare("DELETE FROM `$table` WHERE id=:item_id AND pages_id=:pages_id"); + $query = $database->prepare("DELETE FROM `$table` WHERE id=:item_id AND pages_id=:pages_id"); $query->bindValue(":item_id", $item->id, PDO::PARAM_INT); $query->bindValue(":pages_id", $page->id, PDO::PARAM_INT); $query->execute(); @@ -538,43 +538,43 @@ class FieldtypeComments extends FieldtypeMulti { } } - $maxSort = 0; + $maxSort = 0; $items = $allItems->makeNew(); foreach($allItems as $item) { - if($item->isChanged() || !$item->id) $items->add($item); - if($item->sort > $maxSort) $maxSort = $item->sort; + if($item->isChanged() || !$item->id) $items->add($item); + if($item->sort > $maxSort) $maxSort = $item->sort; } - if(!count($items)) return true; + if(!count($items)) return true; + + $values = $this->sleepValue($page, $field, $items); + $value = reset($values); + $keys = is_array($value) ? array_keys($value) : array('data'); - $values = $this->sleepValue($page, $field, $items); - $value = reset($values); - $keys = is_array($value) ? array_keys($value) : array('data'); - // cycle through the values, executing an update query for each foreach($values as $commentKey => $value) { - + $binds = array(); $sql = $value['id'] ? "UPDATE " : "INSERT INTO "; //$sql .= "`{$table}` SET pages_id=" . ((int) $page->id) . ", "; - $sql .= "`{$table}` SET pages_id=:pages_id, "; - $binds['pages_id'] = (int) $page->id; + $sql .= "`{$table}` SET pages_id=:pages_id, "; + $binds['pages_id'] = (int) $page->id; // if the value is not an associative array, then force it to be one - if(!is_array($value)) $value = array('data' => $value); + if(!is_array($value)) $value = array('data' => $value); // cycle through the keys, which represent DB fields (i.e. data, description, etc.) and generate the update query foreach($keys as $key) { - if($key == 'id') continue; - if($key == 'sort' && !$value['id']) continue; + if($key == 'id') continue; + if($key == 'sort' && !$value['id']) continue; $v = $value[$key]; - $col = $database->escapeCol($key); + $col = $database->escapeCol($key); if(is_null($v) && ($key == 'code' || $key == 'subcode')) { // currently 'code' and 'subcode' are the only column that allows null $sql .= "$col=NULL, "; } else { $sql .= "$col=:$col, "; - $binds[$col] = $v; + $binds[$col] = $v; } } @@ -582,11 +582,11 @@ class FieldtypeComments extends FieldtypeMulti { $sql = rtrim($sql, ', ') . " WHERE id=:id"; // . (int) $value['id']; $binds['id'] = (int) $value['id']; } else { - $sql .= "sort=:sort"; - $binds['sort'] = ++$maxSort; + $sql .= "sort=:sort"; + $binds['sort'] = ++$maxSort; } - $query = $database->prepare($sql); - foreach($binds as $k => $v) $query->bindValue(":$k", $v); + $query = $database->prepare($sql); + foreach($binds as $k => $v) $query->bindValue(":$k", $v); try { $result = $query->execute(); if(!$value['id']) { @@ -597,12 +597,12 @@ class FieldtypeComments extends FieldtypeMulti { } } } catch(Exception $e) { - $result = false; + $result = false; } if(!$result) $this->error("Error saving item $value[id] in savePageField", Notice::log); - } - - return true; + } + + return true; } /** @@ -617,19 +617,19 @@ class FieldtypeComments extends FieldtypeMulti { $fieldset = $this->wire('modules')->get('InputfieldFieldset'); $fieldset->label = $this->_('Behavior'); $fieldset->icon = 'comment-o'; - $inputfields->add($fieldset); + $inputfields->add($fieldset); $name = 'moderate'; - $f = $this->wire('modules')->get('InputfieldRadios'); - $f->attr('name', $name); - $f->addOption(self::moderateNone, $this->_('None - Comments posted immediately')); - $f->addOption(self::moderateAll, $this->_('All - All comments must be approved by user with page edit access')); + $f = $this->wire('modules')->get('InputfieldRadios'); + $f->attr('name', $name); + $f->addOption(self::moderateNone, $this->_('None - Comments posted immediately')); + $f->addOption(self::moderateAll, $this->_('All - All comments must be approved by user with page edit access')); $f->addOption(self::moderateNew, $this->_('Only New - Only comments from users without prior approved comments require approval')); - $f->attr('value', (int) $field->$name); + $f->attr('value', (int) $field->$name); $f->label = $this->_('Comment moderation'); - $f->description = $this->_('This determines when a newly posted comment will appear on your site.'); + $f->description = $this->_('This determines when a newly posted comment will appear on your site.'); $fieldset->append($f); - + $name = 'redirectAfterPost'; $f = $this->wire('modules')->get('InputfieldCheckbox'); $f->attr('name', $name); @@ -658,42 +658,42 @@ class FieldtypeComments extends FieldtypeMulti { $f->addOption(self::notificationNone, $this->_('Do not send notifications')); $f->addOption(self::notificationEmail, $this->_('Send notifications to specific email address')); $f->addOption(self::notificationCreated, $this->_('Send notifications to user that created the page')); - $f->addOption(self::notificationUser, $this->_('Send notifications to specific user')); + $f->addOption(self::notificationUser, $this->_('Send notifications to specific user')); $f->attr('value', $field->$name); $inputfields->append($f); */ - + // ---------------------------- - - $fieldset = $this->wire('modules')->get('InputfieldFieldset'); - $fieldset->label = $this->_('Notifications'); + + $fieldset = $this->wire('modules')->get('InputfieldFieldset'); + $fieldset->label = $this->_('Notifications'); $fieldset->icon = 'bell-o'; - $inputfields->add($fieldset); + $inputfields->add($fieldset); $name = 'notificationEmail'; - $f = $this->wire('modules')->get('InputfieldText'); - $f->attr('name', $name); - $f->attr('value', $field->$name); + $f = $this->wire('modules')->get('InputfieldText'); + $f->attr('name', $name); + $f->attr('value', $field->$name); $f->label = $this->_('Admin notification email'); - $f->description = $this->_('E-mail address to be notified when a new comment is posted. Separate multiple email addresses with commas or spaces.') . ' '; + $f->description = $this->_('E-mail address to be notified when a new comment is posted. Separate multiple email addresses with commas or spaces.') . ' '; $f->description .= $this->_('Users receiving this email will have the ability to approve or deny posts directly from links in the email.'); - $f->notes = + $f->notes = $this->_('In addition to (or instead of) email addresses, you may also use one or more of the following:') . "\n" . - $this->_('1. Enter **user:karen** to email a specific user, replacing "karen" with the name of the actual user.') . "\n" . - $this->_('2. Enter **field:email** to pull the email from a field on the page, replacing "email" with name of field containing email address.') . "\n" . - $this->_('3. Enter **123:email** to pull the email from an given page ID and field name, replacing "123" with the page ID and "email" with name of field containing email address.') . "\n" . + $this->_('1. Enter **user:karen** to email a specific user, replacing "karen" with the name of the actual user.') . "\n" . + $this->_('2. Enter **field:email** to pull the email from a field on the page, replacing "email" with name of field containing email address.') . "\n" . + $this->_('3. Enter **123:email** to pull the email from an given page ID and field name, replacing "123" with the page ID and "email" with name of field containing email address.') . "\n" . $this->_('4. Enter **/path/to/page:email** to pull the email from an given page path and field name, replacing "/path/to/page" with the page path and "email" with name of field containing email address.'); $fieldset->append($f); - + $name = 'fromEmail'; $f = $this->wire('modules')->get('InputfieldEmail'); $f->attr('name', $name); $f->attr('value', $field->$name); $f->label = $this->_('Notifications from email'); $f->description = $this->_('Optional e-mail address that notifications will appear from. Leave blank to use the default server email.'); - $f->columnWidth = 50; + $f->columnWidth = 50; $fieldset->append($f); - + $name = 'notifySpam'; $f = $this->wire('modules')->get('InputfieldCheckbox'); $f->attr('name', $name); @@ -716,12 +716,12 @@ class FieldtypeComments extends FieldtypeMulti { $fieldset->append($f); // --------------------------------------- - + $fieldset = $this->wire('modules')->get('InputfieldFieldset'); - $fieldset->label = $this->_('Spam'); + $fieldset->label = $this->_('Spam'); $fieldset->icon = 'fire-extinguisher'; $inputfields->add($fieldset); - + $name = 'useAkismet'; $f = $this->wire('modules')->get('InputfieldCheckbox'); $f->attr('name', $name); @@ -740,16 +740,16 @@ class FieldtypeComments extends FieldtypeMulti { $f->attr('value', $value); $f->label = $this->_('Number of days after which to delete spam'); $f->description = $this->_('After the number of days indicated, spam will be automatically deleted.'); - $f->columnWidth = 50; + $f->columnWidth = 50; $fieldset->append($f); - + // --------------------------------------- $fieldset = $this->wire('modules')->get('InputfieldFieldset'); $fieldset->label = $this->_('Output'); $fieldset->icon = 'comments-o'; $inputfields->add($fieldset); - + $name = 'depth'; $f = $this->wire('modules')->get('InputfieldInteger'); $f->attr('name', $name); @@ -760,61 +760,96 @@ class FieldtypeComments extends FieldtypeMulti { $fieldset->append($f); - $name = 'sortNewest'; - $f = $this->wire('modules')->get('InputfieldCheckbox'); - $f->attr('name', $name); - $f->attr('value', 1); - $f->attr('checked', $field->$name ? 'checked' : ''); + $name = 'sortNewest'; + $f = $this->wire('modules')->get('InputfieldCheckbox'); + $f->attr('name', $name); + $f->attr('value', 1); + $f->attr('checked', $field->$name ? 'checked' : ''); $f->label = $this->_('Sort newest to oldest?'); $f->description = $this->_('By default, comments will sort chronologically (oldest to newest). To reverse that behavior check this box.'); $f->columnWidth = 50; $fieldset->append($f); - - $name = 'useWebsite'; - $f = $this->wire('modules')->get('InputfieldCheckbox'); - $f->attr('name', $name); - $f->attr('value', 1); - $f->attr('checked', $field->$name ? 'checked' : ''); + + $name = 'useWebsite'; + $f = $this->wire('modules')->get('InputfieldCheckbox'); + $f->attr('name', $name); + $f->attr('value', 1); + $f->attr('checked', $field->$name ? 'checked' : ''); $f->label = $this->_('Use website field in comment form?'); $f->description = $this->_('When checked, the comment submission form will also include a website field.'); $f->columnWidth = 50; $fieldset->append($f); - + $name = 'dateFormat'; $f = $this->wire('modules')->get('InputfieldText'); $f->attr('name', $name); $f->attr('value', $field->dateFormat ? $field->dateFormat : 'relative'); $f->label = $this->_('Date/time format (for comment list)'); $f->description = $this->_('Enter the date/time format you want the default comment list output to use. May be a PHP [date](http://php.net/manual/en/function.date.php) or [strftime](http://php.net/manual/en/function.strftime.php) format. May also be "relative" for relative date format.'); // dateFormat description - $f->columnWidth = 50; + $f->columnWidth = 50; $fieldset->append($f); - + $name = 'useVotes'; $f = $this->wire('modules')->get('InputfieldRadios'); $f->attr('name', $name); $f->label = $this->_('Allow comment voting?'); - $f->description = $this->_('Comment voting enables visitors to upvote and/or downvote comments. Vote counts are displayed alongside each comment. Only one upvote and/or downvote is allowed per comment, per IP address, per hour.'); - $f->addOption(0, $this->_('Voting off')); + $f->description = $this->_('Comment voting enables visitors to upvote and/or downvote comments. Vote counts are displayed alongside each comment. Only one upvote and/or downvote is allowed per comment, per IP address, per hour.'); + $f->addOption(0, $this->_('Voting off')); $f->addOption(1, $this->_('Allow upvoting')); $f->addOption(2, $this->_('Allow upvoting and downvoting')); $f->attr('value', (int) $field->$name); - $fieldset->append($f); - - $name = 'useGravatar'; - $f = $this->wire('modules')->get('InputfieldRadios'); - $f->attr('name', $name); - $f->addOption('', $disabledLabel); + $fieldset->append($f); + + $name = 'yourNameFields'; + $f = $this->wire('modules')->get('InputfieldAsmSelect'); + $f->attr('name', $name); + $f->label = $this->_('Your Name fields?'); + $f->description = $this->_('Fields for automatically populating Your Name in the comments form. Leave blank for default use of "name" field.'); + $f->notes = $this->_('eg first_name and last_name. Be sure to put them in the correct order.'); + $f->addOption('', $disabledLabel); + foreach($this->fields as $nameField) { + if($nameField->type instanceof FieldtypeText) $f->addOption($nameField->id, $nameField->name); + } + $f->attr('value', $field->$name); + $fieldset->append($f); + + $name = 'hideUserFieldsLoggedIn'; + $f = $this->wire('modules')->get('InputfieldCheckbox'); + $f->attr('name', $name); + $f->label = $this->_('Hide user fields if logged in?'); + $f->description = $this->_('If logged in, the Your Name and Your E-mail fields will be hidden.'); + $f->attr('value', 1); + $f->attr('checked', $field->$name ? 'checked' : ''); + $fieldset->append($f); + + $name = 'useImageField'; + $f = $this->wire('modules')->get('InputfieldSelect'); + $f->attr('name', $name); + $f->addOption('', $disabledLabel); + foreach($this->fields as $imageField) { + if($imageField->type instanceof FieldtypeImage) $f->addOption($imageField->id, $imageField->name); + } + $f->attr('value', $field->useImageField); + $f->label = $this->_('Use Image Field?'); + $f->description = $this->_('The image from the selected field will be used for the avatar.'); + $f->notes = $this->_('If both this and Use Gravater are enabled, this will take priority if an image is available, but will fallback to the Gravatar if the image field is blank.'); + $fieldset->append($f); + + $name = 'useGravatar'; + $f = $this->wire('modules')->get('InputfieldRadios'); + $f->attr('name', $name); + $f->addOption('', $disabledLabel); $f->addOption('g', $this->_('G: Suitable for display on all websites with any audience type.')); $f->addOption('pg', $this->_('PG: May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence.')); $f->addOption('r', $this->_('R: May contain such things as harsh profanity, intense violence, nudity, or hard drug use.')); $f->addOption('x', $this->_('X: May contain hardcore sexual imagery or extremely disturbing violence.')); - $f->attr('value', $field->useGravatar); + $f->attr('value', $field->useGravatar); $f->label = $this->_('Use Gravatar?'); $f->description = $this->_('This service provides an avatar image with each comment (unique to the email address). To enable, select the maximum gravatar rating. These are the same as movie ratings, where G is the most family friendly and X is not.'); $f->notes = $this->_('Rating descriptions provided by [Gravatar](https://en.gravatar.com/site/implement/images/).'); - $fieldset->append($f); + $fieldset->append($f); - // @todo + // @todo /* $textformatters = $this->wire('modules')->find("className^=Textformatter"); if(count($textformatters)) { @@ -822,42 +857,42 @@ class FieldtypeComments extends FieldtypeMulti { $f->attr('name', 'textformatters'); $f->label = $this->_('Text Formatters'); $f->description = $this->_('Optionally select one or more text formatters to be applied to the comment text, in the selected order. If you do not select any then the text will be entity encoded, have newlines converted to
    tags, and be output in a

    tag.'); - $f->notes = $this->_('Warning: only select text formatters that are known to be safe with anonymous user input, like Entity Encoder (core) or Textile Restricted (3rd party). If you are not sure, then do not select anything here, as making the wrong choice can be a security problem.'); + $f->notes = $this->_('Warning: only select text formatters that are known to be safe with anonymous user input, like Entity Encoder (core) or Textile Restricted (3rd party). If you are not sure, then do not select anything here, as making the wrong choice can be a security problem.'); foreach($textformatters as $textformatter) { - $info = $this->wire('modules')->getModuleInfo($textformatter); + $info = $this->wire('modules')->getModuleInfo($textformatter); $f->addOption($textformatter->className(), "$info[title]"); } $f->attr('value', is_array($field->textformatters) ? $field->textformatters : array()); $inputfields->append($f); } */ - + // ----------------------------- - + $fieldset = $this->wire('modules')->get('InputfieldFieldset'); - $fieldset->label = $this->_('Implementation'); + $fieldset->label = $this->_('Implementation'); $fieldset->icon = 'file-code-o'; - $fieldset->description = $this->_('This section is here to help you get started with outputting comments on the front-end of your site. Everything here is optional.'); - $fieldset->notes = $this->_('If using a cache for output, configure it to bypass the cache when the GET variable "comment_success" is present.'); - $inputfields->add($fieldset); - - $f = $this->wire('modules')->get('InputfieldMarkup'); - $f->label = $this->_('PHP code to output comments'); - $f->value = + $fieldset->description = $this->_('This section is here to help you get started with outputting comments on the front-end of your site. Everything here is optional.'); + $fieldset->notes = $this->_('If using a cache for output, configure it to bypass the cache when the GET variable "comment_success" is present.'); + $inputfields->add($fieldset); + + $f = $this->wire('modules')->get('InputfieldMarkup'); + $f->label = $this->_('PHP code to output comments'); + $f->value = "

    Please copy and paste the following into your template file(s) where you would like the comments to be output:

    " . - "
    <?php echo \$page->{$field->name}->renderAll(); ?>
    " . + "
    <?php echo \$page->{$field->name}->renderAll(); ?>
    " . "

    For more options please see the comments documentation page.

    "; - $fieldset->add($f); - - $f = $this->wire('modules')->get('InputfieldMarkup'); - $f->label = $this->_('CSS for front-end comments output'); - $f->value = + $fieldset->add($f); + + $f = $this->wire('modules')->get('InputfieldMarkup'); + $f->label = $this->_('CSS for front-end comments output'); + $f->value = "

    Please copy and paste the following into the document <head> of your site:

    " . - "
    <link rel='stylesheet' type='text/css' href='<?=\$config->urls->FieldtypeComments?>comments.css' />
    " . - "

    Or if you prefer, copy the comments.css file to your own location, " . + "

    <link rel='stylesheet' type='text/css' href='<?=\$config->urls->FieldtypeComments?>comments.css' />
    " . + "

    Or if you prefer, copy the comments.css file to your own location, " . "modify it as desired, and link to it in your document head as you do with your other css files.

    "; $fieldset->add($f); - + $f = $this->wire('modules')->get('InputfieldMarkup'); $f->label = $this->_('JS for front-end comments output'); $f->value = @@ -866,12 +901,12 @@ class FieldtypeComments extends FieldtypeMulti { "
    <script type='text/javascript' src='<?=\$config->urls->FieldtypeComments?>comments.js' />
    " . "

    Like with the comments.css file, feel free to copy and link to the comments.js file from your own " . "location if you prefer it.

    "; - $fieldset->add($f); - - $name = 'schemaVersion'; + $fieldset->add($f); + + $name = 'schemaVersion'; $f = $this->wire('modules')->get('InputfieldHidden'); $f->attr('name', $name); - $value = (int) $field->$name; + $value = (int) $field->$name; $f->attr('value', $value); $f->label = 'Schema Version'; $inputfields->append($f); @@ -982,7 +1017,7 @@ class FieldtypeComments extends FieldtypeMulti { while($row = $query->fetch(PDO::FETCH_ASSOC)) { $comment = new Comment(); - $comment->setField($field); + $comment->setField($field); foreach($row as $key => $value) { if($key == 'data') $key = 'text'; $comment->set($key, $value); @@ -990,10 +1025,10 @@ class FieldtypeComments extends FieldtypeMulti { $pageID = $row['pages_id']; if(isset($commentPages[$pageID])) { $page = $commentPages[$pageID]; - $comment->setPage($commentPages[$pageID]); + $comment->setPage($commentPages[$pageID]); } else { $page = $this->wire('pages')->get((int) $pageID); - $commentPages[$page->id] = $page; + $commentPages[$page->id] = $page; } $comment->resetTrackChanges(true); $comments->add($comment); @@ -1007,44 +1042,44 @@ class FieldtypeComments extends FieldtypeMulti { $comments->resetTrackChanges(); $comments->setTotal($total); - return $comments; + return $comments; } /** * Given a comment code or subcode, return the associated comment ID or 0 if it doesn't exist - * + * * @param $page * @param $field * @param $code * @return Comment|null - * + * */ public function getCommentByCode($page, $field, $code) { - if(!is_object($page)) $page = $this->wire('pages')->get((int) $page); + if(!is_object($page)) $page = $this->wire('pages')->get((int) $page); if(!$page->id) return null; - if(!trim($code)) return null; - if(!is_object($field)) $field = $this->wire('fields')->get($this->wire('sanitizer')->fieldName($field)); - if(!$field || !$field->type instanceof FieldtypeComments) return null; + if(!trim($code)) return null; + if(!is_object($field)) $field = $this->wire('fields')->get($this->wire('sanitizer')->fieldName($field)); + if(!$field || !$field->type instanceof FieldtypeComments) return null; $table = $field->getTable(); $col = strlen($code) > 100 ? 'code' : 'subcode'; $sql = "SELECT * FROM `$table` WHERE `$col`!='' AND `$col` IS NOT NULL AND `$col`=:code AND pages_id=:pageID"; $query = $this->wire('database')->prepare($sql); - $query->bindValue(':code', substr($code, 0, 128), PDO::PARAM_STR); - $query->bindValue(':pageID', $page->id, PDO::PARAM_INT); - if(!$query->execute()) return null; - if(!$query->rowCount()) return null; - $data = $query->fetch(PDO::FETCH_ASSOC); - return $this->makeComment($page, $field, $data); + $query->bindValue(':code', substr($code, 0, 128), PDO::PARAM_STR); + $query->bindValue(':pageID', $page->id, PDO::PARAM_INT); + if(!$query->execute()) return null; + if(!$query->rowCount()) return null; + $data = $query->fetch(PDO::FETCH_ASSOC); + return $this->makeComment($page, $field, $data); } /** * Get a comment by ID or NULL if not found - * + * * @param $page * @param $field * @param $id * @return Comment|null - * + * */ public function getCommentByID($page, $field, $id) { if(!is_object($page)) $page = $this->wire('pages')->get((int) $page); @@ -1055,22 +1090,22 @@ class FieldtypeComments extends FieldtypeMulti { $table = $field->getTable(); $sql = "SELECT * FROM `$table` WHERE id=:id AND pages_id=:pageID"; $query = $this->wire('database')->prepare($sql); - $query->bindValue(':id', (int) $id, PDO::PARAM_INT); + $query->bindValue(':id', (int) $id, PDO::PARAM_INT); $query->bindValue(':pageID', $page->id, PDO::PARAM_INT); if(!$query->execute()) return null; if(!$query->rowCount()) return null; $data = $query->fetch(PDO::FETCH_ASSOC); - return $this->makeComment($page, $field, $data); + return $this->makeComment($page, $field, $data); } /** * Given an array of data, convert it to a Comment object - * + * * @param $page * @param $field * @param array $data * @return Comment - * + * */ protected function makeComment($page, $field, array $data) { $comment = new Comment(); @@ -1081,33 +1116,33 @@ class FieldtypeComments extends FieldtypeMulti { $comment->set($key, $val); } $comment->resetTrackChanges(true); - $comment->setIsLoaded(true); - return $comment; + $comment->setIsLoaded(true); + return $comment; } /** * Update specific properties for a comment - * + * * @param Page $page * @param Field $field * @param Comment $comment * @param array $properties Associative array of properties to update * @return mixed * @throws WireException - * + * */ public function ___updateComment(Page $page, $field, Comment $comment, array $properties) { if(!count($properties)) return false; - $commentID = $comment->id; - if(!is_object($field)) $field = $this->wire('fields')->get($this->wire('sanitizer')->fieldName($field)); + $commentID = $comment->id; + if(!is_object($field)) $field = $this->wire('fields')->get($this->wire('sanitizer')->fieldName($field)); if(!$field instanceof Field) return false; $table = $this->wire('database')->escapeTable($field->getTable()); $sql = "UPDATE `$table` SET "; $values = array(); foreach($properties as $property => $value) { - $comment->set($property, $value); - $property = $this->wire('sanitizer')->fieldName($property); - $property = $this->wire('database')->escapeCol($property); + $comment->set($property, $value); + $property = $this->wire('sanitizer')->fieldName($property); + $property = $this->wire('database')->escapeCol($property); if($property == 'text') $property = 'data'; if(is_null($value) && ($property == 'code' || $property == 'subcode')) { $sql .= "`$property`=NULL, "; @@ -1117,23 +1152,23 @@ class FieldtypeComments extends FieldtypeMulti { } } $sql = rtrim($sql, ', ') . " WHERE id=:commentID AND pages_id=:pageID"; - $query = $this->wire('database')->prepare($sql); + $query = $this->wire('database')->prepare($sql); $query->bindValue(':commentID', $commentID, PDO::PARAM_INT); - $query->bindValue(':pageID', $page->id, PDO::PARAM_INT); + $query->bindValue(':pageID', $page->id, PDO::PARAM_INT); foreach($values as $property => $value) { - $query->bindValue(":$property", $value); + $query->bindValue(":$property", $value); } $this->wire('pages')->saveFieldReady($page, $field); try { $result = $query->execute(); - $this->wire('pages')->savedField($page, $field); + $this->wire('pages')->savedField($page, $field); } catch(Exception $e) { $result = false; - if($this->wire('config')->debug) $this->error($e->getMessage(), Notice::log); - else $this->wire('log')->error($e->getMessage()); + if($this->wire('config')->debug) $this->error($e->getMessage(), Notice::log); + else $this->wire('log')->error($e->getMessage()); } - if($result) { - $this->checkExistingComment($page, $field, $comment); + if($result) { + $this->checkExistingComment($page, $field, $comment); } return $result; } @@ -1148,32 +1183,32 @@ class FieldtypeComments extends FieldtypeMulti { * */ public function deleteComment(Page $page, Field $field, Comment $comment, $notes = '') { - + if($field->depth > 0) { foreach($comment->children() as $child) { - $this->deleteComment($page, $field, $child); + $this->deleteComment($page, $field, $child); } } - + $table = $this->wire('database')->escapeTable($field->getTable()); $sql = "DELETE FROM `$table` WHERE id=:id AND pages_id=:pages_id"; - $query = $this->wire('database')->prepare($sql); + $query = $this->wire('database')->prepare($sql); $query->bindValue(':id', $comment->id, PDO::PARAM_INT); $query->bindValue(':pages_id', $page->id, PDO::PARAM_INT); - $comments = $page->get($field->name); - + $comments = $page->get($field->name); + try { - $this->wire('pages')->saveFieldReady($page, $field); + $this->wire('pages')->saveFieldReady($page, $field); $result = $query->execute(); - if($comments) $comments->remove($comment); - $this->commentDeleted($page, $field, $comment, $notes); - $this->wire('pages')->savedField($page, $field); - + if($comments) $comments->remove($comment); + $this->commentDeleted($page, $field, $comment, $notes); + $this->wire('pages')->savedField($page, $field); + } catch(Exception $e) { - $this->error($e->getMessage()); + $this->error($e->getMessage()); $result = false; } - + return $result; } @@ -1191,29 +1226,29 @@ class FieldtypeComments extends FieldtypeMulti { /** * Hook called when a comment goes from un-approved to approved - * + * * @param Page $page * @param Field $field * @param Comment $comment * @param string $notes - * + * */ public function ___commentApproved(Page $page, Field $field, Comment $comment, $notes = '') { - - $this->wire('log')->message("Approved comment $comment->id - $notes"); - + + $this->wire('log')->message("Approved comment $comment->id - $notes"); + if($field->useNotify) { - + $emails = array(); foreach($page->get($field->name) as $c) { - - if($c->status < Comment::statusApproved) continue; - if($c->id == $comment->id) continue; - if(strtolower($c->email) == strtolower($comment->email)) continue; - + + if($c->status < Comment::statusApproved) continue; + if($c->id == $comment->id) continue; + if(strtolower($c->email) == strtolower($comment->email)) continue; + /* * @todo this should be ready to use, but needs more testing before enabling it - * + * if($c->flags & Comment::flagNotifyConfirmed) { // notifications have been confirmed by double opt-in } else { @@ -1226,18 +1261,18 @@ class FieldtypeComments extends FieldtypeMulti { if($c->subcode) $emails[strtolower($c->email)] = $c->subcode; } } - + // emails array contains email address => subcode to send notifications to if(count($emails)) { - require_once(dirname(__FILE__) . '/CommentNotifications.php'); - $no = new CommentNotifications($page, $field); + require_once(dirname(__FILE__) . '/CommentNotifications.php'); + $no = new CommentNotifications($page, $field); foreach($emails as $email => $subcode) { $no->sendNotificationEmail($comment, $email, $subcode); } } } } - + /** * Hook called when a comment goes from approved to pending or spam @@ -1253,31 +1288,31 @@ class FieldtypeComments extends FieldtypeMulti { /** * Add a vote to the current comment from the current user/IP - * + * * @param Page $page * @param Field $field * @param Comment $comment * @param bool $up Specify true for upvote, or false for downvote * @return bool Returns true on success, false on failure or duplicate - * + * */ public function voteComment(Page $page, Field $field, Comment $comment, $up = true) { - - $database = $this->wire('database'); + + $database = $this->wire('database'); $table = $database->escapeTable($field->getTable()) . '_votes'; - + if(!$field->useVotes) return false; if(!$up && $field->useVotes != self::useVotesAll) return false; // downvotes not allowed - + $sql = "INSERT INTO `$table` SET comment_id=:comment_id, vote=:vote, ip=:ip, user_id=:user_id"; $query = $database->prepare($sql); - $query->bindValue(':comment_id', $comment->id, PDO::PARAM_INT); - $query->bindValue(':vote', $up ? 1 : -1, PDO::PARAM_INT); - $query->bindValue(':ip', $this->wire('session')->getIP(), PDO::PARAM_STR); - $query->bindValue(':user_id', $this->wire('user')->id, PDO::PARAM_INT); - + $query->bindValue(':comment_id', $comment->id, PDO::PARAM_INT); + $query->bindValue(':vote', $up ? 1 : -1, PDO::PARAM_INT); + $query->bindValue(':ip', $this->wire('session')->getIP(), PDO::PARAM_STR); + $query->bindValue(':user_id', $this->wire('user')->id, PDO::PARAM_INT); + $result = false; - + try { if($query->execute()) { if($up) { @@ -1292,38 +1327,38 @@ class FieldtypeComments extends FieldtypeMulti { // duplicate or fail $error = $e->getMessage(); if(stripos($error, 'duplicate entry')) { - $this->error($this->_('You have already voted for this comment')); + $this->error($this->_('You have already voted for this comment')); } else { if($this->wire('config')->debug && !$this->wire('user')->isLoggedin()) { $this->error($e->getMessage()); } else { - $this->error($this->_('Error recording vote')); + $this->error($this->_('Error recording vote')); } } } - + return $result; } /** * Check the request for a vote action - * + * * @param Page|HookEvent $page * @return array * @throws WireException - * + * */ public function checkVoteAction($page) { - + $result = array( - 'success' => false, - 'valid' => false, - 'message' => 'Invalid vote', + 'success' => false, + 'valid' => false, + 'message' => 'Invalid vote', 'upvotes' => 0, - 'downvotes' => 0, - 'pageID' => 0, - 'fieldName' => '', - 'commentID' => 0, + 'downvotes' => 0, + 'pageID' => 0, + 'fieldName' => '', + 'commentID' => 0, ); $action = $this->wire('input')->get('comment_success'); @@ -1335,27 +1370,27 @@ class FieldtypeComments extends FieldtypeMulti { $field = $this->wire('fields')->get($fieldID); if(!$field || !$field->type instanceof FieldtypeComments) return $result; - - if($page instanceof HookEvent) $page = $page->object; + + if($page instanceof HookEvent) $page = $page->object; if(!$page || !$page->template->fieldgroup->hasField($field)) return $result; $comment = $this->getCommentByID($page, $field, $commentID); if(!$comment || $comment->getPage()->id != $page->id || $comment->getField()->id != $field->id) return $result; $success = $this->voteComment($page, $field, $comment, $action == 'upvote'); - $message = $success ? '' : $this->errors('clear string'); - + $message = $success ? '' : $this->errors('clear string'); + $result = array( 'success' => $success, - 'valid' => true, + 'valid' => true, 'message' => $message, 'upvotes' => $comment->upvotes, 'downvotes' => $comment->downvotes, - 'pageID' => $page->id, - 'fieldName' => $field->name, + 'pageID' => $page->id, + 'fieldName' => $field->name, 'commentID' => $comment->id ); - + if($this->wire('config')->ajax) { header("Content-type: application/json"); echo json_encode($result);