How to create a Plone form widget that works like MultiContentTreeFieldWidget but preserves the order of items - forms

I have a dexterity behaviour that allows me to relate items and store the relations as UUIDs
relatedItems = schema.List(
title=u"Related Items",
description=u"Search for content that is related to this item",
required=False,
value_type=schema.Choice(
source=UUIDSourceBinder(navigation_tree_query={'portal_type':
TYPES_WITH_TEASERS})))
form.widget(relatedItems='plone.formwidget.contenttree.widget.MultiContentTreeFieldWidget')
This works great except that if you have a few relations then every time you edit the item their order changes. We're displaying the related items in the right hand column of the page (e.g. see this article about food) and want to control the order so that we can put more interesting teasers first.
Debugging it looks likely this reordering is down to the fact z3c.formwidget.query.widget.QuerySourceRadioWidget.update uses a set when processing request parameters. Presumably this is to prevent duplicates but has two nasty side effects:
a field’s value gets updated when it hasn't changed
order is lost when it might be important
Is there an alternative to MultiContentTreeFieldWidget that works in a similar way but preserves the order you add items? Even better is there a widget that does this and also allows you to reorder items as well?

I didn't find an alternative but subsequently z3c.formwidget.query has been updated to use a list instead of a set when processing request parameters. Version 0.7 fixes this
http://pypi.python.org/pypi/z3c.formwidget.query/0.7
Add the following to your [versions] section in buildout to resolve
z3c.formwidget.query = 0.7

Related

Restricting access to some columns in a table, based on another column in the same row

This feels like it should be a common requirement, but I'm not sure how best to implement the requirement.
I'm going to make up a really simple example. It's similar enough to what I actually need, without getting over-complicated.
Imagine we have a table called transport and it has the following columns:
type
model_name
size
number_of_wheels
fuel
maximum_passenger_count
We want to store all sorts of different types of transportation in this table, but not every type will have values in every column. Imagine the commonality is a lot higher, as this is a bit of fake example.
Here's a few examples of how this might work in practice:
type = cycle, we ban fuel, as it's not relevant for a cycle
type = bus, all columns are valid
type = sledge, we ban number_of_wheels, as sledges don't have wheels, we also ban fuel
type = car, all columns are valid
I want my UI to show a grid with a list of all the rows in the transport table. Users can edit the data directly in the grid and add new rows. When they add a new row, they're forced to pick the transport type in a dropdown before it appears in the grid. They then complete the details. All the values are optional, apart from the ones we explicitly don't want to record a value for, where we expect to not see anything at all.
I can see a number of ways to implement this, but none of them seems like a complete solution:
I could put this logic into the UI, enabling/ disabling grid cells based on type. But there's nothing to stop someone directly inserting data into the "wrong" columns in the backend of the database or via the API, which would then come through into the UI unless I added a rule to also mask out values in disabled cells. Making changes to which columns are restricted per transport type would be very difficult
I could put this logic into the API, raising an error if someone enters data into a cell that should be disallowed. This closes one gap for insertion to the database via the API, but SQL scripts would still allow entry into the "wrong" column. Also, the user experience would suck, as users would have to guess which columns to complete and which to leave blank. It would still be difficult to make changes to which columns are allowed/ restricted
I could add a trigger to the database, maybe to set the values to NULL if they shouldn't be allowed, but this seems clunky and users would not understand what was happening
I could add a generated column, but this doesn't help if I sometimes need to set a value and sometimes don't
I could just allow the unnecessary data to be stored in the database, then hide it by using a view to report it back. It doesn't seem great, as users would still see data disappearing from the UI with no explanation
I could add a second table, storing a matrix of which values are allowed and which are restricted by type. The API, UI and database could all implement this list using different mechanisms - this comes with the advantage of having a single place to make changes that will immediately be reflected across the entire system, but it's a lot of work and I have lots of these tables that work the same way

free-jqGrid External Filtering Used With Grid's beforeRequest() or onPaging() Event

Using jqGrid free (version 4.15.6) to show very basic information about invoices (ie: date created, date due, client, total, status). The invoices grid only has a few pertinent columns that are displayed because it is just not needed to show more than that. In reality there are a lot of other invoice-related fields that are not shown. I would like to offer end-users the ability to filter the grid based on a lot of these other parameters that are simply not part of the grid contents.
I know jqGrid offers built-in searching, and you can easily just add hidden columns with all the data, but I feel this is not good for us--invoices contain a lot of data--data that is not necessarily present in just the invoices database table. We want the grid to provide many other filtering options outside of the base invoice data but we do NOT want to use the built-in filter options. Instead, I would like to use a separate HTML table with a bunch of search fields that our server-side code would know how to pull back). When one decides to invoke the external filter, we want the grid to load all invoices matching that combined filter. And if one chooses to navigate using the grid's paging buttons, we want the grid to continue using the original external filtering parameters.
Hope this makes sense. Maybe I am just overthinking this but I am fairly certain the grid is designed to use it's built in filtering/searching tools/dialog and I have not found anyway to override this behavior. Actually I have using an older jqGrid but that involved using jQuery to completely REPLACE the default pager with custom HTML and event handling. I never could figure this out with older jqGrid so I chose to write it myself. But that code is less than optimum and even I know it is subject to much criticism. Having upgraded to 4.15.6, I want to do this the best way and I want to keep it logical and practical.
I have tried using beforeRequest() and onPaging() events to change the 'url' parameter, thinking that if I modified the url, I could change the GET to include all of our custom filtering fields. It seems that does not work as the url NEVER changes from the originally defined value. Console logging does show the events firing but no change to url. On top of that, the grid ALWAYS passes its own page field, _search field, etc. to the server so the server NEVER sees the filter request.
How does one define their own custom filtering coupled with paging loader and still take advantage of the built-in paging events? What am I missing?
**** DELETED CODE THAT WAS ADDED TO QUESTION THAT DID NOT PERTAIN TO ORIGINAL QUESTION ISSUE *********
It's difficult to answer on your question because you didn't posted code fragments, which shows how you use jqGrid and because the total number of data, which could be needed to display in all pages isn't known.
In general there are two main alternatives implementing of custom filtering:
server side filtering
client side filtering
One can additionally use a mix from both filtering. For example, one can load from the server all invoices based on some fixed filters (all invoices of specific user or all invoices of one organization, all invoices of the last month) and then use loadonce: true, forceClientSorting: true options to sort and to filter the returned data on the client side. The user could additionally to filter the subset of data locally using filter toolbar of searching dialog.
The performance of client side is essentially improved last years and loading relatively large JSON data from the server could be done very quickly. Because of that Client-Side-Filtering is strictly recommended. For better understanding the performance of local sorting, filtering and paging I'd recommend you to try the functionality on the demo. You will see that the timing of local filtering of the grid with 5000 rows and 13 columns is better as you can expect mostly from the round trip to the server and processing of server side filtering on some very good organized database. It's the reason why I recommend to consider to use client side sorting (or loadonce: true, forceClientSorting: true options) as far it's possible.
If you need to filter data on the server then you need just send additional parameters to the server on every request. One can do that by including additional parameters in postData. See the old answer for additional details. Alternatively one can use serializeGridData to extend/modify the data, which will be set to the server.
After the data are loaded from the server, it could be sorted and filtered locally before the first page of data will be displayed in the grid. To force local filtering one need just add forceClientSorting: true additionally to well known loadonce: true parameter. It force applying local logic on the data returned from the server. Thus one can use postData.filters, search: true to force additional local filtering and sortname and sortorder parameter to force local sorting.
One more important remark about using hidden columns. Every hidden column will force creating DOM elements, which represent unneeded <td> elements. The more DOM elements you place on the page the more slow will be the page. If local data will be used (or if loadonce: true be used) then jqGrid hold data associated with every row twice: once as JavaScript object and once as cells in the grid (<td> elements). Free jqGrid allows to use "additional properties" instead of hidden columns. In the case no data will be placed in DOM of the grid, but the data will be hold in JavaScript objects and one able to sort or filter by additional properties in the same way like with other columns. In the simplest way one can remove all hidden columns and to add additionalProperties parameter, which should be array of strings with the name of additional properties. Instead of strings elements of additionalProperties could be objects of the same structures like colModel. For example, additionalProperties: [{ name: "taskId", sorttype: "integer"}, "isFinal"]. See the demo as an example. The input data of the grid can be seen here. Another demo shows that searching dialog contains additional properties additionally to jqGrid column. The commented part columns of searching shows more advanced way to specify the list and the order of columns and additional properties displayed in searching dialog.
Forgive my answering like this but this question started out on one subject related to filtering and paging but with using an external filtering source. Oleg actually has several demos over many threads that I was able to use to accomplish the custom filtering and maintain default built-in paging. So his answer will be the accepted answer for the original question topic.
But in the solution of original, I encountered another issue with loading the grid initially. I wanted to have the grid load with default filtering values should no other filter already be in place. That really should have been a different question because it really did not affect the first.
I found yet another Oleg reply on a completely different question:
jqGrid - how to set grid to NOT load any data initially?.
Oleg answered that question and that answer solved our second need to load one way, then allow another way.
So, on initial load, we look for the filter params server-side. None given? We pull records using default filtering. Params present? We use initial provided params. The difference with initial loading we do not AJAX exit. We instead json_encode the data and place it in the grid definition as follows:
$('#grd_invoices').jqGrid(
...
url: '{$modulelink}&sm=130',
data: {$json_encoded_griddata},
datatype: 'local',
...
});
Since the datatype is set to 'local', the grid does NOT go to server initially, so the data parameter is used by the grid. Once we are ready to filter, we use Oleg's solution from yet another answer on yet another question to dynamically apply the filter as follows:
var myfilter = { groupOp: 'AND', rules: []};
myfilter.rules.push({field:'fuserid',op:'eq',data:$('#fuserid').val()});
myfilter.rules.push({field:'finvoicenum',op:'eq',data:$('#finvoicenum').val()});
myfilter.rules.push({field:'fdatefield',op:'eq',data:$('#fdatefield').val()});
myfilter.rules.push({field:'fsdate',op:'eq',data:$('#fsdate').val()});
myfilter.rules.push({field:'fedate',op:'eq',data:$('#fedate').val()});
myfilter.rules.push({field:'fwithin',op:'eq',data:$('#fwithin').val()});
myfilter.rules.push({field:'fnotes',op:'eq',data:$('#fnotes').val()});
myfilter.rules.push({field:'fdescription',op:'eq',data:$('#fdescription').val()});
myfilter.rules.push({field:'fpaymentmethod',op:'eq',data:$('#fpaymentmethod').val()});
myfilter.rules.push({field:'fstatus',op:'eq',data:$('#fstatus').val()});
myfilter.rules.push({field:'ftotalfrom',op:'eq',data:$('#ftotalfrom').val()});
myfilter.rules.push({field:'ftotal',op:'eq',data:$('#ftotal').val()});
myfilter.rules.push({field:'fmake',op:'eq',data:$('#fmake').val()});
myfilter.rules.push({field:'fmodel',op:'eq',data:$('#fmodel').val()});
myfilter.rules.push({field:'fserial',op:'eq',data:$('#fserial').val()});
myfilter.rules.push({field:'fitemid',op:'eq',data:$('#fitemid').val()});
myfilter.rules.push({field:'ftaxid',op:'eq',data:$('#ftaxid').val()});
myfilter.rules.push({field:'fsalesrepid',op:'eq',data:$('#fsalesrepid').val()});
var grid = $('#grd_invoices');
grid[0].p.search = myfilter.rules.length>0;
$.extend(grid[0].p.postData,{filters:JSON.stringify(myfilter)});
$('#grd_invoices').jqGrid('setGridParam',{datatype:'json'}).trigger('reloadGrid',[{page:1}]);
This allows us to have the grid show initial data loaded locally, and then subsequent filtering changes the grid datatype to 'json', which forces the grid to go to server with new filter params where it loads the more specific filtering.
Credit goes to Oleg because I used many of his posts from many questions to reach the end result. Thank you #Oleg!

MongoDB default items with user overwrite

So the problem seems very simple but every way I approach the solution it seems to be a poor implementation approach with either duplicated content or messy data.
The Problem
I want to provide an option for “overwrites” per user on “default” items. Basically I have a mongodb database with a collection containing items with the following information:
ID
Name
Icon
Description
There is a set of 20-30 items in this collection, which each user using the app views.
Most users will be happy to see the default but if a user wishes to say change the icon for an item or the name then how do I handle this “overwrite” on the ”default” item for just that single user.
Possible solutions
My thoughts are to implement one of the following options but all just seem a little wrong (I have provided my thoughts on this):
for each “overwriten” item add a duplicated item to the collection with the changes and a user_id field to link the user - this seems like a little bit of duplicated content as the user might only change the icon and not the name/description. Also if the name is changed int eh future on the default item how do you handle this and also how do you understand that this item must replace one of the “defaults” for just that user. I worry it will be a little bit of a performance issue too when looking up the items and then replacing the changed item
having all the items duplicated per user in the same collection - very much duplication of content but might be the best performing option but could cause issues in the future if new “default” items need to be added or default options need changing
collection per user - same as the previous. This options seems all kinds of wrong but maybe I’m just new to this and it is actually the best option.
collection containing overwrites - this seems like a good idea but equally a bad one due to looking up and comparing. If everything is changed then why not just have all new items rather than effectively a find a replace.
Reason for wanting to get this right
Maybe I’m over thinking this but it seems like I will face this issue a lot and I think I need to get it right to remove future issues with performance, data management and updates to default items.

Database and item orders (general)

I'm right now experimenting with a nodejs based experimental app, where I will be putting in a list of books and it will be posted on a forum automatically every x minutes.
Now my question is about order of these things posted.
I use mongodb (not sure if this changes the question or not) and I just add a new entry for every item to be posted. Normally, things are posted in the exact order I add them.
However, for the web interface of this experimental thing, I made a re-ordering interaction where I can simply drag and drop elements to reorder them.
My question is: how can I reflect this change to the database?
Or more in general terms, how can I order stuff in general, in databases?
For instance if I drag the 1000th item to 1st order, everything below needs to be edited (in db) between 1 and 1000 the entries. This does not seem like a valid and proper solution to me.
Any enlightenment is appreciated.
An elegant way might be lexicographic sorting. Introduce a String attribute for each item. Make the initial length of the values large enough to accomodate the estimated number of items. E.g., if you expect 1000 items, let the keys be baa, bab, bac, ... bba, bbb, bbc, ...
Then, when an item is moved from where it is to another place between two items, assign a value to the sorting attribute of the moved item that is somewhere equidistant (lexicographically) to those items. So to move an item between dei and dej, give it the value deim. To move an item between fadd and fado, give it the value fadi.
Keys starting with a were initially not used to leave space for elements that get dragged before the first one. Never use the key a, as it will be impossible to move an element before this one.
Of course, the characters used may vary according to the sort order provided by the database.
This solution should work fine as long as elements are not reordered extremely frequently. In a worst case scenario, this may lead to longer and longer attribute values. But if the movements are somewhat equally distributed, the length of values should stay reasonable.

Limit amount of content elements in a column in a backend layout of TYPO3

I have to implement a homepage in TYPO3 so that it has a different look and a different backend_layout than the subpages. In one column the editors can insert boxes as content elements. I want to have a maximum limit of boxes (or an exact amount) that can be inserted, e.g. 4 boxes.
Is there any way to achieve this?
The only limitation that I found was the type of content elements not their amount.
You will need to write a special PHP function for that.
TYPO3 offers a system-wide principle called "hooks".
A hook can intercept the data flowing through the system and modify the behaviour of TYPO3.
There are a lot of hooks in several vital places so you should be able to tackle the issue.
In your rather special case you will need several hooks that will change the several places where an editor could add content elements.
You could circumvent some of these places by disallowing the editor to see them (which might in turn reduce the amount of hooks you need to use).
Another thing you could to is to restrict the amount of content elements that are being rendered in the Frontend via Typoscript.
Take a look at select.limit.
Keep in mind that this might look odd to the editors which can't tell, why content elements they added in the BE do not show up in the frontend later.
Currently there's no easy way to configure that. The PHP class \TYPO3\CMS\Backend\View\PageLayoutView is responsible for rendering these boxes (content elements) per page and per column (the database field is named "colPos"). Currently the only way would be to XCLASS/override the PageLayoutView class and to post-process the results of method getTable_tt_content().
A better solution would be to introduce this limiting behaviour or to add a hook that allows custom processing of the accordant data (buttons to be shown, like 'new' and 'paste').