2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2021-01-01 17:51:08 +01:00
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# 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
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
""" Site ScoDoc pour plusieurs departements:
gestion de l ' installation et des creation de départements.
Chaque departement est géré par un ZScolar sous ZScoDoc .
"""
2021-01-28 23:28:48 +01:00
import time
2020-12-15 08:35:44 +01:00
import datetime
2021-01-28 23:28:48 +01:00
import string
import glob
import re
import inspect
import urllib
import urllib2
import cgi
import xml
2020-09-26 16:19:37 +02:00
2021-01-28 23:02:18 +01:00
from cStringIO import StringIO
2020-09-26 16:19:37 +02:00
from zipfile import ZipFile
2021-01-28 23:28:48 +01:00
import os . path
2020-09-26 16:19:37 +02:00
import traceback
2021-01-28 23:28:48 +01:00
from email . MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error
MIMEMultipart ,
)
from email . MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error
from email . MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error
from email . Header import Header # pylint: disable=no-name-in-module,import-error
from email import Encoders # pylint: disable=no-name-in-module,import-error
2020-12-15 08:35:44 +01:00
from sco_zope import (
ObjectManager ,
PropertyManager ,
RoleManager ,
Item ,
Persistent ,
Implicit ,
ClassSecurityInfo ,
DTMLFile ,
Globals ,
)
2020-09-26 16:19:37 +02:00
try :
import Products . ZPsycopgDA . DA as ZopeDA
except :
import ZPsycopgDA . DA as ZopeDA # interp.py
2021-01-28 23:28:48 +01:00
import sco_utils as scu
import VERSION
2020-09-26 16:19:37 +02:00
from notes_log import log
import sco_find_etud
2021-01-16 14:02:18 +01:00
import sco_users
2021-01-28 23:28:48 +01:00
from sco_permissions import (
ScoView ,
ScoEnsView ,
ScoImplement ,
ScoChangeFormation ,
ScoObservateur ,
ScoEtudInscrit ,
ScoEtudChangeGroups ,
ScoEtudChangeAdr ,
ScoEtudSupprAnnotations ,
ScoEditAllEvals ,
ScoEditAllNotes ,
ScoEditFormationTags ,
ScoEditApo ,
ScoSuperAdmin ,
)
from sco_exceptions import ScoValueError , ScoLockedFormError , ScoGenError , AccessDenied
2020-09-26 16:19:37 +02:00
class ZScoDoc ( ObjectManager , PropertyManager , RoleManager , Item , Persistent , Implicit ) :
" ZScoDoc object "
meta_type = " ZScoDoc "
security = ClassSecurityInfo ( )
file_path = Globals . package_home ( globals ( ) )
# This is the list of the methods associated to 'tabs' in the ZMI
# Be aware that The first in the list is the one shown by default, so if
# the 'View' tab is the first, you will never see your tabs by cliquing
# on the object.
manage_options = (
( { " label " : " Contents " , " action " : " manage_main " } , )
+ PropertyManager . manage_options # add the 'Properties' tab
2021-01-16 10:44:02 +01:00
+ ( { " label " : " View " , " action " : " index_html " } , )
2020-09-26 16:19:37 +02:00
+ Item . manage_options # add the 'Undo' & 'Owner' tab
+ RoleManager . manage_options # add the 'Security' tab
)
def __init__ ( self , id , title ) :
" Initialise a new instance of ZScoDoc "
self . id = id
self . title = title
self . manage_addProperty ( " admin_password_initialized " , " 0 " , " string " )
security . declareProtected ( ScoView , " ScoDocURL " )
def ScoDocURL ( self ) :
" base URL for this instance (top level for ScoDoc site) "
return self . absolute_url ( )
def _check_admin_perm ( self , REQUEST ) :
2020-12-02 01:00:23 +01:00
""" Check if user has permission to add/delete departements """
2020-09-26 16:19:37 +02:00
authuser = REQUEST . AUTHENTICATED_USER
if authuser . has_role ( " manager " ) or authuser . has_permission ( ScoSuperAdmin , self ) :
return " "
else :
return """ <h2>Vous n ' avez pas le droit d ' accéder à cette page</h2> """
def _check_users_folder ( self , REQUEST = None ) :
2020-12-02 01:00:23 +01:00
""" Vérifie UserFolder et le crée s ' il le faut """
2020-09-26 16:19:37 +02:00
try :
2021-01-28 23:28:48 +01:00
_ = self . UsersDB
2020-09-26 16:19:37 +02:00
return " <!-- uf ok --> "
except :
e = self . _check_admin_perm ( REQUEST )
if not e : # admin permissions:
self . create_users_cnx ( REQUEST )
self . create_users_folder ( REQUEST )
return ' <div class= " head_message " >Création du connecteur utilisateurs réussie</div> '
else :
return """ <div class= " head_message " >Installation non terminée: connectez vous avec les droits d ' administrateur</div> """
security . declareProtected ( " View " , " create_users_folder " )
def create_users_folder ( self , REQUEST = None ) :
2020-12-02 01:00:23 +01:00
""" Create Zope user folder """
2020-09-26 16:19:37 +02:00
e = self . _check_admin_perm ( REQUEST )
if e :
return e
if REQUEST is None :
REQUEST = { }
REQUEST . form [ " pgauth_connection " ] = " UsersDB "
REQUEST . form [ " pgauth_table " ] = " sco_users "
REQUEST . form [ " pgauth_usernameColumn " ] = " user_name "
REQUEST . form [ " pgauth_passwordColumn " ] = " passwd "
REQUEST . form [ " pgauth_rolesColumn " ] = " roles "
add_method = self . manage_addProduct [ " OFSP " ] . manage_addexUserFolder
log ( " create_users_folder: in %s " % self . id )
return add_method (
authId = " pgAuthSource " ,
propId = " nullPropSource " ,
memberId = " nullMemberSource " ,
groupId = " nullGroupSource " ,
cryptoId = " MD51 " ,
# doAuth='1', doProp='1', doMember='1', doGroup='1', allDone='1',
cookie_mode = 2 ,
session_length = 500 ,
not_session_length = 0 ,
REQUEST = REQUEST ,
)
def _fix_users_folder ( self ) :
""" removes docLogin and docLogout dtml methods from exUserFolder, so that we use ours.
( called each time be index_html , to fix old ScoDoc installations . )
"""
try :
self . acl_users . manage_delObjects ( ids = [ " docLogin " , " docLogout " ] )
except :
pass
# add missing getAuthFailedMessage (bug in exUserFolder ?)
try :
2021-01-28 23:28:48 +01:00
_ = self . getAuthFailedMessage
2020-09-26 16:19:37 +02:00
except :
log ( " adding getAuthFailedMessage to Zope install " )
parent = self . aq_parent
2020-12-15 08:35:44 +01:00
from OFS . DTMLMethod import addDTMLMethod # pylint: disable=import-error
2020-09-26 16:19:37 +02:00
addDTMLMethod ( parent , " getAuthFailedMessage " , file = " Identification " )
security . declareProtected ( " View " , " create_users_cnx " )
def create_users_cnx ( self , REQUEST = None ) :
""" Create Zope connector to UsersDB
Note : la connexion est fixée ( SCOUSERS ) ( base crée par l ' installeur) !
Les utilisateurs avancés pourront la changer ensuite .
"""
# ce connecteur zope - db est encore pour l'instant utilisé par exUserFolder.pgAuthSource
# (en lecture seule en principe)
oid = " UsersDB "
log ( " create_users_cnx: in %s " % self . id )
da = ZopeDA . Connection (
oid ,
" Cnx bd utilisateurs " ,
2021-01-28 23:28:48 +01:00
scu . SCO_DEFAULT_SQL_USERS_CNX ,
2020-09-26 16:19:37 +02:00
False ,
check = 1 ,
tilevel = 2 ,
encoding = " LATIN1 " ,
)
self . _setObject ( oid , da )
security . declareProtected ( " View " , " change_admin_user " )
def change_admin_user ( self , password , REQUEST = None ) :
""" Change password of admin user """
# note: controle sur le role et non pas sur une permission
# (non definies au top level)
if not REQUEST . AUTHENTICATED_USER . has_role ( " Manager " ) :
log ( " user %s is not Manager " % REQUEST . AUTHENTICATED_USER )
log ( " roles= %s " % REQUEST . AUTHENTICATED_USER . getRolesInContext ( self ) )
raise AccessDenied ( " vous n ' avez pas le droit d ' effectuer cette opération " )
log ( " trying to change admin password " )
# 1-- check strong password
2021-01-16 14:02:18 +01:00
if not sco_users . is_valid_password ( password ) :
2020-09-26 16:19:37 +02:00
log ( " refusing weak password " )
return REQUEST . RESPONSE . redirect (
" change_admin_user_form?message=Mot %20d e % 20passe % 20trop %20s imple, %20r ecommencez "
)
# 2-- change password for admin user
username = " admin "
acl_users = self . aq_parent . acl_users
user = acl_users . getUser ( username )
r = acl_users . _changeUser (
username , password , password , user . roles , user . domains
)
if not r :
# OK, set property to indicate we changed the password
log ( " admin password changed successfully " )
self . manage_changeProperties ( admin_password_initialized = " 1 " )
return r or REQUEST . RESPONSE . redirect ( " index_html " )
security . declareProtected ( " View " , " change_admin_user_form " )
def change_admin_user_form ( self , message = " " , REQUEST = None ) :
""" Form allowing to change the ScoDoc admin password """
# note: controle sur le role et non pas sur une permission
# (non definies au top level)
if not REQUEST . AUTHENTICATED_USER . has_role ( " Manager " ) :
raise AccessDenied ( " vous n ' avez pas le droit d ' effectuer cette opération " )
H = [
self . scodoc_top_html_header (
REQUEST , page_title = " ScoDoc: changement mot de passe "
)
]
if message :
H . append ( ' <div id= " message " > %s </div> ' % message )
H . append (
""" <h2>Changement du mot de passe administrateur (utilisateur admin)</h2>
< p >
< form action = " change_admin_user " method = " post " > < table >
< tr > < td > Nouveau mot de passe : < / td > < td > < input type = " password " size = " 14 " name = " password " / > < / td > < / tr >
< tr > < td > Confirmation : < / td > < td > < input type = " password " size = " 14 " name = " password2 " / > < / td > < / tr >
< / table >
< input type = " submit " value = " Changer " >
"""
)
H . append ( """ </body></html> """ )
return " \n " . join ( H )
security . declareProtected ( " View " , " list_depts " )
def list_depts ( self , REQUEST = None ) :
""" List departments folders
( returns a list of Zope folders containing a ZScolar instance )
"""
folders = self . objectValues ( " Folder " )
# select folders with Scolarite object:
r = [ ]
for folder in folders :
try :
2020-12-15 08:35:44 +01:00
_ = folder . Scolarite
2020-09-26 16:19:37 +02:00
r . append ( folder )
except :
pass
return r
security . declareProtected ( " View " , " create_dept " )
def create_dept ( self , REQUEST = None , DeptId = " " , pass2 = False ) :
""" Creation (ajout) d ' un site departement
( instance ZScolar + dossier la contenant )
"""
e = self . _check_admin_perm ( REQUEST )
if e :
return e
if not DeptId :
raise ValueError ( " nom de departement invalide " )
if not pass2 :
# 1- Creation de repertoire Dept
2020-12-26 00:11:55 +01:00
log ( " creating Zope folder " + DeptId )
2020-09-26 16:19:37 +02:00
add_method = self . manage_addProduct [ " OFSP " ] . manage_addFolder
add_method ( DeptId , title = " Site dept. " + DeptId )
DeptFolder = self [ DeptId ]
if not pass2 :
# 2- Creation du repertoire Fotos
2020-12-26 00:11:55 +01:00
log ( " creating Zope folder %s /Fotos " % DeptId )
2020-09-26 16:19:37 +02:00
add_method = DeptFolder . manage_addProduct [ " OFSP " ] . manage_addFolder
add_method ( " Fotos " , title = " Photos identites " + DeptId )
# 3- Creation instance ScoDoc
2020-12-26 00:11:55 +01:00
log ( " creating Zope ZScolar instance " )
2020-09-26 16:19:37 +02:00
add_method = DeptFolder . manage_addProduct [ " ScoDoc " ] . manage_addZScolarForm
return add_method ( DeptId , REQUEST = REQUEST )
security . declareProtected ( " View " , " delete_dept " )
def delete_dept ( self , REQUEST = None , DeptId = " " , force = False ) :
2020-12-02 01:00:23 +01:00
""" Supprime un departement (de Zope seulement, ne touche pas la BD) """
2020-09-26 16:19:37 +02:00
e = self . _check_admin_perm ( REQUEST )
if e :
return e
if not force and DeptId not in [ x . id for x in self . list_depts ( ) ] :
raise ValueError ( " nom de departement invalide " )
self . manage_delObjects ( ids = [ DeptId ] )
return (
" <p>Département "
+ DeptId
+ """ supprimé du serveur web (la base de données n ' est pas affectée)!</p><p><a href= " %s " >Continuer</a></p> """
% REQUEST . URL1
)
_top_level_css = """
< style type = " text/css " >
< / style > """
_html_begin = """ <?xml version= " 1.0 " encoding= " %(encoding)s " ?>
< ! DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Transitional//EN " " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd " >
< html xmlns = " http://www.w3.org/1999/xhtml " >
< head >
< title > % ( page_title ) s < / title >
< meta http - equiv = " Content-Type " content = " text/html; charset= %(encoding)s " / >
< meta http - equiv = " Content-Style-Type " content = " text/css " / >
< meta name = " LANG " content = " fr " / >
< meta name = " DESCRIPTION " content = " ScoDoc " / >
< link type = " text/css " rel = " stylesheet " href = " /ScoDoc/static/libjs/jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.min.css " / >
< link href = " /ScoDoc/static/css/scodoc.css " rel = " stylesheet " type = " text/css " / >
< link href = " /ScoDoc/static/css/menu.css " rel = " stylesheet " type = " text/css " / >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/libjs/menu.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/libjs/sorttable.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/libjs/bubble.js " > < / script >
< script type = " text/javascript " >
window . onload = function ( ) { enableTooltips ( " gtrcontent " ) } ;
< / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/jQuery/jquery.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/jQuery/jquery-migrate-1.2.0.min.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/libjs/jquery.field.min.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js " > < / script >
< link type = " text/css " rel = " stylesheet " href = " /ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.css " / >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/js/scodoc.js " > < / script >
< script language = " javascript " type = " text/javascript " src = " /ScoDoc/static/js/etud_info.js " > < / script >
"""
def scodoc_top_html_header ( self , REQUEST , page_title = " ScoDoc " ) :
H = [
self . _html_begin
2021-01-28 23:28:48 +01:00
% { " page_title " : " ScoDoc: bienvenue " , " encoding " : scu . SCO_ENCODING } ,
2020-09-26 16:19:37 +02:00
self . _top_level_css ,
""" </head><body class= " gtrcontent " id= " gtrcontent " > """ ,
2021-01-28 23:28:48 +01:00
scu . CUSTOM_HTML_HEADER_CNX ,
2020-09-26 16:19:37 +02:00
]
return " \n " . join ( H )
security . declareProtected ( " View " , " index_html " )
def index_html ( self , REQUEST = None , message = None ) :
2020-12-02 01:00:23 +01:00
""" Top level page for ScoDoc """
2020-09-26 16:19:37 +02:00
authuser = REQUEST . AUTHENTICATED_USER
deptList = self . list_depts ( )
self . _fix_users_folder ( ) # fix our exUserFolder
isAdmin = not self . _check_admin_perm ( REQUEST )
try :
admin_password_initialized = self . admin_password_initialized
except :
admin_password_initialized = " 0 "
if isAdmin and admin_password_initialized != " 1 " :
REQUEST . RESPONSE . redirect (
" ScoDoc/change_admin_user_form?message=Le % 20mot %20d e % 20passe %20a dministrateur %20d oit %20e tre %20c hange % 20! "
)
# Si l'URL indique que l'on est dans un folder, affiche page login du departement
try :
deptfoldername = REQUEST . URL0 . split ( " ScoDoc " ) [ 1 ] . split ( " / " ) [ 1 ]
if deptfoldername in [ x . id for x in self . list_depts ( ) ] :
return self . index_dept ( deptfoldername = deptfoldername , REQUEST = REQUEST )
except :
pass
H = [
self . scodoc_top_html_header ( REQUEST , page_title = " ScoDoc: bienvenue " ) ,
self . _check_users_folder ( REQUEST = REQUEST ) , # ensure setup is done
]
if message :
H . append ( ' <div id= " message " > %s </div> ' % message )
if isAdmin and not message :
H . append ( ' <div id= " message " >Attention: connecté comme administrateur</div> ' )
H . append (
"""
< div class = " maindiv " >
< h2 > ScoDoc : gestion scolarité < / h2 >
"""
)
if authuser . has_role ( " Authenticated " ) :
H . append (
""" <p>Bonjour <font color= " red " ><b> %s </b></font>.</p> """ % str ( authuser )
)
H . append (
""" <p>N ' oubliez pas de vous <a href= " acl_users/logout " >déconnecter</a> après usage.</p> """
)
else :
H . append (
""" <p>Ce site est <font color= " red " ><b>réservé au personnel autorisé</b></font></p> """
)
H . append ( self . authentication_form ( destination = " . " ) )
if not deptList :
H . append ( " <em>aucun département existant !</em> " )
# si pas de dept et pas admin, propose lien pour loger admin
if not isAdmin :
H . append (
""" <p><a href= " /force_admin_authentication " >Identifiez vous comme administrateur</a> (au début: nom ' admin ' , mot de passe ' scodoc ' )</p> """
)
else :
H . append ( ' <ul class= " main " > ' )
if isAdmin :
dest_folder = " /Scolarite "
else :
dest_folder = " "
for deptFolder in self . list_depts ( ) :
if authuser . has_permission ( ScoView , deptFolder . Scolarite ) :
link_cls = " link_accessible "
else :
link_cls = " link_unauthorized "
# Essai de recuperer le nom du departement dans ses preferences
try :
DeptName = (
deptFolder . Scolarite . get_preference ( " DeptName " ) or deptFolder . id
)
except :
DeptName = deptFolder . id
H . append (
' <li><a class= " stdlink %s " href= " %s %s " >Département %s </a> '
% ( link_cls , deptFolder . absolute_url ( ) , dest_folder , DeptName )
)
# check if roles are initialized in this depts, and do it if necessary
if deptFolder . Scolarite . roles_initialized == " 0 " :
if isAdmin :
deptFolder . Scolarite . _setup_initial_roles_and_permissions ( )
else :
H . append ( " (non initialisé, connectez vous comme admin) " )
H . append ( " </li> " )
H . append ( " </ul> " )
# Recherche etudiant
H . append ( sco_find_etud . form_search_etud_in_accessible_depts ( self , REQUEST ) )
if isAdmin :
H . append ( ' <p><a href= " scodoc_admin " >Administration de ScoDoc</a></p> ' )
else :
H . append (
' <p><a href= " %s /force_admin_authentication " >Se connecter comme administrateur</a></p> '
% REQUEST . BASE0
)
H . append (
"""
< div id = " scodoc_attribution " >
< p > < a href = " %s " > ScoDoc < / a > est un logiciel libre de suivi de la scolarité des étudiants conçu par
E . Viennet ( Université Paris 13 ) . < / p >
< / div >
< / div > """
2021-01-28 23:28:48 +01:00
% ( scu . SCO_WEBSITE , )
2020-09-26 16:19:37 +02:00
)
H . append ( """ </body></html> """ )
return " \n " . join ( H )
def authentication_form ( self , destination = " " ) :
""" html snippet for authentication """
return (
""" <!-- authentication_form -->
< form action = " doLogin " method = " post " >
< input type = " hidden " name = " destination " value = " %s " / >
< p >
< table border = " 0 " cellpadding = " 3 " >
< tr >
< td > < b > Nom : < / b > < / td >
< td > < input id = " name " type = " text " name = " __ac_name " size = " 20 " / > < / td >
< / tr > < tr >
< td > < b > Mot de passe : < / b > < / td >
< td > < input id = " password " type = " password " name = " __ac_password " size = " 20 " / > < / td >
< td > < input id = " submit " name = " submit " type = " submit " value = " OK " / > < / td >
< / tr >
< / table >
< / p >
< / form > """
% destination
)
security . declareProtected ( " View " , " index_dept " )
def index_dept ( self , deptfoldername = " " , REQUEST = None ) :
""" Page d ' accueil departement """
authuser = REQUEST . AUTHENTICATED_USER
try :
dept = getattr ( self , deptfoldername )
if authuser . has_permission ( ScoView , dept ) :
return REQUEST . RESPONSE . redirect ( " ScoDoc/ %s /Scolarite " % deptfoldername )
except :
log (
" *** problem in index_dept ( %s ) user= %s "
% ( deptfoldername , str ( authuser ) )
)
H = [
self . standard_html_header ( REQUEST ) ,
""" <div style= " margin: 1em; " >
< h2 > Scolarité du département % s < / h2 > """
% deptfoldername ,
""" <p>Ce site est
< font color = " #FF0000 " > < b > réservé au personnel du département < / b > < / font > .
< / p > """ ,
self . authentication_form ( destination = " Scolarite " ) ,
"""
< p > Pour quitter , < a href = " acl_users/logout " > logout < / a > < / p >
< p > < a href = " %s " > Retour à l ' accueil</a></p>
< / div >
"""
% self . ScoDocURL ( ) ,
self . standard_html_footer ( REQUEST ) ,
]
return " \n " . join ( H )
security . declareProtected ( " View " , " doLogin " )
def doLogin ( self , REQUEST = None , destination = None ) :
" redirect to destination after login "
if destination :
return REQUEST . RESPONSE . redirect ( destination )
security . declareProtected ( " View " , " docLogin " )
docLogin = DTMLFile ( " dtml/docLogin " , globals ( ) )
security . declareProtected ( " View " , " docLogout " )
docLogout = DTMLFile ( " dtml/docLogout " , globals ( ) )
security . declareProtected ( " View " , " query_string_to_form_inputs " )
def query_string_to_form_inputs ( self , query_string = " " ) :
""" Return html snippet representing the query string as POST form hidden inputs.
This is useful in conjonction with exUserfolder to correctly redirect the response
after authentication .
"""
H = [ ]
for a in query_string . split ( " & " ) :
if a :
nv = a . split ( " = " )
if len ( nv ) == 2 :
name , value = nv
H . append (
' <input type= " hidden " name= " '
+ name
+ ' " value= " '
+ value
+ ' " /> '
)
return " <!-- query string --> \n " + " \n " . join ( H )
security . declareProtected ( " View " , " standard_html_header " )
def standard_html_header ( self , REQUEST = None ) :
""" Standard HTML header for pages outside depts """
# not used in ZScolar, see sco_header
return """ <!DOCTYPE HTML PUBLIC " -//W3C//DTD HTML 4.01//EN " " http://www.w3.org/TR/html4/strict.dtd " >
< html > < head >
< title > ScoDoc : accueil < / title >
< META http - equiv = " Content-Type " content = " text/html; charset= %s " >
< META http - equiv = " Content-Style-Type " content = " text/css " >
< META name = " LANG " content = " fr " >
< META name = " DESCRIPTION " content = " ScoDoc: gestion scolarite " >
< link HREF = " /ScoDoc/static/css/scodoc.css " rel = " stylesheet " type = " text/css " / >
< / head > < body > % s """ % (
2021-01-28 23:28:48 +01:00
scu . SCO_ENCODING ,
scu . CUSTOM_HTML_HEADER_CNX ,
2020-09-26 16:19:37 +02:00
)
security . declareProtected ( " View " , " standard_html_footer " )
def standard_html_footer ( self , REQUEST = None ) :
2021-01-16 11:49:02 +01:00
""" Le pied de page HTML de la page d ' accueil. """
2020-09-26 16:19:37 +02:00
return """ <p class= " footer " >
Problème de connexion ( identifiant , mot de passe ) : < em > contacter votre responsable ou chef de département < / em > . < / p >
< p > Probl & egrave ; mes et suggestions sur le logiciel : < a href = " mailto: %s " > % s < / a > < / p >
< p > < em > ScoDoc est un logiciel libre développé par Emmanuel Viennet . < / em > < / p >
< / body > < / html > """ % (
2021-01-28 23:28:48 +01:00
scu . SCO_USERS_LIST ,
scu . SCO_USERS_LIST ,
2020-09-26 16:19:37 +02:00
)
# sendEmail is not used through the web
def sendEmail ( self , msg ) :
# sends an email to the address using the mailhost, if there is one
try :
mail_host = self . MailHost
except :
log ( " warning: sendEmail: no MailHost found ! " )
return
# a failed notification shouldn't cause a Zope error on a site.
try :
mail_host . send ( msg . as_string ( ) )
log ( " sendEmail: ok " )
except Exception as e :
log ( " sendEmail: exception while sending message " )
log ( e )
pass
def sendEmailFromException ( self , msg ) :
# Send email by hand, as it seems to be not possible to use Zope Mail Host
# from an exception handler (see https://bugs.launchpad.net/zope2/+bug/246748)
log ( " sendEmailFromException " )
try :
p = os . popen ( " sendmail -t " , " w " ) # old brute force method
p . write ( msg . as_string ( ) )
exitcode = p . close ( )
if exitcode :
log ( " sendmail exit code: %s " % exitcode )
except :
log ( " an exception occurred sending mail " )
security . declareProtected ( " View " , " standard_error_message " )
def standard_error_message (
self ,
error_value = None ,
2021-01-29 07:01:04 +01:00
error_message = None , # unused ?
2020-09-26 16:19:37 +02:00
error_type = None ,
error_traceback = None ,
error_tb = None ,
* * kv
) :
" Recuperation des exceptions Zope "
# neat (or should I say dirty ?) hack to get REQUEST
# in fact, our caller (probably SimpleItem.py) has the REQUEST variable
# that we'd like to use for our logs, but does not pass it as an argument.
try :
frame = inspect . currentframe ( )
REQUEST = frame . f_back . f_locals [ " REQUEST " ]
except :
REQUEST = { }
# Authentication uses exceptions, pass them up
HTTP_X_FORWARDED_FOR = REQUEST . get ( " HTTP_X_FORWARDED_FOR " , " " )
if error_type == " LoginRequired " :
log ( " LoginRequired from %s " % HTTP_X_FORWARDED_FOR )
self . login_page = error_value
return error_value
elif error_type == " Unauthorized " :
log ( " Unauthorized from %s " % HTTP_X_FORWARDED_FOR )
return self . acl_users . docLogin ( self , REQUEST = REQUEST )
log ( " exception caught: %s " % error_type )
log ( traceback . format_exc ( ) )
2021-01-29 07:01:04 +01:00
params = {
" error_type " : error_type ,
" error_value " : error_value ,
" error_tb " : error_tb ,
" sco_exc_mail " : scu . SCO_EXC_MAIL ,
" sco_dev_mail " : scu . SCO_DEV_MAIL ,
}
2020-09-26 16:19:37 +02:00
if error_type == " ScoGenError " :
return " <p> " + str ( error_value ) + " </p> "
elif error_type in ( " ScoValueError " , " FormatError " ) :
# Not a bug, presents a gentle message to the user:
H = [
self . standard_html_header ( REQUEST ) ,
""" <h2>Erreur !</h2><p> %s </p> """ % error_value ,
]
if error_value . dest_url :
H . append ( ' <p><a href= " %s " >Continuer</a></p> ' % error_value . dest_url )
H . append ( self . standard_html_footer ( REQUEST ) )
return " \n " . join ( H )
else : # Other exceptions, try carefully to build an error page...
# log('exc A')
H = [ ]
try :
H . append ( self . standard_html_header ( REQUEST ) )
except :
pass
H . append (
""" <table border= " 0 " width= " 100 %% " ><tr valign= " top " >
< td width = " 10 %% " align = " center " > < / td >
< td width = " 90 %% " > < h2 > Erreur ! < / h2 >
< p > Une erreur est survenue < / p >
< p >
< strong > Error Type : % ( error_type ) s < / strong > < br >
< strong > Error Value : % ( error_value ) s < / strong > < br >
< / p >
< hr noshade >
< p > L ' URL est peut-etre incorrecte ?</p>
< p > Si l ' erreur persiste, contactez Emmanuel Viennet:
< a href = " mailto: %(sco_dev_mail)s " > % ( sco_dev_mail ) s < / a >
en copiant ce message d ' erreur et le contenu du cadre bleu ci-dessous si possible.
< / p >
< / td > < / tr >
< / table > """
2021-01-29 07:01:04 +01:00
% params
2020-09-26 16:19:37 +02:00
)
# display error traceback (? may open a security risk via xss attack ?)
2021-01-29 07:01:04 +01:00
params [ " txt_html " ] = self . _report_request ( REQUEST , fmt = " html " )
2020-09-26 16:19:37 +02:00
H . append (
""" <h4 class= " scodoc " >Zope Traceback (à envoyer par mail à <a href= " mailto: %(sco_dev_mail)s " > %(sco_dev_mail)s </a>)</h4><div style= " background-color: rgb(153,153,204); border: 1px; " >
% ( error_tb ) s
< p > < b > Informations : < / b > < br / >
% ( txt_html ) s
< / p >
< / div >
< p > Merci de votre patience ! < / p >
"""
2021-01-29 07:01:04 +01:00
% params
2020-09-26 16:19:37 +02:00
)
try :
H . append ( self . standard_html_footer ( REQUEST ) )
except :
log ( " no footer found for error page " )
pass
# --- Mail:
2021-01-29 07:01:04 +01:00
params [ " error_traceback_txt " ] = scu . scodoc_html2txt ( error_tb )
2020-09-26 16:19:37 +02:00
txt = (
"""
ErrorType : % ( error_type ) s
% ( error_traceback_txt ) s
"""
2021-01-29 07:01:04 +01:00
% params
2020-09-26 16:19:37 +02:00
)
self . send_debug_alert ( txt , REQUEST = REQUEST )
# ---
log ( " done processing exception " )
# log( '\n page=\n' + '\n'.join(H) )
return " \n " . join ( H )
def _report_request ( self , REQUEST , fmt = " txt " ) :
""" string describing current request for bug reports """
QUERY_STRING = REQUEST . get ( " QUERY_STRING " , " " )
if QUERY_STRING :
QUERY_STRING = " ? " + QUERY_STRING
if fmt == " txt " :
REFERER = REQUEST . get ( " HTTP_REFERER " , " " )
HTTP_USER_AGENT = REQUEST . get ( " HTTP_USER_AGENT " , " " )
else :
REFERER = " na "
HTTP_USER_AGENT = " na "
2021-01-29 07:01:04 +01:00
params = dict (
AUTHENTICATED_USER = REQUEST . get ( " AUTHENTICATED_USER " , " " ) ,
dt = time . asctime ( ) ,
URL = REQUEST . get ( " URL " , " " ) ,
QUERY_STRING = QUERY_STRING ,
METHOD = REQUEST . get ( " REQUEST_METHOD " , " " ) ,
REFERER = REFERER ,
HTTP_USER_AGENT = HTTP_USER_AGENT ,
form = REQUEST . get ( " form " , " " ) ,
HTTP_X_FORWARDED_FOR = REQUEST . get ( " HTTP_X_FORWARDED_FOR " , " " ) ,
svn_version = scu . get_svn_version ( self . file_path ) ,
SCOVERSION = VERSION . SCOVERSION ,
)
2020-09-26 16:19:37 +02:00
txt = (
"""
Version : % ( SCOVERSION ) s
User : % ( AUTHENTICATED_USER ) s
Date : % ( dt ) s
URL : % ( URL ) s % ( QUERY_STRING ) s
Method : % ( METHOD ) s
REFERER : % ( REFERER ) s
Form : % ( form ) s
Origin : % ( HTTP_X_FORWARDED_FOR ) s
Agent : % ( HTTP_USER_AGENT ) s
"""
2021-01-29 07:01:04 +01:00
% params
2020-09-26 16:19:37 +02:00
)
if fmt == " html " :
txt = txt . replace ( " \n " , " <br/> " )
return txt
security . declareProtected (
ScoSuperAdmin , " send_debug_alert "
) # not called through the web
def send_debug_alert ( self , txt , REQUEST = None ) :
""" Send an alert email (bug report) to ScoDoc developpers """
2021-01-28 23:28:48 +01:00
if not scu . SCO_EXC_MAIL :
2020-09-26 16:19:37 +02:00
log ( " send_debug_alert: email disabled " )
return
if REQUEST :
txt = self . _report_request ( REQUEST ) + txt
URL = REQUEST . get ( " URL " , " " )
else :
URL = " send_debug_alert "
msg = MIMEMultipart ( )
2021-01-28 23:28:48 +01:00
subj = Header ( " [scodoc] exc %s " % URL , scu . SCO_ENCODING )
2020-09-26 16:19:37 +02:00
msg [ " Subject " ] = subj
2021-01-28 23:28:48 +01:00
recipients = [ scu . SCO_EXC_MAIL ]
2020-09-26 16:19:37 +02:00
msg [ " To " ] = " , " . join ( recipients )
msg [ " From " ] = " scodoc-alert "
msg . epilogue = " "
2021-01-28 23:28:48 +01:00
msg . attach ( MIMEText ( txt , " plain " , scu . SCO_ENCODING ) )
2020-09-26 16:19:37 +02:00
self . sendEmailFromException ( msg )
log ( " Sent mail alert: \n " + txt )
security . declareProtected ( " View " , " scodoc_admin " )
def scodoc_admin ( self , REQUEST = None ) :
2020-12-02 01:00:23 +01:00
""" Page Operations d ' administration """
2020-09-26 16:19:37 +02:00
e = self . _check_admin_perm ( REQUEST )
if e :
return e
H = [
self . scodoc_top_html_header ( REQUEST , page_title = " ScoDoc: bienvenue " ) ,
"""
< h3 > Administration ScoDoc < / h3 >
< p > < a href = " change_admin_user_form " > changer le mot de passe super - administrateur < / a > < / p >
< p > < a href = " %s " > retour à la page d ' accueil</a></p>
< h4 class = " scodoc " > Création d ' un département</h4>
< p class = " help_important " > Le département doit avoir été créé au préalable sur le serveur en utilisant le script
< tt > create_dept . sh < / tt > ( à lancer comme < tt > root < / tt > dans le répertoire < tt > config < / tt > de ScoDoc ) .
< / p > """
% self . absolute_url ( ) ,
]
deptList = [ x . id for x in self . list_depts ( ) ] # definis dans Zope
2020-12-02 01:00:23 +01:00
deptIds = set ( self . _list_depts_ids ( ) ) # definis sur le filesystem
existingDepts = set ( deptList )
2020-09-26 16:19:37 +02:00
addableDepts = deptIds - existingDepts
if not addableDepts :
# aucun departement defini: aide utilisateur
H . append ( " <p>Aucun département à ajouter !</p> " )
else :
H . append ( """ <form action= " create_dept " ><select name= " DeptId " /> """ )
for deptId in addableDepts :
H . append ( """ <option value= " %s " > %s </option> """ % ( deptId , deptId ) )
H . append (
""" </select>
< input type = " submit " value = " Créer département " >
< / form > """
)
if deptList :
H . append (
"""
< h4 class = " scodoc " > Suppression d ' un département</h4>
< p > Ceci permet de supprimer le site web associé à un département , mais n ' affecte pas la base de données
( le site peut donc être recréé sans perte de données ) .
< / p >
< form action = " delete_dept " >
< select name = " DeptId " >
"""
)
for deptFolder in self . list_depts ( ) :
H . append (
' <option value= " %s " > %s </option> ' % ( deptFolder . id , deptFolder . id )
)
H . append (
""" </select>
< input type = " submit " value = " Supprimer département " >
< / form > """
)
H . append ( """ </body></html> """ )
return " \n " . join ( H )
def _list_depts_ids ( self ) :
""" Liste de id de departements definis par create_dept.sh
( fichiers depts / * . cfg )
"""
2021-01-28 23:28:48 +01:00
filenames = glob . glob ( scu . SCODOC_VAR_DIR + " /config/depts/*.cfg " )
2020-09-26 16:19:37 +02:00
ids = [ os . path . split ( os . path . splitext ( f ) [ 0 ] ) [ 1 ] for f in filenames ]
return ids
security . declareProtected ( " View " , " http_expiration_date " )
def http_expiration_date ( self ) :
" http expiration date for cachable elements (css, ...) "
d = datetime . timedelta ( minutes = 10 )
return ( datetime . datetime . utcnow ( ) + d ) . strftime ( " %a , %d % b % Y % H: % M: % S GMT " )
security . declareProtected ( " View " , " get_etud_dept " )
def get_etud_dept ( self , REQUEST = None ) :
""" Returns the dept id (eg " GEII " ) of an etud (identified by etudid, INE or NIP in REQUEST).
Warning : This function is inefficient and its result should be cached .
"""
depts = self . list_depts ( )
depts_etud = [ ] # liste des depts où l'etud est defini
for dept in depts :
etuds = dept . Scolarite . getEtudInfo ( REQUEST = REQUEST )
if etuds :
depts_etud . append ( ( dept , etuds ) )
if not depts_etud :
return " " # not found
elif len ( depts_etud ) == 1 :
return depts_etud [ 0 ] [ 0 ] . id
# inscriptions dans plusieurs departements: cherche la plus recente
last_dept = None
last_date = None
for ( dept , etuds ) in depts_etud :
dept . Scolarite . fillEtudsInfo ( etuds )
etud = etuds [ 0 ]
if etud [ " sems " ] :
if ( not last_date ) or ( etud [ " sems " ] [ 0 ] [ " date_fin_iso " ] > last_date ) :
last_date = etud [ " sems " ] [ 0 ] [ " date_fin_iso " ]
last_dept = dept
if not last_dept :
# est present dans plusieurs semestres mais inscrit dans aucun
return depts_etud [ 0 ] [ 0 ]
return last_dept . id
security . declareProtected ( " View " , " table_etud_in_accessible_depts " )
table_etud_in_accessible_depts = sco_find_etud . table_etud_in_accessible_depts
security . declareProtected ( " View " , " search_inscr_etud_by_nip " )
search_inscr_etud_by_nip = sco_find_etud . search_inscr_etud_by_nip
def manage_addZScoDoc ( self , id = " ScoDoc " , title = " Site ScoDoc " , REQUEST = None ) :
" Add a ZScoDoc instance to a folder. "
log ( " ============== creating a new ScoDoc instance ============= " )
zscodoc = ZScoDoc (
id , title
) # ne cree (presque rien), tout se passe lors du 1er accès
self . _setObject ( id , zscodoc )
if REQUEST is not None :
REQUEST . RESPONSE . redirect ( " %s /manage_workspace " % REQUEST . URL1 )
return id