[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:
Gaetan Delannay 2012-07-10 14:21:08 +02:00
parent ad14c1258c
commit a7bcd1a098
5 changed files with 676 additions and 619 deletions

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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: