change node access with drupal module - drupal

I run drupal 6 and want to code this functionality:
after the user filled out the formular he can click an extra button, which will make the form readonly for him.
I thought about an hidden_field and an extra button, which after the user clicked the extra button set the hidden_field to a true state. When I load the node and the hidden_field has the true state, I want to set all the fields in the node to readonly. (hook_access, node_access?!?)
puh.. hope this all is not too confusing (because even I am now a little confused...)

Firstly you won't be able to stop someone who has access to the database or the root user from seeing the data (at least not without some encryption).
In terms of normal site operation this isn't too hard to do. The two hooks you need to invoke are hook_node_grants() and hook_node_access_records(), there is quite a good example, which also pretty much does what you want in the question, here.
Put simply hook_node_access_records() returns a structure detailing relms and ids which can perform actions on it, and hook_node_grants() returns the relms and ids for a user. If there is a match between the two then access (read or write) is granted.
This gives you a lot of flexibility. One example would be that people could only read nodes created by other people with the same star sign. In hook_node_grants you would examine the user object to get the starsign and return $grants['starsign'] = 2
When a node is saved hook_node_access_records would have to check the star sign of the user who created it do something like:
$grants = array();
$grants[] = array(
'realm' => 'starsign',
'gid' => $account->starsign,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
return $grants
That way if the relm and group id match the user will be given read access.

Related

Authorization Policy With Multiple Claims

I have created multiple authorization policies, each with 1 claim in it, doing a role check, like so:
options.AddPolicy("SuperAdminPolicy", policy => policy.RequireClaim(ClaimTypes.Role, "SuperAdmin"));
That all works fine.
However, I'm now at the point where I want to check 2 different types of claims, e.g. I want to make sure that the user has a specific role claim (As above), but I also want to check the value of a completely different claim (Such as first name). To clarify, I want to say something like " user must be in role 'x' and must have a first name claim value of 'bob'".
I can't quite figure out how to achieve this (And I'm sure it's probably quite straight forward).
Can someone point me in the right direction please?
Thanks.
We can actually chain the RequireClaim like this.
services.AddAuthorization(option => {
option.AddPolicy("SuperAdmin policy",
policy => policy.RequireClaim(ClaimType.Role,"SuperAdmin")
.RequireClaim(ClaimType.Name,"Bob"));
});
I did a little additional research on this post as I was looking for something very similar. I noticed there is a policy.RequireRole and policy.RequireUser in addition to RequireClaim. Thus, a policy can require a claim, role, user, or any combination.
Also you can add multiple arguments to the RequireClaim statement for Example If you want to accept multiple Roles:
options.AddPolicy("AdminPolicy", policy => policy.RequireClaim(ClaimTypes.Role, "SuperAdmin" , "Admin"));

How to let a user choose different Facebook permissions in the same page?

Suppose you want to build a webpage with Facebook PHP SDK, where you want to allow the user to select the information Facebook will return to the server. I've came with the following code to allow an user to either choose from allowing Facebook to send only the basic profile or else to also send the pages managed by this user.
session_start();
// Load the Facebook PHP SDK
require_once __DIR__ . '/facebook-sdk-v5/autoload.php';
define('APP_ID', 'xxxxxxxxxxx');
define('APP_SECRET', 'xxxxxxxxxxxxxxxxxxxx');
$fbProfile = new Facebook\Facebook([
'app_id' => APP_ID,
'app_secret' => APP_SECRET,
'default_graph_version' => 'v2.7'
]);
$fbPages = new Facebook\Facebook([
'app_id' => APP_ID,
'app_secret' => APP_SECRET,
'default_graph_version' => 'v2.7'
]);
$helperProfile = $fbProfile->getRedirectLoginHelper();
$redirectUrlProfile = 'http://www.example.com/link1.php';
$loginUrlProfile = $helperProfile->getLoginUrl($redirectUrlProfile);
echo 'Get profile with Facebook!<br>';
$helperPages = $fbPages->getRedirectLoginHelper();
$permissions = ['pages_show_list']; // Optional permissions
$redirectUrlPages = "http://www.example.com/link2.php";
$loginUrlPages = $helperPages->getLoginUrl($redirectUrlPages, $permissions);
echo 'Get pages with Facebook!';
If I use the above code (commenting the non-relevant parts) with only one facebook object to either retrieve the profile or the pages managed by user (but not both), everything works fine. But if I use both objects concurrently to give a choice to the user, I get a FacebookSDKException. I guess this is due to CRSF cookies.
Is there any way to circunvent this problem?
I guess this is due to CRSF cookies.
Correct. Calling the getLoginUrl method creates a new random state value and writes it into the session, overwriting any previously stored one.
So if you call the method twice (or more times), login will only work if you call the login dialog via the last login URL created, because only that contains a state value that matches the one stored in the session.
If you want to keep using two different redirect URIs, then you need to implement an additional step to create the correct login URL, and only that one.
So you have two links in your page, both pointing to a script on your server, and passing what permissions to ask for via a GET parameter (whether you want to pass permission names directly, or just a flag like ?loginmode=1/?loginmode=2, is up to you.)
In that script, you decide which redirect URI and scope value to call the getLoginUrl method with - once. And then your script just redirects to that login URL.
(But keep in mind that the step that exchanges the code for an access token also requires the redirect URI parameter to be passed, again - and again with the exact same value that was used in the login dialog call.)
So doing it the way #luschn suggested in comments, using the JS SDK for login purposes, is probably much easier. FB.login can be called with different scopes from different points in your client-side JS code without any such problems.

How do you have managed a status of contents in Symfony2

How do you have created a status of blog, micropost and etc?
I simply have created a status which each application entity has a status code of boolean.
False is private, true is public.
It is a multi-user environment in fosuserbundle.
It was created without any problems until now.
However, I wanted to be able to set user, group, and other status set in a multi-user environment.
(like a linux permissions, 744, etc...)
So, I think that it might create a bundle that manages the status.
The comment bundle that I have created is relation each application bundle by the ID of the string.
{{ render(controller('MyCommentBundle:Default:embed', {
'id' : 'for_example_micropost_' ~ micropost.id, // => relation id
'name' : micropost.owner.username,
'request': app.request
})) }}
However, it can not use when viewing list of application contents.
(Because the query occurs in one by one)
For example, either simply relationship by creating a column for each application.
Status Entity
blog_id
micropost_id
picture_id
etc...
But I think it inconvenient.
It require care of additional column when generated for each new application.
And I feel it is not a smart also.
For example, do so in the list part. Conditions of the string if possible in DQL.
public function find_all($owner, $limit, $offset)
{
$q = $this->getEntityManager()->createQuery('
SELECT p FROM MyMicropostBundle:Post p
LEFT JOIN MyStatusBundle s
With s.relation_id = :prefix + p.id
WHERE s.code > 744
')
->setParameter("prefix", 'for_example_micropost_')
->setParameter("owner", $owner)
->setMaxResults($limit)
->setFirstResult($offset);
return $q->getResult();
}
Etc., various I think, but I do not know the best way.
How do you have created a status?
Do you know status bundle already?
Or do you know bundle may be relevant to me.
If there is an optimal way and ideas, please tell me.
StatusBundle process:
It check a access user first.
It check a status code and groups and owner associated with the contents.
It compare a status code, groups and owner of the content to a access user.
I think it is possible to create (relation) bundle within a single page in my skills.
However, problem is how to relations at such a list display page that query to get the contents of the large number.
For example, I get the data of the last 10 blog posts.
I get the last 10 of public status posts if a access user is not the owner.
It is easy if Post entity has a status.
However, I think how to check a status, groups and owner if a status of a post has been registered in the status entity of other bundle.
I think that how to handle the status bundle while I get the data of the contents of the large number.

Change 'edit account' URL in Drupal

When going to edit account or edit profile in Drupal 7, the URL looks something like http://localhost/user/123/edit where 123 is the user id. Because of this, anyone can see how many users the site has, which I don't want. Is there a way that I can change it to something like http://localhost/user/edit or something without an ID?
I've tried setting up a menu entry in my module, that acts as the edit account/profile page, but had no success.
Also, I don't want to install a new module for this, I'd rather just write my code.
In theory, you could combine the Pathauto module (the widely-used module, used on over 250,000 D6 and D7 sites, which provides URL aliases for normal node and user paths, etc) with the Sub-pathauto module (a new D7 module, currently used on only a few hundred sites). The Sub-pathauto module is the only Drupal 7 module I'm aware of which will allow you to alias the user/uid part of a user/uid/edit -type path.
On the other hand, if your goal is simply to create the illusion that you might have more than a handful of users, when launching a new Drupal site, you could simply increment the UID index by adding (then deleting) a bunch of auto-generated users (with Devel generate), or since this is an auto-increment index, you could likely manually create a user entry in the database with an index of 1507 or something, and then any entry created by Drupal after that would start at 1508, even after you've removed the dummy entry from the table. (Caveat: I've never done this, but in theory it should work.)
Hope that helps. :-)
There is already a module that allows to do what you are trying to do, but as you want to avoid installing a module, you can create a module that contains the following code:
function mymodule_url_outbound_alter(&$path, &$options, $original_path) {
if (preg_match('|^user/([0-9]+)(/.*)?|', $path, $matches)) {
if ($user = user_load($matches[1])) {
$path = 'user/' . $user->name . $matches[2];
}
}
}
function mymodule_url_inbound_alter(&$path, $original_path, $path_language) {
if (preg_match('|^user/([^/]+)(/.*)?|', $path, $matches)) {
$uid = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $matches[1]))->fetchField();
if ($uid) {
$path = "user/$uid" . $matches[2];
}
}
}
This code works if usernames are unique, on your site. This is what normally happens on Drupal sites, where the username is forced to be unique; if a user tried to create an account using a username that already exist, he will get an error message.
The first hook rewrite paths such as "user/100" in "user/username," and the other hook make the inverse operation. This is necessary because Drupal expects user paths in the format "user/userid" and it would not be able to handle a user path containing the username (except when you are using a path alias).
As you are said you don't like that people can know how many users your site has, there is an easier way to avoid that. The fact people know that 123 is a valid user ID, though, doesn't mean they know how many users are registered in your site: You could have 1,000 users, 140,000 users. They just know that you could have 123 users, but if you have blocked users in your site, then some of the user IDs are not usable.
Create a user account that will never be used to log in, and create content on your site.
Editing the "users" database table increase the user ID of the account you created. Supposing that its user ID is 146, increase that number of 100.
Now, the next user that will register on your site will have a user ID equal to 247.
Increase the user ID of the dummy account you created incrementing the higher user ID.
In this way, if somebody notice that there is a user account with ID equal to 247, he will wrongly suppose you have 247 users.
What I did after all, was to create a hook_user_insert and to add 2 URL aliases in the urlalias table:
user/$user->uid/edit -> user/$user->name/edit
and
user/$user->uid/edit/profile -> user/$user->name/edit/profile
Hope this helps somebody.

Drupal module to control user post frequency?

We've been having a new type of spam-bot this week at PortableApps.com which posts at a rate of about 10 comments a minute and doesn't seem to stop - at least the first hour or so (we've always stopped it within that time so far). We've had them about a dozen times in the last week - sometimes stopping it at 50 or 60, sometimes up to 250 or 300. We're working to stop it and other spam bots as much as possible, but at the moment it's still a real pest.
I was wondering whether in the mean time whether there's any sort of module to control the frequency a user can post at to e.g. 50 an hour or something like 10 in an hour for new users. That at least would mean that instead of having to clear up 300 comments 50 at a time in admin/content/comment we'd have a smaller number to clear. (A module to add a page to delete all content by a user and block them would also be helpful!)
I believe that there's a plugin to do this available for WordPress, but can't find any such thing for Drupal.
For your second question, i would have a look at the code of the User Delete module (click).
The module also disables the user account and unpublished all nodes/comments from a certain user. By extending the code, you could easily create another possibility to unpublish + delete all nodes/comments from a certain user and blocking the account.
After the unpublish code in the module, you should just put delete code (in sql if the module is selecting by a sql-query or by using the drupal delete functions).
Another option would be so make a view (using the view module) only to be viewed by administrators, where you choose a certain user using the filters and then lists his/her posts. Then in the node-contenttype.tpl.php you place a button that calls a function which deletes all nodes/comments and the user.
First problem (post frequency)
I've been thinking about the comment post limit. If I remember correctly Drupal stores comments in a seperate table and has comment specific functions.
I'd create a new module and using the comment_nodeapi function i would check in the operation 'insert' how much comments the current user has already made within a certain timeframe.
To check this I would write a custom sql query on the database which takes the count of alle comments made by uid where the post_date is larger then NOW-1hour. If that count is larger then 10 or 15 or whatever post frequency you want then you give a message back to the user. You can retrieve the user id and name by using the global $user variable.
(example: print $user->name;)
You have to check on your own for the sql query but here's some code when you have the amount:
<?php
function comment_nodeapi(&$node, $op, $arg = 0) {
switch ($op) {
case 'insert':
//PLACE HERE THE SQL TO GET THE COUNT
if($count > 15){
$repeat = FALSE;
$type = 'status'
drupal_set_message("You have reached the comment limit for this time.", $type, $repeat);
break;
}else{
db_query('INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d, %d, NULL, %d, 0)', $node->nid, $node->changed, $node->uid);
break;
}
}
}
?>
(this code has not been tested so no guarantees, but this should put you on the right track)
I would suggest something like Mollom (from the creator of Drupal). It scans the message for known spam pattern/keywords/... and if this scan fails, it displays a CAPTCHA to the user to make sure that it's a real human that wants to enter content that has the same properties like spam.
They offer a free service and some paid solutions. We are using it for some customers and it's worth the money. It also integrates very well in Drupal.
Comment Limit is probably what you need.
http://drupal.org/project/spam
http://drupal.org/project/antispam - with akismet support

Resources