Automatic id rename after title change - plone

What could be the best way to achieve a functionality that
All items get new id based no title edits automatically, no need to rename items manually
This would use the id normalization + counter format used by Plone by default
Can be enabled globally for all Archetypes content types

Yuri's on the right track - for a quick and very dirty, I believe this will work:
context.setTitle('lorem ipsum')
context.unmarkCreationFlag()
context.processForm()
Strictly, that's not what the documentation says - as it shouldn't do a rename if it doesn't have the temporary ID generated in the portal factory, but I was getting renaming happening when I modified titles of objects that still had the CreationFlag marked

Apart from the fact that this is a bad idea (all your URLs break every time you edit a title), I'd do this with a custom event. You'll have to copy some of the functionality built into Archetypes used when renaming objects on create, because you don't want to rename every time you edit:
from Products.Archetypes.interfaces import (
IBaseObject,
IObjectEditedEvent,
)
import re
from zope.component import adapter
endsWithNumber = re.compile('-\d+$')
#adapter(IBaseObject, IObjectEditedEvent)
def renameOnEdit(obj, event):
old_id = obj.getId()
without_number = endsWithNumber.sub('', old_id)
# New id based on Title
new_id = obj.generateNewId()
if new_id == old_id or new_id == without_number:
# No change
return
new_id = obj._findUniqueId(new_id)
if new_id is None:
# Couldn't find a new unique id (out of sequence numbers?)
return
obj.setId(new_id)

I think archetypes has this built in:
http://plone.org/documentation/kb/richdocument/controlling-creation
If you need more fine-grained control over how titles are generated, you can re-define the _renameAfterCreation() method from 'Archetypes/BaseObject.py'
So redefining it should be the way.

Related

Set return value as environment variable

I would like to set the return value of an auth call (a token) as an environment variable so that other calls can make use of the value and I don't have to change that value manually.
Can Paw already do this easily?
If not, how would I lay out an extension to achieve this?
The best is that you set your OAuth dynamic value in your header field, then copy the value (select the text and hit Cmd + C):
Go to edit your environments and paste the dynamic value (Cmd + V) in a new variable:
Now go back to your header, and start to write the name of the environment variable you just created, it will offer you to use this variable:
You should be good to go now:
If later you need a change in your token, you can go back to the environments and edit it there, all at the same place.

In Plone, how do I fix Archetypes content that is missing the cmf_uid() annotation?

I'm using Plone 4.3 with relstorage and I've somehow managed to lose the cmf_uid annotation on some of my content objects. This prevents collective.iterate from being able to check in content. Is there an easy way to have Plone walk through the database and re-add cmf_uid where it is missing? Already tried collective.catalogcleanup to no avail.
Here is a script that searches the portal (passed as context) for any Document that has a non-unique cmf_uid. Many of these documents actually have no cmf_uid but the indexed cmf_uid actually comes from the parent folder via Acquisition. Since the manifestation of the problem was that plone.app.iterate was unable to check in Document, the script adds a unique cmf_uid to just the Document types that appear to have non-unique cmf_uid but actually have no cmf_uid.
Although this adds cmf_uid to all Document, it would probably be sufficient to only add the attribute to documents currently being edited in checkouts.
"""
Add missing cmf_uid to Archetypes content.
cmf_uid is required to check in working copies of content.
"""
from Products.CMFUid.UniqueIdHandlerTool import UniqueIdError
from Acquisition import aq_inner
from collections import Counter
def add_missing_uids(context):
"""
context: the portal
"""
portal_uidhandler = context.portal_uidhandler
portal_uidgenerator = context.portal_uidgenerator
catalog = context.portal_catalog
brains = catalog.unrestrictedSearchResults()
freq = Counter(x.cmf_uid for x in brains)
for brain in brains:
# If it's only in use once then it's unique enough. Otherwise it's
# probably inheriting its indexed cmf_uid via Acquisition.
if freq[brain.cmf_uid] < 2 or brain.portal_type != 'Document':
continue
ob = aq_inner(brain.getObject())
if not portal_uidhandler.queryUid(ob):
print brain.Type, brain.portal_type, brain.getPath()
for i in range(3):
try:
portal_uidhandler.setUid(ob, portal_uidgenerator())
ob.reindexObject()
ob.reindexObject(idxs=['modified'])
break
except UniqueIdError:
print "RETRY"
else:
print "FAIL"

How can I ask the state for a content object?

At the end of this tutorial several object attributes are listed. But I need access to the state (published, private,...). I also search that attribute using dir() but I don't see an attribute named as state or something similar. i.e, I need something like this:
>>> app.Plone.foo.bar.state
"published"
Or to keep your code more readable and not having to remember strange method names, you can use plone.api to do this:
from plone import api
api.content.get_state(obj=your_object)
Of course, you need to add plone.api to your eggs first, and re-run buildout.
You can always use the plone_workflow to determine current status:
workflowTool = getToolByName(self.portal, "portal_workflow")
status = workflowTool.getStatusOf("plone_workflow", object)
# where "object" is your content object
print (status)
Unfortunately there is no "state" attribute. Instead, check review_state using the workflow tool e.g.:
>>> app.Plone.portal_workflow.getInfoFor(app.Plone.foo.bar, "review_state")

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

Qt - Selecting multiple folders/directories using a dialog

I want to achieve something like the following:
Where I can select multiple folders across multiple drives and retrieve the folder paths of those selected. Qt only has a crude multi-folder selection feature, but it does not support selected folders from other drives etc.
Can anyone guide me on how to create such a dialog? Better yet, does any one have any sample code I could use (this is an extension to an old project, and I'd much rather save my time and not re-invent the wheel!)
Thanks
You can use QFileSystemModel for represent filesystem on QTreeView. This example explains how to do that.
For checkbox issue, according to this list archives:
The simplest way to do this (I can think of, at least) is to subclass
QDirModel and override flags, data and setData:
flags should add Qt::ItemIsUserCheckable to the returned flags
data should return the Qt::CheckState of the queried index if the role parameter is Qt::CheckStateRole
setData should store the check state of the index
Or, even better, this should work with a QProxyModel pretty much the
same way (after all, "favor object composition over class
inheritance").
Note that QDirModel class is obsolete. You may not use that on newer Qt versions. I recommend to use QFileSystemModel.
####### Retrieve a list of directories with wxPython-Phoenix - tested on python3.5
### installation instruction for wxPython-Phoenix : https://wiki.wxpython.org/How%20to%20install%20wxPython#Installing_wxPython-Phoenix_using_pip
### modified from : https://wxpython.org/Phoenix/docs/html/wx.lib.agw.multidirdialog.html
import os
import wx
import wx.lib.agw.multidirdialog as MDD
# Our normal wxApp-derived class, as usual
app = wx.App(0)
dlg = MDD.MultiDirDialog(None, title="Custom MultiDirDialog", defaultPath=os.getcwd(), # defaultPath="C:/Users/users/Desktop/",
agwStyle=MDD.DD_MULTIPLE|MDD.DD_DIR_MUST_EXIST)
if dlg.ShowModal() != wx.ID_OK:
print("You Cancelled The Dialog!")
dlg.Destroy()
paths = dlg.GetPaths()
#Print directories' path and files
for path in enumerate(paths):
print(path[1])
directory= path[1].replace('OS (C:)','C:')
print(directory)
for file in os.listdir(directory):
print(file)
dlg.Destroy()
app.MainLoop()

Resources