468 lines
18 KiB
Python
468 lines
18 KiB
Python
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
# Glasnost
|
|
# By: Odile Bénassy <obenassy@entrouvert.com>
|
|
# Romain Chantereau <rchantereau@entrouvert.com>
|
|
# Nicolas Clapiès <nclapies@easter-eggs.org>
|
|
# Pierre-Antoine Dejace <padejace@entrouvert.be>
|
|
# Thierry Dulieu <tdulieu@easter-eggs.com>
|
|
# Florent Monnier <monnier@codelutin.com>
|
|
# Cédric Musso <cmusso@easter-eggs.org>
|
|
# Frédéric Péters <fpeters@entrouvert.be>
|
|
# Benjamin Poussin <poussin@codelutin.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
# Sébastien Régnier <regnier@codelutin.com>
|
|
# Emmanuel Saracco <esaracco@easter-eggs.com>
|
|
#
|
|
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
|
|
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
|
|
# Emmanuel Saracco & Théridion
|
|
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
|
|
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
|
|
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
|
|
# Régnier, Emmanuel Saracco, Théridion & Vecam
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
__doc__ = """Glasnost Articles Web"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import copy
|
|
import re
|
|
|
|
import glasnost.common.context as context
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.slots as slots
|
|
|
|
from glasnost.proxy.ArticlesProxy import *
|
|
|
|
from ObjectsWeb import register, AdminMixin, ObjectWebMixin, ObjectsWebMixin
|
|
from tools import *
|
|
|
|
|
|
class AdminArticles(AdminMixin, AdminArticles):
|
|
pass
|
|
register(AdminArticles)
|
|
|
|
|
|
class Article(ObjectWebMixin, Article):
|
|
body_kind_widget_fieldLabel = N_('Text')
|
|
body_kind_widget_colSpan = 2
|
|
body_kind_widget_preview = 1
|
|
body_kind_widgetName = 'TextArea'
|
|
|
|
editionTime_kind_stateInEditMode = 'hidden'
|
|
editionTime_kind_widget_fieldLabel = N_('Edition Time')
|
|
editionTime_kind_widgetName = 'InputText'
|
|
|
|
format_kind_defaultValue = 'spip'
|
|
format_kind_widget_fieldLabel = N_('Format')
|
|
format_kind_widget_labels = {
|
|
'docbook': N_('DocBook'),
|
|
'html': N_('HTML'),
|
|
'spip': N_('SPIP'),
|
|
'text': N_('Text'),
|
|
'rst': N_('ReStructured Text'),
|
|
}
|
|
format_kind_widgetName = 'Select'
|
|
|
|
lastEditorId_kind_stateInEditMode = 'read-only'
|
|
lastEditorId_kind_widget_fieldLabel = N_('Last Editor')
|
|
lastEditorId_kind_widgetName = 'SelectId'
|
|
|
|
title_kind_widget_fieldLabel = N_('Title')
|
|
title_kind_widget_size = 40
|
|
title_kind_widgetName = 'InputText'
|
|
|
|
def getEditLayout(self, fields, parentSlot = None):
|
|
formatSlot = self.getSlot('format', parentSlot = parentSlot)
|
|
formatValue = formatSlot.getField(fields, default = 'spip')
|
|
slot = self.getSlot('body', parentSlot = parentSlot)
|
|
self.body_kind = copy.copy(self.body_kind) # Deleted below.
|
|
self.body_kind.textFormat = formatValue
|
|
layout = ObjectWebMixin.getEditLayout(self, fields, parentSlot)
|
|
del self.body_kind # Created above.
|
|
return layout
|
|
|
|
def getFormattedBody(self, translate = 0):
|
|
# TODO: don't duplicate from widgets.
|
|
fieldValue = self.body
|
|
if translate:
|
|
slot = self.getSlot('body')
|
|
translationsProxy = getProxyForServerRole('translations')
|
|
if translationsProxy:
|
|
fieldValue, destinationLanguage, state = \
|
|
translationsProxy.getTranslationInfos(
|
|
fieldValue, self.id, slot.getPath(),
|
|
self.getLanguage(),
|
|
context.getVar('readLanguages'))
|
|
if state not in ('translated', 'untranslatable'):
|
|
fieldValue = self.body
|
|
virtualHost = context.getVar('virtualHost')
|
|
sectionLevel = 2
|
|
if virtualHost:
|
|
sectionLevel = context.getVar('baseSectionLevel')
|
|
if self.format == 'docbook':
|
|
formattedText = parsers.makeHtmlFromDocBook(fieldValue)
|
|
elif self.format == 'html':
|
|
formattedText = parsers.makeHtmlFromHtml(fieldValue)
|
|
elif self.format == 'text':
|
|
formattedText = parsers.makeHtmlFromPreformattedText(fieldValue)
|
|
elif self.format == 'rst':
|
|
formattedText = parsers.makeHtmlFromReStructuredText(fieldValue,
|
|
sectionLevel = sectionLevel)
|
|
else:
|
|
formattedText = parsers.makeHtmlFromSpip(fieldValue,
|
|
sectionLevel = sectionLevel)
|
|
|
|
return replaceSpecialTags(formattedText)
|
|
|
|
def getViewLayout(self, fields, parentSlot = None):
|
|
layout = X.array()
|
|
if fields.has_key('title'):
|
|
translationsProxy = getProxyForServerRole('translations')
|
|
if translationsProxy:
|
|
title = translationsProxy.getTranslation(
|
|
fields['title'], self.getId(), 'self.title',
|
|
self.getLanguage(), context.getVar('readLanguages'))
|
|
else:
|
|
title = self.title
|
|
else:
|
|
title = ''
|
|
if parentSlot and parentSlot.getLabel() == 'Root':
|
|
context.setVar('pageTitle', title)
|
|
slot = self.getSlot('body', parentSlot = parentSlot)
|
|
self.body_kind = copy.copy(self.body_kind) # Deleted below.
|
|
self.body_kind.textFormat = self.format
|
|
if self.format == 'docbook':
|
|
#article = td # FIXME: this is obsolete
|
|
pass
|
|
else:
|
|
titlePage = X.div(_class = 'titlepage')
|
|
layout += titlePage
|
|
if not (parentSlot and parentSlot.getLabel() == 'Root') and title:
|
|
titlePage += X.h2(_class = 'title')(title)
|
|
from glasnost.proxy.GroupsProxy import getSetContainedIds
|
|
authorIds = getSetContainedIds(
|
|
self.authorsSet,
|
|
self.authorsSet_kind.itemKind.getServerRoles())
|
|
for authorId in authorIds:
|
|
titlePage += X.h3(_class = 'author')(
|
|
X.objectHypertextLabel(authorId))
|
|
slot = self.getSlot('editionTime', parentSlot = parentSlot)
|
|
titlePage += X.p(_class = 'pubdate')(
|
|
slot.getWidget().getHtmlValue(slot, fields))
|
|
slot = self.getSlot('body', parentSlot = parentSlot)
|
|
slot.getWidget().setInForm(0)
|
|
layout += slot.getWidget().getHtmlValue(slot, fields)
|
|
del self.body_kind # Created above.
|
|
|
|
layout += ObjectWebMixin.getViewLayout(
|
|
self, fields, parentSlot = parentSlot)
|
|
return layout
|
|
|
|
def getViewLayoutSlotNames(self, fields, parentSlot = None):
|
|
slotNames = ObjectWebMixin.getViewLayoutSlotNames(
|
|
self, fields, parentSlot = parentSlot)
|
|
slotNames = slotNames[:]
|
|
if 'body' in slotNames:
|
|
slotNames.remove('body')
|
|
userToken = context.getVar('userToken', default = '')
|
|
if context.getVar('useCompactLayout', default = 0) or \
|
|
not self.getWeb().canModifyObject(self.id):
|
|
for slotName in (
|
|
'authorsSet', 'creationTime', 'editionTime', 'format',
|
|
'lastEditorId', 'modificationTime', 'readersSet', 'title',
|
|
'writersSet'):
|
|
if slotName in slotNames:
|
|
slotNames.remove(slotName)
|
|
return slotNames
|
|
register(Article)
|
|
|
|
|
|
class ArticlesWeb(ObjectsWebMixin, ArticlesProxy):
|
|
def all(self):
|
|
return ObjectsWebMixin.viewAll(self, slotNames = ['editionTime'])
|
|
all.isPublicForWeb = 1
|
|
|
|
def diff(self, id, editionTime):
|
|
try:
|
|
editionTime = float(editionTime)
|
|
except ValueError:
|
|
return pageNotFound()
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
if not self.canGetObjectHistory(id):
|
|
return accessForbidden()
|
|
article = self.getObject(id)
|
|
version = self.getObjectDiff(id, editionTime)
|
|
|
|
keywords = {}
|
|
article.makeFieldsFromInstance(keywords)
|
|
article.repairFields(keywords)
|
|
|
|
label = article.getLabelTranslated(
|
|
context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
if version['diff']:
|
|
for line in version['diff']:
|
|
marker = line[0]
|
|
line = line[1:]
|
|
while line and line[-1] in ['\r', '\n']:
|
|
line = line[:-1]
|
|
stripped = line.lstrip()
|
|
if len(line) - len(stripped) > 0:
|
|
newLine = X.array()
|
|
for i in range(len(line) - len(stripped)):
|
|
newLine += X.nbsp
|
|
newLine += stripped
|
|
line = newLine
|
|
if not line:
|
|
line = X.nbsp
|
|
if marker == '@':
|
|
layout += X.hr(_class = 'diff')
|
|
elif marker == '\\':
|
|
if stripped == 'No newline at end of file':
|
|
continue
|
|
layout += X.div(_class = 'diff-error')(line)
|
|
elif marker == '+':
|
|
layout += X.div(_class = 'diff-new')(line)
|
|
elif marker == '-':
|
|
layout += X.div(_class = 'diff-old')(line)
|
|
else:
|
|
layout += X.div(_class = 'diff-context')(line)
|
|
layout += X.hr(_class = 'diff')
|
|
layout += X.br()
|
|
return writePageLayout(layout, _('Differences - %s') % label)
|
|
diff.isPublicForWeb = 1
|
|
|
|
def docBook(self, id):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
article = self.getObject(id)
|
|
title = getProxyForServerRole('translations').getTranslation(
|
|
article.getTitle(), article.getId(), 'self.title',
|
|
article.getLanguage(), context.getVar('readLanguages'))
|
|
body = getProxyForServerRole('translations').getTranslation(
|
|
article.getBody(), article.getId(), 'self.body',
|
|
article.getLanguage(), context.getVar('readLanguages'))
|
|
docBook = makeDocBookFromSpip(body, title = title)
|
|
html = parsers.makeHtmlFromDocBook(docBook)
|
|
html = replaceSpecialTags(html)
|
|
|
|
label = article.getLabelTranslated(
|
|
context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
layout += X.asIs(html)
|
|
return writePageLayout(layout, _('DocBook - %s') % label)
|
|
docBook.isPublicForWeb = 1
|
|
|
|
def docBookSource(self, id):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
article = self.getObject(id)
|
|
title = getProxyForServerRole('translations').getTranslation(
|
|
article.getTitle(), article.getId(), 'self.title',
|
|
article.getLanguage(), context.getVar('readLanguages'))
|
|
body = getProxyForServerRole('translations').getTranslation(
|
|
article.getBody(), article.getId(), 'self.body',
|
|
article.getLanguage(), context.getVar('readLanguages'))
|
|
docBook = makeDocBookFromSpip(body, title = title)
|
|
docBook = replaceSpecialTags(docBook)
|
|
|
|
label = article.getLabelTranslated(
|
|
context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
layout += X.pre(_class = 'spip')(docBook)
|
|
return writePageLayout(layout, _('DocBook Source - %s') % label)
|
|
docBookSource.isPublicForWeb = 1
|
|
|
|
def getViewAllNavigationButtonsBarLayoutSomeTimes(self):
|
|
layout = X.array()
|
|
layout += X.buttonStandalone('every-article', X.actionUrl('all'))
|
|
layout += ObjectsWebMixin.getViewAllNavigationButtonsBarLayout(self)
|
|
return layout
|
|
|
|
def getViewNavigationButtonsBarLayout(self, object, fields):
|
|
layout = X.array()
|
|
layout += ObjectsWebMixin.getViewNavigationButtonsBarLayout(
|
|
self, object, fields)
|
|
if self.canGetObjectHistory(object.id):
|
|
layout += X.buttonStandalone(
|
|
'history', X.idUrl(object.id, 'history'))
|
|
userToken = context.getVar('userToken', default = '')
|
|
if userToken and fields.has_key('format') \
|
|
and fields['format'] != 'text':
|
|
layout += X.buttonStandalone(
|
|
'source', X.idUrl(object.id, 'source'))
|
|
return layout
|
|
|
|
def getViewOtherActionButtonsBarLayout(self, object, fields):
|
|
layout = X.array()
|
|
layout += ObjectsWebMixin.getViewOtherActionButtonsBarLayout(
|
|
self, object, fields)
|
|
userToken = context.getVar('userToken', default = '')
|
|
return layout
|
|
|
|
def history(self, id):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
if not self.canGetObjectHistory(id):
|
|
return accessForbidden()
|
|
article = self.getObject(id)
|
|
history = self.getObjectHistory(id)
|
|
|
|
label = article.getLabelTranslated(
|
|
context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
if len(history) > 0:
|
|
table = X.table(_class = 'history')
|
|
layout += table
|
|
thead = X.thead()
|
|
table += thead
|
|
thead += X.tr(
|
|
X.th(_('Date')),
|
|
X.th(_('Author')),
|
|
X.th(''),
|
|
X.th('') )
|
|
tbody = X.tbody()
|
|
table += tbody
|
|
history.reverse()
|
|
for version in history:
|
|
editionTime = time.strftime(
|
|
'%Y-%m-%d %H:%M:%S',
|
|
time.localtime(version['editionTime']))
|
|
if version.has_key('editorId'):
|
|
editorHypertextLabel= X.objectHypertextLabel(
|
|
version['editorId'])
|
|
else:
|
|
editorHypertextLabel = None
|
|
tbody += X.tr(
|
|
X.td(editionTime),
|
|
X.td(editorHypertextLabel),
|
|
X.td(X.buttonStandalone(
|
|
'version', X.idUrl(id, 'version').add(
|
|
'editionTime', version['editionTime']))),
|
|
X.td(X.buttonStandalone('diff', X.idUrl(id, 'diff').add(
|
|
'editionTime', version['editionTime']))),
|
|
)
|
|
layout += X.br()
|
|
return writePageLayout(layout, _('History - %s') % label)
|
|
history.isPublicForWeb = 1
|
|
|
|
def source(self, id):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
article = self.getObject(id)
|
|
body = getProxyForServerRole('translations').getTranslation(
|
|
article.getBody(), article.getId(), 'self.body',
|
|
article.getLanguage(), context.getVar('readLanguages'))
|
|
|
|
label = article.getLabelTranslated(
|
|
context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
layout += X.pre(_class = 'spip')(body)
|
|
return writePageLayout(layout, _('Source - %s') % label)
|
|
source.isPublicForWeb = 1
|
|
|
|
def version(self, id, editionTime):
|
|
try:
|
|
editionTime = float(editionTime)
|
|
except ValueError:
|
|
return pageNotFound()
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
if not self.canGetObjectHistory(id):
|
|
return accessForbidden()
|
|
article = self.getObject(id)
|
|
version = self.getObjectVersion(id, editionTime)
|
|
article.format = version['format']
|
|
article.body = version['body']
|
|
slot = article.getSlot('body')
|
|
article.body_kind = copy.copy(article.body_kind)
|
|
# Deleted in article.getViewLayout() below.
|
|
article.body_kind.isTranslatable = 0
|
|
if version.has_key('editorId'):
|
|
article.lastEditorId = version['editorId']
|
|
article.editionTime = version['editionTime']
|
|
|
|
keywords = {}
|
|
article.makeFieldsFromInstance(keywords)
|
|
article.repairFields(keywords)
|
|
|
|
label = article.getLabelTranslated(
|
|
context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
slot = slots.Root(article)
|
|
widget = slot.getWidget()
|
|
layout += widget.getModelPageBodyLayout(slot, keywords)
|
|
|
|
return writePageLayout(layout, _('Version - %s') % label)
|
|
version.isPublicForWeb = 1
|
|
|
|
def viewAll(self):
|
|
context.push(_level = 'viewAll',
|
|
defaultDispatcherId = context.getVar('dispatcherId'))
|
|
try:
|
|
if not self.canGetObjects():
|
|
return accessForbidden()
|
|
isAdmin = self.isAdmin()
|
|
userId = context.getVar('userId', default = '')
|
|
if userId:
|
|
userSet = [userId]
|
|
else:
|
|
userSet = None
|
|
|
|
layout = X.array()
|
|
requiredSlotNames = ['editionTime', 'title']
|
|
displayedSlotNames = ['editionTime']
|
|
|
|
if userSet:
|
|
lastArticles = self.getLastObjects(
|
|
10, None, None, userSet, requiredSlotNames)
|
|
layout += self.getObjectsSectionLayout(
|
|
lastArticles,
|
|
_("""Your last articles"""),
|
|
displayedSlotNames)
|
|
|
|
lastArticles = self.getLastObjects(
|
|
10, None, userSet, None, requiredSlotNames)
|
|
layout += self.getObjectsSectionLayout(
|
|
lastArticles,
|
|
_("""The last articles"""),
|
|
displayedSlotNames)
|
|
|
|
self.getViewAllNavigationButtonsBarLayout = \
|
|
self.getViewAllNavigationButtonsBarLayoutSomeTimes
|
|
layout += self.getViewAllButtonsBarLayout()
|
|
del(self.getViewAllNavigationButtonsBarLayout)
|
|
finally:
|
|
context.pull(_level = 'viewAll')
|
|
return writePageLayout(layout, _('Articles'))
|
|
viewAll.isPublicForWeb = 1
|
|
|