Petites modifs faites sans connexion; la plus grosse est la communication du

champ 'email' des People à tout le monde.

(fin de la petite heure de squat internet dans quelques minutes)
This commit is contained in:
fpeters 2003-09-23 09:02:03 +00:00
parent 789cfff28d
commit 8452fe2096
28 changed files with 388 additions and 188 deletions

View File

@ -47,7 +47,7 @@ watercolor - Water Color
- Colours: black on light blue; shades of blue
- Navigation: one level, vertical, top right
- Quality: * (nice but lots of styles needed by Glasnost are not yet done)
- Quality: ** (nice but lots of styles needed by Glasnost are not yet done)
- Inspiration: some watercolor metacity theme elements

View File

@ -45,13 +45,6 @@ __doc__ = """Glasnost Login Python Web Page"""
__version__ = '$Revision$'[11:-2]
import glasnost.common.context as context
from glasnost.common.ObjectsCommon import ObjectCommon
import glasnost.common.slots as slots
import glasnost.common.tools_new as commonTools
import glasnost.common.xhtmlgenerator as X
from glasnost.web.ObjectsWeb import BaseObjectWebMixin
from glasnost.web.tools import *
@ -64,34 +57,6 @@ def index(nextUri = '', access = '', again = '', error = '', **keywords):
def logout(nextUri = ''):
authenticationProxy = getProxyForServerRole('authentication')
authenticationProxy.delUserToken()
session = context.getVar('session')
if session is not None:
# Don't delete the session, just remove userToken & userId from it.
# sessionToken = context.getVar('sessionToken')
# getProxyForServerRole('sessions').deleteSession(sessionToken)
# context.delVar('sessionToken')
# context.delVar('session')
if session.has_key('userId'):
del session['userId']
session['isDirty'] = 1
context.setVar('userId', '')
if session.has_key('userToken'):
del session['userToken']
session['isDirty'] = 1
context.setVar('userToken', '')
# if not context.getVar('sessionTokenInCookie', default = 0):
# # The sessionToken was not stored in a cookie, so don't try to use
# # the cookie when loging out.
# context.setVar('canUseCookie', 0)
if not nextUri:
nextUri = '/'
else:
nextUri = cleanUpUri(nextUri, ['sessionToken'])
canUseCookie = context.getVar('canUseCookie', default = 0)
if not canUseCookie:
nextUri = appendToUri(nextUri,
'sessionToken=' + context.getVar('sessionToken'))
return redirect(nextUri)
authWeb = getWebForServerRole('authentication')
return authWeb.logout(nextUri)

View File

@ -165,7 +165,6 @@ class AuthenticationLoginPasswordVirtualServer(AdministrableVirtualServer):
virtualServerId = context.getVar('applicationId')
hostName = getProxyForServerRole('virtualhosts').getHostName(
virtualServerId)
message = """\
Dear Glasnost administrators,
@ -192,9 +191,11 @@ For more information about this user, please see:
'userLabel': object.getLabel(),
}
toAddresses = self.getAdminEmailAddresses()
sendMail(
mailFrom = self.adminEmailAddress,
mailTo = self.adminEmailAddress,
mailFrom = toAddresses[0],
mailTo = toAddresses,
mailSubject = messageSubject,
mailMessage = message,
)
@ -205,11 +206,11 @@ For more information about this user, please see:
if not self.getServer().getAdminCore().stockPasswordsInClearText:
return # FIXME: raise sth ?
email = getEmailForObject(object)
if not email:
emailAddress = getEmailForObject(object)
if not emailAddress:
return
toAddresses = [email]
toAddress = [emailAddress]
password = authenticationObject.password
try:
@ -260,9 +261,11 @@ The Glasnost administrator - %(fromAddress)s
'login': authenticationObject.login,
'password': authenticationObject.password,
}
fromAddresses = self.getAdminEmailAddresses(stopAsap = 1)
sendMail(
mailFrom = self.adminEmailAddress,
mailTo = toAddresses,
mailFrom = fromAddresses[0],
mailTo = toAddress,
mailSubject = messageSubject,
mailMessage = message,
)
@ -288,7 +291,32 @@ The Glasnost administrator - %(fromAddress)s
if authObject.login == authenticationObject.login:
return userId
raise faults.WrongLogin(authenticationObject.login)
def getAdminEmailAddresses(self, stopAsap = 0):
try:
adminIds = getSetContainedIds(
self.admin.adminsSet, ['people'],
serverId = virtualServerId)
except faults.UncountableGroup:
adminIds = []
toAddresses = []
for adminId in adminIds:
try:
administrator = getObject(adminId)
except: # whatever
continue
emailAddress = getEmailForObject(object)
if emailAddress:
toAddresses.append(emailAddress)
if stopAsap:
return toAddresses
if not toAddresses:
toAddresses.append(self.adminEmailAddress)
return toAddresses
def getServer(self):
# FIXME: should come from somewhere
"""Return the server string."""

View File

@ -154,8 +154,8 @@ class Person(ObjectServerMixin, PersonCommon):
userId = getProxyForServerRole('authentication').getUserId()
if self.id != userId and not self.getServer().isAdmin():
if personExport.has_key('email') and not userId:
del personExport['email']
#if personExport.has_key('email') and not userId:
# del personExport['email']
if personExport.has_key('voteTokens'):
if not userId:
del personExport['voteTokens']

View File

@ -137,6 +137,8 @@ class PreferencesServer(Server):
def rememberId(self, id):
virtualServerId = context.getVar('applicationId')
if virtualServerId == 'glasnost://system/preferences':
return
virtualServer = self.getVirtualServer(virtualServerId)
userId = getProxyForServerRole('authentication').getUserId()
if virtualServer.objects.has_key(userId):

View File

@ -127,7 +127,8 @@ class Translation(ObjectServerMixin, ObjectCommon):
translatorIds = None
translatorIds_kind_keyKind_valueName = 'String'
translatorIds_kind_valueKind_valueName = 'Id'
translatorIds_kind_valueKind_valueName = 'Sequence'
translatorIds_kind_valueKind_value_itemKind_valueName = 'Id'
translatorIds_kindName = 'Mapping'
untranslatableLocalizations = None

View File

@ -118,13 +118,18 @@ class AppointmentCommon(ObjectCommon):
return result
def getLabel(self):
label = self.getTitle()
if label is None:
title = self.getTitle()
if title is None:
return ''
hourTime = self.getBeginningHourAndMinute()
if label and hourTime:
return '%s - %s' % (hourTime, label)
return label
if hourTime:
when = '%s %s' % (
time.strftime('%d/%m/%Y', time.localtime(self.start)),
hourTime)
else:
when = time.strftime('%d/%m/%Y', time.localtime(self.start))
return '%s - %s' % (when, title)
def getTitle(self):
return self.title

View File

@ -66,6 +66,7 @@ class AccountLoginPasswordCommon(things.BaseThing):
password_kind_widgetName = 'InputPassword'
password_kindName = 'Password'
serverRole = 'authentication-login-password'
thingCategory = 'authentication'
def __eq__(self, other):

View File

@ -69,77 +69,6 @@ class PersonCommon(ObjectCommon):
This is the definition of the attributes of a person object.
This object is used by the PeopleProxy.
Attributes
==========
*creationTime*:
+ Is a kind.
+ Set to None.
+ Is public to Xml Rpc
+ kindName : 'CreationTime'
*email*:
+ Is a kind.
+ Set to None.
+ Is required
+ Is public for Xml Rpc
+ kindName : 'Email'
*fingerprint*:
+ This is the GnuPG fingerprint.
+ Is a kind.
+ Set to None.
+ Is public for Xml Rpc
+ kindName : 'Fingerprint'
*firstName*:
+ Is a kind.
+ Set to None.
+ Is public for Xml Rpc
+ Is required
+ Is not translatable
+ kindName : 'String'
*language*:
+ Is a kind.
+ Not set
+ kindName : None
*lastName*:
+ Is a kind.
+ Set to None.
+ Is public for Xml Rpc
+ Is not translatable
+ kindName : 'String'
*modificationTime*:
+ The last modification date.
+ Is a kind.
+ Set to None.
+ Is public for Xml Rpc
+ kindName : 'ModificationTime'
*nickname*:
+ Is a kind.
+ Set to None.
+ Is public for Xml Rpc
+ Is not translatable
+ kindName : 'String'
*serverRole*:
The server name string that manage theses objects isntances. Default :
'people'.
*voteTokens*
+ The map of the user vote tokens, this is like a dictionnary with 'Id'
kind for key and 'Token' kinds as values.
+ Is a kind.
+ Set to None.
+ Is public for Xml Rpc
+ kindName : 'Mapping'
"""
creationTime = None
@ -154,6 +83,7 @@ class PersonCommon(ObjectCommon):
fingerprint_kind_balloonHelp = N_(
'Enter the GnuPG public key fingerprint; '\
'mandatory to receive encrypted e-mails.')
fingerprint_kind_stateInViewMode = 'read-only/hidden-if-empty'
fingerprint_kindName = 'Fingerprint'
firstName = None
@ -175,6 +105,7 @@ class PersonCommon(ObjectCommon):
nickname = None
nickname_kind_balloonHelp = N_('Enter an eventual nickname here.')
nickname_kind_isTranslatable = 0
nickname_kind_stateInViewMode = 'read-only/hidden-if-empty'
nickname_kindName = 'String'
serverRole = 'people'

View File

@ -1875,7 +1875,7 @@ def makeHtmlFromReStructuredText(text, simple = 0, inline = 0, **keywords):
atts = node.attributes.copy()
fullUrl = None
matchObject = re.match(
r'((?P<localId>\d+)((,(?P<width>\d+)\*(?P<height>\d+))|))?', atts['uri'])
r'((?P<localId>\S+)((,(?P<width>\d+)\*(?P<height>\d+))|))?', atts['uri'])
if matchObject and matchObject.group('localId'):
localId = matchObject.group('localId')
width = matchObject.group('width')

View File

@ -81,7 +81,7 @@ class Appointment(ObjectWebMixin, Appointment):
participantsSet_kind_itemKind_value_defaultValue = 'user'
participantsSet_kind_balloonHelp = N_(
'Select the people and groups who are assigned to this appointment.')
participantsSet_kind_widget_fieldLabel = N_('Participants')
participantsSet_kind_label = N_('Participants')
start_kind_widget_fieldLabel = N_('Start')

View File

@ -324,10 +324,6 @@ class ArticlesWeb(ObjectsWebMixin, ArticlesProxy):
layout += ObjectsWebMixin.getViewOtherActionButtonsBarLayout(
self, object, fields)
userToken = context.getVar('userToken', default = '')
if userToken and fields.has_key('format') \
and fields['format'] in ('html', 'spip'):
layout += X.buttonStandalone(
'pretion', X.idUrl(object.id, 'pretion'))
return layout
def history(self, id):

View File

@ -59,6 +59,8 @@ from tools import *
class AdminAuthenticationLoginPassword(AdminWithoutWritersMixin,
AdminAuthenticationLoginPassword):
stockPasswordsInClearText_kind_stateInEditMode = 'hidden'
stockPasswordsInClearText_kind_stateInViewMode = 'hidden'
stockPasswordsInClearText_kind_widgetName = 'InputCheckBox'
userCanChoosePassword_kind_defaultValue = 0
@ -296,6 +298,18 @@ class AuthenticationLoginPasswordWeb(WebMixin,
form = X.form(action = X.actionUrl('newAccountSubmit'),
method = 'post')
layout += form
#context.push(_level = 'edit', layoutMode = 'edit')
#slot = slots.Root(userCardObject)
#widget = slot.getWidget()
#form += widget.getModelPageBodyLayout(slot, keywords)
#slot = slots.Root(authObject)
#widget = slot.getWidget()
#form += widget.getModelPageBodyLayout(slot, keywords)
#context.pull()
form += userCardObject.getEditLayout(keywords)
form += authObject.getEditLayout(keywords)

View File

@ -191,8 +191,7 @@ class AuthenticationWeb(WebMixin, AuthenticationProxy):
def getViewAllButtonsBarLayout(self):
layout = X.div(_class = 'buttons-bar')
userToken = context.getVar('userToken')
if self.canModifyAdmin() and userToken:
if self.canModifyAdmin():
layout += X.buttonStandalone('settings', X.actionUrl('admin'))
return layout
@ -241,6 +240,39 @@ class AuthenticationWeb(WebMixin, AuthenticationProxy):
context.setVar('canUseCookie', 1)
return redirect(uri)
def logout(self, nextUri = ''):
authenticationProxy = getProxyForServerRole('authentication')
authenticationProxy.delUserToken()
session = context.getVar('session')
if session is not None:
# Don't delete the session, just remove userToken & userId from it.
# sessionToken = context.getVar('sessionToken')
# getProxyForServerRole('sessions').deleteSession(sessionToken)
# context.delVar('sessionToken')
# context.delVar('session')
if session.has_key('userId'):
del session['userId']
session['isDirty'] = 1
context.setVar('userId', '')
if session.has_key('userToken'):
del session['userToken']
session['isDirty'] = 1
context.setVar('userToken', '')
# if not context.getVar('sessionTokenInCookie', default = 0):
# # The sessionToken was not stored in a cookie, so don't try to use
# # the cookie when loging out.
# context.setVar('canUseCookie', 0)
if not nextUri:
nextUri = '/'
else:
nextUri = cleanUpUri(nextUri, ['sessionToken'])
canUseCookie = context.getVar('canUseCookie', default = 0)
if not canUseCookie:
nextUri = appendToUri(nextUri,
'sessionToken=' + context.getVar('sessionToken'))
return redirect(nextUri)
logout.isPublicForWeb = 1
def viewAll(self):
admin = self.getAdmin()
labels = admin.authenticationMethods_kind_itemKind_value_widget_labels
@ -259,7 +291,10 @@ class AuthenticationWeb(WebMixin, AuthenticationProxy):
if not authenticationMethodProxy:
continue
layout += X.h3(_(labels[authMethod]))
accounts = authenticationMethodProxy.getAccounts()
try:
accounts = authenticationMethodProxy.getAccounts()
except faults.UserAccessDenied:
accounts = None
if accounts:
table = X.table(_class = 'objects-table')
layout += table

View File

@ -82,6 +82,7 @@ class Person(ObjectWebMixin, Person):
lastName_kind_widgetName = 'InputText'
memberOf_kind_importExport = 'private'
memberOf_kind_stateInViewMode = 'read-only/hidden-if-empty'
memberOf_kind_stateInEditMode = 'read-only'
memberOf_kind_itemKind_valueName = 'Id'
memberOf_kind_widget_fieldLabel = N_('Member of groups')

View File

@ -98,8 +98,12 @@ class Rubric(ObjectWebMixin, Rubric):
layout += X.div(_class = 'clear')(X.hr())
layout += ObjectWebMixin.getViewLayout(
self, fields, parentSlot = parentSlot)
slot = self.getSlot('membersSet')
widget = slot.getWidget()
layout += widget.getHtmlValue(slot, fields)
#layout += ObjectWebMixin.getViewLayout(
# self, fields, parentSlot = parentSlot)
return layout
def getViewLayoutSlotNames(self, fields, parentSlot = None):

View File

@ -161,15 +161,8 @@ class TranslatorsSetWidget(widgets.Multi):
'to': _(translation.languageLabels[toLanguage]),
}
labelAttributes = {'for': fieldName}
if self.isInForm():
layout = X.label(**labelAttributes)(
return X.span(_class = 'label')(
fieldLabelLocalized, X.asIs(_(':')))
# FIXME: Is this line required?
layout += X.input(name = fieldName, type = 'hidden', value = '1')
else:
return X.span(_class = 'label')(
fieldLabelLocalized, X.asIs(_(':')))
return layout
widgets.register(TranslatorsSetWidget)
@ -653,12 +646,14 @@ class TranslationsWeb(ObjectsWebMixin, TranslationsProxy):
if localizationKey:
fromLanguage = localizationKey[:2]
toLanguage = localizationKey[2:4]
localizationLabelLocalized = _('%(from)s to %(to)s') % {
'from': _(translation.languageLabels[fromLanguage]),
'to': _(translation.languageLabels[toLanguage]),
}
pageTitle = '%s: %s' % (
_('Translations'),
_('%(from)s to %(to)s') % {
'from': _(translation.languageLabels[fromLanguage]),
'to': _(translation.languageLabels[toLanguage]),
})
else:
localizationLabelLocalized = ''
pageTitle = _('Translations')
otherLocalizations = []
for otherLocalizationKey in localizationKeys:
@ -722,8 +717,8 @@ class TranslationsWeb(ObjectsWebMixin, TranslationsProxy):
layout += X.br()
if self.canGetAdmin():
layout += X.buttonStandalone('settings', X.actionUrl('admin'))
return writePageLayout(
layout, _('Translations') + ':' + ' ' + localizationLabelLocalized)
layout += X.div(_class = 'buttons-bar')(
X.buttonStandalone('settings', X.actionUrl('admin')))
return writePageLayout(layout, pageTitle)
viewAll.isPublicForWeb = 1

View File

@ -875,6 +875,8 @@ class VotesWeb(ObjectsWebMixin, VotesProxy):
layout = X.array()
if self.canGetAdmin():
layout += X.buttonStandalone('settings', X.actionUrl('admin'))
layout += X.div(_class = 'buttons-bar')(
X.buttonStandalone('settings', X.actionUrl('admin')))
return writePageLayout(layout, _('Votes'))
viewAll.isPublicForWeb = 1

View File

@ -468,7 +468,13 @@ def replaceSpecialTags(text):
def imageReplacer(matchObject):
serverRole = matchObject.group('serverRole')
localId = matchObject.group('localId')
objectId = 'glasnost://%s/%s/%s' % (
if localId.startswith('/remote/'):
objectId = 'glasnost://%s/%s/%s' % (
localId.split('/')[2],
serverRole,
localId.split('/')[3])
else:
objectId = 'glasnost://%s/%s/%s' % (
getDefaultDispatcherHostNameAndPort(),
serverRole, localId)
web = getWebForServerRole(serverRole)
@ -614,7 +620,7 @@ def getTemplateVars():
'nameAndQuery': loginUrl,
}
logoutUrl = X.roleUrl('login', 'logout')
logoutUrl = X.roleUrl('authentication', 'logout')
httpScriptDirectoryPath = context.getVar('httpScriptDirectoryPath')

View File

@ -934,9 +934,9 @@ class Multi(WidgetMixin, proxyWidgets.Multi):
layout += self.getModelErrorLayout(slot, fields)
if self.inline:
fieldset = X.ul(_class = 'inline')
fieldset = X.ul(_class = 'multi inline')
else:
fieldset = X.ul()
fieldset = X.ul(_class = 'multi')
if self.isInForm():
layout += X.input(name = countName, type = 'hidden', value = count)
layout += X.div(fieldset)
@ -1334,7 +1334,7 @@ class SelectId(WidgetMixin, proxyWidgets.SelectId):
layout += select
if showOthersButton:
layout += X.input(
_class = 'button', type = 'button', value = _('Others'),
_class = 'button others', type = 'button', value = _('Others'),
onClick = 'selectOthers(this.parentNode, "%s")' % ','.join(
[x for x in serverRoles if getWebForServerRole(x) and \
hasattr(getWebForServerRole(x), 'objectsNameCapitalized')]))

View File

@ -46,6 +46,7 @@
__version__ = '$Revision$'[11:-2]
import md5
import os
import sys
import unittest
@ -59,11 +60,13 @@ import glasnost.common.faults as faults
import glasnost.proxy.ArticlesProxy as ArticlesProxy
import glasnost.proxy.PageNamesProxy as PageNamesProxy
from glasnost.proxy.TranslationsProxy import Localization
from glasnost.proxy.tools import getProxyForServerRole
articlesProxy = getProxyForServerRole('articles')
pageNamesProxy = getProxyForServerRole('pagenames')
translationsProxy = getProxyForServerRole('translations')
class BuildCase01_articlesFromFiles(unittest.TestCase):
@ -72,7 +75,8 @@ class BuildCase01_articlesFromFiles(unittest.TestCase):
files = [x for x in os.listdir('articles/') if \
x != 'CVS' and \
x[0] != '.' and \
x[-1] != '~' ]
x[-1] != '~' and \
not '.' in x ]
pageNames = {}
for f in files:
fd = open('articles/%s' % f)
@ -96,6 +100,62 @@ class BuildCase01_articlesFromFiles(unittest.TestCase):
pageName.name = f
pageName.mappedId = articleId
pageNamesProxy.addObject(pageName)
def build02_translations(self):
files = [x for x in os.listdir('articles/') if \
x != 'CVS' and \
x[0] != '.' and \
x[-1] != '~' and \
not '.' in x ]
pageNames = {}
for f in files:
translationFiles = [x for x in os.listdir('articles/') if \
x.startswith('%s.' % f) and
len(x) == len(f)+3 ]
if not translationFiles:
continue
articleId = pageNamesProxy.getIdByName(f)
article = articlesProxy.getObject(articleId)
titleStringDigest = md5.new(
article.title.replace('\r\n', '\n')).hexdigest()
bodyStringDigest = md5.new(
article.body.replace('\r\n', '\n')).hexdigest()
sourceLanguage = article.language
translationsProxy.getTranslation(article.title, article.id,
'self.title', sourceLanguage, ['--'])
translationsProxy.getTranslation(article.body, article.id,
'self.body', sourceLanguage, ['--'])
for tFile in translationFiles:
fd = open('articles/' + tFile)
title = fd.readline()[2:].strip()
fd.readline()
body = fd.read()
fd.close()
localization = Localization()
localization.sourceStringDigest = titleStringDigest
localization.destinationLanguage = tFile[-2:]
localization.destinationString = title
localization.isFuzzy = 0
localization.sourceString = article.title
localization.sourceLanguage = sourceLanguage
localization.isTranslatable = 1
translationsProxy.modifyLocalization(localization)
localization = Localization()
localization.sourceStringDigest = bodyStringDigest
localization.destinationLanguage = tFile[-2:]
localization.destinationString = body
localization.isFuzzy = 0
localization.sourceString = article.body
localization.sourceLanguage = sourceLanguage
localization.isTranslatable = 1
translationsProxy.modifyLocalization(localization)
buildLoader = unittest.TestLoader()

View File

@ -40,7 +40,7 @@ Copyright (c) 2003 Odile B
Lutin <http://www.codelutin.com>`_, Pierre-Antoine Dejace, Thierry Dulieu,
`Easter-eggs <http://www.easter-eggs.com>`_, `Entr'ouvert
<http://www.entrouvert.com>`_, Florent Monnier, Cédric Musso, `Ouvaton
<http://ouvaton.coop/>`_, Frédéric Péters, Benjamin Poussin, Rodolphe
<http://ouvaton.coop>`_, Frédéric Péters, Benjamin Poussin, Rodolphe
Quiédeville, Emmanuel Raviart, Sébastien Régnier, Emmanuel Saracco, `Théridion
<http://www.theridion.com>`_ & `Vecam <http://www.vecam.org>`_.
@ -52,6 +52,13 @@ by SMEs - programme.
----
In December 2003 Lyon will hold the `World Summit of Cities and Local
Authorities on the Information Society <http://www.cities-lyon.org>`_. Check
out the `Cities Lyon <http://www.cities-lyon.org>`_ web site to see how
Glasnost can be customized.
----
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
@ -59,6 +66,6 @@ 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.
**FITNESS FOR A PARTICULAR PURPOSE**. See the `GNU General Public License
<alias gpl>`_ for more details.

72
system/articles/about.fr Normal file
View File

@ -0,0 +1,72 @@
# À propos de Glasnost
Développeurs:
- Odile Bénassy <obenassy at entrouvert dot com>
- Romain Chantereau <rchantereau at entrouvert dot com>
- Nicolas Clapiès <nclapies at easter-eggs dot org>
- Pierre-Antoine Dejace <padejace at entrouvert dot be>
- Frédéric Péters <fpeters at entrouvert dot be>
- Benjamin Poussin <poussin at codelutin dot com>
- Emmanuel Raviart <eraviart at entrouvert dot com>
- Sébastien Régnier <regnier at codelutin dot com>
- Emmanuel Saracco <esaracco at easter-eggs dot com>
Auteur de la documentation:
- Cédric Musso <cmusso at easter-eggs dot org>
Graphistes:
- Thierry Dulieu <tdulieu at easter-eggs dot com>
- Florent Monnier <monnier at codelutin dot com>
- Frédéric Péters <fpeters at entrouvert dot be>
Traducteurs:
- Odile Bénassy <obenassy at entrouvert dot com> (Allemand)
- Adam Huuva <sventon at easter-eggs dot com> (Finnois et Suédois)
- Hélène Martin-Brelot <helen.martinb at noos dot fr> (Allemand)
----
Copyright (c) 2000, 2001 `Easter-eggs <http://www.easter-eggs.com>`_ & Emmanuel
Raviart
Copyright (c) 2002 Odile Bénassy, `Code Lutin <http://www.codelutin.com>`_,
Thierry Dulieu, `Easter-eggs <http://www.easter-eggs.com>`_, `Entr'ouvert
<http://www.entrouvert.com>`_, Frédéric Péters, Benjamin Poussin, Emmanuel
Raviart, Emmanuel Saracco & `Théridion <http://www.theridion.com>`_.
Copyright (c) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès, `Code
Lutin <http://www.codelutin.com>`_, Pierre-Antoine Dejace, Thierry Dulieu,
`Easter-eggs <http://www.easter-eggs.com>`_, `Entr'ouvert
<http://www.entrouvert.com>`_, Florent Monnier, Cédric Musso, `Ouvaton
<http://ouvaton.coop>`_, Frédéric Péters, Benjamin Poussin, Rodolphe
Quiédeville, Emmanuel Raviart, Sébastien Régnier, Emmanuel Saracco, `Théridion
<http://www.theridion.com>`_ & `Vecam <http://www.vecam.org>`_.
----
En 2003, le développement de Glasnost est soutenu par le Ministère de
l'Économie, des Finances et de l'Industrie dans le cadre du programme `UCIP
<http://www.industrie.gouv.fr/ucip2002>`_ - Utilisation Collective d'Internet
par les PME.
----
En décembre 2003 la ville de Lyon accueillera le `Sommet mondial mondial des
Villes et des Pouvoirs Locaux sur la Société de l'Information
<http://www.cities-lyon.org>`_. Regardez le site web du `Sommet de Lyon
<http://www.cities-lyon.org>`_ pour voir comment Glasnost peut être
personnalisé.
----
Ce programme est un logiciel libre ; vous pouvez le redistribuer
et/ou le modifier sous les termes de la Licence Publique Générale
GNU (GPL) telle que publiée par la Free Software Foundation ;
soit la version 2 de cette licence, soit (à votre convenance) toute
autre version ultérieure.
Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE
GARANTIE ; sans même la garantie implicite d'AVOIR UNE QUELCONQUE VALEUR
MARCHANDE ou de CONVENIR À UN BESOIN PARTICULIER. Pour plus de détails, lisez
la `Licence Publique Générale GNU <alias gpl>`_.

View File

@ -410,6 +410,7 @@ div.navBox ul {
div.navBox a {
text-decoration: none;
color: inherit;
display: block;
}
div.navBox li {

View File

@ -37,6 +37,7 @@
<div class="personalBar">
<a class="button user" tal:condition="user" tal:attributes="href user.getUrl()" tal:content="user">userName</a>
<span tal:condition="not user" tal:translate="">You are not logged in</span>
<span metal:use-macro="buttons.tal/login" />
<span metal:use-macro="buttons.tal/preferences" />
<span metal:use-macro="buttons.tal/admin" />
<span metal:use-macro="buttons.tal/logout" />
@ -48,32 +49,11 @@
<span metal:use-macro="misc.tal/breadCrumb" />
</div>
<!--
<form metal:use-macro="forms.tal/formGo" />
<form metal:use-macro="forms.tal/formAdd" />
-->
<div id="leftBar">
<div id="navBox" tal:condition="user">
<span class="boxTitle">Navigation</span>
<ul metal:use-macro="nav.tal/ulRoles" />
</div>
<div id="loginBox" tal:condition="0">
<span class="boxTitle" tal:translate="">Login</span>
<form action="/login/submit" method="post">
<label for="login" tal:translate="">Username</label> <br />
<input name="login" size="12" type="text" value="" />
<br />
<label for="password" tal:translate="">Password</label> <br />
<input name="password" size="12" type="password" value="" /> <br />
<input name="nextUri" type="hidden" tal:attributes="value currentURI" />
<input tal:attr-translate="value" class="button" name="loginButton" type="submit" value="Login" />
</form>
<div class="even" tal:condition="not user and canAddObject('people')">
<span metal:use-macro="buttons.tal/newAccount" />
</div>
</div>
</div>
<div id="main-content">

View File

@ -792,3 +792,8 @@ table.calendar-week-full td.hour-busy {
background-color: #dee7ec;
}
ul.multi {
margin-top: 0;
margin-bottom: 0;
}

View File

@ -19,6 +19,12 @@
<a tal:attributes="href item.getUrl()" tal:content="item.label">item</a></li>
</ul>
<ul id="navigation-roles" tal:condition="user">
<li tal:repeat="role [x for x in getServerRoles() if canGetObjects(x)]"><a
tal:attributes="href role.getUrl()"
tal:content="role.label">server</a></li>
</ul>
<div id="content">
<div metal:define-slot="main">
Content comes here.
@ -27,9 +33,11 @@
</div>
<div id="footer">
<span metal:use-macro="buttons.tal/newAccount">new account</span>
<span metal:use-macro="buttons.tal/login">login</span>
<span metal:use-macro="buttons.tal/logout" />
<span metal:use-macro="buttons.tal/preferences" />
<a href="http://glasnost.entrouvert.org">Glasnost</a>
<!--<span metal:use-macro="buttons.tal/newAccount">new account</span>
<span metal:use-macro="buttons.tal/login">login</span>-->
</div>
</body>
</html>

View File

@ -1,3 +1,5 @@
@import url("/css/calendar.css");
html, body {
font-family: sans-serif;
margin: 0;
@ -110,10 +112,19 @@ div#footer {
font-size: 80%;
}
div#footer a.button {
float: right;
margin: 0 0.5em;
}
.tooltip, .balloon-help {
display: none;
}
div#content ul {
list-style: circle;
}
div#content form {
margin-top: 1em;
}
@ -126,17 +137,26 @@ div.row .label {
padding-right: 1em;
}
div.row .cell {
display: block;
margin-left: 10em;
}
div.row {
margin-bottom: 0.2em;
margin: 0.5em 0em;
clear: both;
}
div.row ul {
padding: 0;
}
div.buttons-bar {
margin-top: 1em;
}
div.buttons-bar .button {
margin: 0 1em;
margin: 0 0.7em;
}
input.button, a.button {
@ -152,3 +172,64 @@ caption {
display: none;
}
ul#navigation-roles {
position: absolute;
top: 100px;
left: 5px;
width: 120px;
background: #90abdb;
padding: 0 0.2em;
margin: 0;
list-style: none;
text-align: right;
border: 1px solid white;
}
input.others {
display: none;
}
div.error {
margin: 1em 0;
}
span.error-message {
display: block;
font: bold smaller sans-serif;
color: #c00;
}
div#content div.row ul.multi {
list-style-type: none;
}
hr {
color: #33517f;
background-color: #33517f;
border: 0;
height: 1px;
width: 90%;
}
table {
margin: 1em;
border: 1px solid #44618f;
}
th {
background: #7297ce;
text-align: center;
}
div.diff-new {
background-color: #80ff80;
}
div.diff-old {
background-color: #ffff80;
}
div.diff-error {
font-size: 80%;
}