How to programatically control an object's add-menu list of allowed content types? - plone

I would like pragmatically to control individual objects' add-menu list of allowed content types.
I am building a collection of content types with archgenxml. In one case, I have a simulation class composed of a RangeBase class which has three realizations, valueRange, vectorRange and uniformRange. A simulation can contain exactly one range, i.e., RangeBase's multiplicity is one, so a simulation's add-menu should offer either all three range types or none at all.
To achieve this, I thought to subscribed to the IObjectInitializedEvent and IObjectRemovedEvent events; placing their respective handlers, initializedHook and removedHook, in the RangeBase class. The handlers would solicit an object's list of locally allowed types and remove or add the three ranges accordingly. After perusing the Plone's 'Community Developer Documentation', I thought the initializedHook code might look something like this:
# Set allowed content types
from Products.ATContentTypes.lib import constraintypes
def initializedHook(obj, event):
# Get this range's parent simulation
parent = obj.aq_parent
# Enable constraining
parent.setConstrainTypesMode(constraintypes.ENABLED)
# Remove the three ranges
allowedTypes = parent.getLocallyAllowedTypes()
ranges = ('valueRange','vectorRange','uniformRange')
for range in ranges:
allowedTypes.remove(range)
# Tweak the menu
parent.setLocallyAllowedTypes(allowedTypes)
parent.setImmediatelyAddableTypes(allowedTypes)
Unfortunately, my simulation class has none of these functions.
Is there an adaptor that will provide my simulation class with this functionality, or are there other altogether different approaches to achieve the desired menu behaviour? Any suggestions would be appreciated.

It is possible.
I believe you need to override getLocallyAllowedType()
http://svn.plone.org/svn/collective/Products.ATContentTypes/trunk/Products/ATContentTypes/lib/constraintypes.py
AT was written time before adapters, so AT is not using it.
I suggest you could also update the documentation regarding this... it is pretty common use case.
http://web.archive.org/web/20101010142032/http://collective-docs.plone.org/content/creating.html

After several unsuccessful attempts at tweaking _allowedTypes(), I followed the last suggestion at http://plone.org/documentation/kb/restrict-addable-types and customized getNotAddableTypes.py. My customization merely lists a folder's contents filtering for the three ranges. If the resulting array is not empty, I add the three range types to the list:
# customize this script to filter addable portal types based on
# context, the current user or other criteria
ranges = []
ranges = context.listFolderContents(contentFilter={'portal_type':
('VectorRange','ValueRange','UniformRange')})
return {True: ('Favorite', 'VectorRange', 'ValueRange', 'UniformRange'),
False: ('Favorite')}[len(ranges)]

See the last post here for two possibilities: http://plone.293351.n2.nabble.com/Folder-constraints-not-applicable-to-custom-content-types-td6073100.html

The method
foo.getLocallyAllowedTypes()
gives back a tuple, that you just have to copy / filter into another tuple / list, because it's immutable.
allowed_types = parent.getLocallyAllowedTypes()
filtered_types = []
for v in allowed_types:
if not v in ranges:
filtered_types.append(v)
Then you can just give that tuple to the setter method
parent.setLocallyAllowedTypes(filtered_types)
and your're done. But if you want to access the parent during object creation to restrict content types of the folder, you creating the object in, you can hook up in at_post_create_script() and manage_beforeDelete() from BaseObject. This works great for me, restricting the number of specific content types to a folder and also corrects the AllowedTypes when the object gets deleted.

Related

How do you apply src_indices in promotes_inputs=[], when you have multiple promoted inputs?

The docs only show examples for when a component promotes a single input. How do I use src_indices to indicate that only one of my promoted inputs takes a certain slice?
p.model.add_subsystem('ComputeWakePosition', ComputeWakePosition(num_wake_points_per_side=4),
promotes_inputs=['wake_upper_lengths',
'wake_lower_lengths',
'wake_upper_angles',
'wake_lower_angles',
'displaced_cw_coordinates'], <-- I want to specify src_indices for this input only.
promotes_outputs=['upper_wake_coordinates',
'lower_wake_coordinates'])
I think I would be able to just use connect for that input, but given that everything else I've written doesn't use it, it'd be nice if there was a way to avoid it.
There is a function called promotes that you can call on your group after you've added a subsystem. In your code above, you could remove the promotion of the displaced_cw_coordinates variable from your add_subsystem call and make a separate call something like this p.model.promotes('ComputeWakePosition', inputs=['displaced_cw_coordinates'], src_indices=[2,4,6,8])

Why window.getComputedStyle in Flow returns `any` type?

According to MDN getComputedStyle(element) returns CSSStyleDeclaration object. But Flow tells that return type of method is any. Can I fix this without writing own type declaration?
It doesn't look like there is any particular reason, just that it hasn't yet been given a better type. You can see in the DOM libdef file that it is declared as returning any, and that line hasn't been touched in over four years.
This would be an easy first PR, if you are interested in improving it.

<adlcp:data> & <adlcp:map>element not understandable

I am working on the SCORM 2004 4th edition based LMS, where i am at the initial stage.
Hence, i am reading SCORM based documents.
In the SCORM 2004 4th edition CAM document, i was stuck at the page CAM-3-37, where the element adlcp:data is defined as the container used to define sets of data shared associated with an
activity.
and the child element of adlcp:data i.e; map is defined as
The element is the container used to describe how an activity will utilize a specific
set of shared data.
I thought, I may understand it as I will move forward in the said book.
But, I completed the CAM book and yet i am unable to get it how those two tags work.
And also, let's take an example into the consideration, which is as follows:
<adlcp:data>
<adlcp:map targetID="com.scorm.golfsamples.sequencing.forcedsequential.notesStorage" readSharedData="true" writeSharedData="true"/>
</adlcp:data>
where, readSharedData attribute indicates that
currently available shared data will be utilized by the activity while it is active.
and writeSharedData attribute indicates that
shared data should be persisted (true or false) upon termination ( Terminate(“”)
) of the attempt on the activity.
Here in this case,
i didn't get what this targetID= com.scorm.golfsamples.sequencing.forcedsequential.notesStorage indicates.
i didn't get what is this shared data? and where is it located? what is it actually?
Can anyone help me in understanding the above described elements?
adlcp:data is a way to define space on the LMS to store information that doesn't fit in the CMI data model, or that you want to make accessible across SCOs.
There are 3 pieces to defining this space.
1. adlcp:sharedDataGlobalToSystem attribute on the element, which says whether shared data is available for one attempt or for every attempt (aka is it wiped out each time the learner takes the course).. See CAM-3-27
2. adlcp:data & adlcp:map elements list the space(s) you want made available for that SCO. You define an ID for each storage space, and then add access controls meaning whether or not the SCO can read or write to the storage space. (See CAM-3-37)
Those two set up the LMS storage and behaviors for each SCO in the content package.
The final piece is described in section 4.3 of the RTE book. To access the data storage spaces, you use the SCORM API GetValue and SetValue requests and the data model element adl.data.n.store.
One additional note, since the id order is not necessarily maintained, you will need to loop through the adl.data stores in the SCO and determine which index goes to which ID.
Tom Creightons answer is a very good explanation of the shared buckets implementation.
I am just adding a few pointers we found in our implentation.
The data saved is for the "learner", and can be accessed and set across different SCO's or courses assigned to the learner. Beware though, if you are using SCORM Cloud, Clear GLobals button will clear the data for all courses assigned to the user.
While Tom mentions that adlcp:sharedDataGlobalToSystem is attempt specific, SCORM Cloud support says that it is restricted to course/SCO. I have yet to get clarity on that.
There might be a limit on the number of buckets being saved. I have yet to confirm this and will update this reply shortly.
For those looking for more info on implementation:
Add this to your item (organisation > item) in the manifest:
<adlcp:data>
<adlcp:map targetID="mybucketname" readSharedData="true" writeSharedData="true"/>
</adlcp:data>
JS part (Use your API calls in place of LMSGetValue and LMSSetValue)
var dataBucketsCount = LMSGetValue("adl.data._count");
dataBucketsCount = parseInt(dataBucketsCount);
for (var i=0; i < dataBucketsCount; i++){
if (LMSGetValue("adl.data." + i + ".id") == "mybucketname"){
//do your processing with the data
}
}
I had to search a lot for this and try and fail multiple times until we got this right. So I have added this here, so in future it might help someone.

Navigating along rapid.xml nodes

This question slightly differs from check for variable number of sibling nodes & different siblings in Rapidxml. In most of the examples I have found on the web I see hard coded keys, for example:
xml_node<>* root = doc.first_node("rootnode");
Here "rootnode" is hard coded. My situation is slightly different, in the sense that parsing and tagging is already done by rapidxml. I need to know the names as read by the parser by iterating over nodes and their sibling without knowing the hard coded name and the depth of the node. I am looking for the suggestion/solution on some kind of recursive navigation along rapidxml tree.
Thank you.
According to RapidXml's documentation (see here) the node name parameter in first_node() method is optional. You can just omit that parameter and you will get the first child node, regardless of its name:
xml_node<>* root = doc.first_node();
Then you can just get node's name by calling its name() method.

How to get a LPITEMIDLIST pointer according to the path of a folder?

I want to get the system icon of a specified folder, but maybe the only way to retrieve the icon is to use SHGetFileInfo() method. The first parameter of SHGetFileInfo() method is a pointer of LPITEMIDLIST.
If I only have the absolute path of the folder, how can I get the pointer according to the path?
SHParseDisplayName().
Welcome to the wonderful world of PIDLs.
You can read more at Introduction to the Shell Namespace, but basically a PIDL is a Pointer to an item ID List. You can think of it as a linked list in contiguous memory, but instead of each node having a pointer to the next node, you instead have the cb member which is the Count of Bytes that are contained the item, so you can add that to the base address to get the next item. IDLists are terminated with an item with { cb = 0, abID = NULL }.
So, what's in these magic lits? Basically you don't care and can't know. Any IShellFolder implementation can create a new type of ID to represent its type of item in the shell namespace. The basic file system view that the Shell implements just stores the parts of the path in these lists, so you have something like "c:\" in the first one "Users\" in the next one, etc. In reality they are serialized structs (or classes) that may contain more data. But they can also represent printers, network shares, database searches (for search folders, stacks, etc).
All you really need to know is you can ask IShellFolders to give you a PIDL that represents the items they contain, and later on you can give that PIDL back to them, and other various Shell functions and interfaces, and they know how to deal with them. What SHParseDisplayName() basically does (I think) is go through the registry looking for all registered IShellFolder implementations and asks them if they know what to do with the string you pass in, and the first one to handle it makes the PIDL and gives it back.

Resources