mirror of
https://github.com/processwire/processwire.git
synced 2025-08-11 17:24:46 +02:00
Major refactoring of ProcessWire's FieldtypeComments classes and addition of new CommentListCustom and CommentFormCustom classes for more markup control over comment lists and forms. Plus several new API methods and other upgrades for comments.
This commit is contained in:
@@ -111,7 +111,7 @@ class Comment extends WireData {
|
||||
/**
|
||||
* Field this comment is for
|
||||
*
|
||||
* @var null|Field
|
||||
* @var null|Field|CommentField
|
||||
*
|
||||
*/
|
||||
protected $field = null;
|
||||
@@ -423,7 +423,7 @@ class Comment extends WireData {
|
||||
/**
|
||||
* Get Field that this Comment belongs to
|
||||
*
|
||||
* @return null|CommentField
|
||||
* @return null|Field|CommentField
|
||||
*
|
||||
*/
|
||||
public function getField() {
|
||||
@@ -487,6 +487,7 @@ class Comment extends WireData {
|
||||
*
|
||||
*/
|
||||
public function parents() {
|
||||
if(!$this->parent_id) return $this->wire(new CommentArray());
|
||||
$parents = $this->getPageComments()->makeNew();
|
||||
$parent = $this->parent();
|
||||
while($parent && $parent->id) {
|
||||
@@ -507,7 +508,7 @@ class Comment extends WireData {
|
||||
// $comments = $this->getPageComments();
|
||||
$page = $this->getPage();
|
||||
$field = $this->getField();
|
||||
$comments = $this->getPage()->get($field->name);
|
||||
$comments = $page->get($field->name);
|
||||
$children = $comments->makeNew();
|
||||
if($page) $children->setPage($this->getPage());
|
||||
if($field) $children->setField($this->getField());
|
||||
@@ -519,6 +520,36 @@ class Comment extends WireData {
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of children (replies) for this comment
|
||||
*
|
||||
* ~~~~~
|
||||
* $qty = $comment->numChildren([ 'minStatus' => Comment::statusApproved ]);
|
||||
* ~~~~~
|
||||
*
|
||||
* @param array $options Limit return value by specific properties (below):
|
||||
* - `status` (int): Specify Comment::status* constant to include only this status
|
||||
* - `minStatus` (int): Specify Comment::status* constant to include only comments with at least this status
|
||||
* - `maxStatus` (int): Specify Comment::status* constant or include only comments up to this status
|
||||
* - `minCreated` (int): Minimum created unix timestamp
|
||||
* - `maxCreated` (int): Maximum created unix timestamp
|
||||
* - `stars` (int): Number of stars to match (1-5)
|
||||
* - `minStars` (int): Minimum number of stars to match (1-5)
|
||||
* - `maxStars` (int): Maximum number of stars to match (1-5)
|
||||
* @return int
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function numChildren(array $options = array()) {
|
||||
$options['parent'] = $this->id;
|
||||
$field = $this->getField();
|
||||
if(!$field) return null;
|
||||
/** @var FieldtypeComments $fieldtype */
|
||||
$fieldtype = $field->type;
|
||||
if(!$fieldtype) return 0;
|
||||
return $fieldtype->getNumComments($this->getPage(), $field, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this comment have the given child comment?
|
||||
*
|
||||
|
@@ -135,7 +135,8 @@ class CommentArray extends PaginatedArray implements WirePaginatable {
|
||||
/**
|
||||
* Return instance of CommentList object
|
||||
*
|
||||
* @param array $options See CommentList::$options for details
|
||||
* @param array $options See CommentList::$options for details, plus:
|
||||
* - `className` (string): PHP class name to use for CommentList (default='CommentList')
|
||||
* @return CommentList
|
||||
*
|
||||
*/
|
||||
@@ -148,14 +149,21 @@ class CommentArray extends PaginatedArray implements WirePaginatable {
|
||||
'useStars' => $field->get('useStars'),
|
||||
'depth' => $field->get('depth'),
|
||||
'dateFormat' => $field->get('dateFormat'),
|
||||
'className' => 'CommentList',
|
||||
);
|
||||
} else {
|
||||
$defaults = array(
|
||||
'dateFormat' => 'relative'
|
||||
'dateFormat' => 'relative',
|
||||
'className' => 'CommentList',
|
||||
);
|
||||
}
|
||||
$options = array_merge($defaults, $options);
|
||||
return $this->wire(new CommentList($this, $options));
|
||||
$file = __DIR__ . '/' . $options['className'] . '.php';
|
||||
$className = wireClassName($options['className'], true);
|
||||
if(!class_exists($className) && is_file($file)) include_once($file);
|
||||
unset($options['className']);
|
||||
|
||||
return $this->wire(new $className($this, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,8 +175,20 @@ class CommentArray extends PaginatedArray implements WirePaginatable {
|
||||
*
|
||||
*/
|
||||
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 $this->wire(new CommentForm($this->page, $this, $options));
|
||||
$defaults = array(
|
||||
'className' => 'CommentForm',
|
||||
'depth' => ($this->field ? (int) $this->field->get('depth') : 0)
|
||||
);
|
||||
$options = array_merge($defaults, $options);
|
||||
if(!$this->page) {
|
||||
throw new WireException("You must set a page to this CommentArray before using it i.e. \$ca->setPage(\$page)");
|
||||
}
|
||||
$file = __DIR__ . '/' . $options['className'] . '.php';
|
||||
$className = wireClassName($options['className'], true);
|
||||
if(!class_exists($className) && is_file($file)) include_once($file);
|
||||
unset($options['className']);
|
||||
|
||||
return $this->wire(new $className($this->page, $this, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +204,7 @@ class CommentArray extends PaginatedArray implements WirePaginatable {
|
||||
/**
|
||||
* Set the Field that these comments are on
|
||||
*
|
||||
* @param Field $field
|
||||
* @param CommentField|Field $field
|
||||
*
|
||||
*/
|
||||
public function setField(Field $field) {
|
||||
@@ -204,7 +224,7 @@ class CommentArray extends PaginatedArray implements WirePaginatable {
|
||||
/**
|
||||
* Get the Field that these comments are on
|
||||
*
|
||||
* @return Field
|
||||
* @return CommentField|Field
|
||||
*
|
||||
*/
|
||||
public function getField() {
|
||||
|
@@ -55,6 +55,31 @@ class CommentField extends Field {
|
||||
return $this->getFieldtype()->count($selectorString, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of comments for Page, optionally limited by specific $options
|
||||
*
|
||||
* Unlike the count() method this is focused on a specific Page and may be faster in
|
||||
* cases where it matches what you need to count.
|
||||
*
|
||||
* @param Page $page
|
||||
* @param array $options
|
||||
* - `status` (int): Specify Comment::status* constant to include only this status
|
||||
* - `minStatus` (int): Specify Comment::status* constant to include only comments with at least this status
|
||||
* - `maxStatus` (int): Specify Comment::status* constant or include only comments up to this status
|
||||
* - `parent` (int|Comment): Specify parent comment ID, 0 for root-only, or omit for no parent limitation
|
||||
* - `minCreated` (int): Minimum created unix timestamp
|
||||
* - `maxCreated` (int): Maximum created unix timestamp
|
||||
* - `stars` (int): Number of stars to match (1-5)
|
||||
* - `minStars` (int): Minimum number of stars to match (1-5)
|
||||
* - `maxStars` (int): Maximum number of stars to match (1-5)
|
||||
* @return int
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function getNumComments(Page $page, array $options = array()) {
|
||||
return $this->getFieldtype()->getNumComments($page, $this, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a comment code or subcode, return the associated comment ID or 0 if it doesn't exist
|
||||
*
|
||||
@@ -79,6 +104,41 @@ class CommentField extends Field {
|
||||
return $this->getFieldtype()->getCommentByID($page, $this, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent comments for given Comment
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Comment $comment
|
||||
* @return CommentArray
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function getCommentParents(Page $page, Comment $comment) {
|
||||
return $this->getFieldtype()->getCommentParents($page, $this, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new comment
|
||||
*
|
||||
* Requires a new Comment object with no id, that has all its required field populated and validated
|
||||
* and is ready to add. Note that the `sort` property is assigned automatically if not specified in Comment.
|
||||
*
|
||||
* The primary reason to use this method is if you want to add a comment without loading all the other
|
||||
* comments on a given Page.
|
||||
*
|
||||
* @param Page $page Page where comments field exists
|
||||
* @param Comment $comment New comment to add
|
||||
* @param bool $send Send comment for automatic approval filtering and email notifications?
|
||||
* - `true` if comment was just submitted now from user input and filtering should apply, notifications sent, etc.
|
||||
* - `false` if you are importing comments and NO filtering should be applied, NO notifications sent, etc.
|
||||
* @return bool Returns true on success, false on fail
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function addComment(Page $page, Comment $comment, $send) {
|
||||
return $this->getFieldtype()->addComment($page, $this, $comment, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update specific properties for a comment
|
||||
*
|
||||
|
@@ -35,18 +35,24 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
|
||||
/**
|
||||
* Page object where the comment is being submitted
|
||||
*
|
||||
* @var Page
|
||||
*
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* Reference to the Field object used by this CommentForm
|
||||
*
|
||||
* @var CommentField
|
||||
*
|
||||
*/
|
||||
protected $commentsField;
|
||||
|
||||
/**
|
||||
* Instance of CommentArray, containing all Comment instances for this Page
|
||||
*
|
||||
* @var CommentArray
|
||||
*
|
||||
*/
|
||||
protected $comments;
|
||||
@@ -60,7 +66,7 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
'email' => '',
|
||||
'text' => '',
|
||||
'notify' => '',
|
||||
);
|
||||
);
|
||||
|
||||
protected $postedComment = null;
|
||||
|
||||
@@ -69,10 +75,10 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
*
|
||||
*/
|
||||
protected $options = array(
|
||||
'headline' => '', // Post Comment
|
||||
'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.
|
||||
'headline' => '', // Headline (with markup) above form, or specify false for no headline
|
||||
'successMessage' => '', // Success messsage with markup. "<p>Thank you, your submission has been saved</p>"
|
||||
'pendingMessage' => '', // Comment pending message with markup. "<p>Your comment has been submitted and will appear once approved by the moderator.</p>"
|
||||
'errorMessage' => '', // Error message with markup. "<p>Your submission was not saved due to one or more errors. Try again.</p>"
|
||||
'processInput' => true,
|
||||
'encoding' => 'UTF-8',
|
||||
'attrs' => array(
|
||||
@@ -86,6 +92,7 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
),
|
||||
'inputWrapTag' => 'p', // tag that wraps label/inputs
|
||||
'labels' => array(
|
||||
'headline' => '', // Post Comment
|
||||
'cite' => '', // Your Name
|
||||
'email' => '', // Your E-Mail
|
||||
'website' => '',// Website
|
||||
@@ -93,6 +100,13 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
'text' => '', // Comments
|
||||
'submit' => '', // Submit
|
||||
'starsRequired' => '', // Please select a star rating
|
||||
'success' => '',
|
||||
'pending' => '',
|
||||
'error' => '',
|
||||
'notify' => '', // E-Mail Notifications:
|
||||
'notifyReplies' => '',
|
||||
'notifyAll' => '',
|
||||
'notifyOff' => '',
|
||||
),
|
||||
'classes' => array(
|
||||
'form' => '',
|
||||
@@ -114,6 +128,15 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
'radioInput' => '',
|
||||
'submit' => 'CommentFormSubmit {id}_submit',
|
||||
'submitButton' => '',
|
||||
'success' => 'success',
|
||||
'pending' => 'pending success',
|
||||
'error' => 'error',
|
||||
),
|
||||
'markup' => array(
|
||||
'headline' => "<{tag}>{headline}</{tag}>", // feel free to replace {tag} with "h1", "h2", "h3"
|
||||
'notification' => "<p class='{class}'><strong>{message}</strong></p>",
|
||||
'wrapNotification' => "<div id='CommentPostNote'>{out}</div>",
|
||||
'wrapAll' => "<div id='{id}' class='{class}'>{headline}{note}{form}</div><!--/{id}-->",
|
||||
),
|
||||
|
||||
// values that will be already set, perhaps pulled from a user profile for instance (null = ignore)
|
||||
@@ -145,8 +168,11 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
|
||||
// When a comment is saved to a page, avoid updating the modified time/user
|
||||
'quietSave' => false,
|
||||
|
||||
// interial use: have options been initialized and are ready to use?
|
||||
'_ready' => false,
|
||||
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@@ -160,39 +186,12 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
public function __construct(Page $page, CommentArray $comments, $options = array()) {
|
||||
|
||||
$this->page = $page;
|
||||
$this->comments = $comments;
|
||||
|
||||
// default messages
|
||||
$h3 = $this->_('h3'); // Headline tag
|
||||
$this->options['headline'] = "<$h3>" . $this->_('Post Comment') . "</$h3>"; // Form headline
|
||||
$this->options['successMessage'] = "<p class='success'><strong>" . $this->_('Thank you, your submission has been saved.') . "</strong></p>";
|
||||
$this->options['pendingMessage'] = "<p class='success pending'><strong>" . $this->_('Your comment has been submitted and will appear once approved by the moderator.') . "</strong></p>";
|
||||
$this->options['errorMessage'] = "<p class='error'><strong>" . $this->_('Your submission was not saved due to one or more errors. Please check that you have completed all fields before submitting again.') . "</strong></p>";
|
||||
$this->comments = $comments;
|
||||
|
||||
if(substr($this->wire('input')->url(), -1) != '/') {
|
||||
$this->options['attrs']['action'] = $page->url;
|
||||
}
|
||||
|
||||
// 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']['stars'] = ''; // i.e. "Your Rating"
|
||||
$this->options['labels']['starsRequired'] = $this->_('Please choose a star rating');
|
||||
$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']);
|
||||
}
|
||||
if(isset($options['attrs'])) {
|
||||
$this->options['attrs'] = array_merge($this->options['attrs'], $options['attrs']);
|
||||
unset($options['attrs']);
|
||||
}
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
// determine which field on the page is the commentsField and save the Field instance
|
||||
$this->commentsField = $this->comments->getField();
|
||||
|
||||
/*
|
||||
foreach($this->wire('fields') as $field) {
|
||||
if(!$field->type instanceof FieldtypeComments) continue;
|
||||
$value = $this->page->get($field->name);
|
||||
@@ -201,31 +200,251 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
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->setOptions($options);
|
||||
}
|
||||
|
||||
public function setAttr($attr, $value) {
|
||||
$this->options['attrs'][$attr] = $value;
|
||||
}
|
||||
|
||||
public function setLabel($label, $value) {
|
||||
$this->options['labels'][$label] = $value;
|
||||
}
|
||||
/**
|
||||
* Initialize and set options
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
*/
|
||||
public function setOptions(array $options) {
|
||||
|
||||
if(!$this->options['_ready']) {
|
||||
// default labels
|
||||
$this->options['labels'] = array(
|
||||
'headline' => '', // $this->_('Post Comment'),
|
||||
'cite' => $this->_('Your Name'),
|
||||
'email' => $this->_('Your E-Mail'),
|
||||
'website' => $this->_('Your Website URL'),
|
||||
'text' => $this->_('Comments'),
|
||||
'submit' => $this->_('Submit'),
|
||||
'stars' => '', // i.e. "Your Rating"
|
||||
'starsRequired' => $this->_('Please choose a star rating'),
|
||||
'success' => $this->_('Thank you, your submission has been saved.'),
|
||||
'pending' => $this->_('Your comment has been submitted and will appear once approved by the moderator.'),
|
||||
'error' => $this->_('Your submission was not saved due to one or more errors. Please check that you have completed all fields before submitting again.'),
|
||||
'notify' => $this->_('E-Mail Notifications:'),
|
||||
'notifyReplies' => $this->_('Replies'),
|
||||
'notifyAll' => $this->_('All'),
|
||||
'notifyOff' => $this->_('Off'),
|
||||
);
|
||||
}
|
||||
|
||||
// if request URL does not end with a slash, use the page URL as default action attribute
|
||||
if(substr($this->wire('input')->url(), -1) != '/') {
|
||||
$this->options['attrs']['action'] = $this->page->url;
|
||||
}
|
||||
|
||||
// merge any user supplied array-based options to overwrite defaults
|
||||
foreach(array('labels', 'attrs', 'classes', 'markup', 'presets') as $key) {
|
||||
if(!isset($options[$key])) continue;
|
||||
$this->options[$key] = array_merge($this->options[$key], $options[$key]);
|
||||
unset($options[$key]);
|
||||
}
|
||||
|
||||
// merge user supplied options with defaults
|
||||
$this->options = array_merge($this->options, $options);
|
||||
$options = &$this->options;
|
||||
|
||||
// default headline
|
||||
$headline = $options['headline'];
|
||||
$headlineLabel = $options['labels']['headline'];
|
||||
if($headline && strpos($headline, '</') === false) {
|
||||
// headline present but has no markup, so we will treat it as a non-markup label instead
|
||||
$headlineLabel = $headline;
|
||||
$headline = '';
|
||||
}
|
||||
if(empty($headline) && $headline !== false && $headlineLabel) {
|
||||
$tag = $this->_('h3'); // Default headline tag
|
||||
$markup = $options['markup']['headline'];
|
||||
$options['headline'] = str_replace(array('{headline}', '{tag}'), array($headlineLabel, $tag), $markup);
|
||||
}
|
||||
|
||||
// populate markup version of successMessage, pendingMessage, errorMessage
|
||||
foreach(array('success', 'pending', 'error') as $type) {
|
||||
$property = $type . 'Message';
|
||||
$value = $options[$property];
|
||||
if(empty($value)) {
|
||||
// option not yet populated
|
||||
$label = $options['labels'][$type];
|
||||
} else {
|
||||
// if already populated and has markup, so we will leave it as-is...
|
||||
if(strpos($value, '</')) continue;
|
||||
// ...otherwise if it has no markup, use it as the label
|
||||
$label = $value;
|
||||
}
|
||||
$class = $options['classes'][$type];
|
||||
$markup = $options['markup']['notification']; // For example: <p class='{class}'><strong>{message}</strong></p>
|
||||
$options[$property] = str_replace(array('{class}', '{message}'), array($class, $label), $markup);
|
||||
}
|
||||
|
||||
$options['_ready'] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function getOptions() {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function setOptions(array $options) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
public function option($key, $value = null) {
|
||||
if($value === null) {
|
||||
$value = isset($this->options[$key]) ? $this->options[$key] : null;
|
||||
} else {
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set array property
|
||||
*
|
||||
* @param string $property Name of array property: labels, markup, classes, attrs, presets
|
||||
* @param string|array $name Name of item to get or set or omit to get all, or assoc array to set all/multiple (and omit $value)
|
||||
* @param string|null $value Value to set (if setting) or omit if getting
|
||||
* @return string|array
|
||||
* @throws WireException
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
protected function arrayOption($property, $name = '', $value = null) {
|
||||
if(!is_array($this->options[$property])) {
|
||||
// invalid
|
||||
throw new WireException("Invalid array property: $property");
|
||||
} else if(empty($name)) {
|
||||
// get all
|
||||
$value = $this->options[$property];
|
||||
} else if(is_array($name)) {
|
||||
// set all or multiple
|
||||
$this->options[$property] = array_merge($this->options[$property], $value);
|
||||
} else if($value !== null) {
|
||||
// set one
|
||||
$this->options[$property][$name] = $value;
|
||||
} else if(isset($this->options[$property][$name])) {
|
||||
// get one
|
||||
$value = $this->options[$property][$name];
|
||||
} else {
|
||||
// unknown
|
||||
$value = '';
|
||||
}
|
||||
|
||||
if($value !== null && in_array($name, array('success', 'pending', 'error', 'notification'))) {
|
||||
// reset notifications rendered by setOptions
|
||||
$this->options['successMessage'] = '';
|
||||
$this->options['pendingMessage'] = '';
|
||||
$this->options['errorMessage'] = '';
|
||||
$this->setOptions($this->options);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set label
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function labels($name, $value = null) {
|
||||
$result = $this->arrayOption('labels', $name, $value);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set attribute
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function attrs($name, $value = null) {
|
||||
return $this->arrayOption('attrs', $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set class(es)
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function classes($name, $value = null) {
|
||||
return $this->arrayOption('classes', $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set markup
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function markup($name, $value = null) {
|
||||
return $this->arrayOption('markup', $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set presets
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function presets($name, $value = null) {
|
||||
return $this->arrayOption('presets', $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute
|
||||
*
|
||||
* @param string $attr
|
||||
* @param string $value
|
||||
* @deprecated Use attrs() method instead
|
||||
*
|
||||
*/
|
||||
public function setAttr($attr, $value) {
|
||||
$this->attrs($attr, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set label
|
||||
*
|
||||
* @param string $label Label name
|
||||
* @param string $value Label value
|
||||
* @deprecated Use labels() method instead
|
||||
*
|
||||
*/
|
||||
public function setLabel($label, $value) {
|
||||
$this->labels($label, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the output of the render() method when a Comment is posted
|
||||
*
|
||||
@@ -237,7 +456,7 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
*/
|
||||
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
|
||||
@@ -275,8 +494,8 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
// other/comment still pending
|
||||
$message = $this->options['pendingMessage'];
|
||||
}
|
||||
|
||||
return "<div id='CommentPostNote'>$message</div>";
|
||||
|
||||
return str_replace('{out}', $message, $this->options['markup']['wrapNotification']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,52 +514,36 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
$submitKey = $id . "_submit";
|
||||
$honeypot = $options['requireHoneypotField'];
|
||||
$inputValues = array('cite' => '', 'email' => '', 'website' => '', 'stars' => '', 'text' => '', 'notify' => '');
|
||||
$sanitizer = $this->wire('sanitizer'); /** @var Sanitizer $sanitizer */
|
||||
if($honeypot) $inputValues[$honeypot] = '';
|
||||
|
||||
$user = $this->wire('user');
|
||||
|
||||
if($user->isLoggedin()) {
|
||||
$inputValues['cite'] = $user->name;
|
||||
$inputValues['email'] = $user->email;
|
||||
$inputValues['cite'] = $sanitizer->entities($user->name);
|
||||
$inputValues['email'] = $sanitizer->entities($user->email);
|
||||
}
|
||||
|
||||
|
||||
/** @var WireInput $input */
|
||||
$input = $this->wire('input');
|
||||
$divClass = 'new';
|
||||
$class = trim("CommentForm " . $attrs['class']);
|
||||
$note = '';
|
||||
|
||||
/*
|
||||
* 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(!isset($sessionValues[$key])) $sessionValues[$key] = '';
|
||||
$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;
|
||||
}
|
||||
|
||||
$out = '';
|
||||
$showForm = true;
|
||||
|
||||
if($options['processInput'] && $input->post->$submitKey == 1) {
|
||||
if($options['processInput'] && $input->post($submitKey) == 1) {
|
||||
$comment = $this->processInput();
|
||||
if($comment) {
|
||||
$out .= $this->renderSuccess($comment); // success, return
|
||||
$note = $this->renderSuccess($comment); // success, return
|
||||
} else {
|
||||
$inputValues = array_merge($inputValues, $this->inputValues);
|
||||
foreach($inputValues as $key => $value) {
|
||||
$inputValues[$key] = htmlentities($value, ENT_QUOTES, $this->options['encoding']);
|
||||
}
|
||||
$note = "\n\t$options[errorMessage]";
|
||||
$note = "$options[errorMessage]";
|
||||
$divClass = 'error';
|
||||
}
|
||||
|
||||
@@ -348,29 +551,58 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
$note = $this->renderSuccess();
|
||||
}
|
||||
|
||||
$form = '';
|
||||
if($showForm) {
|
||||
if($this->options['depth'] > 0) {
|
||||
$form = $this->renderFormThread($id, $class, $attrs, $labels, $inputValues);
|
||||
} else {
|
||||
$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);
|
||||
}
|
||||
$form = $this->renderForm($id, $class, $attrs, $labels, $inputValues);
|
||||
|
||||
if(!$options['presetsEditable']) {
|
||||
foreach($options['presets'] as $key => $value) {
|
||||
$a = array(" name='$key'", " name=\"$key\"");
|
||||
if(!is_null($value)) $form = str_replace($a, " name='$key' disabled='disabled'", $form);
|
||||
}
|
||||
}
|
||||
|
||||
$out .=
|
||||
"\n<div id='{$id}' class='{$id}_$divClass'>" .
|
||||
"\n" . $this->options['headline'] . $note . $form .
|
||||
"\n</div><!--/$id-->";
|
||||
|
||||
|
||||
return $out;
|
||||
// <div id='{id}' class='{class}'>\n{headline}{note}{form}\n</div><!--/{id}-->
|
||||
$replacements = array(
|
||||
'{id}' => $id,
|
||||
'{class}' => "{$id}_$divClass",
|
||||
'{headline}' => $this->options['headline'],
|
||||
'{note}' => $note,
|
||||
'{form}' => $form,
|
||||
);
|
||||
|
||||
return str_replace(array_keys($replacements), array_values($replacements), $this->markup('wrapAll'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render form
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $class
|
||||
* @param array $attrs
|
||||
* @param array $labels
|
||||
* @param array $inputValues
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderForm($id, $class, $attrs, $labels, $inputValues) {
|
||||
if($this->options['depth'] > 0) {
|
||||
$form = $this->renderFormThread($id, $class, $attrs, $labels, $inputValues);
|
||||
} else {
|
||||
$form = $this->renderFormNormal($id, $class, $attrs, $labels, $inputValues);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render normal form without threaded comments possibility
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $class
|
||||
* @param array $attrs
|
||||
* @param array $labels
|
||||
* @param array $inputValues
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderFormNormal($id, $class, $attrs, $labels, $inputValues) {
|
||||
$form =
|
||||
"\n<form id='{$id}_form' class='$class CommentFormNormal' action='$attrs[action]#$id' method='$attrs[method]'>" .
|
||||
@@ -434,7 +666,18 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render form for threaded (depth) comments
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $class
|
||||
* @param array $attrs
|
||||
* @param array $labels
|
||||
* @param array $inputValues
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderFormThread($id, $class, $attrs, $labels, $inputValues) {
|
||||
|
||||
$classes = $this->options['classes'];
|
||||
@@ -531,7 +774,13 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the "notify me" options
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderNotifyOptions() {
|
||||
|
||||
if(!$this->commentsField->useNotify) return '';
|
||||
@@ -541,11 +790,11 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
$options = array();
|
||||
|
||||
if($this->commentsField->depth > 0) {
|
||||
$options['2'] = $this->_('Replies');
|
||||
$options['2'] = $this->labels('notifyReplies');
|
||||
}
|
||||
|
||||
if($this->commentsField->useNotify == Comment::flagNotifyAll) {
|
||||
$options['4'] = $this->_('All');
|
||||
$options['4'] = $this->labels('notifyAll');
|
||||
}
|
||||
|
||||
$classes = array(
|
||||
@@ -563,8 +812,8 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
if(count($options)) {
|
||||
$out =
|
||||
"\n\t<$tag$classes[notify]>" .
|
||||
"\n\t\t<label$classes[label]><span$classes[labelSpan]>" . $this->_('E-Mail Notifications:') . "</span></label> " .
|
||||
"\n\t\t<label$classes[radioLabel]><input$classes[radioInput] type='radio' name='notify' checked='checked' value='0' /> " . $this->_('Off') . "</label> ";
|
||||
"\n\t\t<label$classes[label]><span$classes[labelSpan]>" . $this->labels('notify') . "</span></label> " .
|
||||
"\n\t\t<label$classes[radioLabel]><input$classes[radioInput] type='radio' name='notify' checked='checked' value='0' /> " . $this->labels('notifyOff') . "</label> ";
|
||||
|
||||
foreach($options as $value => $label) {
|
||||
$label = str_replace(' ', ' ', $label);
|
||||
@@ -585,7 +834,7 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
public function processInput() {
|
||||
|
||||
$data = $this->wire('input')->post;
|
||||
if(!count($data)) return false;
|
||||
if(!count($data) || !$this->commentsField) return false;
|
||||
|
||||
if($key = $this->options['requireSecurityField']) {
|
||||
if(empty($data[$key])) return false;
|
||||
@@ -594,20 +843,37 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
if($key = $this->options['requireHoneypotField']) {
|
||||
if(!empty($data[$key])) return false;
|
||||
}
|
||||
|
||||
$maxDepth = $this->commentsField->depth;
|
||||
|
||||
/** @var Comment $comment */
|
||||
$comment = $this->wire(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->sort = count($this->comments)+1;
|
||||
$comment->parent_id = $maxDepth ? (int) abs((int) $data->parent_id) : 0;
|
||||
|
||||
if($comment->parent_id && $maxDepth) {
|
||||
$parent = $this->commentsField->getCommentByID($this->page, $comment->parent_id);
|
||||
if($parent) {
|
||||
// validate that depth is in allowed limit
|
||||
$parents = $this->commentsField->getCommentParents($this->page, $comment);
|
||||
if($parents->count() >= $maxDepth) $comment->parent_id = 0;
|
||||
} else {
|
||||
// parent does not exist on this page
|
||||
$comment->parent_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$errors = array();
|
||||
// $sessionData = array();
|
||||
|
||||
foreach(array('cite', 'email', 'website', 'stars', 'text') as $key) {
|
||||
|
||||
if($key == 'website' && (!$this->commentsField || !$this->commentsField->useWebsite)) continue;
|
||||
if($key == 'stars' && (!$this->commentsField || !$this->commentsField->useStars)) continue;
|
||||
|
||||
if($this->options['presetsEditable'] || !isset($this->options['presets'][$key]) || $this->options['presets'][$key] === null) {
|
||||
$comment->$key = $data->$key; // Comment performs sanitization/validation
|
||||
} else {
|
||||
@@ -632,19 +898,28 @@ class CommentForm extends Wire implements CommentFormInterface {
|
||||
// send confirmation email
|
||||
}
|
||||
}
|
||||
|
||||
$comment->flags = $flags;
|
||||
|
||||
if(!count($errors)) {
|
||||
if($this->comments->add($comment) && $this->commentsField) {
|
||||
$outputFormatting = $this->page->outputFormatting;
|
||||
if(!count($errors) && $this->commentsField) {
|
||||
$result = $this->commentsField->addComment($this->page, $comment, true);
|
||||
if($result) {
|
||||
// added successfully
|
||||
if($this->comments->getPage()) $this->comments->add($comment);
|
||||
} else {
|
||||
// fallback legacy add process
|
||||
$this->comments->add($comment);
|
||||
$outputFormatting = $this->page->outputFormatting;
|
||||
$this->page->setOutputFormatting(false);
|
||||
$saveOptions = array();
|
||||
if($this->options['quietSave']) $saveOptions['quiet'] = true;
|
||||
$this->page->save($this->commentsField->name, $saveOptions);
|
||||
if($this->options['quietSave']) $saveOptions['quiet'] = true;
|
||||
$result = $this->page->save($this->commentsField->name, $saveOptions);
|
||||
$this->page->setOutputFormatting($outputFormatting);
|
||||
$this->postedComment = $comment;
|
||||
// $this->wire('session')->set('CommentForm', $sessionData);
|
||||
return $comment;
|
||||
}
|
||||
// $this->wire('session')->set('CommentForm', $sessionData);
|
||||
if($result) {
|
||||
$this->postedComment = $comment;
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
|
||||
|
300
wire/modules/Fieldtype/FieldtypeComments/CommentFormCustom.php
Normal file
300
wire/modules/Fieldtype/FieldtypeComments/CommentFormCustom.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php namespace ProcessWire;
|
||||
|
||||
/**
|
||||
* CommentFormCustom
|
||||
*
|
||||
* CommentForm with 100% customizable form markup
|
||||
*
|
||||
* ~~~~~~
|
||||
* $form = $page->comments->getCommentForm([
|
||||
* 'className' => 'CommentFormCustom',
|
||||
* ]);
|
||||
*
|
||||
* $formMarkup = "
|
||||
* <form class='{form.class}' action='{form.action}' method='{form.method}' {form.attrs}>
|
||||
* ...copy/paste what's in the getFormMarkup() method for a starting point...
|
||||
* </form>
|
||||
* ";
|
||||
*
|
||||
* $form->markup('form', $formMarkup);
|
||||
* $form->labels('submit', 'Submit Feedback');
|
||||
* $form->labels('notify', 'Email Me');
|
||||
*
|
||||
* // form notifications
|
||||
* $form->markup('notification', "<div class='uk-alert {class}'>{message}</div>");
|
||||
* $form->classes('success', 'uk-alert-success');
|
||||
* $form->classes('pending', 'uk-alert-warning');
|
||||
* $form->classes('error', 'uk-alert-danger');
|
||||
|
||||
* echo $form->render();
|
||||
* ~~~~~~
|
||||
*
|
||||
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer
|
||||
* https://processwire.com
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
class CommentFormCustom extends CommentForm {
|
||||
|
||||
/**
|
||||
* Get form markup
|
||||
*
|
||||
* To set form markup use the markup in this method as your starting point and set your custom markup with this:
|
||||
* ~~~~~
|
||||
* $markup = "...your custom form markup...";
|
||||
* $commentForm->markup('form', $markup);
|
||||
* ~~~~~
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function getFormMarkup() {
|
||||
$out = $this->markup('form');
|
||||
|
||||
if(empty($out)) $out = "
|
||||
<form class='{form.class}' action='{form.action}' method='{form.method}' {form.attrs}>
|
||||
|
||||
<p class='{cite.wrap.class}'>
|
||||
<label class='{label.class}'>
|
||||
<span class='{label.span.class}'>{cite.label}</span>
|
||||
<input type='text' name='{cite.input.name}' class='{cite.input.class}' required='required' value='{cite.input.value}' maxlength='128' />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p class='{email.wrap.class}'>
|
||||
<label class='{label.class}'>
|
||||
<span class='{label.span.class}'>{email.label}</span>
|
||||
<input type='email' name='{email.input.name}' class='{email.input.class}' required='required' value='{email.input.value}' maxlength='255' />
|
||||
</label>
|
||||
</p>
|
||||
|
||||
{if.website}
|
||||
<p class='{website.wrap.class}'>
|
||||
<label class='{label.class}'>
|
||||
<span class='{label.span.class}'>{website.label}</span>
|
||||
<input type='text' name='{website.input.name}' class='{website.input.class}' value='{website.input.value}' maxlength='255' />
|
||||
</label>
|
||||
</p>
|
||||
{endif.website}
|
||||
|
||||
{if.stars}
|
||||
<p class='{stars.wrap.class}' {stars.wrap.attrs}>
|
||||
<label class='{label.class}'>
|
||||
<span class='{label.span.class}'>{stars.label}</span>
|
||||
{stars.markup}
|
||||
</label>
|
||||
</p>
|
||||
{endif.stars}
|
||||
|
||||
{if.honeypot}
|
||||
<p class='{honeypot.wrap.class}'>
|
||||
<label>
|
||||
<span>{honeypot.label}</span>
|
||||
<input type='text' name='{honeypot.input.name}' value='{honeypot.input.value}' size='3' />
|
||||
</label>
|
||||
</p>
|
||||
{endif.honeypot}
|
||||
|
||||
<p class='{text.wrap.class}'>
|
||||
<label class='{label.class}'>
|
||||
<span class='{label.span.class}'>{text.label}</span>
|
||||
<textarea name='text' class='{text.input.class}' required='required' rows='{text.input.rows}' cols='{text.input.cols}'>{text.input.value}</textarea>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
{if.notify}
|
||||
<p class='{notify.wrap.class}'>
|
||||
<label class='{notify.label.class}'>
|
||||
<span class='{notify.label.span.class}'>{notify.label}</span>
|
||||
</label>
|
||||
<label class='{notify.input.label.class}'>
|
||||
<input class='{notify.input.class}' type='radio' name='{notify.input.name}' checked='checked' value='{notify.input.off.value}' />
|
||||
{notify.input.off.label}
|
||||
<label>
|
||||
|
||||
{if.notify.replies}
|
||||
<label class='{notify.input.label.class}'>
|
||||
<input class='{notify.input.class}' type='radio' name='{notify.input.name}' value='{notify.input.replies.value}' />
|
||||
{notify.input.replies.label}
|
||||
<label>
|
||||
{endif.notify.replies}
|
||||
|
||||
{if.notify.all}
|
||||
<label class='{notify.input.label.class}'>
|
||||
<input class='{notify.input.class}' type='radio' name='{notify.input.name}' value='{notify.input.all.value}' />
|
||||
{notify.input.all.label}
|
||||
<label>
|
||||
{endif.notify.all}
|
||||
</p>
|
||||
{endif.notify}
|
||||
|
||||
<p class='{submit.wrap.class}'>
|
||||
<button type='submit' class='{submit.input.class}' name='{submit.input.name}' value='{submit.input.value}'>{submit.label}</button>
|
||||
{form.hidden.inputs}
|
||||
</p>
|
||||
</form>
|
||||
";
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an {if.name} ... {endif.name} statement
|
||||
*
|
||||
* @param string $name Property name for if statement
|
||||
* @param bool $value If true the contents of the {if...} statement will stay, if false it will be removed
|
||||
* @param string $out
|
||||
* @return mixed|string
|
||||
*
|
||||
*/
|
||||
protected function applyIf($name, $value, $out) {
|
||||
|
||||
$if = "{if.$name}";
|
||||
$endif = "{endif.$name}";
|
||||
|
||||
if(strpos($out, $if) === false || strpos($out, $endif) === false) return $out;
|
||||
|
||||
if($value) {
|
||||
$out = str_replace(array($if, $endif), '', $out);
|
||||
} else {
|
||||
list($before, $within) = explode($if, $out, 2);
|
||||
list(,$after) = explode($endif, $within, 2);
|
||||
$out = $before . $after;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom markup form render
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $class
|
||||
* @param array $attrs
|
||||
* @param array $labels
|
||||
* @param array $inputValues
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderForm($id, $class, $attrs, $labels, $inputValues) {
|
||||
|
||||
$field = $this->commentsField;
|
||||
$useDepth = $this->options['depth'] > 0;
|
||||
$classes = $this->options['classes'];
|
||||
$classes['form'] = trim("$classes[form] $class " . ($useDepth ? 'CommentFormThread' : 'CommentFormNormal'));
|
||||
$labels = $this->options['labels'];
|
||||
|
||||
foreach($classes as $key => $value) {
|
||||
$classes[$key] = trim(str_replace('{id}', $id, $value));
|
||||
}
|
||||
|
||||
$labelClass = $classes['label'];
|
||||
$labelSpanClass = $classes['labelSpan'];
|
||||
|
||||
$formHiddenInputs = "<input type='hidden' name='page_id' value='{$this->page->id}' />";
|
||||
if($useDepth) $formHiddenInputs .= "<input type='hidden' class='CommentFormParent' name='parent_id' value='0' />";
|
||||
|
||||
$useStars = $field->useStars && $field->schemaVersion > 5;
|
||||
list($starsClass, $starsNote, $starsInput, $starsMarkup, $stars) = array($classes['stars'], '', '', '', '');
|
||||
if($useStars) {
|
||||
$starsInput = "<input type='number' name='stars' value='$inputValues[stars]' min='0' max='5' />";
|
||||
$commentStars = new CommentStars();
|
||||
$stars = $commentStars->render(0, true);
|
||||
$starsMarkup = $starsInput . $stars;
|
||||
}
|
||||
if($useStars && $field->useStars > 1) {
|
||||
$starsNote = $labels['starsRequired'];
|
||||
$starsClass .= " $classes[starsRequired]";
|
||||
}
|
||||
|
||||
// do we need to show the honeypot field?
|
||||
$honeypot = $this->options['requireHoneypotField'];
|
||||
$useHoneypot = $honeypot ? true : false;
|
||||
$honeypotLabel = $useHoneypot && isset($labels[$honeypot]) ? $labels[$honeypot] : '';
|
||||
$honeypotValue = $useHoneypot && isset($inputValues[$honeypot]) ? $inputValues[$honeypot] : '';
|
||||
|
||||
$placeholders = array(
|
||||
'{form.class}' => $classes['form'],
|
||||
'{form.action}' => $attrs['action'] . "#$id",
|
||||
'{form.method}' => $attrs['method'],
|
||||
'{form.attrs}' => $attrs['form'],
|
||||
'{form.hidden.inputs}' => $formHiddenInputs,
|
||||
'{label.class}' => $labelClass,
|
||||
'{label.span.class}' => $labelSpanClass,
|
||||
'{cite.wrap.class}' => $classes['cite'],
|
||||
'{cite.label}' => $labels['cite'],
|
||||
'{cite.input.name}' => 'cite',
|
||||
'{cite.input.class}' => $classes['citeInput'],
|
||||
'{cite.input.value}' => $inputValues['cite'],
|
||||
'{email.wrap.class}' => $classes['email'],
|
||||
'{email.label}' => $labels['email'],
|
||||
'{email.input.name}' => 'email',
|
||||
'{email.input.class}' => $classes['emailInput'],
|
||||
'{email.input.value}' => $inputValues['email'],
|
||||
'{website.wrap.class}' => $classes['website'],
|
||||
'{website.label}' => $labels['website'],
|
||||
'{website.input.name}' => 'website',
|
||||
'{website.input.class}' => $classes['websiteInput'],
|
||||
'{website.input.value}' => $inputValues['website'],
|
||||
'{stars.wrap.class}' => $starsClass,
|
||||
'{stars.wrap.attrs}' => "data-note='$starsNote'",
|
||||
'{stars.label}' => $labels['stars'],
|
||||
'{stars.input.name}' => 'stars',
|
||||
'{stars.input.class}' => '',
|
||||
'{stars.input.value}' => $inputValues['stars'],
|
||||
'{stars.stars}' => $stars, // just stars icons
|
||||
'{stars.input}' => $starsInput, // just stars <input>
|
||||
'{stars.markup}' => $starsMarkup, // both stars <input> and stars icons
|
||||
'{honeypot.wrap.class}' => $classes['honeypot'],
|
||||
'{honeypot.label}' => $honeypotLabel,
|
||||
'{honeypot.input.name}' => $honeypot,
|
||||
'{honeypot.input.class}' => '',
|
||||
'{honeypot.input.value}' => $honeypotValue,
|
||||
'{text.wrap.class}' => $classes['text'],
|
||||
'{text.label}' => $labels['text'],
|
||||
'{text.input.name}' => 'text',
|
||||
'{text.input.class}' => $classes['textInput'],
|
||||
'{text.input.rows}' => $attrs['rows'],
|
||||
'{text.input.cols}' => $attrs['cols'],
|
||||
'{text.input.value}' => $inputValues['text'],
|
||||
'{submit.wrap.class}' => $classes['submit'],
|
||||
'{submit.label}' => $labels['submit'],
|
||||
'{submit.input.name}' => "{$id}_submit",
|
||||
'{submit.input.class}' => $classes['submitButton'],
|
||||
'{submit.input.value}' => 1,
|
||||
'{notify.wrap.class}' => $this->options['classes']['notify'],
|
||||
'{notify.label}' => $labels['notify'],
|
||||
'{notify.label.class}' => $this->options['classes']['label'],
|
||||
'{notify.label.span.class}' => $this->options['classes']['labelSpan'],
|
||||
'{notify.input.label.class}' => $this->options['classes']['radioLabel'],
|
||||
'{notify.input.class}' => $this->options['classes']['radioInput'],
|
||||
'{notify.input.name}' => 'notify',
|
||||
'{notify.input.off.value}' => '0',
|
||||
'{notify.input.off.label}' => $labels['notifyOff'],
|
||||
'{notify.input.replies.value}' => '2',
|
||||
'{notify.input.replies.label}' => $labels['notifyReplies'],
|
||||
'{notify.input.all.value}' => '4',
|
||||
'{notify.input.all.label}' => $labels['notifyAll'],
|
||||
);
|
||||
|
||||
$ifs = array(
|
||||
'website' => $field->useWebsite,
|
||||
'stars' => $useStars,
|
||||
'honeypot' => $useHoneypot,
|
||||
'notify' => $field->useNotify,
|
||||
'notify.replies' => $field->depth > 0,
|
||||
'notify.all' => $field->useNotify == Comment::flagNotifyAll,
|
||||
);
|
||||
|
||||
$out = $this->getFormMarkup();
|
||||
|
||||
foreach($ifs as $name => $value) {
|
||||
$out = $this->applyIf($name, $value, $out);
|
||||
}
|
||||
|
||||
$out = str_replace(array_keys($placeholders), array_values($placeholders), $out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
@@ -28,6 +28,8 @@ interface CommentListInterface {
|
||||
|
||||
/**
|
||||
* CommentList provides the default implementation of the CommentListInterface interface.
|
||||
*
|
||||
* @method void renderItemReady(Comment $comment, $depth)
|
||||
*
|
||||
*/
|
||||
class CommentList extends Wire implements CommentListInterface {
|
||||
@@ -66,11 +68,32 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
'usePermalink' => false, // @todo
|
||||
'useVotes' => 0,
|
||||
'useStars' => 0,
|
||||
'useRepliesLink' => false,
|
||||
'upvoteFormat' => '↑{cnt}',
|
||||
'downvoteFormat' => '↓{cnt}',
|
||||
'depth' => 0,
|
||||
'replyLabel' => 'Reply',
|
||||
);
|
||||
'repliesLabelOne' => '1 Reply',
|
||||
'repliesLabelMulti' => '%d Replies',
|
||||
);
|
||||
|
||||
/**
|
||||
* Other placeholders to populate
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $placeholders = array(
|
||||
// 'foo' => 'Bar' // replaces "{foo}" with "Bar"
|
||||
);
|
||||
|
||||
/**
|
||||
* Number of replies indexed by comment ID, populated by getReplies()
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $numReplies = array();
|
||||
|
||||
/**
|
||||
* Construct the CommentList
|
||||
@@ -84,6 +107,8 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
$h3 = $this->_('h3'); // Headline tag
|
||||
$this->options['headline'] = "<$h3>" . $this->_('Comments') . "</$h3>"; // Header text
|
||||
$this->options['replyLabel'] = $this->_('Reply');
|
||||
$this->options['repliesLabelOne'] = $this->_('1 Reply');
|
||||
$this->options['repliesLabelMulti'] = $this->_('%d Replies');
|
||||
|
||||
if(empty($options['commentHeader'])) {
|
||||
if(empty($options['dateFormat'])) {
|
||||
@@ -146,12 +171,50 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
$replies = array();
|
||||
foreach($this->comments as $c) {
|
||||
if($c->parent_id != $commentID) continue;
|
||||
if(!$admin && $c->status != Comment::statusApproved) continue;
|
||||
if(!$admin && $c->status < Comment::statusApproved) continue;
|
||||
$replies[] = $c;
|
||||
}
|
||||
$this->numReplies[$commentID] = count($replies);
|
||||
return $replies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get classes to use with comment list
|
||||
*
|
||||
* @param int $parent_id
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
protected function getCommentListClasses($parent_id) {
|
||||
$classes = array("CommentList");
|
||||
if($this->options['depth'] > 0) {
|
||||
$classes[] = "CommentListThread";
|
||||
} else {
|
||||
$classes[] = "CommentListNormal";
|
||||
}
|
||||
if($this->options['useGravatar']) $classes[] = "CommentListHasGravatar";
|
||||
if($parent_id) $classes[] = "CommentListReplies";
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get classes to use with comment item
|
||||
*
|
||||
* @param Comment $comment
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
protected function getCommentItemClasses(Comment $comment) {
|
||||
$classes = array('CommentListItem');
|
||||
if($this->options['depth'] > 0 && $comment->numChildren() > 0) $classes[] = 'CommentHasReplies';
|
||||
if($comment->status == Comment::statusPending) {
|
||||
$classes[] = 'CommentStatusPending';
|
||||
} else if($comment->status == Comment::statusSpam) {
|
||||
$classes[] = 'CommentStatusSpam';
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendering of comments for API demonstration and testing purposes (or feel free to use for production if suitable)
|
||||
*
|
||||
@@ -161,22 +224,40 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
*/
|
||||
public function render() {
|
||||
$out = $this->renderList(0);
|
||||
if($out) $out = "\n" . $this->options['headline'] . $out;
|
||||
if($out && $this->options['headline']) $out = "\n" . $this->options['headline'] . $out;
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render comment list
|
||||
*
|
||||
* @param int $parent_id
|
||||
* @param int $depth
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderList($parent_id = 0, $depth = 0) {
|
||||
|
||||
$out = $parent_id ? '' : $this->renderCheckActions();
|
||||
$comments = $this->options['depth'] > 0 ? $this->getReplies($parent_id) : $this->comments;
|
||||
|
||||
if(!count($comments)) return $out;
|
||||
foreach($comments as $comment) $out .= $this->renderItem($comment, $depth);
|
||||
|
||||
foreach($comments as $comment) {
|
||||
$out .= $this->renderItem($comment, $depth);
|
||||
}
|
||||
|
||||
if(!$out) return '';
|
||||
$class = "CommentList";
|
||||
if($this->options['depth'] > 0) $class .= " CommentListThread";
|
||||
else $class .= " CommentListNormal";
|
||||
if($this->options['useGravatar']) $class .= " CommentListHasGravatar";
|
||||
if($parent_id) $class .= " CommentListReplies";
|
||||
$out = "<ul class='$class'>$out\n</ul><!--/CommentList-->";
|
||||
|
||||
$class = implode(' ', $this->getCommentListClasses($parent_id));
|
||||
$attrs = "class='$class'";
|
||||
|
||||
if($parent_id) {
|
||||
$attrs .= " id='CommentList$parent_id'";
|
||||
if($this->options['useRepliesLink']) $attrs .= " hidden";
|
||||
}
|
||||
|
||||
$out = "<ul $attrs>$out\n</ul><!--/CommentList-->";
|
||||
|
||||
return $out;
|
||||
}
|
||||
@@ -191,7 +272,8 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
*
|
||||
*/
|
||||
protected function populatePlaceholders(Comment $comment, $out, $placeholders = array()) {
|
||||
|
||||
|
||||
$placeholders = array_merge($this->placeholders, $placeholders);
|
||||
if(empty($out) || strpos($out, '{') === false) return $out;
|
||||
|
||||
$removals = array(" href=''", ' href=""');
|
||||
@@ -325,10 +407,19 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
}
|
||||
|
||||
if($this->options['depth'] > 0 && $depth < $this->options['depth']) {
|
||||
$numReplies = isset($this->numReplies[$comment->id]) ? $this->numReplies[$comment->id] : 0;
|
||||
if($replies && $numReplies && $this->options['useRepliesLink']) {
|
||||
$repliesLabel = ($numReplies == 1 ? $this->options['repliesLabelOne'] : $this->options['repliesLabelMulti']);
|
||||
if(strpos($repliesLabel, '%d') !== false) $repliesLabel = sprintf($repliesLabel, $numReplies);
|
||||
$repliesLink = "<a class='CommentActionReplies' href='#CommentList{$comment->id}'>$repliesLabel</a>";
|
||||
} else {
|
||||
$repliesLink = '';
|
||||
}
|
||||
$out .=
|
||||
"\n\t\t<div class='CommentFooter'>" . $footer .
|
||||
"\n\t\t\t<p class='CommentAction'>" .
|
||||
"\n\t\t\t\t<a class='CommentActionReply' data-comment-id='$comment->id' href='#Comment{$comment->id}'>" . $this->options['replyLabel'] . "</a> " .
|
||||
($repliesLink ? "\n\t\t\t\t$repliesLink" : "") .
|
||||
($permalink ? "\n\t\t\t\t$permalink" : "") .
|
||||
"\n\t\t\t</p>" .
|
||||
"\n\t\t</div>";
|
||||
@@ -444,5 +535,39 @@ class CommentList extends Wire implements CommentListInterface {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set placeholders that will be populated by populatePlaceholders() method
|
||||
*
|
||||
* @param string|array|false $name Specify placeholder name to get or set, array of placeholders to set, false to unset all, omit to get all
|
||||
* @param string|bool $value Specify placeholder value to set or boolean false to unset, or omit when getting
|
||||
* @return string|array
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function placeholders($name = '', $value = null) {
|
||||
if(is_array($name)) {
|
||||
// set multiple
|
||||
foreach($name as $k => $v) {
|
||||
$this->placeholders[trim($k, '{}')] = $v;
|
||||
}
|
||||
$value = $name;
|
||||
} else if(empty($name)) {
|
||||
// return all
|
||||
$value = $this->placeholders;
|
||||
} else if($value === null) {
|
||||
// get one
|
||||
$value = isset($this->placeholders[$name]) ? $this->placeholders[$name] : null;
|
||||
} else if($value === false) {
|
||||
// unset one
|
||||
$value = isset($this->placeholders[$name]) ? $this->placeholders[$name] : null;
|
||||
unset($this->placeholders[$name]);
|
||||
} else {
|
||||
// set one
|
||||
$name = trim($name, '{}');
|
||||
$this->placeholders[$name] = $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
248
wire/modules/Fieldtype/FieldtypeComments/CommentListCustom.php
Normal file
248
wire/modules/Fieldtype/FieldtypeComments/CommentListCustom.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php namespace ProcessWire;
|
||||
|
||||
/**
|
||||
* ProcessWire CommentListCustom
|
||||
*
|
||||
* Manages custom CommentList implementations where you specify your own markup
|
||||
*
|
||||
* ~~~~~~
|
||||
* $list = $page->comments->getCommentList([
|
||||
* 'className' => 'CommentListCustom',
|
||||
* ]);
|
||||
*
|
||||
* $list->setMarkup([
|
||||
* 'noticeMessage' => "<div id='{id}' class='uk-alert {class}'>{message}</div>",
|
||||
* 'noticeSuccessClass' => 'uk-alert-success',
|
||||
* 'noticeErrorClass' => 'uk-alert-danger',
|
||||
* 'list' => "<ul id='my-comments-list' class='{class}'>{comments}</ul>",
|
||||
* // and so on for any other $markup properties
|
||||
* ]);
|
||||
*
|
||||
* echo $list->render();
|
||||
* ~~~~~~
|
||||
*
|
||||
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer
|
||||
* https://processwire.com
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
class CommentListCustom extends CommentList {
|
||||
|
||||
protected $markup = array(
|
||||
'gravatar' => "<img class='CommentGravatar' src='{src}' alt='{alt}' />",
|
||||
'website' => "<a href='{href}' rel='nofollow' target='_blank'>{cite}</a>",
|
||||
'permalink' => "<a class='CommentActionPermalink' href='{href}'>{label}</a>",
|
||||
'reply' => "<a class='CommentActionReply' data-comment-id='{id}' href='#Comment{id}'>{label}</a>",
|
||||
'list' => "<ul class='{class}'>{comments}</ul><!--/CommentList-->",
|
||||
'sublist' => "<ul id='CommentList{id}' class='{class}'>{comments}</ul><!--/CommentList-->",
|
||||
'replies' => "<a href='#CommentList{id}' class='CommentActionReplies'>{label}</a>",
|
||||
'item' => "
|
||||
<li id='Comment{id}' class='{class}' data-comment='{id}'>
|
||||
{gravatar}
|
||||
<p class='CommentHeader'>
|
||||
<span class='CommentCite'>{cite}</span>
|
||||
<small class='CommentCreated'>{created}</small>
|
||||
{stars}
|
||||
{votes}
|
||||
</p>
|
||||
<div class='CommentText'>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
<div class='CommentFooter'>
|
||||
<p class='CommentAction'>
|
||||
{reply}
|
||||
{permalink}
|
||||
</p>
|
||||
</div>
|
||||
{replies}
|
||||
{sublist}
|
||||
</li>
|
||||
",
|
||||
'subitem' => '', // blank means use same markup as item
|
||||
'noticeMessage' => "<p id='{id}' class='{class}'><strong>{message}</strong></p>",
|
||||
'noticeLink' => "<a href='{href}'>{label}</a>",
|
||||
'noticeSuccessClass' => 'success',
|
||||
'noticeErrorClass' => 'error',
|
||||
);
|
||||
|
||||
/**
|
||||
* Get markup property or all markup
|
||||
*
|
||||
* @param string $property Specify property or omit to get all
|
||||
* @return array|mixed|null
|
||||
*
|
||||
*/
|
||||
public function getMarkup($property = '') {
|
||||
if($property) return isset($this->markup[$property]) ? $this->markup[$property] : null;
|
||||
return $this->markup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set markup
|
||||
*
|
||||
* Set any or all of the following markup properties via associative array:
|
||||
*
|
||||
* list, sublist, item, subitem, gravtar, website, permalink, reply,
|
||||
* noticeMessage, noticeLink, noticeSuccessClass, noticeErrorClass
|
||||
*
|
||||
* @param array $markup
|
||||
*
|
||||
*/
|
||||
public function setMarkup(array $markup) {
|
||||
$this->markup = array_merge($this->markup, $markup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set markup properties
|
||||
*
|
||||
* @param string|array $key Property to get or set, or array of properties to set
|
||||
* @param null|mixed $value Specify only if setting individual property
|
||||
* @return mixed
|
||||
*
|
||||
*/
|
||||
public function markup($key = '', $value = null) {
|
||||
if(empty($key)) {
|
||||
// return all
|
||||
$value = $this->markup;
|
||||
} else if(is_array($key)) {
|
||||
// set multi
|
||||
$this->setMarkup($key);
|
||||
$value = $this->markup;
|
||||
} else if($value !== null) {
|
||||
// set single
|
||||
$this->markup[$key] = $value;
|
||||
} else {
|
||||
// get single
|
||||
$value = isset($this->markup[$key]) ? $this->markup[$key] : null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render comment list
|
||||
*
|
||||
* @param int $parent_id
|
||||
* @param int $depth
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function renderList($parent_id = 0, $depth = 0) {
|
||||
$out = $parent_id ? '' : $this->renderCheckActions();
|
||||
$comments = $this->options['depth'] > 0 ? $this->getReplies($parent_id) : $this->comments;
|
||||
foreach($comments as $comment) {
|
||||
$out .= $this->renderItem($comment, $depth);
|
||||
}
|
||||
if($out) {
|
||||
$class = implode(' ', $this->getCommentListClasses($parent_id));
|
||||
$listMarkup = ($depth ? $this->markup['sublist'] : $this->markup['list']);
|
||||
if($depth && $this->options['useRepliesLink']) {
|
||||
$listMarkup = str_replace(" class=", " hidden class=", $listMarkup);
|
||||
}
|
||||
$out = str_replace(
|
||||
array('{id}', '{class}', '{comments}'),
|
||||
array($parent_id, $class, $out),
|
||||
$listMarkup
|
||||
);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the comment
|
||||
*
|
||||
* @param Comment $comment
|
||||
* @param int $depth Default=0
|
||||
* @return string
|
||||
* @see CommentArray::render()
|
||||
*
|
||||
*/
|
||||
protected function ___renderItem(Comment $comment, $depth = 0) {
|
||||
|
||||
$text = $comment->getFormatted('text');
|
||||
$cite = $comment->getFormatted('cite');
|
||||
$created = wireDate($this->options['dateFormat'], $comment->created);
|
||||
$classes = $this->getCommentItemClasses($comment);
|
||||
$gravatar = '';
|
||||
$permalink = '';
|
||||
$reply = '';
|
||||
$replies = '';
|
||||
|
||||
if($this->options['useGravatar']) {
|
||||
$imgUrl = $comment->gravatar($this->options['useGravatar'], $this->options['useGravatarImageset']);
|
||||
if($imgUrl) {
|
||||
$gravatar = str_replace(array('{src}', '{alt}'), array($imgUrl, $cite), $this->markup['gravatar']);
|
||||
}
|
||||
}
|
||||
|
||||
if($comment->website) {
|
||||
$website = $comment->getFormatted('website');
|
||||
$cite = str_replace(array('{href}', '{cite}'), array($website, $cite), $this->markup['website']);
|
||||
}
|
||||
|
||||
$sublist = $this->options['depth'] > 0 ? $this->renderList($comment->id, $depth+1) : '';
|
||||
|
||||
if($this->options['usePermalink']) {
|
||||
$permalink = $comment->getPage()->httpUrl;
|
||||
$urlSegmentStr = $this->wire('input')->urlSegmentStr;
|
||||
if($urlSegmentStr) $permalink .= rtrim($permalink, '/') . $urlSegmentStr . '/';
|
||||
$permalink .= '#Comment' . $comment->id;
|
||||
$label = $this->_('Permalink');
|
||||
$permalink = str_replace(array('{href}', '{label}'), array($permalink, $label), $this->markup['permalink']);
|
||||
}
|
||||
|
||||
if($this->options['depth'] > 0 && $depth < $this->options['depth']) {
|
||||
$label = $this->_('Reply');
|
||||
$reply = str_replace(array('{id}', '{label}'), array($comment->id, $label), $this->markup['reply']);
|
||||
}
|
||||
|
||||
if($this->options['useRepliesLink']) {
|
||||
$numReplies = isset($this->numReplies[$comment->id]) ? $this->numReplies[$comment->id] : 0; // must be after renderList()
|
||||
if($numReplies) {
|
||||
$repliesLabel = sprintf($this->_n('%d Reply', '%d Replies', $numReplies), $numReplies);
|
||||
$replies = str_replace(
|
||||
array('{id}', '{href}', '{label}'),
|
||||
array($comment->id, "#CommentList$comment->id", $repliesLabel),
|
||||
$this->markup['replies']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$placeholders = array(
|
||||
'id' => $comment->id,
|
||||
'cite' => $cite,
|
||||
'text' => $text,
|
||||
'created' => $created,
|
||||
'gravatar' => $gravatar,
|
||||
'stars' => $this->options['useStars'] ? $this->renderStars($comment) : '',
|
||||
'votes' => $this->options['useVotes'] ? $this->renderVotes($comment) : '',
|
||||
'class' => implode(' ', $classes),
|
||||
'permalink' => $permalink,
|
||||
'reply' => $reply,
|
||||
'replies' => $replies,
|
||||
'sublist' => $sublist,
|
||||
);
|
||||
|
||||
$itemMarkup = $depth ? $this->markup['subitem'] : $this->markup['item'];
|
||||
if(empty($itemMarkup)) $itemMarkup = $this->markup['item'];
|
||||
$out = $this->populatePlaceholders($comment, $itemMarkup, $placeholders);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function renderCheckActions(array $options = array()) {
|
||||
|
||||
$defaults = array(
|
||||
'messageIdAttr' => 'CommentApprovalMessage',
|
||||
'messageMarkup' => $this->markup['noticeMessage'],
|
||||
'linkMarkup' => $this->markup['noticeLink'],
|
||||
'successClass' => $this->markup['noticeSuccessClass'],
|
||||
'errorClass' => $this->markup['noticeErrorClass'],
|
||||
);
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
|
||||
return parent::renderCheckActions($options);
|
||||
}
|
||||
|
||||
}
|
@@ -12,6 +12,7 @@
|
||||
* @property string $star Default star output (can be overridden with HTML)
|
||||
* @property string $starOn Optionally use this star/HTML for ON state
|
||||
* @property string $starOff Optionally use this star/HTML for OFF state
|
||||
* @property string $starClass Class for stars whether on or off
|
||||
* @property string $starOnClass Class for active/on stars
|
||||
* @property string $starOffClass Class for inactive/off stars
|
||||
* @property string $starPartialClass Class for partial (half lit star)
|
||||
@@ -32,6 +33,7 @@ class CommentStars extends WireData {
|
||||
'star' => '★', // this may be overridden with HTML (icons for instance)
|
||||
'starOn' => '', // optionally use separate star for ON...
|
||||
'starOff' => '', // ...and OFF
|
||||
'starClass' => 'CommentStar', // applies to both on and off
|
||||
'starOnClass' => 'CommentStarOn', // class assigned to active/on stars
|
||||
'starOffClass' => 'CommentStarOff', // class assigned to inactive/off stars
|
||||
'starPartialClass' => 'CommentStarPartial',
|
||||
@@ -88,7 +90,7 @@ class CommentStars extends WireData {
|
||||
if(!$this->starOff) $this->starOff = $this->star;
|
||||
|
||||
if($allowInput) {
|
||||
$attr = " data-onclass='$this->starOnClass'";
|
||||
$attr = "data-onclass='$this->starOnClass' data-offclass='$this->starOffClass'";
|
||||
if($this->starOn !== $this->starOff) $attr .= " " .
|
||||
"data-on='" . htmlspecialchars($this->starOn, ENT_QUOTES, 'UTF-8') . "' " .
|
||||
"data-off='" . htmlspecialchars($this->starOff, ENT_QUOTES, 'UTF-8') . "'";
|
||||
@@ -96,28 +98,32 @@ class CommentStars extends WireData {
|
||||
$attr = '';
|
||||
}
|
||||
|
||||
$out = "<span class='$class'$attr>";
|
||||
$out = "<span class='$class' $attr>";
|
||||
|
||||
for($n = 1; $n <= $this->numStars; $n++) {
|
||||
if($n <= $stars) {
|
||||
// full star on
|
||||
$star = $this->starOn;
|
||||
$attr = " class='$this->starOnClass'";
|
||||
$attr = "class='$this->starClass $this->starOnClass'";
|
||||
} else if(is_float($stars) && $n > $stars && $n < $stars + 1) {
|
||||
// partial star on
|
||||
$star = "<span class='$this->starOffClass'>$this->starOff</span>";
|
||||
$star = "<span class='$this->starClass $this->starOffClass'>$this->starOff</span>";
|
||||
if(preg_match('/^\d+[^\d]+(\d+)$/', round($stars, 2), $matches)) {
|
||||
if(strlen($matches[1]) == 1) $matches[1] .= '0';
|
||||
$star .= "<span class='$this->starOnClass' style='width:$matches[1]%;'>$this->starOn</span>";
|
||||
$star .= "<span class='$this->starClass $this->starOnClass' style='width:$matches[1]%;'>$this->starOn</span>";
|
||||
}
|
||||
$attr = " class='$this->starPartialClass'";
|
||||
$attr = "class='$this->starClass $this->starPartialClass'";
|
||||
} else {
|
||||
// star off
|
||||
$attr = " class='$this->starOffClass'";
|
||||
$attr = "class='$this->starClass $this->starOffClass'";
|
||||
$star = $this->starOff;
|
||||
// $attr = "";
|
||||
}
|
||||
$out .= "<span$attr data-value='$n'>$star</span>";
|
||||
if($allowInput) {
|
||||
// add tooltip indicating number of stars
|
||||
$starChars = "$n: " . str_repeat('★ ', $n);
|
||||
$attr .= " title='$starChars'";
|
||||
}
|
||||
$out .= "<span $attr data-value='$n'>$star</span>";
|
||||
}
|
||||
|
||||
$out .= "</span>";
|
||||
|
@@ -28,10 +28,13 @@ require_once($dirname . "/CommentField.php");
|
||||
*
|
||||
* A field that stores user posted comments for a single Page.
|
||||
*
|
||||
* @method mixed updateComment(Page $page, $field, Comment $comment, array $properties)
|
||||
* @method commentDeleted(Page $page, Field $field, Comment $comment, $notes = '') #pw-hooker
|
||||
* @method commentApproved(Page $page, Field $field, Comment $comment, $notes = '') #pw-hooker
|
||||
* @method commentUnapproved(Page $page, Field $field, Comment $comment, $notes = '') #pw-hooker
|
||||
* @method mixed updateComment(Page $page, $field, Comment $comment, array $properties) Update an existing comment
|
||||
* @method void commentDeleted(Page $page, Field $field, Comment $comment, $notes = '') #pw-hooker
|
||||
* @method void commentApproved(Page $page, Field $field, Comment $comment, $notes = '') #pw-hooker
|
||||
* @method void commentUnapproved(Page $page, Field $field, Comment $comment, $notes = '') #pw-hooker
|
||||
* @method void commentAddReady(Page $page, Field $field, Comment $comment) #pw-hooker
|
||||
* @method void commentAdded(Page $page, Field $field, Comment $comment) #pw-hooker
|
||||
*
|
||||
*
|
||||
*/
|
||||
class FieldtypeComments extends FieldtypeMulti {
|
||||
@@ -212,8 +215,7 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
'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) {
|
||||
@@ -359,7 +361,6 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
$no = $this->wire(new CommentNotifications($page, $field));
|
||||
$no->sendAdminNotificationEmail($comment);
|
||||
$this->commentMaintenance($field);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,7 +662,10 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
// populate newly added comment ID to Comment object
|
||||
$value['id'] = $database->lastInsertId();
|
||||
foreach($allItems as $item) {
|
||||
if(!$item->id && $item->code === $value['code']) $item->id = $value['id'];
|
||||
if(!$item->id && $item->code === $value['code']) {
|
||||
$item->id = $value['id'];
|
||||
$this->commentAdded($page, $field, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
$error = '';
|
||||
@@ -1391,6 +1395,36 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
return $this->makeComment($page, $field, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent comments for given Comment
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Field $field
|
||||
* @param Comment $comment
|
||||
* @return CommentArray
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function getCommentParents(Page $page, Field $field, Comment $comment) {
|
||||
|
||||
/** @var CommentArray $parents */
|
||||
$parents = $this->wire(new CommentArray());
|
||||
$parents->setPage($page);
|
||||
$parents->setField($field);
|
||||
|
||||
if(!$comment->parent_id) return $parents;
|
||||
|
||||
$parent = $comment;
|
||||
|
||||
do {
|
||||
$parent = $this->getCommentByID($page, $field, $parent->parent_id);
|
||||
if(!$parent || !$parent->id || $parents->has($parent)) break;
|
||||
$parents->add($parent);
|
||||
} while($parent->parent_id > 0);
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of data, convert it to a Comment object
|
||||
*
|
||||
@@ -1466,6 +1500,117 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new comment
|
||||
*
|
||||
* Requires a new Comment object with no id, that has all its required field populated and validated
|
||||
* and is ready to add. Note that the `sort` property is assigned automatically if not specified in Comment.
|
||||
*
|
||||
* The primary reason to use this method is if you want to add a comment without loading all the other
|
||||
* comments on a given Page.
|
||||
*
|
||||
* Note: if you are hooking this method, comments can also be added by the savePageField method
|
||||
*
|
||||
* @param Page $page Page where comments field exists
|
||||
* @param Field $field Comments field
|
||||
* @param Comment $comment New comment to add
|
||||
* @param bool $send Send comment for automatic approval filtering and email notifications?
|
||||
* - `true` if comment was just submitted now from user input and filtering should apply, notifications sent, etc.
|
||||
* - `false` if you are importing comments and NO filtering should be applied, NO notifications sent, etc.
|
||||
* @return bool Returns true on success, false on fail
|
||||
* @throws WireException if given a Comment object that is not ready to add
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function addComment(Page $page, Field $field, Comment $comment, $send) {
|
||||
|
||||
if($comment->id) {
|
||||
throw new WireException("Comment already has an ID (not a new comment)");
|
||||
} else if($comment->getPage() && $comment->getPage()->id != $page->id) {
|
||||
throw new WireException("Comment is already assigned to a different Page");
|
||||
} else if($comment->getField() && $comment->getField()->id != $field->id) {
|
||||
throw new WireException("Comment is already assigned to a different Field");
|
||||
}
|
||||
|
||||
/** @var WireDatabasePDO $database */
|
||||
$database = $this->wire('database');
|
||||
$table = $database->escapeTable($field->table);
|
||||
$sets = array('pages_id=:pages_id');
|
||||
$binds = array('pages_id' => (int) $page->id);
|
||||
$nextSort = $this->getMaxSort($page, $field) + 1;
|
||||
$comment->quiet(!$send);
|
||||
|
||||
if(!$comment->sort) $comment->sort = $nextSort;
|
||||
|
||||
$this->commentAddReady($page, $field, $comment);
|
||||
|
||||
/** @var CommentArray $commentArray */
|
||||
$commentArray = $this->wire(new CommentArray());
|
||||
$commentArray->setPage($page);
|
||||
$commentArray->setField($field);
|
||||
$commentArray->add($comment);
|
||||
|
||||
// convert CommentArray to sleep value array
|
||||
$value = $this->sleepValue($page, $field, $commentArray);
|
||||
if(!count($value)) return false;
|
||||
|
||||
// use just first item, since only 1 comment
|
||||
$value = $value[0];
|
||||
|
||||
// determine all values to set
|
||||
foreach(array_keys($value) as $key) {
|
||||
if($key == 'id') continue;
|
||||
|
||||
$v = $value[$key];
|
||||
$col = $database->escapeCol($key);
|
||||
|
||||
if($v === null && ($key === 'code' || $key === 'subcode')) {
|
||||
// 'code' and 'subcode' allow null
|
||||
$sets[] = "$col=NULL";
|
||||
} else {
|
||||
$sets[] = "$col=:$col";
|
||||
$binds[$col] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$tries = 0;
|
||||
$maxTries = 10;
|
||||
|
||||
do {
|
||||
try {
|
||||
$error = '';
|
||||
$sql = "INSERT INTO `$table` SET " . implode(', ', $sets);
|
||||
$query = $database->prepare($sql);
|
||||
foreach($binds as $k => $v) {
|
||||
$query->bindValue(":$k", $v);
|
||||
}
|
||||
$result = $query->execute();
|
||||
if($result) $comment->id = $database->lastInsertId();
|
||||
} catch(\Exception $e) {
|
||||
$result = false;
|
||||
$error = $e->getMessage();
|
||||
if($e->getCode() == 23000) {
|
||||
// colliding index, maybe race condition, see if increasing sort solves it
|
||||
if($binds['sort'] == $nextSort) $nextSort++;
|
||||
$binds['sort'] = $nextSort;
|
||||
}
|
||||
}
|
||||
} while(!$result && ++$tries < $maxTries);
|
||||
|
||||
if($result && $comment->id) {
|
||||
// the $page->data() call used so load not triggered, only returns comments if already loaded
|
||||
$comments = $page->data($field->name);
|
||||
// add to loaded CommentArray for page, but only if it is already loaded in memory
|
||||
if($comments && $comments instanceof CommentArray) $comments->add($comment);
|
||||
$this->commentAdded($page, $field, $comment);
|
||||
} else {
|
||||
$error = "Error adding new comment on page $page field $field->name for $comment->email: $error";
|
||||
$this->error($error, Notice::superuser | Notice::log);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a given comment
|
||||
*
|
||||
@@ -1506,6 +1651,98 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max 'sort' value for comments field
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Field $field
|
||||
* @return int
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
protected function getMaxSort(Page $page, Field $field) {
|
||||
$database = $this->wire('database');
|
||||
$table = $database->escapeTable($field->getTable());
|
||||
$sql = "SELECT MAX(sort) FROM `$table` WHERE pages_id=:pages_id";
|
||||
$query = $database->prepare($sql);
|
||||
$query->bindValue(':pages_id', $page->id, \PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
$value = (int) $query->fetchColumn();
|
||||
$query->closeCursor();
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of comments for Page, optionally limited by specific $options
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Field $field
|
||||
* @param array $options
|
||||
* - `status` (int): Specify Comment::status* constant to include only this status
|
||||
* - `minStatus` (int): Specify Comment::status* constant to include only comments with at least this status
|
||||
* - `maxStatus` (int): Specify Comment::status* constant or include only comments up to this status
|
||||
* - `parent` (int|Comment): Specify parent comment ID, 0 for root-only, or omit for no parent limitation
|
||||
* - `minCreated` (int): Minimum created unix timestamp
|
||||
* - `maxCreated` (int): Maximum created unix timestamp
|
||||
* - `stars` (int): Number of stars to match (1-5)
|
||||
* - `minStars` (int): Minimum number of stars to match (1-5)
|
||||
* - `maxStars` (int): Maximum number of stars to match (1-5)
|
||||
* @return int
|
||||
* @since 3.0.153
|
||||
*
|
||||
*/
|
||||
public function getNumComments(Page $page, Field $field, array $options = array()) {
|
||||
|
||||
$defaults = array(
|
||||
'status' => null,
|
||||
'minStatus' => null,
|
||||
'maxStatus' => null,
|
||||
'parent' => null,
|
||||
'minCreated' => null,
|
||||
'maxCreated' => null,
|
||||
'stars' => null,
|
||||
'minStars' => null,
|
||||
'maxStars' => null,
|
||||
);
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
$database = $this->wire('database');
|
||||
$table = $database->escapeTable($field->getTable());
|
||||
$sql = "SELECT COUNT(*) FROM `$table` WHERE pages_id=:page ";
|
||||
$binds = array(':page' => $page->id);
|
||||
|
||||
$cols = array(
|
||||
'status' => 'status=:status',
|
||||
'minStatus' => 'status>=:minStatus',
|
||||
'maxStatus' => 'status<=:maxStatus',
|
||||
'parent' => 'parent_id=:parent',
|
||||
'minCreated' => 'created>=:minCreated',
|
||||
'maxCreated' => 'created<=:maxCreated',
|
||||
'stars' => 'stars=:stars',
|
||||
'minStars' => 'stars>=:minStars',
|
||||
'maxStars' => 'stars<=:maxStars',
|
||||
);
|
||||
|
||||
foreach($cols as $name => $stmt) {
|
||||
$value = $options[$name];
|
||||
if($value === null) continue;
|
||||
if($name === 'parent' && $value instanceof Comment) $value = $value->id;
|
||||
$sql .= "AND $stmt ";
|
||||
$binds[":$name"] = (int) $value;
|
||||
}
|
||||
|
||||
$query = $database->prepare($sql);
|
||||
foreach($binds as $key => $value) {
|
||||
$query->bindValue($key, $value, \PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
$numComments = (int) $query->fetchColumn();
|
||||
$query->closeCursor();
|
||||
|
||||
return $numComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook called after comment has been deleted
|
||||
*
|
||||
@@ -1586,6 +1823,32 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
public function ___commentUnapproved(Page $page, Field $field, Comment $comment, $notes = '') {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook called when new comment about to be added to DB
|
||||
*
|
||||
* #pw-hooker
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Field $field
|
||||
* @param Comment $comment
|
||||
*
|
||||
*/
|
||||
public function ___commentAddReady(Page $page, Field $field, Comment $comment) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook called when new comment has successfully been added to DB
|
||||
*
|
||||
* #pw-hooker
|
||||
*
|
||||
* @param Page $page
|
||||
* @param Field $field
|
||||
* @param Comment $comment
|
||||
*
|
||||
*/
|
||||
public function ___commentAdded(Page $page, Field $field, Comment $comment) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a vote to the current comment from the current user/IP
|
||||
*
|
||||
@@ -1950,6 +2213,6 @@ class FieldtypeComments extends FieldtypeMulti {
|
||||
$options['test'] = true;
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -37,10 +37,8 @@ function CommentFormGetCookie(name) {
|
||||
/**
|
||||
* Handle the 5-star rating system for comments
|
||||
*
|
||||
* @param jQuery $
|
||||
*
|
||||
*/
|
||||
function CommentFormStars($) {
|
||||
function CommentFormStars() {
|
||||
|
||||
function decodeEntities(encodedString) {
|
||||
if(encodedString.indexOf('&') == -1) return encodedString;
|
||||
@@ -49,36 +47,43 @@ function CommentFormStars($) {
|
||||
return textarea.value;
|
||||
}
|
||||
|
||||
// stars
|
||||
function setStars($parent, star) {
|
||||
var onClass = $parent.attr('data-onclass');
|
||||
var onClass, offClass, starOn, starOff;
|
||||
|
||||
onClass = $parent.attr('data-onclass');
|
||||
offClass = $parent.attr('data-offclass');
|
||||
starOn = $parent.attr('data-on');
|
||||
|
||||
var starOn = $parent.attr('data-on');
|
||||
if(typeof onClass == "undefined") onClass = 'CommentStarOff';
|
||||
if(typeof offClass == "undefined") offClass = 'CommentStarOff';
|
||||
|
||||
if(typeof starOn != "undefined") {
|
||||
var starOff = $parent.attr('data-off');
|
||||
starOn = decodeEntities(starOn);
|
||||
starOff = decodeEntities(starOff);
|
||||
} else {
|
||||
var starOn = '';
|
||||
var starOff = '';
|
||||
starOn = '';
|
||||
starOff = '';
|
||||
}
|
||||
|
||||
$parent.children('span').each(function() {
|
||||
var val = parseInt($(this).attr('data-value'));
|
||||
var val = parseInt(jQuery(this).attr('data-value'));
|
||||
if(val <= star) {
|
||||
if(starOn.length) $(this).html(starOn);
|
||||
$(this).addClass(onClass);
|
||||
if(starOn.length) jQuery(this).html(starOn);
|
||||
jQuery(this).addClass(onClass).removeClass(offClass);
|
||||
} else {
|
||||
if(starOff.length) $(this).html(starOff);
|
||||
$(this).removeClass(onClass);
|
||||
if(starOff.length) jQuery(this).html(starOff);
|
||||
jQuery(this).removeClass(onClass).addClass(offClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(".CommentFormStars input").hide();
|
||||
jQuery('.CommentFormStars input').hide();
|
||||
|
||||
$(document).on('click', ".CommentStarsInput span", function(e) {
|
||||
var value = parseInt($(this).attr('data-value'));
|
||||
var $parent = $(this).parent();
|
||||
|
||||
jQuery(document).on('click', '.CommentStarsInput span', function(e) {
|
||||
var value = parseInt(jQuery(this).attr('data-value'));
|
||||
var $parent = jQuery(this).parent();
|
||||
var $input = $parent.prev('input');
|
||||
$input.val(value).attr('value', value); // redundancy intended, val() not working on webkit mobile for some reason
|
||||
setStars($parent, value);
|
||||
@@ -86,6 +91,9 @@ function CommentFormStars($) {
|
||||
return false;
|
||||
});
|
||||
|
||||
/*
|
||||
// removed because on newer jQuery versions (?) adding a mouseover event here
|
||||
// is preventing the above click event from firing
|
||||
$(document).on('mouseover', ".CommentStarsInput span", function(e) {
|
||||
var $parent = $(this).parent();
|
||||
var value = parseInt($(this).attr('data-value'));
|
||||
@@ -96,95 +104,177 @@ function CommentFormStars($) {
|
||||
var value = parseInt($input.val());
|
||||
setStars($parent, value);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for reply button click in threaded comments
|
||||
*
|
||||
*/
|
||||
function CommentActionReplyClick() {
|
||||
|
||||
var $this = jQuery(this);
|
||||
var $item = $this.closest('.CommentListItem');
|
||||
var $form = $this.parent().next('form.CommentForm');
|
||||
var commentID = $item.attr('data-comment');
|
||||
|
||||
if($form.length == 0) {
|
||||
// if form is not parent's next item, see if we can
|
||||
// find it wthin the .CommentListItem somewhere
|
||||
$form = $item.find('.CommentForm' + commentID);
|
||||
}
|
||||
|
||||
if($form.length == 0) {
|
||||
// form does not yet exist for this reply
|
||||
// clone the main CommentForm
|
||||
$form = jQuery('#CommentForm form').clone().removeAttr('id');
|
||||
$form.addClass('CommentForm' + commentID);
|
||||
$form.hide().find('.CommentFormParent').val($(this).attr('data-comment-id'));
|
||||
var $formPlaceholder = $item.find('form:eq(0)');
|
||||
if($formPlaceholder.length) {
|
||||
// use existing <form></form> placed in there as optional target for reply form
|
||||
$formPlaceholder.replaceWith($form);
|
||||
} else {
|
||||
$this.parent().after($form);
|
||||
}
|
||||
if($form.is('form[hidden]')) {
|
||||
$form.removeAttr('hidden');
|
||||
} else if(!$form.is(':visible')) {
|
||||
$form.slideDown();
|
||||
}
|
||||
} else if(!$form.is(':visible')) {
|
||||
$form.slideDown();
|
||||
} else {
|
||||
$form.slideUp();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler when the 'n Replies' link is clicked
|
||||
*
|
||||
*/
|
||||
function CommentActionRepliesClick() {
|
||||
|
||||
var $this = jQuery(this);
|
||||
var href = $this.attr('href');
|
||||
var $list = $this.closest('.CommentListItem').find(href);
|
||||
|
||||
if($list.is(':hidden')) {
|
||||
$list.removeAttr('hidden');
|
||||
} else {
|
||||
$list.attr('hidden', true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for comment submit button click
|
||||
*
|
||||
* Remember values when comment form submitted and save in cookie
|
||||
* so that other comment forms can be populated with same info to
|
||||
* save them a step.
|
||||
*
|
||||
*/
|
||||
function CommentFormSubmitClick() {
|
||||
|
||||
var $this = jQuery(this);
|
||||
var $form = $this.closest('form.CommentForm');
|
||||
var $wrapStars = $form.find('.CommentFormStarsRequired');
|
||||
|
||||
if($wrapStars.length) {
|
||||
var stars = parseInt($wrapStars.find('input').val());
|
||||
if(!stars) {
|
||||
alert($wrapStars.attr('data-note'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var cite = $form.find(".CommentFormCite input").val();
|
||||
var email = $form.find(".CommentFormEmail input").val();
|
||||
var $website = $form.find(".CommentFormWebsite input");
|
||||
var website = $website.length > 0 ? $website.val() : '';
|
||||
var $notify = $form.find(".CommentFormNotify :checked");
|
||||
var notify = $notify.length > 0 ? $notify.val() : '';
|
||||
|
||||
if(cite.indexOf('|') > -1) cite = '';
|
||||
if(email.indexOf('|') > -1) email = '';
|
||||
if(website.indexOf('|') > -1) website = '';
|
||||
|
||||
var cookieValue = cite + '|' + email + '|' + website + '|' + notify;
|
||||
|
||||
CommentFormSetCookie('CommentForm', cookieValue, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate cookie values to comment form
|
||||
*
|
||||
*/
|
||||
function CommentFormCookies() {
|
||||
|
||||
var $form = jQuery('form.CommentForm');
|
||||
if(!$form.length) return;
|
||||
|
||||
var cookieValue = CommentFormGetCookie('CommentForm');
|
||||
if(cookieValue.length < 1) return;
|
||||
|
||||
var values = cookieValue.split('|');
|
||||
|
||||
$form.find(".CommentFormCite input").val(values[0]);
|
||||
$form.find(".CommentFormEmail input").val(values[1]);
|
||||
$form.find(".CommentFormWebsite input").val(values[2]);
|
||||
$form.find(".CommentFormNotify :input[value='" + values[3] + "']").attr('checked', 'checked');
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage upvotes and downvotes
|
||||
*
|
||||
*/
|
||||
function CommentFormUpvoteDownvote() {
|
||||
var voting = false;
|
||||
jQuery('.CommentActionUpvote, .CommentActionDownvote').on('click', function() {
|
||||
if(voting) return false;
|
||||
voting = true;
|
||||
var $a = jQuery(this);
|
||||
jQuery.getJSON($a.attr('data-url'), function(data) {
|
||||
//console.log(data);
|
||||
if('success' in data) {
|
||||
if(data.success) {
|
||||
var $votes = $a.closest('.CommentVotes');
|
||||
$votes.find('.CommentUpvoteCnt').text(data.upvotes);
|
||||
$votes.find('.CommentDownvoteCnt').text(data.downvotes);
|
||||
$a.addClass('CommentVoted');
|
||||
} else if(data.message.length) {
|
||||
alert(data.message);
|
||||
}
|
||||
} else {
|
||||
// let the link passthru to handle via regular pageload rather than ajax
|
||||
voting = false;
|
||||
return true;
|
||||
}
|
||||
voting = false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function CommentFormInit() {
|
||||
jQuery('.CommentActionReply').on('click', CommentActionReplyClick);
|
||||
jQuery('.CommentActionReplies').on('click', CommentActionRepliesClick);
|
||||
jQuery('.CommentFormSubmit button').on('click', CommentFormSubmitClick);
|
||||
|
||||
CommentFormCookies();
|
||||
CommentFormUpvoteDownvote();
|
||||
|
||||
if(jQuery('.CommentStarsInput').length) CommentFormStars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize comments form
|
||||
*
|
||||
*/
|
||||
jQuery(document).ready(function($) {
|
||||
$(".CommentActionReply").click(function() {
|
||||
var $this = $(this);
|
||||
var $form = $this.parent().next('form');
|
||||
if($form.length == 0) {
|
||||
$form = $("#CommentForm form").clone().removeAttr('id');
|
||||
$form.hide().find(".CommentFormParent").val($(this).attr('data-comment-id'));
|
||||
$(this).parent().after($form);
|
||||
$form.slideDown();
|
||||
} else if(!$form.is(":visible")) {
|
||||
$form.slideDown();
|
||||
} else {
|
||||
$form.slideUp();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// remember values when comment form submitted
|
||||
$(".CommentFormSubmit button").on('click', function() {
|
||||
var $this = $(this);
|
||||
var $form = $this.closest('form.CommentForm');
|
||||
|
||||
var $wrapStars = $form.find(".CommentFormStarsRequired");
|
||||
if($wrapStars.length) {
|
||||
var stars = parseInt($wrapStars.find("input").val());
|
||||
if(!stars) {
|
||||
alert($wrapStars.attr('data-note'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var cite = $form.find(".CommentFormCite input").val();
|
||||
var email = $form.find(".CommentFormEmail input").val();
|
||||
var $website = $form.find(".CommentFormWebsite input");
|
||||
var website = $website.length > 0 ? $website.val() : '';
|
||||
var $notify = $form.find(".CommentFormNotify :checked");
|
||||
var notify = $notify.length > 0 ? $notify.val() : '';
|
||||
if(cite.indexOf('|') > -1) cite = '';
|
||||
if(email.indexOf('|') > -1) email = '';
|
||||
if(website.indexOf('|') > -1) website = '';
|
||||
var cookieValue = cite + '|' + email + '|' + website + '|' + notify;
|
||||
CommentFormSetCookie('CommentForm', cookieValue, 0);
|
||||
});
|
||||
|
||||
// populate comment form values if they exist in cookie
|
||||
var cookieValue = CommentFormGetCookie('CommentForm');
|
||||
if(cookieValue.length > 0) {
|
||||
var values = cookieValue.split('|');
|
||||
var $form = $("form.CommentForm");
|
||||
$form.find(".CommentFormCite input").val(values[0]);
|
||||
$form.find(".CommentFormEmail input").val(values[1]);
|
||||
$form.find(".CommentFormWebsite input").val(values[2]);
|
||||
$form.find(".CommentFormNotify :input[value='" + values[3] + "']").attr('checked', 'checked');
|
||||
}
|
||||
|
||||
// upvoting and downvoting
|
||||
var voting = false;
|
||||
$(".CommentActionUpvote, .CommentActionDownvote").on('click', function() {
|
||||
if(voting) return false;
|
||||
voting = true;
|
||||
var $a = $(this);
|
||||
$.getJSON($a.attr('data-url'), function(data) {
|
||||
//console.log(data);
|
||||
if('success' in data) {
|
||||
if(data.success) {
|
||||
var $votes = $a.closest('.CommentVotes');
|
||||
$votes.find('.CommentUpvoteCnt').text(data.upvotes);
|
||||
$votes.find('.CommentDownvoteCnt').text(data.downvotes);
|
||||
$a.addClass('CommentVoted');
|
||||
} else if(data.message.length) {
|
||||
alert(data.message);
|
||||
}
|
||||
} else {
|
||||
// let the link passthru to handle via regular pageload rather than ajax
|
||||
voting = false;
|
||||
return true;
|
||||
}
|
||||
voting = false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
if($(".CommentStarsInput").length) {
|
||||
CommentFormStars($);
|
||||
}
|
||||
jQuery(document).ready(function() {
|
||||
CommentFormInit();
|
||||
});
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user