[gen] allow Manager to change the login of a user; bugfix in dumping XHTML tables whose column sizes are automatically computed; bugfix: apply styles mapping to 'p' tags that are added automatically by xhtml2odt (was only implemented for p tags inside li tags).
This commit is contained in:
parent
ad14c1258c
commit
a7bcd1a098
|
@ -451,7 +451,7 @@ function doConfirm() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrongTextInput = '#ff934a none';
|
var wrongTextInput = '#F0C36D none';
|
||||||
// Function triggered when the user ask password reinitialisation
|
// Function triggered when the user ask password reinitialisation
|
||||||
function doAskPasswordReinit() {
|
function doAskPasswordReinit() {
|
||||||
// Check that the user has typed a login
|
// Check that the user has typed a login
|
||||||
|
|
|
@ -9,6 +9,8 @@ class UserWrapper(AbstractWrapper):
|
||||||
def showLogin(self):
|
def showLogin(self):
|
||||||
'''When must we show the login field?'''
|
'''When must we show the login field?'''
|
||||||
if self.o.isTemporary(): return 'edit'
|
if self.o.isTemporary(): return 'edit'
|
||||||
|
# The manager has the possibility to change the login itself.
|
||||||
|
if self.user.has_role('Manager'): return True
|
||||||
return ('view', 'result')
|
return ('view', 'result')
|
||||||
|
|
||||||
def showName(self):
|
def showName(self):
|
||||||
|
@ -27,12 +29,17 @@ class UserWrapper(AbstractWrapper):
|
||||||
|
|
||||||
def validateLogin(self, login):
|
def validateLogin(self, login):
|
||||||
'''Is this p_login valid?'''
|
'''Is this p_login valid?'''
|
||||||
# The login can't be the id of the whole site or "admin"
|
# 2 cases: (1) The user is being created and has no login yet, or
|
||||||
if login == 'admin': return self.translate('login_reserved')
|
# (2) The user is being edited and has already a login, that
|
||||||
# Check that no user or group already uses this login.
|
# can potentially be changed.
|
||||||
if self.count('User', noSecurity=True, login=login) or \
|
if not self.login or (login != self.login):
|
||||||
self.count('Group', noSecurity=True, login=login):
|
# A new p_login is requested. Check if it is valid and free.
|
||||||
self.translate('login_in_use')
|
# Firstly, the login can't be the id of the whole site or "admin".
|
||||||
|
if login == 'admin': return self.translate('login_reserved')
|
||||||
|
# Check that no user or group already uses this login.
|
||||||
|
if self.count('User', noSecurity=True, login=login) or \
|
||||||
|
self.count('Group', noSecurity=True, login=login):
|
||||||
|
return self.translate('login_in_use')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validatePassword(self, password):
|
def validatePassword(self, password):
|
||||||
|
@ -46,9 +53,9 @@ class UserWrapper(AbstractWrapper):
|
||||||
'''When must we show the 2 fields for entering a password ?'''
|
'''When must we show the 2 fields for entering a password ?'''
|
||||||
# When someone creates the user
|
# When someone creates the user
|
||||||
if self.o.isTemporary(): return 'edit'
|
if self.o.isTemporary(): return 'edit'
|
||||||
# When the user itself (which is Owner of the object representing him)
|
# When the user itself (we don't check role Owner because a Manager can
|
||||||
# wants to edit information about himself.
|
# also own a User instance) wants to edit information about himself.
|
||||||
if self.user.has_role('Owner', self): return 'edit'
|
if self.user.getId() == self.login: return 'edit'
|
||||||
|
|
||||||
def setPassword(self, newPassword=None):
|
def setPassword(self, newPassword=None):
|
||||||
'''Sets a p_newPassword for self. If p_newPassword is not given, we
|
'''Sets a p_newPassword for self. If p_newPassword is not given, we
|
||||||
|
@ -81,11 +88,14 @@ class UserWrapper(AbstractWrapper):
|
||||||
def validate(self, new, errors):
|
def validate(self, new, errors):
|
||||||
'''Inter-field validation.'''
|
'''Inter-field validation.'''
|
||||||
page = self.request.get('page', 'main')
|
page = self.request.get('page', 'main')
|
||||||
|
self.o._oldLogin = None
|
||||||
if page == 'main':
|
if page == 'main':
|
||||||
if hasattr(new, 'password1') and (new.password1 != new.password2):
|
if hasattr(new, 'password1') and (new.password1 != new.password2):
|
||||||
msg = self.translate('passwords_mismatch')
|
msg = self.translate('passwords_mismatch')
|
||||||
errors.password1 = msg
|
errors.password1 = msg
|
||||||
errors.password2 = msg
|
errors.password2 = msg
|
||||||
|
# Remember the previous login
|
||||||
|
if self.login: self.o._oldLogin = self.login
|
||||||
return self._callCustom('validate', new, errors)
|
return self._callCustom('validate', new, errors)
|
||||||
|
|
||||||
def onEdit(self, created):
|
def onEdit(self, created):
|
||||||
|
@ -103,6 +113,18 @@ class UserWrapper(AbstractWrapper):
|
||||||
# granted to it.
|
# granted to it.
|
||||||
zopeUser.groups = PersistentMapping()
|
zopeUser.groups = PersistentMapping()
|
||||||
else:
|
else:
|
||||||
|
# Update the login itself if the user has changed it.
|
||||||
|
oldLogin = self.o._oldLogin
|
||||||
|
if oldLogin and (oldLogin != login):
|
||||||
|
zopeUser = aclUsers.getUserById(oldLogin)
|
||||||
|
zopeUser.name = login
|
||||||
|
del aclUsers.data[oldLogin]
|
||||||
|
aclUsers.data[login] = zopeUser
|
||||||
|
# Update the email if the email corresponds to the login.
|
||||||
|
email = self.email
|
||||||
|
if email == oldLogin:
|
||||||
|
self.email = login
|
||||||
|
del self.o._oldLogin
|
||||||
# Update roles at the Zope level.
|
# Update roles at the Zope level.
|
||||||
zopeUser = aclUsers.getUserById(login)
|
zopeUser = aclUsers.getUserById(login)
|
||||||
zopeUser.roles = self.roles
|
zopeUser.roles = self.roles
|
||||||
|
|
|
@ -177,7 +177,8 @@ class Test(appy.shared.test.Test):
|
||||||
areXml=True, xmlTagsToIgnore=(
|
areXml=True, xmlTagsToIgnore=(
|
||||||
(OdfEnvironment.NS_DC, 'date'),
|
(OdfEnvironment.NS_DC, 'date'),
|
||||||
(OdfEnvironment.NS_STYLE, 'style')),
|
(OdfEnvironment.NS_STYLE, 'style')),
|
||||||
xmlAttrsToIgnore=('draw:name','text:name','text:bullet-char'),
|
xmlAttrsToIgnore=('draw:name','text:name','text:bullet-char',
|
||||||
|
'table:name', 'table:style-name'),
|
||||||
encoding='utf-8')
|
encoding='utf-8')
|
||||||
if diffOccurred:
|
if diffOccurred:
|
||||||
res = True
|
res = True
|
||||||
|
|
1150
pod/test/Tests.rtf
1150
pod/test/Tests.rtf
File diff suppressed because it is too large
Load diff
100
pod/xhtml2odt.py
100
pod/xhtml2odt.py
|
@ -12,6 +12,7 @@
|
||||||
import xml.sax, time, random
|
import xml.sax, time, random
|
||||||
from appy.shared.xml_parser import XmlEnvironment, XmlParser
|
from appy.shared.xml_parser import XmlEnvironment, XmlParser
|
||||||
from appy.pod.odf_parser import OdfEnvironment
|
from appy.pod.odf_parser import OdfEnvironment
|
||||||
|
from appy.pod.styles_manager import Style
|
||||||
from appy.pod import *
|
from appy.pod import *
|
||||||
|
|
||||||
# To which ODT tags do HTML tags correspond ?
|
# To which ODT tags do HTML tags correspond ?
|
||||||
|
@ -156,8 +157,14 @@ class HtmlElement:
|
||||||
else:
|
else:
|
||||||
styleName = env.itemStyles[itemStyle]
|
styleName = env.itemStyles[itemStyle]
|
||||||
env.dumpString(' %s:style-name="%s"' % (env.textNs, styleName))
|
env.dumpString(' %s:style-name="%s"' % (env.textNs, styleName))
|
||||||
|
else:
|
||||||
|
# Check if a style must be applied on 'p' tags
|
||||||
|
odtStyle = env.parser.caller.findStyle('p')
|
||||||
|
if odtStyle:
|
||||||
|
env.dumpString(' %s:style-name="%s"' % (env.textNs,
|
||||||
|
odtStyle.name))
|
||||||
env.dumpString('>')
|
env.dumpString('>')
|
||||||
self.tagsToClose.append(HtmlElement('p',{}))
|
self.tagsToClose.append(HtmlElement('p', {}))
|
||||||
|
|
||||||
def dump(self, start, env):
|
def dump(self, start, env):
|
||||||
'''Dumps the start or end (depending on p_start) tag of this HTML
|
'''Dumps the start or end (depending on p_start) tag of this HTML
|
||||||
|
@ -217,6 +224,14 @@ class HtmlTable:
|
||||||
# into self.res.
|
# into self.res.
|
||||||
self.firstRowParsed = False # Was the first table row completely parsed?
|
self.firstRowParsed = False # Was the first table row completely parsed?
|
||||||
self.nbOfColumns = 0
|
self.nbOfColumns = 0
|
||||||
|
# Are we currently within a table cell? Instead of a boolean, the field
|
||||||
|
# stores an integer. The integer is > 1 if the cell spans more than one
|
||||||
|
# column.
|
||||||
|
self.inCell = 0
|
||||||
|
# The index, within the current row, of the current cell
|
||||||
|
self.cellIndex = -1
|
||||||
|
# The size of the content of the currently parsed table cell
|
||||||
|
self.cellContentSize = 0
|
||||||
# The following list stores, for every column, the size of the biggest
|
# The following list stores, for every column, the size of the biggest
|
||||||
# content of all its cells.
|
# content of all its cells.
|
||||||
self.columnContentSizes = []
|
self.columnContentSizes = []
|
||||||
|
@ -230,7 +245,6 @@ class HtmlTable:
|
||||||
# Ensure first that self.columnContentSizes is correct
|
# Ensure first that self.columnContentSizes is correct
|
||||||
if (len(self.columnContentSizes) != self.nbOfColumns) or \
|
if (len(self.columnContentSizes) != self.nbOfColumns) or \
|
||||||
(None in self.columnContentSizes):
|
(None in self.columnContentSizes):
|
||||||
print 'PROBLEM'
|
|
||||||
# There was a problem while parsing the table. Set every column
|
# There was a problem while parsing the table. Set every column
|
||||||
# with the same width.
|
# with the same width.
|
||||||
widths = [int(total/self.nbOfColumns)] * self.nbOfColumns
|
widths = [int(total/self.nbOfColumns)] * self.nbOfColumns
|
||||||
|
@ -275,14 +289,6 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
# The following attr will be True when parsing parts of the XHTML that
|
# The following attr will be True when parsing parts of the XHTML that
|
||||||
# must be ignored.
|
# must be ignored.
|
||||||
self.ignore = False
|
self.ignore = False
|
||||||
# Are we currently within a table cell? Instead of a boolean, the field
|
|
||||||
# stores an integer. The integer is > 1 if the cell spans more than one
|
|
||||||
# column.
|
|
||||||
self.inCell = 0
|
|
||||||
# The index, within the current row, of the current cell
|
|
||||||
self.cellIndex = -1
|
|
||||||
# The size of the content of the currently parsed table cell
|
|
||||||
self.cellContentSize = 0
|
|
||||||
|
|
||||||
def getCurrentElement(self, isList=False):
|
def getCurrentElement(self, isList=False):
|
||||||
'''Gets the element that is on the top of self.currentElements or
|
'''Gets the element that is on the top of self.currentElements or
|
||||||
|
@ -314,7 +320,7 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
if self.anElementIsMissing(currentElem, None):
|
if self.anElementIsMissing(currentElem, None):
|
||||||
currentElem.addInnerParagraph(self)
|
currentElem.addInnerParagraph(self)
|
||||||
# Dump and reinitialize the current content
|
# Dump and reinitialize the current content
|
||||||
content = self.currentContent.strip('\n')
|
content = self.currentContent.strip('\n\t')
|
||||||
contentSize = len(content)
|
contentSize = len(content)
|
||||||
for c in content:
|
for c in content:
|
||||||
# We remove leading and trailing carriage returns, but not
|
# We remove leading and trailing carriage returns, but not
|
||||||
|
@ -325,7 +331,9 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
self.dumpString(c)
|
self.dumpString(c)
|
||||||
self.currentContent = u''
|
self.currentContent = u''
|
||||||
# If we are within a table cell, update the total size of cell content.
|
# If we are within a table cell, update the total size of cell content.
|
||||||
if self.inCell: self.cellContentSize += contentSize
|
if self.currentTables and self.currentTables[-1].inCell:
|
||||||
|
for table in self.currentTables:
|
||||||
|
table.cellContentSize += contentSize
|
||||||
|
|
||||||
def getOdtAttributes(self, htmlElem, htmlAttrs={}):
|
def getOdtAttributes(self, htmlElem, htmlAttrs={}):
|
||||||
'''Gets the ODT attributes to dump for p_currentElem. p_htmlAttrs are
|
'''Gets the ODT attributes to dump for p_currentElem. p_htmlAttrs are
|
||||||
|
@ -417,17 +425,25 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
self.currentLists.append(currentElem)
|
self.currentLists.append(currentElem)
|
||||||
elif elem == 'table':
|
elif elem == 'table':
|
||||||
# Update stack of current tables
|
# Update stack of current tables
|
||||||
|
if not self.currentTables:
|
||||||
|
# We are within a table. If no local style mapping is defined
|
||||||
|
# for paragraphs, add a specific style mapping for a better
|
||||||
|
# rendering of cells' content.
|
||||||
|
caller = self.parser.caller
|
||||||
|
map = caller.localStylesMapping
|
||||||
|
if 'p' not in map:
|
||||||
|
map['p'] = Style('Appy_Table_Content', 'paragraph')
|
||||||
self.currentTables.append(HtmlTable(self))
|
self.currentTables.append(HtmlTable(self))
|
||||||
elif elem in TABLE_CELL_TAGS:
|
elif elem in TABLE_CELL_TAGS:
|
||||||
# Determine colspan
|
# Determine colspan
|
||||||
colspan = 1
|
colspan = 1
|
||||||
if attrs.has_key('colspan'): colspan = int(attrs['colspan'])
|
if attrs.has_key('colspan'): colspan = int(attrs['colspan'])
|
||||||
self.inCell = colspan
|
table = self.currentTables[-1]
|
||||||
self.cellIndex += colspan
|
table.inCell = colspan
|
||||||
|
table.cellIndex += colspan
|
||||||
# If we are in the first row of a table, update columns count
|
# If we are in the first row of a table, update columns count
|
||||||
currentTable = self.currentTables[-1]
|
if not table.firstRowParsed:
|
||||||
if not currentTable.firstRowParsed:
|
table.nbOfColumns += colspan
|
||||||
currentTable.nbOfColumns += colspan
|
|
||||||
return currentElem
|
return currentElem
|
||||||
|
|
||||||
def onElementEnd(self, elem):
|
def onElementEnd(self, elem):
|
||||||
|
@ -437,36 +453,44 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
if elem in XHTML_LISTS:
|
if elem in XHTML_LISTS:
|
||||||
self.currentLists.pop()
|
self.currentLists.pop()
|
||||||
elif elem == 'table':
|
elif elem == 'table':
|
||||||
lastTable = self.currentTables.pop()
|
table = self.currentTables.pop()
|
||||||
# Computes the column styles required by the table
|
# Computes the column styles required by the table
|
||||||
lastTable.computeColumnStyles(self.parser.caller.renderer)
|
table.computeColumnStyles(self.parser.caller.renderer)
|
||||||
# Dumps the content of the last parsed table into the parent buffer
|
# Dumps the content of the last parsed table into the parent buffer
|
||||||
self.dumpString(lastTable.res)
|
self.dumpString(table.res)
|
||||||
|
# Remove cell-paragraph from local styles mapping if it was added.
|
||||||
|
map = self.parser.caller.localStylesMapping
|
||||||
|
if not self.currentTables and ('p' in map):
|
||||||
|
mapValue = map['p']
|
||||||
|
if isinstance(mapValue, Style) and \
|
||||||
|
(mapValue.name == 'Appy_Table_Content'):
|
||||||
|
del map['p']
|
||||||
elif elem == 'tr':
|
elif elem == 'tr':
|
||||||
self.cellIndex = -1
|
table = self.currentTables[-1]
|
||||||
lastTable = self.currentTables[-1]
|
table.cellIndex = -1
|
||||||
if not lastTable.firstRowParsed:
|
if not table.firstRowParsed:
|
||||||
lastTable.firstRowParsed = True
|
table.firstRowParsed = True
|
||||||
# First row is parsed. I know the number of columns in the
|
# First row is parsed. I know the number of columns in the
|
||||||
# table: I can dump the columns declarations.
|
# table: I can dump the columns declarations.
|
||||||
for i in range(1, lastTable.nbOfColumns + 1):
|
for i in range(1, table.nbOfColumns + 1):
|
||||||
lastTable.res+= '<%s:table-column %s:style-name=' \
|
table.res+= '<%s:table-column %s:style-name="%s.%d"/>' % \
|
||||||
'"%s.%d"/>' % (self.tableNs, self.tableNs,
|
(self.tableNs, self.tableNs, table.name, i)
|
||||||
lastTable.name, i)
|
table.res += table.tempRes
|
||||||
lastTable.res += lastTable.tempRes
|
table.tempRes = u''
|
||||||
lastTable.tempRes = u''
|
|
||||||
elif elem in TABLE_CELL_TAGS:
|
elif elem in TABLE_CELL_TAGS:
|
||||||
# Update attr "columnContentSizes" of the currently parsed table,
|
# Update attr "columnContentSizes" of the currently parsed table,
|
||||||
# excepted if the cell spans several columns.
|
# excepted if the cell spans several columns.
|
||||||
if self.inCell == 1:
|
table = self.currentTables[-1]
|
||||||
lastTable = self.currentTables[-1]
|
if table.inCell == 1:
|
||||||
sizes = lastTable.columnContentSizes
|
sizes = table.columnContentSizes
|
||||||
# Insert None values if the list is too small
|
# Insert None values if the list is too small
|
||||||
while (len(sizes)-1) < self.cellIndex: sizes.append(None)
|
while (len(sizes)-1) < table.cellIndex: sizes.append(None)
|
||||||
sizes[self.cellIndex] = max(sizes[self.cellIndex],
|
highest = max(sizes[table.cellIndex], table.cellContentSize, 5)
|
||||||
self.cellContentSize)
|
# Put a maximum
|
||||||
self.inCell = 0
|
highest = min(highest, 100)
|
||||||
self.cellContentSize = 0
|
sizes[table.cellIndex] = highest
|
||||||
|
table.inCell = 0
|
||||||
|
table.cellContentSize = 0
|
||||||
if currentElem.tagsToClose:
|
if currentElem.tagsToClose:
|
||||||
self.closeConflictualElements(currentElem.tagsToClose)
|
self.closeConflictualElements(currentElem.tagsToClose)
|
||||||
if currentElem.tagsToReopen:
|
if currentElem.tagsToReopen:
|
||||||
|
|
Loading…
Reference in a new issue