Skip to content

Commit

Permalink
refact versioning (#329)
Browse files Browse the repository at this point in the history
* fix import of workflow

* versioning markdown

* more doc

* more doc

* editing

* get_tags/get_tag

* more doc

* more editing

* preview tags

* diff

* diff pretty json

* diff pretty json and preview

* fixed tests

* revised wf zmi

* wf-zmi: disable htmx temporarily

* fix form name

* fixed form names after refact

* gui spacing, version pattern

* manage_UndoVersionForm: spacing

* added more doc

* added more doc

* added more doc

* ensure wf-tab context after save

* added preview toggle to history selector

* added toggle html vs. json

* added btn-func toggle_view for switching json/preview

* typo

* added notebook for difflib

* eval htmldiff2

* added rest-api get_htmldiff

* added POST method to rest-api

* cleaning

* prepared for genshi processing

* cleaning, show primarily diff view

* doc: version vector

* added draft for implementation

* diff json based on change_dt

* fixed view toggeling

* added htmldiff2  to requirements-full

* get_body_content_diff

* refactoring htmldiff

* fix body_content_diff

* diff version_container

* rehderShort atoms

* version-items if not version-container

* added lang to ajax-request. styled zmi block links

* diff: added JS-function show_old_src_images()

* added style to tagdiff_replaced img

* added readme files to docker examples

* squash item-versions to container

* fixed version over-filtering

* workaround the race condition on versions loading

* diff when loaded

* added  change*-attribute fallback

* added lang-request param to get_tags: @todo implement correct version-tags retrieval

* version-tags retrieval

* added lang for tags

---------

Co-authored-by: drfho <[email protected]>
  • Loading branch information
zmsdev and drfho authored Dec 11, 2024
1 parent 6a041ba commit dd1383d
Show file tree
Hide file tree
Showing 16 changed files with 1,492 additions and 588 deletions.
22 changes: 15 additions & 7 deletions Products/zms/ZMSWorkflowProvider.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,13 +316,13 @@ def doAutocommit(self, lang, REQUEST):
Change workflow.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
def manage_changeWorkflow(self, lang, btn='', key='properties', REQUEST=None, RESPONSE=None):
def manage_changeWorkflow(self, lang, btn='', key='workflow_properties', REQUEST=None, RESPONSE=None):
""" ZMSWorkflowProvider.manage_changeWorkflow """
message = ''

# Version Control.
# -----------
if key == 'history':
if key == 'workflow_versioning':
old_active = self.getConfProperty('ZMS.Version.active',0)
new_active = REQUEST.get('active',0)
old_nodes = self.getConfProperty('ZMS.Version.nodes',['{$}'])
Expand All @@ -342,10 +342,19 @@ def manage_changeWorkflow(self, lang, btn='', key='properties', REQUEST=None, RE
except:
message += '[%s: %s]'%(node,'No history to pack')
message = self.getZMILangStr('MSG_CHANGED')+message


# Content Assignment.
# -----------
elif key == 'workflow_assignment':
# Save.
# ------
if btn == 'BTN_SAVE':
self.nodes = standard.string_list(REQUEST.get('nodes', ''))
message = self.getZMILangStr('MSG_CHANGED')

# Properties.
# -----------
elif key == 'properties':
elif key == 'workflow_properties':
# Save.
# ------
if btn == 'BTN_SAVE':
Expand All @@ -354,7 +363,6 @@ def manage_changeWorkflow(self, lang, btn='', key='properties', REQUEST=None, RE
new_autocommit = REQUEST.get('workflow', 0) == 0
self.revision = REQUEST.get('revision', '0.0.0')
self.autocommit = new_autocommit
self.nodes = standard.string_list(REQUEST.get('nodes', ''))
if old_autocommit == 0 and new_autocommit == 1:
self.doAutocommit(lang, REQUEST)
message = self.getZMILangStr('MSG_CHANGED')
Expand Down Expand Up @@ -383,10 +391,10 @@ def manage_changeWorkflow(self, lang, btn='', key='properties', REQUEST=None, RE
else:
filename = REQUEST['init']
self.importConf(filename)
message = self.getZMILangStr('MSG_IMPORTED')%('<i>%s</i>'%f.filename)
message = self.getZMILangStr('MSG_IMPORTED')%('<i>%s</i>'%filename)

# Return with message.
message = standard.url_quote(message)
return RESPONSE.redirect('manage_main?lang=%s&key=%s&manage_tabs_message=%s#_properties'%(lang, key, message))
return RESPONSE.redirect('manage_main?lang=%s&key=%s&manage_tabs_message=%s#%s'%(lang, key, message, key))

################################################################################
28 changes: 14 additions & 14 deletions Products/zms/import/_language.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6406,23 +6406,23 @@
</Row>
<Row>
<Cell><Data ss:Type="String">TAB_WORKFLOW_ASSIGNMENT</Data></Cell>
<Cell><Data ss:Type="String">Workflow-Aktivierung</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Workflow Assignment</Data></Cell>
<Cell><Data ss:Type="String">Zuweisung</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
<Cell><Data ss:Type="String">Assignment</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">TAB_WORKFLOW_MODEL</Data></Cell>
<Cell><Data ss:Type="String">Ablauf-Modell</Data></Cell>
<Cell><Data ss:Type="String">Workflow-Modell</Data></Cell>
<Cell><Data ss:Type="String">Workflow Model</Data></Cell>
<Cell><Data ss:Type="String">Workflow Model</Data></Cell>
<Cell><Data ss:Type="String">Workflow Model</Data></Cell>
Expand Down
2 changes: 1 addition & 1 deletion Products/zms/import/example1.workflow.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<item type="dictionary">
<dictionary>
<item key="icon"></item>
<item key="icon_clazz"><![CDATA[icon-reply text-danger wf-icon]]></item>
<item key="icon_clazz"><![CDATA[fas fa-reply text-danger wf-icon]]></item>
<item key="id"><![CDATA[AC_ROLLEDBACK]]></item>
<item key="name"><![CDATA[Rolled back]]></item>
</dictionary>
Expand Down
123 changes: 121 additions & 2 deletions Products/zms/rest_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ def __bobo_traverse__(self, TraversalRequest, name):
def __call__(self, REQUEST=None, **kw):
""""""
standard.writeBlock(self.context,'__call__: %s'%str(self.ids))
if self.method == 'POST':
if self.ids == ['get_htmldiff']:
decoration, data = self.get_htmldiff(self.context, content_type=True)
return data
else:
return None
if self.method == 'GET':
decoration, data = {'content_type':'text/plain'}, {}
if self.ids == [] and self.context.meta_type == 'ZMSIndex':
Expand All @@ -190,12 +196,22 @@ def __call__(self, REQUEST=None, **kw):
decoration, data = self.get_child_nodes(self.context, content_type=True)
elif self.ids == ['get_tree_nodes']:
decoration, data = self.get_tree_nodes(self.context, content_type=True)
elif self.ids == ['get_tags']:
decoration, data = self.get_tags(self.context, content_type=True)
elif self.ids == ['get_tag']:
decoration, data = self.get_tag(self.context, content_type=True)
elif self.ids == ['body_content']:
decoration, data = self.body_content(self.context, content_type=True)
elif self.ids == [] or self.ids == ['get']:
decoration, data = self.get(self.context, content_type=True)
else:
data = {'ERROR':'Not Found','context':str(self.context),'path_to_handle':self.path_to_handle,'ids':self.ids}
REQUEST.RESPONSE.setHeader('Content-Type',decoration['content_type'])
return json.dumps(data)
ct = decoration['content_type']
REQUEST.RESPONSE.setHeader('Content-Type',ct)
REQUEST.RESPONSE.setHeader('Content-Disposition', 'inline;filename="%s.%s"'%((self.ids+['get'])[-1],ct.split('/')[-1]))
if ct == 'application/json':
return json.dumps(data)
return data
return None

@api(tag="zmsindex", pattern="/zmsindex", content_type="application/json")
Expand Down Expand Up @@ -265,3 +281,106 @@ def get_tree_nodes(self, context):
request = _get_request(context)
nodes = context.getTreeNodes(request)
return [get_attrs(x) for x in nodes]

@api(tag="version", pattern="/{path}/get_tags", method="GET", content_type="application/json")
def get_tags(self, context):
request = _get_request(context)
lang = request.get('lang')
tags = []
version_container = context.getVersionContainer()
version_items = ([version_container] + version_container.getVersionItems(request)) if context.isVersionContainer() else [context]
for version_item in version_items:
for obj_version in version_item.getObjVersions():
request.set('ZMS_VERSION_%s'%version_item.id,obj_version.id)
change_dt = obj_version.attr('change_dt')
change_uid = obj_version.attr('change_uid')
if change_dt and change_uid:
dt = standard.getLangFmtDate(version_item,change_dt,'eng','DATETIME_FMT')
if not [1 for tag in tags if tag[0] == dt]:
tags.append(
(dt
,'r%i.%i.%i'%(obj_version.attr('master_version'), obj_version.attr('major_version'), obj_version.attr('minor_version'))
,'/'.join(version_item.getPhysicalPath())
))
tags = sorted(list(set(tags)),key=lambda x:x[0])
tags.reverse()
physical_path = '/'.join(context.getPhysicalPath())
if context.isVersionContainer():
physical_path = '/'.join(version_container.getPhysicalPath())
rtn = []
for i in range(len(tags)):
tag = list(tags[i])
if i == 0 and not tag[2] == physical_path:
dt = tag[0]
rtn.append(
[dt
,'r*.*.*'
, physical_path
])
if tag[2] == physical_path:
rtn.append(tag)
return [(x[0],x[1]) for x in rtn]

@api(tag="version", pattern="/{path}/get_tag", method="GET", content_type="application/json")
def get_tag(self, context):
request = _get_request(context)
lang = request.get('lang')
tag = request.get('tag').split(",")
dt = tag[0]
data = []
version_container = context.getVersionContainer()
version_items = ([version_container] + version_container.getVersionItems(request)) if context.isVersionContainer() else [context]
for version_item in version_items:
d = {}
for obj_version in version_item.getObjVersions():
request.set('ZMS_VERSION_%s'%version_item.id,obj_version.id)
change_dt = obj_version.attr('change_dt') or obj_version.attr('created_dt')
change_uid = obj_version.attr('change_uid') or obj_version.attr('created_uid')
if change_dt and change_uid:
d[standard.getLangFmtDate(version_item,change_dt,'eng','DATETIME_FMT')] = obj_version.id
tags = list(reversed(sorted(list(d.keys()))))
tags = [x for x in tags if x <= dt]
if tags:
request.set('ZMS_VERSION_%s'%version_item.id,d[tags[0]])
attrs = get_attrs(version_item)
data.append(attrs)
return data

@api(tag="version", pattern="/{path}/body_content", method="GET", content_type="text/html")
def body_content(self, context):
request = _get_request(context)
lang = request.get('lang')
tag = request.get('tag').split(",")
dt = tag[0]
html = []
version_container = context.getVersionContainer()
version_items = ([version_container] + version_container.getVersionItems(request)) if context.isVersionContainer() else [context]
for version_item in version_items:
d = {}
for obj_version in version_item.getObjVersions():
request.set('ZMS_VERSION_%s'%version_item.id,obj_version.id)
change_dt = obj_version.attr('change_dt') or obj_version.attr('created_dt')
change_uid = obj_version.attr('change_uid') or obj_version.attr('created_uid')
if change_dt and change_uid:
d[standard.getLangFmtDate(version_item,change_dt,'eng','DATETIME_FMT')] = obj_version.id
tags = list(reversed(sorted(list(d.keys()))))
tags = [x for x in tags if x <= dt]
if tags:
request.set('ZMS_VERSION_%s'%version_item.id,d[tags[0]])
html.append('<div><a href="%s/manage_main"><small>%s</div></small></a></div>'%(version_item.absolute_url(),'/'.join(version_item.getPhysicalPath())))
if version_item == version_container:
html.append('<div class="%s"><h1>%s<small>%s</small></h1></div>'%(version_item.meta_id,version_item.getTitle(request),version_item.getDCDescription(request)))
else:
html.append(version_item.renderShort(request))
return '\n'.join(html)

@api(tag="standard", pattern="/{path}/get_htmldiff", method="POST", content_type="text/html")
def get_htmldiff(self, context):
decoration, data = {'content_type':'text/html'}, {}
request = _get_request(context)
original = request.get('original','<pre>original</pre>')
changed = request.get('changed','<pre>changed</pre>')
data = standard.htmldiff(original, changed)
ct = decoration['content_type']
request.RESPONSE.setHeader('Content-Type',ct)
return data
24 changes: 24 additions & 0 deletions Products/zms/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -2241,6 +2241,30 @@ def processData(context, processId, data, trans=None):
return _filtermanager.processData(context, processId, data, trans)


security.declarePublic('htmldiff')
def htmldiff(original, changed):
"""
Wrapper for htmldiff2.render_html_diff.
@param original: html-file-0
@type context: C{str}
@param changed: html-file-1
@type changed: C{str}
"""
try:
from htmldiff2 import render_html_diff
def remove_curly_braces(s):
return re.sub(r'/[\{\}]', '', s, flags=re.IGNORECASE)
def remove_html_comments(s):
return re.sub(r'<!--.*?-->', '', s, flags=re.DOTALL)
# Remove html comments for processing with htmldiff2/genshi.
original = remove_html_comments(remove_curly_braces(original))
changed = remove_html_comments(remove_curly_braces(changed))
diff = render_html_diff(original,changed)
except:
diff = '<pre>ERROR: Cannot load or work with htmldiff2</pre>'
return diff


############################################################################
#
#{ Executable
Expand Down
2 changes: 1 addition & 1 deletion Products/zms/zpt/ZMSMetacmdProvider/manage_main.zpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<label for="el_id" class="col-sm-3 control-label mandatory"><span tal:content="python:here.getZMILangStr('ATTR_ID')">Id</span></label>
<div class="col-sm-6"><input type="text" name="el_id" class="form-control form-control-sm" tal:attributes="value python:metaCmd['id']"/></div>
<label for="el_revision" class="col-sm-1 control-label mandatory"><span tal:content="python:here.getZMILangStr('ATTR_VERSION')">Version</span></label>
<div class="col-sm-2"><input type="text" name="el_revision" class="form-control form-control-sm" tal:attributes="value python:metaCmd.get('revision','0.0.0')"/></div>
<div class="col-sm-2"><input type="text" name="el_revision" placeholder="0.0.0" pattern="^\d+\.\d+\.\d+$" class="form-control form-control-sm" tal:attributes="value python:metaCmd.get('revision','0.0.0')"/></div>
</div>

<div class="form-group row">
Expand Down
Loading

0 comments on commit dd1383d

Please sign in to comment.