Override only the Description field of the Plone standard content type - plone

I would like to ovverride only the "view" of the classic "description field" of the Plone standard content type (Document, Folder, blabla), cause I need to "structure" the text of this field with structured text like:
This is my description<br/>
with many lines<br/>
bla bla<br/>

Changing the template that renders the standard description field to convert newlines to breaks isn't hard, but will require a little care to avoid creating a security hole.
Override the skin layer kss_generic_macros.pt template, either in a theming product or the custom folder.
Then, you may use Products.PythonScripts.standard.newline_to_br to convert newlines to breaks. You'll need to insert the converted text with "structure" to prevent escaping of the breaks.
Since you'll be using "structure," you absolutely must also manually html escape the description (use html_quote from standard) before applying newline_to_br, or you'll create a vector for a XSS attack.
The key section of the macro, when fixed, might read:
<div metal:define-macro="description-field-view"
id="parent-fieldname-description"
tal:define="kss_class python:getKssClasses('description',
templateId='kss_generic_macros', macro='description-field-view');
pps modules/Products.PythonScripts.standard"
tal:condition="context/Description"
tal:attributes="class string:documentDescription$kss_class;">
<span metal:define-slot="inside"
tal:replace="structure python:pps.newline_to_br(pps.html_quote(context.Description()))">Description</span>
</div>

You really don't want HTML in the description field. This field is used in a number of places and expects plain text.
You are best using the approach above to add an additional field with a different name.

If you want to customize the description widget for all content types you could create an adapter using archetypes.schemaextender (notably the ISchemaModifier interface) like this:
from my.product.browser.interfaces import IMyProductLayer
from my.product.widgets import MyCustomWidget
from Products.ATContentTypes.interface.interfaces import IATContentType
from archetypes.schemaextender.interfaces import IBrowserLayerAwareExtender
from archetypes.schemaextender.interfaces import ISchemaModifier
class MyExtender(object):
# you could choose a more specific interface for a more fine grained override
adapts(IATContentType)
implements(IBrowserLayerAwareExtender, ISchemaModifier)
# this will limit out override to this browserlayer
layer = IMyProductLayer
def fiddle(self, schema):
# if you want to customize just the template of the original widget
# see links below
schema['description'].widget=MyCustomWidget(
label='...',
....
)
return schema
and then you can register it like this:
<adapter
factory=".extender.MyExtender"
provides="archetypes.schemaextender.interfaces.ISchemaModifier" />
Don't forget to register your browser layer IMyProductLayer or this adapter will never be used.
More info:
http://pypi.python.org/pypi/archetypes.schemaextender
http://weblion.psu.edu/services/documentation/developing-for-plone/fsd-extender/hiding-fields
https://svn.plone.org/svn/archetypes/MoreFieldsAndWidgets/Products.MasterSelectWidget (a custom widget example)
http://docs.plone.org/old-reference-manuals/plone_3_theming/buildingblocks/skin/templates/customizing-at-templates/customizing-widgets.html (customize just the template of a widget)

Related

How to find all SO10 text modules used in an SAP Adobe Form?

How do I find all the text modules used in an Adobe Form? Is there a table somewhat like TADIR where I could type in the Adobe Form name and the object names (text modules) for that form name will appear?
My answer regarding Smart Forms is perfectly valid for Adobe Forms too. Adobe Forms also have SO11 text elements hard-coded in the form FM code. The only difference is link between Adobe form FM and Adobe form INCLUDE: for some reason there is no link for them in the D010INC table.
However, as your intention is to find text elements of a single form, and not mass searching, you can find out the INCLUDE manually.
Find out Adobe form function module name. You can do this either manually (Test button in SFP tcode)
or via the FP_FUNCTION_MODULE_NAME FM. Resulting Adobe Form function module name should follow /1BCDWB/SM0000XXX pattern.
Open this FM in SE37 and go to function group code (GoTo->Main program). There you will find something like this and there get the name of main INCLUDE, which holds the code.
Then by forward navigation go to INCLUDE code and by simple Find of %textkey-name pattern you can observe all text elements which were utilized in the form.

Adding fields dynamically to Share form

I want to add a text field for each file that is added to the attached package items in alfresco to write notes regarding each file, is it possible to do?
I have implemented something that could be reused for your use case.
You can define a property with multiple values that will contain the list of notes associated with each attachment.
There is a simple trick to post a property with multiple values: add "[]" to the name of the property. For example:
<input id="template_x002e_edit-metadata_x002e_edit-metadata_x0023_default_prop_someco_notes_0"
name="prop_someco_notes[]"
tabindex="0"
type="text"
value="Meeting minutes"
title="Notes"
noderef="workflow://...."
>
<input id="template_x002e_edit-metadata_x002e_edit-metadata_x0023_default_prop_someco_notes_1"
name="prop_someco_notes[]"
tabindex="1"
type="text"
value="Meeting minutes"
title="Notes"
noderef="workflow://...."
>
As you can see, the name of the input ends with []. Both input textfields have the same name.
The Alfresco Form Engine will consider these two inputs as the value for the property with multiple values: "someco:notes".
The bigger problem is that you need to generate this html with some smart javascript and free marker template.
You can write a custom free marker template to render the initial html: if a user opens a task on which documents have been already attached, you will need to generate the list of inputs using a custom control (you can of course start from textfield.ftl).
It won't be easy to generate the initial list because unfortunately Alfresco returns the list of values as a single comma separated value.
You can customise the webscript that injects the model in the free marker template "org.alfresco.web.scripts.forms.FormUIGet" to pass an array instead of a csv.
A quicker and dirtier solution is to split the csv value. In share-config-custom.xml, you can specify what textfield.ftl show use as a separator instead of the comma.
When a user adds/remove elements from the package, you can intercept the update and add/remove the correspondent note. Notice that I have added the filed "noderef" to each input so it is possible to know the relation between the notes and the nodes in the package.
UPDATE:
For the associations (used for example to define the package in a workflow task), Share uses a javascript library called "object finder" (or "object picker"). This library fires an event called "formValueChanged" that you can intercept:
YAHOO.Bubbling.fire("formValueChanged",
{
eventGroup: this,
addedItems: addedItems,
removedItems: removedItems,
selectedItems: selectedItems,
selectedItemsMetaData: Alfresco.util.deepCopy(this.selectedItems)
});

How do I make a node title translatable?

I'd like to translate each node title as a string (using i18n). I'm trying this function in my theme template:
function theme_process_page(&$variables) {
$variables['title'] = t($variables['title']);
}
Yet when I refresh strings, none of my node titles are on the list. Is there something I'm missing?
And to clarify the function name is using my theme name, not the word "theme".
Title is my usual solution for this (I use Entity Translation, it works fine with Title module).
This module replaces node titles by a regular translatable text field. You can choose wich content type titles must be replaced (on the "Manage Field" forms, you'll find a "replace" link in the title row). Pretty useful.
Good luck
You should never use t() to translate user-supplied or variable strings. See the documentation on the function.
That said, there are some solutions, one is to use the built-in language support for entity fields. Following that you should be able to do something like this in a field hook (in a module, not in your template):
$langcode = $field_info['translatable'] ? $content_langcode : LANGUAGE_NONE;
$entity->{$field_name}[$langcode][0]['value'] = t("Salut!");

Why does my content object not show up in the portal_catalog?

I am trying to implement a basic Zope2 content type directly without using dexterity or Archetypes because I need this to be extremely lean.
from OFS.SimpleItem import SimpleItem
from Products.ZCatalog.CatalogPathAwareness import CatalogAware
from persistent.list import PersistentList
class Doculite(SimpleItem, CatalogAware):
""" implement our class """
meta_type = 'Doculite'
def __init__(self, id, title="No title", desc=''):
self.id = id
self.title = title
self.desc = desc
self.tags = PersistentList()
self.default_catalog = 'portal_catalog'
def add_tags(self, tags):
self.tags.extend(tags)
def Subject(self):
return self.tags
def indexObject(self):
self.reindex_object()
From an external method I am doing this:
def doit(self):
pc = self.portal_catalog
res1 = pc.searchResults()
o1 = self['doc1']
o1.add_tags(['test1', 'test2'])
o1.reindex_object()
res2 = pc.searchResults()
return 'Done'
I clear the catalog and run my external method. My object does not get into the catalog. But from the indexes tab, when I browse the Subject index, I can see my content item listed with the values. Both res1 and res2 and empty.
Why is my content item not showing up inside the searchResuts() of the catalog?
Plone is a full-fat content management system, if you're after something lean it's probably not the right choice (perhaps try Pyramid.)
For your content type to be a full part of a Plone site it has to fulfil a number of requirements across the Zope2, CMF and Plone layers. plone.app.content.item.Item is about the simplest base class you can get for a content item for a Plone site, though a simpler base class in itself will not really make instances of your content type any more 'lean' - an instance of a class in Python is basically just a dict and a pointer to it's class.
Most of the work on a page view will be rendering the various user interface features of a site. Rendering the schema based add/edit forms of frameworks like Archetypes and Dexterity is also relatively expensive.
I'd spend a little time profiling your application using one of the supported content type systems before putting time into building your own.
In order to see your objects in the "Catalog" tab of the portal_catalog your objects need to have a "getPhysicalPath()" method that returns a tuple representing their path (ex. ('','Plone','myobject')).
Also try to use this:
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
as base class.
You need to register your type with the catalog multiplexer. Look at the configuration in the zmi -> archetypes_tool.
I'm not sure, but you may also need a portal_type registration also...
Like Lawrence said though, you're better off just using one of the current content type frameworks if you want to be able to catalog your data with plone's portal catalog. If you can deal with a separate catalog, take a look at repoze.catalog.
Plone needs every content object to provide an "allowedRolesAndUsers" index to return the object in searchResults.
There is probably a zcml snippet that will enable this for my content type. But I was able to get things working by adding another method as follows:
def allowedRolesAndUsers(self):
return ['Manager', 'Authenticated', 'Anonymous']
CatalogAware will be removed in Zope 4 and then can't be used any more.
cf https://github.com/zopefoundation/Products.ZCatalog/issues/26

Migrating from (now obsolete) custom ATImage content type

We had a whole collection of Plone 3 sites with a custom Image type
subclassed from ATImage. This allowed us to add an extra image scaling to the
standard list ("'logo':(454, 58)", used by our theme package).
While this still works in Plone 4, it isn't really the correct approach now that
plone.app.imaging is part of the standard toolkit. That can define custom scales on
the fly.
It looks like I can enable plone.app.imaging on any type subclassed
from ATImage by simply setting "sizes = None" for the collection of custom
scales on the type. I am however then left with a redundant subclass of ATImage.
Looking long term, it would be useful to replace all of our existing "FalconImage"
content items (hundreds in total) with standard "Image" content items.
A brief experiment on a test site reveals that if I just walk through the document
tree updating the portal_type attribute from "FalconImage" to "Image" then
the content behaves as an "Image": each object suddenly acquires a
Transform tab and all of the scales defined by ##imaging-controlpanel.
I am sure that there would be fallout from such a brute force approach.
Is there a recommended approach for transforming one type into another?
(I'm happy to add source for our custom ATImage type if anyone thinks that
it is relevant. It is really just a very minimal tweak of ATImage, with a
different collection of sizes on the ImageField)
Yes, there is a recommended approach:
http://pypi.python.org/pypi/Products.contentmigration
The only thing that you have to do is to write a custom migration from FalconImage to Image.
Bye,
Giacomo
You need to use Products.contentmigration but the docs there are no place to start. Use the docs at plone.org for a step-by-step on migrating content.
Thanks to Giacomo and Ross for the pointers.
Just in case it is useful to others, my migration code ended up looking like the following:
from Products.contentmigration.walker import CustomQueryWalker
from Products.contentmigration.archetypes import InplaceATItemMigrator
class FalconImageMigrator(InplaceATItemMigrator):
walker = CustomQueryWalker
src_meta_type = "FalconImage"
src_portal_type = "FalconImage"
dst_meta_type = "ATBlob"
dst_portal_type = "Image"
# Following stolen from plone.app.blob.migrations, ATImageToBlobImageMigrator
# migrate all fields except 'image', which needs special handling...
fields_map = {
'image': None,
}
def migrate_data(self):
self.new.getField('image').getMutator(self.new)(self.old)
# ATFileToBlobMigrator reindexes certain fields. Otherwise we
# need to clear and rebuild the entire catalog.
def last_migrate_reindex(self):
self.new.reindexObject(idxs=['object_provides', 'portal_type',
'Type', 'UID'])
migrator = FalconImageMigrator
walker = migrator.walker(portal, FalconImageMigrator)
walker.go()
print walker.getOutput()
Complications:
Image is a little odd as a destination type, as data gets migrated into the blob store.
We need to update the catalog so that "resolveuid/UID" links generated by TinyMCE
continue to work. last_migrate_reindex() method on Migrator class should be faster than clearing and rebuilding the entire catalog from scratch.

Resources