I created a rule to grant users User Points when they added a comment on a node. However, I'd like to grant them these points only once. That means: they don't get more points when they react a second or a third time on the same node, but they will be granted points nevertheless after commenting on another node.
How can I do that?
Rules must have:
Events: Before saving a comment
Conditions: this code
global $user;
$node =array();
$query = db_select('comment', 'c');
$query->fields('c', array('nid'));
$query->condition('c.uid', $user->uid);
$result = $query->execute();
while ($res = $result->fetchAll()) {
foreach ($res AS $re) {
$node[] = $re->nid;
}
}
if(!in_array($comment->nid, $node)){
return TRUE;
}
Actions: grant users User Points
=============================
Conditions: Execute custom PHP code
or create custom Conditions
/**
* Implements hook_rules_condition_info().
*/
function MYMODULE_rules_condition_info() {
$conditions = array();
$conditions['MYCONDITION'] = array(
'label' => t('lable'),
'parameter' => array(
'user' => array(
'type' => 'user',
'label' => t('User'),
),
),
'group' => t('Custom'),
'callbacks' => array(
'execute' => 'MYMODULE_rules_condition_MYCONDITION',
),
);
return $conditions;
}
function MYMODULE_rules_condition_MYCONDITION($user) {
$node =array();
$query = db_select('comment', 'c');
$query->fields('c', array('nid'));
$query->condition('c.uid', $user->uid);
$result = $query->execute();
while ($res = $result->fetchAll()) {
foreach ($res AS $re) {
$node[] = $re->nid;
}
}
if(!in_array($comment->nid, $node)){
return TRUE;
}
}
I solved my problem with the Flags module: I created the flags called 'Commented on this node' & 'First reaction'* and rule called 'Commented on a node'. These are my rule's settings:
EVENTS: After saving a new comment
CONDITIONS:
Content is of type Parameter: Content: [comment:node], Content types:
Article
NOT Node is flagged Parameter: Flag: Commented on this node, node:
[comment:node], User on whose behalf to check: [comment:author]
ACTIONS:
Grant points to a user Parameter: User: [comment:author],
Points: 2, Points category: Reaction, Entity: [comment:node],
Description: New reaction, Operation: Add,
Display: false, Moderate: Automatically approved
Flag a Node Parameter: Flag: Commented on a node, Node:
[comment:node], User on whose behalf to flag: [comment:author], Skip
permission check: false
Flag a Comment: Parameter: Flag: First reaction, Comment: [comment],
User on whose behalf to flag: [comment:author], Skip permission
check: false
So, every time a user adds a comment to a certain node for the first time, the node is flagged as 'Commented on a node', the reaction is flagged as 'First reaction' and the user who added the comment is rewarded 2 points.
**I use the 'First comment' flag in a related question.*
Related
i wanna to do user auth thru db. I've created db User with some fields and inserted some records. username=admin, password=admin; username=demo, password=demo.
Now i've applied the gii to make a model 'User' out of my table, saved the model both in models and in components folders.
In UserIdentity.php i do the model retrieval , yet the dump shows the retrieved object is NULL! Interesting, I've done the same retrieval in my custom controller and it rather worked!
public function authenticate()
{
/* $users=array(
// username => password
'demo'=>'demo',
'admin'=>'admin',
); */
$users = User::model()->findByAttributes(array('username'=>$this->username));
var_dump($users);
// $username=strtolower($this->username);
// $users=User::model()->find('LOWER(username)=?',array($username));
if(!isset($users[$this->username]))
$this->errorCode=self::ERROR_USERNAME_INVALID;
elseif($users[$this->username]->password !== $this->password)
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else
$this->errorCode=self::ERROR_NONE;
return !$this->errorCode;
}
What's wrong, do i need to mention this User model/component in main.php attribute 'user' configuration? and how?
Also, the User class obviously extends the CActiveRecord, rather then CWebUser. Is it normal?
class User extends CActiveRecord
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
/**
* #return string the associated database table name
*/
public function tableName()
{
return 'user';
}
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('username, password, created', 'required'),
array('role, ban', 'numerical', 'integerOnly'=>true),
array('username, password', 'length', 'max'=>255),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, username, password, created, role, ban', 'safe', 'on'=>'search'),
);
}
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
);
}
public function attributeLabels()
{
return array(
'id' => 'ID',
'username' => 'Name',
'password' => 'Password',
'created' => 'Created',
'role' => 'Role',
'ban' => 'Ban',
);
}
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id);
$criteria->compare('username',$this->username,true);
$criteria->compare('password',$this->password,true);
$criteria->compare('created',$this->created,true);
$criteria->compare('role',$this->role);
$criteria->compare('ban',$this->ban);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
The problem is that, when I try to login with this kind of db plugging (sure without dump line), I get always Incorrect username or password.
LoginForm.php:
private $_identity;
public function rules()
{
return array(
// username and password are required
array('username, password', 'required'),
// rememberMe needs to be a boolean
array('rememberMe', 'boolean'),
// password needs to be authenticated
array('password', 'authenticate'),
);
}
public function attributeLabels()
{
return array(
'rememberMe'=>'Remember me next time',
);
}
public function authenticate($attribute,$params)
{
if(!$this->hasErrors())
{
$this->_identity=new UserIdentity($this->username,$this->password);
if(!$this->_identity->authenticate())
$this->addError('password','Incorrect username or password.');
}
}
public function login()
{
if($this->_identity===null)
{
$this->_identity=new UserIdentity($this->username,$this->password);
$this->_identity->authenticate();
}
if($this->_identity->errorCode===UserIdentity::ERROR_NONE)
{
$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days
Yii::app()->user->login($this->_identity,$duration);
return true;
}
else
return false;
}
}
Here is the code from in the other controller for dumping 'admin' record from the model User:
$username = 'admin';
$users = User::model()->findByAttributes(array('username'=>$username));
foreach ($users->attributes as $key=>$value){
echo $key . ' => '. $value. '</br>';
}
Yii::app()->end();
the result is excellent:
id => 1
username => admin
password => admin
created => 2013-08-16 00:00:00
role => 1
ban => 0
The problem seems to be in the following section:
if(!isset($users[$this->username]))
$this->errorCode=self::ERROR_USERNAME_INVALID;
elseif($users[$this->username]->password !== $this->password)
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else
$this->errorCode=self::ERROR_NONE;
return !$this->errorCode;
Try changing that to:
if($users == null))
$this->errorCode=self::ERROR_USERNAME_INVALID;
elseif($users->password !== $this->password)
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else
$this->errorCode=self::ERROR_NONE;
return !$this->errorCode;
The $users variable is an object and cannot be used as an array ($users[$this->username]) as was possible in the original script where the $users array contained the login information. When you changed it to authenticate through the db, the variable now contains the db row corresponding to the username entered in the login form (if any)...
How can I create a custom Rule-Action which will successfully save a value as a replacement pattern for use in the other actions?
I got some very good help here on retrieving Product-Display information from a Product-Order.
As I said, the linked answer helped a great deal but the returned path data for the Product-Display comes back in the http://www.mysite/node/77 format. However, I really just need the numeric value only so I can load the node by performing a Fetch entity by id action supplying the numeric value and publishing the Product-Display node etc.
So, I implemented a custom action which will take the Product-Display URL(node/77) and return 77.
I copied the Fetch entity by id code and modified it so my returned numeric value can be saved and used in other Actions. The code is below:
function my_custom_action_info(){
$actions['publish_product_display_node'] = array(
'label' => t('Fetch product-display id'),
'parameter' => array(
'type' => array(
'type' => 'uri',
'label' => t('My Action'),
'options list' => 'rules_entity_action_type_options2',
'description' => t('Specifies the product-display url.'),
),
),
'provides' => array(
'entity_fetched' => array('type' => 'integer', 'label' => t('Fetched entity')),
),
'group' => t('Entities'),
'access callback' => 'rules_entity_action_access',
);
return $actions;
}
function publish_product_display_node($path = null){
$parts = explode('node/', $path);
return $parts[1];
}
function rules_entity_action_type_options2($element, $name = NULL) {
// We allow calling this function with just the element name too. That way
// we ease manual re-use.
$name = is_object($element) ? $element->getElementName() : $element;
return ($name == 'entity_create') ? rules_entity_type_options2('create') : rules_entity_type_options2();
}
function rules_entity_type_options2($key = NULL) {
$info = entity_get_info();
$types = array();
foreach ($info as $type => $entity_info) {
if (empty($entity_info['configuration']) && empty($entity_info['exportable'])) {
if (!isset($key) || entity_type_supports($type, $key)) {
$types[$type] = $entity_info['label'];
}
}
}
return $types;
}
function rules_action_entity_createfetch_access2(RulesAbstractPlugin $element) {
$op = $element->getElementName() == 'entity_create' ? 'create' : 'view';
return entity_access($op, $element->settings['type']);
}
As I said I copied the modified code so I don't claim to thoroughly understand all the functions aside from publish_product_display_node.
My code modifications work as far as setting the Product-Display URL token as the argument and also setting an entity variable label(Display NID) and value(display_nid).
The problem is when I check display_nid in newly created actions, the value is empty.
I need help figuring out the how to successfully save my entity value so I can use it in following Actions.
in the function publish_product_display_node, can you verify that you don't need to be returning $parts[0], instead of $[parts[1]?
It's just that Drupal paths are frequently in the form 'node/7' or 'taxonomy/term/6', and if you explode with 'node/' as the separator, you'd only have a single value which would start at index 0 for nodes...
So, just wondering if that would solve your issue...
In Drupal 7, I would like to set up a rule that sends an email to all users in an ORGANIC GROUPS Role, based on an action. I know how to get the action, I know how to do the loop, I know how to send the email.
I cannot, for the life of me, figure out how to get a list of group members with group role "X".
PS - I've reviewed this link: http://www.sthlmconnection.se/en/blog/rules-based-notifications-organic-groups , and it's for D6.
GAAH ARGH! (And much hair pulling later), here's the answer:
Custom Module (myutil.module) - .module file empty, .info file with the same sparse info required for any other module.
Add file myutil.rules.inc with following code:
/**
* #file
* Rules code: actions, conditions and events.
*/
/**
* Implements hook_rules_action_info().
*/
function myutil_rules_action_info() {
$actions = array(
'myutil_action_send_email_to_group_editors' => array(
'label' => t('Get group editors from group audience'),
'group' => t('My Utilities'),
'configurable' => TRUE,
'parameter' => array(
'group_content' => array(
'type' => 'entity',
'label' => t('Group content'),
'description' => t('The group content determining the group audience.'),
),
),
'provides' => array(
'group_editors' => array('type' => 'list<user>', 'label' => t('List of group editors')),
),
'base' => 'myutil_rules_get_editors',
),
);
return $actions;
}
function myutil_rules_get_editors($group_content) {
if (!isset($group_content->og_membership)) {
// Not a group content.
return;
}
$members = array();
foreach ($group_content->og_membership->value() as $og_membership) {
// Get the group members the group content belongs to.
$current_members = db_select('og_membership', 'om');
$current_members->join('og_users_roles', 'ogur', 'om.etid = ogur.uid');
$current_members->fields('om', array('etid'));
$current_members->condition('om.gid', $og_membership->gid);
$current_members->condition('om.entity_type', 'user');
// FOR THIS LINE, YOU'LL NEED TO KNOW THE ROLE ID FROM THE `og_role` TABLE
$current_members->condition('ogur.rid', 14);
$result = $current_members->execute();
while ($res = $result->fetchAssoc()) {
$members[] = $res['etid'];
}
}
// Remove duplicate items.
$members = array_keys(array_flip($members));
return array('group_editors' => $members);
}
Enable the module as you would any other module. Clear cache. Go back to Rules and enjoy.
I've submitted a patch to a similar issue for OG https://drupal.org/node/1859698#comment-8719475 which should allow you to do this in Rules without the need for a custom module or needing to know the role id.
Once you apply the patch, you can use the "Get group members from group audience" action, and now filter by the "Membership States" and "Group Roles". Then add a loop to go through the list and use the "Send mail" action to send an email to each member.
I have a problem with Kohana database.
Sometimes I have error
ErrorException [ Recoverable Error ]
Argument 1 passed to Kohana_Database_Query_Builder_Select::compile() must be an instance of Database, string given, called in /srv/sites/mysite/www/modules/database/classes/kohana/database/query.php on line 230 and defined
It's happens because in Database:$instances containts string "dances", but should contain array database configurations.
This my config:
<?php defined('SYSPATH') OR die('No direct access allowed.');
return array
(
'default' => array
(
'type' => 'MySQL',
'connection' => array(
'hostname' => 'localhost',
'database' => 'chat',
'username' => 'root',
'password' => 'root',
'persistent' => FALSE,
),
'table_prefix' => '',
'charset' => 'utf8',
'caching' => FALSE,
'profiling' => TRUE
)
);
Maybe somebody had problem like this or could help me?
Any query to DB causes error.
Like this:
Jelly::factory('user', $user->id())
or this:
DB::select('value')->from('storage')->where('name', '=', 'salt')->limit(1)->execute();
or this:
ORM::factory('node')->where('type', '=', 'page-homepage')->find();
I don't know why this error happen. I checked all methods are called and I have not found any mistakes.
I solved this problem by write method instance in class Database
public static function instance($name = NULL, array $config = NULL)
{
if ($name === NULL)
{
// Use the default instance name
$name = Database::$default;
}
if ( ! is_array(Database::$instances))
{
Database::$instances = array();
}
if ( ! isset(Database::$instances[$name]))
{
if ($config === NULL)
{
// Load the configuration for this database
$config = Kohana::$config->load('database')->get($name);
}
if ( ! isset($config['type']))
{
throw new Kohana_Exception('Database type not defined in :name configuration',
array(':name' => $name));
}
// Set the driver class name
$driver = 'Database_'.ucfirst($config['type']);
// Create the database connection instance
new $driver($name, $config);
}
return Database::$instances[$name];
}
I add condition As you can see
if ( ! is_array(Database::$instances))
{
Database::$instances = array();
}
I don't like this, but I have no choise.
My wild guess would be that you seem to be overwriting the compile, quote, quote_column, quote_identifier, quote_table or execute method from Kohana_Database in your code or simply calling ->execute('dances') should trigger this error.
the compile method shouldn't be called directly, it's an internal function that is called from execute()
Any of these functions take $db as the first parameter. Don't pass anything, since you want to use the database set in your config file, without trying to set it manually in your query builder.
I'm using a simple registration form for CakePHP.
This is my edit view:
<h2>Add User</h2>
<?php echo $form->create('User');
echo $form->input('first_name');
echo $form->input('last_name');
echo $form->input('email');
echo $form->input('address');
echo $form->input('phone');
echo $form->end('Edit');?>
These are my validation rules in User.php
class User extends AppModel {
var $name = 'User';
var $validate = array(
(... more rules here ...)
'password' => array('rule' => array('confirmPassword', 'password'),
'message' => 'Passwords do not match'),
'password_confirm' => array('rule' => 'alphanumeric',
'required' => true)
);
It's a stupid question, but when I use the edit form, the saving of the data fails, because I require a password input in the last rule. That rule was added for the register page, of course.
I've found billions of ways to bypass this problem (disable validation on saving, selecting which fields to save, ...), but I wonder what the "correct" CakePHP way of doing this is? Seems like such a simple problem, but I can't find any documentation on how it has to be done the clean way.
To make my question more clear: I don't want an edit user page where passwords can be changed.
the Cake way is to simply set the validation rule to apply only on create, allowing you to freely edit your data without having to validate the field again.
var $validate = array(
'fieldName' => array(
'rule' => 'ruleName'
'required' => true,
'allowEmpty' => false,
'on' => 'create', // or update <-- here
'message' => 'Your Error Message'
)
);
And of course, if you don't want the password field edited here, just remove the appropriate input
I use multiple validation sets:
In your model, or app_model, override the validates function with this:
function validates($options = array()) {
// copy the data over from a custom var, otherwise
$actionSet = 'validate' . Inflector::camelize(Router::getParam('action'));
if (isset($this->validationSet)) {
$temp = $this->validate;
$param = 'validate' . $this->validationSet;
$this->validate = $this->{$param};
} elseif (isset($this->{$actionSet})) {
$temp = $this->validate;
$param = $actionSet;
$this->validate = $this->{$param};
}
$errors = $this->invalidFields($options);
// copy it back
if (isset($temp)) {
$this->validate = $temp;
unset($this->validationSet);
}
if (is_array($errors)) {
return count($errors) === 0;
}
return $errors;
}
In your controller:
class UsersController extends AppController {
function forgot() {
$this->User->set($this->data);
$this->User->validationSet = 'forgotpassword';
if ($this->User->validates()) {
// send email to reset password and show success message
}
}
}
And finally back in your model, build a validation array, you can have your default validation array, then add a supplemental array such as
class User extends AppModel {
var $validate = array( ... );
var $validateForgotpassword = array( ... );
}
More details here:
http://snook.ca/archives/cakephp/multiple_validation_sets_cakephp
(note the code sample in the article has a bug in it, so be sure to change $param = 'validate' . $validationSet; to $param = 'validate' . $this->validationSet;)
remove your 'required' => true, it is not doing what you think its doing.
'required' => true means that the field should be present in any data that is saved. you are not passing it.