Is there any way that i can append my data into user global variable so i can access it on other pages?
You have two options
You can save it on the user object ($user->data), if it's some static data that doesn't change.
Use hook_user op load which could look like this:
function module_user($op, &$edit, &$account, $category = NULL) {
switch ($op) {
case 'load':
$account->module = db_fetch_object(db_query(
'SELECT * FROM {module} WHERE uid = %d', $account->uid
));
break;
}
}
Hook user, is good if you have complex data that changes a lot. You can store the data in your own table, and append it you the user when it's being loaded. The downside is, that you will need to run user_load you get the data on the user object.
If you don't need/want to handle the storage of your data in the database because you just want it the be globally accessible during the user's session, you can store it in the $_SESSION array.
You can also use variable_set() and variable_get()if the data is common to all users else SESSIONS need to be used
Related
Currently I store filters in session like this:
// Filter action
if ('filter' == $request->get('filter_action')) {
// Bind values from the request
$filterForm->handleRequest($request);
if ($filterForm->isValid()) {
// Build the query from the given form object
$filterUpdater->addFilterConditions($filterForm, $queryBuilder);
// Save filter to session
$filterData = $filterForm->getData();
$session->set(sprintf('%sControllerFilter', $this->filterName), $filterData);
$session->set(sprintf('%sControllerFilterPage', $this->filterName), 1);
}
} else {
// Get filter from session
if ($session->has(sprintf('%sControllerFilter', $this->filterName))) {
$filterData = $session->get(sprintf('%sControllerFilter', $this->filterName));
foreach ($filterData as $key => $filter) {
if (\is_object($filter)) {
$filterData[$key] = $em->merge($filter);
}
}
$filterForm = $this->createFilterForm($filterData, $this->getSiteFromSession($request));
$filterUpdater->addFilterConditions($filterForm, $queryBuilder);
}
}
But due to EntityManager::merge() deprecations I need to change this solution. Any ideas how to do it? The solution is to skip using EntityFilterType and use ChoiceFilterType but I don't want to, cause EntityFilterType is a much more comfortable solution.
If I remove lines that are responsible for merging entities from session, then I get error:
Entity of type "App\Entity\Category" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?
merge is used to re-attach a detached entity. persist tells Doctrine that the entity is to be saved, so it achieves the re-attach. This way you achieve that the entity will be managed, as the error message suggests.
You can read more here: https://symfony.com/doc/current/doctrine.html#persisting-objects-to-the-database
I need to use WordPress Rest API to delete a user and all its own (maybe custom) posts.
I tried to call <url_base>/wp/v2/users/<id> and I have to provide a further parameter ?reassign=USER_ID to reassign its posts to another user.
A silly solution should be adding a "garbage" user to the system and assign him all deleted posts. Then I can create a job to delete all posts. It seems a very stupid way to me.. :(
I just want to delete them. I can do it from WP admin panel... why I can't via Rest Api?
Thanks
EDIT
Passing "reassign" as an empty params (null is not accepted...) the API informs me that I'm not able to delete my own profile. I'm testing it as a subscriber (end user), but I need to be able to manage and delete my own data... Am I wrong?!
This is my solution... maybe not the best one, but it seems to work.
I added delete_users and remove_users capabilities to the Subscriber role (for my convenience).
My /DeleteUsers rest endpoint check for the user ID encrypted inside the JWT token that is sent in header and compare it with the user_id params. If they match, I can delete the user.
Now I just have to avoid that a user can access the WP Admin panel and make some mess!
This is the code:
function myDeleteProfile($data) {
require_once(ABSPATH.'wp-admin/includes/user.php');
$headers = getallheaders();
if(!isset($headers['Authorization']) || !$headers['Authorization']) {
return array('esito'=>'ko', 'descrizione'=>'Fail message!');
}
$jwt_parts = preg_split('/\./', $headers['Authorization']);
$jwt_decoded = json_decode(base64_decode($jwt_parts[1]));
$jwt_user = $jwt_decoded->data->user->id;
if( intval($data['user_id']) != intval($jwt_user) ) {
return array('esito'=>'ko', 'descrizione'=>'User cannot delete.');
}
else {
if( wp_delete_user($jwt_user,null) ){
return array('esito'=>'ok', 'descrizione'=>'Deleting...');
}
else {
return array('esito'=>'ko', 'descrizione'=>'Error.');
}
}
}
I have a page that lets you edit user data. I'm using FlowRouter for the routing and it can be found on the route /employees/:id.
I need to update the detail form when data changes on the server and leave the route if it was deleted by other client.
I decided to use Tracker.autorun which informs me whenever the data changes. The previous user info is stored on the template so it's easy to tell if the record was deleted.
Template.UpdateEmployee.onCreated(function () {
const self = this;
self.subscribe('user', FlowRouter.getParam('id'));
self.autorun(function () {
const _id = FlowRouter.getParam('id');
const user = Meteor.users.findOne({_id});
if(!user && self.user)
FlowRouter.go('/employees');
self.user = user;
if(!user)
return;
user.email = user.emails[0].address;
$('.ui.form').form('set values',user);
});
});
And lastly in the onRendered callback I'm checking if the data was set on template as I believe not doing so could lead to data being available before the template is rendered and hence values wouldn't get set properly. Is this correct?
Template.UpdateEmployee.onRendered(function () {
if(this.user){
user.email = user.emails[0].address;
$('.ui.form').form('set values',user);
}
});
Are there any pitfalls to this solution?
I can see a couple drawbacks inherently. The first one is doing a find query on the client. Typically you would want to return data from the server using Meteor publish and subscribe.
The second is you are passing the key to find the data over the URL. This can be spoofed by other users for them to find that users data.
Lastly if you are doing a find on the user object, I assume you might be storing data there. This is generally bad practice. If you need to store user data with their profile, it's best to create a new collection and publish/subscribe what you need.
I'd like to create a method that returns the count of a generic collection.
Calling the method would look something like this:
Meteor.call('getCollectionCount', 'COLLECTION_NAME');
And the result would be the collection count.
The server method code would look something like this:
getCollectionCount: function (collectionName) {
return window[collectionName].find().count();
}
This won't work because window isn't defined on the server, but is something similar possible?
Use global instead of window.
Note that this uses the variable name assigned to the collection object, not the name given to the collection. For this to work with Meteor.users you need to assign another variable name.
if (Meteor.isServer) {
users = Meteor.users;
}
if (Meteor.isClient) {
Meteor.call('count', 'users', function (err, res) {
// do something with number of users
});
}
Also probably a good idea to check that global[collectionName] is actually a collection.
I came up with this code which makes the following assumptions :
collections are declared in the global scope as top level objects.
collections are searched by collection name, not the collection variable identifier.
So client code should declare their collections like this :
MyCollection=new Meteor.Collection("my-collection");
And use the function like this :
var clientResult=Meteor.call("getCollectionCount","my-collection",function(error,result){
if(error){
console.log(error);
return;
}
console.log("actual server-side count is : ",result);
});
console.log("published subset count is : ",clientResult);
The method supports execution on the client (this is known as method stub or method simulation) but will only yield the count of the collection subset replicated client-side, to get the real count wait for server-side response using a callback.
/packages/my-package/lib/my-package.js
getCollection=function(collectionName){
if(collectionName=="users"){
return Meteor.users;
}
var globalScope=Meteor.isClient?window:global;
for(var property in globalScope){
var object=globalScope[property];
if(object instanceof Meteor.Collection && object._name==collectionName){
return object;
}
}
throw Meteor.Error(500,"No collection named "+collectionName);
};
Meteor.methods({
getCollectionCount:function(collectionName){
return getCollection(collectionName).find().count();
}
});
As Meteor.users is not declared as a top level variable you have to account for the special case (yes, this is ugly).
Digging into Meteor's collection handling code could provide a better alternative (getting access to a collection handle by collection name).
Final words on this : using a method call to count a collection documents is unfortunately non-reactive, so given the Meteor paradigm this might be of little use.
Most of the time you will want to fetch the number of documents in a collection for pagination purpose (something like a "Load more" button in a posts list for example), and as the rest of the Meteor architecture you'll want this to be reactive.
To count documents in a collection reactively you'll have to setup a slightly more complicated publication as showcased in the "counts-by-room" example in the docs.
http://docs.meteor.com/#meteor_publish
This is something you definitely want to read and understand.
This smart package is actually doing it right :
http://atmospherejs.com/package/publish-counts
It provides a helper function that is publishing the counts of any cursor.
Keep track of the collections on some other property that the server has access too. You could even call it window if you really wanted to.
var wow = new Meteor.Collection("wow");
collections["wow"] = wow;
getCollectionCount: function (collectionName) {
return collections[collectionName].find().count();
}
If you don't want the package users to change how they work with collections in the app then I think you should use MongoInternals to get collections by name from the db. Not tested but here is an example:
//on server
Meteor.methods({
count: function( name ){
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var collection = db.collection( name );
return collection && collection.count({});
}
});
Another example of MongoInternals use is here. Documentation of the count() function available from the mongo driver is here.
Is it possible to define a new operation for a node access?
As I know, the operations for a node that are used in hook_access() are:
create
delete
update
view
I have a custom content type for which I need another operation, such as "suggest."
short answer is NO as node_access() who is responsible to call hook_access() does a check
on the $op parameter
if (!$node || !in_array($op,
array('view', 'update', 'delete',
'create'), TRUE)) {
return FALSE; }
you can attach some extra info to the node object in your suggest() function - hopefully called before node_access() - then check these extra informations in your hook_access() and return TRUE/FALSE according.
another option consists in hardcode permission checks into the suggest() action itself without messing around with hook_access.