Related
I would like to do some interesting stuff with the hits that are being displayed based on the search query that user is not only typing into search box but actually filtering using the instant search filters. I have filter based on hierarchical events_location taxonomy. Based on what user selected I would get the info in JS variable that I can then further use to do other operations in the hits div, specifically on each hit card.
So my URL when searching updates like this:
/what-to-see/?q=&idx=sdbeta_posts_events&p=0&hFR%5Btaxonomies_hierarchical.events_calendar.lvl0%5D%5B0%5D=JUL%204&hFR%5Btaxonomies_hierarchical.events_category.lvl0%5D%5B0%5D=All&hFR%5Btaxonomies_hierarchical.events_locations.lvl0%5D%5B0%5D=Paddock%20Stage
I could potentially take the URL and extract the data from it, but I am sure there is more elegant way of working with the query.
In InstantSearch.js, the state is managed by another library called the algoliasearch-helper. Through this library you can read and write the search parameters.
The cleanest to access the helper is to build a custom widget, which is a plain object with lifecycle hooks (initial rendering and the other renderings). You can read more about custom widgets there.
Once you've accessed the helper, you can read and write with the helper API.
This can be found under search.searchParameters
So:
console.log(search.searchParameters);
Will give you whole object that you can then work with.
There is however one issue with this and that is that it works only on initial load. I was unable to make this work or get any data after starting to selecting categories. So if anyone knows how to use this so it updates after each selection please comment bellow.
I would like to use collective.easytemplate to generate templated emails (for content rules). However, I am not sure if it can output an objects workflow state. Anybody know if it is possible and how it is done?
Thanks.
You can, it is possible, and one way is to use the portal_workflow tool e.g. from parts/omelette/plone/app/contentrules/tests/test_action_workflow.py:
self.assertEquals('published',
self.portal.portal_workflow.getInfoFor(self.folder.d1, 'review_state'))
More generally, something like:
context.portal_workflow.getInfoFor(context, 'review_state')
in a page template should work. Or use the portal_catalog as Spanky suggests e.g. if "obj" is a catalog "brain" (i.e. part of a result set from a catalog search) then:
obj.review_state
should work.
The portal_catalog also has an index of the workflow's Review State, so if you don't already have the object you're working on (e.g. context ≠ the object) you could use the catalog, look up the object and get the review state from the resulting "brains" object.
Apparently there are ALSO browser view methods available to you as well, and I notice that one of them is workflow_state. See:
http://plone.org/documentation/manual/theme-reference/page/otherinfo
I will try to be as concise as possible. I'm using Flex/Hibernate technologies for my app. I also use Cairngorm micro-architecture for Flex. Because i'm beginner, i have probably misunderstand something about Caringorm's ModelLocator purpose. I have following problem...
Suppose that we have next data model:
USER ----------------> TOPIC -------------> COMMENT
1 M 1 M
User can start many topics, topics can have many comments etc. It is pretty simple model, just for example. In hibernate, i use EAGER fetching strategy for unidirectional USER->TOPIC and TOPIC->COMMENT relations(here is no question about best practices etc, this is just example of problem).
My ModelLocator looks like this:
...
public class ModelLocator ....
{
//private instance, private constructor, getInstance() etc...
...
//app state
public var users:ArrayCollection;
public var selectedUser:UserVO;
public var selectedTopic:TopicVO;
}
Because i use eager fetching, i can 'walk' through all object graph on my Flex client without hitting the database. This is ok as long as i don't need to insert, update, or delete some of the domain instances. But when that comes, problems with synchronization arise.
For example, if i want to show details about some user from some UserListView, when user(actor) select that user in list, i will take selected index in UserList, get element from users ArrayCollection in ModelLocator at selected index and show details about selected user.
When i want to insert new User, ok, I will save that user in database and in IResponder result method i will add that user in ModelLocator.users ArrayCollection.
But, when i want to add new topic for some user, if i still want to use convenience of EAGER fetching, i need to reload user list again... And to add topic to selected user... And if user is in some other location(indirectly), i need to insert topic there also.
Update is even worst. In that case i need to write even some logic...
My question: is this good way of using ModelLocator in Cairngorm? It seems to me that, because of mentioned, EAGER fetching is somehow pointless. In case of using EAGER fetching, synchronization on Flex client can become big problem. Should I always hit database in order to manipulate with my domain model?
EDIT:
It seems that i didn't make myself clear enough. Excuse me for that.
Ok, i use Spring in technology stack also and DTO(DVO) pattern with flex/spring (de)serializer, but i just wanted to stay out of that because i'm trying to point out how do you stay synchronized with database state in your flex app. I don't even mention multi-user scenario and poling/pushing topic which is, maybe, my solution because i use standard request-response mechanism. I didn't provide some concrete code, because this seems conceptual problem for me, and i use standard Cairngorm terms in order to explain pseudo-names which i use for class names, var names etc.
I'll try to 'simplify' again: you have flex client for administration of above mentioned domain(CRUD for each of domain classes), you have ListOfUsersView(shows list of users with basic infos about them), UserDetailsView(shows user details and list of user topics with delete option for each of topic), InsertNewUserTopicView(form to insert new topic) etc.
Each of view which displays some infos is synchronized with ModelLocator state variables, for example:
ListOfUsersView ------binded to------> users:ArrayCollection in ModelLocator
UserDetailsView ------binded to------> selectedUser:UserVO in ModelLocator
etc.
View state transition look like this:
ListOfUsersView----detailsClick---->UserDetailsView---insertTopic--->InsertTopicView
So when i click on "Details" button in ListOfUsersView, in my logic, i get index of selected row in ListOfUsers, after that i take UserVO object from users:ArrayCollection in ModelLocator at mentioned index, after that i set that UserVO object as selectedUser:UserVO in ModelLocator and after that i change view state to UserDetailsView(it shows user details and selectedUser.topics) which is synchronized with selectedUser:UserVO in ModelLocator.
Now, i click "Insert new topic" button on UserDetailsView which results in InsertTopicView form. I enter some data, click "Save topic"(after successful save, UserDetailsView is shown again) and problem arise.
Because of my EAGER-ly fetched objects, i didn't hit the database in mentioned transitions and because of that there are two places for which i need to be concerned when insert new topic for selected user: one is instance of selectedUser object in users:ArrayCollection (because my logic select users from that collection and shows them in UserDetailsView), and second is selectedUser:UserVO(in order to sync UserDetailsView which comes after successfull save operation).
So, again my question arises... Should i hit database in every transition, should i reload users:ArrayCollection and selectedUser:UserVO after save in order to synchronize database state with flex client, should i take saved topic and on client side, without hitting the database, programmatically pass all places which i need to update or...?
It seems to me that EAGER-ly fetched object with their associations is not good idea. Am i wrong?
Or, to 'simplify' :) again, what should you do in the mentioned scenario? So, you need to handle click on "Save topic" button, and now what...?
Again, i really try to explain this as plastic as possible because i'm confused with this. So, please forgive me for my long post.
From my point of view the point isn't in fetching mode itself but in client/server interaction. From my previous experience with it I've finally found some disadvantages of using pure domain objects (especially with eager fetching) for client/server interaction:
You have to pass all the child collections maybe without necessity to use them on a client side. In your case it is very likely you'll display topics and comments not for all users you get from server. The most like situation you need to display user list then display topics for one of the selected users and then comments for one of the selected topics. But in current implementation you receive all the topics and comments even if they are not needed to display. It is very possible you'll receive all your DB in a single query.
Another problem is it can be very insecure to get all the user data (or some other data) with all fields (emails, addresses, passwords, credit card numbers etc).
I think there can be other reasons not to use pure domain objects especially with eager fetching.
I suggest you to introduce some Mapper (or Assembler) layer to convert your domain objects to Data Transfer Objects aka DTO. So every query to your service layer will receive data from your DAO or Active Record and then convert it to corresponding DTO using corresponding Mapper. So you can get user list without private data and query some additional user details with a separate query.
On a client side you can use these DTOs directly or convert them into client domain objects. You can do it in your Cairngorm responders.
This way you can avoid a lot of your client side problems which you described.
For a Mapper layer you can use Dozer library or create your own lightweight mappers.
Hope this helps!
EDIT
What about your details I'd prefer to get user list with necessary displayable fields like first name and last name (to display in list). Say a list of SimpleUserRepresentationDTO.
Then if user requests user details for editing you request UserDetailsDTO for that user and fill tour selectedUser fields in model with it. The same is for topics.
The only problem is displaying list of users after user details editing. You can:
Request the whole list again. The advantage is you can display changes performed by other users. But if the list is too long it can be very ineffective to query all the users each time even if they are SimpleUserRepresentationDTO with minimal data.
When you get success from server on user details saving you can find corresponding user in model's user list and replace changed details there.
Tell you the truth, there's no good way of using Cairngorm. It's a crap framework.
I'm not too sure exactly what you mean by eager fetching (or what exactly is your problem), but whatever it is, it's still a request/response kind of deal and this shouldn't be a problem per say unless you're not doing something right; in which case I can't see your code.
As for frameworks, I recommend you look at RobotLegs or Parsley.
Look at the "dpHibernate" project. It implements "lazy loading" on the Flex client.
I have the following scenario:
Editor Role should not be allowed to
delete nodes. Therefore the corresponding
permission is de-selected in the
permissions page.
However Editor
should be able to to delete nodes
from Views Bulk operations. Using
Rules an action is created called
"safe delete" that checks things like
if the node is not published etc.
before deleting the node.
The problem is the Views Bulk Operations respects Node permissions. Editor will not be able to delete the node as he has not been given that permission. Is there a way that Editor can become a higher role user (as sort of sudo) while performing that action in VBO? Alternatively is there a way to tell VBO to ignore node access for this action?
I'm sure this is a mainstream requirement but I can't seem to find a solution.
Solutions which do not involve programming will be preferred.
The simple, but not-so-clean way, is the route you already took, but with an additional, small module to help it.
has a function my_module_can_delete($user), that returns TRUE if the user is allowed to delete, FALSE if the user is not.
implements hook_form_alter() to modify and delete the button on the node_edit form, if my_module_can_delete($user)
implements hook_form_alter() to modify the confirm form that is called on /node/%nid/delete, and add a message there, telling the user he or she my_module_can_delete($user). This should be enough, since disabling this form will result in users not being able to get past this form. FORM-API will take care of that.
However, you can make it more sturdy, to catch other deleting modules:
implements hook_nodeapi(), $op == 'delete' to catch delete actions and halt (by invoking drupal_goto(), or calling drupal_access_denied() to enforce a user-error. Only catch delete-actions if the referer was the delete-confirm-form as mentioned above. Or, more secure, whitelist your VBO-action and return false on all other referers. A referer can often be found by reading out the $node passed along to hook_nodeapi().
A, IMHO, much cleaner, but probably more intensive alternative, would be to simply make sure your batches/actions are called on every delete action.
In a module, you could do this by avoiding all the VBO-configuration and leaving all the extra-delete actions out of there.
Then write a module that implements hook_nodeapi() and then calls all the cleaning actions from there. That way you can be sure that your delete-actions are called on every delete-action on any node. Obviously you can add some conditions into your hook_nodeapi() to only invoke your modules in certain cases (node-types, user-roles, permissions and so on).
Well, it seems to me that you've got a setup where you don't want Editor Role users to delete things, really, except in certain extreme situations. Here's my suggestion:
1) Install Flag module. Create a 'To Be Deleted' flag that can only be assigned by Editor Role people.
2) I haven't looked into it, but I"m sure there's probably a rule or trigger/action combo which will unpublish the node when the 'To Be Deleted' flag is assigned to it. This will remove the node from casual view.
3) Then either set up some cron run activity (trigger/action or rule) to delete nodes with 'To Be Deleted' flag set on them, or have another user with higher permissions come in occasionally and delete out the flagged items.
This way you're not actually bypassing the permissions system, and yet things are still being removed from your site.
I got caught out of this for a while until I noticed the "actions_permissions" module, enable this and on the Permissions page you can provide access to specific actions on a role by role basis.
I don't have a good no-coding solution, and I'm not sure I would call this solution "great" - but one way might be to implement a simple module with a form_alter hook that removes the delete button from the node edit forms as they are built.
In general it seems like the role either has permission to delete nodes or not, and monkeying around like this is going to be less robust that you might like.
I have a classic 3-tier ASP.Net 3.5 web application with forms that display business objects and allow them to be edited. Controls on the form correspond to a property of the underlying business object. The user will have read/write, readonly, or no access to the various controls depending on his/her role. Very conventional stuff.
My question is: what is the object-oriented best practice for coding this? Is there anything more elegant than wrapping each control in a test for the user's role and setting its Visible and Enabled properties?
Thanks
You'll want to drive this off of data, trust me. You'll need a lot of tables to do it right, but it is so worth it in the end. Having to crack open code and edit a bunch of if-statements every time the business wants to change permissions is a killer.
You'll want a table for your main high-level types, things you probably already have business object clases for. Then a table for each status of them. Then a table for the fields of these classes. Then a table for user roles (admin, guest, etc.) Finally a table for the permissions themselves. This table will have columns for business class, status, field, user role, and then what permission they have. For permissions I would go with one field and use an enum: Hidden, ReadOnly, Editable, and Required. Required implies Editable. Anything but Hidden implies Visible. Finally put a Priority column on this table to control which permission is used when more than one might apply.
You fill out this table with various combinations of class, status, field, role, and permission. If a value is null then it applies to all possible values. So you don't need a trillion rows to cover all your bases. For example, 99% of the time, Guest users are read-only users. So you can put a single entry in the table with only the Guest role specified, everything else is null, and set it's Priority nice and high, and set the permission to Read Only. Now for all classes, all statuses, all fields, if the user is a Guest, they will have Read Only permission.
I added status to your list of concerns because in my experience, business all the time wants to constrain things by an object's status. So maybe users can edit an item's name while it is in Draft status, for example, but once it is in Posted status, the name is no longer editable. That is really common in my experience.
You'd want to bring this table into memory and store it in the app's cache, because it's not going to change very often, if ever, unless you do a whole new version.
Now the above is going to handle 90% of your needs, I suspect.
One area that will have to be handled in code, unless you want to get really fancy, is the cases where a user's permission is determined in part by the value of fields in the object itself. So say you have a Project class, which has a Project Manager class. Now the Percent Complete field of the class is basically read-only for everybody, except the Project Manager. How are you going to handle that? You'll need to provide a way to incorporate specific instances of a class into the decision making process. I do this in code.
To work properly, I have found that access levels should be in this increasing order:
NONE, VIEW, REQUIRED, EDIT.
Note that REQUIRED is NOT the top level as you may think it would be since EDIT (both populate & de-populate permission) is a greater privilege than REQUIRED (populate-only permission).
The enum would look like this:
/** NO permissions.
* Presentation: "hidden"
* Database: "no access"
*/
NONE(0),
/** VIEW permissions.
* Presentation: "read-only"
* Database: "read access"
*/
VIEW(1),
/** VIEW and POPULATE permissions.
* Presentation: "required/highlighted"
* Database: "non-null"
*/
REQUIRED(2),
/** VIEW, POPULATE, and DEPOPULATE permissions.
* Presentation: "editable"
* Database: "nullable"
*/
EDIT(3);
From the bottom layer (database constraints), create a map of fields-to-access. This map then gets updated (further restrained) at the next layer up (business rules + user permissions). Finally, the top layer (presentation rules) can then further restrain the map again if desired.
Important: The map must be wrapped so that it only allows access to be decreased with any subsequent update. Updates which attempt to increase access should just be ignored without triggering any error. This is because it should act like a voting system on what the access should look like. In essence, the subsequent layering of access levels as mentioned above can happen in any order since it will result in an access-level low-water-mark for each field once all layers have voted.
Ramifications:
1) The presentation layer CAN hide a field (set access to NONE) for a database-specified read-only (VIEW) field.
2) The presentation layer CANNOT display a field when the business rules say that the user does not have at least VIEW access.
3) The presentation layer CANNOT move a field's access up to "editable" (nullable) if the database says it's only "required" (non-nullable).
Note: The presentation layer should be made (custom display tags) to render the fields by reading the access map without the need for any "if" statements.
The same access map that is used for setting up the display can also be using during the submit validations. A generic validator can be written to read any form and its access map to ensure that all the rules have been followed.
I have often found that this is really the only real easy and understandable way to do it, as your interface needs to modify based on the information and level of editing that they can complete.
I do find typically though that depending on the needs, you can interject the "cannot edit" information by passing role information to the business level if you have plans to move to different presentation levels. but this adds complexity, and if you are only building for one interface it would most likely be overkill
For the website menus we can have different menus based on users role by using the Sitemaps. For controls like Buttons we will have to hide them using their Visible property. I think a good idea will be to create a server control (Button) and expose the Role property. This will hide the Button if the user is not in the correct role.
My first instinct for doing this in a more OO way would be to handle your roles and their implementations for this purpose (control permissions read/write/etc) is to use the abstract factory pattern for your roles. I will be happy to explain the ins and outs of what I am talking about if you'd like but there are probably 900 examples on the web. Here is one link (disclaimer: it's my blog but it does happen to talk to using abstract factory for roles specifically)
Using something like this you could then use a number of methods to display the correct controls for each of your business object properties with the correct attributes (read/write/hidden/displayed/etc).