updated core to 7.58 (right after the site was hacked)
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
name = NodeAPI example
|
||||
description = Demonstrates using the hook_node_* APIs (formerly hook_nodeapi) to alter a node from a different module.
|
||||
package = Example modules
|
||||
core = 7.x
|
||||
files[] = nodeapi_example.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-01-10
|
||||
version = "7.x-1.x-dev"
|
||||
core = "7.x"
|
||||
project = "examples"
|
||||
datestamp = "1484076787"
|
||||
|
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the nodeapi_example module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*
|
||||
* @ingroup nodeapi_example
|
||||
*/
|
||||
function nodeapi_example_schema() {
|
||||
$schema['nodeapi_example'] = array(
|
||||
'description' => 'Stores information of extended content.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'Node ID that the rating is applied to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'vid' => array(
|
||||
'description' => 'Revision ID, as we are tracking rating with node revisions',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'rating' => array(
|
||||
'description' => 'The rating of the node.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('vid'),
|
||||
'indexes' => array(
|
||||
'nid' => array('nid'),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*
|
||||
* We need to clean up our variables data when uninstalling our module.
|
||||
*
|
||||
* Our implementation of nodeapi_example_form_alter() automatically
|
||||
* creates a nodeapi_example_node_type_<contentType> variable for each node type
|
||||
* the user wants to rate.
|
||||
*
|
||||
* To delete our variables we call variable_del for our variables'
|
||||
* namespace, 'nodeapi_example_node_type_'. Note that an average module would
|
||||
* have known variables that it had created, and it could just delete those
|
||||
* explicitly. For example, see render_example_uninstall(). It's important
|
||||
* not to delete variables that might be owned by other modules, so normally
|
||||
* we would just explicitly delete a set of known variables.
|
||||
*
|
||||
* hook_uninstall() will only be called when uninstalling a module, not when
|
||||
* disabling a module. This allows our data to stay in the database if the user
|
||||
* only disables our module without uninstalling it.
|
||||
*
|
||||
* @ingroup nodeapi_example
|
||||
*/
|
||||
function nodeapi_example_uninstall() {
|
||||
// Simple DB query to get the names of our variables.
|
||||
$results = db_select('variable', 'v')
|
||||
->fields('v', array('name'))
|
||||
->condition('name', 'nodeapi_example_node_type_%', 'LIKE')
|
||||
->execute();
|
||||
// Loop through and delete each of our variables.
|
||||
foreach ($results as $result) {
|
||||
variable_del($result->name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module implementation for nodeapi_example module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup nodeapi_example Example: NodeAPI
|
||||
* @ingroup examples
|
||||
* @{
|
||||
* Example using NodeAPI.
|
||||
*
|
||||
* This is an example demonstrating how a module can be used to extend existing
|
||||
* node types.
|
||||
*
|
||||
* hook_nodeapi() has been replaced in Drupal 7 with a set of different hooks
|
||||
* providing the same or improved functionality. See the NodeAPI hooks list
|
||||
* at api.drupal.org (linked below).
|
||||
*
|
||||
* We will add the ability for each node to have a "rating," which will be a
|
||||
* number from one to five. The rating will be tracked using the revision
|
||||
* system also, so every node revision may have different rating values.
|
||||
*
|
||||
* @see node_api_hooks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*
|
||||
* By implementing this hook, we're able to modify any form. We'll only make
|
||||
* changes to two types: a node's content type configuration and edit forms.
|
||||
*
|
||||
* We need to have a way for administrators to indicate which content types
|
||||
* should have our rating field added. This is done by inserting radios in
|
||||
* the node's content type configuration page.
|
||||
*
|
||||
* Changes made by this hook will be shown when editing the settings of any
|
||||
* content type.
|
||||
*
|
||||
* Optionally, hook_form_FORM_ID_alter() could be used with the function name
|
||||
* nodeapi_example_form_node_type_form_alter
|
||||
*/
|
||||
function nodeapi_example_form_alter(&$form, $form_state, $form_id) {
|
||||
// First, check for the node type configuration form.
|
||||
if ($form_id == 'node_type_form') {
|
||||
// Alter the node type's configuration form to add our setting. We don't
|
||||
// need to worry about saving this value back to the variable, the form
|
||||
// we're altering will do it for us.
|
||||
$form['rating'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Rating settings'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'additional_settings',
|
||||
'#weight' => -1,
|
||||
);
|
||||
|
||||
$form['rating']['nodeapi_example_node_type'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('NodeAPI Example Rating'),
|
||||
'#default_value' => variable_get('nodeapi_example_node_type_' . $form['#node_type']->type, FALSE),
|
||||
'#options' => array(
|
||||
FALSE => t('Disabled'),
|
||||
TRUE => t('Enabled'),
|
||||
),
|
||||
'#description' => t('Should this node have a rating attached to it?'),
|
||||
);
|
||||
}
|
||||
// Here we check to see if the type and node field are set. If so, it could
|
||||
// be a node edit form.
|
||||
elseif (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) {
|
||||
// If the rating is enabled for this node type, we insert our control
|
||||
// into the form.
|
||||
$node = $form['#node'];
|
||||
if (variable_get('nodeapi_example_node_type_' . $form['type']['#value'], FALSE)) {
|
||||
$form['nodeapi_example_rating'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Rating'),
|
||||
'#default_value' => isset($node->nodeapi_example_rating) ? $node->nodeapi_example_rating : '',
|
||||
'#options' => array(0 => t('Unrated'), 1, 2, 3, 4, 5),
|
||||
'#required' => TRUE,
|
||||
'#weight' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_validate().
|
||||
*
|
||||
* Check that the rating attribute is set in the form submission, since the
|
||||
* field is required. If not, send error message.
|
||||
*/
|
||||
function nodeapi_example_node_validate($node, $form) {
|
||||
if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
|
||||
if (isset($node->nodeapi_example_rating) && !$node->nodeapi_example_rating) {
|
||||
form_set_error('nodeapi_example_rating', t('You must rate this content.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_load().
|
||||
*
|
||||
* Loads the rating information if available for any of the nodes in the
|
||||
* argument list.
|
||||
*/
|
||||
function nodeapi_example_node_load($nodes, $types) {
|
||||
// We can use $types to figure out if we need to process any of these nodes.
|
||||
$our_types = array();
|
||||
foreach ($types as $type) {
|
||||
if (variable_get('nodeapi_example_node_type_' . $type, FALSE)) {
|
||||
$our_types[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
// Now $our_types contains all the types from $types that we want
|
||||
// to deal with. If it's empty, we can safely return.
|
||||
if (!count($our_types)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we need to make a list of revisions based on $our_types
|
||||
foreach ($nodes as $node) {
|
||||
// We are using the revision id instead of node id.
|
||||
if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
|
||||
$vids[] = $node->vid;
|
||||
}
|
||||
}
|
||||
// Check if we should load rating for any of the nodes.
|
||||
if (!isset($vids) || !count($vids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When we read, we don't care about the node->nid; we look for the right
|
||||
// revision ID (node->vid).
|
||||
$result = db_select('nodeapi_example', 'e')
|
||||
->fields('e', array('nid', 'vid', 'rating'))
|
||||
->where('e.vid IN (:vids)', array(':vids' => $vids))
|
||||
->execute();
|
||||
|
||||
foreach ($result as $record) {
|
||||
$nodes[$record->nid]->nodeapi_example_rating = $record->rating;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_insert().
|
||||
*
|
||||
* As a new node is being inserted into the database, we need to do our own
|
||||
* database inserts.
|
||||
*/
|
||||
function nodeapi_example_node_insert($node) {
|
||||
if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
|
||||
// Notice that we are ignoring any revision information using $node->nid
|
||||
db_insert('nodeapi_example')
|
||||
->fields(array(
|
||||
'nid' => $node->nid,
|
||||
'vid' => $node->vid,
|
||||
'rating' => $node->nodeapi_example_rating,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_delete().
|
||||
*
|
||||
* When a node is deleted, we need to remove all related records from our table,
|
||||
* including all revisions. For the delete operations we use node->nid.
|
||||
*/
|
||||
function nodeapi_example_node_delete($node) {
|
||||
// Notice that we're deleting even if the content type has no rating enabled.
|
||||
db_delete('nodeapi_example')
|
||||
->condition('nid', $node->nid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_update().
|
||||
*
|
||||
* As an existing node is being updated in the database, we need to do our own
|
||||
* database updates.
|
||||
*
|
||||
* This hook is called when an existing node has been changed. We can't simply
|
||||
* update, since the node may not have a rating saved, thus no
|
||||
* database field. So we first check the database for a rating. If there is one,
|
||||
* we update it. Otherwise, we call nodeapi_example_node_insert() to create one.
|
||||
*/
|
||||
function nodeapi_example_node_update($node) {
|
||||
if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
|
||||
// Check first if this node has a saved rating.
|
||||
$rating = db_select('nodeapi_example', 'e')
|
||||
->fields('e', array(
|
||||
'rating',
|
||||
))
|
||||
->where('e.vid = (:vid)', array(':vid' => $node->vid))
|
||||
->execute()->fetchField();
|
||||
|
||||
if ($rating) {
|
||||
// Node has been rated before.
|
||||
db_update('nodeapi_example')
|
||||
->fields(array('rating' => $node->nodeapi_example_rating))
|
||||
->condition('vid', $node->vid)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
// Node was not previously rated, so insert a new rating in database.
|
||||
nodeapi_example_node_insert($node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_view().
|
||||
*
|
||||
* This is a typical implementation that simply runs the node text through
|
||||
* the output filters.
|
||||
*
|
||||
* Finally, we need to take care of displaying our rating when the node is
|
||||
* viewed. This operation is called after the node has already been prepared
|
||||
* into HTML and filtered as necessary, so we know we are dealing with an
|
||||
* HTML teaser and body. We will inject our additional information at the front
|
||||
* of the node copy.
|
||||
*
|
||||
* Using node API 'hook_node_view' is more appropriate than using a filter here,
|
||||
* because filters transform user-supplied content, whereas we are extending it
|
||||
* with additional information.
|
||||
*/
|
||||
function nodeapi_example_node_view($node, $build_mode = 'full') {
|
||||
if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
|
||||
// Make sure to set a rating, also for nodes saved previously and not yet
|
||||
// rated.
|
||||
$rating = isset($node->nodeapi_example_rating) ? $node->nodeapi_example_rating : 0;
|
||||
$node->content['nodeapi_example'] = array(
|
||||
'#markup' => theme('nodeapi_example_rating', array('rating' => $rating)),
|
||||
'#weight' => -1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*
|
||||
* This lets us tell Drupal about our theme functions and their arguments.
|
||||
*/
|
||||
function nodeapi_example_theme() {
|
||||
return array(
|
||||
'nodeapi_example_rating' => array(
|
||||
'variables' => array('rating' => NULL),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom theme function.
|
||||
*
|
||||
* By using this function to format our rating, themes can override this
|
||||
* presentation if they wish; for example, they could provide a star graphic
|
||||
* for the rating. We also wrap the default presentation in a CSS class that
|
||||
* is prefixed by the module name. This way, style sheets can modify the output
|
||||
* without requiring theme code.
|
||||
*/
|
||||
function theme_nodeapi_example_rating($variables) {
|
||||
$options = array(
|
||||
0 => t('Unrated'),
|
||||
1 => t('Poor'),
|
||||
2 => t('Needs improvement'),
|
||||
3 => t('Acceptable'),
|
||||
4 => t('Good'),
|
||||
5 => t('Excellent'),
|
||||
);
|
||||
$output = '<div class="nodeapi_example_rating">';
|
||||
$output .= t('Rating: %rating', array('%rating' => $options[(int) $variables['rating']]));
|
||||
$output .= '</div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup nodeapi_example".
|
||||
*/
|
222
sites/all/modules/examples/nodeapi_example/nodeapi_example.test
Normal file
222
sites/all/modules/examples/nodeapi_example/nodeapi_example.test
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test case for Testing the node API example module.
|
||||
*
|
||||
* This file contains the test cases to check if module is performing as
|
||||
* expected.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Functional tests for the NodeAPI Example module.
|
||||
*
|
||||
* @ingroup nodeapi_example
|
||||
*/
|
||||
class NodeApiExampleTestCase extends DrupalWebTestCase {
|
||||
/**
|
||||
* User object to perform site browsing
|
||||
* @var object
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Content type to attach the rating system
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node API example functionality',
|
||||
'description' => 'Demonstrate Node API hooks that allow altering a node. These are the former hook_nodeapi.',
|
||||
'group' => 'Examples',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables modules and create user with specific permissions.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp('nodeapi_example');
|
||||
|
||||
// Create admin user. This module has no access control, so we can use a
|
||||
// trusted user. Revision access and revert permissions are required too.
|
||||
$this->webUser = $this->drupalCreateUser(array(
|
||||
// Required to set revision checkbox.
|
||||
'administer nodes',
|
||||
'administer content types',
|
||||
'bypass node access',
|
||||
'view revisions',
|
||||
'revert revisions',
|
||||
));
|
||||
// Login the admin user.
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log user in, creates an example node, and uses the rating system.
|
||||
*/
|
||||
public function testNodeExampleBasic() {
|
||||
|
||||
// Login the user.
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Create custom content type.
|
||||
$content_type = $this->drupalCreateContentType();
|
||||
$type = $content_type->type;
|
||||
|
||||
// Go to edit the settings of this content type.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check if the new Rating options appear in the settings page.
|
||||
$this->assertText(t('NodeAPI Example Rating'), 'Rating options found in content type.');
|
||||
$this->assertFieldByName('nodeapi_example_node_type', 1, 'Rating is Disabled by default.');
|
||||
|
||||
// Disable the rating for this content type: 0 for Disabled, 1 for Enabled.
|
||||
$content_settings = array(
|
||||
'nodeapi_example_node_type' => 0,
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/' . $type, $content_settings, t('Save content type'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(' has been updated.', 'Settings modified successfully for content type.');
|
||||
|
||||
// Create an example node.
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$edit = array(
|
||||
"title" => $this->randomName(),
|
||||
);
|
||||
$this->drupalPost('node/add/' . $type, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check that the rating is not shown, as we have not yet enabled it.
|
||||
$this->assertNoRaw('Rating: <em>', 'Extended rating information is not shown.');
|
||||
|
||||
// Save current current url (we are viewing the new node).
|
||||
$node_url = $this->getUrl();
|
||||
|
||||
// Enable the rating for this content type: 0 for Disabled, 1 for Enabled.
|
||||
$content_settings = array(
|
||||
'nodeapi_example_node_type' => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/' . $type, $content_settings, t('Save content type'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(' has been updated.', 'Settings modified successfully for content type.');
|
||||
|
||||
// Check previously create node. It should be not rated.
|
||||
$this->drupalGet($node_url);
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(t('Rating: %rating', array('%rating' => t('Unrated'))), 'Content is not rated.');
|
||||
|
||||
// Rate the content, 4 is for "Good"
|
||||
$rate = array(
|
||||
'nodeapi_example_rating' => 4,
|
||||
);
|
||||
$this->drupalPost($node_url . '/edit', $rate, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check that content has been rated.
|
||||
$this->assertRaw(t('Rating: %rating', array('%rating' => t('Good'))), 'Content is successfully rated.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test revisions of ratings.
|
||||
*
|
||||
* Logs user in, creates an example node, and tests rating functionality with
|
||||
* a node using revisions.
|
||||
*/
|
||||
public function testNodeExampleRevision() {
|
||||
|
||||
// Login the user.
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Create custom content type.
|
||||
$content_type = $this->drupalCreateContentType();
|
||||
$type = $content_type->type;
|
||||
|
||||
// Go to edit the settings of this content type.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check if the new Rating options appear in the settings page.
|
||||
$this->assertText(t('NodeAPI Example Rating'), 'Rating options found in content type.');
|
||||
$this->assertFieldByName('nodeapi_example_node_type', 1, 'Rating is Disabled by default.');
|
||||
|
||||
// Disable the rating for this content type: 0 for Disabled, 1 for Enabled.
|
||||
$content_settings = array(
|
||||
'nodeapi_example_node_type' => 0,
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/' . $type, $content_settings, t('Save content type'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(' has been updated.', 'Settings modified successfully for content type.');
|
||||
|
||||
// Create an example node.
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$edit = array(
|
||||
"title" => $this->randomName(),
|
||||
);
|
||||
$this->drupalPost('node/add/' . $type, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check that the rating is not shown, as we have not yet enabled it.
|
||||
$this->assertNoRaw('Rating: <em>', 'Extended rating information is not shown.');
|
||||
|
||||
// Save current current url (we are viewing the new node).
|
||||
$node_url = $this->getUrl();
|
||||
|
||||
// Enable the rating for this content type: 0 for Disabled, 1 for Enabled.
|
||||
$content_settings = array(
|
||||
'nodeapi_example_node_type' => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/' . $type, $content_settings, t('Save content type'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(' has been updated.', 'Settings modified successfully for content type.');
|
||||
|
||||
// Check previously create node. It should be not rated.
|
||||
$this->drupalGet($node_url);
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(t('Rating: %rating', array('%rating' => t('Unrated'))), 'Content is not rated.');
|
||||
|
||||
// Rate the content, 4 is for "Good"
|
||||
$rate = array(
|
||||
'nodeapi_example_rating' => 4,
|
||||
);
|
||||
$this->drupalPost($node_url . '/edit', $rate, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check that content has been rated.
|
||||
$this->assertRaw(t('Rating: %rating', array('%rating' => t('Good'))), 'Content is successfully rated.');
|
||||
|
||||
// Rate the content to poor using a new revision, 1 is for "Poor"
|
||||
$rate = array(
|
||||
'nodeapi_example_rating' => 1,
|
||||
'revision' => 1,
|
||||
);
|
||||
$this->drupalPost($node_url . '/edit', $rate, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check that content has been rated.
|
||||
$this->assertRaw(t('Rating: %rating', array('%rating' => t('Poor'))), 'Content is successfully rated.');
|
||||
|
||||
// Now switch back to previous revision of the node.
|
||||
$this->drupalGet($node_url . '/revisions');
|
||||
// There is only a revision, so it must work just clicking the first link..
|
||||
$this->clickLink('revert');
|
||||
$revert_form = $this->getUrl();
|
||||
$this->drupalPost($revert_form, array(), t('Revert'));
|
||||
|
||||
// Go back to the node page.
|
||||
$this->drupalGet($node_url);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check that content has been rated.
|
||||
$this->assertRaw(t('Rating: %rating', array('%rating' => t('Good'))), 'Content rating matches reverted revision.');
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user