Alfresco document-derived content is missing <content> - alfresco

I've created a custom content type derived from document. I am trying to use CMIS to query my Alfresco server (tried with 4.2.b and 4.2.c) programmatically for my documents using python cmislib. I have a pyramid server that takes REST calls and sends them to my Alfresco server using CMIS.
I get this error:
2013-04-11 11:19:25,526 | ERROR | Exception when serving /access_manager/search_noauth
Traceback (most recent call last):
File "/home/hbrown/.virtualenvs/access_manager_master/local/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/channel.py", line 329, in service
task.service()
[...]
File "/home/hbrown/workspace/spt/access_manager/access_manager/views/search.py", line 223, in cmis_main
for result in repo.query(whole_query)
File "/home/hbrown/.virtualenvs/access_manager_master/local/lib/python2.7/site-packages/cmislib/model.py", line 2467, in getContentStream
assert(len(contentElements) == 1), 'Expected to find exactly one atom:content element.'
AssertionError: Expected to find exactly one atom:content element.
I am using getContentStream() to retrieve content. Based on the code comment, I'd say it is the correct API call:
>>> doc.getName()
u'sample-b.pdf'
>>> o = open('tmp.pdf', 'wb')
>>> result = doc.getContentStream()
>>> o.write(result.read())
>>> result.close()
>>> o.close()
>>> import os.path
>>> os.path.getsize('tmp.pdf')
117248
The python code in cmislib clearly expects the document to have XML that includes an element named content, and mine does not.
The calling code looks like this:
from cmislib import CmisClient
SERVER = "localhost"
url = "http://{0}:8080/alfresco/cmisatom".format(SERVER)
client = CmisClient(url, 'admin', 'alfresco')
repo = client.defaultRepository
results = repo.query("select * from wg:bulletin")
print results[0].getContentStream().read()
The XML being operated on in getContentStream looks like this:
<atom:entry>
<atom:author>
<atom:name/>
</atom:author>
<atom:id>http://chemistry.apache.org/aWQtMQ==</atom:id>
<atom:published>2013-04-12T03:22:38Z</atom:published>
<atom:title>Query Result id-1</atom:title>
<app:edited>2013-04-12T03:22:38Z</app:edited>
<atom:updated>2013-04-12T03:22:38Z</atom:updated>
<cmisra:object xmlns:ns3="http://docs.oasis-open.org/ns/cmis/messaging/200908/">
<cmis:properties>
<cmis:propertyInteger displayName="Content Stream Length" localName="contentStreamLength" propertyDefinitionId="cmis:contentStreamLength" queryName="b.cmis:contentStreamLength">
<cmis:value>249</cmis:value>
</cmis:propertyInteger>
<cmis:propertyId displayName="Object Type Id" localName="objectTypeId" propertyDefinitionId="cmis:objectTypeId" queryName="b.cmis:objectTypeId">
<cmis:value>D:wg:bulletin</cmis:value>
</cmis:propertyId>
<cmis:propertyString displayName="Version Series Checked Out By" localName="versionSeriesCheckedOutBy" propertyDefinitionId="cmis:versionSeriesCheckedOutBy" queryName="b.cmis:versionSeriesCheckedOutBy"/>
<cmis:propertyId displayName="Version Series Checked Out Id" localName="versionSeriesCheckedOutId" propertyDefinitionId="cmis:versionSeriesCheckedOutId" queryName="b.cmis:versionSeriesCheckedOutId"/>
<cmis:propertyId displayName="Version series id" localName="versionSeriesId" propertyDefinitionId="cmis:versionSeriesId" queryName="b.cmis:versionSeriesId">
<cmis:value>workspace://SpacesStore/1cd2053d-1fc4-4e85-b780-ba80284f0841</cmis:value>
</cmis:propertyId>
<cmis:propertyString displayName="wg:account" localName="account" propertyDefinitionId="wg:account" queryName="b.wg:account"/>
<cmis:propertyString displayName="Version Label" localName="versionLabel" propertyDefinitionId="cmis:versionLabel" queryName="b.cmis:versionLabel">
<cmis:value>1.0</cmis:value>
</cmis:propertyString>
<cmis:propertyBoolean displayName="Is Latest Version" localName="isLatestVersion" propertyDefinitionId="cmis:isLatestVersion" queryName="b.cmis:isLatestVersion">
<cmis:value>true</cmis:value>
</cmis:propertyBoolean>
<cmis:propertyBoolean displayName="Is Version Series Checked Out" localName="isVersionSeriesCheckedOut" propertyDefinitionId="cmis:isVersionSeriesCheckedOut" queryName="b.cmis:isVersionSeriesCheckedOut">
<cmis:value>false</cmis:value>
</cmis:propertyBoolean>
<cmis:propertyString displayName="Last Modified By" localName="lastModifiedBy" propertyDefinitionId="cmis:lastModifiedBy" queryName="b.cmis:lastModifiedBy">
<cmis:value>admin</cmis:value>
</cmis:propertyString>
<cmis:propertyString displayName="Created by" localName="createdBy" propertyDefinitionId="cmis:createdBy" queryName="b.cmis:createdBy">
<cmis:value>admin</cmis:value>
</cmis:propertyString>
<cmis:propertyDateTime displayName="wg:displayUntil" localName="displayUntil" propertyDefinitionId="wg:displayUntil" queryName="b.wg:displayUntil"/>
<cmis:propertyId displayName="Alfresco Node Ref" localName="nodeRef" propertyDefinitionId="alfcmis:nodeRef" queryName="b.alfcmis:nodeRef">
<cmis:value>workspace://SpacesStore/1cd2053d-1fc4-4e85-b780-ba80284f0841</cmis:value>
</cmis:propertyId>
<cmis:propertyString displayName="wg:email" localName="email" propertyDefinitionId="wg:email" queryName="b.wg:email"/>
<cmis:propertyBoolean displayName="wg:isActive" localName="isActive" propertyDefinitionId="wg:isActive" queryName="b.wg:isActive">
<cmis:value>false</cmis:value>
</cmis:propertyBoolean>
<cmis:propertyString displayName="wg:username" localName="username" propertyDefinitionId="wg:username" queryName="b.wg:username"/>
<cmis:propertyBoolean displayName="Is Latest Major Version" localName="isLatestMajorVersion" propertyDefinitionId="cmis:isLatestMajorVersion" queryName="b.cmis:isLatestMajorVersion">
<cmis:value>true</cmis:value>
</cmis:propertyBoolean>
<cmis:propertyId displayName="Content Stream Id" localName="contentStreamId" propertyDefinitionId="cmis:contentStreamId" queryName="b.cmis:contentStreamId">
<cmis:value>store://2013/4/10/15/29/20b185d0-afae-4a7f-a06e-58eab399bdc9.bin</cmis:value>
</cmis:propertyId>
<cmis:propertyString displayName="Name" localName="name" propertyDefinitionId="cmis:name" queryName="b.cmis:name">
<cmis:value>.pythonrc</cmis:value>
</cmis:propertyString>
<cmis:propertyString displayName="Content Stream MIME Type" localName="contentStreamMimeType" propertyDefinitionId="cmis:contentStreamMimeType" queryName="b.cmis:contentStreamMimeType">
<cmis:value>text/plain</cmis:value>
</cmis:propertyString>
<cmis:propertyDateTime displayName="Creation Date" localName="creationDate" propertyDefinitionId="cmis:creationDate" queryName="b.cmis:creationDate">
<cmis:value>2013-04-10T15:29:18.146-04:00</cmis:value>
</cmis:propertyDateTime>
<cmis:propertyString displayName="Change token" localName="changeToken" propertyDefinitionId="cmis:changeToken" queryName="b.cmis:changeToken"/>
<cmis:propertyString displayName="wg:state" localName="state" propertyDefinitionId="wg:state" queryName="b.wg:state"/>
<cmis:propertyDateTime displayName="wg:displayFrom" localName="displayFrom" propertyDefinitionId="wg:displayFrom" queryName="b.wg:displayFrom"/>
<cmis:propertyString displayName="Checkin Comment" localName="checkinComment" propertyDefinitionId="cmis:checkinComment" queryName="b.cmis:checkinComment"/>
<cmis:propertyString displayName="wg:application" localName="application" propertyDefinitionId="wg:application" queryName="b.wg:application"/>
<cmis:propertyId displayName="Object Id" localName="objectId" propertyDefinitionId="cmis:objectId" queryName="b.cmis:objectId">
<cmis:value>workspace://SpacesStore/1cd2053d-1fc4-4e85-b780-ba80284f0841;1.0</cmis:value>
</cmis:propertyId>
<cmis:propertyBoolean displayName="Is Immutable" localName="isImmutable" propertyDefinitionId="cmis:isImmutable" queryName="b.cmis:isImmutable">
<cmis:value>false</cmis:value>
</cmis:propertyBoolean>
<cmis:propertyBoolean displayName="Is Major Version" localName="isMajorVersion" propertyDefinitionId="cmis:isMajorVersion" queryName="b.cmis:isMajorVersion">
<cmis:value>true</cmis:value>
</cmis:propertyBoolean>
<cmis:propertyString displayName="wg:institution" localName="institution" propertyDefinitionId="wg:institution" queryName="b.wg:institution"/>
<cmis:propertyId displayName="Base Type Id" localName="baseTypeId" propertyDefinitionId="cmis:baseTypeId" queryName="b.cmis:baseTypeId">
<cmis:value>cmis:document</cmis:value>
</cmis:propertyId>
<cmis:propertyString displayName="Content Stream Filename" localName="contentStreamFileName" propertyDefinitionId="cmis:contentStreamFileName" queryName="b.cmis:contentStreamFileName">
<cmis:value>.pythonrc</cmis:value>
</cmis:propertyString>
<cmis:propertyDateTime displayName="Last Modified Date" localName="lastModificationDate" propertyDefinitionId="cmis:lastModificationDate" queryName="b.cmis:lastModificationDate">
<cmis:value>2013-04-10T15:29:23.384-04:00</cmis:value>
</cmis:propertyDateTime>
</cmis:properties>
</cmisra:object>
</atom:entry>
There is clearly no XML element named content here for the python code to extract.
Is this a misconfiguration of my custom content document or is it a change in CMIS that cmislib does not track or am I calling the wrong API function to get the content or something else?
Later: the minimum change to fix this is to make calls to either reload or getAllowableActions.
This was the original code:
def cmis_main(props, settings):
"""
Create a CMIS query based on props and execute against Alfresco
"""
def cmis_query(props, mapping):
"""
Create CMIS query of AND-separated OR-clauses
"""
# Code that formats a query string from dictionaries...
cmis_mapping = {
# Dictionary config for call to cmis_query
# Nothing to see here. Move on.
"app_sids": {
"where_fmt": IN_WHERE_FMT,
"key": "{0}:application".format(CMIS_NAMESPACE),
"fn": set_format,
},
}
cmis_url, cmis_user, cmis_password = cmis_args(settings)
cmisclient = CmisClient(cmis_url, cmis_user, cmis_password)
repo = cmisclient.getDefaultRepository()
whole_query = cmis_query(props, cmis_mapping)
logger.debug(whole_query)
return [
{
'name': result.name,
'content': result.getContentStream().read(),
'content_mime_type': result.properties["cmis:contentStreamMimeType"],
}
for result in repo.query(whole_query)
]
And it was broken. So I changed the code to this:
results = list(repo.query(whole_query))
for result in results:
print(result.getAllowableActions())
# or: result.reload()
return [
{
'name': result.name,
'content': result.getContentStream().read(),
'content_mime_type': result.properties["cmis:contentStreamMimeType"],
}
for result in results
]
And it worked. I changed it to this:
results = list(repo.query(whole_query))
for result in results:
pass
return [
{
'name': result.name,
'content': result.getContentStream().read(),
'content_mime_type': result.properties["cmis:contentStreamMimeType"],
}
for result in results
]
And it broke. So the XML does not appear to be fully loaded in the CMISLIB object.

Try doing results[0].reload() before calling getContentStream(). That shouldn't be required but it may force the object to reload with the content element.

Related

Loading CLI output to Cisco Genie/pyats parser?

would like to get some help over here for using Cisco Genie parser. Is it possible to load the output of the CLI command (eg. "show version") into the Genie parser.
My customer pass me the output of "show version" for each of their device. I have no ssh access to their devices for security reason. I'm able to extract the output from a Python script.
But how do I load the CLI output to the Genie parser? Usually what I did is below, but this only applicable if I have ssh connection to the device:
output = device.parse("show version")
So how do I load a output string to the parse and tell it which parser to use?? I'm puzzle...
You can take the following example, here CLI is for "show interface" command:
from genie.libs.parser.ios.show_interface import ShowInterfaces
parser = ShowInterfaces(device= '', context='cli')
parsed_dict = parser.cli(output=str_op)
Here, str_op is the output from CLI command in string format
If you don't have SSH access, I can recommend the TTP module. After adding the CLI output to a notepad, you can write your own template. You can easily parse the data you want. I have given an example below.(show users)
Example Code:
from pprint import pprint
from ttp import ttp
import json
import time
with open("showUsers.txt") as f:
data_to_parse = f.read()
ttp_template = """
<group name="showUsers" method="table">
{{User|re(".?")|re(".*")}} {{Type}} {{Login_Date}} {{Login_Time}} {{Idle_day}} {{Idle_time}} --
{{Session_ID}} {{From}}
</group>
"""
parser = ttp(data=data_to_parse, template=ttp_template)
parser.parse()
# print result in JSON format
results = parser.result(format='json')[0]
print(results)
Example Run:
[
{
"showUsers": [
{
"From": "--",
"Session_ID": "6"
},
{
"Idle_day": "0d",
"Idle_time": "00:00:00",
"Login_Date": "08FEB2022",
"Login_Time": "10:53:29",
"Type": "SSHv2",
"User": "admin"
},
{
"From": "135.244.199.185",
"Session_ID": "132"
},
{
"Idle_day": "0d",
"Idle_time": "00:03:35",
"Login_Date": "09FEB2022",
"Login_Time": "11:32:50",
"Type": "SSHv2",
"User": "admin"
},
{
"From": "10.144.208.82",
"Session_ID": "143"
}
]
}
]

Web-scraping the audio and related text form ganjoor site by colab as Persian Speech to text database

I have tried to gather some audio form ganjoor site to gather Audio files with its texts like those contents that is reading by ... shown below:
By using the google colab, so i have tried different method via this coalb :
https://colab.research.google.com/drive/1ntSbqv6iSrNt2F8eyWTvao5ED9Ot0szi?usp=sharing
And I get this kind of errors that you can see at above colab page:
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:29: DeprecationWarning: use options instead of chrome_options
---------------------------------------------------------------------------
WebDriverException Traceback (most recent call last)
<ipython-input-27-c4b1e303b5e7> in <module>()
28
29 wd = webdriver.Chrome('chromedriver', chrome_options=options)
---> 30 wd.get(url)
31 print(wd.page_source) # re
2 frames
/usr/local/lib/python3.6/dist-packages/selenium/webdriver/remote/errorhandler.py in check_response(self, response)
240 alert_text = value['alert'].get('text')
241 raise exception_class(message, screen, stacktrace, alert_text)
--> 242 raise exception_class(message, screen, stacktrace)
243
244 def _value_or_default(self, obj, key, default):
WebDriverException: Message: unknown error: net::ERR_CONNECTION_TIMED_OUT
(Session info: headless chrome=85.0.4183.83)
or
onnectionError: HTTPSConnectionPool(host='ganjoor.net', port=443): Max retries exceeded with url: /hafez/ghazal/sh1/ (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7f6e23c5c8d0>: Failed to establish a new connection: [Errno 110] Connection timed out',))
So my guess is that this kind of web-scraping needs some proper agent header setting or using some proxy in the setting, and I don't know which header is proper and or what proxy sites or free vpn provider is exist and ...
Update:
According to answer of #baduker, it seams that there is some problem with colab to connect the https://ganjoor.net site and its showing that error again (the #baduker codes added to the related GitHub colab notebooks page:
I would be very grateful if you could scrape one audio with its related texts from the ganjoor site, as one example.
Thanks.
You don't really need the raw power of Selenium to get what you're after.
It can be done with requests, BeautifulSoup, and some re.
The regex module is useful for fetching all the .mp3 source urls from the page source. If you look at it you'll see a <script> tag with some JavaScript and all the urls you need.
Parse it and download the .mp3.
Here's how to do it:
import re
import requests
from bs4 import BeautifulSoup
from shutil import copyfileobj
url = "https://ganjoor.net/hafez/ghazal/sh1/"
page = requests.get(url).text
text = BeautifulSoup(page, "html.parser").find_all("div", {"class": "m2"})
print([t.text.replace("\u200c", "") for t in text])
pattern = re.compile(r"https://i\.ganjoor\.net/a2?/\d+[-a-z]+?\.mp3")
audio_tracks = re.findall(pattern, page)
print(audio_tracks)
for track in audio_tracks:
print(f"Fetching track: {track}...")
with requests.get(track, stream=True) as t, \
open(track.split("/")[-1], "wb") as a:
copyfileobj(t.raw, a)
Output for audio:
2130-ak.mp3
2130-az.mp3
2130-ff.mp3
2130-hr.mp3
2130-mfk.mp3
2130-ml.mp3
2130-ng.mp3
2130-zsh.mp3
And the text:
['که عشق آسان نمود اول ولی افتاد مشکلها', 'ز تاب جعد مشکینش چه خون افتاد در دلها', 'جرس فریاد میدارد که بربندید محملها', 'که سالک بیخبر نبود ز راه و رسم منزلها', 'کجا دانند حال ما سبکباران ساحلها', 'نهان کی ماند آن رازی کز او سازند محفلها', 'متی ما تلق من تهوی دع الدنیا و اهملها']

OpenStack SDK - How to create image with Kernel id and Ramdisk parameters?

I've been trying to create an OpenStack image informing the Kernel Id and Ramdisk Id, using the OpenStack Unified SDK (https://github.com/openstack/python-openstacksdk), but without success. I know this is possible, because the OpenStack CLI have this parameters, as shown on this page (http://docs.openstack.org/cli-reference/glance.html#glance-image-create), where the CLI have the "--kernel-id" and "--ramdisk-id" parameters. I've used this parameter in the terminal and confirmed they work, but I need to use them in python.
I'm trying to use the upload_method, as described here http://developer.openstack.org/sdks/python/openstacksdk/users/proxies/image.html#image-api-v2 but I can't get the attrs parameter right. Documentation only say it is suposed to be a dictionary. Here is the code I'm using
...
atrib = {
'properties': {
'kernel_id': 'd84e1f2b-8d8c-4a4a-8858-77a8d5a93cb1',
'ramdisk_id': 'cfef18e0-006e-477a-a098-593d43435a1e'
}
}
with open(file) as fimage:
image = image_service.upload_image(
name=name,
data=fimage,
disk_format='qcow2',
container_format='bare',
**atrib)
....
And here is the error I'm getting:
File "builder.py", line 121, in main
**atrib
File "/usr/lib/python2.7/site-packages/openstack/image/v2/_proxy.py", line 51, in upload_image
**attrs)
File "/usr/lib/python2.7/site-packages/openstack/proxy2.py", line 193, in _create
return res.create(self.session)
File "/usr/lib/python2.7/site-packages/openstack/resource2.py", line 570, in create
json=request.body, headers=request.headers)
File "/usr/lib/python2.7/site-packages/keystoneauth1/session.py", line 675, in post
return self.request(url, 'POST', **kwargs)
File "/usr/lib/python2.7/site-packages/openstack/session.py", line 52, in map_exceptions_wrapper
http_status=e.http_status, cause=e)
openstack.exceptions.HttpException: HttpException: Bad Request, 400 Bad Request
Provided object does not match schema 'image': {u'kernel_id': u'd84e1f2b-8d8c-4a4a-8858-77a8d5a93cb1', u'ramdisk_id': u'cfef18e0-006e-477a-a098-593d43435a1e'} is not of type 'string' Failed validating 'type' in schema['additionalProperties']: {'type': 'string'} On instance[u'properties']: {u'kernel_id': u'd84e1f2b-8d8c-4a4a-8858-77a8d5a93cb1', u'ramdisk_id': u'cfef18e0-006e-477a-a098-593d43435a1e'}
Already tried to use the update_image method, but without success, passing kernel id and ramdisk id as a strings creates the instance, but it does not boot.
Does anyone knows how to solve this?
what the version of the glance api you use?
I have read the code in openstackclient/image/v1/images.py, openstackclient/v1/shell.py
## in shell.py
def do_image_create(gc, args):
...
fields = dict(filter(lambda x: x[1] is not None, vars(args).items()))
raw_properties = fields.pop('property')
fields['properties'] = {}
for datum in raw_properties:
key, value = datum.split('=', 1)
fields['properties'][key] = value
...
image = gc.images.create(**fields)
## in images.py
def create(self, **kwargs):
...
for field in kwargs:
if field in CREATE_PARAMS:
fields[field] = kwargs[field]
elif field == 'return_req_id':
continue
else:
msg = 'create() got an unexpected keyword argument \'%s\''
raise TypeError(msg % field)
hdrs = self._image_meta_to_headers(fields)
...
resp, body = self.client.post('/v1/images',
headers=hdrs,
data=image_data)
...
and openstackclient/v2/shell.py,openstackclient/image/v2.images.py(and i have debuged this too)
## in shell.py
def do_image_create(gc, args):
...
raw_properties = fields.pop('property', [])
for datum in raw_properties:
key, value = datum.split('=', 1)
fields[key] = value
...
image = gc.images.create(**fields)
##in images.py
def create(self, **kwargs):
"""Create an image.""
url = '/v2/images'
image = self.model()
for (key, value) in kwargs.items():
try:
setattr(image, key, value)
except warlock.InvalidOperation as e:
raise TypeError(utils.exception_to_str(e))
resp, body = self.http_client.post(url, data=image)
...
it seems that, you can create a image use your way in version 1.0, but in version 2.0, you should use the kernel_id and ramdisk_id as below
atrib = {
'kernel_id': 'd84e1f2b-8d8c-4a4a-8858-77a8d5a93cb1',
'ramdisk_id': 'cfef18e0-006e-477a-a098-593d43435a1e'
}
but the OpenStack SDK seems it can't trans those two argments to the url (because there is no Body define in openstack/image/v2/image.py. so you should modify the OpenStack SDK to support this.
BTW, the code of OpenStack is a little different from it's version, but many things are same.

Create a portal_user_catalog and have it used (Plone)

I'm creating a fork of my Plone site (which has not been forked for a long time). This site has a special catalog object for user profiles (a special Archetypes-based object type) which is called portal_user_catalog:
$ bin/instance debug
>>> portal = app.Plone
>>> print [d for d in portal.objectMap() if d['meta_type'] == 'Plone Catalog Tool']
[{'meta_type': 'Plone Catalog Tool', 'id': 'portal_catalog'},
{'meta_type': 'Plone Catalog Tool', 'id': 'portal_user_catalog'}]
This looks reasonable because the user profiles don't have most of the indexes of the "normal" objects, but have a small set of own indexes.
Since I found no way how to create this object from scratch, I exported it from the old site (as portal_user_catalog.zexp) and imported it in the new site. This seemed to work, but I can't add objects to the imported catalog, not even by explicitly calling the catalog_object method. Instead, the user profiles are added to the standard portal_catalog.
Now I found a module in my product which seems to serve the purpose (Products/myproduct/exportimport/catalog.py):
"""Catalog tool setup handlers.
$Id: catalog.py 77004 2007-06-24 08:57:54Z yuppie $
"""
from Products.GenericSetup.utils import exportObjects
from Products.GenericSetup.utils import importObjects
from Products.CMFCore.utils import getToolByName
from zope.component import queryMultiAdapter
from Products.GenericSetup.interfaces import IBody
def importCatalogTool(context):
"""Import catalog tool.
"""
site = context.getSite()
obj = getToolByName(site, 'portal_user_catalog')
parent_path=''
if obj and not obj():
importer = queryMultiAdapter((obj, context), IBody)
path = '%s%s' % (parent_path, obj.getId().replace(' ', '_'))
__traceback_info__ = path
print [importer]
if importer:
print importer.name
if importer.name:
path = '%s%s' % (parent_path, 'usercatalog')
print path
filename = '%s%s' % (path, importer.suffix)
print filename
body = context.readDataFile(filename)
if body is not None:
importer.filename = filename # for error reporting
importer.body = body
if getattr(obj, 'objectValues', False):
for sub in obj.objectValues():
importObjects(sub, path+'/', context)
def exportCatalogTool(context):
"""Export catalog tool.
"""
site = context.getSite()
obj = getToolByName(site, 'portal_user_catalog', None)
if tool is None:
logger = context.getLogger('catalog')
logger.info('Nothing to export.')
return
parent_path=''
exporter = queryMultiAdapter((obj, context), IBody)
path = '%s%s' % (parent_path, obj.getId().replace(' ', '_'))
if exporter:
if exporter.name:
path = '%s%s' % (parent_path, 'usercatalog')
filename = '%s%s' % (path, exporter.suffix)
body = exporter.body
if body is not None:
context.writeDataFile(filename, body, exporter.mime_type)
if getattr(obj, 'objectValues', False):
for sub in obj.objectValues():
exportObjects(sub, path+'/', context)
I tried to use it, but I have no idea how it is supposed to be done;
I can't call it TTW (should I try to publish the methods?!).
I tried it in a debug session:
$ bin/instance debug
>>> portal = app.Plone
>>> from Products.myproduct.exportimport.catalog import exportCatalogTool
>>> exportCatalogTool(portal)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File ".../Products/myproduct/exportimport/catalog.py", line 58, in exportCatalogTool
site = context.getSite()
AttributeError: getSite
So, if this is the way to go, it looks like I need a "real" context.
Update: To get this context, I tried an External Method:
# -*- coding: utf-8 -*-
from Products.myproduct.exportimport.catalog import exportCatalogTool
from pdb import set_trace
def p(dt, dd):
print '%-16s%s' % (dt+':', dd)
def main(self):
"""
Export the portal_user_catalog
"""
g = globals()
print '#' * 79
for a in ('__package__', '__module__'):
if a in g:
p(a, g[a])
p('self', self)
set_trace()
exportCatalogTool(self)
However, wenn I called it, I got the same <PloneSite at /Plone> object as the argument to the main function, which didn't have the getSite attribute. Perhaps my site doesn't call such External Methods correctly?
Or would I need to mention this module somehow in my configure.zcml, but how? I searched my directory tree (especially below Products/myproduct/profiles) for exportimport, the module name, and several other strings, but I couldn't find anything; perhaps there has been an integration once but was broken ...
So how do I make this portal_user_catalog work?
Thank you!
Update: Another debug session suggests the source of the problem to be some transaction matter:
>>> portal = app.Plone
>>> puc = portal.portal_user_catalog
>>> puc._catalog()
[]
>>> profiles_folder = portal.some_folder_with_profiles
>>> for o in profiles_folder.objectValues():
... puc.catalog_object(o)
...
>>> puc._catalog()
[<Products.ZCatalog.Catalog.mybrains object at 0x69ff8d8>, ...]
This population of the portal_user_catalog doesn't persist; after termination of the debug session and starting fg, the brains are gone.
It looks like the problem was indeed related with transactions.
I had
import transaction
...
class Browser(BrowserView):
...
def processNewUser(self):
....
transaction.commit()
before, but apparently this was not good enough (and/or perhaps not done correctly).
Now I start the transaction explicitly with transaction.begin(), save intermediate results with transaction.savepoint(), abort the transaction explicitly with transaction.abort() in case of errors (try / except), and have exactly one transaction.commit() at the end, in the case of success. Everything seems to work.
Of course, Plone still doesn't take this non-standard catalog into account; when I "clear and rebuild" it, it is empty afterwards. But for my application it works well enough.

Custom command result

When invoking a custom command, I noticed that only the logs are displayed. For example, if my Custom Comand script contains a retrun statement return "great custom command", I can't find it in the result. Both in API Java client or shell execution cases.
What can I do to be able to retrieve that result at the end of an execution?
Thanks.
Command definition in service description file:
customCommands ([
"getText" : "getText.groovy"
])
getText.groovy file content:
def text = "great custom command"
println "trying to get a text"
return text
Assuming that you service file contains the following :
customCommands ([
"printA" : {
println "111111"
return "222222"
},
"printB" : "fileB.groovy"
])
And fileB.groovy contains the following code :
println "AAAAAA"
return "BBBBBB"
Then if you run the following command : invoke yourService printA
You will get this :
Invocation results:
1: OK from instance #1..., Result: 222222
invocation completed successfully.
and if you run the following command : invoke yourService printB
You will get this :
Invocation results:
1: OK from instance #1..., Result: AAAAAA
invocation completed successfully.
So if your custom command's implementation is a Groovy closure, then its result is its return value.
And if your custom command's implementation is an external Groovy file, then its result is its last statement output.
HTH,
Tamir.

Resources