Added a new feature allowing to create objects by importing them from external sources instead of from filling a web form.
This commit is contained in:
parent
cbc7d257d4
commit
4c4b2d0f87
|
@ -8,11 +8,25 @@ r, w, d = ('read', 'write', 'delete')
|
|||
# Descriptor classes used for refining descriptions of elements in types
|
||||
# (pages, groups,...) ----------------------------------------------------------
|
||||
class Page:
|
||||
'''Used for describing a page, its related phase, show condition, etc.'''
|
||||
def __init__(self, name, phase='main', show=True):
|
||||
self.name = name
|
||||
self.phase = phase
|
||||
self.show = show
|
||||
|
||||
class Import:
|
||||
'''Used for describing the place where to find the data to use for creating
|
||||
an object.'''
|
||||
def __init__(self, path, columnMethod=None, columnHeaders=(),
|
||||
sortMethod=None):
|
||||
self.id = 'import'
|
||||
self.path = path
|
||||
self.columnMethod = columnMethod
|
||||
# This method allows to split every element into subElements that can
|
||||
# be shown as column values in a table.
|
||||
self.columnHeaders = columnHeaders
|
||||
self.sortMethod = sortMethod
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Type:
|
||||
'''Basic abstract class for defining any appy type.'''
|
||||
|
|
|
@ -102,14 +102,24 @@ class Generator(AbstractGenerator):
|
|||
msg('move_up', '', msg.REF_MOVE_UP),
|
||||
msg('move_down', '', msg.REF_MOVE_DOWN),
|
||||
msg('query_create', '', msg.QUERY_CREATE),
|
||||
msg('query_import', '', msg.QUERY_IMPORT),
|
||||
msg('query_no_result', '', msg.QUERY_NO_RESULT),
|
||||
msg('query_consult_all', '', msg.QUERY_CONSULT_ALL),
|
||||
msg('import_title', '', msg.IMPORT_TITLE),
|
||||
msg('import_show_hide', '', msg.IMPORT_SHOW_HIDE),
|
||||
msg('import_already', '', msg.IMPORT_ALREADY),
|
||||
msg('import_many', '', msg.IMPORT_MANY),
|
||||
msg('import_done', '', msg.IMPORT_DONE),
|
||||
msg('ref_invalid_index', '', msg.REF_INVALID_INDEX),
|
||||
msg('bad_int', '', msg.BAD_INT),
|
||||
msg('bad_float', '', msg.BAD_FLOAT),
|
||||
msg('bad_email', '', msg.BAD_EMAIL),
|
||||
msg('bad_url', '', msg.BAD_URL),
|
||||
msg('bad_alphanumeric', '', msg.BAD_ALPHANUMERIC),
|
||||
msg('select_delesect', '', msg.SELECT_DESELECT),
|
||||
msg('no_elem_selected', '', msg.NO_SELECTION),
|
||||
msg('delete_confirm', '', msg.DELETE_CONFIRM),
|
||||
msg('delete_done', '', msg.DELETE_DONE),
|
||||
]
|
||||
# Create basic files (config.py, Install.py, etc)
|
||||
self.generateTool()
|
||||
|
|
|
@ -133,7 +133,7 @@ class PloneInstaller:
|
|||
updateRolesForPermission('Add portal content', tuple(allCreators),
|
||||
appFolder)
|
||||
# Creates the "appy" Directory view
|
||||
if not hasattr(site.aq_base, 'appy'):
|
||||
if not hasattr(site.aq_base, 'skyn'):
|
||||
addDirView = self.ploneStuff['manage_addDirectoryView']
|
||||
addDirView(site, appy.getPath() + '/gen/plone25/skin',id='skyn')
|
||||
|
||||
|
@ -266,18 +266,15 @@ class PloneInstaller:
|
|||
current.append(self.toolInstanceName)
|
||||
nvProps.manage_changeProperties(**{'idsNotToList': current})
|
||||
|
||||
# Remove workflow for the tool
|
||||
#wfTool = self.ploneSite.portal_workflow
|
||||
#wfTool.setChainForPortalTypes([self.toolName], '')
|
||||
|
||||
# Create the default flavour
|
||||
self.tool = getattr(self.ploneSite, self.toolInstanceName)
|
||||
self.appyTool = self.tool._appy_getWrapper(force=True)
|
||||
if self.reinstall:
|
||||
self.tool.at_post_edit_script()
|
||||
self.tool.createOrUpdate(False)
|
||||
else:
|
||||
self.tool.at_post_create_script()
|
||||
self.tool.createOrUpdate(True)
|
||||
|
||||
if not self.appyTool.flavours:
|
||||
# Create the default flavour
|
||||
self.appyTool.create('flavours', title=self.productName, number=1)
|
||||
self.updatePodTemplates()
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ NOT_UNO_ENABLED_PYTHON = '"%s" is not a UNO-enabled Python interpreter. ' \
|
|||
'To check if a Python interpreter is UNO-enabled, ' \
|
||||
'launch it and type "import uno". If you have no ' \
|
||||
'ImportError exception it is ok.'
|
||||
jsMessages = ('no_elem_selected', 'delete_confirm')
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class ToolMixin(AbstractMixin):
|
||||
|
@ -206,4 +207,91 @@ class ToolMixin(AbstractMixin):
|
|||
parent = obj.getParentNode()
|
||||
if parent.id == 'skyn': return parent.getParentNode()
|
||||
return rq['PUBLISHED']
|
||||
|
||||
def getAppyClass(self, contentType):
|
||||
'''Gets the Appy Python class that is related to p_contentType.'''
|
||||
# Retrieve first the Archetypes class corresponding to p_ContentType
|
||||
portalType = self.portal_types.get(contentType)
|
||||
atClassName = portalType.getProperty('content_meta_type')
|
||||
appName = self.getProductConfig().PROJECTNAME
|
||||
exec 'from Products.%s.%s import %s as atClass' % \
|
||||
(appName, atClassName, atClassName)
|
||||
# Get then the Appy Python class
|
||||
return atClass.wrapperClass.__bases__[-1]
|
||||
|
||||
def getCreateMeans(self, contentTypeOrAppyClass):
|
||||
'''Gets the different ways objects of p_contentTypeOrAppyClass (which
|
||||
can be a Plone content type or a Appy class) can be created
|
||||
(via a web form, by importing external data, etc). Result is a
|
||||
dict whose keys are strings (ie "form", "import"...) and whose
|
||||
values are additional data bout the particular mean.'''
|
||||
pythonClass = contentTypeOrAppyClass
|
||||
if isinstance(contentTypeOrAppyClass, basestring):
|
||||
pythonClass = self.getAppyClass(pythonClass)
|
||||
res = {}
|
||||
if not pythonClass.__dict__.has_key('create'):
|
||||
res['form'] = None
|
||||
# No additional data for this means, which is the default one.
|
||||
else:
|
||||
means = pythonClass.create
|
||||
if means:
|
||||
if isinstance(means, basestring): res[means] = None
|
||||
elif isinstance(means, list) or isinstance(means, tuple):
|
||||
for mean in means:
|
||||
if isinstance(mean, basestring):
|
||||
res[mean] = None
|
||||
else:
|
||||
res[mean.id] = mean.__dict__
|
||||
else:
|
||||
res[means.id] = means.__dict__
|
||||
return res
|
||||
|
||||
def getImportElements(self, contentType):
|
||||
'''Returns the list of elements that can be imported from p_path for
|
||||
p_contentType.'''
|
||||
appyClass = self.getAppyClass(contentType)
|
||||
importParams = self.getCreateMeans(appyClass)['import']
|
||||
columnMethod = importParams['columnMethod'].__get__('')
|
||||
sortMethod = importParams['sortMethod']
|
||||
if sortMethod: sortMethod = sortMethod.__get__('')
|
||||
elems = []
|
||||
for elem in os.listdir(importParams['path']):
|
||||
elemFullPath = os.path.join(importParams['path'], elem)
|
||||
niceElem = columnMethod(elemFullPath)
|
||||
niceElem.insert(0, elemFullPath) # To the result, I add the full
|
||||
# path of the elem, which will not be shown.
|
||||
elems.append(niceElem)
|
||||
if sortMethod:
|
||||
elems = sortMethod(elems)
|
||||
return [importParams['columnHeaders'], elems]
|
||||
|
||||
def onImportObjects(self):
|
||||
'''This method is called when the user wants to create objects from
|
||||
external data.'''
|
||||
rq = self.REQUEST
|
||||
appyClass = self.getAppyClass(rq.get('type_name'))
|
||||
importPaths = rq.get('importPath').split('|')
|
||||
appFolder = self.getAppFolder()
|
||||
for importPath in importPaths:
|
||||
if not importPath: continue
|
||||
objectId = os.path.basename(importPath)
|
||||
self.appy().create(appyClass, id=objectId)
|
||||
self.plone_utils.addPortalMessage(self.translate('import_done'))
|
||||
return rq.RESPONSE.redirect(rq['HTTP_REFERER'])
|
||||
|
||||
def isAlreadyImported(self, contentType, importPath):
|
||||
appFolder = self.getAppFolder()
|
||||
objectId = os.path.basename(importPath)
|
||||
if hasattr(appFolder.aq_base, objectId):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def getJavascriptMessages(self):
|
||||
'''Returns the translated version of messages that must be shown in
|
||||
Javascript popups.'''
|
||||
res = ''
|
||||
for msg in jsMessages:
|
||||
res += 'var %s = "%s";\n' % (msg, self.translate(msg))
|
||||
return res
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -20,6 +20,10 @@ class AbstractMixin:
|
|||
inherits from this class. It contains basic functions allowing to
|
||||
minimize the amount of generated code.'''
|
||||
|
||||
def getAppyAttribute(self, name):
|
||||
'''Returns method or attribute value corresponding to p_name.'''
|
||||
return eval('self.%s' % name)
|
||||
|
||||
def createOrUpdate(self, created):
|
||||
'''This method creates (if p_created is True) or updates an object.
|
||||
In the case of an object creation, p_self is a temporary object
|
||||
|
@ -54,6 +58,30 @@ class AbstractMixin:
|
|||
else: obj.reindexObject()
|
||||
return obj
|
||||
|
||||
def delete(self):
|
||||
'''This methods is self's suicide.'''
|
||||
self.getParentNode().manage_delObjects([self.id])
|
||||
|
||||
def onCreate(self):
|
||||
'''This method is called when a user wants to create a root object in
|
||||
the application folder or an object through a reference field.'''
|
||||
rq = self.REQUEST
|
||||
if rq.get('initiator', None):
|
||||
# The object to create will be linked to an initiator object through
|
||||
# a ref field.
|
||||
initiatorRes=self.uid_catalog.searchResults(UID=rq.get('initiator'))
|
||||
rq.SESSION['initiator'] = rq.get('initiator')
|
||||
rq.SESSION['initiatorField'] = rq.get('field')
|
||||
rq.SESSION['initiatorTarget'] = rq.get('type_name')
|
||||
if self._appy_meta_type == 'tool':
|
||||
baseUrl = self.getAppFolder().absolute_url()
|
||||
else:
|
||||
baseUrl = self.absolute_url()
|
||||
objId = self.generateUniqueId(rq.get('type_name'))
|
||||
urlBack = '%s/portal_factory/%s/%s/skyn/edit' % \
|
||||
(baseUrl, rq.get('type_name'), objId)
|
||||
return rq.RESPONSE.redirect(urlBack)
|
||||
|
||||
def onUpdate(self):
|
||||
'''This method is executed when a user wants to update an object.
|
||||
The object may be a temporary object created by portal_factory in
|
||||
|
@ -106,6 +134,13 @@ class AbstractMixin:
|
|||
rq.set('fieldset', rq.get('nextPage'))
|
||||
return obj.skyn.edit(obj)
|
||||
|
||||
def onDelete(self):
|
||||
rq = self.REQUEST
|
||||
msg = self.translate('delete_done')
|
||||
self.delete()
|
||||
self.plone_utils.addPortalMessage(msg)
|
||||
rq.RESPONSE.redirect(rq['HTTP_REFERER'])
|
||||
|
||||
def getAppyType(self, fieldName):
|
||||
'''Returns the Appy type corresponding to p_fieldName.'''
|
||||
res = None
|
||||
|
@ -407,7 +442,7 @@ class AbstractMixin:
|
|||
else:
|
||||
return res
|
||||
|
||||
def changeAppyRefOrder(self, fieldName, objectUid, newIndex, isDelta):
|
||||
def changeRefOrder(self, fieldName, objectUid, newIndex, isDelta):
|
||||
'''This method changes the position of object with uid p_objectUid in
|
||||
reference field p_fieldName to p_newIndex i p_isDelta is False, or
|
||||
to actualIndex+p_newIndex if p_isDelta is True.'''
|
||||
|
@ -421,6 +456,27 @@ class AbstractMixin:
|
|||
pass # To implement later on
|
||||
sortedObjectsUids.insert(newIndex, objectUid)
|
||||
|
||||
def onChangeRefOrder(self):
|
||||
'''This method is called when the user wants to change order of an
|
||||
item in a reference field.'''
|
||||
rq = self.REQUEST
|
||||
# Move the item up (-1), down (+1) or at a given position ?
|
||||
move = -1 # Move up
|
||||
isDelta = True
|
||||
if rq.get('moveDown.x', None) != None:
|
||||
move = 1 # Move down
|
||||
elif rq.get('moveSeveral.x', None) != None:
|
||||
try:
|
||||
move = int(rq.get('moveValue'))
|
||||
# In this case, it is not a delta value; it is the new position
|
||||
# where the item must be moved.
|
||||
isDelta = False
|
||||
except ValueError:
|
||||
self.plone_utils.addPortalMessage(
|
||||
self.translate('ref_invalid_index'))
|
||||
self.changeRefOrder(rq['fieldName'], rq['refObjectUid'], move, isDelta)
|
||||
return rq.RESPONSE.redirect(rq['HTTP_REFERER'])
|
||||
|
||||
def getWorkflow(self, appy=True):
|
||||
'''Returns the Appy workflow instance that is relevant for this
|
||||
object. If p_appy is False, it returns the DC workflow.'''
|
||||
|
@ -511,6 +567,38 @@ class AbstractMixin:
|
|||
self.reindexObject()
|
||||
return res
|
||||
|
||||
def onExecuteAppyAction(self):
|
||||
'''This method is called every time a user wants to execute an Appy
|
||||
action on an object.'''
|
||||
rq = self.REQUEST
|
||||
res, msg = self.executeAppyAction(rq['fieldName'])
|
||||
if not msg:
|
||||
# Use the default i18n messages
|
||||
suffix = 'ko'
|
||||
if res:
|
||||
suffix = 'ok'
|
||||
label='%s_action_%s' % (self.getLabelPrefix(rq['fieldName']),suffix)
|
||||
msg = self.translate(label)
|
||||
self.plone_utils.addPortalMessage(msg)
|
||||
return rq.RESPONSE.redirect(rq['HTTP_REFERER'])
|
||||
|
||||
def onTriggerTransition(self):
|
||||
'''This method is called whenever a user wants to trigger a workflow
|
||||
transition on an object.'''
|
||||
rq = self.REQUEST
|
||||
self.portal_workflow.doActionFor(self, rq['workflow_action'],
|
||||
comment = rq.get('comment', ''))
|
||||
# Where to redirect the user back ?
|
||||
urlBack = rq['HTTP_REFERER']
|
||||
if urlBack.find('?') != -1:
|
||||
# Remove params; this way, the user may be redirected to correct
|
||||
# phase when relevant.
|
||||
urlBack = urlBack[:urlBack.find('?')]
|
||||
msg = self.translate(u'Your content\'s status has been modified.',
|
||||
domain='plone')
|
||||
self.plone_utils.addPortalMessage(msg)
|
||||
return rq.RESPONSE.redirect(urlBack)
|
||||
|
||||
def callAppySelect(self, selectMethod, brains):
|
||||
'''Selects objects from a Reference field.'''
|
||||
if selectMethod:
|
||||
|
|
|
@ -2,63 +2,14 @@
|
|||
##bind context=context
|
||||
##parameters=action
|
||||
rq = context.REQUEST
|
||||
urlBack = rq['HTTP_REFERER']
|
||||
|
||||
if action == 'create':
|
||||
# A user wants to create an object.
|
||||
if rq.get('initiator', None):
|
||||
# The object to create will be linked to an initiator object through a
|
||||
# ref field.
|
||||
initiatorRes= context.uid_catalog.searchResults(UID=rq.get('initiator'))
|
||||
rq.SESSION['initiator'] = rq.get('initiator')
|
||||
rq.SESSION['initiatorField'] = rq.get('field')
|
||||
rq.SESSION['initiatorTarget'] = rq.get('type_name')
|
||||
objId = context.generateUniqueId(rq.get('type_name'))
|
||||
urlBack = '%s/portal_factory/%s/%s/skyn/edit' % \
|
||||
(context.getParentNode().absolute_url(), rq.get('type_name'), objId)
|
||||
|
||||
elif action == 'edit': return context.getParentNode().onUpdate()
|
||||
|
||||
elif action == 'appyAction':
|
||||
# Get the object impacted by the action.
|
||||
if rq.get('objectUid', None):
|
||||
obj = context.uid_catalog(UID=rq['objectUid'])[0].getObject()
|
||||
res, msg = obj.executeAppyAction(rq['fieldName'])
|
||||
if not msg:
|
||||
# Use the default i18n messages
|
||||
suffix = 'ko'
|
||||
if res:
|
||||
suffix = 'ok'
|
||||
label = '%s_action_%s' % (obj.getLabelPrefix(rq['fieldName']), suffix)
|
||||
msg = obj.translate(label)
|
||||
context.plone_utils.addPortalMessage(msg)
|
||||
|
||||
elif action == 'changeRefOrder':
|
||||
# Move the item up (-1), down (+1) or at a given position ?
|
||||
obj = context.getParentNode()
|
||||
move = -1 # Move up
|
||||
isDelta = True
|
||||
if rq.get('moveDown.x', None) != None:
|
||||
move = 1 # Move down
|
||||
elif rq.get('moveSeveral.x', None) != None:
|
||||
try:
|
||||
move = int(rq.get('moveValue'))
|
||||
# In this case, it is not a delta value; it is the new position where
|
||||
# the item must be moved.
|
||||
isDelta = False
|
||||
except ValueError:
|
||||
context.plone_utils.addPortalMessage(
|
||||
obj.translate('ref_invalid_index'))
|
||||
obj.changeAppyRefOrder(rq['fieldName'], rq['objectUid'], move, isDelta)
|
||||
|
||||
elif action == 'triggerTransition':
|
||||
obj = context.getParentNode()
|
||||
from Products.CMFPlone import PloneMessageFactory as _
|
||||
context.portal_workflow.doActionFor(obj, rq['workflow_action'],
|
||||
comment=rq.get('comment', ''))
|
||||
if urlBack.find('?') != -1:
|
||||
# Remove params; this way, the user may be redirected to correct phase
|
||||
# when relevant.
|
||||
urlBack = urlBack[:urlBack.find('?')]
|
||||
context.plone_utils.addPortalMessage(
|
||||
_(u'Your content\'s status has been modified.'))
|
||||
|
||||
return rq.RESPONSE.redirect(urlBack)
|
||||
else:
|
||||
obj = context.getParentNode() # An appy obj or in some cases the app folder.
|
||||
if obj.portal_type == 'AppyFolder':
|
||||
from Products.CMFCore.utils import getToolByName
|
||||
portal = getToolByName(obj, 'portal_url').getPortalObject()
|
||||
obj = portal.get('portal_%s' % obj.id.lower()) # The tool
|
||||
return obj.getAppyAttribute('on'+action)()
|
||||
|
|
|
@ -68,14 +68,14 @@
|
|||
|
||||
<body>
|
||||
<metal:fill fill-slot="main">
|
||||
<div metal:use-macro="here/skyn/macros/macros/showPagePrologue"/>
|
||||
<div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
|
||||
<div metal:use-macro="here/skyn/macros/macros/showPageHeader"/>
|
||||
|
||||
<form name="edit_form" method="post" enctype="multipart/form-data"
|
||||
class="enableUnloadProtection atBaseEditForm"
|
||||
tal:attributes="action python: contextObj.absolute_url()+'/skyn/do'">
|
||||
<div metal:use-macro="here/skyn/macros/macros/listFields" />
|
||||
<input type="hidden" name="action" value="edit"/>
|
||||
<input type="hidden" name="action" value="Update"/>
|
||||
<input type="hidden" name="fieldset" tal:attributes="value fieldset"/>
|
||||
<input type="hidden" name="pageName" tal:attributes="value pageName"/>
|
||||
<input type="hidden" name="phase" tal:attributes="value phase"/>
|
||||
|
|
BIN
gen/plone25/skin/eye.png
Normal file
BIN
gen/plone25/skin/eye.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 372 B |
BIN
gen/plone25/skin/import.png
Normal file
BIN
gen/plone25/skin/import.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 705 B |
123
gen/plone25/skin/import.pt
Normal file
123
gen/plone25/skin/import.pt
Normal file
|
@ -0,0 +1,123 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
|
||||
lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"
|
||||
xmlns:metal="http://xml.zope.org/namespaces/metal"
|
||||
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
||||
metal:use-macro="here/main_template/macros/master">
|
||||
|
||||
<tal:comment replace="nothing">Disable standard Plone green tabs</tal:comment>
|
||||
<div metal:fill-slot="top_slot">
|
||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
||||
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
||||
</div>
|
||||
|
||||
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
|
||||
<body>
|
||||
<metal:fill fill-slot="main"
|
||||
tal:define="appFolder context/getParentNode;
|
||||
contentType request/type_name;
|
||||
tool python: portal.get('portal_%s' % appFolder.id.lower());
|
||||
importElems python: tool.getImportElements(contentType);
|
||||
global allAreImported python:True">
|
||||
|
||||
<div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
|
||||
<script language="javascript">
|
||||
<!--
|
||||
var importedElemsShown = false;
|
||||
function toggleViewableElements() {
|
||||
var rows = cssQuery('#importedElem');
|
||||
var newDisplay = 'table-row';
|
||||
if (importedElemsShown) newDisplay = 'none';
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
rows[i].style.display = newDisplay;
|
||||
}
|
||||
importedElemsShown = !importedElemsShown;
|
||||
}
|
||||
|
||||
var checkBoxesChecked = true;
|
||||
function toggleCheckboxes() {
|
||||
var checkBoxes = cssQuery('#cbElem');
|
||||
var newCheckValue = true;
|
||||
if (checkBoxesChecked) newCheckValue = false;
|
||||
for (var i=0; i<checkBoxes.length; i++) {
|
||||
checkBoxes[i].checked = newCheckValue;
|
||||
}
|
||||
checkBoxesChecked = newCheckValue;
|
||||
}
|
||||
|
||||
function importSingleElement(importPath) {
|
||||
var f = document.forms['importElements'];
|
||||
f.importPath.value = importPath;
|
||||
f.submit();
|
||||
}
|
||||
|
||||
function importManyElements() {
|
||||
var f = document.forms['importElements'];
|
||||
var importPaths = '';
|
||||
// Get the values of the checkboxes
|
||||
var checkBoxes = cssQuery('#cbElem');
|
||||
for (var i=0; i<checkBoxes.length; i++) {
|
||||
if (checkBoxes[i].checked) {
|
||||
importPaths += checkBoxes[i].value + '|';
|
||||
}
|
||||
}
|
||||
if (! importPaths) alert(no_elem_selected);
|
||||
else {
|
||||
f.importPath.value = importPaths;
|
||||
f.submit();
|
||||
}
|
||||
}
|
||||
|
||||
-->
|
||||
</script>
|
||||
<tal:comment replace="nothing">Form for importing several meetings at once.</tal:comment>
|
||||
<form name="importElements"
|
||||
tal:attributes="action python: appFolder.absolute_url()+'/skyn/do'" method="post">
|
||||
<input type="hidden" name="action" value="ImportObjects"/>
|
||||
<input type="hidden" name="type_name" tal:attributes="value contentType"/>
|
||||
<input type="hidden" name="importPath" value=""/>
|
||||
</form>
|
||||
|
||||
<h1 tal:content="python: tool.translate('import_title')"></h1><br/>
|
||||
<table cellpadding="0" cellspacing="0" class="vertical listing" width="100%">
|
||||
<tr>
|
||||
<th tal:repeat="columnHeader python: importElems[0]">
|
||||
<img tal:condition="python: repeat['columnHeader'].number() == 1"
|
||||
tal:attributes="src string:$portal_url/skyn/eye.png;
|
||||
title python: tool.translate('import_show_hide')"
|
||||
style="cursor:pointer" onClick="javascript:toggleViewableElements()" align="left" />
|
||||
<span tal:replace="columnHeader"/>
|
||||
</th>
|
||||
<th tal:content="python: tool.translate('ref_actions')"></th>
|
||||
<th width="20px"><img
|
||||
tal:attributes="src string: $portal_url/skyn/select_elems.png;
|
||||
title python: tool.translate('select_delesect')"
|
||||
onClick="javascript:toggleCheckboxes()" style="cursor:pointer"/>
|
||||
</tr>
|
||||
<tal:row repeat="row python: importElems[1]">
|
||||
<tr tal:define="alreadyImported python: tool.isAlreadyImported(contentType, row[0]);
|
||||
global allAreImported python: allAreImported and alreadyImported"
|
||||
tal:attributes="id python:test(alreadyImported, 'importedElem', 'notImportedElem');
|
||||
style python:test(alreadyImported, 'display:none', 'display:table-row')">
|
||||
<td tal:repeat="elem python: row[1:]" tal:content="elem">
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" tal:condition="not: alreadyImported"
|
||||
tal:attributes="onClick python: 'javascript:importSingleElement(\'%s\')' % row[0];
|
||||
value python: tool.translate('query_import')"/>
|
||||
<span tal:condition="alreadyImported" tal:replace="python: tool.translate('import_already')"/>
|
||||
</td>
|
||||
<td align="center"><input type="checkbox" checked="checked" class="noborder" id="cbElem"
|
||||
tal:attributes="value python: row[0]" tal:condition="not: alreadyImported"/></td>
|
||||
</tr>
|
||||
</tal:row>
|
||||
<tr tal:condition="python: not importElems[1] or allAreImported"><td colspan="15" tal:content="python: tool.translate('query_no_result')"></td></tr>
|
||||
</table>
|
||||
<tal:comment replace="nothing">Button for importing several elements at once.</tal:comment>
|
||||
<p align="right"><br/>
|
||||
<input type="button" onClick="javascript:importManyElements()"
|
||||
tal:condition="python: importElems[1] and not allAreImported"
|
||||
tal:attributes="value python:tool.translate('import_many')"/>
|
||||
</p>
|
||||
</metal:fill>
|
||||
</body>
|
||||
</html>
|
|
@ -104,7 +104,7 @@
|
|||
|
||||
<div metal:define-macro="showActionField">
|
||||
<form name="executeAppyAction" action="skyn/do" method="POST">
|
||||
<input type="hidden" name="actionType" value="appyAction"/>
|
||||
<input type="hidden" name="action" value="ExecuteAppyAction"/>
|
||||
<input type="hidden" name="objectUid" tal:attributes="value contextObj/UID"/>
|
||||
<input type="hidden" name="fieldName" tal:attributes="value field/getName"/>
|
||||
<input type="submit" name="do" tal:attributes="value label"/>
|
||||
|
@ -300,21 +300,25 @@
|
|||
</dl>
|
||||
</span>
|
||||
|
||||
<div metal:define-macro="showPagePrologue">
|
||||
<tal:comment replace="nothing">Global Javascript functions, used in edit and
|
||||
consult views, are defined gere.</tal:comment>
|
||||
<div metal:define-macro="pagePrologue">
|
||||
<tal:comment replace="nothing">Global elements used in every page.</tal:comment>
|
||||
|
||||
<tal:comment replace="nothing">Javascript messages</tal:comment>
|
||||
<script language="javascript" tal:content="tool/getJavascriptMessages"></script>
|
||||
|
||||
<tal:comment replace="nothing">"Static" javascripts</tal:comment>
|
||||
<script language="javascript">
|
||||
<!--
|
||||
// This function turns a checkbox into a radio button... sort of
|
||||
// Function used by checkbox widgets for having radio-button-like behaviour
|
||||
function toggleCheckbox(visibleCheckbox, hiddenBoolean) {
|
||||
vis = document.getElementById(visibleCheckbox);
|
||||
hidden = document.getElementById(hiddenBoolean);
|
||||
if (vis.checked) hidden.value = 'True';
|
||||
else hidden.value = 'False';
|
||||
}
|
||||
// Returns an array of selected options in a select widget
|
||||
// Functions used for master/slave relationships between widgets
|
||||
function getMasterValue(widget) {
|
||||
// Returns an array of selected options in a select widget
|
||||
res = new Array();
|
||||
if (widget.type == 'checkbox') {
|
||||
var mv = widget.checked + '';
|
||||
|
@ -328,9 +332,9 @@
|
|||
}
|
||||
return res;
|
||||
}
|
||||
function updateSlaves(masterValues, appyTypeId) {
|
||||
// Given the value(s) selected in a master field, this function updates the
|
||||
// state of all corresponding slaves.
|
||||
function updateSlaves(masterValues, appyTypeId) {
|
||||
var slaves = cssQuery('div.slave_' + appyTypeId);
|
||||
for (var i=0; i< slaves.length; i++){
|
||||
slaves[i].style.display = "none";
|
||||
|
@ -342,14 +346,26 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
// Triggers a workflow transition
|
||||
// Function used for triggering a workflow transition
|
||||
function triggerTransition(transitionId) {
|
||||
var theForm = document.getElementById('triggerTransitionForm');
|
||||
theForm.workflow_action.value = transitionId;
|
||||
theForm.submit();
|
||||
}
|
||||
-->
|
||||
function onDeleteObject(objectUid) {
|
||||
if (confirm(delete_confirm)) {
|
||||
f = document.getElementById('deleteForm');
|
||||
f.objectUid.value = objectUid;
|
||||
f.submit();
|
||||
}
|
||||
}
|
||||
-->
|
||||
</script>
|
||||
<tal:comment replace="nothing">Global form for deleting an object</tal:comment>
|
||||
<form id="deleteForm" method="post" action="skyn/do">
|
||||
<input type="hidden" name="action" value="Delete"/>
|
||||
<input type="hidden" name="objectUid"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div metal:define-macro="showPageHeader"
|
||||
|
@ -681,10 +697,11 @@
|
|||
<img src="edit.gif" title="Edit" i18n:domain="plone" i18n:attributes="title" />
|
||||
</a></td>
|
||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||
<td class="noPadding"><a tal:attributes="href python: obj.absolute_url() + '/delete_confirmation'"
|
||||
tal:condition="python: member.has_permission('Delete objects', obj)">
|
||||
<img src="delete_icon.gif" title="Delete" i18n:domain="plone" i18n:attributes="title" />
|
||||
</a></td>
|
||||
<td class="noPadding">
|
||||
<img tal:condition="python: member.has_permission('Delete objects', obj)"
|
||||
src="delete_icon.gif" title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
||||
tal:attributes="onClick python:'javascript:onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
|
@ -740,7 +757,7 @@
|
|||
tal:condition="transitions">
|
||||
<form id="triggerTransitionForm" method="post"
|
||||
tal:attributes="action python: contextObj.absolute_url() + '/skyn/do'">
|
||||
<input type="hidden" name="actionType" value="triggerTransition"/>
|
||||
<input type="hidden" name="action" value="TriggerTransition"/>
|
||||
<input type="hidden" name="workflow_action"/>
|
||||
<table>
|
||||
<tr>
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
<tal:comment replace="nothing">This page presents results of queries</tal:comment>
|
||||
<body>
|
||||
<div metal:fill-slot="top_slot">
|
||||
|
||||
<tal:comment replace="nothing">Disable standard Plone green tabs</tal:comment>
|
||||
<div metal:fill-slot="top_slot">
|
||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
||||
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<tal:comment replace="nothing">We suppose we are in the app folder here.</tal:comment>
|
||||
<div metal:fill-slot="main"
|
||||
<tal:comment replace="nothing">We suppose we are in the app folder here.</tal:comment>
|
||||
<div metal:fill-slot="main"
|
||||
tal:define="appFolder context/getParentNode;
|
||||
appName appFolder/id;
|
||||
tool python: portal.get('portal_%s' % appName.lower());
|
||||
|
@ -20,6 +22,7 @@
|
|||
rootClassesQuery python:','.join(rootClasses);
|
||||
mainTabSelected python: queryName.find(',') != -1">
|
||||
|
||||
<div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
|
||||
<span tal:condition="python: queryName and (queryName != 'none')">
|
||||
<span tal:define="queryResult python: tool.executeQuery(queryName, int(flavourNumber));
|
||||
batch queryResult">
|
||||
|
@ -36,16 +39,25 @@
|
|||
</li>
|
||||
<tal:comment replace="nothing">One tab for each root content type</tal:comment>
|
||||
<tal:tab repeat="rootContentType rootTypes">
|
||||
<li tal:define="selected python:queryName == rootContentType"
|
||||
<li tal:define="selected python:queryName == rootContentType;
|
||||
addPermission python: '%s: Add %s' % (appName, rootContentType);
|
||||
userMayAdd python: member.has_permission(addPermission, appFolder);
|
||||
createMeans python: tool.getCreateMeans(rootContentType)"
|
||||
tal:attributes="class python:test(selected, 'selected', 'plain')">
|
||||
<a tal:content="python: tool.translate(rootContentType)"
|
||||
tal:attributes="href python: '%s/skyn/query?query=%s&flavourNumber=%s' % (appFolder.absolute_url(), rootContentType, flavourNumber)"/>
|
||||
<tal:comment replace="nothing">Create a new object from a web form</tal:comment>
|
||||
<img style="cursor:pointer" class="appyPlusImg"
|
||||
tal:define="addPermission python: '%s: Add %s' % (appName, rootContentType)"
|
||||
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/do?action=create&type_name=%s\'' % (appFolder.absolute_url(), rootContentType);
|
||||
tal:condition="python: ('form' in createMeans) and userMayAdd"
|
||||
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/do?action=Create&type_name=%s\'' % (appFolder.absolute_url(), rootContentType);
|
||||
src string: $portal_url/skyn/plus.png;
|
||||
title python: tool.translate('query_create')"
|
||||
tal:condition="python: member.has_permission(addPermission, appFolder)"/>
|
||||
title python: tool.translate('query_create')"/>
|
||||
<tal:comment replace="nothing">Create (a) new object(s) by importing data</tal:comment>
|
||||
<img style="cursor:pointer" class="appyPlusImg"
|
||||
tal:condition="python: ('import' in createMeans) and userMayAdd"
|
||||
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/import?type_name=%s\'' % (appFolder.absolute_url(), rootContentType);
|
||||
src string: $portal_url/skyn/import.png;
|
||||
title python: tool.translate('query_import')"/>
|
||||
</li>
|
||||
</tal:tab>
|
||||
</ul>
|
||||
|
@ -59,6 +71,6 @@
|
|||
tal:content="python: tool.translate('query_no_result')">No result.</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -26,13 +26,13 @@
|
|||
<img src="delete_icon.gif" title="label_remove" i18n:domain="plone" i18n:attributes="title" />
|
||||
</a></td>
|
||||
<tal:comment replace="nothing">Arrows for moving objects up or down</tal:comment>
|
||||
<td class="noPadding" tal:condition="python: len(objs)>1">
|
||||
<td class="noPadding" tal:condition="python: len(objs)>1">
|
||||
<form tal:condition="python: member.has_permission('Modify portal content', obj)"
|
||||
tal:attributes="action python: contextObj.absolute_url() + '/skyn/do'"
|
||||
tal:define="objectIndex python:contextObj.getAppyRefIndex(field.getName(), obj)">
|
||||
<input type="hidden" name="actionType" value="changeRefOrder"/>
|
||||
<input type="hidden" name="action" value="ChangeRefOrder"/>
|
||||
<input type="hidden" name="fieldName" tal:attributes="value field/getName"/>
|
||||
<input type="hidden" name="objectUid" tal:attributes="value obj/UID"/>
|
||||
<input type="hidden" name="refObjectUid" tal:attributes="value obj/UID"/>
|
||||
<tal:comment replace="nothing">Arrow up</tal:comment>
|
||||
<span tal:condition="python: objectIndex > 0">
|
||||
<input type="image" name="moveUp" class="imageInput"
|
||||
|
@ -58,7 +58,7 @@
|
|||
<img style="cursor:pointer" tal:condition="showPlusIcon"
|
||||
tal:attributes="src string:$portal_url/skyn/plus.png;
|
||||
title python: tool.translate('add_ref');
|
||||
onClick python: 'href: window.location=\'%s/skyn/do?action=create&initiator=%s&field=%s&type_name=%s\'' % (folder.absolute_url(), contextObj.UID(), field.getName(), linkedPortalType)"/>
|
||||
onClick python: 'href: window.location=\'%s/skyn/do?action=Create&initiator=%s&field=%s&type_name=%s\'' % (folder.absolute_url(), contextObj.UID(), field.getName(), linkedPortalType)"/>
|
||||
</metal:plusIcon>
|
||||
|
||||
<div metal:define-macro="showReference"
|
||||
|
|
BIN
gen/plone25/skin/select_elems.png
Normal file
BIN
gen/plone25/skin/select_elems.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 235 B |
|
@ -27,7 +27,7 @@
|
|||
phase request/phase|phaseInfo/name;
|
||||
pageName python: contextObj.getAppyPage(isEdit, phaseInfo);
|
||||
showWorkflow python: flavour.getAttr('showWorkflowFor' + contextObj.meta_type)">
|
||||
<div metal:use-macro="here/skyn/macros/macros/showPagePrologue"/>
|
||||
<div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
|
||||
<div metal:use-macro="here/skyn/macros/macros/showPageHeader"/>
|
||||
<div metal:use-macro="here/skyn/macros/macros/listFields" />
|
||||
<div metal:use-macro="here/skyn/macros/macros/showPageFooter"/>
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
float:right;
|
||||
}
|
||||
|
||||
#importedElem {
|
||||
color: grey;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.appyTabs {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
@ -126,10 +131,13 @@ dl.expandedInlineCollapsible dt.collapsibleHeader, dl.expandedBlockCollapsible d
|
|||
|
||||
/* Minor layout changes in fieldsets and tables */
|
||||
fieldset {
|
||||
margin: 0em 0em;
|
||||
line-height: 1.0em;
|
||||
margin: 0 0 0 0;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.fieldset {
|
||||
line-height: 1em;
|
||||
}
|
||||
/* Group fieldsets */
|
||||
.appyGroup {
|
||||
border-width: 2px;
|
||||
|
@ -192,6 +200,9 @@ fieldset {
|
|||
line-height: 1.0em;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin: 0 0.2em 0.2em 0;
|
||||
}
|
||||
/* Portlet elements */
|
||||
.portletAppyItem {
|
||||
margin: 0;
|
||||
|
|
10
gen/po.py
10
gen/po.py
|
@ -49,8 +49,14 @@ class PoMessage:
|
|||
REF_MOVE_DOWN = 'Move down'
|
||||
REF_INVALID_INDEX = 'No move occurred: please specify a valid number.'
|
||||
QUERY_CREATE = 'create'
|
||||
QUERY_IMPORT = 'import'
|
||||
QUERY_CONSULT_ALL = 'consult all'
|
||||
QUERY_NO_RESULT = 'Nothing to see for the moment.'
|
||||
IMPORT_TITLE = 'Importing data into your application'
|
||||
IMPORT_SHOW_HIDE = 'Show / hide alreadly imported elements.'
|
||||
IMPORT_ALREADY = 'Already imported.'
|
||||
IMPORT_MANY = 'Import selected elements'
|
||||
IMPORT_DONE = 'Import terminated successfully.'
|
||||
WORKFLOW_COMMENT = 'Optional comment'
|
||||
WORKFLOW_STATE = 'state'
|
||||
PHASE = 'phase'
|
||||
|
@ -71,6 +77,10 @@ class PoMessage:
|
|||
EMAIL_SUBJECT = '${siteTitle} - Action \\"${transitionName}\\" has been ' \
|
||||
'performed on element entitled \\"${objectTitle}\\".'
|
||||
EMAIL_BODY = 'You can consult this element at ${objectUrl}.'
|
||||
SELECT_DESELECT = '(Un)select all'
|
||||
NO_SELECTION = 'You must select at least one element.'
|
||||
DELETE_CONFIRM = 'Are you sure you want to delete this element?'
|
||||
DELETE_DONE = 'The element has been deleted.'
|
||||
|
||||
def __init__(self, id, msg, default, fuzzy=False, comments=[]):
|
||||
self.id = id
|
||||
|
|
Loading…
Reference in a new issue