I've written a Data Extender class and editor extension that properly displays a few additional columns for items as you browse lists in the CME (folders and structure groups). I had to register my class to handle commands like GetList, GetListSearch, GetListUserFavorites, and GetListCheckedOutItems.
What I've noticed is that the code gets run even when a list of say, schemas is loaded for a drop-down list in the CME (like when creating a new component and you get the list of schemas in a drop-down). so, even though my additional data columns aren't needed in that situation, the code is still being executed and it slows things down.
It seems that it's the GetList command called in those situations. So, I can't just skip processing based on the command. So, I started looking at the XML that the class receives for the list and I've noticed when the code is run for the drop-downs, there's a Managed="0" in the XML. For example:
For a Structure Group list: <tcm:ListItems Managed="64" ID="tcm:103-546-4">
For a Folder list: <tcm:ListItems Managed="16" ID="tcm:103-411-2">
But for a Schema list: <tcm:ListItems ID="tcm:0-103-1" Managed="0">
For a drop-down showing keyword values for a category: <tcm:ListItems Managed="0" ID="tcm:103-506-512">
So, can I just use this Managed="0" as a flag to indicate that the list being processed isn't going to show my additional columns and I can just quit processing?
Managed value is representation of what items can be created inside OrganizationItem:
64 means you can create pages
16 means you can create components
10, for example would mean you can create folders (2) + schemas (8)
518 - folders (2) + structure groups (4) + categories (512)
The value is 0 for non organizational items.
Value depends on the item itself (you can't create pages in folder, for example), as well as on security settings you have on publication and organizational item
Unfortunately CME can't offer right now that kind of granularity level to allow you to tell in a data extender where a particular WCF API call is coming from. Our WCF API is not context aware yet. It may change in the future.
Trusting Managed="0" is not a great idea.
The reason for that is the model lists are client cached per filter. In the current design the filter has CM related data and nothing related to the context the request is being fired from.
Typically the client user interface is reusing cached model data whenever is possible. For instance the same model list could be used in the CME dashboard and a drop down control placed into some item view, but with different xml list definitions: the first one will have more columns defined in the list definition than the latter. They are basically different views of the same data.
Therefore you may want to think of different solutions for your problem.
Now... where is the data behind those additional columns is coming from? Is it Tridion CM or a third party provider?
Sometimes the web server caching may provide an acceptable way to improve the response times. But that's the kind of design you should evaluate and decide upon.
I think you would have a more robust solution if you read the ID of the list, and only execute your code for lists of type 2 and 4 (Folders and Structure Groups respectively). but that won't help you with search views etc.
From previous experience and what User978511 says the Managed attribute is an indication of item types that can be created from the context of that list.
Unfortunately that means that the Managed attribute may well be 0 for any user that doesn't have sufficient rights to create items. E.g. check what Managed is in a Structure Group for a user that isn't allowed to create Pages or Structure Groups. It may well be 0 in that case too, meaning it is useless for your situation.
Update
You may be able to reach your goal better by looking at the columns parameter:
context.Parameters["columns"]
In a few tests I've run I get different values, depending on whether I get a list for the main list view, the tree or a drop down list.
543
23
7
Those values are a bit mask of these constants (from Constants.js):
/**
* Defines the column filter.
* Used to specify which attributes should be included in XML list data.
* #enum
*/
Tridion.Constants.ColumnFilter =
{
ID: 1,
ID_AND_TITLE: 3,
DEFAULT: 7,
EXTENDED: 15,
ALLOWED_ACTIONS: 16,
VERSIONS: 32,
INTERNALS: 64,
URL: 128,
XML_NAME: 256,
CHECK_OUT_USER: 512,
PUBTITLE_AND_ITEM_PATH: 1024
};
So from my limited testing it seems that drop downs request DEFAULT columns, while the main list view and the tree both have ALLOWED_ACTIONS in there. This makes sense to me, since the user gets can interact with the list items in the tree and list view, while they can only select them in the drop downs. So checking for the presence of ALLOWED_ACTIONS in the columns parameter might be one way to reduce the number of places where your data extender adds information.
Related
I'm trying to get a better understanding of what Rows and Rowsets are used for in PeopleCode? I've read through PeopleBooks and still don't feel like I have a good understanding. I'm looking to get more understanding of these as it pertains to Application Engine programs. Perhaps walking through an example may help. Here are some specific questions I have:
I understand that Rowsets, Row, Record, and Field are used to access component buffer data, but is this still the case for stand alone Application Engine programs run via Process Scheduler?
What would be the need or advantage to using these as opposed to using SQL objects/functions (CreateSQL, SQLExec, etc...)? I often see in AE programs where the CreateRowset object is instantiated and uses a .Fill method with a SQL WHERE Clause and I don't quite understand why a SQL was not used instead.
I've seen in PeopleBooks that a Row object in a component scroll is a row, how does a component scroll relate to the row? I've seen references to rows having different scroll levels, is this just a way of grouping and nesting related data?
After you have instantiated the CreateRowset object, what are typical uses of it in the program afterwards? How would you perform logic (If, Then, Else, etc..) on data retrieved by the rowset, or use it to update data?
I appreciate any insight you can share.
You can still use Rowsets, Rows, Records and fields in stand alone Application Engines. Application Engines do not have component buffer data as they are not running within the context of a component. Therefore to use these items you need to populate them using built-in methods like .fill() on a rowset, or .selectByKey() on a record.
The advantage of using rowsets over SQL is that it makes the CRUD easier. There are built-in methods for selecting, updating, inserting and deleting. Additionally you don't have to worry about making a large number of variables if there were multiple fields like you would with a SQL object. Another advantage is when you do the fill, the data is read into memory, where if you looped through the SQL, the SQL cursor would be open longer. The rowset, row, record and field objects also have a lot of other useful methods such as allowing you to executeEdits (validation) or copy from one rowset\row\record to another.
This question is a bit less clear to me but I'll try and explain. If you have a Page, it would have a level 0 row. It then could have multiple Level 1 rowsets. Under each of those it could have a level 2 rowsets.
Level0
/ \
Level1 Level1
/ \ / \
Level2 Level2 Level2 Level2
If one of your level1 rows had 3 rows, then you would find 3 rows in the Rowset associated with that level1. Not sure I explained this to answer what you need, please clarify if I can provide more info
Typically after I create a rowset, I would loop through it. Access the record on each row, do some processing with it. In the example below, I look through all locked accounts and prefix their description with LOCKED and then updated the database.
.
Local boolean &updateResult;
local integer &i;
local record &lockedAccount;
Local rowset &lockedAccounts;
&lockedAccounts = CreateRowset(RECORD.PSOPRDEFN);
&lockedAccounts.fill("WHERE acctlock = 1");
for &i = 1 to &lockedAccounts.ActiveRowCount
&lockedAccount = &lockedAccounts(&i).PSOPRDEFN;
if left(&lockedAccount.OPRDEFNDESCR.value,6) <> "LOCKED" then
&lockedAccount.OPRDEFNDESCR.value = "LOCKED " | &lockedAccount.OPRDEFNDESCR.value;
&updateResult = &lockedAccount.update();
if not &updateResult then
/* Error handle failed update */
end-if;
end-if;
End-for;
I would like to define a field, where there is a list of allowed values as well as give user the option to type it in. For example, I list a bunch of previous jobs that the applicant can have, plus have them pick other and fill it in as well.
Is it possible to do this with one field or do I need two fields where the user has to type it in? Is there a doc. or sample or tutorial I can look up? Thanks.
Here is a super simple Tags sample:
https://drive.google.com/open?id=0BxtQI4fTAVQqcUx4OUJfQ1JYV2c
To cover your exact use case you just need to:
Add logic to check if record already exists
1.1 If record doesn't exist, then create one
Create relation between records
If you don't care about duplicates in your database, then you can skip step 1 and always do 1.1 and 2.
I need to create a report using two data sources in cognos 10.
A master source has a "column" named id which i want to use as second data source parameter.
I create a master detail relationship from id to my parameter, but to all details id from first row is passed.
How to change it to a situation where id from row is passed?
A detail(x) is a effect of second data source with passed parameter.
You should be using two different queries and two different list objects. The detail list should be nested inside the parent list. They should then be linked via the Master-Detail Relationships property. Double-check the join, as many developers are prone to misclicks on this screen.
Here is a good walkthrough on creating what I described above.
There is a way to hide a specific object of my catalog results?
I have a configuration file that I don't want to show.
I'm filtering by id, but it seems so ugly.
from Products.CMFCore.utils import getToolByName
def search(context):
catalog = getToolByName(context, 'portal_catalog')
items = catalog()
for item in items:
if item.id != "config_file":
'do something'
If you are already hiding the object from the navigation tree, you can filter on the same property by testing for exclude_from_nav:
items = catalog()
for item in items:
if item.exclude_from_nav:
continue
# do something with all objects *not* excluded from navigation.
It is harder to filter out things that don't match a criteria. Using a test on the brain object like the above is a perfectly fine way to remove a small subset from your result set.
If you need handle a larger percentage of 'exceptions' you'll need to rethink your architecture, perhaps.
With Products.AdvancedQuery you can create advanced queries and filtering on catalog results. See also this how to.
In the general case, setting a content item's expiration date to some past date hides it from search results (so long as the user does not have the Access inactive portal content permission).
It's an easy way to hide pieces of content that should be visible to all, but that you don't want cluttering up search results e.g. a Document that serves as the homepage.
I always use 1st Jan 2001 as the date so I can easily recognise when I've used this little 'hack'.
I've a problem with my Qt/interview application. I use QTreeView to display tree data. I implemented my own model based on QAbstractItemModel.
I get a following error prior to application crash. It happens often after I add new record.
Could You explain to me what is the meaning of this error. What is a QPersistentModelIndex ?
I'm not using QPersistentModelIndex in my code.
ASSERT failure in QPersistentModelIndex::~QPersistentModelIndex: "persistent model indexes corrupted"
Thanks.
QPersistentModelIndexes are (row, column, parent) references to items that are automatically updated when the referenced items are moved inside the model, unlike regular QModelIndex. For instance, if you insert one row, all existing persistent indexes positioned below the insertion point will have their row property incremented by one.
You may not use them directly, but QTreeView does, to keep track of expanded items and selected items, for example.
And for these persistent indexes to be updated, you have to call the functions QAbstractitemModel::beginInsertRows() and endInsertRows() around the actual row insertion(s) when you add new records.
See the end of the section about subclassing model classes for details: http://doc.trolltech.com/latest/qabstractitemmodel.html#subclassing
I found this method QAbstractItemModel::persistentIndexList and I'm
wondering what indexes it should return. All of them ?
Should this method return all nodes currently visible in the TreeView ?
That method returns only the indexes for which a QPersistentIndexModel was created and is still in scope (as a local variable, a class member, or in a QList<QPersistentIndexModel> for example).
Expanded or selected nodes are not necessarily currently visible, so you can't (and shouldn't anyway) assume anything about what these persistent indexes are used for.
You just have to keep them updated, and you only need to use persistentIndexList for big changes in the model, like sorting (see QTreeWidget internal model : QTreeModel::ensureSorted(link)), for smaller incremental changes you have all the beginXxxRows/beginXxxColumns and endXxxRows/endXxxColumns methods.