From e9fee2c2926d7f05c7bd0fc231a65409132e3aee Mon Sep 17 00:00:00 2001 From: viennet Date: Sat, 26 Sep 2020 16:19:37 +0200 Subject: [PATCH] Upload from subversion 1932 --- ImportScolars.py | 781 + SuppressAccents.py | 206 + TODO | 238 + TrivialFormulator.py | 769 + VERSION.py | 349 + ZAbsences.py | 2516 ++ ZEntreprises.py | 898 + ZNotes.py | 3647 +++ ZScoDoc.py | 954 + ZScoUsers.py | 1334 + ZScolar.py | 2830 +++ ZopeProducts/README | 5 + ZopeProducts/ZPsycopgDA/DA.py | 372 + ZopeProducts/ZPsycopgDA/__init__.py | 29 + ZopeProducts/ZPsycopgDA/db.py | 209 + ZopeProducts/ZPsycopgDA/dtml/add.dtml | 108 + ZopeProducts/ZPsycopgDA/dtml/browse.dtml | 11 + ZopeProducts/ZPsycopgDA/dtml/edit.dtml | 84 + ZopeProducts/ZPsycopgDA/dtml/table_info.dtml | 7 + .../ZPsycopgDA/icons/DBAdapterFolder_icon.gif | Bin 0 -> 897 bytes ZopeProducts/ZPsycopgDA/icons/bin.gif | Bin 0 -> 924 bytes ZopeProducts/ZPsycopgDA/icons/date.gif | Bin 0 -> 930 bytes ZopeProducts/ZPsycopgDA/icons/datetime.gif | Bin 0 -> 925 bytes ZopeProducts/ZPsycopgDA/icons/field.gif | Bin 0 -> 915 bytes ZopeProducts/ZPsycopgDA/icons/float.gif | Bin 0 -> 929 bytes ZopeProducts/ZPsycopgDA/icons/int.gif | Bin 0 -> 918 bytes ZopeProducts/ZPsycopgDA/icons/stable.gif | Bin 0 -> 884 bytes ZopeProducts/ZPsycopgDA/icons/table.gif | Bin 0 -> 878 bytes ZopeProducts/ZPsycopgDA/icons/text.gif | Bin 0 -> 918 bytes ZopeProducts/ZPsycopgDA/icons/time.gif | Bin 0 -> 926 bytes ZopeProducts/ZPsycopgDA/icons/view.gif | Bin 0 -> 893 bytes ZopeProducts/ZPsycopgDA/icons/what.gif | Bin 0 -> 894 bytes ZopeProducts/ZPsycopgDA/pool.py | 193 + .../exUserFolder/AuthSources/__init__.py | 47 + .../AuthSources/pgAuthSource/.cvsignore | 4 + .../AuthSources/pgAuthSource/__init__.py | 2 + .../manage_addpgAuthSourceForm.dtml | 40 + .../manage_editpgAuthSourceForm.dtml | 37 + .../AuthSources/pgAuthSource/pgAuthSource.py | 333 + ZopeProducts/exUserFolder/CHANGES.txt | 865 + .../exUserFolder/CryptoSources/__init__.py | 4 + .../CryptoSources/fcrypt/.cvsignore | 4 + .../CryptoSources/fcrypt/ChangeLog | 35 + .../exUserFolder/CryptoSources/fcrypt/LICENSE | 77 + .../CryptoSources/fcrypt/MANIFEST.in | 1 + .../CryptoSources/fcrypt/PKG-INFO | 13 + .../exUserFolder/CryptoSources/fcrypt/README | 33 + .../CryptoSources/fcrypt/__init__.py | 1 + .../CryptoSources/fcrypt/fcrypt.py | 602 + .../CryptoSources/fcrypt/setup.py | 22 + .../exUserFolder/CryptoSources/pass_crypt.py | 44 + .../exUserFolder/CryptoSources/pass_md5.py | 47 + .../exUserFolder/CryptoSources/pass_plain.py | 31 + .../exUserFolder/CryptoSources/pass_sha.py | 41 + .../exUserFolder/Extensions/.cvsignore | 4 + .../exUserFolder/Extensions/getOldGroups.py | 20 + .../exUserFolder/Extensions/loadOldGroups.py | 26 + .../Extensions/usAuthSourceMethods.py | 140 + .../exUserFolder/GroupSource/.cvsignore | 4 + .../exUserFolder/GroupSource/GroupSource.py | 32 + .../exUserFolder/GroupSource/__init__.py | 2 + .../manage_addGroupSourceForm.dtml | 33 + .../exUserFolder/GroupSources/__init__.py | 31 + .../GroupSources/nullGroupSource/.cvsignore | 4 + .../GroupSources/nullGroupSource/__init__.py | 2 + .../manage_addNullPluginSourceForm.dtml | 21 + .../nullGroupSource/nullGroupSource.py | 34 + .../GroupSources/zodbGroupSource/.cvsignore | 4 + .../GroupSources/zodbGroupSource/__init__.py | 2 + .../manage_addzodbGroupSourceForm.dtml | 21 + .../manage_editzodbGroupSourceForm.dtml | 7 + .../zodbGroupSource/zodbGroupSource.py | 177 + .../I18N/ZBabelDictionary_txt.zexp | Bin 0 -> 38015 bytes ZopeProducts/exUserFolder/LICENSE | 91 + .../exUserFolder/LoginRequiredMessages.py | 27 + .../MembershipSources/__init__.py | 25 + .../basicMemberSource/.cvsignore | 4 + .../basicMemberSource/PasswordForm.dtml | 22 + .../basicMemberSource/SignupForm.dtml | 31 + .../basicMemberSource/__init__.py | 2 + .../basicMemberSource/basicMemberSource.py | 629 + .../forgotPasswordEmail.dtml | 15 + .../manage_addBasicMemberSourceForm.dtml | 143 + .../manage_editBasicMemberSourceForm.dtml | 116 + .../basicMemberSource/newPasswordEmail.dtml | 16 + .../basicMemberSource/passwordHintEmail.dtml | 15 + .../nullMemberSource/.cvsignore | 4 + .../nullMemberSource/__init__.py | 2 + .../manage_addNullPluginSourceForm.dtml | 21 + .../nullMemberSource/nullMemberSource.py | 49 + ZopeProducts/exUserFolder/Plugins.py | 46 + .../exUserFolder/PropSources/__init__.py | 28 + .../PropSources/nullPropSource/.cvsignore | 4 + .../PropSources/nullPropSource/__init__.py | 2 + .../manage_addNullPluginSourceForm.dtml | 21 + .../nullPropSource/nullPropSource.py | 50 + .../exUserFolder/PropertyEditor/.cvsignore | 4 + .../PropertyEditor/PropertyEditor.py | 122 + .../exUserFolder/PropertyEditor/__init__.py | 22 + ZopeProducts/exUserFolder/README.Upgrading | 14 + ZopeProducts/exUserFolder/User.py | 243 + .../exUserFolder/UserCache/.cvsignore | 4 + .../exUserFolder/UserCache/UserCache.py | 409 + .../exUserFolder/UserCache/__init__.py | 22 + ZopeProducts/exUserFolder/__init__.py | 85 + .../exUserFolder/common/DialogFooter.dtml | 4 + .../exUserFolder/common/DialogHeader.dtml | 7 + .../exUserFolder/common/MessageDialog.dtml | 41 + .../exUserFolder/common/manage_tabs.dtml | 176 + .../exUserFolder/common/text_manage_tabs.dtml | 19 + ZopeProducts/exUserFolder/doc/FAQ.txt | 119 + ZopeProducts/exUserFolder/doc/README.API | 171 + .../exUserFolder/doc/README.LDAPAuthSource | 20 + .../exUserFolder/doc/README.Membership | 144 + .../exUserFolder/doc/README.httpsAuthSource | 28 + .../doc/README.pgAuthSource-Alternate | 20 + .../exUserFolder/doc/README.radiusAuthSource | 12 + .../exUserFolder/doc/README.smbAuthSource | 41 + ZopeProducts/exUserFolder/doc/README.txt | 127 + .../exUserFolder/doc/README.zodbAuthSource | 13 + .../doc/UnenlightenedZopistasGuide.txt | 671 + ZopeProducts/exUserFolder/doc/mysql.sql | 18 + .../exUserFolder/doc/pgAndreasScheme.sql | 14 + ZopeProducts/exUserFolder/doc/pgScheme.sql | 46 + ZopeProducts/exUserFolder/dtml/docLogin.dtml | 49 + .../exUserFolder/dtml/docLoginRedirect.dtml | 26 + ZopeProducts/exUserFolder/dtml/docLogout.dtml | 16 + ZopeProducts/exUserFolder/dtml/mainUser.dtml | 55 + ZopeProducts/exUserFolder/dummyZBabelTag.py | 164 + ZopeProducts/exUserFolder/exUser.gif | Bin 0 -> 169 bytes ZopeProducts/exUserFolder/exUserFolder.gif | Bin 0 -> 169 bytes ZopeProducts/exUserFolder/exUserFolder.py | 1380 ++ .../exUserFolder/exUserFolderPlugin.gif | Bin 0 -> 402 bytes .../exUserFolder/nullPlugin/__init__.py | 2 + .../exUserFolder/nullPlugin/nullPlugin.py | 39 + ZopeProducts/exUserFolder/version.txt | 1 + __init__.py | 75 + bonus_sport.py | 422 + config/README | 56 + config/anonymize_db.py | 151 + config/config.sh | 61 + config/create_database.sh | 16 + config/create_dept.sh | 69 + config/create_user_db.sh | 28 + config/default-etapes.txt | 20 + config/delete_dept.sh | 62 + config/diagnostic.sh | 256 + config/distrib.sh | 97 + config/doc_poursuites_etudes/README.txt | 16 + .../doc_poursuites_etudes/distrib/README.txt | 11 + config/doc_poursuites_etudes/distrib/avis.tex | 29 + .../doc_poursuites_etudes/distrib/avisPE.cls | 126 + .../distrib/make_avis.bat | 3 + .../distrib/modeles/parcourstimeline.tex | 67 + .../distrib/modeles/un_avis.tex | 164 + .../distrib/un_footer.tex | 7 + config/etablissements.csv | 5317 ++++ config/etc/README | 9 + config/etc/firehol.conf | 19 + config/etc/scodoc | 75 + config/etc/scodoc-site-ssl-apache2.4.orig | 56 + config/etc/scodoc-site-ssl.orig | 52 + config/etc/scodoc-site.orig | 22 + config/etc/scodoc-updater.service | 18 + config/etc/scodoc-updater.timer | 18 + config/initialize_database.sh | 22 + config/install_cal_modules.sh | 19 + config/install_debian10.sh | 259 + config/install_debian7.sh | 256 + config/install_debian8.sh | 275 + config/install_debian9.sh | 302 + config/postupgrade-db.py | 650 + config/postupgrade.py | 60 + config/psql_restore_databases.sh | 28 + config/restore_scodoc_data.sh | 145 + config/save_scodoc_data.sh | 90 + config/scodoc_config.py | 131 + config/scodocutils.py | 176 + config/set_passwd.sh | 36 + config/softs/get-iana.sh | 214 + config/softs/pyExcelerator-0.6.3a.patched.tgz | Bin 0 -> 222066 bytes config/softs/pydot-0.9.10.tar.gz | Bin 0 -> 14462 bytes config/upgrade.sh | 139 + config/utils.sh | 57 + csv2rules.py | 117 + debug.py | 116 + dtml/docLogin.dtml | 49 + dtml/docLogout.dtml | 16 + .../entreprise_contact_create.dtml | 62 + .../entreprise_contact_delete.dtml | 27 + dtml/entreprises/entreprise_contact_edit.dtml | 62 + dtml/entreprises/entreprise_contact_list.dtml | 54 + .../entreprise_correspondant_create.dtml | 43 + .../entreprise_correspondant_delete.dtml | 27 + .../entreprise_correspondant_edit.dtml | 41 + .../entreprise_correspondant_list.dtml | 52 + dtml/entreprises/entreprise_delete.dtml | 60 + dtml/entreprises/index_html.dtml | 139 + dtml/manage_addZNotesForm.dtml | 49 + dtml/manage_addZScolarForm.dtml | 62 + dtml/manage_editZNotesForm.dtml | 21 + dtml/manage_editZScolarForm.dtml | 21 + dutrules.py | 354 + gen_tables.py | 706 + html_sco_header.py | 262 + html_sidebar.py | 185 + htmlutils.py | 72 + imageresize.py | 29 + intervals.py | 245 + listhistogram.py | 31 + logos/logo_footer.jpg | Bin 0 -> 10000 bytes logos/logo_header.jpg | Bin 0 -> 30519 bytes misc/ArreteDUT2005.txt | 88 + misc/Interface.xlsm | Bin 0 -> 146116 bytes .../Bulletins-Orleans/images/code.jpg | Bin 0 -> 1855 bytes .../Bulletins-Orleans/images/info.png | Bin 0 -> 2076 bytes .../images/puce-iut-transparent.png | Bin 0 -> 46702 bytes .../Bulletins-Orleans/images/stop.png | Bin 0 -> 1887 bytes .../Bulletins-Orleans/index.php | 310 + .../Bulletins-Orleans/style.css | 419 + .../ExemplePHP/index-abs.php | 761 + .../PublicationBulletins/ExemplePHP/index.php | 320 + .../Acces aux notes etudiants.pdf | Bin 0 -> 122618 bytes .../Portail-LeHavre/Billets_etudiants.pdf | Bin 0 -> 203646 bytes .../Portail-LeHavre/PDF_Billet.php | 151 + .../Portail-LeHavre/README.txt | 6 + .../Portail-LeHavre/css/radar_bulletin.css | 90 + .../Portail-LeHavre/css/scodoc.css | 1900 ++ .../Portail-LeHavre/deconnexion.php | 27 + .../Portail-LeHavre/font/courier.php | 7 + .../Portail-LeHavre/font/helvetica.php | 15 + .../Portail-LeHavre/font/helveticab.php | 15 + .../Portail-LeHavre/font/helveticabi.php | 15 + .../Portail-LeHavre/font/helveticai.php | 15 + .../Portail-LeHavre/font/index.html | 1 + .../Portail-LeHavre/font/makefont/cp1250.map | 251 + .../Portail-LeHavre/font/makefont/cp1251.map | 255 + .../Portail-LeHavre/font/makefont/cp1252.map | 251 + .../Portail-LeHavre/font/makefont/cp1253.map | 239 + .../Portail-LeHavre/font/makefont/cp1254.map | 249 + .../Portail-LeHavre/font/makefont/cp1255.map | 233 + .../Portail-LeHavre/font/makefont/cp1257.map | 244 + .../Portail-LeHavre/font/makefont/cp1258.map | 247 + .../Portail-LeHavre/font/makefont/cp874.map | 225 + .../Portail-LeHavre/font/makefont/index.html | 1 + .../font/makefont/iso-8859-1.map | 256 + .../font/makefont/iso-8859-11.map | 248 + .../font/makefont/iso-8859-15.map | 256 + .../font/makefont/iso-8859-16.map | 256 + .../font/makefont/iso-8859-2.map | 256 + .../font/makefont/iso-8859-4.map | 256 + .../font/makefont/iso-8859-5.map | 256 + .../font/makefont/iso-8859-7.map | 250 + .../font/makefont/iso-8859-9.map | 256 + .../Portail-LeHavre/font/makefont/koi8-r.map | 256 + .../Portail-LeHavre/font/makefont/koi8-u.map | 256 + .../font/makefont/makefont.php | 419 + .../Portail-LeHavre/font/symbol.php | 15 + .../Portail-LeHavre/font/times.php | 15 + .../Portail-LeHavre/font/timesb.php | 15 + .../Portail-LeHavre/font/timesbi.php | 15 + .../Portail-LeHavre/font/timesi.php | 15 + .../Portail-LeHavre/font/zapfdingbats.php | 15 + .../Portail-LeHavre/fpdf.php | 2105 ++ .../Portail-LeHavre/imgs/Voir_abs.png | Bin 0 -> 29035 bytes .../Portail-LeHavre/imgs/livre.png | Bin 0 -> 13722 bytes .../Portail-LeHavre/imgs/minus_img.png | Bin 0 -> 214 bytes .../Portail-LeHavre/imgs/modifier_texte.png | Bin 0 -> 17234 bytes .../Portail-LeHavre/imgs/plus_img.png | Bin 0 -> 219 bytes .../Portail-LeHavre/index.php | 885 + .../Portail-LeHavre/jQuery/ColVis.js | 1133 + .../Portail-LeHavre/jQuery/FixedColumns.js | 1226 + .../Portail-LeHavre/jQuery/TableTools.js | 2476 ++ .../jQuery/jquery-1.10.1.min.js | 2 + .../jQuery/jquery-1.3.2.min.js | 19 + .../jQuery/jquery-1.8.2.min.js | 2 + .../jQuery/jquery.dataTables.js | 12099 +++++++++ .../Portail-LeHavre/jQuery/jquery.js | 2 + .../jQuery/jquery.mobile-1.3.1.js | 11191 +++++++++ .../Portail-LeHavre/js/bulletin.js | 50 + .../Portail-LeHavre/js/radar_bulletin.js | 182 + .../Portail-LeHavre/libjs/AutoSuggest.js | 1 + .../libjs/AutoSuggest_2.1.3_comp.js | 1 + .../Portail-LeHavre/libjs/bubble.js | 100 + .../Portail-LeHavre/libjs/d3.v2.min.js | 4 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../custom-theme/jquery-ui-1.7.2.custom.css | 406 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../css/smoothness/jquery-ui-1.7.2.custom.css | 406 + .../ui-bg_diagonals-thick_18_b81900_40x40.png | Bin 0 -> 260 bytes .../ui-bg_diagonals-thick_20_666666_40x40.png | Bin 0 -> 251 bytes .../images/ui-bg_flat_10_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_100_f6f6f6_1x400.png | Bin 0 -> 104 bytes .../images/ui-bg_glass_100_fdf5ce_1x400.png | Bin 0 -> 125 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../ui-bg_gloss-wave_35_f6a828_500x100.png | Bin 0 -> 3762 bytes .../ui-bg_highlight-soft_100_eeeeee_1x100.png | Bin 0 -> 90 bytes .../ui-bg_highlight-soft_75_ffe45c_1x100.png | Bin 0 -> 129 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_228ef1_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ef8c08_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ffd27a_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4369 bytes .../ui-lightness/jquery-ui-1.7.2.custom.css | 406 + .../js/jquery-ui-1.7.2.custom.min.js | 298 + .../libjs/jquery-ui/js/jquery-ui-i18n.js | 771 + .../libjs/jquery.ui.map.full.min.js | 2 + .../Portail-LeHavre/libjs/menu.js | 63 + .../Portail-LeHavre/libjs/qtip/INSTALL | 8 + .../Portail-LeHavre/libjs/qtip/LICENSE | 7 + .../Portail-LeHavre/libjs/qtip/REQUIREMENTS | 5 + .../libjs/qtip/jquery.qtip-1.0.0-rc3.min.js | 15 + .../Portail-LeHavre/libjs/qtip/jquery.qtip.js | 15 + .../Portail-LeHavre/libjs/sorttable.js | 244 + .../Portail-LeHavre/phpToPDF.php | 1394 ++ misc/PublicationBulletins/README.txt | 33 + misc/Restore-database.txt | 69 + misc/SuppressAccents.py | 78 + misc/backup_db | 77 + misc/backup_db2 | 53 + misc/backup_rotation.sh | 77 + misc/backup_to_remote_server.sh | 95 + misc/change_enseignant.py | 57 + misc/change_etudid.py | 65 + misc/count_inscriptions.py | 43 + misc/create_user_table.sql | 27 + misc/createtables.sql | 657 + misc/example-api-1.py | 122 + misc/example-api-python2.py | 81 + misc/format_import_etudiants.txt | 55 + misc/geolocalize_lycees.py | 65 + misc/get_codes_from_names.py | 65 + misc/imp_exp.xls | Bin 0 -> 214528 bytes misc/iscid_create_formation_from_xls.py | 128 + misc/parcoursDUT.csv | 61 + misc/parcoursDUT.xls | Bin 0 -> 18432 bytes misc/reset_sem_ens.py | 40 + misc/testpydot.py | 37 + misc/zopelistmethods.py | 66 + notes_cache.py | 65 + notes_log.py | 114 + notes_table.py | 1496 ++ notes_users.py | 31 + notesdb.py | 630 + pe_avislatex.py | 549 + pe_jurype.py | 1262 + pe_semestretag.py | 504 + pe_settag.py | 329 + pe_tagtable.py | 338 + pe_tools.py | 958 + pe_view.py | 190 + safehtml.py | 13 + sco_abs_notification.py | 291 + sco_abs_views.py | 806 + sco_apogee_compare.py | 345 + sco_apogee_csv.py | 1311 + sco_archives.py | 566 + sco_archives_etud.py | 332 + sco_bac.py | 165 + sco_bulletins.py | 1264 + sco_bulletins_example.py | 72 + sco_bulletins_generator.py | 316 + sco_bulletins_json.py | 394 + sco_bulletins_legacy.py | 540 + sco_bulletins_pdf.py | 261 + sco_bulletins_signature.py | 106 + sco_bulletins_standard.py | 674 + sco_bulletins_ucac.py | 309 + sco_bulletins_xml.py | 399 + sco_cache.py | 81 + sco_codes_parcours.py | 673 + sco_compute_moy.py | 385 + sco_cost_formation.py | 197 + sco_debouche.py | 387 + sco_dept.py | 267 + sco_dump_db.py | 221 + sco_edit_formation.py | 250 + sco_edit_matiere.py | 214 + sco_edit_module.py | 501 + sco_edit_ue.py | 871 + sco_edt_cal.py | 223 + sco_etape_apogee.py | 475 + sco_etape_apogee_view.py | 848 + sco_etape_bilan.py | 768 + sco_evaluations.py | 1047 + sco_excel.py | 663 + sco_exceptions.py | 86 + sco_export_results.py | 350 + sco_find_etud.py | 428 + sco_formations.py | 316 + sco_formsemestre.py | 555 + sco_formsemestre_custommenu.py | 158 + sco_formsemestre_edit.py | 1703 ++ sco_formsemestre_exterieurs.py | 491 + sco_formsemestre_inscriptions.py | 632 + sco_formsemestre_status.py | 1041 + sco_formsemestre_validation.py | 1375 ++ sco_formulas.py | 182 + sco_groups.py | 1489 ++ sco_groups_edit.py | 105 + sco_groups_view.py | 928 + sco_import_users.py | 236 + sco_inscr_passage.py | 642 + sco_liste_notes.py | 874 + sco_lycee.py | 250 + sco_modalites.py | 114 + sco_moduleimpl_inscriptions.py | 588 + sco_moduleimpl_status.py | 580 + sco_news.py | 302 + sco_page_etud.py | 589 + sco_parcours_dut.py | 1099 + sco_pdf.py | 330 + sco_permissions.py | 74 + sco_photos.py | 360 + sco_placement.py | 795 + sco_portal_apogee.py | 552 + sco_poursuite_dut.py | 210 + sco_preferences.py | 2074 ++ sco_prepajury.py | 314 + sco_pvjury.py | 893 + sco_pvpdf.py | 806 + sco_recapcomplet.py | 826 + sco_report.py | 1494 ++ sco_saisie_notes.py | 1270 + sco_semset.py | 530 + sco_synchro_etuds.py | 831 + sco_tag_module.py | 344 + sco_trombino.py | 732 + sco_trombino_tours.py | 478 + sco_ue_external.py | 369 + sco_undo_notes.py | 255 + sco_up_to_date.py | 137 + sco_utils.py | 911 + sco_zope.py | 47 + scolars.py | 785 + scolog.py | 67 + .../css/colReorder.bootstrap.css | 11 + .../css/colReorder.bootstrap.min.css | 1 + .../css/colReorder.dataTables.css | 11 + .../css/colReorder.dataTables.min.css | 1 + .../css/colReorder.foundation.css | 11 + .../css/colReorder.foundation.min.css | 1 + .../css/colReorder.jqueryui.css | 11 + .../css/colReorder.jqueryui.min.css | 1 + .../js/dataTables.colReorder.js | 1353 ++ .../js/dataTables.colReorder.min.js | 27 + .../css/dataTables.bootstrap.css | 184 + .../css/dataTables.bootstrap.min.css | 1 + .../css/dataTables.foundation.css | 118 + .../css/dataTables.foundation.min.css | 1 + .../css/dataTables.jqueryui.css | 482 + .../css/dataTables.jqueryui.min.css | 1 + .../css/dataTables.semanticui.css | 102 + .../css/dataTables.semanticui.min.css | 1 + .../css/jquery.dataTables.css | 455 + .../css/jquery.dataTables.min.css | 1 + .../css/jquery.dataTables_themeroller.css | 416 + .../DataTables-1.10.15/images/sort_asc.png | Bin 0 -> 160 bytes .../images/sort_asc_disabled.png | Bin 0 -> 148 bytes .../DataTables-1.10.15/images/sort_both.png | Bin 0 -> 201 bytes .../DataTables-1.10.15/images/sort_desc.png | Bin 0 -> 158 bytes .../images/sort_desc_disabled.png | Bin 0 -> 146 bytes .../js/dataTables.bootstrap.js | 182 + .../js/dataTables.bootstrap.min.js | 8 + .../js/dataTables.foundation.js | 174 + .../js/dataTables.foundation.min.js | 8 + .../js/dataTables.jqueryui.js | 164 + .../js/dataTables.jqueryui.min.js | 9 + .../js/dataTables.semanticui.js | 208 + .../js/dataTables.semanticui.min.js | 9 + .../js/jquery.dataTables.js | 15345 ++++++++++++ .../js/jquery.dataTables.min.js | 167 + .../css/fixedColumns.bootstrap.css | 44 + .../css/fixedColumns.bootstrap.min.css | 1 + .../css/fixedColumns.dataTables.css | 18 + .../css/fixedColumns.dataTables.min.css | 1 + .../css/fixedColumns.foundation.css | 27 + .../css/fixedColumns.foundation.min.css | 1 + .../css/fixedColumns.jqueryui.css | 8 + .../css/fixedColumns.jqueryui.min.css | 1 + .../js/dataTables.fixedColumns.js | 1623 ++ .../js/dataTables.fixedColumns.min.js | 35 + .../css/fixedHeader.bootstrap.css | 20 + .../css/fixedHeader.bootstrap.min.css | 1 + .../css/fixedHeader.dataTables.css | 19 + .../css/fixedHeader.dataTables.min.css | 1 + .../css/fixedHeader.foundation.css | 20 + .../css/fixedHeader.foundation.min.css | 1 + .../css/fixedHeader.jqueryui.css | 15 + .../css/fixedHeader.jqueryui.min.css | 1 + .../js/dataTables.fixedHeader.js | 672 + .../js/dataTables.fixedHeader.min.js | 17 + .../css/responsive.bootstrap.css | 181 + .../css/responsive.bootstrap.min.css | 1 + .../css/responsive.dataTables.css | 178 + .../css/responsive.dataTables.min.css | 1 + .../css/responsive.foundation.css | 181 + .../css/responsive.foundation.min.css | 1 + .../css/responsive.jqueryui.css | 178 + .../css/responsive.jqueryui.min.css | 1 + .../js/dataTables.responsive.js | 1255 + .../js/dataTables.responsive.min.js | 26 + .../js/responsive.bootstrap.js | 85 + .../js/responsive.bootstrap.min.js | 6 + .../js/responsive.foundation.js | 62 + .../js/responsive.foundation.min.js | 6 + .../js/responsive.jqueryui.js | 63 + .../js/responsive.jqueryui.min.js | 6 + static/DataTables/datatables.css | 703 + static/DataTables/datatables.js | 20270 ++++++++++++++++ static/DataTables/datatables.min.css | 27 + static/DataTables/datatables.min.js | 294 + static/README | 1 + static/css/autosuggest_inquisitor.css | 177 + static/css/calabs.css | 13 + static/css/groups.css | 91 + static/css/gt_table.css | 417 + static/css/menu.css | 153 + static/css/multiple-select.css | 170 + static/css/multiple-select.png | 600 + static/css/radar_bulletin.css | 90 + static/css/rickshaw.min.css | 1 + static/css/scodoc.css | 2895 +++ static/css/verticalhisto.css | 88 + static/icons/arrow_down.png | Bin 0 -> 410 bytes static/icons/arrow_none.png | Bin 0 -> 427 bytes static/icons/arrow_up.png | Bin 0 -> 376 bytes static/icons/as_pointer.png | Bin 0 -> 279 bytes static/icons/borgne_img.png | Bin 0 -> 14082 bytes static/icons/bt_gif.png | Bin 0 -> 1323 bytes static/icons/bullet_arrow.png | Bin 0 -> 162 bytes static/icons/bullet_plus.png | Bin 0 -> 2999 bytes static/icons/bullet_warning_img.png | Bin 0 -> 888 bytes static/icons/calendar_img.png | Bin 0 -> 605 bytes static/icons/contact_img.png | Bin 0 -> 1775 bytes static/icons/correspondant_img.png | Bin 0 -> 618 bytes static/icons/delete_img.png | Bin 0 -> 963 bytes static/icons/delete_small_dis_img.png | Bin 0 -> 503 bytes static/icons/delete_small_img.png | Bin 0 -> 444 bytes static/icons/devel_img.png | Bin 0 -> 3586 bytes static/icons/edit_img.png | Bin 0 -> 691 bytes static/icons/emptygroupicon_img.png | Bin 0 -> 536 bytes static/icons/entreprise_img.png | Bin 0 -> 4617 bytes static/icons/entreprise_side_img.png | Bin 0 -> 4112 bytes static/icons/eye_img.png | Bin 0 -> 673 bytes static/icons/firefox_fr.png | Bin 0 -> 3575 bytes static/icons/flag_uk_img.png | Bin 0 -> 284 bytes static/icons/formula.png | Bin 0 -> 455 bytes static/icons/groupicon_img.png | Bin 0 -> 1059 bytes static/icons/hide_img.png | Bin 0 -> 522 bytes static/icons/hl_corner_bl.png | Bin 0 -> 290 bytes static/icons/hl_corner_br.png | Bin 0 -> 293 bytes static/icons/hl_corner_tl.png | Bin 0 -> 293 bytes static/icons/hl_corner_tr.png | Bin 0 -> 290 bytes static/icons/info-16_img.png | Bin 0 -> 426 bytes static/icons/info-24_img.png | Bin 0 -> 699 bytes static/icons/interdit_img.png | Bin 0 -> 2684 bytes static/icons/loading.jpg | Bin 0 -> 3016 bytes static/icons/lock32_img.png | Bin 0 -> 822 bytes static/icons/lock_img.png | Bin 0 -> 557 bytes static/icons/menuarrow1_img.png | Bin 0 -> 270 bytes static/icons/minus18_img.png | Bin 0 -> 231 bytes static/icons/minus_img.png | Bin 0 -> 214 bytes static/icons/next_img.png | Bin 0 -> 1040 bytes static/icons/notes_icon.png | Bin 0 -> 245 bytes static/icons/notes_img.png | Bin 0 -> 927 bytes static/icons/nouv_img.png | Bin 0 -> 1296 bytes static/icons/pdficon16x20_img.png | Bin 0 -> 327 bytes static/icons/pdficon_img.png | Bin 0 -> 329 bytes static/icons/plus18_img.png | Bin 0 -> 247 bytes static/icons/plus_img.png | Bin 0 -> 219 bytes static/icons/prev_img.png | Bin 0 -> 1040 bytes static/icons/pythonpoweredsmall_img.png | Bin 0 -> 494 bytes static/icons/roundedge_img.png | Bin 0 -> 324 bytes static/icons/rssscodoc_img.png | Bin 0 -> 689 bytes static/icons/sco_icon.png | Bin 0 -> 380 bytes static/icons/scologo_img.png | Bin 0 -> 28219 bytes static/icons/smallpolar_img.png | Bin 0 -> 9638 bytes static/icons/status_green_img.png | Bin 0 -> 641 bytes static/icons/status_greenorange_img.gif | Bin 0 -> 1275 bytes static/icons/status_orange_img.png | Bin 0 -> 698 bytes static/icons/status_visible_img.png | Bin 0 -> 467 bytes static/icons/table_brushed_jpg.png | Bin 0 -> 3035 bytes static/icons/takebacktheweb_small.png | Bin 0 -> 3082 bytes static/icons/trust_firefox_gif.png | Bin 0 -> 2161 bytes static/icons/ul_corner_bl.png | Bin 0 -> 218 bytes static/icons/ul_corner_br.png | Bin 0 -> 219 bytes static/icons/ul_corner_tl.png | Bin 0 -> 220 bytes static/icons/ul_corner_tr.png | Bin 0 -> 222 bytes static/icons/unknown.jpg | Bin 0 -> 10467 bytes static/icons/unknown_img.png | Bin 0 -> 20405 bytes static/icons/warning16_img.png | Bin 0 -> 655 bytes static/icons/warning21_img.png | Bin 0 -> 853 bytes static/icons/warning_img.png | Bin 0 -> 1968 bytes static/icons/xlsicon_img.png | Bin 0 -> 2861 bytes static/index.html | 0 static/jQuery/jquery-1.12.4.min.js | 5 + static/jQuery/jquery-migrate-1.2.0.min.js | 2 + static/jQuery/jquery.js | 1 + static/js/abs_ajax.js | 44 + static/js/apo_semset_maq_status.js | 90 + static/js/apo_semset_maq_status.js.rej | 154 + static/js/bulletin.js | 50 + static/js/calabs.js | 64 + static/js/editPartitionForm.js | 37 + static/js/edit_ue.js | 16 + static/js/etud_debouche.js | 138 + static/js/etud_info.js | 61 + static/js/export_results.js | 14 + .../formsemestre_ext_edit_ue_validations.js | 67 + static/js/groupmgr.js | 446 + static/js/groups_view.js | 179 + static/js/map_lycees.js | 34 + static/js/module_tag_editor.js | 35 + static/js/radar_bulletin.js | 278 + static/js/recap_parcours.js | 59 + static/js/saisie_notes.js | 113 + static/js/sco_ue_external.js | 30 + static/js/scodoc.js | 120 + static/js/trombino.js | 11 + static/js/ue_list.js | 5 + static/js/validate_previous_ue.js | 31 + static/libjs/AutoSuggest.js | 1 + static/libjs/AutoSuggest_2.1.3_comp.js | 1 + static/libjs/bootstrap-3.1.1-dist/._css | Bin 0 -> 4096 bytes static/libjs/bootstrap-3.1.1-dist/._fonts | Bin 0 -> 4096 bytes static/libjs/bootstrap-3.1.1-dist/._js | Bin 0 -> 4096 bytes .../css/._bootstrap-theme.css | Bin 0 -> 4096 bytes .../css/._bootstrap-theme.css.map | Bin 0 -> 4096 bytes .../css/._bootstrap-theme.min.css | Bin 0 -> 4096 bytes .../bootstrap-3.1.1-dist/css/._bootstrap.css | Bin 0 -> 4096 bytes .../css/._bootstrap.css.map | Bin 0 -> 4096 bytes .../css/._bootstrap.min.css | Bin 0 -> 4096 bytes .../css/bootstrap-theme.css | 347 + .../css/bootstrap-theme.css.map | 1 + .../css/bootstrap-theme.min.css | 7 + .../bootstrap-3.1.1-dist/css/bootstrap.css | 5785 +++++ .../css/bootstrap.css.map | 1 + .../css/bootstrap.min.css | 7 + .../fonts/._glyphicons-halflings-regular.eot | Bin 0 -> 4096 bytes .../fonts/._glyphicons-halflings-regular.svg | Bin 0 -> 4096 bytes .../fonts/._glyphicons-halflings-regular.ttf | Bin 0 -> 4096 bytes .../fonts/._glyphicons-halflings-regular.woff | Bin 0 -> 4096 bytes .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20335 bytes .../fonts/glyphicons-halflings-regular.svg | 229 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 41280 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23320 bytes .../bootstrap-3.1.1-dist/js/._bootstrap.js | Bin 0 -> 4096 bytes .../js/._bootstrap.min.js | Bin 0 -> 4096 bytes .../bootstrap-3.1.1-dist/js/bootstrap.js | 1951 ++ .../bootstrap-3.1.1-dist/js/bootstrap.min.js | 6 + .../bootstrap-multiselect.css | 1 + .../bootstrap-multiselect.js | 899 + static/libjs/bubble.js | 100 + static/libjs/d3.v3.min.js | 5 + .../fullcalendar/THIS_IS_VERSION_2.0.0-beta2 | 0 static/libjs/fullcalendar/fullcalendar.css | 601 + static/libjs/fullcalendar/fullcalendar.min.js | 7 + .../libjs/fullcalendar/fullcalendar.print.css | 32 + static/libjs/jQuery-tagEditor/bower.json | 33 + static/libjs/jQuery-tagEditor/demo.html | 461 + .../jQuery-tagEditor/jquery.caret.min.js | 2 + .../jQuery-tagEditor/jquery.tag-editor.css | 45 + .../jQuery-tagEditor/jquery.tag-editor.js | 370 + .../jQuery-tagEditor/jquery.tag-editor.min.js | 3 + static/libjs/jQuery-tagEditor/readme.md | 119 + .../jQuery-tagEditor/tag-editor.jquery.json | 31 + static/libjs/jinplace-1.2.1.min.js | 12 + .../smoothness/images/animated-overlay.gif | Bin 0 -> 1738 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 212 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 208 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 335 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 207 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 280 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 6922 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 6992 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 6999 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4549 bytes .../smoothness/jquery-ui-1.10.4.custom.css | 1178 + .../jquery-ui-1.10.4.custom.min.css | 7 + .../js/jquery-ui-1.10.4.custom.min.js | 7 + static/libjs/jquery.field.min.js | 12 + static/libjs/jquery.ui.map.full.min.js | 2 + static/libjs/menu.js | 63 + static/libjs/moment.min.js | 6 + static/libjs/purl.js | 267 + static/libjs/qtip/LICENSE | 7 + static/libjs/qtip/jquery.qtip-3.0.3.min.css | 1 + static/libjs/qtip/jquery.qtip-3.0.3.min.js | 5 + static/libjs/rickshaw.min.js | 3 + static/libjs/sorttable.js | 244 + static/robots.txt | 2 + test_jurype.py | 168 + tests/conn_info.py | 9 + tests/demo/demo_reset_noms.py | 58 + tests/demo/noms.txt | 1051 + tests/demo/prenoms-f.txt | 32 + tests/demo/prenoms-h.txt | 32 + tests/demo/prenoms.txt | 61 + tests/splinter/README.md | 38 + tests/splinter/common.py | 49 + tests/splinter/conn_info.py | 10 + tests/splinter/test-0.py | 51 + tests/splinter/test-intensive-changes.py | 66 + tests/splinter/test-jury.py | 44 + tests/test-all-moys.py | 45 + tests/test-apo-csv.py | 64 + tests/test-apo-export-impairs.py | 46 + tests/testpyexcelerator.py | 19 + 737 files changed, 204693 insertions(+) create mode 100644 ImportScolars.py create mode 100644 SuppressAccents.py create mode 100644 TODO create mode 100644 TrivialFormulator.py create mode 100644 VERSION.py create mode 100644 ZAbsences.py create mode 100644 ZEntreprises.py create mode 100644 ZNotes.py create mode 100644 ZScoDoc.py create mode 100644 ZScoUsers.py create mode 100644 ZScolar.py create mode 100644 ZopeProducts/README create mode 100644 ZopeProducts/ZPsycopgDA/DA.py create mode 100644 ZopeProducts/ZPsycopgDA/__init__.py create mode 100644 ZopeProducts/ZPsycopgDA/db.py create mode 100644 ZopeProducts/ZPsycopgDA/dtml/add.dtml create mode 100644 ZopeProducts/ZPsycopgDA/dtml/browse.dtml create mode 100644 ZopeProducts/ZPsycopgDA/dtml/edit.dtml create mode 100644 ZopeProducts/ZPsycopgDA/dtml/table_info.dtml create mode 100755 ZopeProducts/ZPsycopgDA/icons/DBAdapterFolder_icon.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/bin.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/date.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/datetime.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/field.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/float.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/int.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/stable.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/table.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/text.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/time.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/view.gif create mode 100644 ZopeProducts/ZPsycopgDA/icons/what.gif create mode 100644 ZopeProducts/ZPsycopgDA/pool.py create mode 100644 ZopeProducts/exUserFolder/AuthSources/__init__.py create mode 100644 ZopeProducts/exUserFolder/AuthSources/pgAuthSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/AuthSources/pgAuthSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_addpgAuthSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_editpgAuthSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/AuthSources/pgAuthSource/pgAuthSource.py create mode 100644 ZopeProducts/exUserFolder/CHANGES.txt create mode 100644 ZopeProducts/exUserFolder/CryptoSources/__init__.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/.cvsignore create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/ChangeLog create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/LICENSE create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/MANIFEST.in create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/PKG-INFO create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/README create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/__init__.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/fcrypt.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/fcrypt/setup.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/pass_crypt.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/pass_md5.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/pass_plain.py create mode 100644 ZopeProducts/exUserFolder/CryptoSources/pass_sha.py create mode 100644 ZopeProducts/exUserFolder/Extensions/.cvsignore create mode 100644 ZopeProducts/exUserFolder/Extensions/getOldGroups.py create mode 100644 ZopeProducts/exUserFolder/Extensions/loadOldGroups.py create mode 100644 ZopeProducts/exUserFolder/Extensions/usAuthSourceMethods.py create mode 100644 ZopeProducts/exUserFolder/GroupSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/GroupSource/GroupSource.py create mode 100644 ZopeProducts/exUserFolder/GroupSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/GroupSource/manage_addGroupSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/GroupSources/__init__.py create mode 100644 ZopeProducts/exUserFolder/GroupSources/nullGroupSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/GroupSources/nullGroupSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/GroupSources/nullGroupSource/manage_addNullPluginSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/GroupSources/nullGroupSource/nullGroupSource.py create mode 100644 ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_addzodbGroupSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_editzodbGroupSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/zodbGroupSource.py create mode 100644 ZopeProducts/exUserFolder/I18N/ZBabelDictionary_txt.zexp create mode 100644 ZopeProducts/exUserFolder/LICENSE create mode 100644 ZopeProducts/exUserFolder/LoginRequiredMessages.py create mode 100644 ZopeProducts/exUserFolder/MembershipSources/__init__.py create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/PasswordForm.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/SignupForm.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/basicMemberSource.py create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/forgotPasswordEmail.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_addBasicMemberSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_editBasicMemberSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/newPasswordEmail.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/passwordHintEmail.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/manage_addNullPluginSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/nullMemberSource.py create mode 100644 ZopeProducts/exUserFolder/Plugins.py create mode 100644 ZopeProducts/exUserFolder/PropSources/__init__.py create mode 100644 ZopeProducts/exUserFolder/PropSources/nullPropSource/.cvsignore create mode 100644 ZopeProducts/exUserFolder/PropSources/nullPropSource/__init__.py create mode 100644 ZopeProducts/exUserFolder/PropSources/nullPropSource/manage_addNullPluginSourceForm.dtml create mode 100644 ZopeProducts/exUserFolder/PropSources/nullPropSource/nullPropSource.py create mode 100644 ZopeProducts/exUserFolder/PropertyEditor/.cvsignore create mode 100644 ZopeProducts/exUserFolder/PropertyEditor/PropertyEditor.py create mode 100644 ZopeProducts/exUserFolder/PropertyEditor/__init__.py create mode 100644 ZopeProducts/exUserFolder/README.Upgrading create mode 100644 ZopeProducts/exUserFolder/User.py create mode 100644 ZopeProducts/exUserFolder/UserCache/.cvsignore create mode 100644 ZopeProducts/exUserFolder/UserCache/UserCache.py create mode 100644 ZopeProducts/exUserFolder/UserCache/__init__.py create mode 100644 ZopeProducts/exUserFolder/__init__.py create mode 100644 ZopeProducts/exUserFolder/common/DialogFooter.dtml create mode 100644 ZopeProducts/exUserFolder/common/DialogHeader.dtml create mode 100644 ZopeProducts/exUserFolder/common/MessageDialog.dtml create mode 100644 ZopeProducts/exUserFolder/common/manage_tabs.dtml create mode 100644 ZopeProducts/exUserFolder/common/text_manage_tabs.dtml create mode 100644 ZopeProducts/exUserFolder/doc/FAQ.txt create mode 100644 ZopeProducts/exUserFolder/doc/README.API create mode 100644 ZopeProducts/exUserFolder/doc/README.LDAPAuthSource create mode 100644 ZopeProducts/exUserFolder/doc/README.Membership create mode 100644 ZopeProducts/exUserFolder/doc/README.httpsAuthSource create mode 100644 ZopeProducts/exUserFolder/doc/README.pgAuthSource-Alternate create mode 100644 ZopeProducts/exUserFolder/doc/README.radiusAuthSource create mode 100644 ZopeProducts/exUserFolder/doc/README.smbAuthSource create mode 100644 ZopeProducts/exUserFolder/doc/README.txt create mode 100644 ZopeProducts/exUserFolder/doc/README.zodbAuthSource create mode 100644 ZopeProducts/exUserFolder/doc/UnenlightenedZopistasGuide.txt create mode 100644 ZopeProducts/exUserFolder/doc/mysql.sql create mode 100644 ZopeProducts/exUserFolder/doc/pgAndreasScheme.sql create mode 100644 ZopeProducts/exUserFolder/doc/pgScheme.sql create mode 100644 ZopeProducts/exUserFolder/dtml/docLogin.dtml create mode 100644 ZopeProducts/exUserFolder/dtml/docLoginRedirect.dtml create mode 100644 ZopeProducts/exUserFolder/dtml/docLogout.dtml create mode 100644 ZopeProducts/exUserFolder/dtml/mainUser.dtml create mode 100644 ZopeProducts/exUserFolder/dummyZBabelTag.py create mode 100644 ZopeProducts/exUserFolder/exUser.gif create mode 100644 ZopeProducts/exUserFolder/exUserFolder.gif create mode 100644 ZopeProducts/exUserFolder/exUserFolder.py create mode 100644 ZopeProducts/exUserFolder/exUserFolderPlugin.gif create mode 100644 ZopeProducts/exUserFolder/nullPlugin/__init__.py create mode 100644 ZopeProducts/exUserFolder/nullPlugin/nullPlugin.py create mode 100644 ZopeProducts/exUserFolder/version.txt create mode 100644 __init__.py create mode 100644 bonus_sport.py create mode 100644 config/README create mode 100755 config/anonymize_db.py create mode 100644 config/config.sh create mode 100755 config/create_database.sh create mode 100755 config/create_dept.sh create mode 100755 config/create_user_db.sh create mode 100644 config/default-etapes.txt create mode 100755 config/delete_dept.sh create mode 100755 config/diagnostic.sh create mode 100644 config/distrib.sh create mode 100644 config/doc_poursuites_etudes/README.txt create mode 100644 config/doc_poursuites_etudes/distrib/README.txt create mode 100755 config/doc_poursuites_etudes/distrib/avis.tex create mode 100755 config/doc_poursuites_etudes/distrib/avisPE.cls create mode 100755 config/doc_poursuites_etudes/distrib/make_avis.bat create mode 100755 config/doc_poursuites_etudes/distrib/modeles/parcourstimeline.tex create mode 100644 config/doc_poursuites_etudes/distrib/modeles/un_avis.tex create mode 100644 config/doc_poursuites_etudes/distrib/un_footer.tex create mode 100644 config/etablissements.csv create mode 100644 config/etc/README create mode 100644 config/etc/firehol.conf create mode 100755 config/etc/scodoc create mode 100644 config/etc/scodoc-site-ssl-apache2.4.orig create mode 100644 config/etc/scodoc-site-ssl.orig create mode 100644 config/etc/scodoc-site.orig create mode 100644 config/etc/scodoc-updater.service create mode 100644 config/etc/scodoc-updater.timer create mode 100755 config/initialize_database.sh create mode 100755 config/install_cal_modules.sh create mode 100755 config/install_debian10.sh create mode 100755 config/install_debian7.sh create mode 100755 config/install_debian8.sh create mode 100755 config/install_debian9.sh create mode 100755 config/postupgrade-db.py create mode 100755 config/postupgrade.py create mode 100755 config/psql_restore_databases.sh create mode 100755 config/restore_scodoc_data.sh create mode 100755 config/save_scodoc_data.sh create mode 100644 config/scodoc_config.py create mode 100644 config/scodocutils.py create mode 100755 config/set_passwd.sh create mode 100755 config/softs/get-iana.sh create mode 100644 config/softs/pyExcelerator-0.6.3a.patched.tgz create mode 100644 config/softs/pydot-0.9.10.tar.gz create mode 100755 config/upgrade.sh create mode 100644 config/utils.sh create mode 100755 csv2rules.py create mode 100644 debug.py create mode 100644 dtml/docLogin.dtml create mode 100644 dtml/docLogout.dtml create mode 100644 dtml/entreprises/entreprise_contact_create.dtml create mode 100644 dtml/entreprises/entreprise_contact_delete.dtml create mode 100644 dtml/entreprises/entreprise_contact_edit.dtml create mode 100644 dtml/entreprises/entreprise_contact_list.dtml create mode 100644 dtml/entreprises/entreprise_correspondant_create.dtml create mode 100644 dtml/entreprises/entreprise_correspondant_delete.dtml create mode 100644 dtml/entreprises/entreprise_correspondant_edit.dtml create mode 100644 dtml/entreprises/entreprise_correspondant_list.dtml create mode 100644 dtml/entreprises/entreprise_delete.dtml create mode 100644 dtml/entreprises/index_html.dtml create mode 100644 dtml/manage_addZNotesForm.dtml create mode 100644 dtml/manage_addZScolarForm.dtml create mode 100644 dtml/manage_editZNotesForm.dtml create mode 100644 dtml/manage_editZScolarForm.dtml create mode 100644 dutrules.py create mode 100644 gen_tables.py create mode 100644 html_sco_header.py create mode 100644 html_sidebar.py create mode 100644 htmlutils.py create mode 100644 imageresize.py create mode 100644 intervals.py create mode 100644 listhistogram.py create mode 100644 logos/logo_footer.jpg create mode 100644 logos/logo_header.jpg create mode 100644 misc/ArreteDUT2005.txt create mode 100644 misc/Interface.xlsm create mode 100644 misc/PublicationBulletins/Bulletins-Orleans/images/code.jpg create mode 100644 misc/PublicationBulletins/Bulletins-Orleans/images/info.png create mode 100644 misc/PublicationBulletins/Bulletins-Orleans/images/puce-iut-transparent.png create mode 100644 misc/PublicationBulletins/Bulletins-Orleans/images/stop.png create mode 100644 misc/PublicationBulletins/Bulletins-Orleans/index.php create mode 100644 misc/PublicationBulletins/Bulletins-Orleans/style.css create mode 100644 misc/PublicationBulletins/ExemplePHP/index-abs.php create mode 100644 misc/PublicationBulletins/ExemplePHP/index.php create mode 100644 misc/PublicationBulletins/Portail-LeHavre/Acces aux notes etudiants.pdf create mode 100644 misc/PublicationBulletins/Portail-LeHavre/Billets_etudiants.pdf create mode 100755 misc/PublicationBulletins/Portail-LeHavre/PDF_Billet.php create mode 100644 misc/PublicationBulletins/Portail-LeHavre/README.txt create mode 100644 misc/PublicationBulletins/Portail-LeHavre/css/radar_bulletin.css create mode 100644 misc/PublicationBulletins/Portail-LeHavre/css/scodoc.css create mode 100755 misc/PublicationBulletins/Portail-LeHavre/deconnexion.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/courier.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/helvetica.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/helveticab.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/helveticabi.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/helveticai.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/index.html create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1250.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1251.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1252.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1253.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1254.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1255.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1257.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1258.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp874.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/index.html create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-1.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-11.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-15.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-16.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-2.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-4.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-5.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-7.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-9.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-r.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-u.map create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/makefont/makefont.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/symbol.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/times.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/timesb.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/timesbi.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/timesi.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/font/zapfdingbats.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/fpdf.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/imgs/Voir_abs.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/imgs/livre.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/imgs/minus_img.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/imgs/modifier_texte.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/imgs/plus_img.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/index.php create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/ColVis.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/FixedColumns.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/TableTools.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.10.1.min.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.3.2.min.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.8.2.min.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.dataTables.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.mobile-1.3.1.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/js/bulletin.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/js/radar_bulletin.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/AutoSuggest.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/AutoSuggest_2.1.3_comp.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/bubble.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/d3.v2.min.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_flat_75_ffffff_40x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_glass_65_ffffff_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_glass_75_dadada_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-icons_222222_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-icons_2e83ff_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-icons_454545_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-icons_888888_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/images/ui-icons_cd0a0a_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/custom-theme/jquery-ui-1.7.2.custom.css create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-icons_222222_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-icons_454545_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-icons_888888_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/smoothness/jquery-ui-1.7.2.custom.css create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-icons_222222_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-icons_228ef1_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-icons_ef8c08_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-icons_ffd27a_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/images/ui-icons_ffffff_256x240.png create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/js/jquery-ui-1.7.2.custom.min.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/js/jquery-ui-i18n.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/jquery.ui.map.full.min.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/menu.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/INSTALL create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/LICENSE create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/REQUIREMENTS create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/jquery.qtip-1.0.0-rc3.min.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/jquery.qtip.js create mode 100644 misc/PublicationBulletins/Portail-LeHavre/libjs/sorttable.js create mode 100755 misc/PublicationBulletins/Portail-LeHavre/phpToPDF.php create mode 100644 misc/PublicationBulletins/README.txt create mode 100644 misc/Restore-database.txt create mode 100644 misc/SuppressAccents.py create mode 100755 misc/backup_db create mode 100755 misc/backup_db2 create mode 100755 misc/backup_rotation.sh create mode 100644 misc/backup_to_remote_server.sh create mode 100755 misc/change_enseignant.py create mode 100644 misc/change_etudid.py create mode 100644 misc/count_inscriptions.py create mode 100644 misc/create_user_table.sql create mode 100644 misc/createtables.sql create mode 100644 misc/example-api-1.py create mode 100644 misc/example-api-python2.py create mode 100644 misc/format_import_etudiants.txt create mode 100644 misc/geolocalize_lycees.py create mode 100644 misc/get_codes_from_names.py create mode 100644 misc/imp_exp.xls create mode 100644 misc/iscid_create_formation_from_xls.py create mode 100644 misc/parcoursDUT.csv create mode 100644 misc/parcoursDUT.xls create mode 100755 misc/reset_sem_ens.py create mode 100644 misc/testpydot.py create mode 100644 misc/zopelistmethods.py create mode 100644 notes_cache.py create mode 100644 notes_log.py create mode 100644 notes_table.py create mode 100644 notes_users.py create mode 100644 notesdb.py create mode 100644 pe_avislatex.py create mode 100644 pe_jurype.py create mode 100644 pe_semestretag.py create mode 100644 pe_settag.py create mode 100644 pe_tagtable.py create mode 100644 pe_tools.py create mode 100644 pe_view.py create mode 100644 safehtml.py create mode 100644 sco_abs_notification.py create mode 100644 sco_abs_views.py create mode 100644 sco_apogee_compare.py create mode 100644 sco_apogee_csv.py create mode 100644 sco_archives.py create mode 100644 sco_archives_etud.py create mode 100644 sco_bac.py create mode 100644 sco_bulletins.py create mode 100644 sco_bulletins_example.py create mode 100644 sco_bulletins_generator.py create mode 100644 sco_bulletins_json.py create mode 100644 sco_bulletins_legacy.py create mode 100644 sco_bulletins_pdf.py create mode 100644 sco_bulletins_signature.py create mode 100644 sco_bulletins_standard.py create mode 100644 sco_bulletins_ucac.py create mode 100644 sco_bulletins_xml.py create mode 100644 sco_cache.py create mode 100644 sco_codes_parcours.py create mode 100644 sco_compute_moy.py create mode 100644 sco_cost_formation.py create mode 100644 sco_debouche.py create mode 100644 sco_dept.py create mode 100644 sco_dump_db.py create mode 100644 sco_edit_formation.py create mode 100644 sco_edit_matiere.py create mode 100644 sco_edit_module.py create mode 100644 sco_edit_ue.py create mode 100644 sco_edt_cal.py create mode 100644 sco_etape_apogee.py create mode 100644 sco_etape_apogee_view.py create mode 100644 sco_etape_bilan.py create mode 100644 sco_evaluations.py create mode 100644 sco_excel.py create mode 100644 sco_exceptions.py create mode 100644 sco_export_results.py create mode 100644 sco_find_etud.py create mode 100644 sco_formations.py create mode 100644 sco_formsemestre.py create mode 100644 sco_formsemestre_custommenu.py create mode 100644 sco_formsemestre_edit.py create mode 100644 sco_formsemestre_exterieurs.py create mode 100644 sco_formsemestre_inscriptions.py create mode 100644 sco_formsemestre_status.py create mode 100644 sco_formsemestre_validation.py create mode 100644 sco_formulas.py create mode 100644 sco_groups.py create mode 100644 sco_groups_edit.py create mode 100644 sco_groups_view.py create mode 100644 sco_import_users.py create mode 100644 sco_inscr_passage.py create mode 100644 sco_liste_notes.py create mode 100644 sco_lycee.py create mode 100644 sco_modalites.py create mode 100644 sco_moduleimpl_inscriptions.py create mode 100644 sco_moduleimpl_status.py create mode 100644 sco_news.py create mode 100644 sco_page_etud.py create mode 100644 sco_parcours_dut.py create mode 100644 sco_pdf.py create mode 100644 sco_permissions.py create mode 100644 sco_photos.py create mode 100644 sco_placement.py create mode 100644 sco_portal_apogee.py create mode 100644 sco_poursuite_dut.py create mode 100644 sco_preferences.py create mode 100644 sco_prepajury.py create mode 100644 sco_pvjury.py create mode 100644 sco_pvpdf.py create mode 100644 sco_recapcomplet.py create mode 100644 sco_report.py create mode 100644 sco_saisie_notes.py create mode 100644 sco_semset.py create mode 100644 sco_synchro_etuds.py create mode 100644 sco_tag_module.py create mode 100644 sco_trombino.py create mode 100644 sco_trombino_tours.py create mode 100644 sco_ue_external.py create mode 100644 sco_undo_notes.py create mode 100644 sco_up_to_date.py create mode 100644 sco_utils.py create mode 100644 sco_zope.py create mode 100644 scolars.py create mode 100644 scolog.py create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.bootstrap.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.bootstrap.min.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.dataTables.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.dataTables.min.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.foundation.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.foundation.min.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.jqueryui.css create mode 100644 static/DataTables/ColReorder-1.3.3/css/colReorder.jqueryui.min.css create mode 100644 static/DataTables/ColReorder-1.3.3/js/dataTables.colReorder.js create mode 100644 static/DataTables/ColReorder-1.3.3/js/dataTables.colReorder.min.js create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.bootstrap.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.bootstrap.min.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.foundation.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.foundation.min.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.jqueryui.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.jqueryui.min.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.semanticui.css create mode 100644 static/DataTables/DataTables-1.10.15/css/dataTables.semanticui.min.css create mode 100644 static/DataTables/DataTables-1.10.15/css/jquery.dataTables.css create mode 100644 static/DataTables/DataTables-1.10.15/css/jquery.dataTables.min.css create mode 100644 static/DataTables/DataTables-1.10.15/css/jquery.dataTables_themeroller.css create mode 100644 static/DataTables/DataTables-1.10.15/images/sort_asc.png create mode 100644 static/DataTables/DataTables-1.10.15/images/sort_asc_disabled.png create mode 100644 static/DataTables/DataTables-1.10.15/images/sort_both.png create mode 100644 static/DataTables/DataTables-1.10.15/images/sort_desc.png create mode 100644 static/DataTables/DataTables-1.10.15/images/sort_desc_disabled.png create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.bootstrap.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.bootstrap.min.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.foundation.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.foundation.min.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.jqueryui.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.jqueryui.min.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.semanticui.js create mode 100644 static/DataTables/DataTables-1.10.15/js/dataTables.semanticui.min.js create mode 100644 static/DataTables/DataTables-1.10.15/js/jquery.dataTables.js create mode 100644 static/DataTables/DataTables-1.10.15/js/jquery.dataTables.min.js create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.bootstrap.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.bootstrap.min.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.dataTables.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.dataTables.min.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.foundation.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.foundation.min.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.jqueryui.css create mode 100644 static/DataTables/FixedColumns-3.2.2/css/fixedColumns.jqueryui.min.css create mode 100644 static/DataTables/FixedColumns-3.2.2/js/dataTables.fixedColumns.js create mode 100644 static/DataTables/FixedColumns-3.2.2/js/dataTables.fixedColumns.min.js create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.bootstrap.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.bootstrap.min.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.dataTables.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.dataTables.min.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.foundation.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.foundation.min.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.jqueryui.css create mode 100644 static/DataTables/FixedHeader-3.1.2/css/fixedHeader.jqueryui.min.css create mode 100644 static/DataTables/FixedHeader-3.1.2/js/dataTables.fixedHeader.js create mode 100644 static/DataTables/FixedHeader-3.1.2/js/dataTables.fixedHeader.min.js create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.bootstrap.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.bootstrap.min.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.dataTables.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.dataTables.min.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.foundation.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.foundation.min.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.jqueryui.css create mode 100644 static/DataTables/Responsive-2.1.1/css/responsive.jqueryui.min.css create mode 100644 static/DataTables/Responsive-2.1.1/js/dataTables.responsive.js create mode 100644 static/DataTables/Responsive-2.1.1/js/dataTables.responsive.min.js create mode 100644 static/DataTables/Responsive-2.1.1/js/responsive.bootstrap.js create mode 100644 static/DataTables/Responsive-2.1.1/js/responsive.bootstrap.min.js create mode 100644 static/DataTables/Responsive-2.1.1/js/responsive.foundation.js create mode 100644 static/DataTables/Responsive-2.1.1/js/responsive.foundation.min.js create mode 100644 static/DataTables/Responsive-2.1.1/js/responsive.jqueryui.js create mode 100644 static/DataTables/Responsive-2.1.1/js/responsive.jqueryui.min.js create mode 100644 static/DataTables/datatables.css create mode 100644 static/DataTables/datatables.js create mode 100644 static/DataTables/datatables.min.css create mode 100644 static/DataTables/datatables.min.js create mode 100644 static/README create mode 100644 static/css/autosuggest_inquisitor.css create mode 100644 static/css/calabs.css create mode 100644 static/css/groups.css create mode 100644 static/css/gt_table.css create mode 100644 static/css/menu.css create mode 100644 static/css/multiple-select.css create mode 100644 static/css/multiple-select.png create mode 100644 static/css/radar_bulletin.css create mode 100644 static/css/rickshaw.min.css create mode 100644 static/css/scodoc.css create mode 100644 static/css/verticalhisto.css create mode 100644 static/icons/arrow_down.png create mode 100644 static/icons/arrow_none.png create mode 100644 static/icons/arrow_up.png create mode 100644 static/icons/as_pointer.png create mode 100644 static/icons/borgne_img.png create mode 100644 static/icons/bt_gif.png create mode 100644 static/icons/bullet_arrow.png create mode 100644 static/icons/bullet_plus.png create mode 100644 static/icons/bullet_warning_img.png create mode 100644 static/icons/calendar_img.png create mode 100644 static/icons/contact_img.png create mode 100644 static/icons/correspondant_img.png create mode 100644 static/icons/delete_img.png create mode 100644 static/icons/delete_small_dis_img.png create mode 100644 static/icons/delete_small_img.png create mode 100644 static/icons/devel_img.png create mode 100644 static/icons/edit_img.png create mode 100644 static/icons/emptygroupicon_img.png create mode 100644 static/icons/entreprise_img.png create mode 100644 static/icons/entreprise_side_img.png create mode 100644 static/icons/eye_img.png create mode 100644 static/icons/firefox_fr.png create mode 100644 static/icons/flag_uk_img.png create mode 100644 static/icons/formula.png create mode 100644 static/icons/groupicon_img.png create mode 100644 static/icons/hide_img.png create mode 100644 static/icons/hl_corner_bl.png create mode 100644 static/icons/hl_corner_br.png create mode 100644 static/icons/hl_corner_tl.png create mode 100644 static/icons/hl_corner_tr.png create mode 100644 static/icons/info-16_img.png create mode 100644 static/icons/info-24_img.png create mode 100644 static/icons/interdit_img.png create mode 100644 static/icons/loading.jpg create mode 100644 static/icons/lock32_img.png create mode 100644 static/icons/lock_img.png create mode 100644 static/icons/menuarrow1_img.png create mode 100644 static/icons/minus18_img.png create mode 100644 static/icons/minus_img.png create mode 100644 static/icons/next_img.png create mode 100644 static/icons/notes_icon.png create mode 100644 static/icons/notes_img.png create mode 100644 static/icons/nouv_img.png create mode 100644 static/icons/pdficon16x20_img.png create mode 100644 static/icons/pdficon_img.png create mode 100644 static/icons/plus18_img.png create mode 100644 static/icons/plus_img.png create mode 100644 static/icons/prev_img.png create mode 100644 static/icons/pythonpoweredsmall_img.png create mode 100644 static/icons/roundedge_img.png create mode 100644 static/icons/rssscodoc_img.png create mode 100644 static/icons/sco_icon.png create mode 100644 static/icons/scologo_img.png create mode 100644 static/icons/smallpolar_img.png create mode 100644 static/icons/status_green_img.png create mode 100644 static/icons/status_greenorange_img.gif create mode 100644 static/icons/status_orange_img.png create mode 100644 static/icons/status_visible_img.png create mode 100644 static/icons/table_brushed_jpg.png create mode 100644 static/icons/takebacktheweb_small.png create mode 100644 static/icons/trust_firefox_gif.png create mode 100644 static/icons/ul_corner_bl.png create mode 100644 static/icons/ul_corner_br.png create mode 100644 static/icons/ul_corner_tl.png create mode 100644 static/icons/ul_corner_tr.png create mode 100644 static/icons/unknown.jpg create mode 100644 static/icons/unknown_img.png create mode 100644 static/icons/warning16_img.png create mode 100644 static/icons/warning21_img.png create mode 100644 static/icons/warning_img.png create mode 100644 static/icons/xlsicon_img.png create mode 100644 static/index.html create mode 100644 static/jQuery/jquery-1.12.4.min.js create mode 100644 static/jQuery/jquery-migrate-1.2.0.min.js create mode 120000 static/jQuery/jquery.js create mode 100644 static/js/abs_ajax.js create mode 100644 static/js/apo_semset_maq_status.js create mode 100644 static/js/apo_semset_maq_status.js.rej create mode 100644 static/js/bulletin.js create mode 100644 static/js/calabs.js create mode 100644 static/js/editPartitionForm.js create mode 100644 static/js/edit_ue.js create mode 100644 static/js/etud_debouche.js create mode 100644 static/js/etud_info.js create mode 100644 static/js/export_results.js create mode 100644 static/js/formsemestre_ext_edit_ue_validations.js create mode 100644 static/js/groupmgr.js create mode 100644 static/js/groups_view.js create mode 100644 static/js/map_lycees.js create mode 100644 static/js/module_tag_editor.js create mode 100644 static/js/radar_bulletin.js create mode 100644 static/js/recap_parcours.js create mode 100644 static/js/saisie_notes.js create mode 100644 static/js/sco_ue_external.js create mode 100644 static/js/scodoc.js create mode 100644 static/js/trombino.js create mode 100644 static/js/ue_list.js create mode 100644 static/js/validate_previous_ue.js create mode 120000 static/libjs/AutoSuggest.js create mode 100644 static/libjs/AutoSuggest_2.1.3_comp.js create mode 100755 static/libjs/bootstrap-3.1.1-dist/._css create mode 100755 static/libjs/bootstrap-3.1.1-dist/._fonts create mode 100755 static/libjs/bootstrap-3.1.1-dist/._js create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/._bootstrap-theme.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/._bootstrap-theme.css.map create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/._bootstrap-theme.min.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/._bootstrap.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/._bootstrap.css.map create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/._bootstrap.min.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/bootstrap-theme.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/bootstrap-theme.css.map create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/bootstrap-theme.min.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/bootstrap.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/bootstrap.css.map create mode 100644 static/libjs/bootstrap-3.1.1-dist/css/bootstrap.min.css create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/._glyphicons-halflings-regular.eot create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/._glyphicons-halflings-regular.svg create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/._glyphicons-halflings-regular.ttf create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/._glyphicons-halflings-regular.woff create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/glyphicons-halflings-regular.eot create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/glyphicons-halflings-regular.svg create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/glyphicons-halflings-regular.ttf create mode 100644 static/libjs/bootstrap-3.1.1-dist/fonts/glyphicons-halflings-regular.woff create mode 100644 static/libjs/bootstrap-3.1.1-dist/js/._bootstrap.js create mode 100644 static/libjs/bootstrap-3.1.1-dist/js/._bootstrap.min.js create mode 100644 static/libjs/bootstrap-3.1.1-dist/js/bootstrap.js create mode 100644 static/libjs/bootstrap-3.1.1-dist/js/bootstrap.min.js create mode 100644 static/libjs/bootstrap-multiselect/bootstrap-multiselect.css create mode 100644 static/libjs/bootstrap-multiselect/bootstrap-multiselect.js create mode 100644 static/libjs/bubble.js create mode 100644 static/libjs/d3.v3.min.js create mode 100644 static/libjs/fullcalendar/THIS_IS_VERSION_2.0.0-beta2 create mode 100644 static/libjs/fullcalendar/fullcalendar.css create mode 100644 static/libjs/fullcalendar/fullcalendar.min.js create mode 100644 static/libjs/fullcalendar/fullcalendar.print.css create mode 100644 static/libjs/jQuery-tagEditor/bower.json create mode 100644 static/libjs/jQuery-tagEditor/demo.html create mode 100644 static/libjs/jQuery-tagEditor/jquery.caret.min.js create mode 100644 static/libjs/jQuery-tagEditor/jquery.tag-editor.css create mode 100644 static/libjs/jQuery-tagEditor/jquery.tag-editor.js create mode 100644 static/libjs/jQuery-tagEditor/jquery.tag-editor.min.js create mode 100644 static/libjs/jQuery-tagEditor/readme.md create mode 100644 static/libjs/jQuery-tagEditor/tag-editor.jquery.json create mode 100644 static/libjs/jinplace-1.2.1.min.js create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/animated-overlay.gif create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-icons_222222_256x240.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-icons_454545_256x240.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-icons_888888_256x240.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.css create mode 100644 static/libjs/jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.min.css create mode 100644 static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js create mode 100644 static/libjs/jquery.field.min.js create mode 100644 static/libjs/jquery.ui.map.full.min.js create mode 100644 static/libjs/menu.js create mode 100644 static/libjs/moment.min.js create mode 100644 static/libjs/purl.js create mode 100644 static/libjs/qtip/LICENSE create mode 100644 static/libjs/qtip/jquery.qtip-3.0.3.min.css create mode 100644 static/libjs/qtip/jquery.qtip-3.0.3.min.js create mode 100644 static/libjs/rickshaw.min.js create mode 100644 static/libjs/sorttable.js create mode 100644 static/robots.txt create mode 100644 test_jurype.py create mode 100644 tests/conn_info.py create mode 100755 tests/demo/demo_reset_noms.py create mode 100644 tests/demo/noms.txt create mode 100644 tests/demo/prenoms-f.txt create mode 100644 tests/demo/prenoms-h.txt create mode 100644 tests/demo/prenoms.txt create mode 100644 tests/splinter/README.md create mode 100644 tests/splinter/common.py create mode 100644 tests/splinter/conn_info.py create mode 100644 tests/splinter/test-0.py create mode 100755 tests/splinter/test-intensive-changes.py create mode 100644 tests/splinter/test-jury.py create mode 100644 tests/test-all-moys.py create mode 100644 tests/test-apo-csv.py create mode 100644 tests/test-apo-export-impairs.py create mode 100644 tests/testpyexcelerator.py diff --git a/ImportScolars.py b/ImportScolars.py new file mode 100644 index 00000000..c819551e --- /dev/null +++ b/ImportScolars.py @@ -0,0 +1,781 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +""" Importation des etudiants à partir de fichiers CSV +""" + +import os, sys, time, pdb + +from sco_utils import * +from notesdb import * +from notes_log import log +import scolars +import sco_formsemestre +import sco_groups +import sco_excel +import sco_groups_view +import sco_news +from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC +from sco_formsemestre_inscriptions import do_formsemestre_inscription_with_modules +from gen_tables import GenTable + +# format description (relative to Product directory)) +FORMAT_FILE = "misc/format_import_etudiants.txt" + +# Champs modifiables via "Import données admission" +ADMISSION_MODIFIABLE_FIELDS = ( + "code_nip", + "code_ine", + "date_naissance", + "lieu_naissance", + "bac", + "specialite", + "annee_bac", + "math", + "physique", + "anglais", + "francais", + "type_admission", + "boursier_prec", + "qualite", + "rapporteur", + "score", + "commentaire", + "classement", + "apb_groupe", + "apb_classement_gr", + "nomlycee", + "villelycee", + "codepostallycee", + "codelycee", + # Adresse: + "email", + "emailperso", + "domicile", + "codepostaldomicile", + "villedomicile", + "paysdomicile", + "telephone", + "telephonemobile", + # Debouche + "debouche", + # Groupes + "groupes", +) + +# ---- + + +def sco_import_format(with_codesemestre=True): + "returns tuples (Attribut, Type, Table, AllowNulls, Description)" + r = [] + for l in open(SCO_SRCDIR + "/" + FORMAT_FILE): + l = l.strip() + if l and l[0] != "#": + fs = l.split(";") + if len(fs) < 5: + # Bug: invalid format file (fatal) + raise ScoException( + "file %s has invalid format (expected %d fields, got %d) (%s)" + % (FORMAT_FILE, 5, len(fs), l) + ) + fieldname = ( + fs[0].strip().lower().split()[0] + ) # titre attribut: normalize, 1er mot seulement (nom du champ en BD) + typ, table, allow_nulls, description = [x.strip() for x in fs[1:5]] + aliases = [x.strip() for x in fs[5:] if x.strip()] + if fieldname not in aliases: + aliases.insert(0, fieldname) # prepend + if with_codesemestre or fs[0] != "codesemestre": + r.append((fieldname, typ, table, allow_nulls, description, aliases)) + return r + + +def sco_import_format_dict(with_codesemestre=True): + """ Attribut: { 'type': , 'table', 'allow_nulls' , 'description' } + """ + fmt = sco_import_format(with_codesemestre=with_codesemestre) + R = collections.OrderedDict() + for l in fmt: + R[l[0]] = { + "type": l[1], + "table": l[2], + "allow_nulls": l[3], + "description": l[4], + "aliases": l[5], + } + return R + + +def sco_import_generate_excel_sample( + fmt, + with_codesemestre=True, + only_tables=None, + with_groups=True, + exclude_cols=[], + extra_cols=[], + group_ids=[], + context=None, + REQUEST=None, +): + """Generates an excel document based on format fmt + (format is the result of sco_import_format()) + If not None, only_tables can specify a list of sql table names + (only columns from these tables will be generated) + If group_ids, liste les etudiants de ces groupes + """ + style = sco_excel.Excel_MakeStyle(bold=True) + style_required = sco_excel.Excel_MakeStyle(bold=True, color="red") + titles = [] + titlesStyles = [] + for l in fmt: + name = strlower(l[0]) + if (not with_codesemestre) and name == "codesemestre": + continue # pas de colonne codesemestre + if only_tables is not None and strlower(l[2]) not in only_tables: + continue # table non demandée + if name in exclude_cols: + continue # colonne exclue + if int(l[3]): + titlesStyles.append(style) + else: + titlesStyles.append(style_required) + titles.append(name) + if with_groups and "groupes" not in titles: + titles.append("groupes") + titlesStyles.append(style) + titles += extra_cols + titlesStyles += [style] * len(extra_cols) + if group_ids and context: + groups_infos = sco_groups_view.DisplayedGroupsInfos( + context, group_ids, REQUEST=REQUEST + ) + members = groups_infos.members + log( + "sco_import_generate_excel_sample: group_ids=%s %d members" + % (group_ids, len(members)) + ) + titles = ["etudid"] + titles + titlesStyles = [style] + titlesStyles + # rempli table avec données actuelles + lines = [] + for i in members: + etud = context.getEtudInfo(etudid=i["etudid"], filled=True)[0] + l = [] + for field in titles: + if field == "groupes": + sco_groups.etud_add_group_infos( + context, etud, groups_infos.formsemestre, sep=";" + ) + l.append(etud["partitionsgroupes"]) + else: + key = strlower(field).split()[0] + l.append(etud.get(key, "")) + lines.append(l) + else: + lines = [[]] # empty content, titles only + return sco_excel.Excel_SimpleTable( + titles=titles, titlesStyles=titlesStyles, SheetName="Etudiants", lines=lines + ) + + +def students_import_excel( + context, + csvfile, + REQUEST=None, + formsemestre_id=None, + check_homonyms=True, + require_ine=False, +): + "import students from Excel file" + diag = scolars_import_excel_file( + csvfile, + context.Notes, + REQUEST, + formsemestre_id=formsemestre_id, + check_homonyms=check_homonyms, + require_ine=require_ine, + exclude_cols=["photo_filename"], + ) + if REQUEST: + if formsemestre_id: + dest = "formsemestre_status?formsemestre_id=%s" % formsemestre_id + else: + dest = REQUEST.URL1 + H = [context.sco_header(REQUEST, page_title="Import etudiants")] + H.append("") + H.append("

Import terminé !

") + H.append('

Continuer

' % dest) + return "\n".join(H) + context.sco_footer(REQUEST) + + +def scolars_import_excel_file( + datafile, + context, + REQUEST, + formsemestre_id=None, + check_homonyms=True, + require_ine=False, + exclude_cols=[], +): + """Importe etudiants depuis fichier Excel + et les inscrit dans le semestre indiqué (et à TOUS ses modules) + """ + log("scolars_import_excel_file: formsemestre_id=%s" % formsemestre_id) + cnx = context.GetDBConnexion(autocommit=False) + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + annee_courante = time.localtime()[0] + always_require_ine = context.get_preference("always_require_ine") + exceldata = datafile.read() + if not exceldata: + raise ScoValueError("Ficher excel vide ou invalide") + diag, data = sco_excel.Excel_to_list(exceldata) + if not data: # probably a bug + raise ScoException("scolars_import_excel_file: empty file !") + + formsemestre_to_invalidate = Set() + + # 1- --- check title line + titles = {} + fmt = sco_import_format() + for l in fmt: + tit = strlower(l[0]).split()[0] # titles in lowercase, and take 1st word + if ( + (not formsemestre_id) or (tit != "codesemestre") + ) and tit not in exclude_cols: + titles[tit] = l[1:] # title : (Type, Table, AllowNulls, Description) + + # log("titles=%s" % titles) + # remove quotes, downcase and keep only 1st word + try: + fs = [strlower(stripquotes(s)).split()[0] for s in data[0]] + except: + raise ScoValueError("Titres de colonnes invalides (ou vides ?)") + # log("excel: fs='%s'\ndata=%s" % (str(fs), str(data))) + + # check columns titles + if len(fs) != len(titles): + missing = {}.fromkeys(titles.keys()) + unknown = [] + for f in fs: + if missing.has_key(f): + del missing[f] + else: + unknown.append(f) + raise ScoValueError( + "Nombre de colonnes incorrect (devrait être %d, et non %d)
(colonnes manquantes: %s, colonnes invalides: %s)" + % (len(titles), len(fs), missing.keys(), unknown) + ) + titleslist = [] + for t in fs: + if not titles.has_key(t): + raise ScoValueError('Colonne invalide: "%s"' % t) + titleslist.append(t) # + # ok, same titles + # Start inserting data, abort whole transaction in case of error + created_etudids = [] + NbImportedHomonyms = 0 + GroupIdInferers = {} + try: # --- begin DB transaction + linenum = 0 + for line in data[1:]: + linenum += 1 + # Read fields, check and convert type + values = {} + fs = line + # remove quotes + for i in range(len(fs)): + if fs[i] and ( + (fs[i][0] == '"' and fs[i][-1] == '"') + or (fs[i][0] == "'" and fs[i][-1] == "'") + ): + fs[i] = fs[i][1:-1] + for i in range(len(fs)): + val = fs[i].strip() + typ, table, an, descr, aliases = tuple(titles[titleslist[i]]) + # log('field %s: %s %s %s %s'%(titleslist[i], table, typ, an, descr)) + if not val and not an: + raise ScoValueError( + "line %d: null value not allowed in column %s" + % (linenum, titleslist[i]) + ) + if val == "": + val = None + else: + if typ == "real": + val = val.replace(",", ".") # si virgule a la française + try: + val = float(val) + except: + raise ScoValueError( + "valeur nombre reel invalide (%s) sur line %d, colonne %s" + % (val, linenum, titleslist[i]) + ) + elif typ == "integer": + try: + # on doit accepter des valeurs comme "2006.0" + val = val.replace(",", ".") # si virgule a la française + val = float(val) + if val % 1.0 > 1e-4: + raise ValueError() + val = int(val) + except: + raise ScoValueError( + "valeur nombre entier invalide (%s) sur ligne %d, colonne %s" + % (val, linenum, titleslist[i]) + ) + # xxx Ad-hoc checks (should be in format description) + if strlower(titleslist[i]) == "sexe": + try: + val = scolars.normalize_sexe(val) + except: + raise ScoValueError( + "valeur invalide pour 'SEXE' (doit etre 'M' ou 'MME' ou 'H' ou 'F', pas '%s') ligne %d, colonne %s" + % (val, linenum, titleslist[i]) + ) + # Excel date conversion: + if strlower(titleslist[i]) == "date_naissance": + if val: + if re.match("^[0-9]*\.?[0-9]*$", str(val)): + val = sco_excel.xldate_as_datetime(float(val)) + # INE + if ( + strlower(titleslist[i]) == "code_ine" + and always_require_ine + and not val + ): + raise ScoValueError( + "Code INE manquant sur ligne %d, colonne %s" + % (linenum, titleslist[i]) + ) + + # -- + values[titleslist[i]] = val + skip = False + is_new_ine = values["code_ine"] and _is_new_ine(cnx, values["code_ine"]) + if require_ine and not is_new_ine: + log("skipping %s (code_ine=%s)" % (values["nom"], values["code_ine"])) + skip = True + + if not skip: + if values["code_ine"] and not is_new_ine: + raise ScoValueError("Code INE dupliquĂ© (%s)" % values["code_ine"]) + # Check nom/prenom + ok, NbHomonyms = scolars.check_nom_prenom( + cnx, nom=values["nom"], prenom=values["prenom"] + ) + if not ok: + raise ScoValueError( + "nom ou prĂ©nom invalide sur la ligne %d" % (linenum) + ) + if NbHomonyms: + NbImportedHomonyms += 1 + # Insert in DB tables + formsemestre_to_invalidate.add( + _import_one_student( + context, + cnx, + REQUEST, + formsemestre_id, + values, + GroupIdInferers, + annee_courante, + created_etudids, + linenum, + ) + ) + + # Verification proportion d'homonymes: si > 10%, abandonne + log("scolars_import_excel_file: detected %d homonyms" % NbImportedHomonyms) + if check_homonyms and NbImportedHomonyms > len(created_etudids) / 10: + log("scolars_import_excel_file: too many homonyms") + raise ScoValueError( + "Il y a trop d'homonymes (%d Ă©tudiants)" % NbImportedHomonyms + ) + except: + cnx.rollback() + log("scolars_import_excel_file: aborting transaction !") + # Nota: db transaction is sometimes partly commited... + # here we try to remove all created students + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + for etudid in created_etudids: + log("scolars_import_excel_file: deleting etudid=%s" % etudid) + cursor.execute( + "delete from notes_moduleimpl_inscription where etudid=%(etudid)s", + {"etudid": etudid}, + ) + cursor.execute( + "delete from notes_formsemestre_inscription where etudid=%(etudid)s", + {"etudid": etudid}, + ) + cursor.execute( + "delete from scolar_events where etudid=%(etudid)s", {"etudid": etudid} + ) + cursor.execute( + "delete from adresse where etudid=%(etudid)s", {"etudid": etudid} + ) + cursor.execute( + "delete from admissions where etudid=%(etudid)s", {"etudid": etudid} + ) + cursor.execute( + "delete from group_membership where etudid=%(etudid)s", + {"etudid": etudid}, + ) + cursor.execute( + "delete from identite where etudid=%(etudid)s", {"etudid": etudid} + ) + cnx.commit() + log("scolars_import_excel_file: re-raising exception") + raise + + diag.append("Import et inscription de %s Ă©tudiants" % len(created_etudids)) + + sco_news.add( + context, + REQUEST, + typ=NEWS_INSCR, + text="Inscription de %d Ă©tudiants" # peuvent avoir ete inscrits a des semestres differents + % len(created_etudids), + object=formsemestre_id, + ) + + log("scolars_import_excel_file: completing transaction") + cnx.commit() + + # Invalide les caches des semestres dans lesquels on a inscrit des etudiants: + context.Notes._inval_cache(formsemestre_id_list=formsemestre_to_invalidate) + + return diag + + +def _import_one_student( + context, + cnx, + REQUEST, + formsemestre_id, + values, + GroupIdInferers, + annee_courante, + created_etudids, + linenum, +): + """ + Import d'un Ă©tudiant et inscription dans le semestre. + Return: id du semestre dans lequel il a Ă©tĂ© inscrit. + """ + log( + "scolars_import_excel_file: formsemestre_id=%s values=%s" + % (formsemestre_id, str(values)) + ) + # Identite + args = values.copy() + etudid = scolars.identite_create(cnx, args, context=context, REQUEST=REQUEST) + created_etudids.append(etudid) + # Admissions + args["etudid"] = etudid + args["annee"] = annee_courante + adm_id = scolars.admission_create(cnx, args) + # Adresse + args["typeadresse"] = "domicile" + args["description"] = "(infos admission)" + adresse_id = scolars.adresse_create(cnx, args) + # Inscription au semestre + args["etat"] = "I" # etat insc. semestre + if formsemestre_id: + args["formsemestre_id"] = formsemestre_id + else: + args["formsemestre_id"] = values["codesemestre"] + formsemestre_id = values["codesemestre"] + # recupere liste des groupes: + if formsemestre_id not in GroupIdInferers: + GroupIdInferers[formsemestre_id] = sco_groups.GroupIdInferer( + context, formsemestre_id + ) + gi = GroupIdInferers[formsemestre_id] + if args["groupes"]: + groupes = args["groupes"].split(";") + else: + groupes = [] + group_ids = [gi[group_name] for group_name in groupes] + group_ids = {}.fromkeys(group_ids).keys() # uniq + if None in group_ids: + raise ScoValueError( + "groupe invalide sur la ligne %d (groupe %s)" % (linenum, groupes) + ) + + do_formsemestre_inscription_with_modules( + context, + args["formsemestre_id"], + etudid, + group_ids, + etat="I", + REQUEST=REQUEST, + method="import_csv_file", + ) + return args["formsemestre_id"] + + +def _is_new_ine(cnx, code_ine): + "True if this code is not in DB" + etuds = scolars.identite_list(cnx, {"code_ine": code_ine}) + return not etuds + + +# ------ Fonction rĂ©-Ă©crite en nov 2016 pour lire des fichiers sans etudid (fichiers APB) +def scolars_import_admission( + datafile, context, REQUEST, formsemestre_id=None, type_admission=None +): + """Importe donnĂ©es admission depuis un fichier Excel quelconque + par exemple ceux utilisĂ©s avec APB + + Cherche dans ce fichier les Ă©tudiants qui correspondent Ă  des inscrits du + semestre formsemestre_id. + Le fichier n'a pas l'INE ni le NIP ni l'etudid, la correspondance se fait + via les noms/prĂ©noms qui doivent ĂŞtre Ă©gaux (la casse, les accents et caractères spĂ©ciaux + Ă©tant ignorĂ©s). + + On tolère plusieurs variantes pour chaque nom de colonne (ici aussi, la casse, les espaces + et les caractères spĂ©ciaux sont ignorĂ©s. Ainsi, la colonne "PrĂ©nom:" sera considĂ©rĂ© comme "prenom". + + Le parametre type_admission remplace les valeurs vides (dans la base ET dans le fichier importĂ©) du champ type_admission. + Si une valeur existe ou est prĂ©sente dans le fichier importĂ©, ce paramètre est ignorĂ©. + + TODO: + - choix onglet du classeur + """ + + log("scolars_import_admission: formsemestre_id=%s" % formsemestre_id) + members = sco_groups.get_group_members( + context, sco_groups.get_default_group(context, formsemestre_id) + ) + etuds_by_nomprenom = {} # { nomprenom : etud } + diag = [] + for m in members: + np = (adm_normalize_string(m["nom"]), adm_normalize_string(m["prenom"])) + if np in etuds_by_nomprenom: + msg = "Attention: hononymie pour %s %s" % (m["nom"], m["prenom"]) + log(msg) + diag.append(msg) + etuds_by_nomprenom[np] = m + + exceldata = datafile.read() + diag2, data = sco_excel.Excel_to_list(exceldata, convert_to_string=False) + if not data: + raise ScoException("scolars_import_admission: empty file !") + diag += diag2 + cnx = context.GetDBConnexion() + + titles = data[0] + # idx -> ('field', convertor) + fields = adm_get_fields(titles, formsemestre_id) + idx_nom = None + idx_prenom = None + for idx in fields: + if fields[idx][0] == "nom": + idx_nom = idx + if fields[idx][0] == "prenom": + idx_prenom = idx + if (idx_nom is None) or (idx_prenom is None): + log("fields indices=" + ", ".join([str(x) for x in fields])) + log("fields titles =" + ", ".join([fields[x][0] for x in fields])) + raise FormatError( + "scolars_import_admission: colonnes nom et prenom requises", + dest_url="form_students_import_infos_admissions?formsemestre_id=%s" + % formsemestre_id, + ) + + modifiable_fields = Set(ADMISSION_MODIFIABLE_FIELDS) + + nline = 2 # la premiere ligne de donnees du fichier excel est 2 + n_import = 0 + for line in data[1:]: + # Retrouve l'Ă©tudiant parmi ceux du semestre par (nom, prenom) + nom = adm_normalize_string(line[idx_nom]) + prenom = adm_normalize_string(line[idx_prenom]) + if not (nom, prenom) in etuds_by_nomprenom: + log( + "unable to find %s %s among members" % (line[idx_nom], line[idx_prenom]) + ) + else: + etud = etuds_by_nomprenom[(nom, prenom)] + cur_adm = scolars.admission_list(cnx, args={"etudid": etud["etudid"]})[0] + # peuple les champs presents dans le tableau + args = {} + for idx in fields: + field_name, convertor = fields[idx] + if field_name in modifiable_fields: + try: + val = convertor(line[idx]) + except ValueError: + raise FormatError( + 'scolars_import_admission: valeur invalide, ligne %d colonne %s: "%s"' + % (nline, field_name, line[idx]), + dest_url="form_students_import_infos_admissions?formsemestre_id=%s" + % formsemestre_id, + ) + if val is not None: # note: ne peut jamais supprimer une valeur + args[field_name] = val + if args: + args["etudid"] = etud["etudid"] + args["adm_id"] = cur_adm["adm_id"] + # Type admission: traitement particulier + if not cur_adm["type_admission"] and not args.get("type_admission"): + args["type_admission"] = type_admission + scolars.etudident_edit(cnx, args) + adr = scolars.adresse_list(cnx, args={"etudid": etud["etudid"]}) + if adr: + args["adresse_id"] = adr[0]["adresse_id"] + scolars.adresse_edit( + cnx, args + ) # ne passe pas le contexte: pas de notification ici + else: + args["typeadresse"] = "domicile" + args["description"] = "(infos admission)" + adresse_id = scolars.adresse_create(cnx, args) + # log('import_adm: %s' % args ) + # Change les groupes si nĂ©cessaire: + if args["groupes"]: + gi = sco_groups.GroupIdInferer(context, formsemestre_id) + groupes = args["groupes"].split(";") + group_ids = [gi[group_name] for group_name in groupes] + group_ids = {}.fromkeys(group_ids).keys() # uniq + if None in group_ids: + raise ScoValueError( + "groupe invalide sur la ligne %d (groupe %s)" + % (nline, groupes) + ) + + for group_id in group_ids: + sco_groups.change_etud_group_in_partition( + context, args["etudid"], group_id, REQUEST=REQUEST + ) + # + diag.append("import de %s" % (etud["nomprenom"])) + n_import += 1 + nline += 1 + diag.append("%d lignes importĂ©es" % n_import) + if n_import > 0: + context._inval_cache(formsemestre_id=formsemestre_id) + return diag + + +_ADM_PATTERN = re.compile(r"[\W]+", re.UNICODE) # supprime tout sauf alphanum + + +def adm_normalize_string(s): # normalize unicode title + return suppression_diacritics(_ADM_PATTERN.sub("", s.strip().lower())).replace( + "_", "" + ) + + +def adm_get_fields(titles, formsemestre_id): + """Cherche les colonnes importables dans les titres (ligne 1) du fichier excel + return: { idx : (field_name, convertor) } + """ + # log('adm_get_fields: titles=%s' % titles) + Fmt = sco_import_format_dict() + fields = {} + idx = 0 + for title in titles: + title_n = adm_normalize_string(title) + for k in Fmt: + for v in Fmt[k]["aliases"]: + if adm_normalize_string(v) == title_n: + typ = Fmt[k]["type"] + if typ == "real": + convertor = adm_convert_real + elif typ == "integer" or typ == "int": + convertor = adm_convert_int + else: + convertor = adm_convert_text + # doublons ? + if k in [x[0] for x in fields.values()]: + raise FormatError( + 'scolars_import_admission: titre "%s" en double (ligne 1)' + % (title), + dest_url="form_students_import_infos_admissions_apb?formsemestre_id=%s" + % formsemestre_id, + ) + fields[idx] = (k, convertor) + idx += 1 + + return fields + + +def adm_convert_text(v): + if type(v) == FloatType: + return "{:g}".format(v) # evite "1.0" + return v + + +def adm_convert_int(v): + if type(v) != IntType and not v: + return None + return int(float(v)) # accept "10.0" + + +def adm_convert_real(v): + if type(v) != FloatType and not v: + return None + return float(v) + + +def adm_table_description_format(context): + """Table HTML (ou autre format) decrivant les donnees d'admissions importables + """ + Fmt = sco_import_format_dict(with_codesemestre=False) + for k in Fmt: + Fmt[k]["attribute"] = k + Fmt[k]["aliases_str"] = ", ".join(Fmt[k]["aliases"]) + if not Fmt[k]["allow_nulls"]: + Fmt[k]["required"] = "*" + if k in ADMISSION_MODIFIABLE_FIELDS: + Fmt[k]["writable"] = "oui" + else: + Fmt[k]["writable"] = "non" + titles = { + "attribute": "Attribut", + "type": "Type", + "required": "Requis", + "writable": "Modifiable", + "description": "Description", + "aliases_str": "Titres (variantes)", + } + columns_ids = ("attribute", "type", "writable", "description", "aliases_str") + + tab = GenTable( + titles=titles, + columns_ids=columns_ids, + rows=Fmt.values(), + html_sortable=True, + html_class="table_leftalign", + preferences=context.get_preferences(), + ) + return tab diff --git a/SuppressAccents.py b/SuppressAccents.py new file mode 100644 index 00000000..f0b4d34f --- /dev/null +++ b/SuppressAccents.py @@ -0,0 +1,206 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +"""Suppression des accents d'une chaine + +Source: http://wikipython.flibuste.net/moin.py/JouerAvecUnicode#head-1213938516c633958921591439c33d202244e2f4 +""" + +_reptable = {} + + +def _fill_reptable(): + _corresp = [ + ( + u"A", + [0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x0100, 0x0102, 0x0104], + ), + (u"AE", [0x00C6]), + ( + u"a", + [0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x0101, 0x0103, 0x0105], + ), + (u"ae", [0x00E6]), + (u"C", [0x00C7, 0x0106, 0x0108, 0x010A, 0x010C]), + (u"c", [0x00E7, 0x0107, 0x0109, 0x010B, 0x010D]), + (u"D", [0x00D0, 0x010E, 0x0110]), + (u"d", [0x00F0, 0x010F, 0x0111]), + ( + u"E", + [0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0112, 0x0114, 0x0116, 0x0118, 0x011A], + ), + ( + u"e", + [ + 0x00E8, + 0xE9, + 0x00E9, + 0x00EA, + 0xEB, + 0x00EB, + 0x0113, + 0x0115, + 0x0117, + 0x0119, + 0x011B, + ], + ), + (u"G", [0x011C, 0x011E, 0x0120, 0x0122]), + (u"g", [0x011D, 0x011F, 0x0121, 0x0123]), + (u"H", [0x0124, 0x0126]), + (u"h", [0x0125, 0x0127]), + ( + u"I", + [0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0128, 0x012A, 0x012C, 0x012E, 0x0130], + ), + ( + u"i", + [0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0129, 0x012B, 0x012D, 0x012F, 0x0131], + ), + (u"IJ", [0x0132]), + (u"ij", [0x0133]), + (u"J", [0x0134]), + (u"j", [0x0135]), + (u"K", [0x0136]), + (u"k", [0x0137, 0x0138]), + (u"L", [0x0139, 0x013B, 0x013D, 0x013F, 0x0141]), + (u"l", [0x013A, 0x013C, 0x013E, 0x0140, 0x0142]), + (u"N", [0x00D1, 0x0143, 0x0145, 0x0147, 0x014A]), + (u"n", [0x00F1, 0x0144, 0x0146, 0x0148, 0x0149, 0x014B]), + ( + u"O", + [0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D8, 0x014C, 0x014E, 0x0150], + ), + ( + u"o", + [0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F8, 0x014D, 0x014F, 0x0151], + ), + (u"OE", [0x0152]), + (u"oe", [0x0153]), + (u"R", [0x0154, 0x0156, 0x0158]), + (u"r", [0x0155, 0x0157, 0x0159]), + (u"S", [0x015A, 0x015C, 0x015E, 0x0160]), + (u"s", [0x015B, 0x015D, 0x015F, 0x01610, 0x017F, 0x0218]), + (u"T", [0x0162, 0x0164, 0x0166]), + (u"t", [0x0163, 0x0165, 0x0167]), + ( + u"U", + [ + 0x00D9, + 0x00DA, + 0x00DB, + 0x00DC, + 0x0168, + 0x016A, + 0x016C, + 0x016E, + 0x0170, + 0x172, + ], + ), + ( + u"u", + [ + 0x00F9, + 0x00FA, + 0x00FB, + 0x00FC, + 0x0169, + 0x016B, + 0x016D, + 0x016F, + 0x0171, + 0xB5, + ], + ), + (u"W", [0x0174]), + (u"w", [0x0175]), + (u"Y", [0x00DD, 0x0176, 0x0178]), + (u"y", [0x00FD, 0x00FF, 0x0177]), + (u"Z", [0x0179, 0x017B, 0x017D]), + (u"z", [0x017A, 0x017C, 0x017E]), + ( + u"", + [ + 0x80, + 0x81, + 0x82, + 0x83, + 0x84, + 0x85, + 0x86, + 0x87, + 0x88, + 0x89, + 0x8A, + 0x8B, + 0x8C, + 0x8D, + 0x8E, + 0x8F, + 0x90, + 0x91, + 0x92, + 0x93, + 0x94, + 0x95, + 0x96, + 0x97, + 0x98, + 0x99, + 0x9A, + 0x9B, + 0x9C, + 0x9D, + 0x9E, + 0x9F, + ], + ), # misc controls + (u" ", [0x00A0]), #   + (u"!", [0xA1]), # ¡ + (u"c", [0xA2]), # cent + (u"L", [0xA3]), # pound + (u"o", [0xA4]), # currency symbol + (u"Y", [0xA5]), # yen + (u"|", [0xA6]), # Broken Bar ¦ + (u"S", [0xA7]), # section + (u"", [0xA8]), # diaeresis ¨ + (u"", [0xA9]), # copyright + (u'"', [0xAB, 0xBA]), # «, » <<, >> + (u" ", [0xAC]), # Math Not Sign + (u"", [0xAD]), # DashPunctuation + (u"(r)", [0xAE]), # registred + (u"-", [0xAF]), # macron + (u"", [0xB0]), # degre + (u"+-", [0xB1]), # +- + (u"2", [0x00B2, 0xB2]), # deux exposant + (u"3", [0xB3]), # 3 exposant + (u".", [0xB7]), # ·, + (u"1/4", [0xBC]), # 1/4 + (u"1/2", [0xBD]), # 1/2 + (u"3/4", [0xBE]), # 3/4 + (u"e", [0x20AC]), # euro + (u"--", [0x2013]), # EN DASH + (u"'", [0x2018, 0x2019, 0x201A]), # LEFT, RIGHT SINGLE QUOTATION MARK + (u" ", [0x2020]), # dagger + ] + global _reptable + for repchar, codes in _corresp: + for code in codes: + _reptable[code] = repchar + + +_fill_reptable() + + +def suppression_diacritics(s): + """Suppression des accents et autres marques. + + @param s: le texte Ă  nettoyer. + @type s: str ou unicode + @return: le texte nettoyĂ© de ses marques diacritiques. + @rtype: unicode + """ + if isinstance(s, str): + s = unicode(s, "utf8", "replace") + return s.translate(_reptable) diff --git a/TODO b/TODO new file mode 100644 index 00000000..4041532f --- /dev/null +++ b/TODO @@ -0,0 +1,238 @@ + + NOTES EN VRAC / Brouillon / Trucs obsoletes + + +#do_moduleimpl_list\(\{"([a-z_]*)"\s*:\s*(.*)\}\) +#do_moduleimpl_list( $1 = $2 ) + +#do_moduleimpl_list\([\s\n]*args[\s\n]*=[\s\n]*\{"([a-z_]*)"[\s\n]*:[\s\n]*(.*)[\s\n]*\}[\s\n]*\) + +Upgrade JavaScript + - jquery-ui-1.12.1 introduit un problème d'affichage de la barre de menu. + Il faudrait la revoir entièrement pour upgrader. + On reste donc Ă  jquery-ui-1.10.4.custom + Or cette version est incompatible avec jQuery 3 (messages d'erreur dans la console) + On reste donc avec jQuery 1.12.14 + + +Suivi des requĂŞtes utilisateurs: + table sql: id, ip, authuser, request + + +* Optim: +porcodeb4, avant memorisation des moy_ue: +S1 SEM14133 cold start: min 9s, max 12s, avg > 11s + inval (add note): 1.33s (pas de recalcul des autres) + inval (add abs) : min8s, max 12s (recalcule tout :-() +LP SEM14946 cold start: 0.7s - 0.86s + + + +----------------- LISTE OBSOLETE (très ancienne, Ă  trier) ----------------------- +BUGS +---- + + - formsemestre_inscription_with_modules + si inscription 'un etud deja inscrit, IntegrityError + +FEATURES REQUESTS +----------------- + +* Bulletins: + . logos IUT et Univ sur bull PDF + . nom departement: nom abbrĂ©gĂ© (CJ) ou complet (Carrière Juridiques) + . bulletin: deplacer la barre indicateur (cf OLDGEA S2: gĂŞne) + . bulletin: click nom titre -> ficheEtud + + . formsemestre_pagebulletin_dialog: marges en mm: accepter "2,5" et "2.5" + et valider correctement le form ! + +* Jury + . recapcomplet: revenir avec qq lignes au dessus de l'Ă©tudiant en cours + + +* Divers + . formsemestre_editwithmodules: confirmer suppression modules + (et pour l'instant impossible si evaluations dans le module) + +* Modules et UE optionnelles: + . UE capitalisĂ©es: donc dispense possible dans semestre redoublĂ©. + traitable en n'inscrivant pas l'etudiant au modules + de cette UE: faire interface utilisateur + + . page pour inscription d'un etudiant a un module + . page pour visualiser les modules auquel un etudiant est inscrit, + et le desinscrire si besoin. + + . ficheEtud indiquer si inscrit au module sport + +* Absences + . EtatAbsences : verifier dates (en JS) + . Listes absences pdf et listes groupes pdf + emargements (cf mail Nathalie) + . absences par demi-journĂ©es sur EtatAbsencesDate (? Ă  vĂ©rifier) + . formChoixSemestreGroupe: utilisĂ© par Absences/index_html + a amĂ©liorer + + +* Notes et Ă©valuations: + . Exception "Not an OLE file": generer page erreur plus explicite + . Dates evaluation: utiliser JS pour calendrier + . Saisie des notes: si une note invalide, l'indiquer dans le listing (JS ?) + . et/ou: notes invalides: afficher les noms des etudiants concernes + dans le message d'erreur. + . upload excel: message erreur peu explicite: + * Feuille "Saisie notes", 17 lignes + * Erreur: la feuille contient 1 notes invalides + * Notes invalides pour les id: ['10500494'] + (pas de notes modifiĂ©es) + Notes chargĂ©es. <<< CONTRADICTOIRE !! + + . recap complet semestre: + Options: + - choix groupes + - critère de tri (moy ou alphab) + - nb de chiffres a afficher + + + definir des "catĂ©gories" d'Ă©valuations (eg "thĂ©orie","pratique") + afin de n'afficher que des moyennes "de catĂ©gorie" dans + le bulletin. + + . liste des absents Ă  une eval et croisement avec BD absences + + . notes_evaluation_listenotes + - afficher groupes, moyenne, #inscrits, #absents, #manquantes dans l'en-tete. + - lien vers modif notes (selon role) + + . Export excel des notes d'evaluation: indiquer date, et autres infos en haut. + . GĂ©nĂ©ration PDF listes notes + . Page recap notes moyennes par groupes (choisir type de groupe?) + + . (GEA) edition tableau notes avec tous les evals d'un module + (comme notes_evaluation_listenotes mais avec tt les evals) + + +* Non prioritaire: + . optimiser scolar_news_summary + . recapitulatif des "nouvelles" + - dernieres notes + - changement de statuts (demissions,inscriptions) + - annotations + - entreprises + + . notes_table: pouvoir changer decision sans invalider tout le cache + . navigation: utiliser Session pour montrer historique pages vues ? + + + +------------------------------------------------------------------------ + + +A faire: + - fiche etud: code dec jury sur ligne 1 + si ancien, indiquer autorisation inscription sous le parcours + + - saisie notes: undo + - saisie notes: validation +- ticket #18: +UE capitalisĂ©es: donc dispense possible dans semestre redoublĂ©. Traitable en n'inscrivant pas l'etudiant aux modules de cette UE: faire interface utilisateur. + +PrĂ©voir d'entrer une UE capitalisĂ©e avec sa note, date d'obtention et un commentaire. Coupler avec la dĂ©sincription aux modules (si l'Ă©tudiant a Ă©tĂ© inscrit avec ses condisciples). + + + - Ticket #4: Afin d'Ă©viter les doublons, vĂ©rifier qu'il n'existe pas d'homonyme proche lors de la crĂ©ation manuelle d'un Ă©tudiant. (confirmĂ© en ScoDoc 6, vĂ©rifier aussi les imports Excel) + + - Ticket #74: Il est possible d'inscrire un Ă©tudiant sans prĂ©nom par un import excel !!! + + - Ticket #64: saisir les absences pour la promo entiere (et pas par groupe). Des fois, je fais signer une feuille de presence en amphi a partir de la liste de tous les etudiants. Ensuite pour reporter les absents par groupe, c'est galere. + + - Ticket #62: Lors des exports Excel, le format des cellules n'est pas reconnu comme numĂ©rique sous Windows (pas de problèmes avec Macintosh et Linux). + +A confirmer et corriger. + + - Ticket #75: On peut modifier une dĂ©cision de jury (et les autorisations de passage associĂ©es), mais pas la supprimer purement et simplement. +Ajoute ce choix dans les "dĂ©cisions manuelles". + + - Ticket #37: Page recap notes moyennes par groupes +Construire une page avec les moyennes dans chaque UE ou module par groupe d'Ă©tudiants. +Et aussi pourquoi pas ventiler par type de bac, sexe, parcours (nombre de semestre de parcours) ? +redemandĂ© par CJ: Ă  faire avant mai 2008 ! + + - Ticket #75: Synchro ApogĂ©e: choisir les etudiants +Sur la page de syncho ApogĂ©e (formsemestre_synchro_etuds), on peut choisir (cocher) les Ă©tudiants ApogĂ©e Ă  importer. mais on ne peut pas le faire s'ils sont dĂ©jĂ  dans ScoDoc: il faudrait ajouter des checkboxes dans toutes les listes. + + - Ticket #9: Format des valeurs de marges des bulletins. +formsemestre_pagebulletin_dialog: marges en mm: accepter "2,5" et "2.5" et valider correctement le form ! + + - Ticket #17: Suppression modules dans semestres +formsemestre_editwithmodules: confirmer suppression modules + + - Ticket #29: changer le stoquage des photos, garder une version HD. + + - bencher NotesTable sans calcul de moyennes. Etudier un cache des moyennes de modules. + - listes d'utilisateurs (modules): remplacer menus par champs texte + completions javascript + - documenter archives sur Wiki + - verifier paquet Debian pour font pdf (reportab: helvetica ... plante si font indisponible) + - chercher comment obtenir une page d'erreur correcte pour les pages POST + (eg: si le font n'existe pas, archive semestre echoue sans page d'erreur) + ? je ne crois pas que le POST soit en cause. HTTP status=500 + ne se produit pas avec Safari + - essayer avec IE / Win98 + - faire apparaitre les diplĂ´mĂ©s sur le graphe des parcours + - dĂ©mission: formulaire: vĂ©rifier que la date est bien dans le semestre + + + graphe parcours: aligner en colonnes selon les dates (de fin), placer les diplomes + dans la mĂŞme colone que le semestre terminal. + + - modif gestion utilisateurs (donner droits en fct du dept. d'appartenance, bug #57) + - modif form def. utilisateur (dept appartenance) + - utilisateurs: source externe + - archivage des semestres + + + o-------------------------------------o + +* Nouvelle gestion utilisateurs: + objectif: dissocier l'authentification de la notion "d'enseignant" + On a une source externe "d'utilisateurs" (annuaire LDAP ou base SQL) + qui permet seulement de: + - authentifier un utilisateur (login, passwd) + - lister un utilisateur: login => firstname, lastname, email + - lister les utilisateurs + + et une base interne ScoDoc "d'acteurs" (enseignants, administratifs). + Chaque acteur est dĂ©fini par: + - actor_id, firstname, lastname + date_creation, date_expiration, + roles, departement, + email (+flag indiquant s'il faut utiliser ce mail ou celui de + l'utilisateur ?) + state (on, off) (pour desactiver avant expiration ?) + user_id (login) => lien avec base utilisateur + + On offrira une source d'utilisateurs SQL (base partagĂ©e par tous les dept. + d'une instance ScoDoc), mais dans la plupart des cas les gens utiliseront + un annuaire LDAP. + + La base d'acteurs remplace ScoUsers. Les objets ScoDoc (semestres, + modules etc) font rĂ©fĂ©rence Ă  des acteurs (eg responsable_id est un actor_id). + + Le lien entre les deux ? + Loger un utilisateur => authentification utilisateur + association d'un acteur + Cela doit se faire au niveau d'un UserFolder Zope, pour avoir les + bons rĂ´les et le contrĂ´le d'accès adĂ©quat. + (Il faut donc coder notre propre UserFolder). + On ne peut associer qu'un acteur Ă  l'Ă©tat 'on' et non expirĂ©. + + OpĂ©rations ScoDoc: + - paramĂ©trage: choisir et paramĂ©trer source utilisateurs + - ajouter utilisateur: choisir un utilisateur dans la liste + et lui associer un nouvel acteur (choix des rĂ´les, des dates) + + Ă©ventuellement: synchro d'un ensemble d'utilisateurs, basĂ© sur + une requĂŞte (eg LDAP) prĂ©cise (quelle interface utilisateur proposer ?) + + - rĂ©gulièrement (cron) aviser quelqu'un (le chef) de l'expiration des acteurs. + - changer etat d'un acteur (on/off) + + + o-------------------------------------o + diff --git a/TrivialFormulator.py b/TrivialFormulator.py new file mode 100644 index 00000000..788d95da --- /dev/null +++ b/TrivialFormulator.py @@ -0,0 +1,769 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + + +"""Simple form generator/validator + + E. Viennet 2005 - 2008 + + v 1.2 +""" + +from types import * + + +def TrivialFormulator( + form_url, + values, + formdescription=(), + initvalues={}, + method="post", + enctype=None, + submitlabel="OK", + name=None, + formid="tf", + cssclass="", + cancelbutton=None, + submitbutton=True, + submitbuttonattributes=[], + top_buttons=False, # place buttons at top of form + bottom_buttons=True, # buttons after form + html_foot_markup="", + readonly=False, + is_submitted=False, +): + """ + form_url : URL for this form + initvalues : dict giving default values + values : dict with all HTML form variables (may start empty) + is_submitted: handle form as if already submitted + + Returns (status, HTML form, values) + status = 0 (html to display), + 1 (ok, validated values in "values") + -1 cancel (if cancelbutton specified) + HTML form: html string (form to insert in your web page) + values: None or, when the form is submitted and correctly filled, + a dictionnary with the requeted values. + formdescription: sequence [ (field, description), ... ] + where description is a dict with following (optional) keys: + default : default value for this field ('') + title : text titre (default to field name) + allow_null : if true, field can be left empty (default true) + type : 'string', 'int', 'float' (default to string), 'list' (only for hidden) + readonly : default False. if True, no form element, display current value. + convert_numbers: covert int and float values (from string) + allowed_values : list of possible values (default: any value) + validator : function validating the field (called with (value,field)). + min_value : minimum value (for floats and ints) + max_value : maximum value (for floats and ints) + explanation: text string to display next the input widget + title_buble: help bubble on field title (needs bubble.js or equivalent) + comment : comment, showed under input widget + withcheckbox: if true, place a checkbox at the left of the input + elem. Checked items will be returned in 'tf-checked' + attributes: a liste of strings to put in the HTML form element + template: HTML template for element + HTML elements: + input_type : 'text', 'textarea', 'password', + 'radio', 'menu', 'checkbox', + 'hidden', 'separator', 'file', 'date', 'boolcheckbox', + 'text_suggest' + (default text) + size : text field width + rows, cols: textarea geometry + labels : labels for radio or menu lists (associated to allowed_values) + vertical: for checkbox; if true, vertical layout + disabled_items: for checkbox, dict such that disabled_items[i] true if disabled checkbox + To use text_suggest elements, one must: + - specify options in text_suggest_options (a dict) + - HTML page must load JS AutoSuggest.js and CSS autosuggest_inquisitor.css + - bodyOnLoad must call JS function init_tf_form(formid) + """ + method = method.lower() + if method == "get": + enctype = None + t = TF( + form_url, + values, + formdescription, + initvalues, + method, + enctype, + submitlabel, + name, + formid, + cssclass, + cancelbutton=cancelbutton, + submitbutton=submitbutton, + submitbuttonattributes=submitbuttonattributes, + top_buttons=top_buttons, + bottom_buttons=bottom_buttons, + html_foot_markup=html_foot_markup, + readonly=readonly, + is_submitted=is_submitted, + ) + form = t.getform() + if t.canceled(): + res = -1 + elif t.submitted() and t.result: + res = 1 + else: + res = 0 + return res, form, t.result + + +class TF: + def __init__( + self, + form_url, + values, + formdescription=[], + initvalues={}, + method="POST", + enctype=None, + submitlabel="OK", + name=None, + formid="tf", + cssclass="", + cancelbutton=None, + submitbutton=True, + submitbuttonattributes=[], + top_buttons=False, # place buttons at top of form + bottom_buttons=True, # buttons after form + html_foot_markup="", # html snippet put at the end, just after the table + readonly=False, + is_submitted=False, + ): + self.form_url = form_url + self.values = values + self.formdescription = list(formdescription) + self.initvalues = initvalues + self.method = method + self.enctype = enctype + self.submitlabel = submitlabel + if name: + self.name = name + else: + self.name = formid # 'tf' + self.formid = formid + self.cssclass = cssclass + self.cancelbutton = cancelbutton + self.submitbutton = submitbutton + self.submitbuttonattributes = submitbuttonattributes + self.top_buttons = top_buttons + self.bottom_buttons = bottom_buttons + self.html_foot_markup = html_foot_markup + self.readonly = readonly + self.result = None + self.is_submitted = is_submitted + if readonly: + self.top_buttons = self.bottom_buttons = False + self.cssclass += " readonly" + + def submitted(self): + "true if form has been submitted" + if self.is_submitted: + return True + return self.values.get("%s-submitted" % self.formid, False) + + def canceled(self): + "true if form has been canceled" + return self.values.get("%s_cancel" % self.formid, False) + + def getform(self): + "return HTML form" + R = [] + msg = None + self.setdefaultvalues() + if self.submitted() and not self.readonly: + msg = self.checkvalues() + # display error message + R.append(tf_error_message(msg)) + # form or view + if self.readonly: + R = R + self._ReadOnlyVersion(self.formdescription) + else: + R = R + self._GenForm() + # + return "\n".join(R) + + __str__ = getform + __repr__ = getform + + def setdefaultvalues(self): + "set default values and convert numbers to strings" + for (field, descr) in self.formdescription: + # special case for boolcheckbox + if descr.get("input_type", None) == "boolcheckbox" and self.submitted(): + if not self.values.has_key(field): + self.values[field] = 0 + else: + self.values[field] = 1 + if not self.values.has_key(field): + if descr.has_key("default"): # first: default in form description + self.values[field] = descr["default"] + else: # then: use initvalues dict + self.values[field] = self.initvalues.get(field, "") + if self.values[field] == None: + self.values[field] = "" + + # convert numbers + if type(self.values[field]) == type(1) or type(self.values[field]) == type( + 1.0 + ): + self.values[field] = str(self.values[field]) + # + if not self.values.has_key("tf-checked"): + if self.submitted(): + # si rien n'est cochĂ©, tf-checked n'existe plus dans la reponse + self.values["tf-checked"] = [] + else: + self.values["tf-checked"] = self.initvalues.get("tf-checked", []) + self.values["tf-checked"] = [str(x) for x in self.values["tf-checked"]] + + def checkvalues(self): + "check values. Store .result and returns msg" + ok = 1 + msg = [] + for (field, descr) in self.formdescription: + val = self.values[field] + # do not check "unckecked" items + if descr.get("withcheckbox", False): + if not field in self.values["tf-checked"]: + continue + # null values + allow_null = descr.get("allow_null", True) + if not allow_null: + if val == "" or val == None: + msg.append( + "Le champ '%s' doit ĂŞtre renseignĂ©" % descr.get("title", field) + ) + ok = 0 + # type + typ = descr.get("type", "string") + if val != "" and val != None: + # check only non-null values + if typ[:3] == "int": + try: + val = int(val) + self.values[field] = val + except: + msg.append( + "La valeur du champ '%s' doit ĂŞtre un nombre entier" % field + ) + ok = 0 + elif typ == "float" or typ == "real": + self.values[field] = self.values[field].replace(",", ".") + try: + val = float(val.replace(",", ".")) # allow , + self.values[field] = val + except: + msg.append( + "La valeur du champ '%s' doit ĂŞtre un nombre" % field + ) + ok = 0 + if typ[:3] == "int" or typ == "float" or typ == "real": + if descr.has_key("min_value") and val < descr["min_value"]: + msg.append( + "La valeur (%d) du champ '%s' est trop petite (min=%s)" + % (val, field, descr["min_value"]) + ) + ok = 0 + + if descr.has_key("max_value") and val > descr["max_value"]: + msg.append( + "La valeur (%s) du champ '%s' est trop grande (max=%s)" + % (val, field, descr["max_value"]) + ) + ok = 0 + + # allowed values + if descr.has_key("allowed_values"): + if descr.get("input_type", None) == "checkbox": + # for checkboxes, val is a list + for v in val: + if not v in descr["allowed_values"]: + msg.append( + "valeur invalide (%s) pour le champ '%s'" % (val, field) + ) + ok = 0 + elif descr.get("input_type", None) == "boolcheckbox": + pass + elif not val in descr["allowed_values"]: + msg.append("valeur invalide (%s) pour le champ '%s'" % (val, field)) + ok = 0 + if descr.has_key("validator"): + if not descr["validator"](val, field): + msg.append("valeur invalide (%s) pour le champ '%s'" % (val, field)) + ok = 0 + # boolean checkbox + if descr.get("input_type", None) == "boolcheckbox": + if int(val): + self.values[field] = 1 + else: + self.values[field] = 0 + # open('/tmp/toto','a').write('checkvalues: val=%s (%s) values[%s] = %s\n' % (val, type(val), field, self.values[field])) + if descr.get("convert_numbers", False): + if typ[:3] == "int": + self.values[field] = int(self.values[field]) + elif typ == "float" or typ == "real": + self.values[field] = float(self.values[field].replace(",", ".")) + if ok: + self.result = self.values + else: + self.result = None + return msg + + def _GenForm(self, method="", enctype=None, form_url=""): + values = self.values + add_no_enter_js = False # add JS function to prevent 'enter' -> submit + # form template + + # default template for each input element + itemtemplate = """ + %(label)s%(elem)s + + """ + hiddenitemtemplate = "%(elem)s" + separatortemplate = '%(label)s' + # ---- build form + buttons_markup = "" + if self.submitbutton: + buttons_markup += ( + '' + % ( + self.formid, + self.formid, + self.submitlabel, + " ".join(self.submitbuttonattributes), + ) + ) + if self.cancelbutton: + buttons_markup += ( + ' ' + % (self.formid, self.formid, self.cancelbutton) + ) + + R = [] + suggest_js = [] + if self.enctype is None: + if self.method == "post": + enctype = "multipart/form-data" + else: + enctype = "application/x-www-form-urlencoded" + if self.cssclass: + klass = ' class="%s"' % self.cssclass + else: + klass = "" + name = self.name + R.append( + '
' + % (self.form_url, self.method, self.formid, enctype, name, klass) + ) + R.append('' % self.formid) + if self.top_buttons: + R.append(buttons_markup + "

") + R.append('') + idx = 0 + for idx in range(len(self.formdescription)): + (field, descr) = self.formdescription[idx] + nextitemname = None + if idx < len(self.formdescription) - 2: + nextitemname = self.formdescription[idx + 1][0] + if descr.get("readonly", False): + R.append(self._ReadOnlyElement(field, descr)) + continue + wid = self.name + "_" + field + size = descr.get("size", 12) + rows = descr.get("rows", 5) + cols = descr.get("cols", 60) + title = descr.get("title", field.capitalize()) + title_bubble = descr.get("title_bubble", None) + withcheckbox = descr.get("withcheckbox", False) + input_type = descr.get("input_type", "text") + item_dom_id = descr.get("dom_id", "") + if item_dom_id: + item_dom_attr = ' id="%s"' % item_dom_id + else: + item_dom_attr = "" + # choix du template + etempl = descr.get("template", None) + if etempl is None: + if input_type == "hidden": + etempl = hiddenitemtemplate + elif input_type == "separator": + etempl = separatortemplate + R.append(etempl % {"label": title, "item_dom_attr": item_dom_attr}) + continue + else: + etempl = itemtemplate + lab = [] + lem = [] + if withcheckbox and input_type != "hidden": + if field in values["tf-checked"]: + checked = 'checked="checked"' + else: + checked = "" + lab.append( + '' + % ("tf-checked", field, checked) + ) + if title_bubble: + lab.append( + '%s' + % (title_bubble, title) + ) + else: + lab.append(title) + # + attribs = " ".join(descr.get("attributes", [])) + if ( + withcheckbox and not checked + ) or not descr.get( # desactive les element non coches: + "enabled", True + ): + attribs += ' disabled="true"' + # + if input_type == "text": + lem.append( + '') % values) + elif input_type == "password": + lem.append( + '') % values) + elif input_type == "radio": + labels = descr.get("labels", descr["allowed_values"]) + for i in range(len(labels)): + if descr["allowed_values"][i] == values[field]: + checked = 'checked="checked"' + else: + checked = "" + lem.append( + '%s' + % ( + field, + descr["allowed_values"][i], + checked, + attribs, + labels[i], + ) + ) + elif input_type == "menu": + lem.append('") + elif input_type == "checkbox" or input_type == "boolcheckbox": + if input_type == "checkbox": + labels = descr.get("labels", descr["allowed_values"]) + else: # boolcheckbox + labels = [""] + descr["allowed_values"] = ["0", "1"] + vertical = descr.get("vertical", False) + disabled_items = descr.get("disabled_items", {}) + if vertical: + lem.append("
") + for i in range(len(labels)): + if input_type == "checkbox": + # from notes_log import log # debug only + # log('checkbox: values[%s] = "%s"' % (field,repr(values[field]) )) + # log("descr['allowed_values'][%s] = '%s'" % (i, repr(descr['allowed_values'][i]))) + if descr["allowed_values"][i] in values[field]: + checked = 'checked="checked"' + else: + checked = "" + else: # boolcheckbox + # open('/tmp/toto','a').write('GenForm: values[%s] = %s (%s)\n' % (field, values[field], type(values[field]))) + try: + v = int(values[field]) + except: + v = 0 + if v: + checked = 'checked="checked"' + else: + checked = "" + if vertical: + lem.append("") + if vertical: + lem.append("
") + if disabled_items.get(i, False): + disab = 'disabled="1"' + ilab = ( + '' + + labels[i] + + " (non modifiable)" + ) + else: + disab = "" + ilab = "" + labels[i] + "" + lem.append( + '%s' + % ( + field, + descr["allowed_values"][i], + attribs, + disab, + checked, + ilab, + ) + ) + if vertical: + lem.append("
") + elif input_type == "textarea": + lem.append( + '' + % (field, wid, rows, cols, attribs, values[field]) + ) + elif input_type == "hidden": + if descr.get("type", "") == "list": + for v in values[field]: + lem.append( + '' + % (field, v, attribs) + ) + else: + lem.append( + '' + % (field, wid, values[field], attribs) + ) + elif input_type == "separator": + pass + elif input_type == "file": + lem.append( + '' + % (field, size, values[field], attribs) + ) + elif input_type == "date": # JavaScript widget for date input + lem.append( + '' + % (field, values[field]) + ) + elif input_type == "text_suggest": + lem.append( + '') % values) + suggest_js.append( + """var %s_opts = %s; + var %s_as = new bsn.AutoSuggest('%s', %s_opts); + """ + % ( + field, + dict2js(descr.get("text_suggest_options", {})), + field, + field, + field, + ) + ) + else: + raise ValueError("unkown input_type for form (%s)!" % input_type) + explanation = descr.get("explanation", "") + if explanation: + lem.append('%s' % explanation) + comment = descr.get("comment", "") + if comment: + lem.append('
%s' % comment) + R.append( + etempl + % { + "label": "\n".join(lab), + "elem": "\n".join(lem), + "item_dom_attr": item_dom_attr, + } + ) + R.append("") + + R.append(self.html_foot_markup) + + if self.bottom_buttons: + R.append("
" + buttons_markup) + + if add_no_enter_js: + R.append( + """ + """ + ) # enter_focus_next, ne focus que les champs text + if suggest_js: + # nota: formid is currently ignored + # => only one form with text_suggest field on a page. + R.append( + """""" + % "\n".join(suggest_js) + ) + # Javascript common to all forms: + R.append( + """""" + ) + R.append("
") + return R + + def _ReadOnlyElement(self, field, descr): + "Generate HTML for an element, read-only" + R = [] + title = descr.get("title", field.capitalize()) + withcheckbox = descr.get("withcheckbox", False) + input_type = descr.get("input_type", "text") + klass = descr.get("cssclass", "") + klass = " " + klass + if input_type == "hidden": + return "" + + R.append('' % klass) + + if input_type == "separator": # separator + R.append('%s' % title) + else: + R.append('' % klass) + R.append("%s" % title) + R.append('' % klass) + + if input_type == "text" or input_type == "text_suggest": + R.append(("%(" + field + ")s") % self.values) + elif input_type in ("radio", "menu", "checkbox", "boolcheckbox"): + if input_type == "boolcheckbox": + labels = descr.get( + "labels", descr.get("allowed_values", ["oui", "non"]) + ) + # XXX open('/tmp/log', 'w').write('%s labels=%s, val=%s\ndescr=%s\n'%(field, labels, self.values[field], descr)) + R.append(labels[int(self.values[field])]) + if int(self.values[field]): + R.append('' % field) + else: + labels = descr.get("labels", descr["allowed_values"]) + for i in range(len(labels)): + if str(descr["allowed_values"][i]) == str(self.values[field]): + R.append('%s' % labels[i]) + elif input_type == "textarea": + R.append('
%s
' % self.values[field]) + elif input_type == "separator" or input_type == "hidden": + pass + elif input_type == "file": + R.append("'%s'" % self.values[field]) + else: + raise ValueError("unkown input_type for form (%s)!" % input_type) + + explanation = descr.get("explanation", "") + if explanation: + R.append('%s' % explanation) + + R.append("") + + return "\n".join(R) + + def _ReadOnlyVersion(self, formdescription): + "Generate HTML for read-only view of the form" + R = [''] + for (field, descr) in formdescription: + R.append(self._ReadOnlyElement(field, descr)) + R.append("
") + return R + + +def dict2js(d): + """convert Python dict to JS code""" + r = [] + for k in d: + v = d[k] + if type(v) == BooleanType: + if v: + v = "true" + else: + v = "false" + elif type(v) == StringType: + v = '"' + v + '"' + + r.append("%s: %s" % (k, v)) + return "{" + ",\n".join(r) + "}" + + +def tf_error_message(msg): + """html for form error message""" + if not msg: + return "" + if type(msg) == StringType: + msg = [msg] + return ( + '
  • %s
' + % '
  • '.join(msg) + ) diff --git a/VERSION.py b/VERSION.py new file mode 100644 index 00000000..853ccfbb --- /dev/null +++ b/VERSION.py @@ -0,0 +1,349 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +SCOVERSION = "7.18" + +SCONAME = "ScoDoc" + +SCONEWS = """ +

    Année 2020

    +
      +
    • Nouveau site scodoc.org pour la documentation.
    • +
    • Enregistrement de semestres extĂ©rieurs
    • +
    • AmĂ©liorations PV de Jury
    • +
    • Contributions J.-M. Place: aide au diagnostic problèmes export ApogĂ©e +
    • +
    +

    Année 2019

    +
      +
    • Support Linux Debian 10
    • +
    • Petites amĂ©liorations: import groupes, droits de suppression notes pour vacataires, etc.
    • +
    • Exports listes pour Moodle
    • +
    • Fonction facilitant l'envoi de donnĂ©es pour l'assistance technique
    • +
    +

    Année 2018

    +
      +
    • Affichage date finalisation inscription ApogĂ©e
    • +
    • Co-responsables de semestres
    • +
    • AmĂ©lioration page d'accueil dĂ©partement
    • +
    • Corrections diverses et petites amĂ©liorations
    • +
    • Avis de poursuites d'Ă©tudes plus robustes et configurables
    • +
    +

    Année 2017

    +
      +
    • Bonus/Malus sur les moyennes d'UE
    • +
    • Enregistrement des informations sur le devenir de l'Ă©tudiant
    • +
    • Export global des rĂ©sultats d'un dĂ©partement (utile pour les rapports d'Ă©valuation)
    • +
    • CompatibilitĂ© Linux Debian 9, et modernisation de certains composants
    • +
    • GĂ©nĂ©ration des avis de poursuite d'Ă©tudes
    • +
    • Toilettage page liste programme pĂ©dagogiques
    • +
    • AmĂ©lioration exports rĂ©sultats vers ApogĂ©e
    • +
    • AmĂ©lioration calcul des ECTS
    • +
    • PossibilitĂ© d'utilisation des adresses mail personnelles des Ă©tudiant
    • +
    • Corrections diverses
    • +
    + +

    Année 2016

    +
      +
    • Import des donnĂ©es d'admissions depuis fichiers APB ou autres
    • +
    • Nouveau formulaire saisie des notes
    • +
    • Export des rĂ©sultats vers ApogĂ©e pour un ensemble de semestre
    • +
    • Enregistrement du classement lors de l'admission
    • +
    • Modification du calcul des coefficients des UE capitalisĂ©es
    • +
    + +

    Année 2015

    +
      +
    • Exports fichiers ApogĂ©e
    • +
    • Recherche Ă©tudiants sur plusieurs dĂ©partements
    • +
    • Corrections diverses
    • +
    + + +

    Année 2014

    +
      +
    • Nouvelle interface pour listes groupes, photos, feuilles d'Ă©margement.
    • +
    +

    Année 2013

    +
      +
    • Modernisation de nombreux composants logiciels (ScoDoc 7)
    • +
    • Saisie des absences par matières
    • +
    +

    Année 2012

    +
      +
    • Table lycĂ©es d'origine avec carte google
    • +
    • AmĂ©lioration des PV de jury (logos, ...)
    • +
    • AccĂ©lĂ©ration du code de calcul des semestres
    • +
    • Changement documentation en ligne (nouveau site web)
    • +
    + + +

    Année 2011

    +
      +
    • AmĂ©lioration de la prĂ©sentation des bulletins de notes, et possibilitĂ© de dĂ©finir de nouveaux formats
    • +
    • PossibilitĂ© de modifier les moyennes d'UE via un "bonus" (sport/culture)
    • +
    • Ajout parcours spĂ©cifique pour UCAC (Cameroun)
    • +
    • PossibilitĂ© d'indiquer des mentions sur les PV
    • +
    • Evaluations de "rattrapage"
    • +
    • Support pour installation en Linux Debian "Squeeze"
    • +
    • Corrections diverses
    • +
    + + +

    Novembre 2010

    +
      +
    • PossibilitĂ© d'indiquer des Ă©valuations avec publication immĂ©diate des notes (mĂŞme si incomplètes)
    • +
    + +

    Octobre 2010

    +
      +
    • Nouvelle API JSON
    • +
    • PossibilitĂ© d'associer 2 Ă©tapes ApogĂ©e au mĂŞme semestre
    • +
    • Table "poursuite Ă©tudes"
    • +
    • PossibilitĂ© d'envoyer un mail auto aux Ă©tudiants absentĂ©istes
    • +
    + +

    Août 2010

    +
      +
    • DĂ©finitions de parcours (DUT, LP, ...) avec prise en compte des spĂ©cificitĂ©s (par ex., certaines barres d'UE diffĂ©rentes en LP)
    • +
    + +

    Avril - Juin 2010

    +
      +
    • Formules utilisateur pour le calcul des moyennes d'UE
    • +
    • Nouveau système de notification des absences par mail
    • +
    • Affichage optionnel des valeurs mini et maxi des moyennes sur les bulletins
    • +
    • Nouveau code de dĂ©cision jury semestre: "RAT" : en attente de rattrapage
    • +
    + +

    Janvier 2010

    +
      +
    • Suivez l'actualitĂ© du dĂ©veloppement sur Twitter: @ScoDoc
    • +
    • Nouveau menu "Groupes" pour faciliter la prise en main
    • +
    • PossibilitĂ© de dĂ©finir des règles ad hoc de calcul des moyennes de modules (formules)
    • +
    • PossibilitĂ© d'inclure des images (logos) dans les bulletins PDF
    • +
    • Bandeau "provisoire" sur les bulletins en cours de semestre
    • +
    • Possibilite de valider (capitaliser) une UE passee hors ScoDoc
    • +
    • Amelioration de l'Ă©dition des programmes (formations)
    • +
    • Nombreuses amĂ©liorations mineures
    • +
    + +

    Novembre 2009

    +
      +
    • Gestion des partitions et groupes en nombres quelconques
    • +
    • Nouvelle gestion des photos
    • +
    • Imports d'Ă©tudiants excel incrĂ©mentaux
    • +
    • Optimisations et petites amĂ©liorations
    • +
    + +

    Septembre 2009

    +
      +
    • Traitement de "billets d'absences" (saisis par les Ă©tudiants sur le portail)
    • +
    + +

    Juin 2009

    +
      +
    • Nouveau système plus flexibles de gestion des prĂ©fĂ©rences (ou "paramètres")
    • +
    • PossiblitĂ© d'associer une nouvelle version de programme Ă  un semestre
    • +
    • Corrections et amĂ©liorations diverses +
    + +

    Juillet 2008: version 6.0

    +
      +
    • Installeur automatisĂ© pour Linux
    • +
    • AmĂ©lioration ergonomie (barre menu pages semestres)
    • +
    • Refonte fiche Ă©tudiant (parcours)
    • +
    • Archivage des documents (PV)
    • +
    • Nouvel affichage des notes des Ă©valuations
    • +
    • Nombreuses corrections et amĂ©liorations
    • +
    + +

    Juin 2008

    +
      +
    • Rangs sur les bulletins
    • +
    + +

    FĂ©vrier 2008

    +
      +
    • Statistiques et suivis de cohortes (chiffres et graphes)
    • +
    • Nombreuses petites corrections suites aux jurys de janvier
    • +
    + +

    Janvier 2008

    +
      +
    • Personnalisation des rĂ©gles de calculs notes d'option (sport, culture)
    • +
    • Edition de PV de jury individuel
    • +
    + +

    Novembre 2007

    +
      +
    • VĂ©rification des absences aux Ă©valuations
    • +
    • Import des photos depuis portail, trombinoscopes en PDF
    • +
    + +

    Septembre 2007

    +
      +
    • Importation des etudiants depuis Ă©tapes ApogĂ©e
    • +
    • Inscription de groupes Ă  des modules (options ou parcours)
    • +
    • Listes de Ă©tapes ApogĂ©e (importĂ©es du portail)
    • +
    + +

    Juillet 2007

    +
      +
    • Import utilisateurs depuis Excel
    • +
    • Nouvelle gestion des passage d'un semestre Ă  l'autre
    • +
    + +

    Juin 2007: version 5.0

    +
      +
    • Suivi des parcours et règles de dĂ©cision des jurys DUT
    • +
    • Capitalisation des UEs
    • +
    • Edition des PV de jurys et courriers aux Ă©tudiants
    • +
    • Feuilles (excel) pour prĂ©paration jurys
    • +
    • Nombreuses petites amĂ©liorations
    • +
    + +

    Avril 2007

    +
      +
    • Paramètres de mise en page des bulletins en PDF
    • +
    + +

    FĂ©vrier 2007

    + +
      +
    • PossibilitĂ© de ne pas publier les bulletins sur le portail
    • +
    • Gestion des notes "en attente" (publication d'Ă©valuations sans correction de toutes les copies)
    • +
    • AmĂ©lioration formulaire saisie absences, saisie absences par semestre.
    • +
    + +

    Janvier 2007

    +
      +
    • PossibilitĂ© d'initialiser les notes manquantes d'une Ă©valuation
    • +
    • RecupĂ©ration des codes NIP depuis ApogĂ©e
    • +
    • Gestion des compensations inter-semestre DUT (en cours de dĂ©veloppement)
    • +
    • Export trombinoscope en archive zip
    • +
    + +

    Octobre 2006

    +
      +
    • RĂ©organisation des pages d'accueil
    • +
    • Ajout des "nouvelles" (dernières opĂ©rations), avec flux RSS
    • +
    • Import/Export XML des formations, duplication d'une formation (versions)
    • +
    • Bulletins toujours sur une seule feuille (passage Ă  ReportLab 2.0)
    • +
    • Suppression d'un utilisateur +
    +

    Septembre 2006

    +
      +
    • Page pour suppression des groupes.
    • +
    • AmĂ©lioration gestion des utilisateurs
    • +
    • "Verrouillage" des semestres
    • +
    • Liste d'enseignants (chargĂ©s de TD) associĂ©s Ă  un module (et pouvant saisir des notes)
    • +
    • Noms de types de groupes (TD, TP, ...) modifiables
    • +
    • Tableau rudimentaire donnant la rĂ©partition des bacs dans un semestre
    • +
    • AmĂ©lioration mise en page des listes au format excel
    • +
    • Annulation des dĂ©missions
    • +
    + +

    Juillet 2006

    +
      +
    • Dialogue permettant au directeur des Ă©tudes de modifier +les options d'un semestre
    • +
    • Option pour ne pas afficher les UE validĂ©es sur les bulletins
    • +
    + +

    30 juin 2006

    +
      +
    • Option pour ne pas afficher les dĂ©cisions sur les bulletins
    • +
    • GĂ©nĂ©ration feuilles pour prĂ©paration jury
    • +
    • Gestion des modules optionnels
    • +
    • Prise en compte note "activitĂ©s culturelles ou sportives"
    • +
    • AmĂ©lioration tableau de bord semestre
    • +
    • Import listes Ă©tudiants depuis Excel (avec code ApogĂ©e)
    • +
    + +

    12 juin 2006

    +
      +
    • Formulaire dynamique d'affectation aux groupes
    • +
    • Tri des tableaux (listes, rĂ©capitulatif)
    • +
    • Export XML des infos sur un etudiant et des groupes
    • +
    + +

    12 mai 2006

    +
      +
    • PossibilitĂ© de suppression d'un semestre
    • +
    • Export XML du tableau recapitulatif des notes du semestre
    • +
    • PossibilitĂ© de supression d'une formation complète
    • +
    + +

    24 avril 2006

    +
      +
    • Export bulletins en XML (expĂ©rimental)
    • +
    • Flag "gestion_absence" sur les semestres de formation
    • +
    + +

    4 mars 2006

    +
      +
    • Formulaire d'inscription au semestre suivant.
    • +
    • Format "nombre" dans les feuilles excel exportĂ©es.
    • +
    + +

    23 février 2006

    +
      +
    • DĂ©cisions jury sur bulletins.
    • +
    + +

    17 janvier 2006

    +
      +
    • Ajout et Ă©dition d'apprĂ©ciations sur les bulletins.
    • +
    +

    12 janvier 2006

    +
      +
    • Envoi des bulletins en PDF par mail aux Ă©tudiants.
    • +
    + +

    6 janvier 2006

    +
      +
    • Affichage des ex-aequos.
    • +
    • Classeurs bulletins PDF en diffĂ©rentes versions.
    • +
    • CorrigĂ© gestion des notes des dĂ©missionnaires.
    • +
    + +

    1er janvier 2006

    +
      +
    • Import du projet dans Subversion / LIPN.
    • +
    • Lecture des feuilles de notes Excel.
    • +
    + +

    31 décembre 2005

    +
      +
    • Listes gĂ©nĂ©rĂ©es au format Excel au lieu de CSV.
    • +
    • Bug fix (crĂ©ation/saisie evals).
    • +
    + +

    29 décembre 2005

    +
      +
    • Affichage des moyennes de chaque groupe dans tableau de bord module. +
    + +

    26 décembre 2005

    +
      +
    • RĂ©vision inscription/Ă©dition individuelle d'Ă©tudiants.
    • +
    • AmĂ©lioration fiche Ă©tudiant (cosmĂ©tique, liste formations, actions).
    • +
    • Listings notes d'Ă©valuations anonymes (utilitĂ© douteuse ?).
    • +
    • AmĂ©lioration formulaire saisie notes ('enter' -> champ suivant).
    • +
    + +

    24 décembre 2005

    +
      +
    • GĂ©nĂ©ration de bulletins PDF +
    • +
    • Suppression de notes (permet donc de supprimer une Ă©valuation) +
    • +
    • Bulletins en versions courtes (seulement moyennes de chaque module), longues +(toutes les notes) et intermĂ©diaire (moyenne de chaque module plus notes dans les Ă©valuations sĂ©lectionnĂ©es). +
    • +
    • Notes moyennes sous les barres en rouge dans le tableau rĂ©capitulatif (seuil=10 sur la moyenne gĂ©nĂ©rale, et 8 sur chaque UE). +
    • +
    • Colonne "groupe de TD" dans le tableau rĂ©capitulatif des notes. +
    +""" diff --git a/ZAbsences.py b/ZAbsences.py new file mode 100644 index 00000000..1bdd11b7 --- /dev/null +++ b/ZAbsences.py @@ -0,0 +1,2516 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +""" Gestion des absences (v4) + +C'est la partie la plus ancienne de ScoDoc, et elle est à revoir. + +L'API de plus bas niveau est en gros: + + AnnuleAbsencesDatesNoJust(etudid, dates) + CountAbs(etudid, debut, fin, matin=None, moduleimpl_id=None) + CountAbsJust(etudid, debut, fin, matin=None, moduleimpl_id=None) + ListeAbsJust(etudid, datedebut) [pas de fin ?] + ListeAbsNonJust(etudid, datedebut) [pas de fin ?] + ListeJustifs(etudid, datedebut, datefin=None, only_no_abs=True) + + ListeAbsJour(date, am=True, pm=True, is_abs=None, is_just=None) + ListeAbsNonJustJour(date, am=True, pm=True) + + +""" + +import urllib + +from sco_zope import * + +# --------------- + +from notesdb import * +from notes_log import log +from scolog import logdb +from sco_utils import * + +# import notes_users +from TrivialFormulator import TrivialFormulator, TF +from gen_tables import GenTable +import scolars +import sco_formsemestre +import sco_groups +import sco_groups_view +import sco_excel +import sco_abs_notification, sco_abs_views +import sco_compute_moy +import string, re +import time, calendar +from mx.DateTime import DateTime as mxDateTime +from mx.DateTime.ISO import ParseDateTimeUTC + + +def _toboolean(x): + "convert a value to boolean (ensure backward compat with OLD intranet code)" + if type(x) == type(""): + x = x.lower() + if x and x != "false": # backward compat... + return True + else: + return False + + +def MonthNbDays(month, year): + "returns nb of days in month" + if month > 7: + month = month + 1 + if month % 2: + return 31 + elif month == 2: + if calendar.isleap(year): + return 29 + else: + return 28 + else: + return 30 + + +class ddmmyyyy: + """immutable dates""" + + def __init__(self, date=None, fmt="ddmmyyyy", work_saturday=False): + self.work_saturday = work_saturday + if date is None: + return + try: + if fmt == "ddmmyyyy": + self.day, self.month, self.year = string.split(date, "/") + elif fmt == "iso": + self.year, self.month, self.day = string.split(date, "-") + else: + raise ValueError("invalid format spec. (%s)" % fmt) + self.year = string.atoi(self.year) + self.month = string.atoi(self.month) + self.day = string.atoi(self.day) + except: + raise ScoValueError("date invalide: %s" % date) + # accept years YYYY or YY, uses 1970 as pivot + if self.year < 1970: + if self.year > 100: + raise ScoInvalidDateError("Année invalide: %s" % self.year) + if self.year < 70: + self.year = self.year + 2000 + else: + self.year = self.year + 1900 + if self.month < 1 or self.month > 12: + raise ScoInvalidDateError("Mois invalide: %s" % self.month) + + if self.day < 1 or self.day > MonthNbDays(self.month, self.year): + raise ScoInvalidDateError("Jour invalide: %s" % self.day) + + # weekday in 0-6, where 0 is monday + self.weekday = calendar.weekday(self.year, self.month, self.day) + + self.time = time.mktime((self.year, self.month, self.day, 0, 0, 0, 0, 0, 0)) + + def iswork(self): + "returns true if workable day" + if self.work_saturday: + nbdays = 6 + else: + nbdays = 5 + if ( + self.weekday >= 0 and self.weekday < nbdays + ): # monday-friday or monday-saturday + return 1 + else: + return 0 + + def __repr__(self): + return "'%02d/%02d/%04d'" % (self.day, self.month, self.year) + + def __str__(self): + return "%02d/%02d/%04d" % (self.day, self.month, self.year) + + def ISO(self): + "iso8601 representation of the date" + return "%04d-%02d-%02d" % (self.year, self.month, self.day) + + def next(self, days=1): + "date for the next day (nota: may be a non workable day)" + day = self.day + days + month = self.month + year = self.year + + while day > MonthNbDays(month, year): + day = day - MonthNbDays(month, year) + month = month + 1 + if month > 12: + month = 1 + year = year + 1 + return self.__class__( + "%02d/%02d/%04d" % (day, month, year), work_saturday=self.work_saturday + ) + + def prev(self, days=1): + "date for previous day" + day = self.day - days + month = self.month + year = self.year + while day <= 0: + month = month - 1 + if month == 0: + month = 12 + year = year - 1 + day = day + MonthNbDays(month, year) + + return self.__class__( + "%02d/%02d/%04d" % (day, month, year), work_saturday=self.work_saturday + ) + + def next_monday(self): + "date of next monday" + return self.next((7 - self.weekday) % 7) + + def prev_monday(self): + "date of last monday, but on sunday, pick next monday" + if self.weekday == 6: + return self.next_monday() + else: + return self.prev(self.weekday) + + def __cmp__(self, other): + """return a negative integer if self < other, + zero if self == other, a positive integer if self > other""" + return int(self.time - other.time) + + def __hash__(self): + "we are immutable !" + return hash(self.time) ^ hash(str(self)) + + +# d = ddmmyyyy( '21/12/99' ) + + +def YearTable( + context, + year, + events=[], + firstmonth=9, + lastmonth=7, + halfday=0, + dayattributes="", + pad_width=8, +): + """Generate a calendar table + events = list of tuples (date, text, color, href [,halfday]) + where date is a string in ISO format (yyyy-mm-dd) + halfday is boolean (true: morning, false: afternoon) + text = text to put in calendar (must be short, 1-5 cars) (optional) + if halfday, generate 2 cells per day (morning, afternoon) + """ + T = [ + '' + ] + T.append("") + month = firstmonth + while 1: + T.append('") + if month == lastmonth: + break + month = month + 1 + if month > 12: + month = 1 + year = year + 1 + T.append("
    ') + T.append(MonthTableHead(month)) + T.append( + MonthTableBody( + month, + year, + events, + halfday, + dayattributes, + context.is_work_saturday(), + pad_width=pad_width, + ) + ) + T.append(MonthTableTail()) + T.append("
    ") + return string.join(T, "\n") + + +# --------------- + + +class ZAbsences( + ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit +): + + "ZAbsences object" + + meta_type = "ZAbsences" + security = ClassSecurityInfo() + + # 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 + + ({"label": "View", "action": "index_html"},) + + Item.manage_options # add the 'Undo' & 'Owner' tab + + RoleManager.manage_options # add the 'Security' tab + ) + + # no permissions, only called from python + def __init__(self, id, title): + "initialise a new instance" + self.id = id + self.title = title + + # The form used to edit this object + def manage_editZAbsences(self, title, RESPONSE=None): + "Changes the instance values" + self.title = title + self._p_changed = 1 + RESPONSE.redirect("manage_editForm") + + # -------------------------------------------------------------------- + # + # ABSENCES (top level) + # + # -------------------------------------------------------------------- + # used to view content of the object + security.declareProtected(ScoView, "index_html") + index_html = sco_abs_views.absences_index_html + + security.declareProtected(ScoView, "EtatAbsences") + EtatAbsences = sco_abs_views.EtatAbsences + + security.declareProtected(ScoView, "CalAbs") + CalAbs = sco_abs_views.CalAbs + + security.declareProtected(ScoAbsChange, "SignaleAbsenceEtud") + SignaleAbsenceEtud = sco_abs_views.SignaleAbsenceEtud + security.declareProtected(ScoAbsChange, "doSignaleAbsence") + doSignaleAbsence = sco_abs_views.doSignaleAbsence + + security.declareProtected(ScoAbsChange, "JustifAbsenceEtud") + JustifAbsenceEtud = sco_abs_views.JustifAbsenceEtud + security.declareProtected(ScoAbsChange, "doJustifAbsence") + doJustifAbsence = sco_abs_views.doJustifAbsence + + security.declareProtected(ScoAbsChange, "AnnuleAbsenceEtud") + AnnuleAbsenceEtud = sco_abs_views.AnnuleAbsenceEtud + security.declareProtected(ScoAbsChange, "doAnnuleAbsence") + doAnnuleAbsence = sco_abs_views.doAnnuleAbsence + security.declareProtected(ScoAbsChange, "doAnnuleJustif") + doAnnuleJustif = sco_abs_views.doAnnuleJustif + + security.declareProtected(ScoView, "ListeAbsEtud") + ListeAbsEtud = sco_abs_views.ListeAbsEtud + + # -------------------------------------------------------------------- + # + # SQL METHODS + # + # -------------------------------------------------------------------- + + def _AddAbsence( + self, + etudid, + jour, + matin, + estjust, + REQUEST, + description=None, + moduleimpl_id=None, + ): + "Ajoute une absence dans la bd" + # unpublished + if self._isFarFutur(jour): + raise ScoValueError("date absence trop loin dans le futur !") + estjust = _toboolean(estjust) + matin = _toboolean(matin) + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "insert into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) values (%(etudid)s, %(jour)s, TRUE, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s )", + vars(), + ) + logdb( + REQUEST, + cnx, + "AddAbsence", + etudid=etudid, + msg="JOUR=%(jour)s,MATIN=%(matin)s,ESTJUST=%(estjust)s,description=%(description)s,moduleimpl_id=%(moduleimpl_id)s" + % vars(), + ) + cnx.commit() + invalidateAbsEtudDate(self, etudid, jour) + sco_abs_notification.abs_notify(self, etudid, jour) + + def _AddJustif(self, etudid, jour, matin, REQUEST, description=None): + "Ajoute un justificatif dans la base" + # unpublished + if self._isFarFutur(jour): + raise ScoValueError("date justificatif trop loin dans le futur !") + matin = _toboolean(matin) + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )", + vars(), + ) + logdb( + REQUEST, + cnx, + "AddJustif", + etudid=etudid, + msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(), + ) + cnx.commit() + invalidateAbsEtudDate(self, etudid, jour) + + def _AnnuleAbsence(self, etudid, jour, matin, moduleimpl_id=None, REQUEST=None): + """Annule une absence ds base + Si moduleimpl_id, n'annule que pour ce module + """ + # unpublished + matin = _toboolean(matin) + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + req = "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and estabs" + if moduleimpl_id: + req += " and moduleimpl_id=%(moduleimpl_id)s" + cursor.execute(req, vars()) + logdb( + REQUEST, + cnx, + "AnnuleAbsence", + etudid=etudid, + msg="JOUR=%(jour)s,MATIN=%(matin)s,moduleimpl_id=%(moduleimpl_id)s" + % vars(), + ) + cnx.commit() + invalidateAbsEtudDate(self, etudid, jour) + + def _AnnuleJustif(self, etudid, jour, matin, REQUEST=None): + "Annule un justificatif" + # unpublished + matin = _toboolean(matin) + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "delete from absences where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s and ESTJUST AND NOT ESTABS", + vars(), + ) + cursor.execute( + "update absences set estjust=false where jour=%(jour)s and matin=%(matin)s and etudid=%(etudid)s", + vars(), + ) + logdb( + REQUEST, + cnx, + "AnnuleJustif", + etudid=etudid, + msg="JOUR=%(jour)s,MATIN=%(matin)s" % vars(), + ) + cnx.commit() + invalidateAbsEtudDate(self, etudid, jour) + + # Fonction inutile à supprimer (gestion moduleimpl_id incorrecte): + # def _AnnuleAbsencesPeriodNoJust(self, etudid, datedebut, datefin, + # moduleimpl_id=None, REQUEST=None): + # """Supprime les absences entre ces dates (incluses). + # mais ne supprime pas les justificatifs. + # """ + # # unpublished + # cnx = self.GetDBConnexion() + # cursor = cnx.cursor(cursor_factory=ScoDocCursor) + # # supr les absences non justifiees + # cursor.execute("delete from absences where etudid=%(etudid)s and (not estjust) and moduleimpl_id=(moduleimpl_id)s and jour BETWEEN %(datedebut)s AND %(datefin)s", + # vars() ) + # # s'assure que les justificatifs ne sont pas "absents" + # cursor.execute("update absences set estabs=FALSE where etudid=%(etudid)s and jour and moduleimpl_id=(moduleimpl_id)s BETWEEN %(datedebut)s AND %(datefin)s", vars()) + # logdb(REQUEST, cnx, 'AnnuleAbsencesPeriodNoJust', etudid=etudid, + # msg='%(datedebut)s - %(datefin)s - (moduleimpl_id)s'%vars()) + # cnx.commit() + # invalidateAbsEtudDate(self, etudid, datedebut) + # invalidateAbsEtudDate(self, etudid, datefin) # si un semestre commence apres datedebut et termine avant datefin, il ne sera pas invalide. Tant pis ;-) + + security.declareProtected(ScoAbsChange, "AnnuleAbsencesDatesNoJust") + + def AnnuleAbsencesDatesNoJust( + self, etudid, dates, moduleimpl_id=None, REQUEST=None + ): + """Supprime les absences aux dates indiquées + mais ne supprime pas les justificatifs. + """ + # log('AnnuleAbsencesDatesNoJust: moduleimpl_id=%s' % moduleimpl_id) + if not dates: + return + date0 = dates[0] + if len(date0.split(":")) == 2: + # am/pm is present + for date in dates: + jour, ampm = date.split(":") + if ampm == "am": + matin = 1 + elif ampm == "pm": + matin = 0 + else: + raise ValueError("invalid ampm !") + self._AnnuleAbsence(etudid, jour, matin, moduleimpl_id, REQUEST) + return + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + # supr les absences non justifiees + for date in dates: + cursor.execute( + "delete from absences where etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s", + vars(), + ) + invalidateAbsEtudDate(self, etudid, date) + # s'assure que les justificatifs ne sont pas "absents" + for date in dates: + cursor.execute( + "update absences set estabs=FALSE where etudid=%(etudid)s and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s", + vars(), + ) + if dates: + date0 = dates[0] + else: + date0 = None + if len(dates) > 1: + date1 = dates[1] + else: + date1 = None + logdb( + REQUEST, + cnx, + "AnnuleAbsencesDatesNoJust", + etudid=etudid, + msg="%s - %s - %s" % (date0, date1, moduleimpl_id), + ) + cnx.commit() + + security.declareProtected(ScoView, "CountAbs") + + def CountAbs(self, etudid, debut, fin, matin=None, moduleimpl_id=None): + """CountAbs + matin= 1 ou 0. + """ + if matin != None: + matin = _toboolean(matin) + ismatin = " AND A.MATIN = %(matin)s " + else: + ismatin = "" + if moduleimpl_id: + modul = " AND A.MODULEIMPL_ID = %(moduleimpl_id)s " + else: + modul = "" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + """SELECT COUNT(*) AS NbAbs FROM ( + SELECT DISTINCT A.JOUR, A.MATIN + FROM ABSENCES A + WHERE A.ETUDID = %(etudid)s + AND A.ESTABS""" + + ismatin + + modul + + """ + AND A.JOUR BETWEEN %(debut)s AND %(fin)s + ) AS tmp + """, + vars(), + ) + res = cursor.fetchone()[0] + return res + + security.declareProtected(ScoView, "CountAbsJust") + + def CountAbsJust(self, etudid, debut, fin, matin=None, moduleimpl_id=None): + if matin != None: + matin = _toboolean(matin) + ismatin = " AND A.MATIN = %(matin)s " + else: + ismatin = "" + if moduleimpl_id: + modul = " AND A.MODULEIMPL_ID = %(moduleimpl_id)s " + else: + modul = "" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + """SELECT COUNT(*) AS NbAbsJust FROM ( + SELECT DISTINCT A.JOUR, A.MATIN + FROM ABSENCES A, ABSENCES B + WHERE A.ETUDID = %(etudid)s + AND A.ETUDID = B.ETUDID + AND A.JOUR = B.JOUR AND A.MATIN = B.MATIN + AND A.JOUR BETWEEN %(debut)s AND %(fin)s + AND A.ESTABS AND (A.ESTJUST OR B.ESTJUST)""" + + ismatin + + modul + + """ +) AS tmp + """, + vars(), + ) + res = cursor.fetchone()[0] + return res + + def _ListeAbsDate(self, etudid, beg_date, end_date): + # Liste des absences et justifs entre deux dates + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + """SELECT jour, matin, estabs, estjust, description FROM ABSENCES A + WHERE A.ETUDID = %(etudid)s + AND A.jour >= %(beg_date)s + AND A.jour <= %(end_date)s + """, + vars(), + ) + Abs = cursor.dictfetchall() + # log('ListeAbsDate: abs=%s' % Abs) + # remove duplicates + A = {} # { (jour, matin) : abs } + for a in Abs: + jour, matin = a["jour"], a["matin"] + if (jour, matin) in A: + # garde toujours la description + a["description"] = a["description"] or A[(jour, matin)]["description"] + # et la justif: + a["estjust"] = a["estjust"] or A[(jour, matin)]["estjust"] + a["estabs"] = a["estabs"] or A[(jour, matin)]["estabs"] + A[(jour, matin)] = a + else: + A[(jour, matin)] = a + if A[(jour, matin)]["description"] is None: + A[(jour, matin)]["description"] = "" + # add hours: matin = 8:00 - 12:00, apresmidi = 12:00 - 18:00 + dat = "%04d-%02d-%02d" % (a["jour"].year, a["jour"].month, a["jour"].day) + if a["matin"]: + A[(jour, matin)]["begin"] = dat + " 08:00:00" + A[(jour, matin)]["end"] = dat + " 11:59:59" + else: + A[(jour, matin)]["begin"] = dat + " 12:00:00" + A[(jour, matin)]["end"] = dat + " 17:59:59" + # sort + R = A.values() + R.sort(key=lambda x: (x["begin"])) + # log('R=%s' % R) + return R + + security.declareProtected(ScoView, "ListeAbsJust") + + def ListeAbsJust(self, etudid, datedebut): + "Liste des absences justifiees (par ordre chronologique)" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + """SELECT DISTINCT A.ETUDID, A.JOUR, A.MATIN FROM ABSENCES A, ABSENCES B + WHERE A.ETUDID = %(etudid)s + AND A.ETUDID = B.ETUDID + AND A.JOUR = B.JOUR AND A.MATIN = B.MATIN AND A.JOUR >= %(datedebut)s + AND A.ESTABS AND (A.ESTJUST OR B.ESTJUST) + ORDER BY A.JOUR + """, + vars(), + ) + A = cursor.dictfetchall() + for a in A: + a["description"] = self._GetAbsDescription(a, cursor=cursor) + return A + + security.declareProtected(ScoView, "ListeAbsNonJust") + + def ListeAbsNonJust(self, etudid, datedebut): + "Liste des absences NON justifiees (par ordre chronologique)" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + """SELECT ETUDID, JOUR, MATIN FROM ABSENCES A + WHERE A.ETUDID = %(etudid)s + AND A.estabs + AND A.jour >= %(datedebut)s + EXCEPT SELECT ETUDID, JOUR, MATIN FROM ABSENCES B + WHERE B.estjust + AND B.ETUDID = %(etudid)s + ORDER BY JOUR + """, + vars(), + ) + A = cursor.dictfetchall() + for a in A: + a["description"] = self._GetAbsDescription(a, cursor=cursor) + return A + + security.declareProtected(ScoView, "ListeAbsJust") + + def ListeJustifs(self, etudid, datedebut, datefin=None, only_no_abs=False): + """Liste des justificatifs (sans absence relevée) à partir d'une date, + ou, si datefin spécifié, entre deux dates. + Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées. + """ + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + req = """SELECT DISTINCT ETUDID, JOUR, MATIN FROM ABSENCES A + WHERE A.ETUDID = %(etudid)s + AND A.ESTJUST + AND A.JOUR >= %(datedebut)s""" + if datefin: + req += """AND A.JOUR <= %(datefin)s""" + if only_no_abs: + req += """ + EXCEPT SELECT ETUDID, JOUR, MATIN FROM ABSENCES B + WHERE B.estabs + AND B.ETUDID = %(etudid)s + """ + cursor.execute(req, vars()) + A = cursor.dictfetchall() + for a in A: + a["description"] = self._GetAbsDescription(a, cursor=cursor) + + return A + + def _GetAbsDescription(self, a, cursor=None): + "Description associee a l'absence" + if not cursor: + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + a = a.copy() + # a['jour'] = a['jour'].date() + if a["matin"]: # devrait etre booleen... :-( + a["matin"] = True + else: + a["matin"] = False + cursor.execute( + """select * from absences where etudid=%(etudid)s and jour=%(jour)s and matin=%(matin)s order by entry_date desc""", + a, + ) + A = cursor.dictfetchall() + desc = None + module = "" + for a in A: + if a["description"]: + desc = a["description"] + if a["moduleimpl_id"] and a["moduleimpl_id"] != "NULL": + # Trouver le nom du module + Mlist = self.Notes.do_moduleimpl_withmodule_list( + moduleimpl_id=a["moduleimpl_id"] + ) + if Mlist: + M = Mlist[0] + module += "%s " % M["module"]["code"] + + if desc: + return "(%s) %s" % (desc, module) + return desc + if module: + return module + return "" + + security.declareProtected(ScoView, "ListeAbsJour") + + def ListeAbsJour(self, date, am=True, pm=True, is_abs=True, is_just=None): + """Liste des absences et/ou justificatifs ce jour. + is_abs: None (peu importe), True, False + is_just: idem + """ + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A + WHERE A.jour = %(date)s + """ + if is_abs != None: + req += " AND A.estabs = %(is_abs)s" + if is_just != None: + req += " AND A.estjust = %(is_just)s" + if not am: + req += " AND NOT matin " + if not pm: + req += " AND matin" + + cursor.execute(req, {"date": date, "is_just": is_just, "is_abs": is_abs}) + A = cursor.dictfetchall() + for a in A: + a["description"] = self._GetAbsDescription(a, cursor=cursor) + return A + + security.declareProtected(ScoView, "ListeAbsNonJustJour") + + def ListeAbsNonJustJour(self, date, am=True, pm=True): + "Liste des absences non justifiees ce jour" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + reqa = "" + if not am: + reqa += " AND NOT matin " + if not pm: + reqa += " AND matin " + req = ( + """SELECT etudid, jour, matin FROM ABSENCES A + WHERE A.estabs + AND A.jour = %(date)s + """ + + reqa + + """EXCEPT SELECT etudid, jour, matin FROM ABSENCES B + WHERE B.estjust AND B.jour = %(date)s""" + + reqa + ) + + cursor.execute(req, {"date": date}) + A = cursor.dictfetchall() + for a in A: + a["description"] = self._GetAbsDescription(a, cursor=cursor) + return A + + security.declareProtected(ScoAbsChange, "doSignaleAbsenceGrSemestre") + + def doSignaleAbsenceGrSemestre( + self, + moduleimpl_id=None, + abslist=[], + dates="", + etudids="", + destination=None, + REQUEST=None, + ): + """Enregistre absences aux dates indiquees (abslist et dates). + dates est une liste de dates ISO (séparées par des ','). + Efface les absences aux dates indiquées par dates, + ou bien ajoute celles de abslist. + """ + if etudids: + etudids = etudids.split(",") + else: + etudids = [] + if dates: + dates = dates.split(",") + else: + dates = [] + + # 1- Efface les absences + if dates: + for etudid in etudids: + self.AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id, REQUEST) + return "Absences effacées" + + # 2- Ajoute les absences + if abslist: + self._add_abslist(abslist, REQUEST, moduleimpl_id) + return "Absences ajoutées" + + return "" + + def _add_abslist(self, abslist, REQUEST, moduleimpl_id=None): + for a in abslist: + etudid, jour, ampm = a.split(":") + if ampm == "am": + matin = 1 + elif ampm == "pm": + matin = 0 + else: + raise ValueError("invalid ampm !") + # ajoute abs si pas deja absent + if self.CountAbs(etudid, jour, jour, matin, moduleimpl_id) == 0: + self._AddAbsence(etudid, jour, matin, 0, REQUEST, "", moduleimpl_id) + + # + security.declareProtected(ScoView, "CalSelectWeek") + + def CalSelectWeek(self, year=None, REQUEST=None): + "display calendar allowing week selection" + if not year: + year = AnneeScolaire(REQUEST) + sems = sco_formsemestre.do_formsemestre_list(self) + if not sems: + js = "" + else: + js = 'onmouseover="highlightweek(this);" onmouseout="deselectweeks();" onclick="wclick(this);"' + C = YearTable(self, int(year), dayattributes=js) + return C + + # --- Misc tools.... ------------------ + + def _isFarFutur(self, jour): + # check si jour est dans le futur "lointain" + # pour autoriser les saisies dans le futur mais pas a plus de 6 mois + y, m, d = [int(x) for x in jour.split("-")] + j = datetime.date(y, m, d) + # 6 mois ~ 182 jours: + return j - datetime.date.today() > datetime.timedelta(182) + + security.declareProtected(ScoView, "is_work_saturday") + + def is_work_saturday(self): + "Vrai si le samedi est travaillé" + return int(self.get_preference("work_saturday")) + + def day_names(self): + """Returns week day names. + If work_saturday property is set, include saturday + """ + if self.is_work_saturday(): + return ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"] + else: + return ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"] + + security.declareProtected(ScoView, "ListMondays") + + def ListMondays(self, year=None, REQUEST=None): + """return list of mondays (ISO dates), from september to june + """ + if not year: + year = AnneeScolaire(REQUEST) + d = ddmmyyyy("1/9/%d" % year, work_saturday=self.is_work_saturday()) + while d.weekday != 0: + d = d.next() + end = ddmmyyyy("1/7/%d" % (year + 1), work_saturday=self.is_work_saturday()) + L = [d] + while d < end: + d = d.next(days=7) + L.append(d) + return map(lambda x: x.ISO(), L) + + security.declareProtected(ScoView, "NextISODay") + + def NextISODay(self, date): + "return date after date" + d = ddmmyyyy(date, fmt="iso", work_saturday=self.is_work_saturday()) + return d.next().ISO() + + security.declareProtected(ScoView, "DateRangeISO") + + def DateRangeISO(self, date_beg, date_end, workable=1): + """returns list of dates in [date_beg,date_end] + workable = 1 => keeps only workable days""" + if not date_beg: + raise ScoValueError("pas de date spécifiée !") + if not date_end: + date_end = date_beg + r = [] + cur = ddmmyyyy(date_beg, work_saturday=self.is_work_saturday()) + end = ddmmyyyy(date_end, work_saturday=self.is_work_saturday()) + while cur <= end: + if (not workable) or cur.iswork(): + r.append(cur) + cur = cur.next() + + return map(lambda x: x.ISO(), r) + + # ------------ HTML Interfaces + security.declareProtected(ScoAbsChange, "SignaleAbsenceGrHebdo") + + def SignaleAbsenceGrHebdo( + self, datelundi, group_ids=[], destination="", moduleimpl_id=None, REQUEST=None + ): + "Saisie hebdomadaire des absences" + if not moduleimpl_id: + moduleimp_id = None + + groups_infos = sco_groups_view.DisplayedGroupsInfos( + self, group_ids, REQUEST=REQUEST + ) + if not groups_infos.members: + return ( + self.sco_header(page_title="Saisie des absences", REQUEST=REQUEST) + + "

    Aucun Ă©tudiant !

    " + + self.sco_footer(REQUEST) + ) + + base_url = "SignaleAbsenceGrHebdo?datelundi=%s&%s&destination=%s" % ( + datelundi, + groups_infos.groups_query_args, + urllib.quote(destination), + ) + + formsemestre_id = groups_infos.formsemestre_id + etuds = [ + self.getEtudInfo(etudid=m["etudid"], filled=True)[0] + for m in groups_infos.members + ] + nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id) + sem = sco_formsemestre.do_formsemestre_list( + self, {"formsemestre_id": formsemestre_id} + )[0] + + # calcule dates jours de cette semaine + datessem = [DateDMYtoISO(datelundi)] + for jour in self.day_names()[1:]: + datessem.append(self.NextISODay(datessem[-1])) + + # + if groups_infos.tous_les_etuds_du_sem: + gr_tit = "en" + else: + if len(groups_infos.group_ids) > 1: + p = "des groupes" + else: + p = "du groupe" + gr_tit = ( + p + '' + groups_infos.groups_titles + "" + ) + + H = [ + self.sco_header( + page_title="Saisie hebdomadaire des absences", + init_qtip=True, + javascripts=["js/etud_info.js", "js/abs_ajax.js"], + no_side_bar=1, + REQUEST=REQUEST, + ), + """
    +

    Saisie des absences %s %s, + semaine du lundi %s

    + +

    Annuler

    + +

    +

    + """ + % (gr_tit, sem["titre_num"], datelundi, REQUEST.URL0), + ] + # + modimpls_list = [] + # Initialize with first student + ues = nt.get_ues(etudid=etuds[0]["etudid"]) + for ue in ues: + modimpls_list += nt.get_modimpls(ue_id=ue["ue_id"]) + + # Add modules other students are subscribed to + for etud in etuds[1:]: + modimpls_etud = [] + ues = nt.get_ues(etudid=etud["etudid"]) + for ue in ues: + modimpls_etud += nt.get_modimpls(ue_id=ue["ue_id"]) + modimpls_list += [m for m in modimpls_etud if m not in modimpls_list] + + menu_module = "" + for modimpl in modimpls_list: + if modimpl["moduleimpl_id"] == moduleimpl_id: + sel = "selected" + else: + sel = "" + menu_module += ( + """\n""" + % { + "modimpl_id": modimpl["moduleimpl_id"], + "modname": modimpl["module"]["code"] + + " " + + (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]), + "sel": sel, + } + ) + if moduleimpl_id: + sel = "" + else: + sel = "selected" # aucun module specifie + + H.append( + """ + Module concerné par ces absences (optionnel): +

    """ + % {"menu_module": menu_module, "url": base_url, "sel": sel} + ) + + H += self._gen_form_saisie_groupe( + etuds, self.day_names(), datessem, destination, None, moduleimpl_id + ) + + H.append(self.sco_footer(REQUEST)) + return "\n".join(H) + + security.declareProtected(ScoAbsChange, "SignaleAbsenceGrSemestre") + + def SignaleAbsenceGrSemestre( + self, + datedebut, + datefin, + destination="", + group_ids=[], # list of groups to display + nbweeks=4, # ne montre que les nbweeks dernieres semaines + moduleimpl_id=None, + REQUEST=None, + ): + """Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier + """ + # log('SignaleAbsenceGrSemestre: moduleimpl_id=%s destination=%s' % (moduleimpl_id, destination)) + groups_infos = sco_groups_view.DisplayedGroupsInfos( + self, group_ids, REQUEST=REQUEST + ) + if not groups_infos.members: + return ( + self.sco_header(page_title="Saisie des absences", REQUEST=REQUEST) + + "

    Aucun Ă©tudiant !

    " + + self.sco_footer(REQUEST) + ) + + formsemestre_id = groups_infos.formsemestre_id + etuds = [ + self.getEtudInfo(etudid=m["etudid"], filled=True)[0] + for m in groups_infos.members + ] + + if not moduleimpl_id: + moduleimp_id = None + base_url_noweeks = ( + "SignaleAbsenceGrSemestre?datedebut=%s&datefin=%s&%s&destination=%s" + % ( + datedebut, + datefin, + groups_infos.groups_query_args, + urllib.quote(destination), + ) + ) + base_url = ( + base_url_noweeks + "&nbweeks=%s" % nbweeks + ) # sans le moduleimpl_id + + if etuds: + nt = self.Notes._getNotesCache().get_NotesTable(self.Notes, formsemestre_id) + sem = sco_formsemestre.do_formsemestre_list( + self, {"formsemestre_id": formsemestre_id} + )[0] + + jourdebut = ddmmyyyy(datedebut, work_saturday=self.is_work_saturday()) + jourfin = ddmmyyyy(datefin, work_saturday=self.is_work_saturday()) + today = ddmmyyyy( + time.strftime("%d/%m/%Y", time.localtime()), + work_saturday=self.is_work_saturday(), + ) + today.next() + if jourfin > today: # ne propose jamais les semaines dans le futur + jourfin = today + if jourdebut > today: + raise ScoValueError("date de début dans le futur (%s) !" % jourdebut) + # + if not jourdebut.iswork() or jourdebut > jourfin: + raise ValueError( + "date debut invalide (%s, ouvrable=%d)" + % (str(jourdebut), jourdebut.iswork()) + ) + # calcule dates + dates = [] # ddmmyyyy instances + d = ddmmyyyy(datedebut, work_saturday=self.is_work_saturday()) + while d <= jourfin: + dates.append(d) + d = d.next(7) # avance d'une semaine + # + msg = "Montrer seulement les 4 dernières semaines" + nwl = 4 + if nbweeks: + nbweeks = int(nbweeks) + if nbweeks > 0: + dates = dates[-nbweeks:] + msg = "Montrer toutes les semaines" + nwl = 0 + url_link_semaines = base_url_noweeks + "&nbweeks=%s" % nwl + if moduleimpl_id: + url_link_semaines += "&moduleimpl_id=" + moduleimpl_id + # + colnames = [str(x) for x in dates] + dates = [x.ISO() for x in dates] + dayname = self.day_names()[jourdebut.weekday] + + if groups_infos.tous_les_etuds_du_sem: + gr_tit = "en" + else: + if len(groups_infos.group_ids) > 1: + p = "des groupes" + else: + p = "du groupe" + gr_tit = ( + p + '' + groups_infos.groups_titles + "" + ) + + H = [ + self.sco_header( + page_title="Saisie des absences", + init_qtip=True, + javascripts=["js/etud_info.js", "js/abs_ajax.js"], + no_side_bar=1, + REQUEST=REQUEST, + ), + """
    +

    Saisie des absences %s %s, + les %s

    +

    + %s + + """ + % (gr_tit, sem["titre_num"], dayname, url_link_semaines, msg), + ] + # + if etuds: + modimpls_list = [] + # Initialize with first student + ues = nt.get_ues(etudid=etuds[0]["etudid"]) + for ue in ues: + modimpls_list += nt.get_modimpls(ue_id=ue["ue_id"]) + + # Add modules other students are subscribed to + for etud in etuds[1:]: + modimpls_etud = [] + ues = nt.get_ues(etudid=etud["etudid"]) + for ue in ues: + modimpls_etud += nt.get_modimpls(ue_id=ue["ue_id"]) + modimpls_list += [m for m in modimpls_etud if m not in modimpls_list] + + menu_module = "" + for modimpl in modimpls_list: + if modimpl["moduleimpl_id"] == moduleimpl_id: + sel = "selected" + else: + sel = "" + menu_module += ( + """\n""" + % { + "modimpl_id": modimpl["moduleimpl_id"], + "modname": modimpl["module"]["code"] + + " " + + (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]), + "sel": sel, + } + ) + if moduleimpl_id: + sel = "" + else: + sel = "selected" # aucun module specifie + H.append( + """

    + Module concerné par ces absences (optionnel): +

    """ + % {"menu_module": menu_module, "url": base_url, "sel": sel} + ) + + H += self._gen_form_saisie_groupe( + etuds, colnames, dates, destination, dayname, moduleimpl_id + ) + H.append(self.sco_footer(REQUEST)) + return "\n".join(H) + + def _gen_form_saisie_groupe( + self, etuds, colnames, dates, destination="", dayname="", moduleimpl_id=None + ): + H = [ + """ + +
    +
    + + + """ + % len(etuds) + ] + # Titres colonnes + if dayname: + for jour in colnames: + H.append( + '" + ) + H.append("") + + for jour in colnames: + H.append( + '" + ) + + H.append("") + H.append("" * len(colnames)) + H.append("") + # + if not etuds: + H.append( + '' + ) + i = 1 + for etud in etuds: + i += 1 + etudid = etud["etudid"] + # UE capitalisee dans semestre courant ? + cap = [] + if etud["cursem"]: + nt = self.Notes._getNotesCache().get_NotesTable( + self.Notes, etud["cursem"]["formsemestre_id"] + ) # > get_ues, get_etud_ue_status + for ue in nt.get_ues(): + status = nt.get_etud_ue_status(etudid, ue["ue_id"]) + if status["is_capitalized"]: + cap.append(ue["acronyme"]) + if cap: + capstr = ' (%s cap.)' % ", ".join(cap) + else: + capstr = "" + + tr_class = ("row_1", "row_2", "row_3")[i % 3] + td_matin_class = ("matin_1", "matin_2", "matin_3")[i % 3] + + H.append( + '' + % (tr_class, etudid, etudid, etud["nomprenom"], capstr) + ) + for date in dates: + # matin + if self.CountAbs(etudid, date, date, True, moduleimpl_id=moduleimpl_id): + checked = "checked" + else: + checked = "" + H.append( + '' + % ( + td_matin_class, + etudid + ":" + date + ":" + "am", + checked, + etudid, + date + ":am", + ) + ) + # apres midi + if self.CountAbs( + etudid, date, date, False, moduleimpl_id=moduleimpl_id + ): + checked = "checked" + else: + checked = "" + H.append( + '' + % (etudid + ":" + date + ":" + "pm", checked, etudid, date + ":pm") + ) + H.append("") + H.append("
    %d Ă©tudiants' + + dayname + + "
     ' + + jour + + "
     AMPM
    Aucun Ă©tudiant inscrit !
    %s%s
    ") + # place la liste des etudiants et les dates pour pouvoir effacer les absences + H.append( + '' + % ",".join([etud["etudid"] for etud in etuds]) + ) + H.append('' % dates[0]) + H.append('' % dates[-1]) + H.append('' % ",".join(dates)) + H.append( + '' + % urllib.quote(destination) + ) + # + # version pour formulaire avec AJAX (Yann LB) + H.append( + """ +

    +

    + +

    +
    +

    Les cases cochées correspondent à des absences. + Les absences saisies ne sont pas justifiées (sauf si un justificatif a été entré + par ailleurs). +

    Si vous "décochez" une case, l'absence correspondante sera supprimée. + Attention, les modifications sont automatiquement entregistrées au fur et à mesure. +

    + """ + % destination + ) + return H + + def _TablesAbsEtud( + self, + etudid, + datedebut, + with_evals=True, + format="html", + absjust_only=0, + REQUEST=None, + ): + """Tables des absences justifiees et non justifiees d'un étudiant sur l'année en cours + """ + absjust = self.ListeAbsJust(etudid=etudid, datedebut=datedebut) + absnonjust = self.ListeAbsNonJust(etudid=etudid, datedebut=datedebut) + # examens ces jours là ? + if with_evals: + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + for a in absnonjust + absjust: + cursor.execute( + """select eval.* + from notes_evaluation eval, notes_moduleimpl_inscription mi, notes_moduleimpl m + where eval.jour = %(jour)s and eval.moduleimpl_id = m.moduleimpl_id + and mi.moduleimpl_id = m.moduleimpl_id and mi.etudid = %(etudid)s""", + {"jour": a["jour"].strftime("%Y-%m-%d"), "etudid": etudid}, + ) + a["evals"] = cursor.dictfetchall() + cursor.execute( + """SELECT mi.moduleimpl_id + from absences abs, notes_moduleimpl_inscription mi, notes_moduleimpl m + where abs.matin = %(matin)s and abs.jour = %(jour)s and abs.etudid=%(etudid)s and abs.moduleimpl_id=mi.moduleimpl_id and mi.moduleimpl_id=m.moduleimpl_id + and mi.etudid = %(etudid)s""", + { + "matin": bool(a["matin"]), + "jour": a["jour"].strftime("%Y-%m-%d"), + "etudid": etudid, + }, + ) + a["absent"] = cursor.dictfetchall() + + def matin(x): + if x: + return "matin" + else: + return "après midi" + + def descr_exams(a): + if not a.has_key("evals"): + return "" + ex = [] + for ev in a["evals"]: + mod = self.Notes.do_moduleimpl_withmodule_list( + moduleimpl_id=ev["moduleimpl_id"] + )[0] + if format == "html": + ex.append( + '%s' + % (mod["moduleimpl_id"], mod["module"]["code"]) + ) + else: + ex.append(mod["module"]["code"]) + if ex: + return ", ".join(ex) + return "" + + def descr_abs(a): + ex = [] + for ev in a.get("absent", []): + mod = self.Notes.do_moduleimpl_withmodule_list( + moduleimpl_id=ev["moduleimpl_id"] + )[0] + if format == "html": + ex.append( + '%s' + % (mod["moduleimpl_id"], mod["module"]["code"]) + ) + else: + ex.append(mod["module"]["code"]) + if ex: + return ", ".join(ex) + return "" + + # ajoute date formatée et évaluations + for L in (absnonjust, absjust): + for a in L: + if with_evals: + a["exams"] = descr_exams(a) + a["datedmy"] = a["jour"].strftime("%d/%m/%Y") + a["matin_o"] = int(a["matin"]) + a["matin"] = matin(a["matin"]) + index = a["description"].find(")") + if index != -1: + a["motif"] = a["description"][1:index] + else: + a["motif"] = "" + a["description"] = descr_abs(a) or "" + + # ajoute lien pour justifier + if format == "html": + for a in absnonjust: + a["justlink"] = "justifier" + a["_justlink_target"] = ( + "doJustifAbsence?etudid=%s&datedebut=%s&datefin=%s&demijournee=%s" + % (etudid, a["datedmy"], a["datedmy"], a["matin_o"]) + ) + # + titles = { + "datedmy": "Date", + "matin": "", + "exams": "Examens ce jour", + "justlink": "", + "description": "Modules", + "motif": "Motif", + } + columns_ids = ["datedmy", "matin"] + if with_evals: + columns_ids.append("exams") + + columns_ids.append("description") + columns_ids.append("motif") + if format == "html": + columns_ids.append("justlink") + + return titles, columns_ids, absnonjust, absjust + + security.declareProtected(ScoView, "EtatAbsencesGr") # ported from dtml + + def EtatAbsencesGr( + self, + group_ids=[], # list of groups to display + debut="", + fin="", + with_boursier=True, # colonne boursier + format="html", + REQUEST=None, + ): + """Liste les absences de groupes + """ + datedebut = DateDMYtoISO(debut) + datefin = DateDMYtoISO(fin) + # Informations sur les groupes à afficher: + groups_infos = sco_groups_view.DisplayedGroupsInfos( + self, group_ids, REQUEST=REQUEST + ) + formsemestre_id = groups_infos.formsemestre_id + sem = groups_infos.formsemestre + + # Construit tableau (etudid, statut, nomprenom, nbJust, nbNonJust, NbTotal) + T = [] + for m in groups_infos.members: + etud = self.getEtudInfo(etudid=m["etudid"], filled=True)[0] + nbabs = self.CountAbs(etudid=etud["etudid"], debut=datedebut, fin=datefin) + nbabsjust = self.CountAbsJust( + etudid=etud["etudid"], debut=datedebut, fin=datefin + ) + nbjustifs_noabs = len( + self.ListeJustifs( + etudid=etud["etudid"], datedebut=datedebut, only_no_abs=True + ) + ) + # retrouve sem dans etud['sems'] + s = None + for s in etud["sems"]: + if s["formsemestre_id"] == formsemestre_id: + break + if not s or s["formsemestre_id"] != formsemestre_id: + raise ValueError( + "EtatAbsencesGr: can't retreive sem" + ) # bug or malicious arg + T.append( + { + "etudid": etud["etudid"], + "etatincursem": s["ins"]["etat"], + "nomprenom": etud["nomprenom"], + "nbabsjust": nbabsjust, + "nbabsnonjust": nbabs - nbabsjust, + "nbabs": nbabs, + "nbjustifs_noabs": nbjustifs_noabs, + "_nomprenom_target": "CalAbs?etudid=%s" % etud["etudid"], + "_nomprenom_td_attrs": 'id="%s" class="etudinfo"' % etud["etudid"], + "boursier": etud["boursier"], + } + ) + if s["ins"]["etat"] == "D": + T[-1]["_css_row_class"] = "etuddem" + T[-1]["nomprenom"] += " (dem)" + columns_ids = [ + "nomprenom", + "nbjustifs_noabs", + "nbabsjust", + "nbabsnonjust", + "nbabs", + ] + if with_boursier: + columns_ids[1:1] = ["boursier"] + if groups_infos.tous_les_etuds_du_sem: + gr_tit = "" + else: + if len(groups_infos.group_ids) > 1: + p = "des groupes" + else: + p = "du groupe" + if format == "html": + h = ' ' + groups_infos.groups_titles + "" + else: + h = groups_infos.groups_titles + gr_tit = p + h + + title = "Etat des absences %s" % gr_tit + if format == "xls" or format == "xml": + columns_ids = ["etudid"] + columns_ids + tab = GenTable( + columns_ids=columns_ids, + rows=T, + preferences=self.get_preferences(formsemestre_id), + titles={ + "etatincursem": "Etat", + "nomprenom": "Nom", + "nbabsjust": "Justifiées", + "nbabsnonjust": "Non justifiées", + "nbabs": "Total", + "nbjustifs_noabs": "Justifs non utilisés", + "boursier": "Bourse", + }, + html_sortable=True, + html_class="table_leftalign", + html_header=self.sco_header( + REQUEST, + page_title=title, + init_qtip=True, + javascripts=["js/etud_info.js"], + ), + html_title=self.Notes.html_sem_header( + REQUEST, "%s" % title, sem, with_page_header=False + ) + + "

    Période du %s au %s (nombre de demi-journées)
    " + % (debut, fin), + base_url="%s&formsemestre_id=%s&debut=%s&fin=%s" + % (groups_infos.base_url, formsemestre_id, debut, fin), + filename="etat_abs_" + + make_filename( + "%s de %s" % (groups_infos.groups_filename, sem["titreannee"]) + ), + caption=title, + html_next_section="""

    +

    +Justifs non utilisés: nombre de demi-journées avec justificatif mais sans absences relevées. +

    +

    +Cliquez sur un nom pour afficher le calendrier des absences
    +ou entrez une date pour visualiser les absents un jour donnĂ© : +

    +
    +
    + +%s + + +
    + """ + % (REQUEST.URL0, formsemestre_id, groups_infos.get_form_elem()), + ) + return tab.make_page(self, format=format, REQUEST=REQUEST) + + security.declareProtected(ScoView, "EtatAbsencesDate") # ported from dtml + + def EtatAbsencesDate( + self, group_ids=[], date=None, REQUEST=None # list of groups to display + ): + """Etat des absences pour un groupe à une date donnée + """ + # Informations sur les groupes à afficher: + groups_infos = sco_groups_view.DisplayedGroupsInfos( + self, group_ids, REQUEST=REQUEST + ) + formsemestre_id = groups_infos.formsemestre_id + sem = sco_formsemestre.do_formsemestre_list( + self, {"formsemestre_id": formsemestre_id} + )[0] + H = [self.sco_header(page_title="Etat des absences", REQUEST=REQUEST)] + if date: + dateiso = DateDMYtoISO(date) + nbetud = 0 + t_nbabsjustam = 0 + t_nbabsam = 0 + t_nbabsjustpm = 0 + t_nbabspm = 0 + etuds = self.getEtudInfoGroupes(groups_infos.group_ids) + H.append("

    Etat des absences le %s

    " % date) + H.append( + """ + + + """ + ) + for etud in groups_infos.members: + nbabsam = self.CountAbs( + etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=1 + ) + nbabspm = self.CountAbs( + etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=0 + ) + if (nbabsam != 0) or (nbabspm != 0): + nbetud += 1 + nbabsjustam = self.CountAbsJust( + etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=1 + ) + nbabsjustpm = self.CountAbsJust( + etudid=etud["etudid"], debut=dateiso, fin=dateiso, matin=0 + ) + H.append( + """") + H.append( + """""" + % (t_nbabsam, t_nbabsjustam, t_nbabspm, t_nbabsjustpm) + ) + H.append("
     MatinAprès-midi
    + %(nomprenom)s""" + % etud + ) # """ + if nbabsam != 0: + if nbabsjustam: + H.append("Just.") + t_nbabsjustam += 1 + else: + H.append("Abs.") + t_nbabsam += 1 + else: + H.append("") + H.append('') + if nbabspm != 0: + if nbabsjustpm: + H.append("Just.") + t_nbabsjustam += 1 + else: + H.append("Abs.") + t_nbabspm += 1 + else: + H.append("") + H.append("
    %d abs, %d just.%d abs, %d just.
    ") + if nbetud == 0: + H.append("

    Aucune absence !

    ") + else: + H.append( + """

    Erreur: vous n'avez pas choisi de date !

    + Continuer""" + % REQUEST.HTTP_REFERER + ) + + return "\n".join(H) + self.sco_footer(REQUEST) + + # ----- Gestion des "billets d'absence": signalement par les etudiants eux mêmes (à travers le portail) + security.declareProtected(ScoAbsAddBillet, "AddBilletAbsence") + + def AddBilletAbsence( + self, + begin, + end, + description, + etudid=False, + code_nip=None, + code_ine=None, + justified=True, + REQUEST=None, + xml_reply=True, + ): + """Memorise un "billet" + begin et end sont au format ISO (eg "1999-01-08 04:05:06") + """ + t0 = time.time() + # check etudid + etuds = self.getEtudInfo( + etudid=etudid, code_nip=code_nip, REQUEST=REQUEST, filled=True + ) + if not etuds: + return log_unknown_etud(self, REQUEST=REQUEST) + etud = etuds[0] + # check dates + begin_date = ParseDateTimeUTC(begin) # may raises ValueError + end_date = ParseDateTimeUTC(end) + if begin_date > end_date: + raise ValueError("invalid dates") + # + justified = int(justified) + # + cnx = self.GetDBConnexion() + billet_id = billet_absence_create( + cnx, + { + "etudid": etud["etudid"], + "abs_begin": begin, + "abs_end": end, + "description": description, + "etat": 0, + "justified": justified, + }, + ) + if xml_reply: + # Renvoie le nouveau billet en XML + if REQUEST: + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + + billets = billet_absence_list(cnx, {"billet_id": billet_id}) + tab = self._tableBillets(billets, etud=etud) + log( + "AddBilletAbsence: new billet_id=%s (%gs)" + % (billet_id, time.time() - t0) + ) + return tab.make_page(self, REQUEST=REQUEST, format="xml") + else: + return billet_id + + security.declareProtected(ScoAbsAddBillet, "AddBilletAbsenceForm") + + def AddBilletAbsenceForm(self, etudid, REQUEST=None): + """Formulaire ajout billet (pour tests seulement, le vrai formulaire accessible aux etudiants + étant sur le portail étudiant). + """ + etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] + H = [ + self.sco_header( + REQUEST, page_title="Billet d'absence de %s" % etud["nomprenom"] + ) + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ("etudid", {"input_type": "hidden"}), + ("begin", {"input_type": "date"}), + ("end", {"input_type": "date"}), + ( + "justified", + {"input_type": "boolcheckbox", "default": 0, "title": "Justifiée"}, + ), + ("description", {"input_type": "textarea"}), + ), + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + e = tf[2]["begin"].split("/") + begin = e[2] + "-" + e[1] + "-" + e[0] + " 00:00:00" + e = tf[2]["end"].split("/") + end = e[2] + "-" + e[1] + "-" + e[0] + " 00:00:00" + log( + self.AddBilletAbsence( + begin, + end, + tf[2]["description"], + etudid=etudid, + xml_reply=True, + justified=tf[2]["justified"], + ) + ) + return REQUEST.RESPONSE.redirect("listeBilletsEtud?etudid=" + etudid) + + def _tableBillets(self, billets, etud=None, title=""): + for b in billets: + if b["abs_begin"].hour < 12: + m = " matin" + else: + m = " après midi" + b["abs_begin_str"] = b["abs_begin"].strftime("%d/%m/%Y") + m + if b["abs_end"].hour < 12: + m = " matin" + else: + m = " après midi" + b["abs_end_str"] = b["abs_end"].strftime("%d/%m/%Y") + m + if b["etat"] == 0: + if b["justified"] == 0: + b["etat_str"] = "à traiter" + else: + b["etat_str"] = "à justifier" + b["_etat_str_target"] = ( + "ProcessBilletAbsenceForm?billet_id=%s" % b["billet_id"] + ) + if etud: + b["_etat_str_target"] += "&etudid=%s" % etud["etudid"] + b["_billet_id_target"] = b["_etat_str_target"] + else: + b["etat_str"] = "ok" + if not etud: + # ajoute info etudiant + e = self.getEtudInfo(etudid=b["etudid"], filled=1) + if not e: + b["nomprenom"] = "???" # should not occur + else: + b["nomprenom"] = e[0]["nomprenom"] + b["_nomprenom_target"] = "ficheEtud?etudid=%s" % b["etudid"] + if etud and not title: + title = "Billets d'absence déclarés par %(nomprenom)s" % etud + else: + title = title + columns_ids = ["billet_id"] + if not etud: + columns_ids += ["nomprenom"] + columns_ids += ["abs_begin_str", "abs_end_str", "description", "etat_str"] + + tab = GenTable( + titles={ + "billet_id": "Numéro", + "abs_begin_str": "Début", + "abs_end_str": "Fin", + "description": "Raison de l'absence", + "etat_str": "Etat", + }, + columns_ids=columns_ids, + page_title=title, + html_title="

    %s

    " % title, + preferences=self.get_preferences(), + rows=billets, + html_sortable=True, + ) + return tab + + security.declareProtected(ScoView, "listeBilletsEtud") + + def listeBilletsEtud(self, etudid=False, REQUEST=None, format="html"): + """Liste billets pour un etudiant + """ + etuds = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST) + if not etuds: + return log_unknown_etud(self, format=format, REQUEST=REQUEST) + + etud = etuds[0] + cnx = self.GetDBConnexion() + billets = billet_absence_list(cnx, {"etudid": etud["etudid"]}) + tab = self._tableBillets(billets, etud=etud) + return tab.make_page(self, REQUEST=REQUEST, format=format) + + security.declareProtected(ScoView, "XMLgetBilletsEtud") + + def XMLgetBilletsEtud(self, etudid=False, REQUEST=None): + """Liste billets pour un etudiant + """ + if not self.get_preference("handle_billets_abs"): + return "" + t0 = time.time() + r = self.listeBilletsEtud(etudid, REQUEST=REQUEST, format="xml") + log("XMLgetBilletsEtud (%gs)" % (time.time() - t0)) + return r + + security.declareProtected(ScoView, "listeBillets") + + def listeBillets(self, REQUEST=None): + """Page liste des billets non traités et formulaire recherche d'un billet""" + cnx = self.GetDBConnexion() + billets = billet_absence_list(cnx, {"etat": 0}) + tab = self._tableBillets(billets) + T = tab.html() + H = [ + self.sco_header(REQUEST, page_title="Billet d'absence non traités"), + "

    Billets d'absence en attente de traitement (%d)

    " % len(billets), + ] + + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + (("billet_id", {"input_type": "text", "title": "Numéro du billet"}),), + submitbutton=False, + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + T + self.sco_footer(REQUEST) + else: + return REQUEST.RESPONSE.redirect( + "ProcessBilletAbsenceForm?billet_id=" + tf[2]["billet_id"] + ) + + security.declareProtected(ScoAbsChange, "deleteBilletAbsence") + + def deleteBilletAbsence(self, billet_id, REQUEST=None, dialog_confirmed=False): + """Supprime un billet. + """ + cnx = self.GetDBConnexion() + billets = billet_absence_list(cnx, {"billet_id": billet_id}) + if not billets: + return REQUEST.RESPONSE.redirect( + "listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id + ) + if not dialog_confirmed: + tab = self._tableBillets(billets) + return self.confirmDialog( + """

    Supprimer ce billet ?

    """ + tab.html(), + dest_url="", + REQUEST=REQUEST, + cancel_url="listeBillets", + parameters={"billet_id": billet_id}, + ) + + billet_absence_delete(cnx, billet_id) + + return REQUEST.RESPONSE.redirect("listeBillets?head_message=Billet%20supprimé") + + def _ProcessBilletAbsence(self, billet, estjust, description, REQUEST): + """Traite un billet: ajoute absence(s) et éventuellement justificatifs, + et change l'état du billet à 1. + NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après midi. + """ + cnx = self.GetDBConnexion() + if billet["etat"] != 0: + log("billet=%s" % billet) + log("billet deja traité !") + return -1 + n = 0 # nombre de demi-journées d'absence ajoutées + # 1-- ajout des absences (et justifs) + datedebut = billet["abs_begin"].strftime("%d/%m/%Y") + datefin = billet["abs_end"].strftime("%d/%m/%Y") + dates = self.DateRangeISO(datedebut, datefin) + # commence apres midi ? + if dates and billet["abs_begin"].hour > 11: + self._AddAbsence( + billet["etudid"], dates[0], 0, estjust, REQUEST, description=description + ) + n += 1 + dates = dates[1:] + # termine matin ? + if dates and billet["abs_end"].hour < 12: + self._AddAbsence( + billet["etudid"], + dates[-1], + 1, + estjust, + REQUEST, + description=description, + ) + n += 1 + dates = dates[:-1] + + for jour in dates: + self._AddAbsence( + billet["etudid"], jour, 0, estjust, REQUEST, description=description + ) + self._AddAbsence( + billet["etudid"], jour, 1, estjust, REQUEST, description=description + ) + n += 2 + + # 2- change etat du billet + billet_absence_edit(cnx, {"billet_id": billet["billet_id"], "etat": 1}) + + return n + + security.declareProtected(ScoAbsChange, "ProcessBilletAbsenceForm") + + def ProcessBilletAbsenceForm(self, billet_id, REQUEST=None): + """Formulaire traitement d'un billet""" + cnx = self.GetDBConnexion() + billets = billet_absence_list(cnx, {"billet_id": billet_id}) + if not billets: + return REQUEST.RESPONSE.redirect( + "listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id + ) + billet = billets[0] + etudid = billet["etudid"] + etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] + + H = [ + self.sco_header( + REQUEST, + page_title="Traitement billet d'absence de %s" % etud["nomprenom"], + ), + '

    Traitement du billet %s : %s

    ' + % (billet_id, etudid, etud["nomprenom"]), + ] + + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ("billet_id", {"input_type": "hidden"}), + ( + "etudid", + {"input_type": "hidden"}, + ), # pour centrer l'UI sur l'étudiant + ( + "estjust", + {"input_type": "boolcheckbox", "title": "Absences justifiées"}, + ), + ("description", {"input_type": "text", "size": 42, "title": "Raison"}), + ), + initvalues={ + "description": billet["description"], + "estjust": billet["justified"], + "etudid": etudid, + }, + submitlabel="Enregistrer ces absences", + ) + if tf[0] == 0: + tab = self._tableBillets([billet], etud=etud) + H.append(tab.html()) + if billet["justified"] == 1: + H.append( + """

    L'Ă©tudiant pense pouvoir justifier cette absence.
    VĂ©rifiez le justificatif avant d'enregistrer.

    """ + ) + F = ( + """

    Supprimer ce billet (utiliser en cas d'erreur, par ex. billet en double)

    """ + % billet_id + ) + F += '

    Liste de tous les billets en attente

    ' + + return "\n".join(H) + "
    " + tf[1] + F + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + n = self._ProcessBilletAbsence( + billet, tf[2]["estjust"], tf[2]["description"], REQUEST + ) + if tf[2]["estjust"]: + j = "justifiées" + else: + j = "non justifiées" + H.append('
    ') + if n > 0: + H.append("%d absences (1/2 journées) %s ajoutées" % (n, j)) + elif n == 0: + H.append("Aucun jour d'absence dans les dates indiquées !") + elif n < 0: + H.append("Ce billet avait déjà été traité !") + H.append( + '

    Autre billets en attente

    Billets déclarés par %s

    ' + % (etud["nomprenom"]) + ) + billets = billet_absence_list(cnx, {"etudid": etud["etudid"]}) + tab = self._tableBillets(billets, etud=etud) + H.append(tab.html()) + return "\n".join(H) + self.sco_footer(REQUEST) + + security.declareProtected(ScoView, "XMLgetAbsEtud") + + def XMLgetAbsEtud(self, beg_date="", end_date="", REQUEST=None): + """returns list of absences in date interval""" + t0 = time.time() + etud = self.getEtudInfo(REQUEST=REQUEST)[0] + exp = re.compile(r"^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$") + if not exp.match(beg_date): + raise ScoValueError("invalid date: %s" % beg_date) + if not exp.match(end_date): + raise ScoValueError("invalid date: %s" % end_date) + + Abs = self._ListeAbsDate(etud["etudid"], beg_date, end_date) + + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + doc = jaxml.XML_document(encoding=SCO_ENCODING) + doc.absences(etudid=etud["etudid"], beg_date=beg_date, end_date=end_date) + doc._push() + for a in Abs: + if a["estabs"]: # ne donne pas les justifications si pas d'absence + doc._push() + doc.abs( + begin=a["begin"], + end=a["end"], + description=a["description"], + justified=a["estjust"], + ) + doc._pop() + doc._pop() + log("XMLgetAbsEtud (%gs)" % (time.time() - t0)) + return repr(doc) + + +_billet_absenceEditor = EditableTable( + "billet_absence", + "billet_id", + ( + "billet_id", + "etudid", + "abs_begin", + "abs_end", + "description", + "etat", + "entry_date", + "justified", + ), + sortkey="entry_date desc", +) + +billet_absence_create = _billet_absenceEditor.create +billet_absence_delete = _billet_absenceEditor.delete +billet_absence_list = _billet_absenceEditor.list +billet_absence_edit = _billet_absenceEditor.edit + +# ------ HTML Calendar functions (see YearTable function) + +# MONTH/DAY NAMES: + +MONTHNAMES = ( + "Janvier", + "Février", + "Mars", + "Avril", + "Mai", + "Juin", + "Juillet", + "Aout", + "Septembre", + "Octobre", + "Novembre", + "Décembre", +) + +MONTHNAMES_ABREV = ( + "Jan.", + "Fév.", + "Mars", + "Avr.", + "Mai ", + "Juin", + "Juil", + "Aout", + "Sept", + "Oct.", + "Nov.", + "Déc.", +) + +DAYNAMES = ("Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche") + +DAYNAMES_ABREV = ("L", "M", "M", "J", "V", "S", "D") + +# COLORS: + +WHITE = "#FFFFFF" +GRAY1 = "#EEEEEE" +GREEN3 = "#99CC99" +WEEKDAYCOLOR = GRAY1 +WEEKENDCOLOR = GREEN3 + + +def MonthTableHead(month): + color = WHITE + return """ + \n""" % ( + color, + MONTHNAMES_ABREV[month - 1], + ) + + +def MonthTableTail(): + return "
    %s
    \n" + + +def MonthTableBody( + month, year, events=[], halfday=0, trattributes="", work_saturday=False, pad_width=8 +): + # log('XXX events=%s' % events) + firstday, nbdays = calendar.monthrange(year, month) + localtime = time.localtime() + current_weeknum = time.strftime("%U", localtime) + current_year = localtime[0] + T = [] + # cherche date du lundi de la 1ere semaine de ce mois + monday = ddmmyyyy("1/%d/%d" % (month, year)) + while monday.weekday != 0: + monday = monday.prev() + + if work_saturday: + weekend = ("D",) + else: + weekend = ("S", "D") + + if not halfday: + for d in range(1, nbdays + 1): + weeknum = time.strftime( + "%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y") + ) + day = DAYNAMES_ABREV[(firstday + d - 1) % 7] + if day in weekend: + bgcolor = WEEKENDCOLOR + weekclass = "wkend" + attrs = "" + else: + bgcolor = WEEKDAYCOLOR + weekclass = "wk" + str(monday).replace("/", "_") + attrs = trattributes + color = None + legend = "" + href = "" + descr = "" + # event this day ? + # each event is a tuple (date, text, color, href) + # where date is a string in ISO format (yyyy-mm-dd) + for ev in events: + ev_year = int(ev[0][:4]) + ev_month = int(ev[0][5:7]) + ev_day = int(ev[0][8:10]) + if year == ev_year and month == ev_month and ev_day == d: + if ev[1]: + legend = ev[1] + if ev[2]: + color = ev[2] + if ev[3]: + href = ev[3] + if len(ev) > 4 and ev[4]: + descr = ev[4] + # + cc = [] + if color != None: + cc.append('' % color) + else: + cc.append('') + + if href: + href = 'href="%s"' % href + if descr: + descr = 'title="%s"' % descr + if href or descr: + cc.append("" % (href, descr)) + + if legend or d == 1: + if pad_width != None: + n = pad_width - len(legend) # pad to 8 cars + if n > 0: + legend = " " * (n / 2) + legend + " " * ((n + 1) / 2) + else: + legend = " " # empty cell + cc.append(legend) + if href or descr: + cc.append("") + cc.append("") + cell = string.join(cc, "") + if day == "D": + monday = monday.next(7) + if ( + weeknum == current_weeknum + and current_year == year + and weekclass != "wkend" + ): + weekclass += " currentweek" + T.append( + '%d%s%s' + % (bgcolor, weekclass, attrs, d, day, cell) + ) + else: + # Calendar with 2 cells / day + for d in range(1, nbdays + 1): + weeknum = time.strftime( + "%U", time.strptime("%d/%d/%d" % (d, month, year), "%d/%m/%Y") + ) + day = DAYNAMES_ABREV[(firstday + d - 1) % 7] + if day in weekend: + bgcolor = WEEKENDCOLOR + weekclass = "wkend" + attrs = "" + else: + bgcolor = WEEKDAYCOLOR + weekclass = "wk" + str(monday).replace("/", "_") + attrs = trattributes + if ( + weeknum == current_weeknum + and current_year == year + and weekclass != "wkend" + ): + weeknum += " currentweek" + + if day == "D": + monday = monday.next(7) + T.append( + '%d%s' + % (bgcolor, weekclass, attrs, d, day) + ) + cc = [] + for morning in (1, 0): + color = None + legend = "" + href = "" + descr = "" + for ev in events: + ev_year = int(ev[0][:4]) + ev_month = int(ev[0][5:7]) + ev_day = int(ev[0][8:10]) + if ev[4] != None: + ev_half = int(ev[4]) + else: + ev_half = 0 + if ( + year == ev_year + and month == ev_month + and ev_day == d + and morning == ev_half + ): + if ev[1]: + legend = ev[1] + if ev[2]: + color = ev[2] + if ev[3]: + href = ev[3] + if len(ev) > 5 and ev[5]: + descr = ev[5] + # + if color != None: + cc.append('' % (color)) + else: + cc.append('') + if href: + href = 'href="%s"' % href + if descr: + descr = 'title="%s"' % descr + if href or descr: + cc.append("" % (href, descr)) + if legend or d == 1: + n = 3 - len(legend) # pad to 3 cars + if n > 0: + legend = " " * (n / 2) + legend + " " * ((n + 1) / 2) + else: + legend = "   " # empty cell + cc.append(legend) + if href or descr: + cc.append("") + cc.append("\n") + T.append(string.join(cc, "") + "") + return string.join(T, "\n") + + +# -------------------------------------------------------------------- +# +# Zope Product Administration +# +# -------------------------------------------------------------------- +def manage_addZAbsences( + self, id="id_ZAbsences", title="The Title for ZAbsences Object", REQUEST=None +): + "Add a ZAbsences instance to a folder." + self._setObject(id, ZAbsences(id, title)) + if REQUEST is not None: + return self.manage_main(self, REQUEST) + # return self.manage_editForm(self, REQUEST) + + +# The form used to get the instance id from the user. +# manage_addZAbsencesForm = DTMLFile('dtml/manage_addZAbsencesForm', globals()) + + +# -------------------------------------------------------------------- +# +# Cache absences +# +# On cache simplement (Ă  la demande) le nombre d'absences de chaque etudiant +# dans un semestre donnĂ©. +# Toute modification du semestre (invalidation) invalide le cache +# (simple mĂ©canisme de "listener" sur le cache de semestres) +# Toute modification des absences d'un Ă©tudiant invalide les caches des semestres +# concernĂ©s Ă  cette date (en gĂ©nĂ©ral un seul semestre) +# +# On ne cache pas la liste des absences car elle est rarement utilisĂ©e (calendrier, +# absences Ă  une date donnĂ©e). +# +# -------------------------------------------------------------------- +class CAbsSemEtud: + """Comptes d'absences d'un etudiant dans un semestre""" + + def __init__(self, context, sem, etudid): + self.context = context + self.sem = sem + self.etudid = etudid + self._loaded = False + formsemestre_id = sem["formsemestre_id"] + context.Notes._getNotesCache().add_listener( + self.invalidate, formsemestre_id, (etudid, formsemestre_id) + ) + + def CountAbs(self): + if not self._loaded: + self.load() + return self._CountAbs + + def CountAbsJust(self): + if not self._loaded: + self.load() + return self._CountAbsJust + + def load(self): + "Load state from DB" + # log('loading CAbsEtudSem(%s,%s)' % (self.etudid, self.sem['formsemestre_id'])) + # Reload sem, it may have changed + self.sem = sco_formsemestre.get_formsemestre( + self.context, self.sem["formsemestre_id"] + ) + debut_sem = DateDMYtoISO(self.sem["date_debut"]) + fin_sem = DateDMYtoISO(self.sem["date_fin"]) + + self._CountAbs = self.context.Absences.CountAbs( + etudid=self.etudid, debut=debut_sem, fin=fin_sem + ) + self._CountAbsJust = self.context.Absences.CountAbsJust( + etudid=self.etudid, debut=debut_sem, fin=fin_sem + ) + self._loaded = True + + def invalidate(self, args=None): + "Notify me that DB has been modified" + # log('invalidate CAbsEtudSem(%s,%s)' % (self.etudid, self.sem['formsemestre_id'])) + self._loaded = False + + +# Accès au cache des absences +ABS_CACHE_INST = {} # { DeptId : { formsemestre_id : { etudid : CAbsEtudSem } } } + + +def getAbsSemEtud(context, sem, etudid): + AbsSemEtuds = getAbsSemEtuds(context, sem) + if not etudid in AbsSemEtuds: + AbsSemEtuds[etudid] = CAbsSemEtud(context, sem, etudid) + return AbsSemEtuds[etudid] + + +def getAbsSemEtuds(context, sem): + u = context.GetDBConnexionString() # identifie le dept de facon fiable + if not u in ABS_CACHE_INST: + ABS_CACHE_INST[u] = {} + C = ABS_CACHE_INST[u] + if sem["formsemestre_id"] not in C: + C[sem["formsemestre_id"]] = {} + return C[sem["formsemestre_id"]] + + +def invalidateAbsEtudDate(context, etudid, date): + """Doit etre appelĂ© Ă  chaque modification des absences pour cet Ă©tudiant et cette date. + Invalide cache absence et PDF bulletins si nĂ©cessaire. + date: date au format ISO + """ + # Semestres a cette date: + etud = context.getEtudInfo(etudid=etudid, filled=True)[0] + sems = [ + sem + for sem in etud["sems"] + if sem["date_debut_iso"] <= date and sem["date_fin_iso"] >= date + ] + + # Invalide les PDF et les abscences: + for sem in sems: + # Inval cache bulletin et/ou note_table + if sco_compute_moy.formsemestre_expressions_use_abscounts( + context, sem["formsemestre_id"] + ): + pdfonly = False # seules certaines formules utilisent les absences + else: + pdfonly = ( + True # efface toujours le PDF car il affiche en gĂ©nĂ©ral les absences + ) + + context.Notes._inval_cache( + pdfonly=pdfonly, formsemestre_id=sem["formsemestre_id"] + ) + + # Inval cache compteurs absences: + AbsSemEtuds = getAbsSemEtuds(context, sem) + if etudid in AbsSemEtuds: + AbsSemEtuds[etudid].invalidate() diff --git a/ZEntreprises.py b/ZEntreprises.py new file mode 100644 index 00000000..3ee42a84 --- /dev/null +++ b/ZEntreprises.py @@ -0,0 +1,898 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +""" Gestion des relations avec les entreprises +""" +import urllib + +from sco_zope import * + +# --------------- + +from notesdb import * +from notes_log import log +from scolog import logdb +from sco_utils import * +import html_sidebar + +from TrivialFormulator import TrivialFormulator, TF +import scolars +import string, re +import time, calendar + + +def _format_nom(nom): + "formatte nom (filtre en entree db) d'une entreprise" + if not nom: + return nom + nom = nom.decode(SCO_ENCODING) + return (nom[0].upper() + nom[1:]).encode(SCO_ENCODING) + + +class EntreprisesEditor(EditableTable): + def delete(self, cnx, oid): + "delete correspondants and contacts, then self" + # first, delete all correspondants and contacts + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "delete from entreprise_contact where entreprise_id=%(entreprise_id)s", + {"entreprise_id": oid}, + ) + cursor.execute( + "delete from entreprise_correspondant where entreprise_id=%(entreprise_id)s", + {"entreprise_id": oid}, + ) + cnx.commit() + EditableTable.delete(self, cnx, oid) + + def list( + self, + cnx, + args={}, + operator="and", + test="=", + sortkey=None, + sort_on_contact=False, + ZEntrepriseInstance=None, + ): + # list, then sort on date of last contact + R = EditableTable.list( + self, cnx, args=args, operator=operator, test=test, sortkey=sortkey + ) + if sort_on_contact: + for r in R: + c = ZEntrepriseInstance.do_entreprise_contact_list( + args={"entreprise_id": r["entreprise_id"]}, disable_formatting=True + ) + if c: + r["date"] = max([x["date"] or datetime.date.min for x in c]) + else: + r["date"] = datetime.date.min + # sort + R.sort(lambda r1, r2: cmp(r2["date"], r1["date"])) + for r in R: + r["date"] = DateISOtoDMY(r["date"]) + return R + + def list_by_etud( + self, cnx, args={}, sort_on_contact=False, disable_formatting=False + ): + "cherche rentreprise ayant eu contact avec etudiant" + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select E.*, I.nom as etud_nom, I.prenom as etud_prenom, C.date from entreprises E, entreprise_contact C, identite I where C.entreprise_id = E.entreprise_id and C.etudid = I.etudid and I.nom ~* %(etud_nom)s ORDER BY E.nom", + args, + ) + titles, res = [x[0] for x in cursor.description], cursor.dictfetchall() + R = [] + for r in res: + r["etud_prenom"] = r["etud_prenom"] or "" + d = {} + for key in r: + v = r[key] + # format value + if not disable_formatting and self.output_formators.has_key(key): + v = self.output_formators[key](v) + d[key] = v + R.append(d) + # sort + if sort_on_contact: + R.sort( + lambda r1, r2: cmp( + r2["date"] or datetime.date.min, r1["date"] or datetime.date.min + ) + ) + for r in R: + r["date"] = DateISOtoDMY(r["date"] or datetime.date.min) + return R + + +_entreprisesEditor = EntreprisesEditor( + "entreprises", + "entreprise_id", + ( + "entreprise_id", + "nom", + "adresse", + "ville", + "codepostal", + "pays", + "contact_origine", + "secteur", + "privee", + "localisation", + "qualite_relation", + "plus10salaries", + "note", + "date_creation", + ), + sortkey="nom", + input_formators={"nom": _format_nom}, +) + +# ----------- Correspondants +_entreprise_correspEditor = EditableTable( + "entreprise_correspondant", + "entreprise_corresp_id", + ( + "entreprise_corresp_id", + "entreprise_id", + "civilite", + "nom", + "prenom", + "fonction", + "phone1", + "phone2", + "mobile", + "fax", + "mail1", + "mail2", + "note", + ), + sortkey="nom", +) + + +# ----------- Contacts +_entreprise_contactEditor = EditableTable( + "entreprise_contact", + "entreprise_contact_id", + ( + "entreprise_contact_id", + "date", + "type_contact", + "entreprise_id", + "entreprise_corresp_id", + "etudid", + "description", + "enseignant", + ), + sortkey="date", + output_formators={"date": DateISOtoDMY}, + input_formators={"date": DateDMYtoISO}, +) + +# --------------- + + +class ZEntreprises( + ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit +): + + "ZEntreprises object" + + meta_type = "ZEntreprises" + security = ClassSecurityInfo() + + # 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 + + ( + # this line is kept as an example with the files : + # dtml/manage_editZScolarForm.dtml + # html/ZScolar-edit.stx + # {'label': 'Properties', 'action': 'manage_editForm',}, + {"label": "View", "action": "index_html"}, + ) + + Item.manage_options # add the 'Undo' & 'Owner' tab + + RoleManager.manage_options # add the 'Security' tab + ) + + # no permissions, only called from python + def __init__(self, id, title): + "initialise a new instance" + self.id = id + self.title = title + + # The form used to edit this object + def manage_editZEntreprises(self, title, RESPONSE=None): + "Changes the instance values" + self.title = title + self._p_changed = 1 + RESPONSE.redirect("manage_editForm") + + # Ajout (dans l'instance) d'un dtml modifiable par Zope + def defaultDocFile(self, id, title, file): + f = open(file_path + "/dtml-editable/" + file + ".dtml") + file = f.read() + f.close() + self.manage_addDTMLMethod(id, title, file) + + security.declareProtected(ScoEntrepriseView, "entreprise_header") + + def entreprise_header(self, REQUEST=None, page_title=""): + "common header for all Entreprises pages" + authuser = REQUEST.AUTHENTICATED_USER + # _read_only is used to modify pages properties (links, buttons) + # Python methods (do_xxx in this class) are also protected individualy) + if authuser.has_permission(ScoEntrepriseChange, self): + REQUEST.set("_read_only", False) + else: + REQUEST.set("_read_only", True) + return self.sco_header(REQUEST, container=self, page_title=page_title) + + security.declareProtected(ScoEntrepriseView, "entreprise_footer") + + def entreprise_footer(self, REQUEST): + "common entreprise footer" + return self.sco_footer(REQUEST) + + security.declareProtected(ScoEntrepriseView, "sidebar") + + def sidebar(self, REQUEST): + "barre gauche (overide std sco sidebar)" + # rewritten from legacy DTML code + context = self + params = {"ScoURL": context.ScoURL()} + + H = [ + """ """) + return "".join(H) + + # -------------------------------------------------------------------- + # + # Entreprises : Methodes en DTML + # + # -------------------------------------------------------------------- + # used to view content of the object + security.declareProtected(ScoEntrepriseView, "index_html") + index_html = DTMLFile("dtml/entreprises/index_html", globals()) + + security.declareProtected(ScoEntrepriseView, "entreprise_contact_list") + entreprise_contact_list = DTMLFile( + "dtml/entreprises/entreprise_contact_list", globals() + ) + security.declareProtected(ScoEntrepriseView, "entreprise_correspondant_list") + entreprise_correspondant_list = DTMLFile( + "dtml/entreprises/entreprise_correspondant_list", globals() + ) + # les methodes "edit" sont aussi en ScoEntrepriseView car elles permettent + # la visualisation (via variable _read_only positionnee dans entreprise_header) + security.declareProtected(ScoEntrepriseView, "entreprise_contact_edit") + entreprise_contact_edit = DTMLFile( + "dtml/entreprises/entreprise_contact_edit", globals() + ) + security.declareProtected(ScoEntrepriseView, "entreprise_correspondant_edit") + entreprise_correspondant_edit = DTMLFile( + "dtml/entreprises/entreprise_correspondant_edit", globals() + ) + + # Acces en modification: + security.declareProtected(ScoEntrepriseChange, "entreprise_contact_create") + entreprise_contact_create = DTMLFile( + "dtml/entreprises/entreprise_contact_create", globals() + ) + security.declareProtected(ScoEntrepriseChange, "entreprise_contact_delete") + entreprise_contact_delete = DTMLFile( + "dtml/entreprises/entreprise_contact_delete", globals() + ) + security.declareProtected(ScoEntrepriseChange, "entreprise_correspondant_create") + entreprise_correspondant_create = DTMLFile( + "dtml/entreprises/entreprise_correspondant_create", globals() + ) + security.declareProtected(ScoEntrepriseChange, "entreprise_correspondant_delete") + entreprise_correspondant_delete = DTMLFile( + "dtml/entreprises/entreprise_correspondant_delete", globals() + ) + security.declareProtected(ScoEntrepriseChange, "entreprise_delete") + entreprise_delete = DTMLFile("dtml/entreprises/entreprise_delete", globals()) + + # -------------------------------------------------------------------- + # + # Entreprises : Methodes en Python + # + # -------------------------------------------------------------------- + security.declareProtected(ScoEntrepriseChange, "do_entreprise_create") + + def do_entreprise_create(self, args): + "entreprise_create" + cnx = self.GetDBConnexion() + r = _entreprisesEditor.create(cnx, args) + return r + + security.declareProtected(ScoEntrepriseChange, "do_entreprise_delete") + + def do_entreprise_delete(self, oid): + "entreprise_delete" + cnx = self.GetDBConnexion() + _entreprisesEditor.delete(cnx, oid) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_list") + + def do_entreprise_list(self, **kw): + "entreprise_list" + cnx = self.GetDBConnexion() + kw["ZEntrepriseInstance"] = self + return _entreprisesEditor.list(cnx, **kw) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_list_by_etud") + + def do_entreprise_list_by_etud(self, **kw): + "entreprise_list_by_etud" + cnx = self.GetDBConnexion() + return _entreprisesEditor.list_by_etud(cnx, **kw) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_edit") + + def do_entreprise_edit(self, *args, **kw): + "entreprise_edit" + cnx = self.GetDBConnexion() + _entreprisesEditor.edit(cnx, *args, **kw) + + security.declareProtected(ScoEntrepriseChange, "do_entreprise_correspondant_create") + + def do_entreprise_correspondant_create(self, args): + "entreprise_correspondant_create" + cnx = self.GetDBConnexion() + r = _entreprise_correspEditor.create(cnx, args) + return r + + security.declareProtected(ScoEntrepriseChange, "do_entreprise_correspondant_delete") + + def do_entreprise_correspondant_delete(self, oid): + "entreprise_correspondant_delete" + cnx = self.GetDBConnexion() + _entreprise_correspEditor.delete(cnx, oid) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_correspondant_list") + + def do_entreprise_correspondant_list(self, **kw): + "entreprise_correspondant_list" + cnx = self.GetDBConnexion() + return _entreprise_correspEditor.list(cnx, **kw) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_correspondant_edit") + + def do_entreprise_correspondant_edit(self, *args, **kw): + "entreprise_correspondant_edit" + cnx = self.GetDBConnexion() + _entreprise_correspEditor.edit(cnx, *args, **kw) + + def do_entreprise_correspondant_listnames(self, args={}): + "-> liste des noms des correspondants (pour affichage menu)" + cnx = self.GetDBConnexion() + C = self.do_entreprise_correspondant_list(args=args) + return [ + (x["prenom"] + " " + x["nom"], str(x["entreprise_corresp_id"])) for x in C + ] + + security.declareProtected(ScoEntrepriseChange, "do_entreprise_contact_create") + + def do_entreprise_contact_create(self, args): + "entreprise_contact_create" + cnx = self.GetDBConnexion() + r = _entreprise_contactEditor.create(cnx, args) + return r + + security.declareProtected(ScoEntrepriseChange, "do_entreprise_contact_delete") + + def do_entreprise_contact_delete(self, oid): + "entreprise_contact_delete" + cnx = self.GetDBConnexion() + _entreprise_contactEditor.delete(cnx, oid) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_contact_list") + + def do_entreprise_contact_list(self, **kw): + "entreprise_contact_list" + cnx = self.GetDBConnexion() + return _entreprise_contactEditor.list(cnx, **kw) + + security.declareProtected(ScoEntrepriseView, "do_entreprise_contact_edit") + + def do_entreprise_contact_edit(self, *args, **kw): + "entreprise_contact_edit" + cnx = self.GetDBConnexion() + _entreprise_contactEditor.edit(cnx, *args, **kw) + + # + security.declareProtected(ScoEntrepriseView, "do_entreprise_check_etudiant") + + def do_entreprise_check_etudiant(self, etudiant): + """Si etudiant est vide, ou un ETUDID valide, ou un nom unique, + retourne (1, ETUDID). + Sinon, retourne (0, 'message explicatif') + """ + etudiant = etudiant.strip().translate( + None, "'()" + ) # suppress parens and quote from name + if not etudiant: + return 1, None + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select etudid, nom, prenom from identite where upper(nom) ~ upper(%(etudiant)s) or etudid=%(etudiant)s", + {"etudiant": etudiant}, + ) + r = cursor.fetchall() + if len(r) < 1: + return 0, 'Aucun etudiant ne correspond Ă  "%s"' % etudiant + elif len(r) > 10: + return ( + 0, + "%d etudiants correspondent Ă  ce nom (utilisez le code)" + % len(r), + ) + elif len(r) > 1: + e = ['
      '] + for x in r: + e.append( + "
    • %s %s (code %s)
    • " + % (strupper(x[1]), x[2] or "", x[0].strip()) + ) + e.append("
    ") + return ( + 0, + "Les étudiants suivants correspondent: préciser le nom complet ou le code\n" + + "\n".join(e), + ) + else: # une seule reponse ! + return 1, r[0][0].strip() + + security.declareProtected(ScoEntrepriseView, "do_entreprise_list_by_contact") + + def do_entreprise_list_by_contact( + self, args={}, operator="and", test="=", sortkey=None + ): + """Recherche dans entreprises, avec date de contact""" + # (fonction ad-hoc car requete sur plusieurs tables) + raise NotImplementedError + # XXXXX fonction non achevee , non testee... + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + vals = dictfilter(args, self.dbfields) + # DBSelect + what = ["*"] + operator = " " + operator + " " + cond = " E.entreprise_id = C.entreprise_id " + if vals: + cond += " where " + operator.join( + ["%s%s%%(%s)s" % (x, test, x) for x in vals.keys() if vals[x] != None] + ) + cnuls = " and ".join( + ["%s is NULL" % x for x in vals.keys() if vals[x] is None] + ) + if cnuls: + cond = cond + " and " + cnuls + else: + cond += "" + cursor.execute( + "select distinct" + + ", ".join(what) + + " from entreprises E, entreprise_contact C " + + cond + + orderby, + vals, + ) + titles, res = [x[0] for x in cursor.description], cursor.fetchall() + # + R = [] + for r in res: + d = {} + for i in range(len(titles)): + v = r[i] + # value not formatted ! (see EditableTable.list()) + d[titles[i]] = v + R.append(d) + return R + + # -------- Formulaires: traductions du DTML + security.declareProtected(ScoEntrepriseChange, "entreprise_create") + + def entreprise_create(self, REQUEST=None): + """Form. création entreprise""" + context = self + H = [ + self.entreprise_header(REQUEST, page_title="Création d'une entreprise"), + """

    Création d'une entreprise

    """, + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ("nom", {"size": 25, "title": "Nom de l'entreprise"}), + ( + "adresse", + {"size": 30, "title": "Adresse", "explanation": "(numéro, rue)"}, + ), + ("codepostal", {"size": 8, "title": "Code Postal"}), + ("ville", {"size": 30, "title": "Ville"}), + ("pays", {"size": 30, "title": "Pays", "default": "France"}), + ( + "localisation", + { + "input_type": "menu", + "labels": ["Ile de France", "Province", "Etranger"], + "allowed_values": ["IDF", "Province", "Etranger"], + }, + ), + ("secteur", {"size": 30, "title": "Secteur d'activités"}), + ( + "privee", + { + "input_type": "menu", + "title": "Statut", + "labels": [ + "Entreprise privee", + "Entreprise Publique", + "Association", + ], + "allowed_values": ["privee", "publique", "association"], + }, + ), + ( + "plus10salaries", + { + "title": "Masse salariale", + "type": "integer", + "input_type": "menu", + "labels": [ + "10 salariés ou plus", + "Moins de 10 salariés", + "Inconnue", + ], + "allowed_values": [1, 0, -1], + }, + ), + ( + "qualite_relation", + { + "title": "Qualité relation IUT/Entreprise", + "input_type": "menu", + "default": "-1", + "labels": [ + "Très bonne", + "Bonne", + "Moyenne", + "Mauvaise", + "Inconnue", + ], + "allowed_values": ["100", "75", "50", "25", "-1"], + }, + ), + ("contact_origine", {"size": 30, "title": "Origine du contact"}), + ( + "note", + {"input_type": "textarea", "rows": 3, "cols": 40, "title": "Note"}, + ), + ), + cancelbutton="Annuler", + submitlabel="Ajouter cette entreprise", + readonly=REQUEST["_read_only"], + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + context.entreprise_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + self.do_entreprise_create(tf[2]) + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + + security.declareProtected(ScoEntrepriseView, "entreprise_edit") + + def entreprise_edit(self, entreprise_id, REQUEST=None, start=1): + """Form. edit entreprise""" + context = self + authuser = REQUEST.AUTHENTICATED_USER + readonly = not authuser.has_permission(ScoEntrepriseChange, self) + F = self.do_entreprise_list(args={"entreprise_id": entreprise_id})[0] + H = [ + self.entreprise_header(REQUEST, page_title="Entreprise"), + """

    %(nom)s

    """ % F, + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ("entreprise_id", {"default": entreprise_id, "input_type": "hidden"}), + ("start", {"default": 1, "input_type": "hidden"}), + ( + "date_creation", + {"default": time.strftime("%Y-%m-%d"), "input_type": "hidden"}, + ), + ("nom", {"size": 25, "title": "Nom de l'entreprise"}), + ( + "adresse", + {"size": 30, "title": "Adresse", "explanation": "(numéro, rue)"}, + ), + ("codepostal", {"size": 8, "title": "Code Postal"}), + ("ville", {"size": 30, "title": "Ville"}), + ("pays", {"size": 30, "title": "Pays", "default": "France"}), + ( + "localisation", + { + "input_type": "menu", + "labels": ["Ile de France", "Province", "Etranger"], + "allowed_values": ["IDF", "Province", "Etranger"], + }, + ), + ("secteur", {"size": 30, "title": "Secteur d'activités"}), + ( + "privee", + { + "input_type": "menu", + "title": "Statut", + "labels": [ + "Entreprise privee", + "Entreprise Publique", + "Association", + ], + "allowed_values": ["privee", "publique", "association"], + }, + ), + ( + "plus10salaries", + { + "title": "Masse salariale", + "input_type": "menu", + "labels": [ + "10 salariés ou plus", + "Moins de 10 salariés", + "Inconnue", + ], + "allowed_values": ["1", "0", "-1"], + }, + ), + ( + "qualite_relation", + { + "title": "Qualité relation IUT/Entreprise", + "input_type": "menu", + "labels": [ + "Très bonne", + "Bonne", + "Moyenne", + "Mauvaise", + "Inconnue", + ], + "allowed_values": ["100", "75", "50", "25", "-1"], + }, + ), + ("contact_origine", {"size": 30, "title": "Origine du contact"}), + ( + "note", + {"input_type": "textarea", "rows": 3, "cols": 40, "title": "Note"}, + ), + ), + cancelbutton="Annuler", + initvalues=F, + submitlabel="Modifier les valeurs", + readonly=readonly, + ) + + if tf[0] == 0: + H.append(tf[1]) + Cl = self.do_entreprise_correspondant_list( + args={"entreprise_id": F["entreprise_id"]} + ) + Cts = self.do_entreprise_contact_list( + args={"entreprise_id": F["entreprise_id"]} + ) + if not readonly: + H.append( + """

    %s Supprimer cette entreprise

    """ + % ( + icontag("delete_img", title="delete", border="0"), + F["entreprise_id"], + ) + ) + if len(Cl): + H.append( + """

    %d correspondants dans l'entreprise %s (liste complète) :

    +") + if len(Cts): + H.append( + """

    %d contacts avec l'entreprise %s (liste complète) :

      """ + % (len(Cts), F["nom"], F["entreprise_id"]) + ) + for c in Cts: + H.append( + """
    • %s   """ + % (c["entreprise_contact_id"], c["date"]) + ) + if c["type_contact"]: + H.append(c["type_contact"]) + if c["etudid"]: + etud = self.getEtudInfo(etudid=c["etudid"], filled=1) + if etud: + etud = etud[0] + H.append( + """%s""" + % (self.ScoURL(), c["etudid"], etud["nomprenom"]) + ) + if c["description"]: + H.append("(%s)" % c["description"]) + H.append("
    • ") + H.append("
    ") + return "\n".join(H) + context.entreprise_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1 + "?start=" + start) + else: + self.do_entreprise_edit(tf[2]) + return REQUEST.RESPONSE.redirect(REQUEST.URL1 + "?start=" + start) + + # --- Misc tools.... ------------------ + security.declareProtected(ScoEntrepriseView, "str_abbrev") + + def str_abbrev(self, s, maxlen): + "abreviation" + if s == None: + return "?" + if len(s) < maxlen: + return s + return s[: maxlen - 3] + "..." + + security.declareProtected(ScoEntrepriseView, "setPageSizeCookie") + + def setPageSizeCookie(self, REQUEST=None): + "set page size cookie" + RESPONSE = REQUEST.RESPONSE + # + if REQUEST.form.has_key("entreprise_page_size"): + RESPONSE.setCookie( + "entreprise_page_size", + REQUEST.form["entreprise_page_size"], + path="/", + expires="Wed, 31-Dec-2025 23:55:00 GMT", + ) + RESPONSE.redirect(REQUEST.form["target_url"]) + + security.declareProtected(ScoEntrepriseView, "make_link_create_corr") + + def make_link_create_corr(self, entreprise_id): + "yet another stupid code snippet" + return ( + 'créer un nouveau correspondant' + ) + + +# -------------------------------------------------------------------- +# +# Zope Product Administration +# +# -------------------------------------------------------------------- +def manage_addZEntreprises( + self, id="id_ZEntreprises", title="The Title for ZEntreprises Object", REQUEST=None +): + "Add a ZEntreprises instance to a folder." + self._setObject(id, ZEntreprises(id, title)) + if REQUEST is not None: + return self.manage_main(self, REQUEST) + # return self.manage_editForm(self, REQUEST) + + +# The form used to get the instance id from the user. +# manage_addZAbsencesForm = DTMLFile('dtml/manage_addZAbsencesForm', globals()) diff --git a/ZNotes.py b/ZNotes.py new file mode 100644 index 00000000..b9946c86 --- /dev/null +++ b/ZNotes.py @@ -0,0 +1,3647 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +"""Interface Zope <-> Notes +""" + +from sets import Set + +from sco_zope import * + +# --------------- + +from notesdb import * +from notes_log import log, sendAlarm +import scolog +from scolog import logdb +from sco_utils import * +import htmlutils +import sco_excel + +# import notes_users +from gen_tables import GenTable +import sco_cache +import scolars +import sco_news +from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC + +import sco_formsemestre +import sco_formsemestre_edit +import sco_formsemestre_status +import sco_formsemestre_inscriptions +import sco_formsemestre_custommenu +import sco_moduleimpl_status +import sco_moduleimpl_inscriptions +import sco_evaluations +import sco_groups +import sco_edit_ue +import sco_edit_formation +import sco_edit_matiere +import sco_edit_module +import sco_tag_module +import sco_bulletins +import sco_bulletins_pdf +import sco_recapcomplet +import sco_liste_notes +import sco_saisie_notes +import sco_placement +import sco_undo_notes +import sco_formations +import sco_report +import sco_lycee +import sco_poursuite_dut +import pe_view +import sco_debouche +import sco_ue_external +import sco_cost_formation +import sco_formsemestre_validation +import sco_parcours_dut +import sco_codes_parcours +import sco_pvjury +import sco_pvpdf +import sco_prepajury +import sco_inscr_passage +import sco_synchro_etuds +import sco_archives +import sco_apogee_csv +import sco_etape_apogee_view +import sco_apogee_compare +import sco_semset +import sco_export_results +import sco_formsemestre_exterieurs + +from sco_pdf import PDFLOCK +from notes_table import * +import VERSION + +# +# Cache global: chaque instance, repérée par sa connexion db, a un cache +# qui est recréé à la demande +# +CACHE_formsemestre_inscription = {} +CACHE_evaluations = {} + +# --------------- + + +class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit): + + "ZNotes object" + + meta_type = "ZNotes" + security = ClassSecurityInfo() + + # 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 + + ( + # this line is kept as an example with the files : + # dtml/manage_editZScolarForm.dtml + # html/ZScolar-edit.stx + # {'label': 'Properties', 'action': 'manage_editForm',}, + {"label": "View", "action": "index_html"}, + ) + + Item.manage_options # add the 'Undo' & 'Owner' tab + + RoleManager.manage_options # add the 'Security' tab + ) + + # no permissions, only called from python + def __init__(self, id, title): + "initialise a new instance of ZNotes" + self.id = id + self.title = title + + # The form used to edit this object + def manage_editZNotes(self, title, RESPONSE=None): + "Changes the instance values" + self.title = title + self._p_changed = 1 + RESPONSE.redirect("manage_editForm") + + def _getNotesCache(self): + "returns CacheNotesTable instance for us" + u = self.GetDBConnexionString() # identifie le dept de facon fiable + if not NOTES_CACHE_INST.has_key(u): + log("getNotesCache: creating cache for %s" % u) + NOTES_CACHE_INST[u] = CacheNotesTable() + return NOTES_CACHE_INST[u] + + def _inval_cache( + self, formsemestre_id=None, pdfonly=False, formsemestre_id_list=None + ): # > + "expire cache pour un semestre (ou tous si pas d'argument)" + if formsemestre_id_list: + for formsemestre_id in formsemestre_id_list: + self._getNotesCache().inval_cache( + self, formsemestre_id=formsemestre_id, pdfonly=pdfonly + ) + # Affecte aussi cache inscriptions + self.get_formsemestre_inscription_cache().inval_cache( + key=formsemestre_id + ) # > + else: + self._getNotesCache().inval_cache( + self, formsemestre_id=formsemestre_id, pdfonly=pdfonly + ) # > + # Affecte aussi cache inscriptions + self.get_formsemestre_inscription_cache().inval_cache( + key=formsemestre_id + ) # > + + security.declareProtected(ScoView, "clearcache") + + def clearcache(self, REQUEST=None): + "Efface les caches de notes (utile pendant developpement slt)" + log("*** clearcache request") + # Debugging code: compare results before and after cache reconstruction + # (_should_ be identicals !) + # Compare XML representation + cache = self._getNotesCache() + formsemestre_ids = cache.get_cached_formsemestre_ids() + docs_before = [] + for formsemestre_id in formsemestre_ids: + docs_before.append( + sco_recapcomplet.do_formsemestre_recapcomplet( + self, REQUEST, formsemestre_id, format="xml", xml_nodate=True + ) + ) + # + cache.inval_cache(self) # > + # Rebuild cache (useful only to debug) + docs_after = [] + for formsemestre_id in formsemestre_ids: + docs_after.append( + sco_recapcomplet.do_formsemestre_recapcomplet( + self, REQUEST, formsemestre_id, format="xml", xml_nodate=True + ) + ) + if docs_before != docs_after: + log("clearcache: inconsistency !") + txt = "before=" + repr(docs_before) + "\n\nafter=" + repr(docs_after) + "\n" + log(txt) + sendAlarm(self, "clearcache: inconsistency !", txt) + + # -------------------------------------------------------------------- + # + # NOTES (top level) + # + # -------------------------------------------------------------------- + # XXX essai + security.declareProtected(ScoView, "gloups") + + def gloups(self, REQUEST): + "essai gloups" + return "" + # return pdfbulletins.essaipdf(REQUEST) + # return sendPDFFile(REQUEST, pdfbulletins.pdftrombino(0,0), 'toto.pdf' ) + + # Python methods: + security.declareProtected(ScoView, "formsemestre_status") + formsemestre_status = sco_formsemestre_status.formsemestre_status + + security.declareProtected(ScoImplement, "formsemestre_createwithmodules") + formsemestre_createwithmodules = ( + sco_formsemestre_edit.formsemestre_createwithmodules + ) + + security.declareProtected(ScoView, "formsemestre_editwithmodules") + formsemestre_editwithmodules = sco_formsemestre_edit.formsemestre_editwithmodules + + security.declareProtected(ScoView, "formsemestre_clone") + formsemestre_clone = sco_formsemestre_edit.formsemestre_clone + + security.declareProtected(ScoChangeFormation, "formsemestre_associate_new_version") + formsemestre_associate_new_version = ( + sco_formsemestre_edit.formsemestre_associate_new_version + ) + + security.declareProtected(ScoImplement, "formsemestre_delete") + formsemestre_delete = sco_formsemestre_edit.formsemestre_delete + security.declareProtected(ScoImplement, "formsemestre_delete2") + formsemestre_delete2 = sco_formsemestre_edit.formsemestre_delete2 + + security.declareProtected(ScoView, "formsemestre_recapcomplet") + formsemestre_recapcomplet = sco_recapcomplet.formsemestre_recapcomplet + + security.declareProtected(ScoView, "moduleimpl_status") + moduleimpl_status = sco_moduleimpl_status.moduleimpl_status + + security.declareProtected(ScoView, "formsemestre_description") + formsemestre_description = sco_formsemestre_status.formsemestre_description + + security.declareProtected(ScoView, "formsemestre_lists") + formsemestre_lists = sco_formsemestre_status.formsemestre_lists + + security.declareProtected(ScoView, "formsemestre_status_menubar") + formsemestre_status_menubar = sco_formsemestre_status.formsemestre_status_menubar + security.declareProtected(ScoChangeFormation, "formation_create") + formation_create = sco_edit_formation.formation_create + security.declareProtected(ScoChangeFormation, "formation_delete") + formation_delete = sco_edit_formation.formation_delete + security.declareProtected(ScoChangeFormation, "formation_edit") + formation_edit = sco_edit_formation.formation_edit + + security.declareProtected(ScoView, "formsemestre_bulletinetud") + formsemestre_bulletinetud = sco_bulletins.formsemestre_bulletinetud + + security.declareProtected(ScoView, "formsemestre_evaluations_cal") + formsemestre_evaluations_cal = sco_evaluations.formsemestre_evaluations_cal + security.declareProtected(ScoView, "formsemestre_evaluations_delai_correction") + formsemestre_evaluations_delai_correction = ( + sco_evaluations.formsemestre_evaluations_delai_correction + ) + + security.declareProtected(ScoView, "module_evaluation_renumber") + module_evaluation_renumber = sco_evaluations.module_evaluation_renumber + security.declareProtected(ScoView, "module_evaluation_move") + module_evaluation_move = sco_evaluations.module_evaluation_move + + security.declareProtected(ScoView, "formsemestre_list_saisies_notes") + formsemestre_list_saisies_notes = sco_undo_notes.formsemestre_list_saisies_notes + + security.declareProtected(ScoChangeFormation, "ue_create") + ue_create = sco_edit_ue.ue_create + security.declareProtected(ScoChangeFormation, "ue_delete") + ue_delete = sco_edit_ue.ue_delete + security.declareProtected(ScoChangeFormation, "ue_edit") + ue_edit = sco_edit_ue.ue_edit + security.declareProtected(ScoView, "ue_list") + ue_list = sco_edit_ue.ue_list + security.declareProtected(ScoView, "ue_sharing_code") + ue_sharing_code = sco_edit_ue.ue_sharing_code + security.declareProtected(ScoChangeFormation, "edit_ue_set_code_apogee") + edit_ue_set_code_apogee = sco_edit_ue.edit_ue_set_code_apogee + security.declareProtected(ScoView, "formation_table_recap") + formation_table_recap = sco_edit_ue.formation_table_recap + security.declareProtected(ScoChangeFormation, "formation_add_malus_modules") + formation_add_malus_modules = sco_edit_module.formation_add_malus_modules + + security.declareProtected(ScoChangeFormation, "matiere_create") + matiere_create = sco_edit_matiere.matiere_create + security.declareProtected(ScoChangeFormation, "matiere_delete") + matiere_delete = sco_edit_matiere.matiere_delete + security.declareProtected(ScoChangeFormation, "matiere_edit") + matiere_edit = sco_edit_matiere.matiere_edit + + security.declareProtected(ScoChangeFormation, "module_create") + module_create = sco_edit_module.module_create + security.declareProtected(ScoChangeFormation, "module_delete") + module_delete = sco_edit_module.module_delete + security.declareProtected(ScoChangeFormation, "module_edit") + module_edit = sco_edit_module.module_edit + security.declareProtected(ScoChangeFormation, "edit_module_set_code_apogee") + edit_module_set_code_apogee = sco_edit_module.edit_module_set_code_apogee + security.declareProtected(ScoView, "module_list") + module_list = sco_edit_module.module_list + # Tags + security.declareProtected(ScoView, "module_tag_search") + module_tag_search = sco_tag_module.module_tag_search + security.declareProtected( + ScoView, "module_tag_set" + ) # should be ScoEditFormationTags, but not present in old installs => check in method + module_tag_set = sco_tag_module.module_tag_set + + # + security.declareProtected(ScoView, "index_html") + + def index_html(self, REQUEST=None): + "Page accueil formations" + lockicon = icontag( + "lock32_img", title="Comporte des semestres verrouillés", border="0" + ) + suppricon = icontag( + "delete_small_img", border="0", alt="supprimer", title="Supprimer" + ) + editicon = icontag( + "edit_img", border="0", alt="modifier", title="Modifier titres et code" + ) + + editable = REQUEST.AUTHENTICATED_USER.has_permission(ScoChangeFormation, self) + + H = [ + self.sco_header(REQUEST, page_title="Programmes formations"), + """

    Programmes pédagogiques

    + """, + ] + T = sco_formations.formation_list_table(self, REQUEST=REQUEST) + + H.append(T.html()) + + if editable: + H.append( + """

    Créer une formation

    +

    Importer une formation (xml)

    +

    Une "formation" est un programme pédagogique structuré en UE, matières et modules. Chaque semestre se réfère à une formation. La modification d'une formation affecte tous les semestres qui s'y réfèrent.

    + """ + ) + + H.append(self.sco_footer(REQUEST)) + return "\n".join(H) + + # -------------------------------------------------------------------- + # + # Notes Methods + # + # -------------------------------------------------------------------- + + # --- Formations + _formationEditor = EditableTable( + "notes_formations", + "formation_id", + ( + "formation_id", + "acronyme", + "titre", + "titre_officiel", + "version", + "formation_code", + "type_parcours", + "code_specialite", + ), + sortkey="acronyme", + ) + + security.declareProtected(ScoChangeFormation, "do_formation_create") + + def do_formation_create(self, args, REQUEST): + "create a formation" + cnx = self.GetDBConnexion() + # check unique acronyme/titre/version + a = args.copy() + if a.has_key("formation_id"): + del a["formation_id"] + F = self.formation_list(args=a) + if len(F) > 0: + log( + "do_formation_create: error: %d formations matching args=%s" + % (len(F), a) + ) + raise ScoValueError("Formation non unique (%s) !" % str(a)) + # Si pas de formation_code, l'enleve (default SQL) + if args.has_key("formation_code") and not args["formation_code"]: + del args["formation_code"] + # + r = self._formationEditor.create(cnx, args) + + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + text="Création de la formation %(titre)s (%(acronyme)s)" % args, + ) + return r + + security.declareProtected(ScoChangeFormation, "do_formation_delete") + + def do_formation_delete(self, oid, REQUEST): + """delete a formation (and all its UE, matieres, modules) + XXX delete all ues, will break if there are validations ! USE WITH CARE ! + """ + F = self.formation_list(args={"formation_id": oid})[0] + if self.formation_has_locked_sems(oid): + raise ScoLockedFormError() + cnx = self.GetDBConnexion() + # delete all UE in this formation + ues = self.do_ue_list({"formation_id": oid}) + for ue in ues: + self._do_ue_delete(ue["ue_id"], REQUEST=REQUEST, force=True) + + self._formationEditor.delete(cnx, oid) + + # news + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=oid, + text="Suppression de la formation %(acronyme)s" % F, + ) + + security.declareProtected(ScoView, "formation_list") + + def formation_list(self, format=None, REQUEST=None, formation_id=None, args={}): + """List formation(s) with given id, or matching args + (when args is given, formation_id is ignored). + """ + # logCallStack() + if not args: + if formation_id is None: + args = {} + else: + args = {"formation_id": formation_id} + cnx = self.GetDBConnexion() + r = self._formationEditor.list(cnx, args=args) + # log('%d formations found' % len(r)) + return sendResult(REQUEST, r, name="formation", format=format) + + security.declareProtected(ScoView, "formation_export") + + def formation_export( + self, formation_id, export_ids=False, format=None, REQUEST=None + ): + "Export de la formation au format indiqué (xml ou json)" + return sco_formations.formation_export( + self, formation_id, export_ids=export_ids, format=format, REQUEST=REQUEST + ) + + security.declareProtected(ScoChangeFormation, "formation_import_xml") + + def formation_import_xml(self, file, REQUEST): + "import d'une formation en XML" + log("formation_import_xml") + doc = file.read() + return sco_formations.formation_import_xml(self, REQUEST, doc) + + security.declareProtected(ScoChangeFormation, "formation_import_xml_form") + + def formation_import_xml_form(self, REQUEST): + "form import d'une formation en XML" + H = [ + self.sco_header(page_title="Import d'une formation", REQUEST=REQUEST), + """

    Import d'une formation

    +

    Création d'une formation (avec UE, matières, modules) + à partir un fichier XML (réservé aux utilisateurs avertis)

    + """, + ] + footer = self.sco_footer(REQUEST) + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + (("xmlfile", {"input_type": "file", "title": "Fichier XML", "size": 30}),), + submitlabel="Importer", + cancelbutton="Annuler", + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + footer + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + formation_id, junk, junk = self.formation_import_xml( + tf[2]["xmlfile"], REQUEST + ) + + return ( + "\n".join(H) + + """

    Import effectué !

    +

    Voir la formation

    """ + % formation_id + + footer + ) + + security.declareProtected(ScoChangeFormation, "formation_create_new_version") + + def formation_create_new_version(self, formation_id, redirect=True, REQUEST=None): + "duplicate formation, with new version number" + xml = sco_formations.formation_export( + self, formation_id, export_ids=True, format="xml" + ) + new_id, modules_old2new, ues_old2new = sco_formations.formation_import_xml( + self, REQUEST, xml + ) + # news + F = self.formation_list(args={"formation_id": new_id})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=new_id, + text="Nouvelle version de la formation %(acronyme)s" % F, + ) + if redirect: + return REQUEST.RESPONSE.redirect( + "ue_list?formation_id=" + new_id + "&msg=Nouvelle version !" + ) + else: + return new_id, modules_old2new, ues_old2new + + # --- UE + _ueEditor = EditableTable( + "notes_ue", + "ue_id", + ( + "ue_id", + "formation_id", + "acronyme", + "numero", + "titre", + "type", + "ue_code", + "ects", + "is_external", + "code_apogee", + "coefficient", + ), + sortkey="numero", + input_formators={"type": int_null_is_zero}, + output_formators={ + "numero": int_null_is_zero, + "ects": float_null_is_null, + "coefficient": float_null_is_zero, + }, + ) + + security.declareProtected(ScoChangeFormation, "do_ue_create") + + def do_ue_create(self, args, REQUEST): + "create an ue" + cnx = self.GetDBConnexion() + # check duplicates + ues = self.do_ue_list( + {"formation_id": args["formation_id"], "acronyme": args["acronyme"]} + ) + if ues: + raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) + # create + r = self._ueEditor.create(cnx, args) + + # news + F = self.formation_list(args={"formation_id": args["formation_id"]})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=args["formation_id"], + text="Modification de la formation %(acronyme)s" % F, + ) + return r + + def _do_ue_delete(self, ue_id, delete_validations=False, REQUEST=None, force=False): + "delete UE and attached matieres (but not modules (it should ?))" + cnx = self.GetDBConnexion() + log( + "do_ue_delete: ue_id=%s, delete_validations=%s" + % (ue_id, delete_validations) + ) + # check + ue = self.do_ue_list({"ue_id": ue_id}) + if not ue: + raise ScoValueError("UE inexistante !") + ue = ue[0] + if self.ue_is_locked(ue["ue_id"]): + raise ScoLockedFormError() + # Il y a-t-il des etudiants ayant validé cette UE ? + # si oui, propose de supprimer les validations + validations = sco_parcours_dut.scolar_formsemestre_validation_list( + cnx, args={"ue_id": ue_id} + ) + if validations and not delete_validations and not force: + return self.confirmDialog( + "

    %d étudiants ont validé l'UE %s (%s)

    Si vous supprimez cette UE, ces validations vont être supprimées !

    " + % (len(validations), ue["acronyme"], ue["titre"]), + dest_url="", + REQUEST=REQUEST, + target_variable="delete_validations", + cancel_url="ue_list?formation_id=%s" % ue["formation_id"], + parameters={"ue_id": ue_id, "dialog_confirmed": 1}, + ) + if delete_validations: + log("deleting all validations of UE %s" % ue_id) + SimpleQuery( + self, + "DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s", + {"ue_id": ue_id}, + ) + + # delete all matiere in this UE + mats = self.do_matiere_list({"ue_id": ue_id}) + for mat in mats: + self.do_matiere_delete(mat["matiere_id"], REQUEST) + # delete uecoef and events + SimpleQuery( + self, + "DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s", + {"ue_id": ue_id}, + ) + SimpleQuery( + self, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id} + ) + cnx = self.GetDBConnexion() + self._ueEditor.delete(cnx, ue_id) + self._inval_cache() # > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement utilisé: acceptable de tout invalider ?) + # news + F = self.formation_list(args={"formation_id": ue["formation_id"]})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=ue["formation_id"], + text="Modification de la formation %(acronyme)s" % F, + ) + # + if not force: + return REQUEST.RESPONSE.redirect( + REQUEST.URL1 + "/ue_list?formation_id=" + str(ue["formation_id"]) + ) + else: + return None + + security.declareProtected(ScoView, "do_ue_list") + + def do_ue_list(self, *args, **kw): + "list UEs" + cnx = self.GetDBConnexion() + return self._ueEditor.list(cnx, *args, **kw) + + # --- Matieres + _matiereEditor = EditableTable( + "notes_matieres", + "matiere_id", + ("matiere_id", "ue_id", "numero", "titre"), + sortkey="numero", + output_formators={"numero": int_null_is_zero}, + ) + + security.declareProtected(ScoChangeFormation, "do_matiere_create") + + def do_matiere_create(self, args, REQUEST): + "create a matiere" + cnx = self.GetDBConnexion() + # check + ue = self.do_ue_list({"ue_id": args["ue_id"]})[0] + # create matiere + r = self._matiereEditor.create(cnx, args) + + # news + F = self.formation_list(args={"formation_id": ue["formation_id"]})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=ue["formation_id"], + text="Modification de la formation %(acronyme)s" % F, + ) + return r + + security.declareProtected(ScoChangeFormation, "do_matiere_delete") + + def do_matiere_delete(self, oid, REQUEST): + "delete matiere and attached modules" + cnx = self.GetDBConnexion() + # check + mat = self.do_matiere_list({"matiere_id": oid})[0] + ue = self.do_ue_list({"ue_id": mat["ue_id"]})[0] + locked = self.matiere_is_locked(mat["matiere_id"]) + if locked: + log("do_matiere_delete: mat=%s" % mat) + log("do_matiere_delete: ue=%s" % ue) + log("do_matiere_delete: locked sems: %s" % locked) + raise ScoLockedFormError() + log("do_matiere_delete: matiere_id=%s" % oid) + # delete all modules in this matiere + mods = self.do_module_list({"matiere_id": oid}) + for mod in mods: + self.do_module_delete(mod["module_id"], REQUEST) + self._matiereEditor.delete(cnx, oid) + + # news + F = self.formation_list(args={"formation_id": ue["formation_id"]})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=ue["formation_id"], + text="Modification de la formation %(acronyme)s" % F, + ) + + security.declareProtected(ScoView, "do_matiere_list") + + def do_matiere_list(self, *args, **kw): + "list matieres" + cnx = self.GetDBConnexion() + return self._matiereEditor.list(cnx, *args, **kw) + + security.declareProtected(ScoChangeFormation, "do_matiere_edit") + + def do_matiere_edit(self, *args, **kw): + "edit a matiere" + cnx = self.GetDBConnexion() + # check + mat = self.do_matiere_list({"matiere_id": args[0]["matiere_id"]})[0] + ue = self.do_ue_list({"ue_id": mat["ue_id"]})[0] + if self.matiere_is_locked(mat["matiere_id"]): + raise ScoLockedFormError() + # edit + self._matiereEditor.edit(cnx, *args, **kw) + self._inval_cache() # > modif matiere + + security.declareProtected(ScoView, "do_matiere_formation_id") + + def do_matiere_formation_id(self, matiere_id): + "get formation_id from matiere" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select UE.formation_id from notes_matieres M, notes_ue UE where M.matiere_id = %(matiere_id)s and M.ue_id = UE.ue_id", + {"matiere_id": matiere_id}, + ) + res = cursor.fetchall() + return res[0][0] + + # --- Modules + _moduleEditor = EditableTable( + "notes_modules", + "module_id", + ( + "module_id", + "titre", + "code", + "abbrev", + "heures_cours", + "heures_td", + "heures_tp", + "coefficient", + "ue_id", + "matiere_id", + "formation_id", + "semestre_id", + "numero", + "code_apogee", + "module_type" + #'ects' + ), + sortkey="numero, code, titre", + output_formators={ + "heures_cours": float_null_is_zero, + "heures_td": float_null_is_zero, + "heures_tp": float_null_is_zero, + "numero": int_null_is_zero, + "coefficient": float_null_is_zero, + "module_type": int_null_is_zero + #'ects' : float_null_is_null + }, + ) + + security.declareProtected(ScoChangeFormation, "do_module_create") + + def do_module_create(self, args, REQUEST): + "create a module" + # create + cnx = self.GetDBConnexion() + r = self._moduleEditor.create(cnx, args) + + # news + F = self.formation_list(args={"formation_id": args["formation_id"]})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=args["formation_id"], + text="Modification de la formation %(acronyme)s" % F, + ) + return r + + security.declareProtected(ScoChangeFormation, "do_module_delete") + + def do_module_delete(self, oid, REQUEST): + "delete module" + mod = self.do_module_list({"module_id": oid})[0] + if self.module_is_locked(mod["module_id"]): + raise ScoLockedFormError() + + # S'il y a des moduleimpls, on ne peut pas detruire le module ! + mods = self.do_moduleimpl_list(module_id=oid) + if mods: + err_page = self.confirmDialog( + message="""

    Destruction du module impossible car il est utilisé dans des semestres existants !

    """, + helpmsg="""Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.""", + dest_url="ue_list", + parameters={"formation_id": mod["formation_id"]}, + REQUEST=REQUEST, + ) + raise ScoGenError(err_page) + # delete + cnx = self.GetDBConnexion() + self._moduleEditor.delete(cnx, oid) + + # news + F = self.formation_list(args={"formation_id": mod["formation_id"]})[0] + sco_news.add( + self, + REQUEST, + typ=NEWS_FORM, + object=mod["formation_id"], + text="Modification de la formation %(acronyme)s" % F, + ) + + security.declareProtected(ScoView, "do_module_list") + + def do_module_list(self, *args, **kw): + "list modules" + cnx = self.GetDBConnexion() + return self._moduleEditor.list(cnx, *args, **kw) + + security.declareProtected(ScoChangeFormation, "do_module_edit") + + def do_module_edit(self, val): + "edit a module" + # check + mod = self.do_module_list({"module_id": val["module_id"]})[0] + if self.module_is_locked(mod["module_id"]): + # formation verrouillée: empeche de modifier certains champs: + protected_fields = ("coefficient", "ue_id", "matiere_id", "semestre_id") + for f in protected_fields: + if f in val: + del val[f] + # edit + cnx = self.GetDBConnexion() + self._moduleEditor.edit(cnx, val) + + sems = sco_formsemestre.do_formsemestre_list( + self, args={"formation_id": mod["formation_id"]} + ) + if sems: + self._inval_cache( + formsemestre_id_list=[s["formsemestre_id"] for s in sems] + ) # > modif module + + # + security.declareProtected(ScoView, "formation_has_locked_sems") + + def formation_has_locked_sems(self, formation_id): + "True if there is a locked formsemestre in this formation" + sems = sco_formsemestre.do_formsemestre_list( + self, args={"formation_id": formation_id, "etat": "0"} + ) + return sems + + security.declareProtected(ScoView, "formation_count_sems") + + def formation_count_sems(self, formation_id): + "Number of formsemestre in this formation (locked or not)" + sems = sco_formsemestre.do_formsemestre_list( + self, args={"formation_id": formation_id} + ) + return len(sems) + + security.declareProtected(ScoView, "module_count_moduleimpls") + + def module_count_moduleimpls(self, module_id): + "Number of moduleimpls using this module" + mods = self.do_moduleimpl_list(module_id=module_id) + return len(mods) + + security.declareProtected(ScoView, "module_is_locked") + + def module_is_locked(self, module_id): + """True if UE should not be modified + (used in a locked formsemestre) + """ + r = SimpleDictFetch( + self, + """SELECT mi.* from notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi + WHERE mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id + AND mi.module_id = %(module_id)s AND sem.etat = 0 + """, + {"module_id": module_id}, + ) + return len(r) > 0 + + security.declareProtected(ScoView, "matiere_is_locked") + + def matiere_is_locked(self, matiere_id): + """True if matiere should not be modified + (contains modules used in a locked formsemestre) + """ + r = SimpleDictFetch( + self, + """SELECT ma.* from notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi + WHERE ma.matiere_id = mod.matiere_id AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id + AND ma.matiere_id = %(matiere_id)s AND sem.etat = 0 + """, + {"matiere_id": matiere_id}, + ) + return len(r) > 0 + + security.declareProtected(ScoView, "ue_is_locked") + + def ue_is_locked(self, ue_id): + """True if module should not be modified + (contains modules used in a locked formsemestre) + """ + r = SimpleDictFetch( + self, + """SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi + WHERE ue.ue_id = mod.ue_id + AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id + AND ue.ue_id = %(ue_id)s AND sem.etat = 0 + """, + {"ue_id": ue_id}, + ) + return len(r) > 0 + + security.declareProtected(ScoChangeFormation, "module_move") + + def module_move(self, module_id, after=0, REQUEST=None, redirect=1): + """Move before/after previous one (decrement/increment numero)""" + module = self.do_module_list({"module_id": module_id})[0] + redirect = int(redirect) + after = int(after) # 0: deplace avant, 1 deplace apres + if after not in (0, 1): + raise ValueError('invalid value for "after"') + formation_id = module["formation_id"] + others = self.do_module_list({"matiere_id": module["matiere_id"]}) + # log('others=%s' % others) + if len(others) > 1: + idx = [p["module_id"] for p in others].index(module_id) + # log('module_move: after=%s idx=%s' % (after, idx)) + neigh = None # object to swap with + if after == 0 and idx > 0: + neigh = others[idx - 1] + elif after == 1 and idx < len(others) - 1: + neigh = others[idx + 1] + if neigh: # + # swap numero between partition and its neighbor + # log('moving module %s' % module_id) + cnx = self.GetDBConnexion() + module["numero"], neigh["numero"] = neigh["numero"], module["numero"] + if module["numero"] == neigh["numero"]: + neigh["numero"] -= 2 * after - 1 + self._moduleEditor.edit(cnx, module) + self._moduleEditor.edit(cnx, neigh) + + # redirect to ue_list page: + if redirect: + return REQUEST.RESPONSE.redirect("ue_list?formation_id=" + formation_id) + + def ue_move(self, ue_id, after=0, REQUEST=None, redirect=1): + """Move UE before/after previous one (decrement/increment numero)""" + o = self.do_ue_list({"ue_id": ue_id})[0] + # log('ue_move %s (#%s) after=%s' % (ue_id, o['numero'], after)) + redirect = int(redirect) + after = int(after) # 0: deplace avant, 1 deplace apres + if after not in (0, 1): + raise ValueError('invalid value for "after"') + formation_id = o["formation_id"] + others = self.do_ue_list({"formation_id": formation_id}) + if len(others) > 1: + idx = [p["ue_id"] for p in others].index(ue_id) + neigh = None # object to swap with + if after == 0 and idx > 0: + neigh = others[idx - 1] + elif after == 1 and idx < len(others) - 1: + neigh = others[idx + 1] + if neigh: # + # swap numero between partition and its neighbor + # log('moving ue %s (neigh #%s)' % (ue_id, neigh['numero'])) + cnx = self.GetDBConnexion() + o["numero"], neigh["numero"] = neigh["numero"], o["numero"] + if o["numero"] == neigh["numero"]: + neigh["numero"] -= 2 * after - 1 + self._ueEditor.edit(cnx, o) + self._ueEditor.edit(cnx, neigh) + # redirect to ue_list page + if redirect: + return REQUEST.RESPONSE.redirect( + "ue_list?formation_id=" + o["formation_id"] + ) + + # --- Semestres de formation + + security.declareProtected(ScoImplement, "do_formsemestre_create") + + def do_formsemestre_create(self, args, REQUEST, silent=False): + "create a formsemestre" + cnx = self.GetDBConnexion() + formsemestre_id = sco_formsemestre._formsemestreEditor.create(cnx, args) + if args["etapes"]: + args["formsemestre_id"] = formsemestre_id + sco_formsemestre.write_formsemestre_etapes(self, args) + if args["responsables"]: + args["formsemestre_id"] = formsemestre_id + sco_formsemestre.write_formsemestre_responsables(self, args) + + # create default partition + partition_id = sco_groups.partition_create( + self, formsemestre_id, default=True, redirect=0, REQUEST=REQUEST + ) + _group_id = sco_groups.createGroup( + self, partition_id, default=True, REQUEST=REQUEST + ) + + # news + if not args.has_key("titre"): + args["titre"] = "sans titre" + args["formsemestre_id"] = formsemestre_id + args["url"] = ( + "Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % args + ) + if not silent: + sco_news.add( + self, + REQUEST, + typ=NEWS_SEM, + text='Création du semestre %(titre)s' % args, + url=args["url"], + ) + return formsemestre_id + + def formsemestre_list( + self, + format=None, + REQUEST=None, + formsemestre_id=None, + formation_id=None, + etape_apo=None, + ): + """List formsemestres in given format. + kw can specify some conditions: examples: + formsemestre_list( format='json', formation_id='F777', REQUEST=REQUEST) + """ + # XAPI: new json api + args = {} + L = locals() + for argname in ("formsemestre_id", "formation_id", "etape_apo"): + if L[argname] is not None: + args[argname] = L[argname] + sems = sco_formsemestre.do_formsemestre_list(self, args=args) + # log('formsemestre_list: format="%s", %s semestres found' % (format,len(sems))) + return sendResult(REQUEST, sems, name="formsemestre", format=format) + + security.declareProtected(ScoView, "XMLgetFormsemestres") + + def XMLgetFormsemestres(self, etape_apo=None, formsemestre_id=None, REQUEST=None): + """List all formsemestres matching etape, XML format + DEPRECATED: use formsemestre_list() + """ + log("Warning: calling deprecated XMLgetFormsemestres") + args = {} + if etape_apo: + args["etape_apo"] = etape_apo + if formsemestre_id: + args["formsemestre_id"] = formsemestre_id + if REQUEST: + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + doc = jaxml.XML_document(encoding=SCO_ENCODING) + doc.formsemestrelist() + for sem in sco_formsemestre.do_formsemestre_list(self, args=args): + doc._push() + doc.formsemestre(sem) + doc._pop() + return repr(doc) + + security.declareProtected(ScoImplement, "do_formsemestre_edit") + do_formsemestre_edit = sco_formsemestre.do_formsemestre_edit + + security.declareProtected(ScoView, "formsemestre_edit_options") + formsemestre_edit_options = sco_formsemestre_edit.formsemestre_edit_options + + security.declareProtected(ScoView, "formsemestre_change_lock") + formsemestre_change_lock = sco_formsemestre_edit.formsemestre_change_lock + + security.declareProtected(ScoView, "formsemestre_change_publication_bul") + formsemestre_change_publication_bul = ( + sco_formsemestre_edit.formsemestre_change_publication_bul + ) + + security.declareProtected(ScoView, "view_formsemestre_by_etape") + view_formsemestre_by_etape = sco_formsemestre.view_formsemestre_by_etape + + def _check_access_diretud( + self, formsemestre_id, REQUEST, required_permission=ScoImplement + ): + """Check if access granted: responsable or ScoImplement + Return True|False, HTML_error_page + """ + authuser = REQUEST.AUTHENTICATED_USER + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + header = self.sco_header(page_title="Accès interdit", REQUEST=REQUEST) + footer = self.sco_footer(REQUEST) + if (str(authuser) not in sem["responsables"]) and not authuser.has_permission( + required_permission, self + ): + return ( + False, + "\n".join( + [ + header, + "

    Opération non autorisée pour %s

    " % authuser, + "

    Responsable de ce semestre : %s

    " + % ", ".join(sem["responsables"]), + footer, + ] + ), + ) + else: + return True, "" + + security.declareProtected(ScoView, "formsemestre_custommenu_edit") + + def formsemestre_custommenu_edit(self, REQUEST, formsemestre_id): + "Dialogue modif menu" + # accessible Ă  tous ! + return sco_formsemestre_custommenu.formsemestre_custommenu_edit( + self, formsemestre_id, REQUEST=REQUEST + ) + + security.declareProtected(ScoView, "formsemestre_custommenu_html") + formsemestre_custommenu_html = ( + sco_formsemestre_custommenu.formsemestre_custommenu_html + ) + + security.declareProtected(ScoView, "html_sem_header") + + def html_sem_header( + self, + REQUEST, + title, + sem=None, + with_page_header=True, + with_h2=True, + page_title=None, + **args + ): + "Titre d'une page semestre avec lien vers tableau de bord" + # sem now unused and thus optional... + if with_page_header: + h = self.sco_header( + REQUEST, page_title="%s" % (page_title or title), **args + ) + else: + h = "" + if with_h2: + return h + """

    %s

    """ % (title) + else: + return h + + # --- Gestion des "Implémentations de Modules" + # Un "moduleimpl" correspond a la mise en oeuvre d'un module + # dans une formation spécifique, à une date spécifique. + _moduleimplEditor = EditableTable( + "notes_moduleimpl", + "moduleimpl_id", + ( + "moduleimpl_id", + "module_id", + "formsemestre_id", + "responsable_id", + "computation_expr", + ), + ) + + _modules_enseignantsEditor = EditableTable( + "notes_modules_enseignants", + "modules_enseignants_id", + ("modules_enseignants_id", "moduleimpl_id", "ens_id"), + ) + + security.declareProtected(ScoImplement, "do_moduleimpl_create") + + def do_moduleimpl_create(self, args): + "create a moduleimpl" + cnx = self.GetDBConnexion() + r = self._moduleimplEditor.create(cnx, args) + self._inval_cache( + formsemestre_id=args["formsemestre_id"] + ) # > creation moduleimpl + return r + + security.declareProtected(ScoImplement, "do_moduleimpl_delete") + + def do_moduleimpl_delete(self, oid, formsemestre_id=None): + "delete moduleimpl (desinscrit tous les etudiants)" + cnx = self.GetDBConnexion() + # --- desinscription des etudiants + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + req = "DELETE FROM notes_moduleimpl_inscription WHERE moduleimpl_id=%(moduleimpl_id)s" + cursor.execute(req, {"moduleimpl_id": oid}) + # --- suppression des enseignants + cursor.execute( + "DELETE FROM notes_modules_enseignants WHERE moduleimpl_id=%(moduleimpl_id)s", + {"moduleimpl_id": oid}, + ) + # --- suppression des references dans les absences + cursor.execute( + "UPDATE absences SET moduleimpl_id=NULL WHERE moduleimpl_id=%(moduleimpl_id)s", + {"moduleimpl_id": oid}, + ) + # --- destruction du moduleimpl + self._moduleimplEditor.delete(cnx, oid) + self._inval_cache(formsemestre_id=formsemestre_id) # > moduleimpl_delete + + security.declareProtected(ScoView, "do_moduleimpl_list") + + def do_moduleimpl_list( + self, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None + ): + "list moduleimpls" + args = locals() + cnx = self.GetDBConnexion() + modimpls = self._moduleimplEditor.list(cnx, args) # *args, **kw) + # Ajoute la liste des enseignants + for mo in modimpls: + mo["ens"] = self.do_ens_list(args={"moduleimpl_id": mo["moduleimpl_id"]}) + return return_text_if_published(modimpls, REQUEST) + + security.declareProtected(ScoImplement, "do_moduleimpl_edit") + + def do_moduleimpl_edit(self, args, formsemestre_id=None, cnx=None): + "edit a moduleimpl" + if not cnx: + cnx = self.GetDBConnexion() + self._moduleimplEditor.edit(cnx, args) + + self._inval_cache(formsemestre_id=formsemestre_id) # > modif moduleimpl + + security.declareProtected(ScoView, "do_moduleimpl_withmodule_list") + + def do_moduleimpl_withmodule_list( + self, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None + ): + """Liste les moduleimpls et ajoute dans chacun le module correspondant + Tri la liste par semestre/UE/numero_matiere/numero_module + """ + args = locals() + del args["self"] + del args["REQUEST"] + modimpls = self.do_moduleimpl_list(**args) + for mo in modimpls: + mo["module"] = self.do_module_list(args={"module_id": mo["module_id"]})[0] + mo["ue"] = self.do_ue_list(args={"ue_id": mo["module"]["ue_id"]})[0] + mo["matiere"] = self.do_matiere_list( + args={"matiere_id": mo["module"]["matiere_id"]} + )[0] + + # tri par semestre/UE/numero_matiere/numero_module + + extr = lambda x: ( + x["ue"]["numero"], + x["ue"]["ue_id"], + x["matiere"]["numero"], + x["matiere"]["matiere_id"], + x["module"]["numero"], + x["module"]["code"], + ) + + modimpls.sort(lambda x, y: cmp(extr(x), extr(y))) + # log('after sort args=%s' % args) + # log( ',\n'.join( [ str(extr(m)) for m in modimpls ] )) + # log('after sort: Mlist=\n' + ',\n'.join( [ str(m) for m in modimpls ] ) + '\n') + return return_text_if_published(modimpls, REQUEST) + + security.declareProtected(ScoView, "do_ens_list") + + def do_ens_list(self, *args, **kw): + "liste les enseignants d'un moduleimpl (pas le responsable)" + cnx = self.GetDBConnexion() + ens = self._modules_enseignantsEditor.list(cnx, *args, **kw) + return ens + + security.declareProtected(ScoImplement, "do_ens_edit") + + def do_ens_edit(self, *args, **kw): + "edit ens" + cnx = self.GetDBConnexion() + self._modules_enseignantsEditor.edit(cnx, *args, **kw) + + security.declareProtected(ScoImplement, "do_ens_create") + + def do_ens_create(self, args): + "create ens" + cnx = self.GetDBConnexion() + r = self._modules_enseignantsEditor.create(cnx, args) + return r + + security.declareProtected(ScoImplement, "do_ens_delete") + + def do_ens_delete(self, oid): + "delete ens" + cnx = self.GetDBConnexion() + r = self._modules_enseignantsEditor.delete(cnx, oid) + return r + + # --- dialogue modif enseignants/moduleimpl + security.declareProtected(ScoView, "edit_enseignants_form") + + def edit_enseignants_form(self, REQUEST, moduleimpl_id): + "modif liste enseignants/moduleimpl" + M, sem = self.can_change_ens(REQUEST, moduleimpl_id) + # -- + header = self.html_sem_header( + REQUEST, + 'Enseignants du module %s' + % (moduleimpl_id, M["module"]["titre"]), + page_title="Enseignants du module %s" % M["module"]["titre"], + javascripts=["libjs/AutoSuggest.js"], + cssstyles=["css/autosuggest_inquisitor.css"], + bodyOnLoad="init_tf_form('')", + ) + footer = self.sco_footer(REQUEST) + + # Liste des enseignants avec forme pour affichage / saisie avec suggestion + userlist = self.Users.get_userlist() + login2display = {} # user_name : forme pour affichage = "NOM Prenom (login)" + for u in userlist: + login2display[u["user_name"]] = u["nomplogin"] + allowed_user_names = login2display.values() + + H = [ + "
    • %s (responsable)
    • " + % login2display.get(M["responsable_id"], M["responsable_id"]) + ] + for ens in M["ens"]: + H.append( + '
    • %s (supprimer)
    • ' + % ( + login2display.get(ens["ens_id"], ens["ens_id"]), + moduleimpl_id, + ens["ens_id"], + ) + ) + H.append("
    ") + F = """

    Les enseignants d'un module ont le droit de + saisir et modifier toutes les notes des Ă©valuations de ce module. +

    +

    Pour changer le responsable du module, passez par la + page "Modification du semestre", accessible uniquement au responsable de la formation (chef de département) +

    + """ % ( + sem["formation_id"], + M["formsemestre_id"], + ) + + modform = [ + ("moduleimpl_id", {"input_type": "hidden"}), + ( + "ens_id", + { + "input_type": "text_suggest", + "size": 50, + "title": "Ajouter un enseignant", + "allowed_values": allowed_user_names, + "allow_null": False, + "text_suggest_options": { + "script": "Users/get_userlist_xml?", + "varname": "start", + "json": False, + "noresults": "Valeur invalide !", + "timeout": 60000, + }, + }, + ), + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + modform, + submitlabel="Ajouter enseignant", + cancelbutton="Annuler", + ) + if tf[0] == 0: + return header + "\n".join(H) + tf[1] + F + footer + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect( + "moduleimpl_status?moduleimpl_id=" + moduleimpl_id + ) + else: + ens_id = self.Users.get_user_name_from_nomplogin(tf[2]["ens_id"]) + if not ens_id: + H.append( + '

    Pour ajouter un enseignant, choisissez un nom dans le menu

    ' + ) + else: + # et qu'il n'est pas deja: + if ( + ens_id in [x["ens_id"] for x in M["ens"]] + or ens_id == M["responsable_id"] + ): + H.append( + '

    Enseignant %s déjà dans la liste !

    ' + % ens_id + ) + else: + self.do_ens_create( + {"moduleimpl_id": moduleimpl_id, "ens_id": ens_id} + ) + return REQUEST.RESPONSE.redirect( + "edit_enseignants_form?moduleimpl_id=%s" % moduleimpl_id + ) + return header + "\n".join(H) + tf[1] + F + footer + + security.declareProtected(ScoView, "edit_moduleimpl_resp") + + def edit_moduleimpl_resp(self, REQUEST, moduleimpl_id): + """Changement d'un enseignant responsable de module + Accessible par Admin et dir des etud si flag resp_can_change_ens + """ + M, sem = self.can_change_module_resp(REQUEST, moduleimpl_id) + H = [ + self.html_sem_header( + REQUEST, + 'Modification du responsable du module %s' + % (moduleimpl_id, M["module"]["titre"]), + sem, + javascripts=["libjs/AutoSuggest.js"], + cssstyles=["css/autosuggest_inquisitor.css"], + bodyOnLoad="init_tf_form('')", + ) + ] + help = """

    Taper le début du nom de l'enseignant.

    """ + # Liste des enseignants avec forme pour affichage / saisie avec suggestion + userlist = self.Users.get_userlist() + login2display = {} # user_name : forme pour affichage = "NOM Prenom (login)" + for u in userlist: + login2display[u["user_name"]] = u["nomplogin"] + allowed_user_names = login2display.values() + + initvalues = M + initvalues["responsable_id"] = login2display.get( + M["responsable_id"], M["responsable_id"] + ) + form = [ + ("moduleimpl_id", {"input_type": "hidden"}), + ( + "responsable_id", + { + "input_type": "text_suggest", + "size": 50, + "title": "Responsable du module", + "allowed_values": allowed_user_names, + "allow_null": False, + "text_suggest_options": { + "script": "Users/get_userlist_xml?", + "varname": "start", + "json": False, + "noresults": "Valeur invalide !", + "timeout": 60000, + }, + }, + ), + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + form, + submitlabel="Changer responsable", + cancelbutton="Annuler", + initvalues=initvalues, + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + help + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect( + "moduleimpl_status?moduleimpl_id=" + moduleimpl_id + ) + else: + responsable_id = self.Users.get_user_name_from_nomplogin( + tf[2]["responsable_id"] + ) + if ( + not responsable_id + ): # presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps) + return REQUEST.RESPONSE.redirect( + "moduleimpl_status?moduleimpl_id=" + moduleimpl_id + ) + self.do_moduleimpl_edit( + {"moduleimpl_id": moduleimpl_id, "responsable_id": responsable_id}, + formsemestre_id=sem["formsemestre_id"], + ) + return REQUEST.RESPONSE.redirect( + "moduleimpl_status?moduleimpl_id=" + + moduleimpl_id + + "&head_message=responsable%20modifié" + ) + + _expr_help = """

    Expérimental: formule de calcul de la moyenne %(target)s

    +

    Dans la formule, les variables suivantes sont définies:

    +
      +
    • moy la moyenne, calculĂ©e selon la règle standard (moyenne pondĂ©rĂ©e)
    • +
    • moy_is_valid vrai si la moyenne est valide (numĂ©rique)
    • +
    • moy_val la valeur de la moyenne (nombre, valant 0 si invalide)
    • +
    • notes vecteur des notes (/20) aux %(objs)s
    • +
    • coefs vecteur des coefficients des %(objs)s, les coefs des %(objs)s sans notes (ATT, EXC) Ă©tant mis Ă  zĂ©ro
    • +
    • cmask vecteur de 0/1, 0 si le coef correspondant a Ă©tĂ© annulĂ©
    • +
    • Nombre d'absences: nb_abs, nb_abs_just, nb_abs_nojust (en demi-journĂ©es)
    • +
    +

    Les éléments des vecteurs sont ordonnés dans l'ordre des %(objs)s%(ordre)s.

    +

    Les fonctions suivantes sont utilisables: abs, cmp, dot, len, map, max, min, pow, reduce, round, sum, ifelse

    +

    La notation V(1,2,3) représente un vecteur (1,2,3)

    +

    Vous pouvez désactiver la formule (et revenir au mode de calcul "classique") + en supprimant le texte ou en faisant précéder la première ligne par #

    + """ + + security.declareProtected(ScoView, "edit_moduleimpl_expr") + + def edit_moduleimpl_expr(self, REQUEST, moduleimpl_id): + """Edition formule calcul moyenne module + Accessible par Admin, dir des etud et responsable module + """ + M, sem = self.can_change_ens(REQUEST, moduleimpl_id) + H = [ + self.html_sem_header( + REQUEST, + 'Modification règle de calcul du module %s' + % (moduleimpl_id, M["module"]["titre"]), + sem, + ), + self._expr_help + % { + "target": "du module", + "objs": "évaluations", + "ordre": " (le premier élément est la plus ancienne évaluation)", + }, + ] + initvalues = M + form = [ + ("moduleimpl_id", {"input_type": "hidden"}), + ( + "computation_expr", + { + "title": "Formule de calcul", + "input_type": "textarea", + "rows": 4, + "cols": 60, + "explanation": "formule de calcul (expérimental)", + }, + ), + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + form, + submitlabel="Modifier formule de calcul", + cancelbutton="Annuler", + initvalues=initvalues, + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect( + "moduleimpl_status?moduleimpl_id=" + moduleimpl_id + ) + else: + self.do_moduleimpl_edit( + { + "moduleimpl_id": moduleimpl_id, + "computation_expr": tf[2]["computation_expr"], + }, + formsemestre_id=sem["formsemestre_id"], + ) + self._inval_cache( + formsemestre_id=sem["formsemestre_id"] + ) # > modif regle calcul + return REQUEST.RESPONSE.redirect( + "moduleimpl_status?moduleimpl_id=" + + moduleimpl_id + + "&head_message=règle%20de%20calcul%20modifiée" + ) + + security.declareProtected(ScoView, "view_module_abs") + + def view_module_abs(self, REQUEST, moduleimpl_id, format="html"): + """Visulalisation des absences a un module + """ + M = self.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) + debut_sem = DateDMYtoISO(sem["date_debut"]) + fin_sem = DateDMYtoISO(sem["date_fin"]) + list_insc = self.do_moduleimpl_listeetuds(moduleimpl_id) + + T = [] + for etudid in list_insc: + nb_abs = self.Absences.CountAbs( + etudid=etudid, debut=debut_sem, fin=fin_sem, moduleimpl_id=moduleimpl_id + ) + if nb_abs: + nb_abs_just = self.Absences.CountAbsJust( + etudid=etudid, + debut=debut_sem, + fin=fin_sem, + moduleimpl_id=moduleimpl_id, + ) + etud = self.getEtudInfo(etudid=etudid, filled=True)[0] + T.append( + { + "nomprenom": etud["nomprenom"], + "just": nb_abs_just, + "nojust": nb_abs - nb_abs_just, + "total": nb_abs, + "_nomprenom_target": "ficheEtud?etudid=%s" % etudid, + } + ) + + H = [ + self.html_sem_header( + REQUEST, + 'Absences du module %s' + % (moduleimpl_id, M["module"]["titre"]), + page_title="Absences du module %s" % (M["module"]["titre"]), + sem=sem, + ) + ] + if not T and format == "html": + return ( + "\n".join(H) + + "

    Aucune absence signalée

    " + + self.sco_footer(REQUEST) + ) + + tab = GenTable( + titles={ + "nomprenom": "Nom", + "just": "Just.", + "nojust": "Non Just.", + "total": "Total", + }, + columns_ids=("nomprenom", "just", "nojust", "total"), + rows=T, + html_class="table_leftalign", + base_url="%s?moduleimpl_id=%s" % (REQUEST.URL0, moduleimpl_id), + filename="absmodule_" + make_filename(M["module"]["titre"]), + caption="Absences dans le module %s" % M["module"]["titre"], + preferences=self.get_preferences(), + ) + + if format != "html": + return tab.make_page(self, format=format, REQUEST=REQUEST) + + return "\n".join(H) + tab.html() + self.sco_footer(REQUEST) + + security.declareProtected(ScoView, "edit_ue_expr") + + def edit_ue_expr(self, REQUEST, formsemestre_id, ue_id): + """Edition formule calcul moyenne UE""" + # Check access + sem = sco_formsemestre_edit.can_edit_sem(self, REQUEST, formsemestre_id) + if not sem: + raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération") + cnx = self.GetDBConnexion() + # + ue = self.do_ue_list({"ue_id": ue_id})[0] + H = [ + self.html_sem_header( + REQUEST, + "Modification règle de calcul de l'UE %s (%s)" + % (ue["acronyme"], ue["titre"]), + sem, + ), + self._expr_help % {"target": "de l'UE", "objs": "modules", "ordre": ""}, + ] + el = sco_compute_moy.formsemestre_ue_computation_expr_list( + cnx, {"formsemestre_id": formsemestre_id, "ue_id": ue_id} + ) + if el: + initvalues = el[0] + else: + initvalues = {} + form = [ + ("ue_id", {"input_type": "hidden"}), + ("formsemestre_id", {"input_type": "hidden"}), + ( + "computation_expr", + { + "title": "Formule de calcul", + "input_type": "textarea", + "rows": 4, + "cols": 60, + "explanation": "formule de calcul (expérimental)", + }, + ), + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + form, + submitlabel="Modifier formule de calcul", + cancelbutton="Annuler", + initvalues=initvalues, + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect( + "formsemestre_status?formsemestre_id=" + formsemestre_id + ) + else: + if el: + el[0]["computation_expr"] = tf[2]["computation_expr"] + sco_compute_moy.formsemestre_ue_computation_expr_edit(cnx, el[0]) + else: + sco_compute_moy.formsemestre_ue_computation_expr_create(cnx, tf[2]) + + self._inval_cache(formsemestre_id=formsemestre_id) # > modif regle calcul + return REQUEST.RESPONSE.redirect( + "formsemestre_status?formsemestre_id=" + + formsemestre_id + + "&head_message=règle%20de%20calcul%20modifiée" + ) + + security.declareProtected(ScoView, "formsemestre_enseignants_list") + + def formsemestre_enseignants_list(self, REQUEST, formsemestre_id, format="html"): + """Liste les enseignants intervenants dans le semestre (resp. modules et chargés de TD) + et indique les absences saisies par chacun. + """ + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + # resp. de modules: + mods = self.do_moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) + sem_ens = {} + for mod in mods: + if not mod["responsable_id"] in sem_ens: + sem_ens[mod["responsable_id"]] = {"mods": [mod]} + else: + sem_ens[mod["responsable_id"]]["mods"].append(mod) + # charges de TD: + for mod in mods: + for ensd in mod["ens"]: + if not ensd["ens_id"] in sem_ens: + sem_ens[ensd["ens_id"]] = {"mods": [mod]} + else: + sem_ens[ensd["ens_id"]]["mods"].append(mod) + # compte les absences ajoutées par chacun dans tout le semestre + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + for ens in sem_ens: + cursor.execute( + "select * from scolog L, notes_formsemestre_inscription I where method='AddAbsence' and authenticated_user=%(authenticated_user)s and L.etudid = I.etudid and I.formsemestre_id=%(formsemestre_id)s and date > %(date_debut)s and date < %(date_fin)s", + { + "authenticated_user": ens, + "formsemestre_id": formsemestre_id, + "date_debut": DateDMYtoISO(sem["date_debut"]), + "date_fin": DateDMYtoISO(sem["date_fin"]), + }, + ) + + events = cursor.dictfetchall() + sem_ens[ens]["nbabsadded"] = len(events) + + # description textuelle des modules + for ens in sem_ens: + sem_ens[ens]["descr_mods"] = ", ".join( + [x["module"]["code"] for x in sem_ens[ens]["mods"]] + ) + + # ajoute infos sur enseignant: + for ens in sem_ens: + sem_ens[ens].update(self.Users.user_info(ens)) + if sem_ens[ens]["email"]: + sem_ens[ens]["_email_target"] = "mailto:%s" % sem_ens[ens]["email"] + + sem_ens_list = sem_ens.values() + sem_ens_list.sort(lambda x, y: cmp(x["nomprenom"], y["nomprenom"])) + + # --- Generate page with table + title = "Enseignants de " + sem["titremois"] + T = GenTable( + columns_ids=["nom_fmt", "prenom_fmt", "descr_mods", "nbabsadded", "email"], + titles={ + "nom_fmt": "Nom", + "prenom_fmt": "Prénom", + "email": "Mail", + "descr_mods": "Modules", + "nbabsadded": "Saisies Abs.", + }, + rows=sem_ens_list, + html_sortable=True, + html_class="table_leftalign", + filename=make_filename("Enseignants-" + sem["titreannee"]), + html_title=self.html_sem_header( + REQUEST, "Enseignants du semestre", sem, with_page_header=False + ), + base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id), + caption="Tous les enseignants (responsables ou associés aux modules de ce semestre) apparaissent. Le nombre de saisies d'absences est le nombre d'opérations d'ajout effectuées sur ce semestre, sans tenir compte des annulations ou double saisies.", + preferences=self.get_preferences(formsemestre_id), + ) + return T.make_page( + self, page_title=title, title=title, REQUEST=REQUEST, format=format + ) + + security.declareProtected(ScoView, "edit_enseignants_form_delete") + + def edit_enseignants_form_delete(self, REQUEST, moduleimpl_id, ens_id): + "remove ens" + M, sem = self.can_change_ens(REQUEST, moduleimpl_id) + # search ens_id + ok = False + for ens in M["ens"]: + if ens["ens_id"] == ens_id: + ok = True + break + if not ok: + raise ScoValueError("invalid ens_id (%s)" % ens_id) + self.do_ens_delete(ens["modules_enseignants_id"]) + return REQUEST.RESPONSE.redirect( + "edit_enseignants_form?moduleimpl_id=%s" % moduleimpl_id + ) + + security.declareProtected(ScoView, "can_change_ens") + + def can_change_ens(self, REQUEST, moduleimpl_id, raise_exc=True): + "check if current user can modify ens list (raise exception if not)" + M = self.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + # -- check lock + sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) + if sem["etat"] != "1": + if raise_exc: + raise ScoValueError("Modification impossible: semestre verrouille") + else: + return False + # -- check access + authuser = REQUEST.AUTHENTICATED_USER + uid = str(authuser) + # admin, resp. module ou resp. semestre + if ( + uid != M["responsable_id"] + and not authuser.has_permission(ScoImplement, self) + and (uid not in sem["responsables"]) + ): + if raise_exc: + raise AccessDenied("Modification impossible pour %s" % uid) + else: + return False + return M, sem + + security.declareProtected(ScoView, "can_change_module_resp") + + def can_change_module_resp(self, REQUEST, moduleimpl_id): + """Check if current user can modify module resp. (raise exception if not). + = Admin, et dir des etud. (si option l'y autorise) + """ + M = self.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + # -- check lock + sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) + if sem["etat"] != "1": + raise ScoValueError("Modification impossible: semestre verrouille") + # -- check access + authuser = REQUEST.AUTHENTICATED_USER + uid = str(authuser) + # admin ou resp. semestre avec flag resp_can_change_resp + if not authuser.has_permission(ScoImplement, self) and ( + (uid not in sem["responsables"]) or (not sem["resp_can_change_ens"]) + ): + raise AccessDenied("Modification impossible pour %s" % uid) + return M, sem + + # --- Gestion des inscriptions aux modules + _formsemestre_inscriptionEditor = EditableTable( + "notes_formsemestre_inscription", + "formsemestre_inscription_id", + ("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat"), + sortkey="formsemestre_id", + ) + + security.declareProtected(ScoEtudInscrit, "do_formsemestre_inscription_create") + + def do_formsemestre_inscription_create(self, args, REQUEST, method=None): + "create a formsemestre_inscription (and sco event)" + cnx = self.GetDBConnexion() + log("do_formsemestre_inscription_create: args=%s" % str(args)) + sems = sco_formsemestre.do_formsemestre_list( + self, {"formsemestre_id": args["formsemestre_id"]} + ) + if len(sems) != 1: + raise ScoValueError( + "code de semestre invalide: %s" % args["formsemestre_id"] + ) + sem = sems[0] + # check lock + if sem["etat"] != "1": + raise ScoValueError("inscription: semestre verrouille") + # + r = self._formsemestre_inscriptionEditor.create(cnx, args) + # Evenement + scolars.scolar_events_create( + cnx, + args={ + "etudid": args["etudid"], + "event_date": time.strftime("%d/%m/%Y"), + "formsemestre_id": args["formsemestre_id"], + "event_type": "INSCRIPTION", + }, + ) + # Log etudiant + logdb( + REQUEST, + cnx, + method=method, + etudid=args["etudid"], + msg="inscription en semestre %s" % args["formsemestre_id"], + commit=False, + ) + # + self._inval_cache( + formsemestre_id=args["formsemestre_id"] + ) # > inscription au semestre + return r + + security.declareProtected(ScoImplement, "do_formsemestre_inscription_delete") + + def do_formsemestre_inscription_delete(self, oid, formsemestre_id=None): + "delete formsemestre_inscription" + cnx = self.GetDBConnexion() + self._formsemestre_inscriptionEditor.delete(cnx, oid) + + self._inval_cache( + formsemestre_id=formsemestre_id + ) # > desinscription du semestre + + security.declareProtected(ScoView, "do_formsemestre_inscription_list") + + def do_formsemestre_inscription_list(self, *args, **kw): + "list formsemestre_inscriptions" + cnx = self.GetDBConnexion() + return self._formsemestre_inscriptionEditor.list(cnx, *args, **kw) + + security.declareProtected(ScoView, "do_formsemestre_inscription_listinscrits") + + def do_formsemestre_inscription_listinscrits(self, formsemestre_id): + """Liste les inscrits (état I) à ce semestre et cache le résultat""" + cache = self.get_formsemestre_inscription_cache() + r = cache.get(formsemestre_id) + if r != None: + return r + # retreive list + r = self.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id, "etat": "I"} + ) + cache.set(formsemestre_id, r) + return r + + security.declareProtected(ScoImplement, "do_formsemestre_inscription_edit") + + def do_formsemestre_inscription_edit(self, args=None, formsemestre_id=None): + "edit a formsemestre_inscription" + cnx = self.GetDBConnexion() + self._formsemestre_inscriptionEditor.edit(cnx, args) + self._inval_cache( + formsemestre_id=formsemestre_id + ) # > modif inscription semestre (demission ?) + + # Cache inscriptions semestres + def get_formsemestre_inscription_cache(self): + u = self.GetDBConnexionString() + if CACHE_formsemestre_inscription.has_key(u): + return CACHE_formsemestre_inscription[u] + else: + log("get_formsemestre_inscription_cache: new simpleCache") + CACHE_formsemestre_inscription[u] = sco_cache.simpleCache() + return CACHE_formsemestre_inscription[u] + + security.declareProtected(ScoImplement, "formsemestre_desinscription") + + def formsemestre_desinscription( + self, etudid, formsemestre_id, REQUEST=None, dialog_confirmed=False + ): + """desinscrit l'etudiant de ce semestre (et donc de tous les modules). + A n'utiliser qu'en cas d'erreur de saisie. + S'il s'agit d'un semestre extérieur et qu'il n'y a plus d'inscrit, + le semestre sera supprimé. + """ + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + # -- check lock + if sem["etat"] != "1": + raise ScoValueError("desinscription impossible: semestre verrouille") + + # -- Si décisions de jury, désinscription interdite + nt = self._getNotesCache().get_NotesTable(self, formsemestre_id) + if nt.etud_has_decision(etudid): + raise ScoValueError( + """Désinscription impossible: l'étudiant a une décision de jury + (la supprimer avant si nécessaire: + + supprimer décision jury + ) + """ + % (etudid, formsemestre_id) + ) + if not dialog_confirmed: + etud = self.getEtudInfo(etudid=etudid, filled=1)[0] + if sem["modalite"] != "EXT": + msg_ext = """ +

    %s sera désinscrit de tous les modules du semestre %s (%s - %s).

    +

    Cette opération ne doit être utilisée que pour corriger une erreur ! + Un étudiant réellement inscrit doit le rester, le faire éventuellement démissionner. +

    + """ % ( + etud["nomprenom"], + sem["titre_num"], + sem["date_debut"], + sem["date_fin"], + ) + else: # semestre extérieur + msg_ext = """ +

    %s sera désinscrit du semestre extérieur %s (%s - %s).

    + """ % ( + etud["nomprenom"], + sem["titre_num"], + sem["date_debut"], + sem["date_fin"], + ) + inscrits = self.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id} + ) + nbinscrits = len(inscrits) + if nbinscrits <= 1: + msg_ext = """

    Attention: le semestre extérieur sera supprimé + car il n'a pas d'autre étudiant inscrit. +

    + """ + return self.confirmDialog( + """

    Confirmer la demande de desinscription ?

    """ + msg_ext, + dest_url="", + REQUEST=REQUEST, + cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id, + parameters={"etudid": etudid, "formsemestre_id": formsemestre_id}, + ) + + self.do_formsemestre_desinscription(etudid, formsemestre_id, REQUEST=REQUEST) + + return ( + self.sco_header(REQUEST) + + '

    Etudiant désinscrit !

    retour à la fiche' + % (self.ScoURL(), etudid) + + self.sco_footer(REQUEST) + ) + + def do_formsemestre_desinscription(self, etudid, formsemestre_id, REQUEST=None): + """Désinscription d'un étudiant. + Si semestre extérieur et dernier inscrit, suppression de ce semestre. + """ + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + # -- check lock + if sem["etat"] != "1": + raise ScoValueError("desinscription impossible: semestre verrouille") + + # -- Si decisions de jury, desinscription interdite + nt = self._getNotesCache().get_NotesTable(self, formsemestre_id) + if nt.etud_has_decision(etudid): + raise ScoValueError( + "desinscription impossible: l'étudiant a une décision de jury (la supprimer avant si nécessaire)" + ) + + insem = self.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id, "etudid": etudid} + ) + if not insem: + raise ScoValueError("%s n'est pas inscrit au semestre !" % etudid) + insem = insem[0] + # -- desinscription de tous les modules + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select moduleimpl_inscription_id from notes_moduleimpl_inscription Im, notes_moduleimpl M where Im.etudid=%(etudid)s and Im.moduleimpl_id = M.moduleimpl_id and M.formsemestre_id = %(formsemestre_id)s", + {"etudid": etudid, "formsemestre_id": formsemestre_id}, + ) + res = cursor.fetchall() + moduleimpl_inscription_ids = [x[0] for x in res] + for moduleimpl_inscription_id in moduleimpl_inscription_ids: + self.do_moduleimpl_inscription_delete( + moduleimpl_inscription_id, formsemestre_id=formsemestre_id + ) + # -- desincription du semestre + self.do_formsemestre_inscription_delete( + insem["formsemestre_inscription_id"], formsemestre_id=formsemestre_id + ) + # --- Semestre extérieur + if sem["modalite"] == "EXT": + inscrits = self.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id} + ) + nbinscrits = len(inscrits) + if nbinscrits == 0: + log( + "do_formsemestre_desinscription: suppression du semestre extérieur %s" + % formsemestre_id + ) + sco_formsemestre_edit.do_formsemestre_delete( + self, formsemestre_id, REQUEST=REQUEST + ) + + if REQUEST: + logdb( + REQUEST, + cnx, + method="formsemestre_desinscription", + etudid=etudid, + msg="desinscription semestre %s" % formsemestre_id, + commit=False, + ) + + # --- Inscriptions aux modules + _moduleimpl_inscriptionEditor = EditableTable( + "notes_moduleimpl_inscription", + "moduleimpl_inscription_id", + ("moduleimpl_inscription_id", "etudid", "moduleimpl_id"), + ) + + security.declareProtected(ScoEtudInscrit, "do_moduleimpl_inscription_create") + + def do_moduleimpl_inscription_create( + self, args, REQUEST=None, formsemestre_id=None + ): + "create a moduleimpl_inscription" + cnx = self.GetDBConnexion() + log("do_moduleimpl_inscription_create: " + str(args)) + r = self._moduleimpl_inscriptionEditor.create(cnx, args) + self._inval_cache(formsemestre_id=formsemestre_id) # > moduleimpl_inscription + if REQUEST: + logdb( + REQUEST, + cnx, + method="moduleimpl_inscription", + etudid=args["etudid"], + msg="inscription module %s" % args["moduleimpl_id"], + commit=False, + ) + return r + + security.declareProtected(ScoImplement, "do_moduleimpl_inscription_delete") + + def do_moduleimpl_inscription_delete(self, oid, formsemestre_id=None): + "delete moduleimpl_inscription" + cnx = self.GetDBConnexion() + self._moduleimpl_inscriptionEditor.delete(cnx, oid) + self._inval_cache(formsemestre_id=formsemestre_id) # > moduleimpl_inscription + + security.declareProtected(ScoView, "do_moduleimpl_inscription_list") + + def do_moduleimpl_inscription_list( + self, moduleimpl_id=None, etudid=None, REQUEST=None + ): + "list moduleimpl_inscriptions" + args = locals() + cnx = self.GetDBConnexion() + return return_text_if_published( + self._moduleimpl_inscriptionEditor.list(cnx, args), REQUEST + ) + + security.declareProtected(ScoView, "do_moduleimpl_listeetuds") + + def do_moduleimpl_listeetuds(self, moduleimpl_id): + "retourne liste des etudids inscrits a ce module" + req = "select distinct Im.etudid from notes_moduleimpl_inscription Im, notes_formsemestre_inscription Isem, notes_moduleimpl M where Isem.etudid=Im.etudid and Im.moduleimpl_id=M.moduleimpl_id and M.moduleimpl_id = %(moduleimpl_id)s" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute(req, {"moduleimpl_id": moduleimpl_id}) + res = cursor.fetchall() + return [x[0] for x in res] + + security.declareProtected(ScoEtudInscrit, "do_moduleimpl_inscrit_tout_semestre") + + def do_moduleimpl_inscrit_tout_semestre(self, moduleimpl_id, formsemestre_id): + "inscrit tous les etudiants inscrit au semestre a ce module" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + req = """INSERT INTO notes_moduleimpl_inscription + (moduleimpl_id, etudid) + SELECT %(moduleimpl_id)s, I.etudid + FROM notes_formsemestre_inscription I + WHERE I.formsemestre_id=%(formsemestre_id)s""" + args = {"moduleimpl_id": moduleimpl_id, "formsemestre_id": formsemestre_id} + cursor.execute(req, args) + + security.declareProtected(ScoEtudInscrit, "do_moduleimpl_inscrit_etuds") + + def do_moduleimpl_inscrit_etuds( + self, moduleimpl_id, formsemestre_id, etudids, reset=False, REQUEST=None + ): + """Inscrit les etudiants (liste d'etudids) a ce module. + Si reset, desinscrit tous les autres. + """ + # Verifie qu'ils sont tous bien inscrits au semestre + for etudid in etudids: + insem = self.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id, "etudid": etudid} + ) + if not insem: + raise ScoValueError("%s n'est pas inscrit au semestre !" % etudid) + + # Desinscriptions + if reset: + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "delete from notes_moduleimpl_inscription where moduleimpl_id = %(moduleimpl_id)s", + {"moduleimpl_id": moduleimpl_id}, + ) + # Inscriptions au module: + inmod_set = Set( + [ + x["etudid"] + for x in self.do_moduleimpl_inscription_list( + moduleimpl_id=moduleimpl_id + ) + ] + ) + for etudid in etudids: + # deja inscrit ? + if not etudid in inmod_set: + self.do_moduleimpl_inscription_create( + {"moduleimpl_id": moduleimpl_id, "etudid": etudid}, + REQUEST=REQUEST, + formsemestre_id=formsemestre_id, + ) + + self._inval_cache(formsemestre_id=formsemestre_id) # > moduleimpl_inscrit_etuds + + security.declareProtected(ScoEtudInscrit, "etud_desinscrit_ue") + + def etud_desinscrit_ue(self, etudid, formsemestre_id, ue_id, REQUEST=None): + """Desinscrit l'etudiant de tous les modules de cette UE dans ce semestre. + """ + sco_moduleimpl_inscriptions.do_etud_desinscrit_ue( + self, etudid, formsemestre_id, ue_id, REQUEST=REQUEST + ) + return REQUEST.RESPONSE.redirect( + self.ScoURL() + + "/Notes/moduleimpl_inscriptions_stats?formsemestre_id=" + + formsemestre_id + ) + + security.declareProtected(ScoEtudInscrit, "etud_inscrit_ue") + + def etud_inscrit_ue(self, etudid, formsemestre_id, ue_id, REQUEST=None): + """Inscrit l'etudiant de tous les modules de cette UE dans ce semestre. + """ + sco_moduleimpl_inscriptions.do_etud_inscrit_ue( + self, etudid, formsemestre_id, ue_id, REQUEST=REQUEST + ) + return REQUEST.RESPONSE.redirect( + self.ScoURL() + + "/Notes/moduleimpl_inscriptions_stats?formsemestre_id=" + + formsemestre_id + ) + + # --- Inscriptions + security.declareProtected( + ScoEtudInscrit, "formsemestre_inscription_with_modules_form" + ) + formsemestre_inscription_with_modules_form = ( + sco_formsemestre_inscriptions.formsemestre_inscription_with_modules_form + ) + + security.declareProtected( + ScoEtudInscrit, "formsemestre_inscription_with_modules_etud" + ) + formsemestre_inscription_with_modules_etud = ( + sco_formsemestre_inscriptions.formsemestre_inscription_with_modules_etud + ) + + security.declareProtected(ScoEtudInscrit, "formsemestre_inscription_with_modules") + formsemestre_inscription_with_modules = ( + sco_formsemestre_inscriptions.formsemestre_inscription_with_modules + ) + + security.declareProtected(ScoEtudInscrit, "formsemestre_inscription_option") + formsemestre_inscription_option = ( + sco_formsemestre_inscriptions.formsemestre_inscription_option + ) + + security.declareProtected(ScoEtudInscrit, "do_moduleimpl_incription_options") + do_moduleimpl_incription_options = ( + sco_formsemestre_inscriptions.do_moduleimpl_incription_options + ) + + security.declareProtected(ScoView, "formsemestre_inscrits_ailleurs") + formsemestre_inscrits_ailleurs = ( + sco_formsemestre_inscriptions.formsemestre_inscrits_ailleurs + ) + + security.declareProtected(ScoEtudInscrit, "moduleimpl_inscriptions_edit") + moduleimpl_inscriptions_edit = ( + sco_moduleimpl_inscriptions.moduleimpl_inscriptions_edit + ) + + security.declareProtected(ScoView, "moduleimpl_inscriptions_stats") + moduleimpl_inscriptions_stats = ( + sco_moduleimpl_inscriptions.moduleimpl_inscriptions_stats + ) + + # --- Evaluations + _evaluationEditor = EditableTable( + "notes_evaluation", + "evaluation_id", + ( + "evaluation_id", + "moduleimpl_id", + "jour", + "heure_debut", + "heure_fin", + "description", + "note_max", + "coefficient", + "visibulletin", + "publish_incomplete", + "evaluation_type", + "numero", + ), + sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord + output_formators={ + "jour": DateISOtoDMY, + "visibulletin": str, + "publish_incomplete": str, + "numero": int_null_is_zero, + }, + input_formators={ + "jour": DateDMYtoISO, + "heure_debut": TimetoISO8601, # converti par do_evaluation_list + "heure_fin": TimetoISO8601, # converti par do_evaluation_list + "visibulletin": int, + "publish_incomplete": int, + }, + ) + + def _evaluation_check_write_access(self, REQUEST, moduleimpl_id=None): + """Vérifie que l'on a le droit de modifier, créer ou détruire une + évaluation dans ce module. + Sinon, lance une exception. + (nb: n'implique pas le droit de saisir ou modifier des notes) + """ + # acces pour resp. moduleimpl et resp. form semestre (dir etud) + if moduleimpl_id is None: + raise ValueError("no moduleimpl specified") # bug + authuser = REQUEST.AUTHENTICATED_USER + uid = str(authuser) + M = self.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) + + if ( + (not authuser.has_permission(ScoEditAllEvals, self)) + and uid != M["responsable_id"] + and uid not in sem["responsables"] + ): + if sem["ens_can_edit_eval"]: + for ens in M["ens"]: + if ens["ens_id"] == uid: + return # ok + raise AccessDenied("Modification évaluation impossible pour %s" % (uid,)) + + security.declareProtected(ScoEnsView, "do_evaluation_create") + + def do_evaluation_create( + self, + moduleimpl_id=None, + jour=None, + heure_debut=None, + heure_fin=None, + description=None, + note_max=None, + coefficient=None, + visibulletin=None, + publish_incomplete=None, + evaluation_type=None, + numero=None, + REQUEST=None, + **kw + ): + """Create an evaluation + """ + args = locals() + log("do_evaluation_create: args=" + str(args)) + self._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) + self._check_evaluation_args(args) + # Check numeros + sco_evaluations.module_evaluation_renumber( + self, moduleimpl_id, REQUEST=REQUEST, only_if_unumbered=True + ) + if not "numero" in args or args["numero"] is None: + n = None + # determine le numero avec la date + # Liste des eval existantes triees par date, la plus ancienne en tete + ModEvals = self.do_evaluation_list( + args={"moduleimpl_id": moduleimpl_id}, + sortkey="jour asc, heure_debut asc", + ) + if args["jour"]: + next_eval = None + t = (DateDMYtoISO(args["jour"]), TimetoISO8601(args["heure_debut"])) + for e in ModEvals: + if (DateDMYtoISO(e["jour"]), TimetoISO8601(e["heure_debut"])) > t: + next_eval = e + break + if next_eval: + n = sco_evaluations.module_evaluation_insert_before( + self, ModEvals, next_eval, REQUEST + ) + else: + n = None # a placer en fin + if n is None: # pas de date ou en fin: + if ModEvals: + log(pprint.pformat(ModEvals[-1])) + n = ModEvals[-1]["numero"] + 1 + else: + n = 0 # the only one + # log("creating with numero n=%d" % n) + args["numero"] = n + + # + cnx = self.GetDBConnexion() + r = self._evaluationEditor.create(cnx, args) + + # news + M = self.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + mod = self.do_module_list(args={"module_id": M["module_id"]})[0] + mod["moduleimpl_id"] = M["moduleimpl_id"] + mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod + sco_news.add( + self, + REQUEST, + typ=NEWS_NOTE, + object=moduleimpl_id, + text='Création d\'une évaluation dans %(titre)s' + % mod, + url=mod["url"], + ) + + return r + + def _check_evaluation_args(self, args): + "Check coefficient, dates and duration, raises exception if invalid" + moduleimpl_id = args["moduleimpl_id"] + # check bareme + note_max = args.get("note_max", None) + if note_max is None: + raise ScoValueError("missing note_max") + try: + note_max = float(note_max) + except ValueError: + raise ScoValueError("Invalid note_max value") + if note_max < 0: + raise ScoValueError("Invalid note_max value (must be positive or null)") + # check coefficient + coef = args.get("coefficient", None) + if coef is None: + raise ScoValueError("missing coefficient") + try: + coef = float(coef) + except ValueError: + raise ScoValueError("Invalid coefficient value") + if coef < 0: + raise ScoValueError("Invalid coefficient value (must be positive or null)") + # check date + jour = args.get("jour", None) + args["jour"] = jour + if jour: + M = self.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) + d, m, y = [int(x) for x in sem["date_debut"].split("/")] + date_debut = datetime.date(y, m, d) + d, m, y = [int(x) for x in sem["date_fin"].split("/")] + date_fin = datetime.date(y, m, d) + # passe par DateDMYtoISO pour avoir date pivot + y, m, d = [int(x) for x in DateDMYtoISO(jour).split("-")] + jour = datetime.date(y, m, d) + if (jour > date_fin) or (jour < date_debut): + raise ScoValueError( + "La date de l'évaluation (%s/%s/%s) n'est pas dans le semestre !" + % (d, m, y) + ) + heure_debut = args.get("heure_debut", None) + args["heure_debut"] = heure_debut + heure_fin = args.get("heure_fin", None) + args["heure_fin"] = heure_fin + if jour and ((not heure_debut) or (not heure_fin)): + raise ScoValueError("Les heures doivent être précisées") + d = TimeDuration(heure_debut, heure_fin) + if d and ((d < 0) or (d > 60 * 12)): + raise ScoValueError("Heures de l'évaluation incohérentes !") + + security.declareProtected(ScoEnsView, "evaluation_delete") + + def evaluation_delete(self, REQUEST, evaluation_id): + """Form delete evaluation""" + El = self.do_evaluation_list(args={"evaluation_id": evaluation_id}) + if not El: + raise ValueError("Evalution inexistante ! (%s)" % evaluation_id) + E = El[0] + M = self.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + Mod = self.do_module_list(args={"module_id": M["module_id"]})[0] + tit = "Suppression de l'évaluation %(description)s (%(jour)s)" % E + etat = sco_evaluations.do_evaluation_etat(self, evaluation_id) + H = [ + self.html_sem_header(REQUEST, tit, with_h2=False), + """

    Module %(code)s %(titre)s

    """ + % Mod, + """

    %s

    """ % tit, + """

    Opération irréversible. Si vous supprimez l'évaluation, vous ne pourrez pas retrouver les notes associées.

    """, + ] + warning = False + if etat["nb_notes_total"]: + warning = True + nb_desinscrits = etat["nb_notes_total"] - etat["nb_notes"] + H.append( + """
    Il y a %s notes""" + % etat["nb_notes_total"] + ) + if nb_desinscrits: + H.append( + """ (dont %s d'Ă©tudiants qui ne sont plus inscrits)""" + % nb_desinscrits + ) + H.append(""" dans l'Ă©valuation""") + if etat["nb_notes"] == 0: + H.append( + """

    Vous pouvez quand même supprimer l'évaluation, les notes des étudiants désincrits seront effacées.

    """ + ) + + if etat["nb_notes"]: + H.append( + """

    Suppression impossible (effacer les notes d'abord)

    retour au tableau de bord du module

    """ + % E["moduleimpl_id"] + ) + return "\n".join(H) + self.sco_footer(REQUEST) + if warning: + H.append("""""") + + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + (("evaluation_id", {"input_type": "hidden"}),), + initvalues=E, + submitlabel="Confirmer la suppression", + cancelbutton="Annuler", + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect( + self.ScoURL() + + "/Notes/moduleimpl_status?moduleimpl_id=" + + E["moduleimpl_id"] + ) + else: + sco_evaluations.do_evaluation_delete(self, REQUEST, E["evaluation_id"]) + return ( + "\n".join(H) + + """

    OK, évaluation supprimée.

    +

    Continuer

    """ + % ( + self.ScoURL() + + "/Notes/moduleimpl_status?moduleimpl_id=" + + E["moduleimpl_id"] + ) + + self.sco_footer(REQUEST) + ) + + security.declareProtected(ScoView, "do_evaluation_list") + + def do_evaluation_list(self, args, sortkey=None): + """List evaluations, sorted by numero (or most recent date first). + + Ajoute les champs: + 'duree' : '2h30' + 'matin' : 1 (commence avant 12:00) ou 0 + 'apresmidi' : 1 (termine après 12:00) ou 0 + 'descrheure' : ' de 15h00 à 16h30' + """ + cnx = self.GetDBConnexion() + evals = self._evaluationEditor.list(cnx, args, sortkey=sortkey) + # calcule duree (chaine de car.) de chaque evaluation et ajoute jouriso, matin, apresmidi + for e in evals: + heure_debut_dt = e["heure_debut"] or datetime.time( + 8, 00 + ) # au cas ou pas d'heure (note externe?) + heure_fin_dt = e["heure_fin"] or datetime.time(8, 00) + e["heure_debut"] = TimefromISO8601(e["heure_debut"]) + e["heure_fin"] = TimefromISO8601(e["heure_fin"]) + e["jouriso"] = DateDMYtoISO(e["jour"]) + heure_debut, heure_fin = e["heure_debut"], e["heure_fin"] + d = TimeDuration(heure_debut, heure_fin) + if d is not None: + m = d % 60 + e["duree"] = "%dh" % (d / 60) + if m != 0: + e["duree"] += "%02d" % m + else: + e["duree"] = "" + if heure_debut and (not heure_fin or heure_fin == heure_debut): + e["descrheure"] = " à " + heure_debut + elif heure_debut and heure_fin: + e["descrheure"] = " de %s à %s" % (heure_debut, heure_fin) + else: + e["descrheure"] = "" + # matin, apresmidi: utile pour se referer aux absences: + if heure_debut_dt < datetime.time(12, 00): + e["matin"] = 1 + else: + e["matin"] = 0 + if heure_fin_dt > datetime.time(12, 00): + e["apresmidi"] = 1 + else: + e["apresmidi"] = 0 + + return evals + + security.declareProtected(ScoView, "do_evaluation_list_in_formsemestre") + + def do_evaluation_list_in_formsemestre(self, formsemestre_id): + "list evaluations in this formsemestre" + cnx = self.GetDBConnexion() + mods = self.do_moduleimpl_list(formsemestre_id=formsemestre_id) + evals = [] + for mod in mods: + evals += self.do_evaluation_list( + args={"moduleimpl_id": mod["moduleimpl_id"]} + ) + return evals + + security.declareProtected(ScoEnsView, "do_evaluation_edit") + + def do_evaluation_edit(self, REQUEST, args): + "edit a evaluation" + evaluation_id = args["evaluation_id"] + the_evals = self.do_evaluation_list({"evaluation_id": evaluation_id}) + if not the_evals: + raise ValueError("evaluation inexistante !") + moduleimpl_id = the_evals[0]["moduleimpl_id"] + args["moduleimpl_id"] = moduleimpl_id + self._check_evaluation_args(args) + self._evaluation_check_write_access(REQUEST, moduleimpl_id=moduleimpl_id) + cnx = self.GetDBConnexion() + self._evaluationEditor.edit(cnx, args) + # inval cache pour ce semestre + M = self.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + self._inval_cache( + formsemestre_id=M["formsemestre_id"] + ) # > evaluation_edit (coef, ...) + + security.declareProtected(ScoEnsView, "evaluation_edit") + + def evaluation_edit(self, evaluation_id, REQUEST): + "form edit evaluation" + return sco_evaluations.evaluation_create_form( + self, evaluation_id=evaluation_id, REQUEST=REQUEST, edit=True + ) + + security.declareProtected(ScoEnsView, "evaluation_create") + + def evaluation_create(self, moduleimpl_id, REQUEST): + "form create evaluation" + return sco_evaluations.evaluation_create_form( + self, moduleimpl_id=moduleimpl_id, REQUEST=REQUEST, edit=False + ) + + security.declareProtected(ScoView, "evaluation_listenotes") + + def evaluation_listenotes(self, REQUEST=None): + """Affichage des notes d'une évaluation""" + if REQUEST.form.get("format", "html") == "html": + H = self.sco_header( + REQUEST, + cssstyles=["css/verticalhisto.css"], + javascripts=["js/etud_info.js"], + init_qtip=True, + ) + F = self.sco_footer(REQUEST) + else: + H, F = "", "" + B = self.do_evaluation_listenotes(REQUEST) + return H + B + F + + security.declareProtected(ScoView, "do_evaluation_listenotes") + do_evaluation_listenotes = sco_liste_notes.do_evaluation_listenotes + + security.declareProtected(ScoView, "evaluation_list_operations") + evaluation_list_operations = sco_undo_notes.evaluation_list_operations + + security.declareProtected(ScoView, "evaluation_check_absences_html") + evaluation_check_absences_html = sco_liste_notes.evaluation_check_absences_html + + security.declareProtected(ScoView, "formsemestre_check_absences_html") + formsemestre_check_absences_html = sco_liste_notes.formsemestre_check_absences_html + + # --- Placement des étudiants pour l'évaluation + security.declareProtected(ScoEnsView, "placement_eval_selectetuds") + placement_eval_selectetuds = sco_placement.placement_eval_selectetuds + + security.declareProtected(ScoEnsView, "do_placement") + do_placement = sco_placement.do_placement + + # --- Saisie des notes + security.declareProtected(ScoEnsView, "saisie_notes_tableur") + saisie_notes_tableur = sco_saisie_notes.saisie_notes_tableur + + security.declareProtected(ScoEnsView, "feuille_saisie_notes") + feuille_saisie_notes = sco_saisie_notes.feuille_saisie_notes + + security.declareProtected(ScoEnsView, "saisie_notes") + saisie_notes = sco_saisie_notes.saisie_notes + + security.declareProtected(ScoEnsView, "save_note") + save_note = sco_saisie_notes.save_note + + security.declareProtected(ScoEnsView, "do_evaluation_set_missing") + do_evaluation_set_missing = sco_saisie_notes.do_evaluation_set_missing + + security.declareProtected(ScoView, "evaluation_suppress_alln") + evaluation_suppress_alln = sco_saisie_notes.evaluation_suppress_alln + + security.declareProtected(ScoView, "can_edit_notes") + + def can_edit_notes(self, authuser, moduleimpl_id, allow_ens=True): + """True if authuser can enter or edit notes in this module. + If allow_ens, grant access to all ens in this module + + Si des décisions de jury ont déjà été saisies dans ce semestre, + seul le directeur des études peut saisir des notes (et il ne devrait pas). + """ + uid = str(authuser) + M = self.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) + if sem["etat"] != "1": + return False # semestre verrouillé + + if sco_parcours_dut.formsemestre_has_decisions(self, sem["formsemestre_id"]): + # il y a des décisions de jury dans ce semestre ! + return ( + authuser.has_permission(ScoEditAllNotes, self) + or uid in sem["responsables"] + ) + else: + if ( + (not authuser.has_permission(ScoEditAllNotes, self)) + and uid != M["responsable_id"] + and uid not in sem["responsables"] + ): + # enseignant (chargé de TD) ? + if allow_ens: + for ens in M["ens"]: + if ens["ens_id"] == uid: + return True + return False + else: + return True + + security.declareProtected(ScoEditAllNotes, "dummy_ScoEditAllNotes") + + def dummy_ScoEditAllNotes(self): + "dummy method, necessary to declare permission ScoEditAllNotes" + return True + + security.declareProtected(ScoEditAllEvals, "dummy_ScoEditAllEvals") + + def dummy_ScoEditAllEvals(self): + "dummy method, necessary to declare permission ScoEditAllEvals" + return True + + security.declareProtected(ScoSuperAdmin, "dummy_ScoSuperAdmin") + + def dummy_ScoSuperAdmin(self): + "dummy method, necessary to declare permission ScoSuperAdmin" + return True + + security.declareProtected(ScoEtudChangeGroups, "dummy_ScoEtudChangeGroups") + + def dummy_ScoEtudChangeGroups(self): + "dummy method, necessary to declare permission ScoEtudChangeGroups" + return True + + security.declareProtected(ScoEtudSupprAnnotations, "dummy_ScoEtudSupprAnnotations") + + def dummy_ScoEtudSupprAnnotations(self): + "dummy method, necessary to declare permission ScoEtudSupprAnnotations" + return True + + security.declareProtected(ScoEditFormationTags, "dummy_ScoEditFormationTags") + + def dummy_ScoEditFormationTags(self): + "dummy method, necessary to declare permission ScoEditFormationTags" + return True + + # cache notes evaluations + def get_evaluations_cache(self): + u = self.GetDBConnexionString() + if CACHE_evaluations.has_key(u): + return CACHE_evaluations[u] + else: + log("get_evaluations_cache: new simpleCache") + CACHE_evaluations[u] = sco_cache.simpleCache() + return CACHE_evaluations[u] + + def _notes_getall( + self, evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None + ): + """get tt les notes pour une evaluation: { etudid : { 'value' : value, 'date' : date ... }} + Attention: inclue aussi les notes des étudiants qui ne sont plus inscrits au module. + """ + # log('_notes_getall( e=%s fs=%s )' % (evaluation_id, filter_suppressed)) + do_cache = ( + filter_suppressed and table == "notes_notes" and (by_uid is None) + ) # pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant + if do_cache: + cache = self.get_evaluations_cache() + r = cache.get(evaluation_id) + if r != None: + return r + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cond = " where evaluation_id=%(evaluation_id)s" + if by_uid: + cond += " and uid=%(by_uid)s" + + cursor.execute( + "select * from " + table + cond, + {"evaluation_id": evaluation_id, "by_uid": by_uid}, + ) + res = cursor.dictfetchall() + d = {} + if filter_suppressed: + for x in res: + if x["value"] != NOTES_SUPPRESS: + d[x["etudid"]] = x + else: + for x in res: + d[x["etudid"]] = x + if do_cache: + cache.set(evaluation_id, d) + return d + + # --- Bulletins + security.declareProtected(ScoView, "formsemestre_bulletins_pdf") + + def formsemestre_bulletins_pdf( + self, formsemestre_id, REQUEST, version="selectedevals" + ): + "Publie les bulletins dans un classeur PDF" + pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( + self, formsemestre_id, REQUEST, version=version + ) + return sendPDFFile(REQUEST, pdfdoc, filename) + + security.declareProtected(ScoView, "etud_bulletins_pdf") + + def etud_bulletins_pdf(self, etudid, REQUEST, version="selectedevals"): + "Publie tous les bulletins d'un etudiants dans un classeur PDF" + pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf( + self, etudid, REQUEST, version=version + ) + return sendPDFFile(REQUEST, pdfdoc, filename) + + security.declareProtected(ScoView, "formsemestre_bulletins_pdf_choice") + formsemestre_bulletins_pdf_choice = sco_bulletins.formsemestre_bulletins_pdf_choice + + security.declareProtected(ScoView, "formsemestre_bulletins_mailetuds_choice") + formsemestre_bulletins_mailetuds_choice = ( + sco_bulletins.formsemestre_bulletins_mailetuds_choice + ) + + security.declareProtected(ScoView, "formsemestre_bulletins_mailetuds") + + def formsemestre_bulletins_mailetuds( + self, + formsemestre_id, + REQUEST, + version="long", + dialog_confirmed=False, + prefer_mail_perso=0, + ): + "envoi a chaque etudiant (inscrit et ayant un mail) son bulletin" + prefer_mail_perso = int(prefer_mail_perso) + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + nt = self._getNotesCache().get_NotesTable( + self, formsemestre_id + ) # > get_etudids + etudids = nt.get_etudids() + # + if not sco_bulletins.can_send_bulletin_by_mail(self, formsemestre_id, REQUEST): + raise AccessDenied("vous n'avez pas le droit d'envoyer les bulletins") + # Confirmation dialog + if not dialog_confirmed: + return self.confirmDialog( + "

    Envoyer les %d bulletins par e-mail aux Ă©tudiants ?" + % len(etudids), + dest_url="", + REQUEST=REQUEST, + cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id, + parameters={ + "version": version, + "formsemestre_id": formsemestre_id, + "prefer_mail_perso": prefer_mail_perso, + }, + ) + + # Make each bulletin + nb_send = 0 + for etudid in etudids: + h, i = sco_bulletins.do_formsemestre_bulletinetud( + self, + formsemestre_id, + etudid, + version=version, + prefer_mail_perso=prefer_mail_perso, + format="pdfmail", + nohtml=True, + REQUEST=REQUEST, + ) + if h: + nb_send += 1 + # + return ( + self.sco_header(REQUEST) + + '

    %d bulletins sur %d envoyés par mail !

    continuer

    ' + % (nb_send, len(etudids), formsemestre_id) + + self.sco_footer(REQUEST) + ) + + security.declareProtected(ScoView, "formsemestre_bulletins_mailetuds") + external_ue_create_form = sco_ue_external.external_ue_create_form + + security.declareProtected(ScoEnsView, "appreciation_add_form") + + def appreciation_add_form( + self, + etudid=None, + formsemestre_id=None, + id=None, # si id, edit + suppress=False, # si true, supress id + REQUEST=None, + ): + "form ajout ou edition d'une appreciation" + cnx = self.GetDBConnexion() + authuser = REQUEST.AUTHENTICATED_USER + if id: # edit mode + apps = scolars.appreciations_list(cnx, args={"id": id}) + if not apps: + raise ScoValueError("id d'appreciation invalide !") + app = apps[0] + formsemestre_id = app["formsemestre_id"] + etudid = app["etudid"] + if REQUEST.form.has_key("edit"): + edit = int(REQUEST.form["edit"]) + elif id: + edit = 1 + else: + edit = 0 + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + # check custom access permission + can_edit_app = (str(authuser) in sem["responsables"]) or ( + authuser.has_permission(ScoEtudInscrit, self) + ) + if not can_edit_app: + raise AccessDenied("vous n'avez pas le droit d'ajouter une appreciation") + # + bull_url = "formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s" % ( + formsemestre_id, + etudid, + ) + if suppress: + scolars.appreciations_delete(cnx, id) + logdb(REQUEST, cnx, method="appreciation_suppress", etudid=etudid, msg="") + return REQUEST.RESPONSE.redirect(bull_url) + # + etud = self.getEtudInfo(etudid=etudid, filled=1)[0] + if id: + a = "Edition" + else: + a = "Ajout" + H = [ + self.sco_header(REQUEST) + + "

    %s d'une appréciation sur %s

    " % (a, etud["nomprenom"]) + ] + F = self.sco_footer(REQUEST) + descr = [ + ("edit", {"input_type": "hidden", "default": edit}), + ("etudid", {"input_type": "hidden"}), + ("formsemestre_id", {"input_type": "hidden"}), + ("id", {"input_type": "hidden"}), + ("comment", {"title": "", "input_type": "textarea", "rows": 4, "cols": 60}), + ] + if id: + initvalues = { + "etudid": etudid, + "formsemestre_id": formsemestre_id, + "comment": app["comment"], + } + else: + initvalues = {} + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + descr, + initvalues=initvalues, + cancelbutton="Annuler", + submitlabel="Ajouter appréciation", + ) + if tf[0] == 0: + return "\n".join(H) + "\n" + tf[1] + F + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(bull_url) + else: + args = { + "etudid": etudid, + "formsemestre_id": formsemestre_id, + "author": str(authuser), + "comment": tf[2]["comment"], + "zope_authenticated_user": str(authuser), + "zope_remote_addr": REQUEST.REMOTE_ADDR, + } + if edit: + args["id"] = id + scolars.appreciations_edit(cnx, args) + else: # nouvelle + scolars.appreciations_create(cnx, args, has_uniq_values=False) + # log + logdb( + REQUEST, + cnx, + method="appreciation_add", + etudid=etudid, + msg=tf[2]["comment"], + ) + # ennuyeux mais necessaire (pour le PDF seulement) + self._inval_cache( + pdfonly=True, formsemestre_id=formsemestre_id + ) # > appreciation_add + return REQUEST.RESPONSE.redirect(bull_url) + + security.declareProtected(ScoView, "can_change_groups") + + def can_change_groups(self, REQUEST, formsemestre_id): + "Vrai si utilisateur peut changer les groupes dans ce semestre" + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if sem["etat"] != "1": + return False # semestre verrouillé + authuser = REQUEST.AUTHENTICATED_USER + if authuser.has_permission(ScoEtudChangeGroups, self): + return True # admin, chef dept + uid = str(authuser) + if uid in sem["responsables"]: + return True + return False + + def _can_edit_pv(self, REQUEST, formsemestre_id): + "Vrai si utilisateur peut editer un PV de jury de ce semestre" + + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if self._is_chef_or_diretud(REQUEST, sem): + return True + # Autorise les secrétariats, repérés via la permission ScoEtudChangeAdr + # (ceci nous évite d'ajouter une permission Zope aux installations existantes) + authuser = REQUEST.AUTHENTICATED_USER + return authuser.has_permission(ScoEtudChangeAdr, self) + + # --- FORMULAIRE POUR VALIDATION DES UE ET SEMESTRES + def _can_validate_sem(self, REQUEST, formsemestre_id): + "Vrai si utilisateur peut saisir decision de jury dans ce semestre" + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if sem["etat"] != "1": + return False # semestre verrouillé + + return self._is_chef_or_diretud(REQUEST, sem) + + def _is_chef_or_diretud(self, REQUEST, sem): + "Vrai si utilisateur est admin, chef dept ou responsable du semestre" + authuser = REQUEST.AUTHENTICATED_USER + if authuser.has_permission(ScoImplement, self): + return True # admin, chef dept + uid = str(authuser) + if uid in sem["responsables"]: + return True + + return False + + security.declareProtected(ScoView, "formsemestre_validation_etud_form") + + def formsemestre_validation_etud_form( + self, + formsemestre_id, + etudid=None, + etud_index=None, + check=0, + desturl="", + sortcol=None, + REQUEST=None, + ): + "Formulaire choix jury pour un étudiant" + readonly = not self._can_validate_sem(REQUEST, formsemestre_id) + return sco_formsemestre_validation.formsemestre_validation_etud_form( + self, + formsemestre_id, + etudid=etudid, + etud_index=etud_index, + check=check, + readonly=readonly, + desturl=desturl, + sortcol=sortcol, + REQUEST=REQUEST, + ) + + security.declareProtected(ScoView, "formsemestre_validation_etud") + + def formsemestre_validation_etud( + self, + formsemestre_id, + etudid=None, + codechoice=None, + desturl="", + sortcol=None, + REQUEST=None, + ): + "Enregistre choix jury pour un étudiant" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + + return sco_formsemestre_validation.formsemestre_validation_etud( + self, + formsemestre_id, + etudid=etudid, + codechoice=codechoice, + desturl=desturl, + sortcol=sortcol, + REQUEST=REQUEST, + ) + + security.declareProtected(ScoView, "formsemestre_validation_etud_manu") + + def formsemestre_validation_etud_manu( + self, + formsemestre_id, + etudid=None, + code_etat="", + new_code_prev="", + devenir="", + assidu=False, + desturl="", + sortcol=None, + REQUEST=None, + ): + "Enregistre choix jury pour un étudiant" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + + return sco_formsemestre_validation.formsemestre_validation_etud_manu( + self, + formsemestre_id, + etudid=etudid, + code_etat=code_etat, + new_code_prev=new_code_prev, + devenir=devenir, + assidu=assidu, + desturl=desturl, + sortcol=sortcol, + REQUEST=REQUEST, + ) + + security.declareProtected(ScoView, "formsemestre_validate_previous_ue") + + def formsemestre_validate_previous_ue( + self, formsemestre_id, etudid=None, REQUEST=None + ): + "Form. saisie UE validée hors ScoDoc " + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + return sco_formsemestre_validation.formsemestre_validate_previous_ue( + self, formsemestre_id, etudid, REQUEST=REQUEST + ) + + security.declareProtected(ScoView, "formsemestre_ext_create_form") + formsemestre_ext_create_form = ( + sco_formsemestre_exterieurs.formsemestre_ext_create_form + ) + + security.declareProtected(ScoView, "formsemestre_ext_edit_ue_validations") + + def formsemestre_ext_edit_ue_validations( + self, formsemestre_id, etudid=None, REQUEST=None + ): + "Form. edition UE semestre extérieur" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations( + self, formsemestre_id, etudid, REQUEST=REQUEST + ) + + security.declareProtected(ScoView, "get_etud_ue_cap_html") + get_etud_ue_cap_html = sco_formsemestre_validation.get_etud_ue_cap_html + + security.declareProtected(ScoView, "etud_ue_suppress_validation") + + def etud_ue_suppress_validation(self, etudid, formsemestre_id, ue_id, REQUEST=None): + """Suppress a validation (ue_id, etudid) and redirect to formsemestre""" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + return sco_formsemestre_validation.etud_ue_suppress_validation( + self, etudid, formsemestre_id, ue_id, REQUEST=REQUEST + ) + + security.declareProtected(ScoView, "formsemestre_validation_auto") + + def formsemestre_validation_auto(self, formsemestre_id, REQUEST): + "Formulaire saisie automatisee des decisions d'un semestre" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + + return sco_formsemestre_validation.formsemestre_validation_auto( + self, formsemestre_id, REQUEST + ) + + security.declareProtected(ScoView, "formsemestre_validation_auto") + + def do_formsemestre_validation_auto(self, formsemestre_id, REQUEST): + "Formulaire saisie automatisee des decisions d'un semestre" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + + return sco_formsemestre_validation.do_formsemestre_validation_auto( + self, formsemestre_id, REQUEST + ) + + security.declareProtected(ScoView, "formsemestre_fix_validation_ues") + + def formsemestre_fix_validation_ues(self, formsemestre_id, REQUEST=None): + "Verif/reparation codes UE" + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + + return sco_formsemestre_validation.formsemestre_fix_validation_ues( + self, formsemestre_id, REQUEST + ) + + security.declareProtected(ScoView, "formsemestre_validation_suppress_etud") + + def formsemestre_validation_suppress_etud( + self, formsemestre_id, etudid, REQUEST=None, dialog_confirmed=False + ): + """Suppression des decisions de jury pour un etudiant. + """ + if not self._can_validate_sem(REQUEST, formsemestre_id): + return self.confirmDialog( + message="

    Opération non autorisée pour %s" + % REQUEST.AUTHENTICATED_USER, + dest_url=self.ScoURL(), + REQUEST=REQUEST, + ) + if not dialog_confirmed: + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + etud = self.getEtudInfo(etudid=etudid, filled=1)[0] + nt = self._getNotesCache().get_NotesTable( + self, formsemestre_id + ) # > get_etud_decision_sem + decision_jury = nt.get_etud_decision_sem(etudid) + if decision_jury: + existing = ( + "

    DĂ©cision existante: %(code)s du %(event_date)s

    " + % decision_jury + ) + else: + existing = "" + return self.confirmDialog( + """

    Confirmer la suppression des décisions du semestre %s (%s - %s) pour %s ?

    %s +

    Cette opération est irréversible. +

    + """ + % ( + sem["titre_num"], + sem["date_debut"], + sem["date_fin"], + etud["nomprenom"], + existing, + ), + OK="Supprimer", + dest_url="", + REQUEST=REQUEST, + cancel_url="formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s" + % (formsemestre_id, etudid), + parameters={"etudid": etudid, "formsemestre_id": formsemestre_id}, + ) + + sco_formsemestre_validation.formsemestre_validation_suppress_etud( + self, formsemestre_id, etudid + ) + return REQUEST.RESPONSE.redirect( + self.ScoURL() + + "/Notes/formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&head_message=Décision%%20supprimée" + % (formsemestre_id, etudid) + ) + + # ------------- PV de JURY et archives + security.declareProtected(ScoView, "formsemestre_pvjury") + formsemestre_pvjury = sco_pvjury.formsemestre_pvjury + + security.declareProtected(ScoView, "formsemestre_lettres_individuelles") + formsemestre_lettres_individuelles = sco_pvjury.formsemestre_lettres_individuelles + security.declareProtected(ScoView, "formsemestre_pvjury_pdf") + formsemestre_pvjury_pdf = sco_pvjury.formsemestre_pvjury_pdf + + security.declareProtected(ScoView, "feuille_preparation_jury") + feuille_preparation_jury = sco_prepajury.feuille_preparation_jury + + security.declareProtected(ScoView, "formsemestre_archive") + formsemestre_archive = sco_archives.formsemestre_archive + + security.declareProtected(ScoView, "formsemestre_delete_archive") + formsemestre_delete_archive = sco_archives.formsemestre_delete_archive + + security.declareProtected(ScoView, "formsemestre_list_archives") + formsemestre_list_archives = sco_archives.formsemestre_list_archives + + security.declareProtected(ScoView, "formsemestre_get_archived_file") + formsemestre_get_archived_file = sco_archives.formsemestre_get_archived_file + + security.declareProtected(ScoEditApo, "view_apo_csv") + view_apo_csv = sco_etape_apogee_view.view_apo_csv + + security.declareProtected(ScoEditApo, "view_apo_csv_store") + view_apo_csv_store = sco_etape_apogee_view.view_apo_csv_store + + security.declareProtected(ScoEditApo, "view_apo_csv_download_and_store") + view_apo_csv_download_and_store = ( + sco_etape_apogee_view.view_apo_csv_download_and_store + ) + + security.declareProtected(ScoEditApo, "view_apo_csv_delete") + view_apo_csv_delete = sco_etape_apogee_view.view_apo_csv_delete + + security.declareProtected(ScoEditApo, "view_scodoc_etuds") + view_scodoc_etuds = sco_etape_apogee_view.view_scodoc_etuds + + security.declareProtected(ScoEditApo, "view_apo_etuds") + view_apo_etuds = sco_etape_apogee_view.view_apo_etuds + + security.declareProtected(ScoEditApo, "apo_semset_maq_status") + apo_semset_maq_status = sco_etape_apogee_view.apo_semset_maq_status + + security.declareProtected(ScoEditApo, "apo_csv_export_results") + apo_csv_export_results = sco_etape_apogee_view.apo_csv_export_results + + # sco_semset + security.declareProtected(ScoEditApo, "semset_page") + semset_page = sco_semset.semset_page + + security.declareProtected(ScoEditApo, "do_semset_create") + do_semset_create = sco_semset.do_semset_create + + security.declareProtected(ScoEditApo, "do_semset_delete") + do_semset_delete = sco_semset.do_semset_delete + + security.declareProtected(ScoEditApo, "edit_semset_set_title") + edit_semset_set_title = sco_semset.edit_semset_set_title + + security.declareProtected(ScoEditApo, "do_semset_add_sem") + do_semset_add_sem = sco_semset.do_semset_add_sem + + security.declareProtected(ScoEditApo, "do_semset_remove_sem") + do_semset_remove_sem = sco_semset.do_semset_remove_sem + + # sco_export_result + security.declareProtected(ScoEditApo, "scodoc_table_results") + scodoc_table_results = sco_export_results.scodoc_table_results + + security.declareProtected(ScoView, "apo_compare_csv_form") + apo_compare_csv_form = sco_apogee_compare.apo_compare_csv_form + + security.declareProtected(ScoView, "apo_compare_csv") + apo_compare_csv = sco_apogee_compare.apo_compare_csv + + # ------------- INSCRIPTIONS: PASSAGE D'UN SEMESTRE A UN AUTRE + security.declareProtected(ScoEtudInscrit, "formsemestre_inscr_passage") + formsemestre_inscr_passage = sco_inscr_passage.formsemestre_inscr_passage + + security.declareProtected(ScoView, "formsemestre_synchro_etuds") + formsemestre_synchro_etuds = sco_synchro_etuds.formsemestre_synchro_etuds + + # ------------- RAPPORTS STATISTIQUES + security.declareProtected(ScoView, "formsemestre_report_counts") + formsemestre_report_counts = sco_report.formsemestre_report_counts + + security.declareProtected(ScoView, "formsemestre_suivi_cohorte") + formsemestre_suivi_cohorte = sco_report.formsemestre_suivi_cohorte + + security.declareProtected(ScoView, "formsemestre_suivi_parcours") + formsemestre_suivi_parcours = sco_report.formsemestre_suivi_parcours + + security.declareProtected(ScoView, "formsemestre_etuds_lycees") + formsemestre_etuds_lycees = sco_lycee.formsemestre_etuds_lycees + + security.declareProtected(ScoView, "scodoc_table_etuds_lycees") + scodoc_table_etuds_lycees = sco_lycee.scodoc_table_etuds_lycees + + security.declareProtected(ScoView, "formsemestre_graph_parcours") + formsemestre_graph_parcours = sco_report.formsemestre_graph_parcours + + security.declareProtected(ScoView, "formsemestre_poursuite_report") + formsemestre_poursuite_report = sco_poursuite_dut.formsemestre_poursuite_report + + security.declareProtected(ScoView, "pe_view_sem_recap") + pe_view_sem_recap = pe_view.pe_view_sem_recap + + security.declareProtected(ScoView, "report_debouche_date") + report_debouche_date = sco_debouche.report_debouche_date + + security.declareProtected(ScoView, "formsemestre_estim_cost") + formsemestre_estim_cost = sco_cost_formation.formsemestre_estim_cost + + # -------------------------------------------------------------------- + # DEBUG + security.declareProtected(ScoView, "check_sem_integrity") + + def check_sem_integrity(self, formsemestre_id, REQUEST): + """Debug. + Check that ue and module formations are consistents + """ + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + + modimpls = self.do_moduleimpl_list(formsemestre_id=formsemestre_id) + bad_ue = [] + bad_sem = [] + for modimpl in modimpls: + mod = self.do_module_list({"module_id": modimpl["module_id"]})[0] + ue = self.do_ue_list({"ue_id": mod["ue_id"]})[0] + if ue["formation_id"] != mod["formation_id"]: + modimpl["mod"] = mod + modimpl["ue"] = ue + bad_ue.append(modimpl) + if sem["formation_id"] != mod["formation_id"]: + bad_sem.append(modimpl) + modimpl["mod"] = mod + + return ( + self.sco_header(REQUEST=REQUEST) + + "

    formation_id=%s" % sem["formation_id"] + + "

    Inconsistent UE/MOD:

    " + + "
    ".join([str(x) for x in bad_ue]) + + "

    Inconsistent SEM/MOD:

    " + + "
    ".join([str(x) for x in bad_sem]) + + self.sco_footer(REQUEST) + ) + + security.declareProtected(ScoView, "check_form_integrity") + + def check_form_integrity(self, formation_id, fix=False, REQUEST=None): + "debug" + log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix)) + F = self.formation_list(args={"formation_id": formation_id})[0] + ues = self.do_ue_list(args={"formation_id": formation_id}) + bad = [] + for ue in ues: + mats = self.do_matiere_list(args={"ue_id": ue["ue_id"]}) + for mat in mats: + mods = self.do_module_list({"matiere_id": mat["matiere_id"]}) + for mod in mods: + if mod["ue_id"] != ue["ue_id"]: + if fix: + # fix mod.ue_id + log( + "fix: mod.ue_id = %s (was %s)" + % (ue["ue_id"], mod["ue_id"]) + ) + mod["ue_id"] = ue["ue_id"] + self.do_module_edit(mod) + bad.append(mod) + if mod["formation_id"] != formation_id: + bad.append(mod) + if bad: + txth = "
    ".join([str(x) for x in bad]) + txt = "\n".join([str(x) for x in bad]) + log( + "check_form_integrity: formation_id=%s\ninconsistencies:" % formation_id + ) + log(txt) + # Notify by e-mail + sendAlarm(self, "Notes: formation incoherente !", txt) + else: + txth = "OK" + log("ok") + return self.sco_header(REQUEST=REQUEST) + txth + self.sco_footer(REQUEST) + + security.declareProtected(ScoView, "check_formsemestre_integrity") + + def check_formsemestre_integrity(self, formsemestre_id, REQUEST=None): + "debug" + log("check_formsemestre_integrity: formsemestre_id=%s" % (formsemestre_id)) + # verifie que tous les moduleimpl d'un formsemestre + # se réfèrent à un module dont l'UE appartient a la même formation + # Ancien bug: les ue_id étaient mal copiés lors des création de versions + # de formations + diag = [] + + Mlist = self.do_moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) + for mod in Mlist: + if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]: + diag.append( + "moduleimpl %s: module.ue_id=%s != matiere.ue_id=%s" + % ( + mod["moduleimpl_id"], + mod["module"]["ue_id"], + mod["matiere"]["ue_id"], + ) + ) + if mod["ue"]["formation_id"] != mod["module"]["formation_id"]: + diag.append( + "moduleimpl %s: ue.formation_id=%s != mod.formation_id=%s" + % ( + mod["moduleimpl_id"], + mod["ue"]["formation_id"], + mod["module"]["formation_id"], + ) + ) + if diag: + sendAlarm( + self, + "Notes: formation incoherente dans semestre %s !" % formsemestre_id, + "\n".join(diag), + ) + log("check_formsemestre_integrity: formsemestre_id=%s" % formsemestre_id) + log("inconsistencies:\n" + "\n".join(diag)) + else: + diag = ["OK"] + log("ok") + return ( + self.sco_header(REQUEST=REQUEST) + + "
    ".join(diag) + + self.sco_footer(REQUEST) + ) + + security.declareProtected(ScoView, "check_integrity_all") + + def check_integrity_all(self, REQUEST=None): + "debug: verifie tous les semestres et tt les formations" + # formations + for F in self.formation_list(): + self.check_form_integrity(F["formation_id"], REQUEST=REQUEST) + # semestres + for sem in sco_formsemestre.do_formsemestre_list(self): + self.check_formsemestre_integrity(sem["formsemestre_id"], REQUEST=REQUEST) + return ( + self.sco_header(REQUEST=REQUEST) + + "

    empty page: see logs and mails

    " + + self.sco_footer(REQUEST) + ) + + # -------------------------------------------------------------------- + + +# -------------------------------------------------------------------- +# +# Zope Product Administration +# +# -------------------------------------------------------------------- +def manage_addZNotes( + self, id="id_ZNotes", title="The Title for ZNotes Object", REQUEST=None +): + "Add a ZNotes instance to a folder." + self._setObject(id, ZNotes(id, title)) + if REQUEST is not None: + return self.manage_main(self, REQUEST) + # return self.manage_editForm(self, REQUEST) + + +# The form used to get the instance id from the user. +manage_addZNotesForm = DTMLFile("dtml/manage_addZNotesForm", globals()) diff --git a/ZScoDoc.py b/ZScoDoc.py new file mode 100644 index 00000000..a3f1d890 --- /dev/null +++ b/ZScoDoc.py @@ -0,0 +1,954 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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. +""" + +import time, string, glob, re, inspect +import urllib, urllib2, cgi, xml + +try: + from cStringIO import StringIO +except: + from StringIO import StringIO +from zipfile import ZipFile +import os.path, glob +import traceback + +from email.MIMEMultipart import MIMEMultipart +from email.MIMEText import MIMEText +from email.MIMEBase import MIMEBase +from email.Header import Header +from email import Encoders + +from sco_zope import * + +# +try: + import Products.ZPsycopgDA.DA as ZopeDA +except: + import ZPsycopgDA.DA as ZopeDA # interp.py + +from sco_utils import * +from notes_log import log +import sco_find_etud +from ZScoUsers import pwdFascistCheck + + +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 + + ( + # this line is kept as an example with the files : + # dtml/manage_editZScolarForm.dtml + # html/ZScolar-edit.stx + # {'label': 'Properties', 'action': 'manage_editForm',}, + {"label": "View", "action": "index_html"}, + ) + + 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): + """Check if user has permission to add/delete departements + """ + authuser = REQUEST.AUTHENTICATED_USER + if authuser.has_role("manager") or authuser.has_permission(ScoSuperAdmin, self): + return "" + else: + return """

    Vous n'avez pas le droit d'accéder à cette page

    """ + + def _check_users_folder(self, REQUEST=None): + """Vérifie UserFolder et le crée s'il le faut + """ + try: + udb = self.UsersDB + return "" + except: + e = self._check_admin_perm(REQUEST) + if not e: # admin permissions: + self.create_users_cnx(REQUEST) + self.create_users_folder(REQUEST) + return '
    Création du connecteur utilisateurs réussie
    ' + else: + return """
    Installation non terminée: connectez vous avec les droits d'administrateur
    """ + + security.declareProtected("View", "create_users_folder") + + def create_users_folder(self, REQUEST=None): + """Create Zope user folder + """ + 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: + x = self.getAuthFailedMessage + except: + log("adding getAuthFailedMessage to Zope install") + parent = self.aq_parent + from OFS.DTMLMethod import addDTMLMethod + + 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", + SCO_DEFAULT_SQL_USERS_CNX, + 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 + if pwdFascistCheck(password) != None: + log("refusing weak password") + return REQUEST.RESPONSE.redirect( + "change_admin_user_form?message=Mot%20de%20passe%20trop%20simple,%20recommencez" + ) + # 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('
    %s
    ' % message) + H.append( + """

    Changement du mot de passe administrateur (utilisateur admin)

    +

    +

    + + +
    Nouveau mot de passe:
    Confirmation:
    + +""" + ) + H.append("""""") + 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: + s = folder.Scolarite + 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 + 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 + add_method = DeptFolder.manage_addProduct["OFSP"].manage_addFolder + add_method("Fotos", title="Photos identites " + DeptId) + + # 3- Creation instance ScoDoc + 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): + """Supprime un departement (de Zope seulement, ne touche pas la BD) + """ + 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 ( + "

    Département " + + DeptId + + """ supprimé du serveur web (la base de données n'est pas affectée)!

    Continuer

    """ + % REQUEST.URL1 + ) + + _top_level_css = """ + """ + + _html_begin = """ + + + +%(page_title)s + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + def scodoc_top_html_header(self, REQUEST, page_title="ScoDoc"): + H = [ + self._html_begin + % {"page_title": "ScoDoc: bienvenue", "encoding": SCO_ENCODING}, + self._top_level_css, + """""", + CUSTOM_HTML_HEADER_CNX, + ] + return "\n".join(H) + + security.declareProtected("View", "index_html") + + def index_html(self, REQUEST=None, message=None): + """Top level page for ScoDoc + """ + 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%20de%20passe%20administrateur%20doit%20etre%20change%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('
    %s
    ' % message) + + if isAdmin and not message: + H.append('
    Attention: connecté comme administrateur
    ') + + H.append( + """ +
    +

    ScoDoc: gestion scolarité

    + """ + ) + if authuser.has_role("Authenticated"): + H.append( + """

    Bonjour %s.

    """ % str(authuser) + ) + H.append( + """

    N'oubliez pas de vous déconnecter après usage.

    """ + ) + else: + H.append( + """

    Ce site est réservé au personnel autorisé

    """ + ) + H.append(self.authentication_form(destination=".")) + + if not deptList: + H.append("aucun département existant !") + # si pas de dept et pas admin, propose lien pour loger admin + if not isAdmin: + H.append( + """

    Identifiez vous comme administrateur (au début: nom 'admin', mot de passe 'scodoc')

    """ + ) + else: + H.append('
      ') + 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( + '
    • DĂ©partement %s' + % (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("
    • ") + H.append("
    ") + # Recherche etudiant + H.append(sco_find_etud.form_search_etud_in_accessible_depts(self, REQUEST)) + + if isAdmin: + H.append('

    Administration de ScoDoc

    ') + else: + H.append( + '

    Se connecter comme administrateur

    ' + % REQUEST.BASE0 + ) + + H.append( + """ +
    +

    ScoDoc est un logiciel libre de suivi de la scolarité des étudiants conçu par +E. Viennet (Université Paris 13).

    +
    +
    """ + % (SCO_WEBSITE,) + ) + + H.append("""""") + return "\n".join(H) + + def authentication_form(self, destination=""): + """html snippet for authentication""" + return ( + """ + + +

    + + + + + + + + + +
    Nom:
    Mot de passe:
    +

    +
    """ + % 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), + """
    +

    Scolarité du département %s

    """ + % deptfoldername, + """

    Ce site est +réservé au personnel du département. +

    """, + self.authentication_form(destination="Scolarite"), + """ +

    Pour quitter, logout

    +

    Retour Ă  l'accueil

    +
    +""" + % 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( + '' + ) + + return "\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 """ + +ScoDoc: accueil + + + + + + + +%s""" % ( + SCO_ENCODING, + CUSTOM_HTML_HEADER_CNX, + ) + + security.declareProtected("View", "standard_html_footer") + + def standard_html_footer(self, REQUEST=None): + return """ +

    Problèmes et suggestions sur le logiciel: %s

    +

    ScoDoc est un logiciel libre développé par Emmanuel Viennet.

    +""" % ( + SCO_USERS_LIST, + SCO_USERS_LIST, + ) + + # 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, + error_message=None, + error_type=None, + error_traceback=None, + error_tb=None, + **kv + ): + "Recuperation des exceptions Zope" + sco_exc_mail = SCO_EXC_MAIL + sco_dev_mail = SCO_DEV_MAIL + # 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": + # raise 'LoginRequired', '' # copied from exuserFolder (beurk, old style exception...) + # if REQUEST: + # REQUEST.response.setStatus( 401, "Unauthorized") # ?????? + 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()) + + if error_type == "ScoGenError": + return "

    " + str(error_value) + "

    " + elif error_type in ("ScoValueError", "FormatError"): + # Not a bug, presents a gentle message to the user: + H = [ + self.standard_html_header(REQUEST), + """

    Erreur !

    %s

    """ % error_value, + ] + if error_value.dest_url: + H.append('

    Continuer

    ' % 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( + """ + + +

    Erreur !

    +

    Une erreur est survenue

    +

    + Error Type: %(error_type)s
    + Error Value: %(error_value)s
    +

    +
    +

    L'URL est peut-etre incorrecte ?

    + +

    Si l'erreur persiste, contactez Emmanuel Viennet: + %(sco_dev_mail)s + en copiant ce message d'erreur et le contenu du cadre bleu ci-dessous si possible. +

    +
    """ + % vars() + ) + # display error traceback (? may open a security risk via xss attack ?) + # log('exc B') + txt_html = self._report_request(REQUEST, fmt="html") + H.append( + """

    Zope Traceback (Ă  envoyer par mail Ă  %(sco_dev_mail)s)

    +%(error_tb)s +

    Informations:
    +%(txt_html)s +

    +
    + +

    Merci de votre patience !

    +""" + % vars() + ) + try: + H.append(self.standard_html_footer(REQUEST)) + except: + log("no footer found for error page") + pass + + # --- Mail: + error_traceback_txt = scodoc_html2txt(error_tb) + txt = ( + """ +ErrorType: %(error_type)s + +%(error_traceback_txt)s +""" + % vars() + ) + + 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""" + AUTHENTICATED_USER = REQUEST.get("AUTHENTICATED_USER", "") + dt = time.asctime() + URL = REQUEST.get("URL", "") + QUERY_STRING = REQUEST.get("QUERY_STRING", "") + if QUERY_STRING: + QUERY_STRING = "?" + QUERY_STRING + METHOD = REQUEST.get("REQUEST_METHOD", "") + + if fmt == "txt": + REFERER = REQUEST.get("HTTP_REFERER", "") + HTTP_USER_AGENT = REQUEST.get("HTTP_USER_AGENT", "") + else: + REFERER = "na" + HTTP_USER_AGENT = "na" + form = REQUEST.get("form", "") + HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "") + svn_version = get_svn_version(self.file_path) + SCOVERSION = VERSION.SCOVERSION + + 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 + +subversion: %(svn_version)s +""" + % vars() + ) + if fmt == "html": + txt = txt.replace("\n", "
    ") + 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""" + if not SCO_EXC_MAIL: + 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() + subj = Header("[scodoc] exc %s" % URL, SCO_ENCODING) + msg["Subject"] = subj + recipients = [SCO_EXC_MAIL] + msg["To"] = " ,".join(recipients) + msg["From"] = "scodoc-alert" + msg.epilogue = "" + msg.attach(MIMEText(txt, "plain", SCO_ENCODING)) + self.sendEmailFromException(msg) + log("Sent mail alert:\n" + txt) + + security.declareProtected("View", "scodoc_admin") + + def scodoc_admin(self, REQUEST=None): + """Page Operations d'administration + """ + e = self._check_admin_perm(REQUEST) + if e: + return e + + H = [ + self.scodoc_top_html_header(REQUEST, page_title="ScoDoc: bienvenue"), + """ +

    Administration ScoDoc

    + +

    changer le mot de passe super-administrateur

    +

    retour Ă  la page d'accueil

    + +

    Création d'un département

    +

    Le département doit avoir été créé au préalable sur le serveur en utilisant le script +create_dept.sh (à lancer comme root dans le répertoire config de ScoDoc). +

    """ + % self.absolute_url(), + ] + + deptList = [x.id for x in self.list_depts()] # definis dans Zope + deptIds = Set(self._list_depts_ids()) # definis sur le filesystem + existingDepts = Set(deptList) + addableDepts = deptIds - existingDepts + + if not addableDepts: + # aucun departement defini: aide utilisateur + H.append("

    Aucun département à ajouter !

    ") + else: + H.append("""
    + +
    """ + ) + + if deptList: + H.append( + """ +

    Suppression d'un département

    +

    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). +

    +
    + + + +
    """ + ) + + H.append("""""") + return "\n".join(H) + + def _list_depts_ids(self): + """Liste de id de departements definis par create_dept.sh + (fichiers depts/*.cfg) + """ + filenames = glob.glob(SCODOC_VAR_DIR + "/config/depts/*.cfg") + 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 diff --git a/ZScoUsers.py b/ZScoUsers.py new file mode 100644 index 00000000..f270d742 --- /dev/null +++ b/ZScoUsers.py @@ -0,0 +1,1334 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +""" Gestion des utilisateurs (table SQL pour Zope User Folder) +""" +import string, re +import time +import md5, base64 + + +from sco_zope import * + +# --------------- + +import notesdb +from notesdb import * +from notes_log import log +from scolog import logdb +from sco_utils import * +from scolars import format_prenom, format_nom +import sco_import_users, sco_excel +from TrivialFormulator import TrivialFormulator, TF +from gen_tables import GenTable +import scolars +import sco_cache + +# ----------------- password checking +import cracklib + + +def pwdFascistCheck(cleartxt): + "returns None if OK" + if ( + hasattr(CONFIG, "MIN_PASSWORD_LENGTH") + and CONFIG.MIN_PASSWORD_LENGTH > 0 + and len(cleartxt) < CONFIG.MIN_PASSWORD_LENGTH + ): + return True # invalid + try: + x = cracklib.FascistCheck(cleartxt) + return None + except ValueError as e: + return str(e) + + +# --------------- +# cache global: chaque instance, repérée par son URL, a un cache +# qui est recréé à la demande +# On cache ici la liste des utilisateurs, pour une duree limitee +# (une minute). + +CACHE_userlist = {} + +# --------------- + + +class ZScoUsers( + ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit +): + + "ZScousers object" + + meta_type = "ZScoUsers" + security = ClassSecurityInfo() + + # 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 + + ({"label": "View", "action": "index_html"},) + + Item.manage_options # add the 'Undo' & 'Owner' tab + + RoleManager.manage_options # add the 'Security' tab + ) + + # no permissions, only called from python + def __init__(self, id, title): + "initialise a new instance" + self.id = id + self.title = title + + # The form used to edit this object + def manage_editZScousers(self, title, RESPONSE=None): + "Changes the instance values" + self.title = title + self._p_changed = 1 + RESPONSE.redirect("manage_editForm") + + # Ajout (dans l'instance) d'un dtml modifiable par Zope + def defaultDocFile(self, id, title, file): + f = open(file_path + "/dtml-editable/" + file + ".dtml") + file = f.read() + f.close() + self.manage_addDTMLMethod(id, title, file) + + # Connexion to SQL database of users: + + # Ugly but necessary during transition out of Zope: + _db_cnx_string = "dbname=SCOUSERS port=5432" + security.declareProtected("Change DTML Documents", "GetUsersDBConnexion") + GetUsersDBConnexion = notesdb.GetUsersDBConnexion + + # -------------------------------------------------------------------- + # + # Users (top level) + # + # -------------------------------------------------------------------- + # used to view content of the object + security.declareProtected(ScoUsersView, "index_html") + + def index_html(self, REQUEST, all=0, with_olds=0, format="html"): + "gestion utilisateurs..." + all = int(all) + with_olds = int(with_olds) + # Controle d'acces + authuser = REQUEST.AUTHENTICATED_USER + user_name = str(authuser) + # log('user: %s roles: %s'%(user_name,authuser.getRolesInContext(self))) + user = self._user_list(args={"user_name": user_name}) + if not user: + zope_roles = authuser.getRolesInContext(self) + if ("Manager" in zope_roles) or ("manage" in zope_roles): + dept = "" # special case for zope admin + else: + raise AccessDenied("Vous n'avez pas la permission de voir cette page") + else: + dept = user[0]["dept"] + + H = [self.sco_header(REQUEST, page_title="Gestion des utilisateurs")] + H.append("

    Gestion des utilisateurs

    ") + + if authuser.has_permission(ScoUsersAdmin, self): + H.append( + '

    Ajouter un utilisateur' + ) + H.append( + '   Importer des utilisateurs

    ' + ) + if all: + checked = "checked" + else: + checked = "" + if with_olds: + olds_checked = "checked" + else: + olds_checked = "" + H.append( + """

    + Tous les départements + Avec anciens utilisateurs +

    """ + % (REQUEST.URL0, checked, olds_checked) + ) + + L = self.list_users( + dept, + all=all, + with_olds=with_olds, + format=format, + REQUEST=REQUEST, + with_links=authuser.has_permission(ScoUsersAdmin, self), + ) + if format != "html": + return L + H.append(L) + + F = self.sco_footer(REQUEST) + return "\n".join(H) + F + + _userEditor = EditableTable( + "sco_users", + "user_id", + ( + "user_id", + "user_name", + "passwd", + "roles", + "date_modif_passwd", + "nom", + "prenom", + "email", + "dept", + "passwd_temp", + "status", + "date_expiration", + ), + output_formators={ + "date_modif_passwd": DateISOtoDMY, + "date_expiration": DateISOtoDMY, + }, + input_formators={ + "date_modif_passwd": DateDMYtoISO, + "date_expiration": DateDMYtoISO, + }, + sortkey="nom", + filter_nulls=False, + ) + + def _user_list(self, **kw): + # list info sur utilisateur(s) + cnx = self.GetUsersDBConnexion() + users = self._userEditor.list(cnx, **kw) + for u in users: + if u["status"] == "old": + u["status_txt"] = "(ancien)" + else: + u["status_txt"] = "" + + return users + + def _user_edit(self, user_name, vals): + # edit user + cnx = self.GetUsersDBConnexion() + vals["user_name"] = user_name + self._userEditor.edit(cnx, vals) + self.get_userlist_cache().inval_cache() # > + self.acl_users.cache_removeUser(user_name) # exUserFolder's caches + self.acl_users.xcache_removeUser(user_name) + # Ensure that if status is "old", login is disabled + # note that operation is reversible without having to re-enter a password + # We change the roles (to avoid dealing with passwd hash, controled by exUserFolder) + u = self._user_list(args={"user_name": user_name})[0] + if u["status"] == "old" and u["roles"] and u["roles"][0] != "-": + roles = ["-" + r for r in u["roles"].split(",")] + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + self.acl_users.scodoc_editUser(cursor, user_name, roles=roles) + self.get_userlist_cache().inval_cache() + elif not u["status"] and u["roles"] and u["roles"][0] == "-": + roles = [r[1:] for r in u["roles"].split(",") if (r and r[0] == "-")] + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + self.acl_users.scodoc_editUser(cursor, user_name, roles=roles) + self.get_userlist_cache().inval_cache() + + def _user_delete(self, user_name): + # delete user + cnx = self.GetUsersDBConnexion() + user_id = self._user_list(args={"user_name": user_name})[0]["user_id"] + self._userEditor.delete(cnx, user_id) + self.get_userlist_cache().inval_cache() # > + + def _all_roles(self): + "ensemble de tous les roles attribués ou attribuables" + roles = set(self.DeptUsersRoles()) + cnx = self.GetUsersDBConnexion() + L = self._userEditor.list(cnx, {}) + for l in L: + roles.update([x.strip() for x in l["roles"].split(",")]) + return [r for r in roles if r and r[0] != "-"] + + security.declareProtected(ScoUsersAdmin, "user_info") + + def user_info(self, user_name=None, user=None): + """Donne infos sur l'utilisateur (qui peut ne pas etre dans notre base). + Si user_name est specifie, interroge la BD. Sinon, user doit etre un dict. + """ + if user_name: + infos = self._user_list(args={"user_name": user_name}) + else: + infos = [user.copy()] + user_name = user["user_name"] + + if not infos: + # special case: user is not in our database + return { + "user_name": user_name, + "nom": user_name, + "prenom": "", + "email": "", + "dept": "", + "nomprenom": user_name, + "prenomnom": user_name, + "prenom_fmt": "", + "nom_fmt": user_name, + "nomcomplet": user_name, + "nomplogin": user_name, + "nomnoacc": suppress_accents(user_name), + "passwd_temp": 0, + "status": "", + "date_expiration": None, + } + else: + info = infos[0] + # always conceal password ! + del info["passwd"] # always conceal password ! + # + if info["prenom"]: + p = format_prenom(info["prenom"]) + else: + p = "" + if info["nom"]: + n = format_nom( + info["nom"], uppercase=False + ) # strcapitalize(strlower(info['nom'])) + else: + n = user_name + + prenom_abbrv = abbrev_prenom(p) + # nomprenom est le nom capitalisé suivi de l'initiale du prénom + info["nomprenom"] = (n + " " + prenom_abbrv).strip() + # prenomnom est l'initiale du prénom suivie du nom + info["prenomnom"] = (prenom_abbrv + " " + n).strip() + # nom_fmt et prenom_fmt: minuscule capitalisé + info["nom_fmt"] = n + info["prenom_fmt"] = scolars.format_prenom(p) + # nomcomplet est le prenom et le nom complets + info["nomcomplet"] = info["prenom_fmt"] + " " + info["nom_fmt"] + # nomplogin est le nom en majuscules suivi du prénom et du login + # e.g. Dupont Pierre (dupont) + info["nomplogin"] = "%s %s (%s)" % (strupper(n), p, info["user_name"]) + # nomnoacc est le nom en minuscules sans accents + info["nomnoacc"] = suppress_accents(strlower(info["nom"])) + + return info + + def _can_handle_passwd(self, authuser, user_name, allow_admindepts=False): + """true if authuser can see or change passwd of user_name. + If allow_admindepts, allow Admin from all depts (so they can view users from other depts + and add roles to them). + authuser is a Zope user object. user_name is a string. + """ + # Is authuser a zope admin ? + zope_roles = authuser.getRolesInContext(self) + if ("Manager" in zope_roles) or ("manage" in zope_roles): + return True + # Anyone can change its own passwd (or see its informations) + if str(authuser) == user_name: + return True + # has permission ? + if not authuser.has_permission(ScoUsersAdmin, self): + return False + # Ok, now check that authuser can manage users from this departement + # Get user info + user = self._user_list(args={"user_name": user_name}) + if not user: + return False # we don't have infos on this user ! + # Get authuser info + auth_name = str(authuser) + authuser_info = self._user_list(args={"user_name": auth_name}) + if not authuser_info: + return False # not admin, and not in out database + auth_dept = authuser_info[0]["dept"] + if not auth_dept: + return True # if no dept, can access users from all depts ! + if auth_dept == user[0]["dept"] or allow_admindepts: + return True + else: + return False + + def _is_valid_passwd(self, passwd): + "check if passwd is secure enough" + return not pwdFascistCheck(passwd) + + def do_change_password(self, user_name, password): + user = self._user_list(args={"user_name": user_name}) + assert len(user) == 1, "database inconsistency: len(r)=%d" % len(r) + # should not occur, already tested in _can_handle_passwd + cnx = self.GetUsersDBConnexion() # en mode autocommit + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "update sco_users set date_modif_passwd=now(), passwd_temp=0 where user_name=%(user_name)s", + {"user_name": user_name}, + ) + + # Laisse le exUserFolder modifier les donnees: + self.acl_users.scodoc_editUser( + cursor, user_name, password=password, roles=[user[0]["roles"]] + ) + + log("change_password: change ok for %s" % user_name) + self.get_userlist_cache().inval_cache() # > + + security.declareProtected(ScoView, "change_password") + + def change_password(self, user_name, password, password2, REQUEST): + "change a password" + # ScoUsersAdmin: modif tous les passwd de SON DEPARTEMENT + # sauf si pas de dept (admin global) + H = [] + F = self.sco_footer(REQUEST) + # Check access permission + if not self._can_handle_passwd(REQUEST.AUTHENTICATED_USER, user_name): + # access denied + log( + "change_password: access denied (authuser=%s, user_name=%s, ip=%s)" + % (authuser, user_name, REQUEST.REMOTE_ADDR) + ) + raise AccessDenied( + "vous n'avez pas la permission de changer ce mot de passe" + ) + # check password + if password != password2: + H.append( + """

    Les deux mots de passes saisis sont différents !

    +

    Recommencer

    """ + % user_name + ) + else: + if not self._is_valid_passwd(password): + H.append( + """

    ce mot de passe n\'est pas assez compliqué !
    (oui, il faut un mot de passe vraiment compliqué !)

    +

    Recommencer

    + """ + % user_name + ) + else: + # ok, strong password + # MD5 hash (now computed by exUserFolder) + # digest = md5.new() + # digest.update(password) + # digest = digest.digest() + # md5pwd = string.strip(base64.encodestring(digest)) + # + self.do_change_password(user_name, password) + # + # ici page simplifiee car on peut ne plus avoir + # le droit d'acceder aux feuilles de style + H.append( + "

    Changement effectué !

    Ne notez pas ce mot de passe, mais mémorisez le !

    Rappel: il est interdit de communiquer son mot de passe à un tiers, même si c'est un collègue de confiance !

    Si vous n'êtes pas administrateur, le système va vous redemander votre login et nouveau mot de passe au prochain accès.

    " + ) + return ( + """ + + + +Mot de passe changé + +

    Mot de passe changé !

    +""" + % (SCO_ENCODING, SCO_ENCODING) + + "\n".join(H) + + 'Continuer' + % self.ScoURL() + ) + return self.sco_header(REQUEST) + "\n".join(H) + F + + security.declareProtected(ScoView, "form_change_password") + + def form_change_password(self, REQUEST, user_name=None): + """Formulaire changement mot de passe + Un utilisateur peut toujours changer son mot de passe""" + authuser = REQUEST.AUTHENTICATED_USER + if not user_name: + user_name = str(authuser) + H = [self.sco_header(REQUEST, user_check=False)] + F = self.sco_footer(REQUEST) + # check access + if not self._can_handle_passwd(authuser, user_name): + return ( + "\n".join(H) + + "

    Vous n'avez pas la permission de changer ce mot de passe

    " + + F + ) + # + H.append( + """

    Changement du mot de passe de %(user_name)s

    +

    +

    + + +
    Nouveau mot de passe:
    Confirmation:
    + + +

    +

    Vous pouvez aussi: renvoyer un mot de passe aléatoire temporaire par mail à l'utilisateur +""" + % {"user_name": user_name, "url": REQUEST.URL0} + ) + return "\n".join(H) + F + + security.declareProtected(ScoView, "userinfo") + + def userinfo(self, user_name=None, REQUEST=None): + "display page of info about connected user" + authuser = REQUEST.AUTHENTICATED_USER + if not user_name: + user_name = str(authuser) + # peut on divulguer ces infos ? + if not self._can_handle_passwd( + REQUEST.AUTHENTICATED_USER, user_name, allow_admindepts=True + ): + raise AccessDenied("Vous n'avez pas la permission de voir cette page") + H = [self.sco_header(REQUEST, page_title="Utilisateur %s" % user_name)] + F = self.sco_footer(REQUEST) + H.append("

    Utilisateur: %s" % user_name) + info = self._user_list(args={"user_name": user_name}) + if info: + H.append("%(status_txt)s" % info[0]) + H.append("

    ") + if not info: + H.append( + "

    L' utilisateur '%s' n'est pas défini dans ce module.

    " + % user_name + ) + if authuser.has_permission(ScoEditAllNotes, self): + H.append("

    (il peut modifier toutes les notes)

    ") + if authuser.has_permission(ScoEditAllEvals, self): + H.append("

    (il peut modifier toutes les Ă©valuations)

    ") + if authuser.has_permission(ScoImplement, self): + H.append("

    (il peut creer des formations)

    ") + else: + H.append( + """

    + Login : %(user_name)s
    + Nom : %(nom)s
    + Prénom : %(prenom)s
    + Mail : %(email)s
    + Roles : %(roles)s
    + Dept : %(dept)s
    + Dernière modif mot de passe: %(date_modif_passwd)s
    + Date d'expiration: %(date_expiration)s +

    ") + + if str(authuser) == user_name: + H.append( + '

    Se déconnecter: logout

    ' + ) + # Liste des permissions + H.append( + '

    Permission de cet utilisateur:

      ' + ) + permissions = self.ac_inherited_permissions(1) + scoperms = [p for p in permissions if p[0][:3] == "Sco"] + try: + thisuser = self.acl_users.getUser(user_name) + except: + # expired from cache ? retry... + thisuser = self.acl_users.getUser(user_name) + if not thisuser: + # Cas de figure incompris ? (bug IUT Amiens janvier 2014: login avec accent ?) + H.append( + "
    • impossible de retrouver les permissions de l'utilisateur (contacter l'administrateur)
    • " + ) + else: + for p in scoperms: + permname, value = p[:2] + if thisuser.has_permission(permname, self): + b = "oui" + else: + b = "non" + H.append("
    • %s : %s
    • " % (permname, b)) + H.append("
    ") + if authuser.has_permission(ScoUsersAdmin, self): + H.append( + '

    Liste de tous les utilisateurs

    ' + % self.ScoURL() + ) + return "\n".join(H) + F + + security.declareProtected(ScoUsersAdmin, "create_user_form") + + def create_user_form(self, REQUEST, user_name=None, edit=0): + "form. creation ou edit utilisateur" + # Get authuser info + authuser = REQUEST.AUTHENTICATED_USER + auth_name = str(authuser) + authuser_info = self._user_list(args={"user_name": auth_name}) + initvalues = {} + + # Access control + zope_roles = authuser.getRolesInContext(self) + if ( + not authuser_info + and not ("Manager" in zope_roles) + and not ("manage" in zope_roles) + ): + # not admin, and not in database + raise AccessDenied("invalid user (%s)" % auth_name) + if authuser_info: + auth_dept = authuser_info[0]["dept"] + else: + auth_dept = "" + # + edit = int(edit) + H = [self.sco_header(REQUEST, bodyOnLoad="init_tf_form('')")] + F = self.sco_footer(REQUEST) + if edit: + if not user_name: + raise ValueError("missing argument: user_name") + initvalues = self._user_list(args={"user_name": user_name})[0] + H.append("

    Modification de l'utilisateur %s

    " % user_name) + else: + H.append("

    Création d'un utilisateur

    ") + + if authuser.has_permission(ScoSuperAdmin, self): + H.append("""

    Vous ĂŞtes super administrateur !

    """) + + # Noms de roles pouvant etre attribues aux utilisateurs via ce dialogue + # si pas SuperAdmin, restreint aux rôles EnsX, SecrX, DeptX + # + if authuser.has_permission(ScoSuperAdmin, self): + log("create_user_form called by %s (super admin)" % (auth_name,)) + editable_roles = set(self._all_roles()) + else: + editable_roles = set(self.DeptUsersRoles()) + # log('create_user_form: editable_roles=%s' % editable_roles) + # + if not edit: + submitlabel = "Créer utilisateur" + orig_roles = set() + else: + submitlabel = "Modifier utilisateur" + initvalues["roles"] = initvalues["roles"].split(",") or [] + orig_roles = set(initvalues["roles"]) + if initvalues["status"] == "old": + editable_roles = set() # can't change roles of a disabled user + # add existing user roles + displayed_roles = list(editable_roles.union(orig_roles)) + displayed_roles.sort() + disabled_roles = {} # pour desactiver les roles que l'on ne peut pas editer + for i in range(len(displayed_roles)): + if displayed_roles[i] not in editable_roles: + disabled_roles[i] = True + + # log('create_user_form: displayed_roles=%s' % displayed_roles) + + descr = [ + ("edit", {"input_type": "hidden", "default": edit}), + ("nom", {"title": "Nom", "size": 20, "allow_null": False}), + ("prenom", {"title": "Prénom", "size": 20, "allow_null": False}), + ] + if auth_name != user_name: # no one can't change its own status + descr.append( + ( + "status", + { + "title": "Statut", + "input_type": "radio", + "labels": ("actif", "ancien"), + "allowed_values": ("", "old"), + }, + ) + ) + if not edit: + descr += [ + ( + "user_name", + { + "title": "Pseudo (login)", + "size": 20, + "allow_null": False, + "explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs.", + }, + ), + ( + "passwd", + { + "title": "Mot de passe", + "input_type": "password", + "size": 14, + "allow_null": False, + }, + ), + ( + "passwd2", + { + "title": "Confirmer mot de passe", + "input_type": "password", + "size": 14, + "allow_null": False, + }, + ), + ] + else: + descr += [ + ( + "user_name", + {"input_type": "hidden", "default": initvalues["user_name"]}, + ), + ("user_id", {"input_type": "hidden", "default": initvalues["user_id"]}), + ] + descr += [ + ( + "email", + { + "title": "e-mail", + "input_type": "text", + "explanation": "vivement recommandé: utilisé pour contacter l'utilisateur", + "size": 20, + "allow_null": True, + }, + ) + ] + + if not auth_dept: + # si auth n'a pas de departement (admin global) + # propose de choisir le dept du nouvel utilisateur + # sinon, il sera créé dans le même département que auth + descr.append( + ( + "dept", + { + "title": "Département", + "input_type": "text", + "size": 12, + "allow_null": True, + "explanation": """département d\'appartenance de l\'utilisateur (s'il s'agit d'un administrateur, laisser vide si vous voulez qu'il puisse créer des utilisateurs dans d'autres départements)""", + }, + ) + ) + can_choose_dept = True + else: + can_choose_dept = False + if edit: + descr.append( + ( + "d", + { + "input_type": "separator", + "title": "L'utilisateur appartient au département %s" + % auth_dept, + }, + ) + ) + else: + descr.append( + ( + "d", + { + "input_type": "separator", + "title": "L'utilisateur sera crée dans le département %s" + % auth_dept, + }, + ) + ) + + descr += [ + ( + "date_expiration", + { + "title": "Date d'expiration", # j/m/a + "input_type": "date", + "explanation": "j/m/a, laisser vide si pas de limite", + "size": 9, + "allow_null": True, + }, + ), + ( + "roles", + { + "title": "Rôles", + "input_type": "checkbox", + "vertical": True, + "allowed_values": displayed_roles, + "disabled_items": disabled_roles, + }, + ), + ( + "force", + { + "title": "Ignorer les avertissements", + "input_type": "checkbox", + "explanation": "passer outre les avertissements (homonymes, etc)", + "labels": ("",), + "allowed_values": ("1",), + }, + ), + ] + + if "tf-submitted" in REQUEST.form and not "roles" in REQUEST.form: + REQUEST.form["roles"] = [] + if "tf-submitted" in REQUEST.form: + # Ajoute roles existants mais non modifiables (disabled dans le form) + # orig_roles - editable_roles + REQUEST.form["roles"] = list( + set(REQUEST.form["roles"]).union(orig_roles - editable_roles) + ) + + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + descr, + initvalues=initvalues, + submitlabel=submitlabel, + ) + if tf[0] == 0: + return "\n".join(H) + "\n" + tf[1] + F + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + vals = tf[2] + roles = set(vals["roles"]).intersection(editable_roles) + if REQUEST.form.has_key("edit"): + edit = int(REQUEST.form["edit"]) + else: + edit = 0 + try: + force = int(vals["force"][0]) + except: + force = 0 + + if edit: + user_name = initvalues["user_name"] + else: + user_name = vals["user_name"] + # ce login existe ? + err = None + users = self._user_list(args={"user_name": user_name}) + if edit and not users: # safety net, le user_name ne devrait pas changer + err = "identifiant %s inexistant" % user_name + if not edit and users: + err = "identifiant %s déjà utilisé" % user_name + if err: + H.append(tf_error_message("""Erreur: %s""" % err)) + return "\n".join(H) + "\n" + tf[1] + F + + if not force: + ok, msg = self._check_modif_user( + edit, + user_name=user_name, + nom=vals["nom"], + prenom=vals["prenom"], + email=vals["email"], + roles=vals["roles"], + ) + if not ok: + H.append( + tf_error_message( + """Attention: %s (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page)""" + % msg + ) + ) + + return "\n".join(H) + "\n" + tf[1] + F + + if edit: # modif utilisateur (mais pas passwd) + if (not can_choose_dept) and vals.has_key("dept"): + del vals["dept"] + if vals.has_key("passwd"): + del vals["passwd"] + if vals.has_key("date_modif_passwd"): + del vals["date_modif_passwd"] + if vals.has_key("user_name"): + del vals["user_name"] + if (auth_name == user_name) and vals.has_key("status"): + del vals["status"] # no one can't change its own status + + # traitement des roles: ne doit pas affecter les roles + # que l'on en controle pas: + for role in orig_roles: + if role and not role in editable_roles: + roles.add(role) + + vals["roles"] = ",".join(roles) + + # ok, edit + log("sco_users: editing %s by %s" % (user_name, auth_name)) + # log('sco_users: previous_values=%s' % initvalues) + # log('sco_users: new_values=%s' % vals) + self._user_edit(user_name, vals) + return REQUEST.RESPONSE.redirect( + "userinfo?user_name=%s&head_message=Utilisateur %s modifié" + % (user_name, user_name) + ) + else: # creation utilisateur + vals["roles"] = ",".join(vals["roles"]) + # check identifiant + if not re.match(r"^[a-zA-Z0-9@\\\-_\\\.]+$", vals["user_name"]): + msg = tf_error_message( + "identifiant invalide (pas d'accents ni de caractères spéciaux)" + ) + return "\n".join(H) + msg + "\n" + tf[1] + F + # check passwords + if vals["passwd"] != vals["passwd2"]: + msg = tf_error_message( + """Les deux mots de passes ne correspondent pas !""" + ) + return "\n".join(H) + msg + "\n" + tf[1] + F + if not self._is_valid_passwd(vals["passwd"]): + msg = tf_error_message( + """Mot de passe trop simple, recommencez !""" + ) + return "\n".join(H) + msg + "\n" + tf[1] + F + if not can_choose_dept: + vals["dept"] = auth_dept + # ok, go + log("sco_users: new_user %s by %s" % (vals["user_name"], auth_name)) + self.create_user(vals, REQUEST=REQUEST) + + def _check_modif_user( + self, edit, user_name="", nom="", prenom="", email="", roles=[] + ): + """Vérifie que et utilisateur peut etre crée (edit=0) ou modifié (edit=1) + Cherche homonymes. + returns (ok, msg) + - ok : si vrai, peut continuer avec ces parametres + (si ok est faux, l'utilisateur peut quand même forcer la creation) + - msg: message warning a presenter l'utilisateur + """ + if not user_name or not nom or not prenom: + return False, "champ requis vide" + if not email: + return False, "vous devriez indiquer le mail de l'utilisateur créé !" + # ce login existe ? + users = self._user_list(args={"user_name": user_name}) + if edit and not users: # safety net, le user_name ne devrait pas changer + return False, "identifiant %s inexistant" % user_name + if not edit and users: + return False, "identifiant %s déjà utilisé" % user_name + + # Des noms/prénoms semblables existent ? + cnx = self.GetUsersDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select * from sco_users where lower(nom) ~ %(nom)s and lower(prenom) ~ %(prenom)s;", + {"nom": nom.lower().strip(), "prenom": prenom.lower().strip()}, + ) + res = cursor.dictfetchall() + if edit: + minmatch = 1 + else: + minmatch = 0 + if len(res) > minmatch: + return ( + False, + "des utilisateurs proches existent: " + + ", ".join( + [ + "%s %s (pseudo=%s)" % (x["prenom"], x["nom"], x["user_name"]) + for x in res + ] + ), + ) + # Roles ? + if not roles and not (edit and users[0]["status"] == "old"): + # nb: si utilisateur desactivé (old), pas de role attribué + return False, "aucun rôle sélectionné, êtes vous sûr ?" + # ok + return True, "" + + security.declareProtected(ScoUsersAdmin, "import_users_form") + + def import_users_form(self, REQUEST, user_name=None, edit=0): + """Import utilisateurs depuis feuille Excel""" + head = self.sco_header(REQUEST, page_title="Import utilisateurs") + H = [ + head, + """

    Téléchargement d'une nouvelle liste d'utilisateurs

    +

    A utiliser pour importer de nouveaux utilisateurs (enseignants ou secrétaires) +

    +

    + L'opération se déroule en deux étapes. Dans un premier temps, + vous téléchargez une feuille Excel type. Vous devez remplir + cette feuille, une ligne décrivant chaque utilisateur. Ensuite, + vous indiquez le nom de votre fichier dans la case "Fichier Excel" + ci-dessous, et cliquez sur "Télécharger" pour envoyer au serveur + votre liste. +

    + """, + ] + help = """

    + Lors de la creation des utilisateurs, les opérations suivantes sont effectuées: +

    +
      +
    1. vérification des données;
    2. +
    3. génération d'un mot de passe alétoire pour chaque utilisateur;
    4. +
    5. création de chaque utilisateur;
    6. +
    7. envoi Ă  chaque utilisateur de son mot de passe initial par mail.
    8. +
    """ + H.append( + """
    1. + Obtenir la feuille excel Ă  remplir
    2. """ + ) + F = self.sco_footer(REQUEST) + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ( + "xlsfile", + {"title": "Fichier Excel:", "input_type": "file", "size": 40}, + ), + ("formsemestre_id", {"input_type": "hidden"}), + ), + submitlabel="Télécharger", + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + "
    " + help + F + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + # IMPORT + diag = sco_import_users.import_excel_file( + tf[2]["xlsfile"], REQUEST=REQUEST, context=self + ) + H = [head] + H.append("

    Import excel: %s

    " % diag) + H.append("

    OK, import terminé !

    ") + H.append('

    Continuer

    ' % REQUEST.URL1) + return "\n".join(H) + help + F + + security.declareProtected(ScoUsersAdmin, "import_users_generate_excel_sample") + + def import_users_generate_excel_sample(self, REQUEST): + "une feuille excel pour importation utilisateurs" + data = sco_import_users.generate_excel_sample() + return sco_excel.sendExcelFile(REQUEST, data, "ImportUtilisateurs.xls") + + security.declareProtected(ScoUsersAdmin, "create_user") + + def create_user(self, args, REQUEST=None): + "creation utilisateur zope" + cnx = self.GetUsersDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + passwd = args["passwd"] + args["passwd"] = "undefined" + if "passwd2" in args: + del args["passwd2"] + log("create_user: args=%s" % args) # log apres supr. du mot de passe ! + r = self._userEditor.create(cnx, args) + self.get_userlist_cache().inval_cache() # > + + # call exUserFolder to set passwd + roles = args["roles"].split(",") + self.acl_users.scodoc_editUser( + cursor, args["user_name"], password=passwd, roles=roles + ) + + if REQUEST: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + + security.declareProtected(ScoUsersAdmin, "delete_user_form") + + def delete_user_form(self, REQUEST, user_name, dialog_confirmed=False): + "delete user" + authuser = REQUEST.AUTHENTICATED_USER + if not self._can_handle_passwd(authuser, user_name): + return ( + self.sco_header(REQUEST, user_check=False) + + "

    Vous n'avez pas la permission de supprimer cet utilisateur

    " + + self.sco_footer(REQUEST) + ) + + r = self._user_list(args={"user_name": user_name}) + if len(r) != 1: + return ScoValueError("utilisateur %s inexistant" % user_name) + if not dialog_confirmed: + return self.confirmDialog( + """

    Confirmer la suppression de l\'utilisateur %s ?

    +

    En général, il est déconseillé de supprimer un utilisateur, son + identité étant référencé dans les modules de formation. N'utilisez + cette fonction qu'en cas d'erreur (création de doublons, etc). +

    + """ + % user_name, + dest_url="", + REQUEST=REQUEST, + cancel_url=REQUEST.URL1, + parameters={"user_name": user_name}, + ) + self._user_delete(user_name) + REQUEST.RESPONSE.redirect(REQUEST.URL1) + + def list_users( + self, + dept, + all=False, # tous les departements + with_olds=False, # inclue les anciens utilisateurs (status "old") + format="html", + with_links=True, + REQUEST=None, + ): + "List users" + authuser = REQUEST.AUTHENTICATED_USER + if dept and not all: + r = self.get_userlist(dept=dept, with_olds=with_olds) + comm = "dept. %s" % dept + else: + r = self.get_userlist(with_olds=with_olds) + comm = "tous" + if with_olds: + comm += ", avec anciens" + comm = "(" + comm + ")" + # -- Add some information and links: + for u in r: + # Can current user modify this user ? + can_modify = self._can_handle_passwd( + authuser, u["user_name"], allow_admindepts=True + ) + + # Add links + if with_links and can_modify: + target = "userinfo?user_name=%(user_name)s" % u + u["_user_name_target"] = target + u["_nom_target"] = target + u["_prenom_target"] = target + + # Hide passwd modification date (depending on rights wrt user) + if not can_modify: + u["date_modif_passwd"] = "(non visible)" + + # Add spaces between roles to ease line wrap + if u["roles"]: + u["roles"] = ", ".join(u["roles"].split(",")) + + # Convert dates to ISO if XML output + if format == "xml" and u["date_modif_passwd"] != "NA": + u["date_modif_passwd"] = DateDMYtoISO(u["date_modif_passwd"]) or "" + + # Convert date_expiration and date_modif_passwd to ISO to ease sorting + if u["date_expiration"]: + u["date_expiration_iso"] = DateDMYtoISO(u["date_expiration"]) + else: + u["date_expiration_iso"] = "" + if u["date_modif_passwd"]: + u["date_modif_passwd_iso"] = DateDMYtoISO(u["date_expiration"]) + else: + u["date_modif_passwd_iso"] = "" + + title = "Utilisateurs définis dans ScoDoc" + tab = GenTable( + rows=r, + columns_ids=( + "user_name", + "nom_fmt", + "prenom_fmt", + "email", + "dept", + "roles", + "date_expiration_iso", + "date_modif_passwd_iso", + "passwd_temp", + "status_txt", + ), + titles={ + "user_name": "Login", + "nom_fmt": "Nom", + "prenom_fmt": "Prénom", + "email": "Mail", + "dept": "Dept.", + "roles": "Rôles", + "date_expiration_iso": "Expiration", + "date_modif_passwd_iso": "Modif. mot de passe", + "passwd_temp": "Temp.", + "status_txt": "Etat", + }, + caption=title, + page_title="title", + html_title="""

    %d utilisateurs %s

    +

    Cliquer sur un nom pour changer son mot de passe

    """ + % (len(r), comm), + html_class="table_leftalign list_users", + html_with_td_classes=True, + html_sortable=True, + base_url="%s?all=%s" % (REQUEST.URL0, all), + pdf_link=False, # table is too wide to fit in a paper page => disable pdf + preferences=self.get_preferences(), + ) + + return tab.make_page( + self, format=format, with_html_headers=False, REQUEST=REQUEST + ) + + def get_userlist_cache(self): + url = self.ScoURL() + if CACHE_userlist.has_key(url): + return CACHE_userlist[url] + else: + log("get_userlist_cache: new simpleCache") + CACHE_userlist[url] = sco_cache.expiringCache(max_validity=60) + return CACHE_userlist[url] + + security.declareProtected(ScoView, "get_userlist") + + def get_userlist(self, dept=None, with_olds=False): + """Returns list of users. + If dept, select users from this dept, + else return all users. + """ + # on ne cache que la liste sans les "olds" + if with_olds: + r = None + else: + cache = self.get_userlist_cache() + r = cache.get(dept) + + if r != None: + return r + else: + args = {} + if not with_olds: + args["status"] = None + if dept != None: + args["dept"] = dept + + r = self._user_list(args=args) + + l = [self.user_info(user=user) for user in r] + + if not with_olds: + cache.set(dept, l) + return l + + security.declareProtected(ScoView, "get_userlist_xml") + + def get_userlist_xml(self, dept=None, start="", limit=25, REQUEST=None): + """Returns XML list of users with name (nomplogin) starting with start. + Used for forms auto-completion.""" + userlist = self.get_userlist(dept=dept) + start = suppression_diacritics(unicode(start, "utf-8")) + start = strlower(str(start)) + + userlist = [user for user in userlist if user["nomnoacc"].startswith(start)] + if REQUEST: + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + doc = jaxml.XML_document(encoding=SCO_ENCODING) + doc.results() + for user in userlist[:limit]: + doc._push() + doc.rs(user["nomplogin"], id=user["user_id"], info="") + doc._pop() + return repr(doc) + + security.declareProtected(ScoView, "get_user_name_from_nomplogin") + + def get_user_name_from_nomplogin(self, nomplogin): + """Returns user_name (login) from nomplogin + """ + m = re.match(r".*\((.*)\)", nomplogin.strip()) + if m: + return m.group(1) + else: + return None + + security.declareProtected(ScoView, "reset_password_form") + + def reset_password_form(self, user_name=None, dialog_confirmed=False, REQUEST=None): + """Form to reset a password""" + if not dialog_confirmed: + return self.confirmDialog( + """

    RĂ©-initialiser le mot de passe de %s ?

    +

    Le mot de passe de %s va être choisi au hasard et lui être envoyé par mail. +Il devra ensuite se connecter et le changer. +

    + """ + % (user_name, user_name), + parameters={"user_name": user_name}, + REQUEST=REQUEST, + ) + self.reset_password(user_name=user_name, REQUEST=REQUEST) + return REQUEST.RESPONSE.redirect( + REQUEST.URL1 + + "?head_message=mot%20de%20passe%20de%20" + + user_name + + "%20reinitialise" + ) + + security.declareProtected(ScoView, "reset_password") + + def reset_password(self, user_name=None, REQUEST=None): + """Reset a password: + - set user's passwd_temp to 1 + - set roles to 'ScoReset' + - generate a random password and mail it + """ + authuser = REQUEST.AUTHENTICATED_USER + auth_name = str(authuser) + if not user_name: + user_name = auth_name + # Access control + if not self._can_handle_passwd(authuser, user_name): + raise AccessDenied( + "vous n'avez pas la permission de changer ce mot de passe" + ) + log("reset_password: %s" % user_name) + # Check that user has valid mail + info = self.user_info(user_name=user_name) + if not is_valid_mail(info["email"]): + raise Exception("pas de mail valide associé à l'utilisateur") + # Generate random password + password = sco_import_users.generate_password() + self.do_change_password(user_name, password) + # Flag it as temporary: + cnx = self.GetUsersDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + ui = {"user_name": user_name} + cursor.execute( + "update sco_users set passwd_temp=1 where user_name='%(user_name)s'" % ui + ) + # Send email + info["passwd"] = password + sco_import_users.mail_password(info, context=self, reset=True) + + +# -------------------------------------------------------------------- +# +# Zope Product Administration +# +# -------------------------------------------------------------------- +def manage_addZScoUsers( + self, id="id_ZScousers", title="The Title for ZScoUsers Object", REQUEST=None +): + "Add a ZScoUsers instance to a folder." + self._setObject(id, ZScoUsers(id, title)) + if REQUEST is not None: + return self.manage_main(self, REQUEST) diff --git a/ZScolar.py b/ZScolar.py new file mode 100644 index 00000000..209a6094 --- /dev/null +++ b/ZScolar.py @@ -0,0 +1,2830 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 Scolarite pour département IUT +""" + +import sys +import traceback +import time, string, glob, re +import urllib, urllib2, cgi, xml + +try: + from cStringIO import StringIO +except: + from StringIO import StringIO +from zipfile import ZipFile +import thread +import psycopg2 + +from sco_zope import * + + +# --------------- +from notes_log import log + +log.set_log_directory(INSTANCE_HOME + "/log") +log("restarting...") + +log("ZScolar home=%s" % file_path) + + +from sco_utils import * +import notesdb +from notesdb import * +from scolog import logdb + +import scolars +import sco_preferences +import sco_formations +from scolars import ( + format_nom, + format_prenom, + format_sexe, + format_lycee, + format_lycee_from_code, +) +from scolars import format_telephone, format_pays, make_etud_args +import sco_find_etud +import sco_photos +import sco_formsemestre +import sco_formsemestre_edit + +import sco_news +from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC + +import html_sco_header, html_sidebar + +from gen_tables import GenTable +import sco_excel +import imageresize + +import ZNotes +import ZAbsences +import ZEntreprises +import ZScoUsers +import sco_modalites +import ImportScolars +import sco_portal_apogee +import sco_synchro_etuds +import sco_page_etud +import sco_groups +import sco_trombino +import sco_groups_view +import sco_trombino_tours +import sco_parcours_dut +import sco_report +import sco_archives_etud +import sco_debouche +import sco_groups_edit +import sco_up_to_date +import sco_edt_cal +import sco_dept +import sco_dump_db + +from VERSION import SCOVERSION, SCONEWS + +if CONFIG.ABSOLUTE_URL: + log("ScoDoc: ABSOLUTE_URL='%s'" % CONFIG.ABSOLUTE_URL) +log("ScoDoc: using encoding %s" % SCO_ENCODING) + +# import essai_cas + +# --------------- + + +class ZScolar(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit): + + "ZScolar object" + + meta_type = "ZScolar" + 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 + + ( + # this line is kept as an example with the files : + # dtml/manage_editZScolarForm.dtml + # html/ZScolar-edit.stx + # {'label': 'Properties', 'action': 'manage_editForm',}, + {"label": "View", "action": "index_html"}, + ) + + Item.manage_options # add the 'Undo' & 'Owner' tab + + RoleManager.manage_options # add the 'Security' tab + ) + + # no permissions, only called from python + def __init__(self, id, title, db_cnx_string=None): + "initialise a new instance of ZScolar" + log("*** creating ZScolar instance") + self.id = id + self.title = title + self._db_cnx_string = db_cnx_string + self._cnx = None + # --- add editable DTML documents: + # self.defaultDocFile('sidebar_dept', + # 'barre gauche (partie haute)', + # 'sidebar_dept') + + # --- add DB connector + # id = 'DB' + # da = ZopeDA.Connection( + # id, 'DB connector', db_cnx_string, False, + # check=1, tilevel=2, encoding='utf-8') + # self._setObject(id, da) + # --- add Scousers instance + id = "Users" + obj = ZScoUsers.ZScoUsers(id, "Gestion utilisateurs zope") + self._setObject(id, obj) + # --- add Notes instance + id = "Notes" + obj = ZNotes.ZNotes(id, "Gestion Notes") + self._setObject(id, obj) + # --- add Absences instance + id = "Absences" + obj = ZAbsences.ZAbsences(id, "Gestion absences") + self._setObject(id, obj) + # --- add Entreprises instance + id = "Entreprises" + obj = ZEntreprises.ZEntreprises(id, "Suivi entreprises") + self._setObject(id, obj) + + # + self.manage_addProperty("roles_initialized", "0", "string") + + # The for used to edit this object + def manage_editZScolar(self, title, RESPONSE=None): + "Changes the instance values" + self.title = title + self._p_changed = 1 + RESPONSE.redirect("manage_editForm") + + def _setup_initial_roles_and_permissions(self): + """Initialize roles and permissions + create 3 roles: EnsXXX, SecrXXX, AdminXXX + and set default permissions for each one. + """ + DeptId = self.DeptId() + log("initializing roles and permissions for %s" % DeptId) + H = [] + ok = True + DeptRoles = self.DeptUsersRoles() + + container = self.aq_parent # creates roles and permissions in parent folder + valid_roles = set(container.valid_roles()) + for role_name in DeptRoles: + if role_name not in valid_roles: + r = container._addRole(role_name) + if r: # error + H.append(r) + ok = False + + for permission in Sco_Default_Permissions.keys(): + roles = [r + DeptId for r in Sco_Default_Permissions[permission]] + roles.append("Manager") + log("granting '%s' to %s" % (permission, roles)) + try: + r = container.manage_permission(permission, roles=roles, acquire=0) + if r: + H.append(r) + ok = False + except ValueError: + log(traceback.format_exc()) + log("failed, ignoring.") + # set property indicating that we did the job: + self.manage_changeProperties(roles_initialized="1") + + return ok, "\n".join(H) + + security.declareProtected(ScoView, "DeptId") + + def DeptId(self): + """Returns Id for this department + (retreived as the name of the parent folder) + (c'est normalement l'id donne à create_dept.sh) + NB: la preference DeptName est au depart la même chose de cet id + mais elle peut être modifiée (préférences). + """ + return self.aq_parent.id + + def DeptUsersRoles(self): # not published + # Donne les rôles utilisés dans ce departement. + DeptId = self.DeptId() + DeptRoles = [] + for role_type in ("Ens", "Secr", "Admin", "RespPe"): + role_name = role_type + DeptId + DeptRoles.append(role_name) + return DeptRoles + + security.declareProtected(ScoView, "essai") + + def essai(self, x="", REQUEST=None): + """essai: header / body / footer""" + return """ + + + +Programme DUT R&T + + + + + + + + + + + + + +

    +UE11 Découverte métiers (code UCOD46, 16 ECTS, Apo VRTU11) +[verrouillé] +

    + + """ + return ( + self.sco_header(REQUEST) + + """
    %s
    """ % x + + self.sco_footer(REQUEST) + ) + b = "

    Hello, World !


    " + raise ValueError("essai exception") + # raise ScoValueError('essai exception !', dest_url='totoro', REQUEST=REQUEST) + + # cursor = cnx.cursor(cursor_factory=ScoDocCursor) + # cursor.execute("select * from notes_formations") + # b += str(cursor.fetchall()) + # b = self.Notes.gloups() + # raise NoteProcessError('test exception !') + + # essai: liste des permissions + from AccessControl import getSecurityManager + from AccessControl.Permission import Permission + + permissions = self.ac_inherited_permissions(1) + scoperms = [p for p in permissions if p[0][:3] == "Sco"] + # H.append( str(self.aq_parent.aq_parent.permission_settings()) ) + # H.append('

    perms: %s

    '%str(scoperms)) + # H.append('

    valid_roles: %s

    '%str(self.valid_roles())) + # H.append('

    ac_inherited_permissions=%s

    '%str(self.ac_inherited_permissions(1))) + def collect_roles(context, rd): + for p in scoperms: + name, value = p[:2] + P = Permission(name, value, context) + roles = list(P.getRoles()) + if rd.has_key(name): + rd[name] += roles + else: + rd[name] = roles + if hasattr(context, "aq_parent"): + collect_roles(context.aq_parent, rd) + + b = "" + rd = {} + collect_roles(self, rd) + b = "

    " + str(rd) + "

    " + + authuser = REQUEST.AUTHENTICATED_USER + for p in scoperms: + permname, value = p[:2] + b += "

    " + permname + " : " + if authuser.has_permission(permname, self): + b += "yes" + else: + b += "no" + b += "

    " + b += "

    xxx


    " + str(self.aq_parent.aq_parent) + + return self.sco_header(REQUEST) + str(b) + self.sco_footer(REQUEST) + + # essais calendriers: + security.declareProtected(ScoView, "experimental_calendar") + experimental_calendar = sco_edt_cal.experimental_calendar + security.declareProtected(ScoView, "group_edt_json") + group_edt_json = sco_edt_cal.group_edt_json + + security.declareProtected(ScoView, "ScoURL") + + def ScoURL(self): + "base URL for this sco instance" + # absolute_url is the classic Zope method + # The avoid the burden of configuring a proxy zope object, we offer + # a custom configuration via scodoc_config + return CONFIG.ABSOLUTE_URL or self.absolute_url() + + security.declareProtected(ScoView, "sco_header") + sco_header = html_sco_header.sco_header + security.declareProtected(ScoView, "sco_footer") + sco_footer = html_sco_header.sco_footer + + # -------------------------------------------------------------------- + # + # GESTION DE LA BD + # + # -------------------------------------------------------------------- + security.declareProtected(ScoSuperAdmin, "GetDBConnexionString") + + def GetDBConnexionString(self): + # should not be published (but used from contained classes via acquisition) + return self._db_cnx_string + + security.declareProtected(ScoSuperAdmin, "GetDBConnexion") + GetDBConnexion = notesdb.GetDBConnexion + + # A enlever après re-ecriture de ZEntreprises.py + security.declareProtected(ScoView, "TrivialFormulator") + + def TrivialFormulator( + self, + form_url, + values, + formdescription=(), + initvalues={}, + method="POST", + submitlabel="OK", + formid="tf", + cancelbutton=None, + readonly=False, + ): + "generator/validator of simple forms" + # obsolete, still used by dtml/entreprises old code... + return TrivialFormulator( + form_url, + values, + formdescription=formdescription, + initvalues=initvalues, + method=method, + submitlabel=submitlabel, + formid=formid, + cancelbutton=cancelbutton, + readonly=readonly, + ) + + # -------------------------------------------------------------------- + # + # SCOLARITE (top level) + # + # -------------------------------------------------------------------- + + security.declareProtected(ScoView, "about") + + def about(self, REQUEST): + "version info" + H = [ + """

    Système de gestion scolarité

    +

    © Emmanuel Viennet 1997-2020

    +

    Version %s (subversion %s)

    + """ + % (SCOVERSION, get_svn_version(file_path)) + ] + H.append( + '

    Logiciel libre Ă©crit en Python.

    Utilise ReportLab pour générer les documents PDF, et pyExcelerator pour le traitement des documents Excel.

    ' + ) + H.append("

    Dernières évolutions

    " + SCONEWS) + H.append( + '" + ) + d = "" + return self.sco_header(REQUEST) + "\n".join(H) + d + self.sco_footer(REQUEST) + + security.declareProtected(ScoView, "ScoErrorResponse") + + def ScoErrorResponse(self, msg, format="html", REQUEST=None): + """Send an error message to the client, in html or xml format. + """ + REQUEST.RESPONSE.setStatus(404, reason=msg) + if format == "html" or format == "pdf": + raise ScoValueError(msg) + elif format == "xml": + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + doc = jaxml.XML_document(encoding=SCO_ENCODING) + doc.error(msg=msg) + return repr(doc) + elif format == "json": + REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE) + return "undefined" # XXX voir quoi faire en cas d'erreur json + else: + raise ValueError("ScoErrorResponse: invalid format") + + # XXX essai XXX + # security.declareProtected(ScoView, 'essai_cas') + # essai_cas = essai_cas.essai_cas + + # -------------------------------------------------------------------- + # + # PREFERENCES + # + # -------------------------------------------------------------------- + security.declareProtected(ScoView, "get_preferences") + + def get_preferences(self, formsemestre_id=None): + "Get preferences for this instance (a dict-like instance)" + return sco_preferences.sem_preferences(self, formsemestre_id) + + security.declareProtected(ScoView, "get_preference") + + def get_preference(self, name, formsemestre_id=None): + """Returns value of named preference. + All preferences have a sensible default value (see sco_preferences.py), + this function always returns a usable value for all defined preferences names. + """ + return sco_preferences.get_base_preferences(self).get(formsemestre_id, name) + + security.declareProtected(ScoChangePreferences, "edit_preferences") + + def edit_preferences(self, REQUEST): + """Edit global preferences""" + return sco_preferences.get_base_preferences(self).edit(REQUEST=REQUEST) + + security.declareProtected(ScoView, "formsemestre_edit_preferences") + + def formsemestre_edit_preferences(self, formsemestre_id, REQUEST): + """Edit preferences for a semestre""" + authuser = REQUEST.AUTHENTICATED_USER + sem = sco_formsemestre.get_formsemestre(self.Notes, formsemestre_id) + ok = ( + authuser.has_permission(ScoImplement, self) + or ((str(authuser) in sem["responsables"]) and sem["resp_can_edit"]) + ) and (sem["etat"] == "1") + if ok: + return self.get_preferences(formsemestre_id=formsemestre_id).edit( + REQUEST=REQUEST + ) + else: + raise AccessDenied("Modification impossible pour %s" % authuser) + + security.declareProtected(ScoView, "doc_preferences") + + def doc_preferences(self, REQUEST): + """List preferences for wiki documentation""" + REQUEST.RESPONSE.setHeader("content-type", "text/plain" ) + return sco_preferences.doc_preferences(self) + + # -------------------------------------------------------------------- + # + # ETUDIANTS + # + # -------------------------------------------------------------------- + + # ----------------- BANDEAUX ------------------- + security.declareProtected(ScoView, "sidebar") + sidebar = html_sidebar.sidebar + security.declareProtected(ScoView, "sidebar_dept") + sidebar_dept = html_sidebar.sidebar_dept + + security.declareProtected(ScoView, "showEtudLog") + + def showEtudLog(self, etudid, format="html", REQUEST=None): + """Display log of operations on this student""" + etud = self.getEtudInfo(filled=1, REQUEST=REQUEST)[0] + + ops = self.listScoLog(etudid) + + tab = GenTable( + titles={ + "date": "Date", + "authenticated_user": "Utilisateur", + "remote_addr": "IP", + "method": "Opération", + "msg": "Message", + }, + columns_ids=("date", "authenticated_user", "remote_addr", "method", "msg"), + rows=ops, + html_sortable=True, + html_class="table_leftalign", + base_url="%s?etudid=%s" % (REQUEST.URL0, etudid), + page_title="Opérations sur %(nomprenom)s" % etud, + html_title="

    Opérations effectuées sur l'étudiant %(nomprenom)s

    " + % etud, + filename="log_" + make_filename(etud["nomprenom"]), + html_next_section='' + % etud, + preferences=self.get_preferences(), + ) + + return tab.make_page(self, format=format, REQUEST=REQUEST) + + def listScoLog(self, etudid): + "liste des operations effectuees sur cet etudiant" + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select * from scolog where etudid=%(etudid)s ORDER BY DATE DESC", + {"etudid": etudid}, + ) + return cursor.dictfetchall() + + # ---------- PAGE ACCUEIL (listes) -------------- + security.declareProtected(ScoView, "index_html") + index_html = sco_dept.index_html + + security.declareProtected(ScoView, "rssnews") + + def rssnews(self, REQUEST=None): + "rss feed" + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + return sco_news.scolar_news_summary_rss( + self, "Nouvelles de " + self.get_preference("DeptName"), self.ScoURL() + ) + + # genere liste html pour accès aux groupes de ce semestre + def make_listes_sem(self, sem, REQUEST=None, with_absences=True): + context = self + authuser = REQUEST.AUTHENTICATED_USER + r = self.ScoURL() # root url + # construit l'URL "destination" + # (a laquelle on revient apres saisie absences) + query_args = cgi.parse_qs(REQUEST.QUERY_STRING) + if "head_message" in query_args: + del query_args["head_message"] + destination = "%s?%s" % (REQUEST.URL, urllib.urlencode(query_args, True)) + destination = destination.replace( + "%", "%%" + ) # car ici utilisee dans un format string ! + + # + H = [] + # pas de menu absences si pas autorise: + if with_absences and not authuser.has_permission(ScoAbsChange, self): + with_absences = False + + # + H.append( + '

    Listes de %(titre)s (%(mois_debut)s - %(mois_fin)s)

    ' + % sem + ) + + formsemestre_id = sem["formsemestre_id"] + + # calcule dates 1er jour semaine pour absences + try: + if with_absences: + first_monday = ZAbsences.ddmmyyyy(sem["date_debut"]).prev_monday() + FA = [] # formulaire avec menu saisi absences + FA.append( + '' + ) + FA.append( + '' % sem + ) + FA.append( + '' + ) + + FA.append( + '' % destination + ) + FA.append('') + FA.append('") + FA.append( + 'Ă©tat' + % sem + ) + FA.append("
    ") + FormAbs = "\n".join(FA) + else: + FormAbs = "" + except ScoInvalidDateError: # dates incorrectes dans semestres ? + FormAbs = "" + # + H.append('
    ') + # Genere liste pour chaque partition (categorie de groupes) + for partition in sco_groups.get_partitions_list( + context, sem["formsemestre_id"] + ): + if not partition["partition_name"]: + H.append("

    Tous les Ă©tudiants

    " % partition) + else: + H.append("

    Groupes de %(partition_name)s

    " % partition) + groups = sco_groups.get_partition_groups(context, partition) + if groups: + H.append("") + for group in groups: + n_members = len( + sco_groups.get_group_members(context, group["group_id"]) + ) + group["url"] = r + if group["group_name"]: + group["label"] = "groupe %(group_name)s" % group + else: + group["label"] = "liste" + H.append('') + H.append( + """""" + % group + ) + H.append("" % n_members) + + if with_absences: + H.append(FormAbs % group) + + H.append("") + H.append("
    ") + else: + H.append('

    Aucun groupe dans cette partition') + if self.Notes.can_change_groups(REQUEST, formsemestre_id): + H.append( + ' (créer)' + % partition["partition_id"] + ) + H.append("

    ") + if self.Notes.can_change_groups(REQUEST, formsemestre_id): + H.append( + '

    Ajouter une partition

    ' + % formsemestre_id + ) + + H.append("
    ") + return "\n".join(H) + + security.declareProtected(ScoView, "trombino") + trombino = sco_trombino.trombino + + security.declareProtected(ScoView, "pdf_trombino_tours") + pdf_trombino_tours = sco_trombino_tours.pdf_trombino_tours + + security.declareProtected(ScoView, "pdf_feuille_releve_absences") + pdf_feuille_releve_absences = sco_trombino_tours.pdf_feuille_releve_absences + + security.declareProtected(ScoView, "trombino_copy_photos") + trombino_copy_photos = sco_trombino.trombino_copy_photos + + security.declareProtected(ScoView, "groups_view") + groups_view = sco_groups_view.groups_view + + security.declareProtected(ScoView, "export_groups_as_moodle_csv") + export_groups_as_moodle_csv = sco_groups_view.export_groups_as_moodle_csv + + security.declareProtected(ScoView, "getEtudInfoGroupes") + + def getEtudInfoGroupes(self, group_ids, etat=None): + """liste triée d'infos (dict) sur les etudiants du groupe indiqué. + Attention: lent, car plusieurs requetes SQL par etudiant ! + """ + etuds = [] + for group_id in group_ids: + members = sco_groups.get_group_members(self, group_id, etat=etat) + for m in members: + etud = self.getEtudInfo(etudid=m["etudid"], filled=True)[0] + etuds.append(etud) + + return etuds + + # -------------------------- INFOS SUR ETUDIANTS -------------------------- + security.declareProtected(ScoView, "getEtudInfo") + + def getEtudInfo(self, etudid=False, code_nip=False, filled=False, REQUEST=None): + """infos sur un etudiant pour utilisation en Zope DTML + On peut specifier etudid + ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine + (dans cet ordre). + """ + if etudid is None: + return [] + cnx = self.GetDBConnexion() + args = make_etud_args(etudid=etudid, code_nip=code_nip, REQUEST=REQUEST) + etud = scolars.etudident_list(cnx, args=args) + if filled: + self.fillEtudsInfo(etud) + return etud + + security.declareProtected(ScoView, "search_etud_in_dept") + search_etud_in_dept = sco_find_etud.search_etud_in_dept + + security.declareProtected(ScoView, "search_etud_by_name") + search_etud_by_name = sco_find_etud.search_etud_by_name + + security.declareProtected(ScoView, "fillEtudsInfo") + + def fillEtudsInfo(self, etuds): + """etuds est une liste d'etudiants (mappings) + Pour chaque etudiant, ajoute ou formatte les champs + -> informations pour fiche etudiant ou listes diverses + """ + cnx = self.GetDBConnexion() + # open('/tmp/t','w').write( str(etuds) ) + for etud in etuds: + etudid = etud["etudid"] + etud["dept"] = self.DeptId() + adrs = scolars.adresse_list(cnx, {"etudid": etudid}) + if not adrs: + # certains "vieux" etudiants n'ont pas d'adresse + adr = {}.fromkeys(scolars._adresseEditor.dbfields, "") + adr["etudid"] = etudid + else: + adr = adrs[0] + if len(adrs) > 1: + log("fillEtudsInfo: etudid=%s a %d adresses" % (etudid, len(adrs))) + etud.update(adr) + scolars.format_etud_ident(etud) + + # Semestres dans lesquel il est inscrit + ins = self.Notes.do_formsemestre_inscription_list({"etudid": etudid}) + etud["ins"] = ins + now = time.strftime("%Y-%m-%d") + sems = [] + cursem = None # semestre "courant" ou il est inscrit + for i in ins: + sem = sco_formsemestre.get_formsemestre(self, i["formsemestre_id"]) + debut = DateDMYtoISO(sem["date_debut"]) + fin = DateDMYtoISO(sem["date_fin"]) + if debut <= now and now <= fin: + cursem = sem + curi = i + sem["ins"] = i + sems.append(sem) + # trie les semestres par date de debut, le plus recent d'abord + # (important, ne pas changer (suivi cohortes)) + sems.sort(lambda x, y: cmp(y["dateord"], x["dateord"])) + etud["sems"] = sems + etud["cursem"] = cursem + if cursem: + etud["inscription"] = cursem["titremois"] + etud["inscriptionstr"] = "Inscrit en " + cursem["titremois"] + etud["inscription_formsemestre_id"] = cursem["formsemestre_id"] + etud["etatincursem"] = curi["etat"] + etud["situation"] = self._descr_situation_etud(etudid, etud["ne"]) + # XXX est-ce utile ? sco_groups.etud_add_group_infos(self, etud, cursem) + else: + if etud["sems"]: + if etud["sems"][0]["dateord"] > time.strftime( + "%Y-%m-%d", time.localtime() + ): + etud["inscription"] = "futur" + etud["situation"] = "futur élève" + else: + etud["inscription"] = "ancien" + etud["situation"] = "ancien élève" + else: + etud["inscription"] = "non inscrit" + etud["situation"] = etud["inscription"] + etud["inscriptionstr"] = etud["inscription"] + etud["inscription_formsemestre_id"] = None + # XXXetud['partitions'] = {} # ne va pas chercher les groupes des anciens semestres + etud["etatincursem"] = "?" + + # nettoyage champs souvents vides + if etud["nomlycee"]: + etud["ilycee"] = "Lycée " + format_lycee(etud["nomlycee"]) + if etud["villelycee"]: + etud["ilycee"] += " (%s)" % etud["villelycee"] + etud["ilycee"] += "
    " + else: + if etud["codelycee"]: + etud["ilycee"] = format_lycee_from_code(etud["codelycee"]) + else: + etud["ilycee"] = "" + rap = "" + if etud["rapporteur"] or etud["commentaire"]: + rap = "Note du rapporteur" + if etud["rapporteur"]: + rap += " (%s)" % etud["rapporteur"] + rap += ": " + if etud["commentaire"]: + rap += "%s" % etud["commentaire"] + etud["rap"] = rap + + # if etud['boursier_prec']: + # pass + + if etud["telephone"]: + etud["telephonestr"] = "Tél.: " + format_telephone( + etud["telephone"] + ) + else: + etud["telephonestr"] = "" + if etud["telephonemobile"]: + etud["telephonemobilestr"] = "Mobile: " + format_telephone( + etud["telephonemobile"] + ) + else: + etud["telephonemobilestr"] = "" + etud["debouche"] = etud["debouche"] or "" + + security.declareProtected(ScoView, "etud_info") + + def etud_info(self, etudid=None, format="xml", REQUEST=None): + "Donne les informations sur un etudiant" + t0 = time.time() + args = make_etud_args(etudid=etudid, REQUEST=REQUEST) + cnx = self.GetDBConnexion() + etuds = scolars.etudident_list(cnx, args) + if not etuds: + # etudiant non trouvé: message d'erreur + d = { + "etudid": etudid, + "nom": "?", + "nom_usuel": "", + "prenom": "?", + "sexe": "?", + "email": "?", + "emailperso": "", + "error": "code etudiant inconnu", + } + return sendResult( + REQUEST, d, name="etudiant", format=format, force_outer_xml_tag=False + ) + d = {} + etud = etuds[0] + self.fillEtudsInfo([etud]) + etud["date_naissance_iso"] = DateDMYtoISO(etud["date_naissance"]) + for a in ( + "etudid", + "code_nip", + "code_ine", + "nom", + "nom_usuel", + "prenom", + "sexe", + "nomprenom", + "email", + "emailperso", + "domicile", + "codepostaldomicile", + "villedomicile", + "paysdomicile", + "telephone", + "telephonemobile", + "fax", + "bac", + "specialite", + "annee_bac", + "nomlycee", + "villelycee", + "codepostallycee", + "codelycee", + "date_naissance_iso", + ): + d[a] = quote_xml_attr(etud[a]) + d["photo_url"] = quote_xml_attr(sco_photos.etud_photo_url(self, etud)) + + sem = etud["cursem"] + if sem: + sco_groups.etud_add_group_infos(self, etud, sem) + d["insemestre"] = [ + { + "current": "1", + "formsemestre_id": sem["formsemestre_id"], + "date_debut": DateDMYtoISO(sem["date_debut"]), + "date_fin": DateDMYtoISO(sem["date_fin"]), + "etat": quote_xml_attr(sem["ins"]["etat"]), + "groupes": quote_xml_attr( + etud["groupes"] + ), # slt pour semestre courant + } + ] + else: + d["insemestre"] = [] + for sem in etud["sems"]: + if sem != etud["cursem"]: + d["insemestre"].append( + { + "formsemestre_id": sem["formsemestre_id"], + "date_debut": DateDMYtoISO(sem["date_debut"]), + "date_fin": DateDMYtoISO(sem["date_fin"]), + "etat": quote_xml_attr(sem["ins"]["etat"]), + } + ) + + log("etud_info (%gs)" % (time.time() - t0)) + return sendResult( + REQUEST, d, name="etudiant", format=format, force_outer_xml_tag=False + ) + + security.declareProtected(ScoView, "XMLgetEtudInfos") + XMLgetEtudInfos = etud_info # old name, deprecated + + def isPrimoEtud(self, etud, sem): + """Determine si un (filled) etud a ete inscrit avant ce semestre. + Regarde la liste des semestres dans lesquels l'étudiant est inscrit + """ + now = sem["dateord"] + for s in etud["sems"]: # le + recent d'abord + if s["dateord"] < now: + return False + return True + + # -------------------------- FICHE ETUDIANT -------------------------- + security.declareProtected(ScoView, "ficheEtud") + ficheEtud = sco_page_etud.ficheEtud + + security.declareProtected(ScoView, "etud_upload_file_form") + etud_upload_file_form = sco_archives_etud.etud_upload_file_form + + security.declareProtected(ScoView, "etud_delete_archive") + etud_delete_archive = sco_archives_etud.etud_delete_archive + + security.declareProtected(ScoView, "etud_get_archived_file") + etud_get_archived_file = sco_archives_etud.etud_get_archived_file + + security.declareProtected(ScoView, "etudarchive_import_files_form") + etudarchive_import_files_form = sco_archives_etud.etudarchive_import_files_form + + security.declareProtected(ScoView, "etudarchive_generate_excel_sample") + etudarchive_generate_excel_sample = ( + sco_archives_etud.etudarchive_generate_excel_sample + ) + + def _descr_situation_etud(self, etudid, ne=""): + """chaine decrivant la situation actuelle de l'etudiant + """ + cnx = self.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select I.formsemestre_id, I.etat from notes_formsemestre_inscription I, notes_formsemestre S where etudid=%(etudid)s and S.formsemestre_id = I.formsemestre_id and date_debut < now() and date_fin > now() order by S.date_debut desc;", + {"etudid": etudid}, + ) + r = cursor.dictfetchone() + if not r: + situation = "non inscrit" + else: + sem = sco_formsemestre.get_formsemestre(self, r["formsemestre_id"]) + if r["etat"] == "I": + situation = "inscrit%s en %s" % (ne, sem["titremois"]) + # Cherche la date d'inscription dans scolar_events: + events = scolars.scolar_events_list( + cnx, + args={ + "etudid": etudid, + "formsemestre_id": sem["formsemestre_id"], + "event_type": "INSCRIPTION", + }, + ) + if not events: + log( + "*** situation inconsistante pour %s (inscrit mais pas d'event)" + % etudid + ) + date_ins = "???" # ??? + else: + date_ins = events[0]["event_date"] + situation += " le " + str(date_ins) + else: + situation = "démission de %s" % sem["titremois"] + # Cherche la date de demission dans scolar_events: + events = scolars.scolar_events_list( + cnx, + args={ + "etudid": etudid, + "formsemestre_id": sem["formsemestre_id"], + "event_type": "DEMISSION", + }, + ) + if not events: + log( + "*** situation inconsistante pour %s (demission mais pas d'event)" + % etudid + ) + date_dem = "???" # ??? + else: + date_dem = events[0]["event_date"] + situation += " le " + str(date_dem) + return situation + + # Debouche / devenir etudiant + + # vrai si l'utilisateur peut modifier les informations de suivi sur la page etud" + def can_edit_suivi(self, REQUEST=None): + authuser = REQUEST.AUTHENTICATED_USER + return authuser.has_permission(ScoEtudChangeAdr, self) + + security.declareProtected(ScoEtudChangeAdr, "itemsuivi_suppress") + itemsuivi_suppress = sco_debouche.itemsuivi_suppress + security.declareProtected(ScoEtudChangeAdr, "itemsuivi_create") + itemsuivi_create = sco_debouche.itemsuivi_create + security.declareProtected(ScoEtudChangeAdr, "itemsuivi_set_date") + itemsuivi_set_date = sco_debouche.itemsuivi_set_date + security.declareProtected(ScoEtudChangeAdr, "itemsuivi_set_situation") + itemsuivi_set_situation = sco_debouche.itemsuivi_set_situation + security.declareProtected(ScoView, "itemsuivi_list_etud") + itemsuivi_list_etud = sco_debouche.itemsuivi_list_etud + security.declareProtected(ScoView, "itemsuivi_tag_list") + itemsuivi_tag_list = sco_debouche.itemsuivi_tag_list + security.declareProtected(ScoView, "itemsuivi_tag_search") + itemsuivi_tag_search = sco_debouche.itemsuivi_tag_search + security.declareProtected(ScoEtudChangeAdr, "itemsuivi_tag_set") + itemsuivi_tag_set = sco_debouche.itemsuivi_tag_set + + security.declareProtected(ScoEtudAddAnnotations, "doAddAnnotation") + + def doAddAnnotation(self, etudid, comment, REQUEST): + "ajoute annotation sur etudiant" + authuser = REQUEST.AUTHENTICATED_USER + cnx = self.GetDBConnexion() + scolars.etud_annotations_create( + cnx, + args={ + "etudid": etudid, + "comment": comment, + "zope_authenticated_user": str(authuser), + "zope_remote_addr": REQUEST.REMOTE_ADDR, + }, + ) + logdb(REQUEST, cnx, method="addAnnotation", etudid=etudid) + REQUEST.RESPONSE.redirect("ficheEtud?etudid=" + etudid) + + security.declareProtected(ScoView, "canSuppressAnnotation") + + def canSuppressAnnotation(self, annotation_id, REQUEST): + """True if current user can suppress this annotation + Seuls l'auteur de l'annotation et le chef de dept peuvent supprimer + une annotation. + """ + cnx = self.GetDBConnexion() + annos = scolars.etud_annotations_list(cnx, args={"id": annotation_id}) + if len(annos) != 1: + raise ScoValueError("annotation inexistante !") + anno = annos[0] + authuser = REQUEST.AUTHENTICATED_USER + # note: les anciennes installations n'ont pas le role ScoEtudSupprAnnotations + # c'est pourquoi on teste aussi ScoEtudInscrit (normalement détenue par le chef) + return ( + (str(authuser) == anno["zope_authenticated_user"]) + or authuser.has_permission(ScoEtudSupprAnnotations, self) + or authuser.has_permission(ScoEtudInscrit, self) + ) + + security.declareProtected(ScoView, "doSuppressAnnotation") + + def doSuppressAnnotation(self, etudid, annotation_id, REQUEST): + """Suppression annotation. + """ + if not self.canSuppressAnnotation(annotation_id, REQUEST): + raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") + + cnx = self.GetDBConnexion() + annos = scolars.etud_annotations_list(cnx, args={"id": annotation_id}) + if len(annos) != 1: + raise ScoValueError("annotation inexistante !") + anno = annos[0] + log("suppress annotation: %s" % str(anno)) + logdb(REQUEST, cnx, method="SuppressAnnotation", etudid=etudid) + scolars.etud_annotations_delete(cnx, annotation_id) + + REQUEST.RESPONSE.redirect( + "ficheEtud?etudid=%s&head_message=Annotation%%20supprimée" % (etudid) + ) + + security.declareProtected(ScoEtudChangeAdr, "formChangeCoordonnees") + + def formChangeCoordonnees(self, etudid, REQUEST): + "edit coordonnees etudiant" + cnx = self.GetDBConnexion() + etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] + adrs = scolars.adresse_list(cnx, {"etudid": etudid}) + if adrs: + adr = adrs[0] + else: + adr = {} # no data for this student + H = [ + '

    Changement des coordonnées de %(nomprenom)s

    ' + % etud + ] + header = self.sco_header( + REQUEST, page_title="Changement adresse de %(nomprenom)s" % etud + ) + + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ("adresse_id", {"input_type": "hidden"}), + ("etudid", {"input_type": "hidden"}), + ( + "email", + { + "size": 40, + "title": "e-mail", + "explanation": "adresse institutionnelle", + }, + ), + ( + "emailperso", + { + "size": 40, + "title": "e-mail", + "explanation": "adresse personnelle", + }, + ), + ( + "domicile", + {"size": 65, "explanation": "numĂ©ro, rue", "title": "Adresse"}, + ), + ("codepostaldomicile", {"size": 6, "title": "Code postal"}), + ("villedomicile", {"size": 20, "title": "Ville"}), + ("paysdomicile", {"size": 20, "title": "Pays"}), + ("", {"input_type": "separator", "default": " "}), + ("telephone", {"size": 13, "title": "TĂ©lĂ©phone"}), + ("telephonemobile", {"size": 13, "title": "Mobile"}), + ), + initvalues=adr, + submitlabel="Valider le formulaire", + ) + if tf[0] == 0: + return header + "\n".join(H) + tf[1] + self.sco_footer(REQUEST) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + if adrs: + scolars.adresse_edit(cnx, args=tf[2], context=self) + else: + scolars.adresse_create(cnx, args=tf[2]) + logdb(REQUEST, cnx, method="changeCoordonnees", etudid=etudid) + REQUEST.RESPONSE.redirect("ficheEtud?etudid=" + etudid) + + security.declareProtected(ScoView, "formChangeGroup") + + def formChangeGroup(self, formsemestre_id, etudid, REQUEST): + "changement groupe etudiant dans semestre" + if not self.Notes.can_change_groups(REQUEST, formsemestre_id): + raise ScoValueError( + "Vous n'avez pas le droit d'effectuer cette opĂ©ration !" + ) + cnx = self.GetDBConnexion() + etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] + sem = sco_formsemestre.do_formsemestre_list(self, formsemestre_id) + ins = self.Notes.do_formsemestre_inscription_list( + {"etudid": etudid, "formsemestre_id": formsemestre_id} + )[0] + # + # -- check lock + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if sem["etat"] != "1": + raise ScoValueError("Modification impossible: semestre verrouille") + # + etud["semtitre"] = sem["titremois"] + H = [ + '

    Changement de groupe de %(nomprenom)s (semestre %(semtitre)s)

    ' + % etud + ] + header = self.sco_header( + REQUEST, page_title="Changement de groupe de %(nomprenom)s" % etud + ) + # Liste des groupes existants + raise NotImplementedError # XXX utiliser form_group_choice ou supprimer completement ? + # + H.append("""

    """) + + H.append( + """ + +

    +(attention, vérifier que les groupes sont compatibles, selon votre organisation) +

    + + +

    Créer un nouveau groupe: + + + + +

    +

    + + + + +
    """ + % ( + etudid, + formsemestre_id, + sem["nomgroupetd"], + sem["nomgroupeta"], + sem["nomgroupetp"], + REQUEST.URL1, + ) + ) + + return header + "\n".join(H) + self.sco_footer(REQUEST) + + # --- Gestion des groupes: + security.declareProtected(ScoView, "affectGroups") + affectGroups = sco_groups_edit.affectGroups + + security.declareProtected(ScoView, "XMLgetGroupsInPartition") + XMLgetGroupsInPartition = sco_groups.XMLgetGroupsInPartition + + security.declareProtected(ScoView, "formsemestre_partition_list") + formsemestre_partition_list = sco_groups.formsemestre_partition_list + + security.declareProtected(ScoView, "setGroups") + setGroups = sco_groups.setGroups + + security.declareProtected(ScoView, "createGroup") + createGroup = sco_groups.createGroup + + security.declareProtected(ScoView, "suppressGroup") + suppressGroup = sco_groups.suppressGroup + + security.declareProtected(ScoView, "group_set_name") + group_set_name = sco_groups.group_set_name + + security.declareProtected(ScoView, "group_rename") + group_rename = sco_groups.group_rename + + security.declareProtected(ScoView, "groups_auto_repartition") + groups_auto_repartition = sco_groups.groups_auto_repartition + + security.declareProtected(ScoView, "editPartitionForm") + editPartitionForm = sco_groups.editPartitionForm + + security.declareProtected(ScoView, "partition_delete") + partition_delete = sco_groups.partition_delete + + security.declareProtected(ScoView, "partition_set_attr") + partition_set_attr = sco_groups.partition_set_attr + + security.declareProtected(ScoView, "partition_move") + partition_move = sco_groups.partition_move + + security.declareProtected(ScoView, "partition_set_name") + partition_set_name = sco_groups.partition_set_name + + security.declareProtected(ScoView, "partition_rename") + partition_rename = sco_groups.partition_rename + + security.declareProtected(ScoView, "partition_create") + partition_create = sco_groups.partition_create + + security.declareProtected(ScoView, "etud_info_html") + etud_info_html = sco_page_etud.etud_info_html + + # --- Gestion des photos: + security.declareProtected(ScoView, "get_photo_image") + + get_photo_image = sco_photos.get_photo_image + + security.declareProtected(ScoView, "etud_photo_html") + + etud_photo_html = sco_photos.etud_photo_html + + security.declareProtected(ScoView, "etud_photo_orig_page") + + def etud_photo_orig_page(self, etudid=None, REQUEST=None): + "Page with photo in orig. size" + etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] + H = [ + self.sco_header(REQUEST, page_title=etud["nomprenom"]), + "

    %s

    " % etud["nomprenom"], + '", + self.sco_footer(REQUEST), + ] + return "\n".join(H) + + security.declareProtected(ScoEtudChangeAdr, "formChangePhoto") + + def formChangePhoto(self, etudid=None, REQUEST=None): + """Formulaire changement photo Ă©tudiant + """ + etud = self.getEtudInfo(filled=1, REQUEST=REQUEST)[0] + if sco_photos.etud_photo_is_local(self, etud): + etud["photoloc"] = "dans ScoDoc" + else: + etud["photoloc"] = "externe" + H = [ + self.sco_header(REQUEST, page_title="Changement de photo"), + """

    Changement de la photo de %(nomprenom)s

    +

    Photo actuelle (%(photoloc)s): + """ + % etud, + sco_photos.etud_photo_html( + self, etud, title="photo actuelle", REQUEST=REQUEST + ), + """

    Le fichier ne doit pas dépasser 500Ko (recadrer l'image, format "portrait" de préférence).

    +

    L'image sera automagiquement réduite pour obtenir une hauteur de 90 pixels.

    + """, + ] + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ("etudid", {"default": etudid, "input_type": "hidden"}), + ( + "photofile", + {"input_type": "file", "title": "Fichier image", "size": 20}, + ), + ), + submitlabel="Valider", + cancelbutton="Annuler", + ) + if tf[0] == 0: + return ( + "\n".join(H) + + tf[1] + + '

    Supprimer cette photo

    ' + % etudid + + self.sco_footer(REQUEST) + ) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect( + REQUEST.URL1 + "/ficheEtud?etudid=" + etud["etudid"] + ) + else: + data = tf[2]["photofile"].read() + status, diag = sco_photos.store_photo(self, etud, data, REQUEST=REQUEST) + if status != 0: + return REQUEST.RESPONSE.redirect( + self.ScoURL() + "/ficheEtud?etudid=" + etud["etudid"] + ) + else: + H.append('

    Erreur:' + diag + "

    ") + return "\n".join(H) + self.sco_footer(REQUEST) + + security.declareProtected(ScoEtudChangeAdr, "formSuppressPhoto") + + def formSuppressPhoto(self, etudid=None, REQUEST=None, dialog_confirmed=False): + """Formulaire suppression photo Ă©tudiant + """ + etud = self.getEtudInfo(filled=1, REQUEST=REQUEST)[0] + if not dialog_confirmed: + return self.confirmDialog( + "

    Confirmer la suppression de la photo de %(nomprenom)s ?

    " % etud, + dest_url="", + REQUEST=REQUEST, + cancel_url="ficheEtud?etudid=%s" % etudid, + parameters={"etudid": etudid}, + ) + + sco_photos.suppress_photo(self, etud, REQUEST=REQUEST) + + return REQUEST.RESPONSE.redirect( + REQUEST.URL1 + "/ficheEtud?etudid=" + etud["etudid"] + ) + + # + security.declareProtected(ScoEtudInscrit, "formDem") + + def formDem(self, etudid, formsemestre_id, REQUEST): + "Formulaire Démission Etudiant" + return self._formDem_of_Def( + etudid, + formsemestre_id, + REQUEST=REQUEST, + operation_name="Démission", + operation_method="doDemEtudiant", + ) + + security.declareProtected(ScoEtudInscrit, "formDef") + + def formDef(self, etudid, formsemestre_id, REQUEST): + "Formulaire Défaillance Etudiant" + return self._formDem_of_Def( + etudid, + formsemestre_id, + REQUEST=REQUEST, + operation_name="Défaillance", + operation_method="doDefEtudiant", + ) + + def _formDem_of_Def( + self, + etudid, + formsemestre_id, + REQUEST=None, + operation_name="", + operation_method="", + ): + "Formulaire démission ou défaillance Etudiant" + cnx = self.GetDBConnexion() + etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0] + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if sem["etat"] != "1": + raise ScoValueError("Modification impossible: semestre verrouille") + + etud["formsemestre_id"] = formsemestre_id + etud["semtitre"] = sem["titremois"] + etud["nowdmy"] = time.strftime("%d/%m/%Y") + etud["operation_name"] = operation_name + # + header = self.sco_header( + REQUEST, + page_title="%(operation_name)s de %(nomprenom)s (du semestre %(semtitre)s)" + % etud, + ) + H = [ + '

    %(operation_name)s de %(nomprenom)s (semestre %(semtitre)s)

    ' + % etud + ] + H.append( + """

    + Date de la %s (J/M/AAAA):  + """ + % (operation_method, strlower(operation_name)) + ) + H.append( + """ + + + +

    + +

    """ + % etud + ) + return header + "\n".join(H) + self.sco_footer(REQUEST) + + security.declareProtected(ScoEtudInscrit, "doDemEtudiant") + + def doDemEtudiant(self, etudid, formsemestre_id, event_date=None, REQUEST=None): + "Déclare la démission d'un etudiant dans le semestre" + return self._doDem_or_Def_Etudiant( + etudid, + formsemestre_id, + event_date=event_date, + etat_new="D", + operation_method="demEtudiant", + event_type="DEMISSION", + REQUEST=REQUEST, + ) + + security.declareProtected(ScoEtudInscrit, "doDefEtudiant") + + def doDefEtudiant(self, etudid, formsemestre_id, event_date=None, REQUEST=None): + "Déclare la défaillance d'un etudiant dans le semestre" + return self._doDem_or_Def_Etudiant( + etudid, + formsemestre_id, + event_date=event_date, + etat_new=DEF, + operation_method="defailleEtudiant", + event_type="DEFAILLANCE", + REQUEST=REQUEST, + ) + + def _doDem_or_Def_Etudiant( + self, + etudid, + formsemestre_id, + event_date=None, + etat_new="D", # 'D' or DEF + operation_method="demEtudiant", + event_type="DEMISSION", + REQUEST=None, + ): + "Démission ou défaillance d'un étudiant" + # marque 'D' ou DEF dans l'inscription au semestre et ajoute + # un "evenement" scolarite + cnx = self.GetDBConnexion() + # check lock + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if sem["etat"] != "1": + raise ScoValueError("Modification impossible: semestre verrouille") + # + ins = self.Notes.do_formsemestre_inscription_list( + {"etudid": etudid, "formsemestre_id": formsemestre_id} + )[0] + if not ins: + raise ScoException("etudiant non inscrit ?!") + ins["etat"] = etat_new + self.Notes.do_formsemestre_inscription_edit( + args=ins, formsemestre_id=formsemestre_id + ) + logdb(REQUEST, cnx, method=operation_method, etudid=etudid) + scolars.scolar_events_create( + cnx, + args={ + "etudid": etudid, + "event_date": event_date, + "formsemestre_id": formsemestre_id, + "event_type": event_type, + }, + ) + if REQUEST: + return REQUEST.RESPONSE.redirect("ficheEtud?etudid=" + etudid) + + security.declareProtected(ScoEtudInscrit, "doCancelDem") + + def doCancelDem( + self, etudid, formsemestre_id, dialog_confirmed=False, args=None, REQUEST=None + ): + "Annule une démission" + return self._doCancelDem_or_Def( + etudid, + formsemestre_id, + dialog_confirmed=dialog_confirmed, + args=args, + operation_name="démission", + etat_current="D", + etat_new="I", + operation_method="cancelDem", + event_type="DEMISSION", + REQUEST=REQUEST, + ) + + security.declareProtected(ScoEtudInscrit, "doCancelDef") + + def doCancelDef( + self, etudid, formsemestre_id, dialog_confirmed=False, args=None, REQUEST=None + ): + "Annule la défaillance de l'étudiant" + return self._doCancelDem_or_Def( + etudid, + formsemestre_id, + dialog_confirmed=dialog_confirmed, + args=args, + operation_name="défaillance", + etat_current=DEF, + etat_new="I", + operation_method="cancelDef", + event_type="DEFAILLANCE", + REQUEST=REQUEST, + ) + + def _doCancelDem_or_Def( + self, + etudid, + formsemestre_id, + dialog_confirmed=False, + args=None, + operation_name="", # "démission" ou "défaillance" + etat_current="D", + etat_new="I", + operation_method="cancelDem", + event_type="DEMISSION", + REQUEST=None, + ): + "Annule une demission ou une défaillance" + # check lock + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + if sem["etat"] != "1": + raise ScoValueError("Modification impossible: semestre verrouille") + # verif + info = self.getEtudInfo(etudid, filled=True)[0] + ok = False + for i in info["ins"]: + if i["formsemestre_id"] == formsemestre_id: + if i["etat"] != etat_current: + raise ScoValueError("etudiant non %s !" % operation_name) + ok = True + break + if not ok: + raise ScoValueError("etudiant non inscrit ???") + if not dialog_confirmed: + return self.confirmDialog( + "

    Confirmer l'annulation de la %s ?

    " % operation_name, + dest_url="", + REQUEST=REQUEST, + cancel_url="ficheEtud?etudid=%s" % etudid, + parameters={"etudid": etudid, "formsemestre_id": formsemestre_id}, + ) + # + ins = self.Notes.do_formsemestre_inscription_list( + {"etudid": etudid, "formsemestre_id": formsemestre_id} + )[0] + if ins["etat"] != etat_current: + raise ScoException("etudiant non %s !!!" % etat_current) # obviously a bug + ins["etat"] = etat_new + cnx = self.GetDBConnexion() + self.Notes.do_formsemestre_inscription_edit( + args=ins, formsemestre_id=formsemestre_id + ) + logdb(REQUEST, cnx, method=operation_method, etudid=etudid) + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "delete from scolar_events where etudid=%(etudid)s and formsemestre_id=%(formsemestre_id)s and event_type='" + + event_type + + "'", + {"etudid": etudid, "formsemestre_id": formsemestre_id}, + ) + cnx.commit() + return REQUEST.RESPONSE.redirect("ficheEtud?etudid=%s" % etudid) + + security.declareProtected(ScoEtudInscrit, "etudident_create_form") + + def etudident_create_form(self, REQUEST=None): + "formulaire creation individuelle etudiant" + return self.etudident_create_or_edit_form(REQUEST, edit=False) + + security.declareProtected(ScoEtudInscrit, "etudident_edit_form") + + def etudident_edit_form(self, REQUEST=None): + "formulaire edition individuelle etudiant" + return self.etudident_create_or_edit_form(REQUEST, edit=True) + + security.declareProtected(ScoEtudInscrit, "etudident_create_or_edit_form") + + def etudident_create_or_edit_form(self, REQUEST, edit): + "Le formulaire HTML" + H = [self.sco_header(REQUEST, init_jquery_ui=True)] + F = self.sco_footer(REQUEST) + AUTHENTICATED_USER = REQUEST.AUTHENTICATED_USER + etudid = REQUEST.form.get("etudid", None) + cnx = self.GetDBConnexion() + descr = [] + if not edit: + # creation nouvel etudiant + initvalues = {} + submitlabel = "Ajouter cet Ă©tudiant" + H.append( + """

    Création d'un étudiant

    +

    En général, il est recommandé d'importer les étudiants depuis Apogée. + N'utilisez ce formulaire que pour les cas particuliers ou si votre établissement + n'utilise pas d'autre logiciel de gestion des inscriptions.

    +

    L'étudiant créé ne sera pas inscrit. + Pensez à l'inscrire dans un semestre !

    + """ + ) + else: + # edition donnees d'un etudiant existant + # setup form init values + if not etudid: + raise ValueError("missing etudid parameter") + descr.append(("etudid", {"default": etudid, "input_type": "hidden"})) + H.append( + '

    Modification d\'un Ă©tudiant (fiche)

    ' + % etudid + ) + initvalues = scolars.etudident_list(cnx, {"etudid": etudid}) + assert len(initvalues) == 1 + initvalues = initvalues[0] + submitlabel = "Modifier les données" + + # recuperation infos Apogee + # Si on a le code NIP, fait juste une requete, sinon tente de rechercher par nom + # (la recherche par nom ne fonctionne plus à Paris 13) + code_nip = initvalues.get("code_nip", "") + if code_nip: + try: + info = sco_portal_apogee.get_etud_apogee(self, code_nip) + except ValueError: + pass # XXX a terminer + nom = REQUEST.form.get("nom", None) + if nom is None: + nom = initvalues.get("nom", None) + if nom is None: + infos = [] + else: + prenom = REQUEST.form.get("prenom", "") + if REQUEST.form.get("tf-submitted", False) and not prenom: + prenom = initvalues.get("prenom", "") + infos = sco_portal_apogee.get_infos_apogee(self, nom, prenom) + + if infos: + formatted_infos = [ + """ + +
      """ + ] + nanswers = len(infos) + nmax = 10 # nb max de reponse montrees + infos = infos[:nmax] + for i in infos: + formatted_infos.append("
      • ") + for k in i.keys(): + if k != "nip": + item = "
      • %s : %s
      • " % (k, i[k]) + else: + item = ( + '
      • %s : %s
      • ' + % (k, i[k], i[k]) + ) + formatted_infos.append(item) + + formatted_infos.append("
    1. ") + formatted_infos.append("
    ") + m = "%d étudiants trouvés" % nanswers + if len(infos) != nanswers: + m += " (%d montrés)" % len(infos) + A = """
    +
    Informations Apogée
    +

    %s

    + %s +
    """ % ( + m, + "\n".join(formatted_infos), + ) + else: + A = """

    Pas d'informations d'Apogée

    """ + + require_ine = self.get_preference("always_require_ine") + + descr += [ + ("adm_id", {"input_type": "hidden"}), + ("nom", {"size": 25, "title": "Nom", "allow_null": False}), + ("nom_usuel", {"size": 25, "title": "Nom usuel", "allow_null": True}), + ( + "prenom", + {"size": 25, "title": "Prénom", "allow_null": CONFIG.ALLOW_NULL_PRENOM}, + ), + ( + "sexe", + { + "input_type": "menu", + "labels": ["H", "F"], + "allowed_values": ["MR", "MME"], + "title": "Genre", + }, + ), + ( + "date_naissance", + { + "title": "Date de naissance", + "input_type": "date", + "explanation": "j/m/a", + }, + ), + ("lieu_naissance", {"title": "Lieu de naissance", "size": 32}), + ("dept_naissance", {"title": "Département de naissance", "size": 5}), + ("nationalite", {"size": 25, "title": "Nationalité"}), + ( + "statut", + { + "size": 25, + "title": "Statut", + "explanation": '("salarie", ...) inutilisé par ScoDoc', + }, + ), + ( + "annee", + { + "size": 5, + "title": "Année admission IUT", + "type": "int", + "allow_null": False, + "explanation": "année 1ere inscription (obligatoire)", + }, + ), + # + ("sep", {"input_type": "separator", "title": "Scolarité antérieure:"}), + ("bac", {"size": 5, "explanation": "série du bac (S, STI, STT, ...)"}), + ( + "specialite", + { + "size": 25, + "title": "Spécialité", + "explanation": "spécialité bac: SVT M, GENIE ELECTRONIQUE, ...", + }, + ), + ( + "annee_bac", + { + "size": 5, + "title": "Année bac", + "type": "int", + "explanation": "année obtention du bac", + }, + ), + ( + "math", + { + "size": 3, + "type": "float", + "title": "Note de mathématiques", + "explanation": "note sur 20 en terminale", + }, + ), + ( + "physique", + { + "size": 3, + "type": "float", + "title": "Note de physique", + "explanation": "note sur 20 en terminale", + }, + ), + ( + "anglais", + { + "size": 3, + "type": "float", + "title": "Note d'anglais", + "explanation": "note sur 20 en terminale", + }, + ), + ( + "francais", + { + "size": 3, + "type": "float", + "title": "Note de français", + "explanation": "note sur 20 obtenue au bac", + }, + ), + ( + "type_admission", + { + "input_type": "menu", + "title": "Voie d'admission", + "allowed_values": TYPES_ADMISSION, + }, + ), + ( + "boursier_prec", + { + "input_type": "boolcheckbox", + "labels": ["non", "oui"], + "title": "Boursier ?", + "explanation": "dans le cycle précédent (lycée)", + }, + ), + ( + "rang", + { + "size": 1, + "type": "int", + "title": "Position établissement", + "explanation": "rang de notre établissement dans les voeux du candidat (si connu)", + }, + ), + ( + "qualite", + { + "size": 3, + "type": "float", + "title": "Qualité", + "explanation": "Note de qualité attribuée au dossier (par le jury d'adm.)", + }, + ), + ( + "decision", + { + "input_type": "menu", + "title": "Décision", + "allowed_values": [ + "ADMIS", + "ATTENTE 1", + "ATTENTE 2", + "ATTENTE 3", + "REFUS", + "?", + ], + }, + ), + ( + "score", + { + "size": 3, + "type": "float", + "title": "Score", + "explanation": "score calculé lors de l'admission", + }, + ), + ( + "classement", + { + "size": 3, + "type": "int", + "title": "Classement", + "explanation": "Classement par le jury d'admission (de 1 à N)", + }, + ), + ("apb_groupe", {"size": 15, "title": "Groupe APB ou PS"}), + ( + "apb_classement_gr", + { + "size": 3, + "type": "int", + "title": "Classement", + "explanation": "Classement par le jury dans le groupe ABP ou PS (de 1 à Ng)", + }, + ), + ("rapporteur", {"size": 50, "title": "Enseignant rapporteur"}), + ( + "commentaire", + { + "input_type": "textarea", + "rows": 4, + "cols": 50, + "title": "Note du rapporteur", + }, + ), + ("nomlycee", {"size": 20, "title": "Lycée d'origine"}), + ("villelycee", {"size": 15, "title": "Commune du lycée"}), + ("codepostallycee", {"size": 15, "title": "Code Postal lycée"}), + ( + "codelycee", + { + "size": 15, + "title": "Code Lycée", + "explanation": "Code national établissement du lycée ou établissement d'origine", + }, + ), + ("sep", {"input_type": "separator", "title": "Codes Apogée: (optionnels)"}), + ( + "code_nip", + { + "size": 25, + "title": "Numéro NIP", + "allow_null": True, + "explanation": "numéro identité étudiant (Apogée)", + }, + ), + ( + "code_ine", + { + "size": 25, + "title": "Numéro INE", + "allow_null": not require_ine, + "explanation": "numéro INE", + }, + ), + ( + "dont_check_homonyms", + { + "title": "Autoriser les homonymes", + "input_type": "boolcheckbox", + "explanation": "ne vérifie pas les noms et prénoms proches", + }, + ), + ] + initvalues["dont_check_homonyms"] = False + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + descr, + submitlabel=submitlabel, + cancelbutton="Re-interroger Apogee", + initvalues=initvalues, + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + "

    " + A + F + elif tf[0] == -1: + return "\n".join(H) + tf[1] + "

    " + A + F + # return '\n'.join(H) + '

    annulation

    ' + F + else: + # form submission + if edit: + etudid = tf[2]["etudid"] + else: + etudid = None + ok, NbHomonyms = scolars.check_nom_prenom( + cnx, nom=tf[2]["nom"], prenom=tf[2]["prenom"], etudid=etudid + ) + if not ok: + return ( + "\n".join(H) + + tf_error_message("Nom ou prénom invalide") + + tf[1] + + "

    " + + A + + F + ) + # log('NbHomonyms=%s' % NbHomonyms) + if not tf[2]["dont_check_homonyms"] and NbHomonyms > 0: + return ( + "\n".join(H) + + tf_error_message( + """Attention: il y a déjà un étudiant portant des noms et prénoms proches. Vous pouvez forcer la présence d'un homonyme en cochant "autoriser les homonymes" en bas du formulaire.""" + ) + + tf[1] + + "

    " + + A + + F + ) + + if not edit: + # creation d'un etudiant + etudid = scolars.etudident_create( + cnx, tf[2], context=self, REQUEST=REQUEST + ) + # crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !) + adresse_id = scolars.adresse_create( + cnx, + { + "etudid": etudid, + "typeadresse": "domicile", + "description": "(creation individuelle)", + }, + ) + + # event + scolars.scolar_events_create( + cnx, + args={ + "etudid": etudid, + "event_date": time.strftime("%d/%m/%Y"), + "formsemestre_id": None, + "event_type": "CREATION", + }, + ) + # log + logdb( + REQUEST, + cnx, + method="etudident_edit_form", + etudid=etudid, + msg="creation initiale", + ) + etud = scolars.etudident_list(cnx, {"etudid": etudid})[0] + self.fillEtudsInfo([etud]) + etud["url"] = "ficheEtud?etudid=%(etudid)s" % etud + sco_news.add( + self, + REQUEST, + typ=NEWS_INSCR, + object=None, # pas d'object pour ne montrer qu'un etudiant + text='Nouvel étudiant %(nomprenom)s' % etud, + url=etud["url"], + ) + else: + # modif d'un etudiant + scolars.etudident_edit(cnx, tf[2], context=self, REQUEST=REQUEST) + etud = scolars.etudident_list(cnx, {"etudid": etudid})[0] + self.fillEtudsInfo([etud]) + # Inval semesters with this student: + to_inval = [s["formsemestre_id"] for s in etud["sems"]] + if to_inval: + self.Notes._inval_cache( + formsemestre_id_list=to_inval + ) # > etudident_create_or_edit + # + return REQUEST.RESPONSE.redirect("ficheEtud?etudid=" + etudid) + + security.declareProtected(ScoEtudInscrit, "etudident_delete") + + def etudident_delete(self, etudid, dialog_confirmed=False, REQUEST=None): + "Delete a student" + cnx = self.GetDBConnexion() + etuds = scolars.etudident_list(cnx, {"etudid": etudid}) + if not etuds: + raise ScoValueError("Etudiant inexistant !") + else: + etud = etuds[0] + self.fillEtudsInfo([etud]) + if not dialog_confirmed: + return self.confirmDialog( + """

    Confirmer la suppression de l'Ă©tudiant %(nomprenom)s ?

    +

    +

    Prenez le temps de vérifier que vous devez vraiment supprimer cet étudiant !

    +

    Cette opération irréversible efface toute trace de l'étudiant: inscriptions, notes, absences... dans tous les semestres qu'il a fréquenté.

    +

    Dans la plupart des cas, vous avez seulement besoin de le

      désinscrire
    d'un semestre ? (dans ce cas passez par sa fiche, menu associé au semestre)

    + +

    VĂ©rifier la fiche de %(nomprenom)s +

    """ + % etud, + dest_url="", + REQUEST=REQUEST, + cancel_url="ficheEtud?etudid=%s" % etudid, + OK="Supprimer définitivement cet étudiant", + parameters={"etudid": etudid}, + ) + log("etudident_delete: etudid=%(etudid)s nomprenom=%(nomprenom)s" % etud) + # delete in all tables ! + tables = [ + "notes_appreciations", + "scolar_autorisation_inscription", + "scolar_formsemestre_validation", + "scolar_events", + "notes_notes_log", + "notes_notes", + "notes_moduleimpl_inscription", + "notes_formsemestre_inscription", + "group_membership", + "entreprise_contact", + "etud_annotations", + "scolog", + "admissions", + "adresse", + "absences", + "billet_absence", + "identite", + ] + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + for table in tables: + cursor.execute("delete from %s where etudid=%%(etudid)s" % table, etud) + cnx.commit() + # Inval semestres où il était inscrit: + to_inval = [s["formsemestre_id"] for s in etud["sems"]] + if to_inval: + self.Notes._inval_cache(formsemestre_id_list=to_inval) # > + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + + security.declareProtected(ScoEtudInscrit, "check_group_apogee") + + def check_group_apogee( + self, group_id, REQUEST=None, etat=None, fix=False, fixmail=False + ): + """Verification des codes Apogee et mail de tout un groupe. + Si fix == True, change les codes avec Apogée. + + XXX A re-écrire pour API 2: prendre liste dans l'étape et vérifier à partir de cela. + """ + etat = etat or None + members, group, group_tit, sem, nbdem = sco_groups.get_group_infos( + self, group_id, etat=etat + ) + formsemestre_id = group["formsemestre_id"] + + cnx = self.GetDBConnexion() + H = [ + self.Notes.html_sem_header( + REQUEST, "Etudiants du %s" % (group["group_name"] or "semestre"), sem + ), + '', + "", + ] + nerrs = 0 # nombre d'anomalies détectées + nfix = 0 # nb codes changes + nmailmissing = 0 # nb etuds sans mail + for t in members: + nom, nom_usuel, prenom, etudid, email, code_nip = ( + t["nom"], + t["nom_usuel"], + t["prenom"], + t["etudid"], + t["email"], + t["code_nip"], + ) + infos = sco_portal_apogee.get_infos_apogee(self, nom, prenom) + if not infos: + info_apogee = ( + 'Pas d\'information (Modifier identité)' + % etudid + ) + nerrs += 1 + else: + if len(infos) == 1: + nip_apogee = infos[0]["nip"] + if code_nip != nip_apogee: + if fix: + # Update database + scolars.identite_edit( + cnx, + args={"etudid": etudid, "code_nip": nip_apogee}, + context=self, + ) + info_apogee = ( + 'copié %s' % nip_apogee + ) + nfix += 1 + else: + info_apogee = ( + '%s' % nip_apogee + ) + nerrs += 1 + else: + info_apogee = "ok" + else: + info_apogee = ( + '%d correspondances (Choisir)' + % (len(infos), etudid) + ) + nerrs += 1 + # check mail + if email: + mailstat = "ok" + else: + if fixmail and len(infos) == 1 and "mail" in infos[0]: + mail_apogee = infos[0]["mail"] + adrs = scolars.adresse_list(cnx, {"etudid": etudid}) + if adrs: + adr = adrs[0] # modif adr existante + args = {"adresse_id": adr["adresse_id"], "email": mail_apogee} + scolars.adresse_edit(cnx, args=args) + else: + # creation adresse + args = {"etudid": etudid, "email": mail_apogee} + scolars.adresse_create(cnx, args=args) + mailstat = 'copié' + else: + mailstat = "inconnu" + nmailmissing += 1 + H.append( + '' + % (etudid, nom, nom_usuel, prenom, mailstat, code_nip, info_apogee) + ) + H.append("
    NomNom usuelPrénomMailNIP (ScoDoc)Apogée
    %s%s%s%s%s%s
    ") + H.append("
      ") + if nfix: + H.append("
    • %d codes modifiĂ©s
    • " % nfix) + H.append("
    • Codes NIP: %d anomalies dĂ©tectĂ©es
    • " % nerrs) + H.append( + "
    • Adresse mail: %d Ă©tudiants sans adresse
    • " % nmailmissing + ) + H.append("
    ") + H.append( + """ +
    + + + + + +
    +

    Retour au semestre + """ + % ( + REQUEST.URL0, + formsemestre_id, + strnone(group_id), + strnone(etat), + formsemestre_id, + ) + ) + H.append( + """ +

    + + + + + +
    +

    Retour au semestre + """ + % ( + REQUEST.URL0, + formsemestre_id, + strnone(group_id), + strnone(etat), + formsemestre_id, + ) + ) + + return "\n".join(H) + self.sco_footer(REQUEST) + + security.declareProtected(ScoEtudInscrit, "form_students_import_excel") + + def form_students_import_excel(self, REQUEST, formsemestre_id=None): + "formulaire import xls" + if formsemestre_id: + sem = sco_formsemestre.get_formsemestre(self.Notes, formsemestre_id) + else: + sem = None + if sem and sem["etat"] != "1": + raise ScoValueError("Modification impossible: semestre verrouille") + H = [ + self.sco_header(REQUEST, page_title="Import etudiants"), + """

    Téléchargement d\'une nouvelle liste d\'etudiants

    +
    +

    A utiliser pour importer de nouveaux Ă©tudiants (typiquement au + premier semestre).

    +

    Si les étudiants à inscrire sont déjà dans un autre + semestre, utiliser le menu "Inscriptions (passage des étudiants) + depuis d'autres semestres à partir du semestre destination. +

    +

    Si vous avez un portail Apogée, il est en général préférable d'importer les + étudiants depuis Apogée, via le menu "Synchroniser avec étape Apogée". +

    +
    +

    + L'opération se déroule en deux étapes. Dans un premier temps, + vous téléchargez une feuille Excel type. Vous devez remplir + cette feuille, une ligne décrivant chaque étudiant. Ensuite, + vous indiquez le nom de votre fichier dans la case "Fichier Excel" + ci-dessous, et cliquez sur "Télécharger" pour envoyer au serveur + votre liste. +

    + """, + ] # ' + if sem: + H.append( + """

    Les étudiants importés seront inscrits dans + le semestre %s

    """ + % sem["titremois"] + ) + else: + H.append( + """ +

    Pour inscrire directement les étudiants dans un semestre de + formation, il suffit d'indiquer le code de ce semestre + (qui doit avoir été créé au préalable). Cliquez ici pour afficher les codes +

    + """ + % (self.ScoURL()) + ) + + H.append("""
    1. """) + if formsemestre_id: + H.append( + """ + + """ + ) + else: + H.append("""""") + H.append( + """Obtenir la feuille excel Ă  remplir
    2. +
    3. """ + ) + + F = self.sco_footer(REQUEST) + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ( + "csvfile", + {"title": "Fichier Excel:", "input_type": "file", "size": 40}, + ), + ( + "check_homonyms", + { + "title": "Vérifier les homonymes", + "input_type": "boolcheckbox", + "explanation": "arrète l'importation si plus de 10% d'homonymes", + }, + ), + ( + "require_ine", + { + "title": "Importer INE", + "input_type": "boolcheckbox", + "explanation": "n'importe QUE les étudiants avec nouveau code INE", + }, + ), + ("formsemestre_id", {"input_type": "hidden"}), + ), + initvalues={"check_homonyms": True, "require_ine": False}, + submitlabel="Télécharger", + ) + S = [ + """

      Le fichier Excel décrivant les étudiants doit comporter les colonnes suivantes. +

      Les colonnes peuvent être placées dans n'importe quel ordre, mais +le titre exact (tel que ci-dessous) doit être sur la première ligne. +

      +

      +Les champs avec un astérisque (*) doivent être présents (nulls non autorisés). +

      + + +

      + +""" + ] + for t in ImportScolars.sco_import_format( + with_codesemestre=(formsemestre_id == None) + ): + if int(t[3]): + ast = "" + else: + ast = "*" + S.append( + "" + % (t[0], t[1], t[4], ast) + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + "" + "\n".join(S) + F + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + return ImportScolars.students_import_excel( + self, + tf[2]["csvfile"], + REQUEST=REQUEST, + formsemestre_id=formsemestre_id, + check_homonyms=tf[2]["check_homonyms"], + require_ine=tf[2]["require_ine"], + ) + + security.declareProtected(ScoEtudInscrit, "import_generate_excel_sample") + + def import_generate_excel_sample(self, REQUEST, with_codesemestre="1"): + "une feuille excel pour importation etudiants" + if with_codesemestre: + with_codesemestre = int(with_codesemestre) + else: + with_codesemestre = 0 + format = ImportScolars.sco_import_format() + data = ImportScolars.sco_import_generate_excel_sample( + format, with_codesemestre, exclude_cols=["photo_filename"], REQUEST=REQUEST + ) + return sco_excel.sendExcelFile(REQUEST, data, "ImportEtudiants.xls") + + # --- Données admission + security.declareProtected(ScoView, "import_generate_admission_sample") + + def import_generate_admission_sample(self, REQUEST, formsemestre_id): + "une feuille excel pour importation données admissions" + group = sco_groups.get_group( + self, sco_groups.get_default_group(self, formsemestre_id) + ) + fmt = ImportScolars.sco_import_format() + data = ImportScolars.sco_import_generate_excel_sample( + fmt, + only_tables=["identite", "admissions", "adresse"], + exclude_cols=["nationalite", "foto", "photo_filename"], + group_ids=[group["group_id"]], + context=self.Notes, + REQUEST=REQUEST, + ) + return sco_excel.sendExcelFile(REQUEST, data, "AdmissionEtudiants.xls") + + # --- Données admission depuis fichier excel (version nov 2016) + security.declareProtected(ScoView, "form_students_import_infos_admissions") + + def form_students_import_infos_admissions(self, REQUEST, formsemestre_id=None): + "formulaire import xls" + sem = sco_formsemestre.get_formsemestre(self.Notes, formsemestre_id) + authuser = REQUEST.AUTHENTICATED_USER + F = self.sco_footer(REQUEST) + if not authuser.has_permission(ScoEtudInscrit, self): + # autorise juste l'export + H = [ + self.sco_header( + REQUEST, + page_title="Export données admissions (Parcoursup ou autre)", + ), + """

      Téléchargement des informations sur l'admission des étudiants

      +

      + Exporter les informations de ScoDoc (classeur Excel) (ce fichier peut être ré-importé après d'éventuelles modifications) +

      +

      Vous n'avez pas le droit d'importer les données

      + """ + % {"formsemestre_id": formsemestre_id}, + ] + return "\n".join(H) + F + + # On a le droit d'importer: + H = [ + self.sco_header(REQUEST, page_title="Import données admissions Parcoursup"), + """

      Téléchargement des informations sur l'admission des étudiants depuis feuilles import Parcoursup

      +
      +

      A utiliser pour renseigner les informations sur l'origine des étudiants (lycées, bac, etc). Ces informations sont facultatives mais souvent utiles pour mieux connaitre les étudiants et aussi pour effectuer des statistiques (résultats suivant le type de bac...). Les données sont affichées sur les fiches individuelles des étudiants.

      +
      +

      + Importer ici la feuille excel utilisée pour envoyer le classement Parcoursup. + Seuls les étudiants actuellement inscrits dans ce semestre ScoDoc seront affectés, + les autres lignes de la feuille seront ignorées. Et seules les colonnes intéressant ScoDoc + seront importées: il est inutile d'éliminer les autres. +
      + Seules les données "admission" seront modifiées (et pas l'identité de l'étudiant). +
      + Les colonnes "nom" et "prenom" sont requises, ou bien une colonne "etudid". +

      +

      + Avant d'importer vos données, il est recommandé d'enregistrer les informations actuelles: + exporter les données actuelles de ScoDoc (ce fichier peut être ré-importé après d'éventuelles modifications) +

      + """ + % {"formsemestre_id": formsemestre_id}, + ] # ' + + type_admission_list = ( + "Autre", + "Parcoursup", + "Parcoursup PC", + "APB", + "APB PC", + "CEF", + "Direct", + ) + + tf = TrivialFormulator( + REQUEST.URL0, + REQUEST.form, + ( + ( + "csvfile", + {"title": "Fichier Excel:", "input_type": "file", "size": 40}, + ), + ( + "type_admission", + { + "title": "Type d'admission", + "explanation": "sera attribué aux étudiants modifiés par cet import n'ayant pas déjà un type", + "input_type": "menu", + "allowed_values": type_admission_list, + }, + ), + ("formsemestre_id", {"input_type": "hidden"}), + ), + submitlabel="Télécharger", + ) + + help_text = ( + """

      Les colonnes importables par cette fonction sont indiquées dans la table ci-dessous. + Seule la première feuille du classeur sera utilisée. +

      + """ + + ImportScolars.adm_table_description_format(self).html() + + """
      """ + ) + + if tf[0] == 0: + return "\n".join(H) + tf[1] + help_text + F + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + return self._students_import_admission( + tf[2]["csvfile"], + type_admission=tf[2]["type_admission"], + REQUEST=REQUEST, + formsemestre_id=formsemestre_id, + ) + + # unpublished + def _students_import_admission( + self, csvfile, type_admission="", REQUEST=None, formsemestre_id=None + ): + "import donnees admission from Excel file (v2016)" + diag = ImportScolars.scolars_import_admission( + csvfile, + self.Notes, + REQUEST, + formsemestre_id=formsemestre_id, + type_admission=type_admission, + ) + if REQUEST: + H = [self.sco_header(REQUEST, page_title="Import données admissions")] + H.append("

      Import terminé !

      ") + H.append( + '

      Continuer

      ' + % "formsemestre_status?formsemestre_id=%s" + % formsemestre_id + ) + if diag: + H.append( + "

      Diagnostic:

      • %s

      " % "
    4. ".join(diag) + ) + + return "\n".join(H) + self.sco_footer(REQUEST) + + security.declareProtected(ScoEtudInscrit, "formsemestre_import_etud_admission") + + def formsemestre_import_etud_admission( + self, formsemestre_id, import_email=True, REQUEST=None + ): + """Reimporte donnees admissions par synchro Portail Apogée""" + ( + no_nip, + unknowns, + changed_mails, + ) = sco_synchro_etuds.formsemestre_import_etud_admission( + self.Notes, formsemestre_id, import_identite=True, import_email=import_email + ) + H = [ + self.Notes.html_sem_header(REQUEST, "Reimport données admission"), + "

      Opération effectuée

      ", + ] + if no_nip: + H.append("

      Attention: Ă©tudiants sans NIP: " + str(no_nip) + "

      ") + if unknowns: + H.append( + "

      Attention: Ă©tudiants inconnus du portail: codes NIP=" + + str(unknowns) + + "

      " + ) + if changed_mails: + H.append("

      Adresses mails modifiées:

      ") + for (info, new_mail) in changed_mails: + H.append( + "%s: %s devient %s
      " + % (info["nom"], info["email"], new_mail) + ) + return "\n".join(H) + self.sco_footer(REQUEST) + + security.declareProtected(ScoEtudChangeAdr, "photos_import_files_form") + photos_import_files_form = sco_trombino.photos_import_files_form + security.declareProtected(ScoEtudChangeAdr, "photos_generate_excel_sample") + photos_generate_excel_sample = sco_trombino.photos_generate_excel_sample + + # --- Statistiques + security.declareProtected(ScoView, "stat_bac") + + def stat_bac(self, formsemestre_id): + "Renvoie statistisques sur nb d'etudiants par bac" + cnx = self.GetDBConnexion() + sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) + ins = self.Notes.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id} + ) + Bacs = {} # type bac : nb etud + for i in ins: + etud = scolars.etudident_list(cnx, {"etudid": i["etudid"]})[0] + typebac = "%(bac)s %(specialite)s" % etud + Bacs[typebac] = Bacs.get(typebac, 0) + 1 + return Bacs + + # --- Dump + security.declareProtected(ScoView, "sco_dump_and_send_db") + sco_dump_and_send_db = sco_dump_db.sco_dump_and_send_db + + # + def confirmDialog( + self, + message="

      Confirmer ?

      ", + OK="OK", + Cancel="Annuler", + dest_url="", + cancel_url="", + target_variable="dialog_confirmed", + parameters={}, + add_headers=True, # complete page + REQUEST=None, # required + helpmsg=None, + ): + # dialog de confirmation simple + parameters[target_variable] = 1 + # Attention: la page a pu etre servie en GET avec des parametres + # si on laisse l'url "action" vide, les parametres restent alors que l'on passe en POST... + if not dest_url: + dest_url = REQUEST.URL + # strip remaining parameters from destination url: + dest_url = urllib.splitquery(dest_url)[0] + H = [ + """
      """ % dest_url, + message, + """""" % OK, + ] + if cancel_url: + H.append( + """""" + % (Cancel, cancel_url) + ) + for param in parameters.keys(): + if parameters[param] is None: + parameters[param] = "" + if type(parameters[param]) == type([]): + for e in parameters[param]: + H.append('' % (param, e)) + else: + H.append( + '' + % (param, parameters[param]) + ) + H.append("") + if helpmsg: + H.append('

      ' + helpmsg + "

      ") + if add_headers and REQUEST: + return self.sco_header(REQUEST) + "\n".join(H) + self.sco_footer(REQUEST) + else: + return "\n".join(H) + + # -------------------------------------------------------------------- + # Certaines methodes devant etre appeles en dehors de ZNotes: + security.declareProtected(ScoView, "formsemestre_edit_uecoefs") + formsemestre_edit_uecoefs = sco_formsemestre_edit.formsemestre_edit_uecoefs + + +# +# Product Administration +# + + +def manage_addZScolar( + self, + id="id_ZScolar", + title="The Title for ZScolar Object", + db_cnx_string="the db connexion string", + REQUEST=None, +): + "Add a ZScolar instance to a folder." + zscolar = ZScolar(id, title, db_cnx_string=db_cnx_string) + self._setObject(id, zscolar) + + +# The form used to get the instance id from the user. +def manage_addZScolarForm(context, DeptId, REQUEST=None): + """Form used to create a new ZScolar instance""" + + if not re.match("^[a-zA-Z0-9_]+$", DeptId): + raise ScoValueError("Invalid department id: %s" % DeptId) + + H = [ + context.standard_html_header(context), + "

      Ajout d'un département ScoDoc

      ", + """

      Cette page doit être utilisée pour ajouter un nouveau + département au site.

      + +

      Avant d'ajouter le département, il faut impérativement + avoir préparé la base de données en lançant le script + create_dept.sh nom_du_site en tant que + root sur le serveur. +

      """, + ] + + descr = [ + ( + "db_cnx_string", + { + "title": "DB connexion string", + "size": 32, + "explanation": "laisser vide si BD locale standard", + }, + ), + ("pass2", {"input_type": "hidden", "default": "1"}), + ("DeptId", {"input_type": "hidden", "default": DeptId}), + ] + + tf = TrivialFormulator( + REQUEST.URL0, REQUEST.form, descr, submitlabel="Créer le site ScoDoc" + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + context.standard_html_footer(context) + elif tf[0] == -1: + return REQUEST.RESPONSE.redirect(REQUEST.URL1) + else: + DeptId = tf[2]["DeptId"].strip() + db_cnx_string = tf[2]["db_cnx_string"].strip() + # default connexion string + if not db_cnx_string: + db_name = "SCO" + DeptId.upper() + db_user = SCO_DEFAULT_SQL_USER + db_cnx_string = "user=%s dbname=%s port=%s" % ( + db_user, + db_name, + SCO_DEFAULT_SQL_PORT, + ) + # vérifie que la bd existe et possede le meme nom de dept. + try: + cnx = psycopg2.connect(db_cnx_string) + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute("select * from sco_prefs where name='DeptName'") + except: + return _simple_error_page( + context, "Echec de la connexion à la BD (%s)" % db_cnx_string, DeptId + ) + r = cursor.dictfetchall() + if not r: + return _simple_error_page( + context, "Pas de departement défini dans la BD", DeptId + ) + if r[0]["value"] != DeptId: + return _simple_error_page( + context, + "La BD ne correspond pas: nom departement='%s'" % r[0]["value"], + DeptId, + ) + # ok, crée instance ScoDoc: + manage_addZScolar( + context, + id="Scolarite", + title="ScoDoc for %s" % DeptId, + db_cnx_string=db_cnx_string, + ) + + return REQUEST.RESPONSE.redirect("index_html") + + +def _simple_error_page(context, msg, DeptId=None): + """Minimal error page (used by installer only). + """ + H = [context.standard_html_header(context), "

      Erreur !

      ", "

      ", msg, "

      "] + if DeptId: + H.append( + '

      Supprimer le dossier %s(très recommandé !)

      ' + % (DeptId, DeptId) + ) + H.append(context.standard_html_footer(context)) + return "\n".join(H) diff --git a/ZopeProducts/README b/ZopeProducts/README new file mode 100644 index 00000000..74226803 --- /dev/null +++ b/ZopeProducts/README @@ -0,0 +1,5 @@ + + Produits Zope2 anciens et adaptes pour ScoDoc + +E. Viennet 2013 + diff --git a/ZopeProducts/ZPsycopgDA/DA.py b/ZopeProducts/ZPsycopgDA/DA.py new file mode 100644 index 00000000..723a6880 --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/DA.py @@ -0,0 +1,372 @@ +# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection +# +# Copyright (C) 2004-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 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 Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. + + +import sys +import time +import db +import re + +import Acquisition +import Shared.DC.ZRDB.Connection + +from db import DB +from Globals import HTMLFile +from ExtensionClass import Base +from App.Dialogs import MessageDialog +from DateTime import DateTime + +# ImageFile is deprecated in Zope >= 2.9 +try: + from App.ImageFile import ImageFile +except ImportError: + # Zope < 2.9. If PIL's installed with a .pth file, we're probably + # hosed. + from ImageFile import ImageFile + +# import psycopg and functions/singletons needed for date/time conversions + +import psycopg2 +from psycopg2 import NUMBER, STRING, ROWID, DATETIME +from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE +from psycopg2.extensions import TIME, INTERVAL +from psycopg2.extensions import new_type, register_type + + +# add a new connection to a folder + +manage_addZPsycopgConnectionForm = HTMLFile('dtml/add',globals()) + +def manage_addZPsycopgConnection(self, id, title, connection_string, + zdatetime=None, tilevel=2, + encoding='', check=None, REQUEST=None): + """Add a DB connection to a folder.""" + self._setObject(id, Connection(id, title, connection_string, + zdatetime, check, tilevel, encoding)) + if REQUEST is not None: return self.manage_main(self, REQUEST) + + +# the connection object + +class Connection(Shared.DC.ZRDB.Connection.Connection): + """ZPsycopg Connection.""" + _isAnSQLConnection = 1 + + id = 'Psycopg2_database_connection' + database_type = 'Psycopg2' + meta_type = title = 'Z Psycopg 2 Database Connection' + icon = 'misc_/conn' + + def __init__(self, id, title, connection_string, + zdatetime, check=None, tilevel=2, encoding='UTF-8'): + self.zdatetime = zdatetime + self.id = str(id) + self.edit(title, connection_string, zdatetime, + check=check, tilevel=tilevel, encoding=encoding) + + def factory(self): + return DB + + ## connection parameters editing ## + + def edit(self, title, connection_string, + zdatetime, check=None, tilevel=2, encoding='UTF-8'): + self.title = title + self.connection_string = connection_string + self.zdatetime = zdatetime + self.tilevel = tilevel + self.encoding = encoding + + if check: self.connect(self.connection_string) + + manage_properties = HTMLFile('dtml/edit', globals()) + + def manage_edit(self, title, connection_string, + zdatetime=None, check=None, tilevel=2, encoding='UTF-8', + REQUEST=None): + """Edit the DB connection.""" + self.edit(title, connection_string, zdatetime, + check=check, tilevel=tilevel, encoding=encoding) + if REQUEST is not None: + msg = "Connection edited." + return self.manage_main(self,REQUEST,manage_tabs_message=msg) + + def connect(self, s): + try: + self._v_database_connection.close() + except: + pass + + # check psycopg version and raise exception if does not match + check_psycopg_version(psycopg2.__version__) + + self._v_connected = '' + dbf = self.factory() + + # TODO: let the psycopg exception propagate, or not? + self._v_database_connection = dbf( + self.connection_string, self.tilevel, self.get_type_casts(), self.encoding) + self._v_database_connection.open() + self._v_connected = DateTime() + + return self + + def get_type_casts(self): + # note that in both cases order *is* important + if self.zdatetime: + return ZDATETIME, ZDATE, ZTIME + else: + return DATETIME, DATE, TIME + + ## browsing and table/column management ## + + manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options + # + ( + # {'label': 'Browse', 'action':'manage_browse'},) + + #manage_tables = HTMLFile('dtml/tables', globals()) + #manage_browse = HTMLFile('dtml/browse', globals()) + + info = None + + def table_info(self): + return self._v_database_connection.table_info() + + + def __getitem__(self, name): + if name == 'tableNamed': + if not hasattr(self, '_v_tables'): self.tpValues() + return self._v_tables.__of__(self) + raise KeyError, name + + def tpValues(self): + res = [] + conn = self._v_database_connection + for d in conn.tables(rdb=0): + try: + name = d['TABLE_NAME'] + b = TableBrowser() + b.__name__ = name + b._d = d + b._c = c + try: + b.icon = table_icons[d['TABLE_TYPE']] + except: + pass + r.append(b) + except: + pass + return res + +def check_psycopg_version(version): + """ + Check that the psycopg version used is compatible with the zope adpter. + """ + try: + m = re.match(r'\d+\.\d+(\.\d+)?', version.split(' ')[0]) + tver = tuple(map(int, m.group().split('.'))) + except: + raise ImportError("failed to parse psycopg version %s" % version) + + if tver < (2, 4): + raise ImportError("psycopg version %s is too old" % version) + + if tver in ((2,4,2), (2,4,3)): + raise ImportError("psycopg version %s is known to be buggy" % version) + + +## database connection registration data ## + +classes = (Connection,) + +meta_types = ({'name':'Z Psycopg 2 Database Connection', + 'action':'manage_addZPsycopgConnectionForm'},) + +folder_methods = { + 'manage_addZPsycopgConnection': manage_addZPsycopgConnection, + 'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm} + +__ac_permissions__ = ( + ('Add Z Psycopg Database Connections', + ('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),) + +# add icons + +misc_={'conn': ImageFile('icons/DBAdapterFolder_icon.gif', globals())} + +for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin', + 'int', 'float', 'date', 'time', 'datetime'): + misc_[icon] = ImageFile('icons/%s.gif' % icon, globals()) + + +## zope-specific psycopg typecasters ## + +# convert an ISO timestamp string from postgres to a Zope DateTime object +def _cast_DateTime(iso, curs): + if iso: + if iso in ['-infinity', 'infinity']: + return iso + else: + return DateTime(iso) + +# convert an ISO date string from postgres to a Zope DateTime object +def _cast_Date(iso, curs): + if iso: + if iso in ['-infinity', 'infinity']: + return iso + else: + return DateTime(iso) + +# Convert a time string from postgres to a Zope DateTime object. +# NOTE: we set the day as today before feeding to DateTime so +# that it has the same DST settings. +def _cast_Time(iso, curs): + if iso: + if iso in ['-infinity', 'infinity']: + return iso + else: + return DateTime(time.strftime('%Y-%m-%d %H:%M:%S', + time.localtime(time.time())[:3]+ + time.strptime(iso[:8], "%H:%M:%S")[3:])) + +# NOTE: we don't cast intervals anymore because they are passed +# untouched to Zope. +def _cast_Interval(iso, curs): + return iso + +ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime) +ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval) +ZDATE = new_type((1082,), "ZDATE", _cast_Date) +ZTIME = new_type((1083,), "ZTIME", _cast_Time) + + +## table browsing helpers ## + +class TableBrowserCollection(Acquisition.Implicit): + pass + +class Browser(Base): + def __getattr__(self, name): + try: + return self._d[name] + except KeyError: + raise AttributeError, name + +class values: + def len(self): + return 1 + + def __getitem__(self, i): + try: + return self._d[i] + except AttributeError: + pass + self._d = self._f() + return self._d[i] + +class TableBrowser(Browser, Acquisition.Implicit): + icon = 'what' + Description = check = '' + info = HTMLFile('table_info', globals()) + menu = HTMLFile('table_menu', globals()) + + def tpValues(self): + v = values() + v._f = self.tpValues_ + return v + + def tpValues_(self): + r=[] + tname=self.__name__ + for d in self._c.columns(tname): + b=ColumnBrowser() + b._d=d + try: b.icon=field_icons[d['Type']] + except: pass + b.TABLE_NAME=tname + r.append(b) + return r + + def tpId(self): return self._d['TABLE_NAME'] + def tpURL(self): return "Table/%s" % self._d['TABLE_NAME'] + def Name(self): return self._d['TABLE_NAME'] + def Type(self): return self._d['TABLE_TYPE'] + + manage_designInput=HTMLFile('designInput',globals()) + def manage_buildInput(self, id, source, default, REQUEST=None): + "Create a database method for an input form" + args=[] + values=[] + names=[] + columns=self._columns + for i in range(len(source)): + s=source[i] + if s=='Null': continue + c=columns[i] + d=default[i] + t=c['Type'] + n=c['Name'] + names.append(n) + if s=='Argument': + values.append("'" % + (n, vartype(t))) + a='%s%s' % (n, boboType(t)) + if d: a="%s=%s" % (a,d) + args.append(a) + elif s=='Property': + values.append("'" % + (n, vartype(t))) + else: + if isStringType(t): + if find(d,"\'") >= 0: d=join(split(d,"\'"),"''") + values.append("'%s'" % d) + elif d: + values.append(str(d)) + else: + raise ValueError, ( + 'no default was given for %s' % n) + +class ColumnBrowser(Browser): + icon='field' + + def check(self): + return ('\t' % + (self.TABLE_NAME, self._d['Name'])) + def tpId(self): return self._d['Name'] + def tpURL(self): return "Column/%s" % self._d['Name'] + def Description(self): + d=self._d + if d['Scale']: + return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d + else: + return " %(Type)s(%(Precision)s) %(Nullable)s" % d + +table_icons={ + 'TABLE': 'table', + 'VIEW':'view', + 'SYSTEM_TABLE': 'stable', + } + +field_icons={ + NUMBER.name: 'i', + STRING.name: 'text', + DATETIME.name: 'date', + INTEGER.name: 'int', + FLOAT.name: 'float', + BOOLEAN.name: 'bin', + ROWID.name: 'int' + } diff --git a/ZopeProducts/ZPsycopgDA/__init__.py b/ZopeProducts/ZPsycopgDA/__init__.py new file mode 100644 index 00000000..b501cbfd --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/__init__.py @@ -0,0 +1,29 @@ +# ZPsycopgDA/__init__.py - ZPsycopgDA Zope product +# +# Copyright (C) 2004-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 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 Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. + +__doc__ = "ZPsycopg Database Adapter Registration." +__version__ = '2.4.6' + +import DA + +def initialize(context): + context.registerClass( + DA.Connection, + permission = 'Add Z Psycopg 2 Database Connections', + constructors = (DA.manage_addZPsycopgConnectionForm, + DA.manage_addZPsycopgConnection), + icon = 'icons/DBAdapterFolder_icon.gif') diff --git a/ZopeProducts/ZPsycopgDA/db.py b/ZopeProducts/ZPsycopgDA/db.py new file mode 100644 index 00000000..a272c02d --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/db.py @@ -0,0 +1,209 @@ +# ZPsycopgDA/db.py - query execution +# +# Copyright (C) 2004-2010 Federico Di Gregorio +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 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 Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. + +from Shared.DC.ZRDB.TM import TM +from Shared.DC.ZRDB import dbi_db + +from ZODB.POSException import ConflictError + +import site +import pool + +import psycopg2 +from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE, TIME +from psycopg2.extensions import TransactionRollbackError, register_type +from psycopg2 import NUMBER, STRING, ROWID, DATETIME + + +# the DB object, managing all the real query work + +class DB(TM, dbi_db.DB): + + _p_oid = _p_changed = _registered = None + + def __init__(self, dsn, tilevel, typecasts, enc='utf-8'): + self.dsn = dsn + self.tilevel = tilevel + self.typecasts = typecasts + if enc is None or enc == "": + self.encoding = "utf-8" + else: + self.encoding = enc + self.failures = 0 + self.calls = 0 + self.make_mappings() + + def getconn(self, init=True): + # if init is False we are trying to get hold on an already existing + # connection, so we avoid to (re)initialize it risking errors. + conn = pool.getconn(self.dsn) + if init: + # use set_session where available as in these versions + # set_isolation_level generates an extra query. + if psycopg2.__version__ >= '2.4.2': + conn.set_session(isolation_level=int(self.tilevel)) + else: + conn.set_isolation_level(int(self.tilevel)) + conn.set_client_encoding(self.encoding) + for tc in self.typecasts: + register_type(tc, conn) + return conn + + def putconn(self, close=False): + try: + conn = pool.getconn(self.dsn, False) + except AttributeError: + pass + pool.putconn(self.dsn, conn, close) + + def getcursor(self): + conn = self.getconn(False) + return conn.cursor() + + def _finish(self, *ignored): + try: + conn = self.getconn(False) + conn.commit() + self.putconn() + except AttributeError: + pass + + def _abort(self, *ignored): + try: + conn = self.getconn(False) + conn.rollback() + self.putconn() + except AttributeError: + pass + + def open(self): + # this will create a new pool for our DSN if not already existing, + # then get and immediately release a connection + self.getconn() + self.putconn() + + def close(self): + # FIXME: if this connection is closed we flush all the pool associated + # with the current DSN; does this makes sense? + pool.flushpool(self.dsn) + + def sortKey(self): + return 1 + + def make_mappings(self): + """Generate the mappings used later by self.convert_description().""" + self.type_mappings = {} + for t, s in [(INTEGER,'i'), (LONGINTEGER, 'i'), (NUMBER, 'n'), + (BOOLEAN,'n'), (ROWID, 'i'), + (DATETIME, 'd'), (DATE, 'd'), (TIME, 'd')]: + for v in t.values: + self.type_mappings[v] = (t, s) + + def convert_description(self, desc, use_psycopg_types=False): + """Convert DBAPI-2.0 description field to Zope format.""" + items = [] + for name, typ, width, ds, p, scale, null_ok in desc: + m = self.type_mappings.get(typ, (STRING, 's')) + items.append({ + 'name': name, + 'type': use_psycopg_types and m[0] or m[1], + 'width': width, + 'precision': p, + 'scale': scale, + 'null': null_ok, + }) + return items + + ## tables and rows ## + + def tables(self, rdb=0, _care=('TABLE', 'VIEW')): + self._register() + c = self.getcursor() + c.execute( + "SELECT t.tablename AS NAME, 'TABLE' AS TYPE " + " FROM pg_tables t WHERE tableowner <> 'postgres' " + "UNION SELECT v.viewname AS NAME, 'VIEW' AS TYPE " + " FROM pg_views v WHERE viewowner <> 'postgres' " + "UNION SELECT t.tablename AS NAME, 'SYSTEM_TABLE\' AS TYPE " + " FROM pg_tables t WHERE tableowner = 'postgres' " + "UNION SELECT v.viewname AS NAME, 'SYSTEM_TABLE' AS TYPE " + "FROM pg_views v WHERE viewowner = 'postgres'") + res = [] + for name, typ in c.fetchall(): + if typ in _care: + res.append({'TABLE_NAME': name, 'TABLE_TYPE': typ}) + self.putconn() + return res + + def columns(self, table_name): + self._register() + c = self.getcursor() + try: + r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name) + except: + return () + self.putconn() + return self.convert_description(c.description, True) + + ## query execution ## + + def query(self, query_string, max_rows=None, query_data=None): + self._register() + self.calls = self.calls+1 + + desc = () + res = [] + nselects = 0 + + c = self.getcursor() + + try: + for qs in [x for x in query_string.split('\0') if x]: + try: + if query_data: + c.execute(qs, query_data) + else: + c.execute(qs) + except TransactionRollbackError: + # Ha, here we have to look like we are the ZODB raising conflict errrors, raising ZPublisher.Publish.Retry just doesn't work + #logging.debug("Serialization Error, retrying transaction", exc_info=True) + raise ConflictError("TransactionRollbackError from psycopg2") + except psycopg2.OperationalError: + #logging.exception("Operational error on connection, closing it.") + try: + # Only close our connection + self.putconn(True) + except: + #logging.debug("Something went wrong when we tried to close the pool", exc_info=True) + pass + if c.description is not None: + nselects += 1 + if c.description != desc and nselects > 1: + raise psycopg2.ProgrammingError( + 'multiple selects in single query not allowed') + if max_rows: + res = c.fetchmany(max_rows) + else: + res = c.fetchall() + desc = c.description + self.failures = 0 + + except StandardError, err: + self._abort() + raise err + + return self.convert_description(desc), res diff --git a/ZopeProducts/ZPsycopgDA/dtml/add.dtml b/ZopeProducts/ZPsycopgDA/dtml/add.dtml new file mode 100644 index 00000000..330a001b --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/dtml/add.dtml @@ -0,0 +1,108 @@ + + + + +

      +A Zope Psycopg 2 Database Connection is used to connect and execute +queries on a PostgreSQL database. +

      + +

      +In the form below Connection String (also called the Data Source Name +or DSN for short) is a string... (TODO: finish docs) +

      + +
      +
    5. AttributTypeDescription
      %s%s%s%s
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + Id +
      +
      + +
      +
      + Title +
      +
      + +
      +
      + Connection string +
      +
      + +
      +
      + Connect immediately +
      +
      + +
      +
      + Use Zope's internal DateTime +
      +
      + +
      +
      + Transaction isolation level +
      +
      + +
      +
      + Encoding +
      +
      + +
      +
      + +
      +
      + + + diff --git a/ZopeProducts/ZPsycopgDA/dtml/browse.dtml b/ZopeProducts/ZPsycopgDA/dtml/browse.dtml new file mode 100644 index 00000000..deffd0ab --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/dtml/browse.dtml @@ -0,0 +1,11 @@ + + <dtml-var title_or_id >tables + + + + <dtml-var Type> + + + + diff --git a/ZopeProducts/ZPsycopgDA/dtml/edit.dtml b/ZopeProducts/ZPsycopgDA/dtml/edit.dtml new file mode 100644 index 00000000..cffb43bf --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/dtml/edit.dtml @@ -0,0 +1,84 @@ + + + +

      + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + Title +
      +
      + +
      +
      + Connection string +
      +
      + +
      +
      + Use Zope's internal DateTime +
      +
      + checked="YES" /> +
      +
      + Transaction isolation level +
      +
      + +
      +
      + Encoding +
      +
      + +
      +
      + +
      +
      +
      + + diff --git a/ZopeProducts/ZPsycopgDA/dtml/table_info.dtml b/ZopeProducts/ZPsycopgDA/dtml/table_info.dtml new file mode 100644 index 00000000..639c23fd --- /dev/null +++ b/ZopeProducts/ZPsycopgDA/dtml/table_info.dtml @@ -0,0 +1,7 @@ + + + + owned by +
      + + diff --git a/ZopeProducts/ZPsycopgDA/icons/DBAdapterFolder_icon.gif b/ZopeProducts/ZPsycopgDA/icons/DBAdapterFolder_icon.gif new file mode 100755 index 0000000000000000000000000000000000000000..ced0ef26a54d7150fe6f0589e561f8854eb2960a GIT binary patch literal 897 zcmZ?wbhEHb6krfw_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?mZh21X7Ui3JM|HgfPxL&3qNpUqTn%Z-o8?p@0ARc9)MR6Toiwc~VV3NCG)Zjx*^M>BAV4}+rU bE|C|PpZP6tYZubZxUA^3z)_Hmg~1vCl&?8A literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/bin.gif b/ZopeProducts/ZPsycopgDA/icons/bin.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4691265687b23c83b3032da963526c0b3a8e526 GIT binary patch literal 924 zcmZ?wbhEHbRA3Nb_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vf!21X7Uj|~eBHnR!}M68(b(20pl(XK~A zaM6J-e&eh=I|L6N?NpHMnqqND>Cj{it{|4q1*smLy4qDN8;VkVCt3I=JvlM?`I#vW z{>NmzwyX$TB(J`!hO;p9kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vgP21X7Uj|~eBHnR!}I4qcO;7}XCv{8x2 z#6?alOp0N5PHbF!w1->POC-}U#kE6Ra8*p@hevL`a-Nfpa2#0b+szYOkRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vfU21X7Uj|~eBHnR!}C^R@cbZX-hH|yaD zT;#;Uq-l5O#KvUz9tIh+Co2kGduN%ZT-=-?&BVdM GU=0Ad>`i|F literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/field.gif b/ZopeProducts/ZPsycopgDA/icons/field.gif new file mode 100644 index 0000000000000000000000000000000000000000..9bf8692be6c734783164f7c8df4a99c7696c6635 GIT binary patch literal 915 zcmZ?wbhEHbRA3Nb_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?ve>21X7Uj|~eBHnR!}M4Xtg@K77StXWKl zL&A|x3FEXi91j*9>z5YnlBv9q?9!^vxn$0Xi^{Ik44lhYJcHFv&Q=n9ap&ab<>%*H v=_cKI8IT@0U8^une6?JLt4=KqQ4PCmwN?4T`h;dS76xkoP%cQ! literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/float.gif b/ZopeProducts/ZPsycopgDA/icons/float.gif new file mode 100644 index 0000000000000000000000000000000000000000..dd427299369f706417724103e20b3b5c89480ffd GIT binary patch literal 929 zcmZ?wbhEHbRA3Nb_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vfk21X7Uj|~eBHnR!}M64)S=*+|=>(-(n zxadG9zj4-`6CaXX+a#6!L^1^zo}A1ksub~aqSDD}TDDC!9fixz&S1zYx+5vH)PIV0 zGncH_mW+UzBC4BGc1(E|#Ntt!Boir^b#7Y3?0Y&tFQs|R3SF$lb!yAP6U)VWSHkRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vg121X7Uj|~eBHnR!}M4Xt=aHx%2-e`(N zpn_|MgmIEfg5lyL-SXy2GM=9lJXjQ!t>$P59dMn>6~C_K#Kon@W?RTkyAvt2^!$8> yX0ER{3zL1O2v6KL<>ZuSLH%x%t8`vXQ4N_DlC1VsGEg&Yy7S>KS55^525SI%rbtNu literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/stable.gif b/ZopeProducts/ZPsycopgDA/icons/stable.gif new file mode 100644 index 0000000000000000000000000000000000000000..acdd37df61c44de3b08fa95c8302e6702a423da5 GIT binary patch literal 884 zcmZ?wbhEHb6krfw_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vfq21X7Uj|~b8%p5{mF*`0WIJF4~TlGj3 zD!I0a>1&-?A;{?7%jeuBqbbD5$fOoH&!@BK(W&WL;k$ZfPFm{TVvs#Ar*pEJ&pfNz NQ!1V&DGm$_)&Ox}G%ElA literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/table.gif b/ZopeProducts/ZPsycopgDA/icons/table.gif new file mode 100644 index 0000000000000000000000000000000000000000..cce83beaf96b7d4e6147e7c6a18c975c541df349 GIT binary patch literal 878 zcmZ?wbhEHb6krfw_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?q{u21X7Uj|~ePSXej|LS_^!Jk-pkY?Q-M z_{gc9gC|R;B2ejAm#n%Si-6IS6TQ-cD`HMgdU|l0LFm4on?-78r<&!bsjL)MKR-{B InTf#~0OKh&NdN!< literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/text.gif b/ZopeProducts/ZPsycopgDA/icons/text.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2e5aab6f29769c52dff27f822fc9da76f0be924 GIT binary patch literal 918 zcmZ?wbhEHbRA3Nb_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vg121X7Uj|~eBHnR!}M4S+4Jlx7B?=G`RG y&0MlxAuIf5SWJ{FJ-PXD&;(^sH`|{jtFF#9^1sG&YeJGAgX`h0UJeBX25SJ2OGq*R literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/time.gif b/ZopeProducts/ZPsycopgDA/icons/time.gif new file mode 100644 index 0000000000000000000000000000000000000000..6d089150008c5f5e5d35771e238266d2e0f37cc5 GIT binary patch literal 926 zcmZ?wbhEHbRA3Nb_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vg921X7Uj|~eBHnR!}M4SkC=+w@`snNm_ zxadHapmElnA0HOE^$1C~)kp|39-6GdALer4(_)VkRPi6k zYyb-U2XaQiXb23W5K#Qd!U*z?4u}Bd2?vfS21X7Uj|~b8>?}e`DjOywI@28Bd|4!>#Jt%g($=P-b8;NIuo#QLyZ6GhflGJ1-T~{N~xz XGO=ovyg0YWtysus)#L;R1_o;Yx92&e literal 0 HcmV?d00001 diff --git a/ZopeProducts/ZPsycopgDA/icons/what.gif b/ZopeProducts/ZPsycopgDA/icons/what.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b5516e397d2684c0ee4628ddd2730964e07bee0 GIT binary patch literal 894 zcmZ?wbhEHb6krfw_|Cxa9|##30s;aW8X689H~{1{FgP$UEMRbGXkch?U~u>kRPi6k zYyb-U2XaQiXb23W5K#Qd!pOkD%Af=C6DUtOa6~gOa>z(5Sa7hBMOGsu +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# psycopg2 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 Lesser General Public +# License for more details. + +# Import modules needed by _psycopg to allow tools like py2exe to do +# their work without bothering about the module dependencies. + +# All the connections are held in a pool of pools, directly accessible by the +# ZPsycopgDA code in db.py. + +import threading +import psycopg2 +from psycopg2.pool import PoolError + + +class AbstractConnectionPool(object): + """Generic key-based pooling code.""" + + def __init__(self, minconn, maxconn, *args, **kwargs): + """Initialize the connection pool. + + New 'minconn' connections are created immediately calling 'connfunc' + with given parameters. The connection pool will support a maximum of + about 'maxconn' connections. + """ + self.minconn = minconn + self.maxconn = maxconn + self.closed = False + + self._args = args + self._kwargs = kwargs + + self._pool = [] + self._used = {} + self._rused = {} # id(conn) -> key map + self._keys = 0 + + for i in range(self.minconn): + self._connect() + + def _connect(self, key=None): + """Create a new connection and assign it to 'key' if not None.""" + conn = psycopg2.connect(*self._args, **self._kwargs) + if key is not None: + self._used[key] = conn + self._rused[id(conn)] = key + else: + self._pool.append(conn) + return conn + + def _getkey(self): + """Return a new unique key.""" + self._keys += 1 + return self._keys + + def _getconn(self, key=None): + """Get a free connection and assign it to 'key' if not None.""" + if self.closed: raise PoolError("connection pool is closed") + if key is None: key = self._getkey() + + if key in self._used: + return self._used[key] + + if self._pool: + self._used[key] = conn = self._pool.pop() + self._rused[id(conn)] = key + return conn + else: + if len(self._used) == self.maxconn: + raise PoolError("connection pool exausted") + return self._connect(key) + + def _putconn(self, conn, key=None, close=False): + """Put away a connection.""" + if self.closed: raise PoolError("connection pool is closed") + if key is None: key = self._rused[id(conn)] + + if not key: + raise PoolError("trying to put unkeyed connection") + + if len(self._pool) < self.minconn and not close: + self._pool.append(conn) + else: + conn.close() + + # here we check for the presence of key because it can happen that a + # thread tries to put back a connection after a call to close + if not self.closed or key in self._used: + del self._used[key] + del self._rused[id(conn)] + + def _closeall(self): + """Close all connections. + + Note that this can lead to some code fail badly when trying to use + an already closed connection. If you call .closeall() make sure + your code can deal with it. + """ + if self.closed: raise PoolError("connection pool is closed") + for conn in self._pool + list(self._used.values()): + try: + conn.close() + except: + pass + self.closed = True + + +class PersistentConnectionPool(AbstractConnectionPool): + """A pool that assigns persistent connections to different threads. + + Note that this connection pool generates by itself the required keys + using the current thread id. This means that until a thread puts away + a connection it will always get the same connection object by successive + `!getconn()` calls. This also means that a thread can't use more than one + single connection from the pool. + """ + + def __init__(self, minconn, maxconn, *args, **kwargs): + """Initialize the threading lock.""" + import threading + AbstractConnectionPool.__init__( + self, minconn, maxconn, *args, **kwargs) + self._lock = threading.Lock() + + # we we'll need the thread module, to determine thread ids, so we + # import it here and copy it in an instance variable + import thread + self.__thread = thread + + def getconn(self): + """Generate thread id and return a connection.""" + key = self.__thread.get_ident() + self._lock.acquire() + try: + return self._getconn(key) + finally: + self._lock.release() + + def putconn(self, conn=None, close=False): + """Put away an unused connection.""" + key = self.__thread.get_ident() + self._lock.acquire() + try: + if not conn: conn = self._used[key] + self._putconn(conn, key, close) + finally: + self._lock.release() + + def closeall(self): + """Close all connections (even the one currently in use.)""" + self._lock.acquire() + try: + self._closeall() + finally: + self._lock.release() + + +_connections_pool = {} +_connections_lock = threading.Lock() + +def getpool(dsn, create=True): + _connections_lock.acquire() + try: + if not _connections_pool.has_key(dsn) and create: + _connections_pool[dsn] = \ + PersistentConnectionPool(4, 200, dsn) + finally: + _connections_lock.release() + return _connections_pool[dsn] + +def flushpool(dsn): + _connections_lock.acquire() + try: + _connections_pool[dsn].closeall() + del _connections_pool[dsn] + finally: + _connections_lock.release() + +def getconn(dsn, create=True): + return getpool(dsn, create=create).getconn() + +def putconn(dsn, conn, close=False): + getpool(dsn).putconn(conn, close=close) diff --git a/ZopeProducts/exUserFolder/AuthSources/__init__.py b/ZopeProducts/exUserFolder/AuthSources/__init__.py new file mode 100644 index 00000000..dedaed0a --- /dev/null +++ b/ZopeProducts/exUserFolder/AuthSources/__init__.py @@ -0,0 +1,47 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: __init__.py,v 1.1 2004/11/10 14:15:34 akm Exp $ + +#import etcAuthSource +#import httpsAuthSource +#import mysqlAuthSource +import pgAuthSource +#import pgAuthSourceAlt +#import radiusAuthSource +#import smbAuthSource +#import usAuthSource +#import zodbAuthSource +#import zodbBTreeAuthSource + +# +# These have special requirements for external libraries +# that my not be present. +# + +# try: +# import nisAuthSource +# except: +# pass + +# try: +# import LDAPAuthSource +# except: +# pass diff --git a/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/.cvsignore b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/__init__.py b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/__init__.py new file mode 100644 index 00000000..a23a103c --- /dev/null +++ b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2004/11/10 14:15:36 akm Exp $ +import pgAuthSource diff --git a/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_addpgAuthSourceForm.dtml b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_addpgAuthSourceForm.dtml new file mode 100644 index 00000000..ff0790c3 --- /dev/null +++ b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_addpgAuthSourceForm.dtml @@ -0,0 +1,40 @@ + +
      + +"> + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Database Connection: + +
      Table Name:
      Username Column:
      Password Column:
      Roles Column:

      Add">
      +
      + diff --git a/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_editpgAuthSourceForm.dtml b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_editpgAuthSourceForm.dtml new file mode 100644 index 00000000..7cd811cf --- /dev/null +++ b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/manage_editpgAuthSourceForm.dtml @@ -0,0 +1,37 @@ + + +
      + + + + + + + + + + + + + + + + + + + + + + + + +
      Database Connection: + +
      Table Name:">
      Username Column:">
      Password Column:">
      Roles Column:">

      Edit ">
      +
      + diff --git a/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/pgAuthSource.py b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/pgAuthSource.py new file mode 100644 index 00000000..bffe3b71 --- /dev/null +++ b/ZopeProducts/exUserFolder/AuthSources/pgAuthSource/pgAuthSource.py @@ -0,0 +1,333 @@ +# +# Extensible User Folder +# +# Postgres Authentication Source for exUserFolder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: pgAuthSource.py,v 1.1 2004/11/10 14:15:36 akm Exp $ + +# +# This class only authenticates users, it stores no properties. +# + +import string,Acquisition + +from Globals import HTMLFile, MessageDialog, INSTANCE_HOME + +from OFS.Folder import Folder + +from Products.ZSQLMethods.SQL import SQL + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister + +try: + from crypt import crypt +except: + from Products.exUserFolder.fcrypt.fcrypt import crypt + +# debug XXX +# def xLOG(msg): +# f = open('/tmp/debug.log','a') +# f.write(msg+'\n') +# f.close() + +def manage_addpgAuthSource(self, REQUEST): + """ Add a Postgres Auth Source """ + + connection=REQUEST['pgauth_connection'] + table=REQUEST['pgauth_table'] + usernameColumn=REQUEST['pgauth_usernameColumn'] + passwordColumn=REQUEST['pgauth_passwordColumn'] + rolesColumn=REQUEST['pgauth_rolesColumn'] + o = pgAuthSource(connection, table, usernameColumn, passwordColumn, + rolesColumn) + self._setObject('pgAuthSource', o, None, None, 0) + o=getattr(self,'pgAuthSource') + if hasattr(o, 'postInitialisation'): + o.postInitialisation(REQUEST) + + self.currentAuthSource=o + return '' + +manage_addpgAuthSourceForm=HTMLFile('manage_addpgAuthSourceForm', globals()) +manage_editpgAuthSourceForm=HTMLFile('manage_editpgAuthSourceForm', globals()) + +class pgAuthSource(Folder): + """ Authenticate Users against a Postgres Database """ + + meta_type='Authentication Source' + title='Postgresql Authentication' + + icon ='misc_/exUserFolder/exUserFolderPlugin.gif' + + manage_tabs=Acquisition.Acquired + + manage_editForm=manage_editpgAuthSourceForm + + # + # You can define this to go off and do the authentication instead of + # using the basic one inside the User Object + # + remoteAuthMethod=None + + def __init__(self, connection, table, usernameColumn, passwordColumn, + rolesColumn): + self.id='pgAuthSource' + self.connection=connection + self.table=table + self.usernameColumn=usernameColumn + self.passwordColumn=passwordColumn + self.rolesColumn=rolesColumn + self.addSQLQueries() + + def manage_editAuthSource(self, REQUEST): + """ Edit a Postgres Auth Source """ + + self.connection=REQUEST['pgauth_connection'] + self.table=REQUEST['pgauth_table'] + self.usernameColumn=REQUEST['pgauth_usernameColumn'] + self.passwordColumn=REQUEST['pgauth_passwordColumn'] + self.rolesColumn=REQUEST['pgauth_rolesColumn'] + self.delSQLQueries() + self.addSQLQueries() # Re-add queries with new parameters + + def createUser(self, username, password, roles): + """ Add A Username """ + + if type(roles) != type([]): + if roles: + roles=list(roles) + else: + roles=[] + + rolestring='' + for role in roles: + rolestring=rolestring+role+',' + + rolestring=rolestring[:-1] + secret=self.cryptPassword(username, password) + self.sqlInsertUser(username=username, + password=secret, + roles=rolestring) + self._v_lastUser={} + + def updateUser(self, username, password, roles): + if type(roles) != type([]): + if roles: + roles=list(roles) + else: + roles=[] + + rolestring='' + for role in roles: + print role + rolestring=rolestring+role+',' + + rolestring=rolestring[:-1] + + # Don't change passwords if it's null + if password: + secret=self.cryptPassword(username, password) + self.sqlUpdateUserPassword(username=username, + password=secret) + + self.sqlUpdateUser(username=username, + roles=rolestring) + self._v_lastUser={} + + def delSQLQueries(self): + sqllist=self.objectIds('Z SQL Method') + self.manage_delObjects(ids=sqllist) + + def addSQLQueries(self): + sqlListUsers=SQL( + 'sqlListUsers', + 'List All Users', + self.connection, + 'table=%s'%(self.table), + _sqlListUsers) + + self._setObject('sqlListUsers', sqlListUsers) + + sqlListOneUser=SQL( + 'sqlListOneUser', + 'List ONE User', + self.connection, + 'table=%s usernameColumn=%s username:string'%( + self.table, self.usernameColumn), + _sqlListOneUser) + + self._setObject('sqlListOneUser', sqlListOneUser) + + sqlDeleteOneUser=SQL( + 'sqlDeleteOneUser', + 'Delete One User', + self.connection, + 'table=%s usernameColumn=%s username:string'%( + self.table,self.usernameColumn), + _sqlDeleteOneUser) + + self._setObject('sqlDeleteOneUser', sqlDeleteOneUser) + + sqlInsertUser=SQL( + 'sqlInsertUser', + 'Insert One User', + self.connection, + 'table=%s usernameColumn=%s passwordColumn=%s rolesColumn=%s username:string password:string roles:string'%( + self.table, self.usernameColumn, self.passwordColumn, self.rolesColumn), + _sqlInsertUser) + + self._setObject('sqlInsertUser', sqlInsertUser) + + sqlUpdateUser=SQL( + 'sqlUpdateUser', + 'Update User', + self.connection, + 'table=%s rolesColumn=%s username:string roles:string'%(self.table, self.rolesColumn), + _sqlUpdateUser) + + self._setObject('sqlUpdateUser', sqlUpdateUser) + + sqlUpdateUserPassword=SQL( + 'sqlUpdateUserPassword', + 'Update just the password', + self.connection, + 'table=%s usernameColumn=%s passwordColumn=%s username:string password:string'%(self.table, self.usernameColumn, self.passwordColumn), + _sqlUpdateUserPassword) + + self._setObject('sqlUpdateUserPassword', sqlUpdateUserPassword) + + def cryptPassword_old(self, username, password): + salt =username[:2] + secret = crypt(password, salt) + return secret + + def deleteUsers(self, userids): + for uid in userids: + self.sqlDeleteOneUser(username=uid) + self._v_lastUser={} + + def listUserNames(self): + """Returns a real list of user names """ + users = [] + result=self.sqlListUsers() + for n in result: + username=sqlattr(n,self.usernameColumn) + users.append(username) + return users + + def listUsers(self): + """Returns a list of user names or [] if no users exist""" + users = [] + result=self.sqlListUsers() + for n in result: + roles=[] + username=sqlattr(n,self.usernameColumn) + if sqlattr(n, self.rolesColumn): + roles=string.split(sqlattr(n,self.rolesColumn),',') + password=sqlattr(n, self.passwordColumn) + N={'username':username, 'password':password, 'roles':roles} + users.append(N) + return users + + def listOneUser(self,username): + #xLOG('pg.listOneUser(%s)' % username) + if getattr(self, '_v_lastUser', {}): + if self._v_lastUser['username']==username: + return self._v_lastUser['users'] + #xLOG('pg.listOneUser continuing') + users = [] + result=self.sqlListOneUser(username=username) + #xLOG('pg.listOneUser result=%s' % result) + for n in result: + roles=[] + username=sqlattr(n,self.usernameColumn) + password=sqlattr(n,self.passwordColumn) + if sqlattr(n, self.rolesColumn): + roles=string.split(sqlattr(n,self.rolesColumn),',') #Andreas + N={'username':username, 'password':password, 'roles':roles} + users.append(N) + self._v_lastUser={} + self._v_lastUser['username']=username + self._v_lastUser['users']=users + return users + + def postInitialisation(self, REQUEST): + self._v_lastUser={} + +pgAuthReg=PluginRegister('pgAuthSource', 'Postgresql Authentication Source', + pgAuthSource, manage_addpgAuthSourceForm, + manage_addpgAuthSource, + manage_editpgAuthSourceForm) +exUserFolder.authSources['pgAuthSource']=pgAuthReg + +from string import upper, lower +import Missing +mt=type(Missing.Value) + +def typeconv(val): + if type(val)==mt: + return '' + return val + +def sqlattr(ob, attr): + name=attr + if hasattr(ob, attr): + return typeconv(getattr(ob, attr)) + attr=upper(attr) + if hasattr(ob, attr): + return typeconv(getattr(ob, attr)) + attr=lower(attr) + if hasattr(ob, attr): + return typeconv(getattr(ob, attr)) + raise NameError, name + + + +_sqlListUsers=""" +SELECT * FROM +""" + +_sqlListOneUser=""" +SELECT * FROM +where = +""" + +_sqlDeleteOneUser=""" +DELETE FROM +where = +""" + +_sqlInsertUser=""" +INSERT INTO (, , ) +VALUES (, + , + ) +""" + +_sqlUpdateUserPassword=""" +UPDATE set = +WHERE = +""" + +_sqlUpdateUser=""" +UPDATE set = +WHERE = +""" diff --git a/ZopeProducts/exUserFolder/CHANGES.txt b/ZopeProducts/exUserFolder/CHANGES.txt new file mode 100644 index 00000000..6271ba5e --- /dev/null +++ b/ZopeProducts/exUserFolder/CHANGES.txt @@ -0,0 +1,865 @@ +Changes for 0.50.1 + +Add a README.Upgrading file to explain the impact of the 0.50.0 source +restructure, since people don't seem to be reading this file. --akm + +Fix the default docLogin to use &dtml-URL as the default destination. +I porked the fcrypt import. It obviously doesn't get imported here since +I have a crypt module installed. -- akm + +Fixed; https://sourceforge.net/tracker/?func=detail&aid=1084903&group_id=36318&atid=416446 +thanks to vigine -- akm + +Changes for 0.50.0 + +Restructured Source Tree. This will make this version incompatible with +previous versions, as the classes have moved. This breaks upgrading existing +installs unless you keep the old classes around. If you only use external +Auth/Prop/Group sources, you will probably be unaffected. + +o Auth Sources moved to single directory +o Prop Sources moved to single directory +o Group Sources moved to single directory +o Docs moved to doc directory +--akm + +Added Pluggable Crypto methods. Any authSource that contains a +cryptPassword method, will have it's method called, otherwise the +method selected by the user is called. --akm + +Removed the cryptPassword method from existing Auth Sources. --akm + +docLoginRedirect is no longer used. --akm + +Changes for 0.20.2 +BLAH! I missed some LDAP changes! --akm + +Changes for 0.20.1 + +Fix import problem for pgPropSource --akm +Add performance boost to pgAuthSource and pgPropSource --akm +Make zodbAuthSource.listUsernames return a list. --akm +Update some LDAP Auth source bugs. --akm +Change references to "Authorisation" to "Authentication" since XUF +auth sources authenticate, they don't authorise. --akm +Changed the

      tags to tags in the manage_adds. + +Changes for 0.20.0 + +Fix: +https://sourceforge.net/tracker/index.php?func=detail&aid=547327&group_id=36318&atid=416446 +https://sourceforge.net/tracker/index.php?func=detail&aid=616485&group_id=36318&atid=416448 +https://sourceforge.net/tracker/index.php?func=detail&aid=594081&group_id=36318&atid=416448 +https://sourceforge.net/tracker/index.php?func=detail&aid=594526&group_id=36318&atid=416448 + +Added LDAPAuthSource, based on the auth_ldap module for Apache +(http://www.rudedog.org/auth_ldap/) and the NDS Auth Source of +Phil Harris (AKA ftmpsh). This is only lightly tested, I don't have +the LDAP resources here to test all the features. Binding using uid/ +cn and using various filters works (if the userPassword item is +present). This needs more testing by people with better LDAP setups +that I do. --akm + +Padded docLoginRedirect to prevent IE from displaying "Friendly" error +messages when -D flag not present when running Zope --akm. + +Update UZG to contain entry for LDAPAuthSource. Reformat text +slightly. --akm + +Propogate "unable to auth" here requests up. This means the Manager +doesn't get locked out in cookie mode after adding an XUF instance. +It also means that people using a non-existant username at this level +get thrown up a level higher. This might not be what people want to +happen. --akm + +Added method makeRedirectPath which is called from docLoginRedirect. +This makes the destination include any querystring that was present +when needing to redirect. -- akm. + +Removed some Class globals from exUseFolder.py. These are now set +in __set_state__ if not present in the class so that upgrading users +don't get a crash (hopefully). -- akm. + +pgPropSource was losing track of properties under heavy load. +Only noticable if you were setting and deleting a lot of temporary +properties. There is a global property timeout for pgPropSource. --akm + +Jason Gibson provided a nisAuthSource, +I've added it here --akm. + +Refactored validate method to behave a lot more like BasicUserFolder. +Among other things, this fixes the issue where a local role could not +be granted to a user and granted permissions on the same object. --mb + +Add NuxUserGroups support (previously on NuxUserGroups_support_branch) +and group sources. --bmh, mb + +Now passes authFailedCode to Membership Login Page, The Default Login +Page as defined in the README.Membership will correctly display reason +for login being required --cab + +Fixed Edit management pages for user-supplied auth and property +sources --bmh + +Removed overriding of __len__ to return the number of users. This was +causing performance problems during authentication. See +http://sourceforge.net/mailarchive/message.php?msg_id=2230743 for +details. WARNING: this means using len(acl_users) to get the number +of users will no longer work! If you were using this trick, please +use len(acl_users.listUsers()) instead. --bmh + +Make title property editable --bmh + +Make Group Sources changeable dynamically after the acl_users folder has +been created --bmh + +Inital import of https Auth source. Also, added a listUsers method +to the zodbBTreeProps source to support listUsers. -- jsb + +Changes for 0.10.10 + +Added mysql Auth and mysql Prop source and mysql.sql schema. Just a +copy of the appropriate pg source with sql that works with myqsl -cab + +Fixed negative user cache lookup in std_validade so that it actually +works for users being authenticated thru basic auth, especially if +they're authenticating in outer user folders -- rochael + +Made smbAuthSource catch NetBIOSTimeout errors during authentication -- rochael + +Fixed dtml/mainUser.dtml to be virtualhost-sensitive when displaying user +icons -- rochael + +Updated UZG per user request. Fixed numbering, added information about +addition parameters like Negative Caching. + +Changes for 0.10.9 + +Made dummyZBabelTag compatible to replace the NoBabel in OrderedFolder +while keeping its functionality in XUF -- cab + +Changed _doAddUser, _doChangeUser to work with the public interface for +userfolders introduced in Zope2.5. Optional keyword arguments can now +be passed to _doAddUser and _doChangeUser. + +PropertySource: Please note that createUser and updateUser, when called +from _doAddUser and _doChangeUser, will no longer be passed a REQUEST, +but a mapping with items from REQUEST updated with those from the +optional keyword arguments. -- pj + +Fixed the problem with upgrading from 0.10.7 and below that didn't +account for existing XUF's not having a MessageDialog in their +contents. Now unless specificy replace it will use the MessageDialog +provided. Added how to do that to FAQ and README.Membership --cab + +Made docLoginRedirect provide an absolute URL --bmh + +MessageDialog in common no longer uses mangage_page_header and +mangage_page_footer v--cab + +Changes for 0.10.8 + +Added the ability for members to change properties, and a default page +in the README.Membership to show how to do it --cab + +MessageDialog is now an object in the ZODB that can be changed to fit +the site --cab + +Now with 100% guaranteed race-condition-free UserCache goodness! Those +subclassing XUFUser, you will have to change your code. See User.py +for details. --mb + +zodbBTreePropSource was returning None instead of the requested +default value, when called with (e.g.) someuser.getProperty('shoesize',13). +(Other property sources didn't have that bug.) +--davidc@debian.org + +The tutorial loginform was wrong for Membership in README.Membership + +Seems delProperty has never worked.. fixed --akm +Seems delProperty for pgPropSource has never worked.. fixed --akm + +Fixed Basic Auth not auth problem. --akm +Fixed Basic Auth not cache problem. --akm +Fixed Cached Users bypassing some auth checks. --akm + +Added usPropSource, which allows users to supply property methods TTW. +--bmh + +Changes for 0.10.7 + +PropertyEditor had a typo in dtml and was casting int to None. --zxc + +BasicAuth is now broken the other way, it'll allow any user to validate +with any password. --akm + +Negative cache checking move was bogus. --akm + +redirectToLogin didn't have a security declaration so 2.5.0 refused to +work in cookie mode *sigh* --akm + +Fixed the 'None' object has no attribute 'load' setstate errors that +could crop up on propSources, and preemptively took care of the +authSources as well. Also fixed some of the weirder bugs relating to +user object acquisition context. --mb + +Bug fixes from sf applied. --akm + +Changes for 0.10.6 + +dummyZBabelTag used the python 2 re, which broke installations using +python 1.5 which still used the now deprecated regex, changed it to +catch the exception and use regex instead for python 1.5, else still +use re --cab + +The redirectToLogin without Membership had a little logic problem where it +would basically garantee the existence of a query string, with at least a +lonely question mark even when there was no query string in the original +URL --rochael + +smbAuthSource needed to cast NULL role properties to an empty list --akm + +smbAuthSource had some dodgey zLOGing in it. --akm + +smbAuthSource had some methods that should return [] instead of None. --akm + +s/postgres/RADIUS/ in the radiusAuthSource DTML --akm + +cookie_validate no longer pulls you from the cache if you're +logging in (which means your cookie wouldn't get set). --akm + +Cookies are no longer expired if you're successfully authenticated but +merely unauthorized. --mb + +Basic auth resynched with standard user folder, trying to fix +some basic auth issues. --akm. + +Negative cache checking now performed outside of the two specific +validate methods. --akm. + +A fairly innocuous print debug statement turned into a zLOG at error +level, removed --akm. + +Clean up smbAuthSource log messages, and quieten. Only truly +exceptional cases are now logged above BLATHER. --mb + +Changes for 0.10.5 + +Membership redirecting to login was still broken. It should be better +now (twice) --akm + +logout() wasn't clearing the advanced cookie. --akm + +Negative Cache Value wasn't being passed through to the XUF constructor. --akm +Log Users Out DTML code was broken, should work now. --akm + +The User object now contains the authSource as well as the propSource, +making access to roles for custom User-objects possible. --dlk + +Following akm's advice, fixed manage_beforeDelete to use two separate +try:except blocks to ensure that if cache-removal fails, deleting +the container.__allow_groups__ property is attempted. This should +fix the problem where deleted xuf instances remain as "ghost" products +causing interference with newer versions of xuf, and also fixes the +problem where deleting a xuf acl_users in a folder makes that folder +inaccessible. --dlk + +Fixed cache_delete that was missing the "self" parameter in the method +defintion. --dlk + +Fixed xcache_delete that was missing the "self" parameter in the method +definition --akm d8) + +These previous two fix the problems with manage_beforeDelete, but, it +will stay the same for now --akm. + +Fixed cache_deleteCookieCache that was missing the "self" parameter in +the method defintion. --dlk ;) + +Changes for 0.10.4 + +The instructions for File Based Auth were incorrect in the UZG --akm + +redirectToLogin was totally wrong for membership... --akm +docLogin was fixed for VHM use. --akm + +Advanced Cookie Mode has changed so that it no longer sends the username +and password. Instead a hash is used as a key into a module level cache. +This should be 100% more secure than standard cookie mode, and removes +the stupid back doors I enabled in the previous version. This work was +based on conversations I had with Stuart Bishop (I basically lifted +the hashing scheme from GUF). This makes use of the Module level cache +code. --akm + +There was a code cleanup and a slight reorganisation of some files. --akm + +The main User Object has migrated to XUFUser and simarly with the +AnonUser. There is now an empty [Anon]User class that has XUFUser as +it's base. This allows people to create custom User Objects without +jumping through hoops (and simplifies maintaining patches) --akm + +Cache Code has changed again. Now there is a module level cache, so +that auth data is shared between threads for a single XUF (thanks to +Stuart Bishop for an enlightening discussion on this and other issues, +and thanks to Chris McDonough for talking me through setting up module +level globals [and sending me some code to work from]) --akm + +A Negative User Cache now exists. This is only generally useful for +use with remote auth sources where repeatedly trying to auth non-existant +users is very expensive (where they are authed at a higher level). +You can enable this on creation or from the parameters screen (positive +time in seconds enables). --akm + +Domain checking code finally removed. --akm + +zodbBTreePropSource changed to be friendlier about users that exist +in remote locations (i.e. aren't create as such through the ZMI). -- akm + +Changed some 'print's in the code to use zLOG.LOG +instead. Files affected so far (more to follow): -- rochael + + * exUserFolder.py + * basicMemberSource/basicMemberSource.py + * zodbBTreePropSource/zodbBTreePropSource.py + * zodbPropSource/zodbPropSource.py + +Changed a couple things in smbAuthSource.py: -- rbanffy + + * Method _authenticate_retry now logs several kinds of information + for debugging and diagnostics. + + * Modified socket.error handling in _authenticate_retry: changed + "raise" to "return 0". + + * Since this generated more problems (failed authentications) than + it solved (our impression it was not right not to return 0 in an + auth fail even due to a communications malfunction), we also + changed socket.error handling to retry no mather what errno tells + us (it said different things for the same problem under Windows + and Linux). + + * In order to prevent infinite retries, changed retry handling a + bit. It now retries 3 times. Real-use data will tell us if we + should increase or not retries. To better convey the meaning of + the parameter, changed "retry_depth" to "retries". I strongly + advise the use of credential caching with smbAuthSource, tough, as + it reduces socket errors and load on the domain controllers. + +Changes for 0.10.3.1 + +Readded support for I18N without ZBabel installation, somehow missed +during the transition to SF CVS. + +Some text changes as well as an update to the dictionary while we're +at it. No functional changes for this release though. + +Changes for 0.10.3 + +Missed a few LoginRequireds. + +Fixed a bug with __allow_groups__ not being set after paste +(probably also not after import). + +The sources are now sorted by name in the drop down box.. + +a BTree version of zodbAuthSource +a BTree version of zodbPropSource + +These aren't really all that different to the originals that were +provided by Alex, but, they use BTrees instead of PersistentMappings, +and try to avoid various persistence problems associated with dicts. +Both versions will continue to be supported. + +Patches from SF applied. + +Advanced Cookie Mode added. +This mode adds a rotor cipher around the cookie. A secret is provided +in order to encode the cookie. The username and password are placed +within a small class which is pickled and then encrypted and then +base64 encoded for transport. There is also a timestamp inside the cookie, +so the ultra-paranoid of you can rotate the cookie based on the timestamp +inside. + +Abstracted out the setting and decoding of cookies. + +Changes for 0.10.2 + +all raise 'LoginRequired' <- raise 'Unauthorized' + +Raising unauthorizes breaks a million things. CMF people can just +put up with configuring their portal properly. + +Radius resynced with version from sourceforge. +manage_tabs redone to be ZBabel'd and to look like standard tabs. + +German Language added to the ZBabel dictionary. + + +Changes for 0.10.1 + +all raise 'LoginRequired' -> raise 'Unauthorized' + +Bug in etcAuthSource listUsers fixed, +and cryptPassword also fixed to get the actual salt. + +Zope 2.4.3 has dicked with security settings again.. I've had a round +of permission whacking. + +Buggy handling of empty role lists was fixed. + +Change to smbAuthSource to use string.lower on usernames for +python 1.5.2 compatibility? + + +Changes for 0.10.0 + +Added explicit roles for manage_editUser and friends, to allow +the "Manage users" permission to be useful to non-Manager Users. +Thanks to Heimo Laukkanen for reporting this +one. + +zodbAuthSource made more persistent +zodbPropSource was blowing when deleting temporary properties. + +XUF is now ZBabel'd which means you can view XUF in different languages +for logging in and installation, if your browser locale is set up. +You will need the latest ZBabel installed. The translation file is in the +I18N directory. + +Import this (using Import/Export in ZODB) at the same level as your +ZBabelTower, and then import it from ZBabel. If you have ZBabel installed, +but, your application can't find a ZBabelTower, because of a bug in the +current dtml-fish tag, you might experience some problems. This ZBabel +bug should be fixed sometime soon. + +You do not need ZBabel installed to run XUF, XUF installs a dummy +interface for ZBabel so that XUF can continue to run (sorry folks it +defaults to Australian English). + +getUserNames() was returning the wrong stuff (notably affected TheJester's +WorkOrders Product) + +There is a now an 'Advanced Postgres' Auth Source that uses a seperate +Roles table and a 'more relational' layout. The schema is with the +auth source in pgAuthSourceAlt. Contributed by +Adam Manock + +If you had a membership source and had specified a login page, XUF was +still using the stock docLogin instead of the membership specified page +(for redirectToLogin, exceptions still raise the docLogin). + +I changed the icon to something a *little* less hideous + +Leonardo Rochael Almeida made the following changes +to smbAuthSource + +* Added a 'winsserver' constructor parameter and a '_winsserver' + instance variable to the 'smbAuthSource' class. This variable should + be the empty string, meaning that the authenticaton host will be + looked up by broadcast, or an IP address string pointing to a WINS + server. + +* Modified the dtml templates to ask for the above mentioned WINS + server (and also to replace 'Add' with 'Change' in + 'manage_editsmbAuthSourceForm'). + +* Refactored the smbAuthSource class to isolate all smb interaction + inside well defined methods. + + +Changes for 0.9.0 + +Messages are now sent back to the docLogin form. There's a file called +LoginRequiredMessages.py where the messages are kept for now (it might +end up a run-time configurable thing later). + +There's a new docLogin.dtml file on disk that shows how to use the new +messages. Because docLogin is in the ZODB this won't be automatically +upgraded. + +Idle Session Timeouts are in (this is the reason for the minor bump). +If you flick the switch, then users are forced back to the login form +(with a message saying their session timed out), when they're removed +from the cache. + +I made some adjustments to the tabs on the management interface because +they were too big, and I cleaned it up a bit for times when they run +together. + +The internal API was inconsistent, so that's been updated. +AuthSources no longer need to provide getUsers(), it was never +being called anyway since exUserFolder built it's own. +listUsers now returns the same data as listOneUser, this is used in +other places as if it were a list of listOneUser calls. + +Fixed pgAuthSource to deal with NULL rather than empty roles +columns (legacy columns). + +Changed Home Directory creation to use copy & paste functions to +copy the skeleton data. + +Changes for 0.8.5 + +I forgot to update the schema file for userproperties to reflect +the temporary properties flag. + +Checks for existing cache weren't being performed before removing users +from it, when their data was updated. + +Reversed the order for checking in cookie_validate, to allow logging +in as a new user, when session tracking was on. Also now you can +login as a different user, without logging out first, which might +be useful to some people. + +etcAuthSource now looks for the correct salt from the file for +encrypting the user supplied password + +Changes for 0.8.4 + +Activating Session Tracking and then adding a new user when there +were none in the XUF was broken. + +Changes for 0.8.3 + +The idle users are flushed from the cache when you ask for the list +of cache users (since it's iterating over the whole list anyway). So +you can manually clear your cache by looking at the Cache Stats page. + +If you display the list of logged in users on your site, then your cache +will be flushed for you automagically. + +Allowed a destination to be sent to redirectToLogin to allow you to +manually override the destination after logging in. + +Added in a __setstate__ for pgPropSource to deal with new ZSQL Methods +being added. + +Changes for 0.8.2 +A number of bugs related to temp properties fixed in pgPropSource + +FTP Access to folders protected with cookie_mode has been fixed, it +now reverts to std_auth (which handles the FTP connection fine), since +FTP auths are handled by getting a "Basic" auth tag coming through, which +should never happen in cookie mode. + +This has the knock-on effect of authenticating users that auth from a +higher acl_users that doesn't use cookies, 'more' correctly now. Which is +if you have a user defined above, and in XUF and the XUF user has less +permissions, it'll 401 you if you don't have permissions locally +(which is the correct behaviour). This bit me in the arse when I changed it, +and I'm still leaving it this way. d8) + +Users are now flushed from the cache when you edit them (in case you changed +roles), so that new roles should take effect immediately. + +The credential cache now uses the (Zope) builtin BTree Module for caching +rather than the AVL Tree implementation. There was a nasty issue with users +appearing multiple times in the AVL Tree which sucked. + +There is a report of the Radius Auth Source being broken (most likely +by me), if your radius source stops working, you can try copying the +py-radius.py file from sourceforge over the top of radius.py. If someone +gives me a traceback, I can fix it. I don't seem to be having problems, +but, I don't have a full time RADIUS source either. + + +Changes for 0.8.1 + +A bug in _doAddUser was fixed +A bug in the User Object unconditionally calling the prop source was fixed. + + +Changes for 0.8.0 + +Experimental "Session Tracking" added (why is it called that? we don't really +track anything, just associate arbitrary data with anonymous users). +This relies on the credential cache being active. Your session will +automatically expire when the anonymous user is so idle that they are +expired from the cache. This is not currently acceptable (to me), but, +it might be to other people, I await feedback on how sessions should expire +gracefully. + +Updated the README.txt file to point at the UZG and to explain the +version numbering system. + +All this time you couldn't delete properties from a user... who knew? +It's fixed now. + +Temporary properties now available, you can setTempProperty() on a +user object, and also flushTempProperties() on a user object. +Temporary properties are accessed like normal properties, and can be +deleted in the same way. flushTempProperties is there to do a quick +flush of all the crap you might have inserted (useful for sessions). +If your user is flushed from the cache, then all temp properties will +also be removed at that point. + +Propsource providers should look at the new temp properties stuff and +update accordingly. + +Alex provided a whole heap of patches to make basicMembership more usable, +well make it actually work. + +Matt Behrens supplied patches to prevent null logins and to allow case +insensitive logins for smbAuthSource + +Added a basic FAQ. + + +Changes for 0.7.10 + +Active Users type functionality was added. The new function is called +getUserCacheUsers(). It returns a list of dicts; + +{'username': theusername, 'lastAccessed': float_value} + +lastAccessed represents the last time the user touched something. +The Cache Stats page shows an example usage showing idle time (very cool +I think :-) + +The logout method was not correctly removing users from the cache, +although the cookie was removed, so logins were still enforced. I'm not +sure of any side-effects related to it, but, + +Some permissions were a little too liberal, including allowing arbitrary +users to set and get Properties on the acl_users folder. + +Copy/Paste support for pasting exUserFolders into the root was added. +I'm not sure I like the way this is done. I haven't found any side effects +so far, but, just be wary. Adding an exUserFolder to the root becomes +semi-trivial now. Create one in a sub-folder. Login as the emergency user. +CUT the exUserFolder. Delete the standard acl_users folder. Paste exUserFolder. +You should be away. At least it worked fine for me... YMMV + +_doChangeUser and _doDelUsers added so users can be altered and deleted +like for Standard UserFolder. + +_createInitialUser added so there should always be your initUser (hopefully) +when you create your exUserFolder. + +Emergency User checking brought into line with Standard Folder + +__creatable_by_emergency_user_ added and returns 1 to explicitly allow this. + +Unenlightened Zopistas Guide updated to have a 'Recipe' like section. +Currently contains a section about adding exUserFolders from python. + + +Changes for 0.7.9 + +RADIUS authSource had a problem with non-integers being extracted from +REQUEST (I wish someone at DC would fix this already). I worked around +this problem + +Default port for RADIUS is now 1812 in line with the IANA sanctioned list. + +Unenlightened Zopistas Guide to exUserFolder version 0.0 included, +covers installation and authentication sources, and the most common +configuration mistake (or misunderstanding). + +I almost released with the daggy management screens all Purple or SkyBlue, +so consider yoursevles lucky. This would have been the "Blue" release. + +Changes for 0.7.8 + +zodbPropSource had a bug that must have been there since 0.0.0 where +_p_changed wasn't being called on create, update, or delete user. +Thanks to Bouke Scheurwater for spotting that one. + +Alex provided a number of patched to fix a whole bunch of goofy stuff +with Basic Member Source that was stupidly wrong. + +Matt Behrens provided a patch to allow emergency user to own exUserFolders +and some of the sources. I've grudgingly updated all the sources to allow +this. It's just a hey nonny nonny to people using it as a root authenticator +now. + +Matt Behrens also provided a patch to fix 'broken pipe' problems with +smbAuthSource. + +pySMB is now at 0.2 for smbAuthSource WARNING: This will try to use DES +encrypted passwords. Apparently it should be ok if your server doesn't want +them. However if it breaks, unpack the pySMB distribution in the +smbAuthSource directory, there are registry examples there to turn +it off. It unfortunately needs the mxCrypto tools for encrypted passwords +to work. When I've got a bit more time, I'll see if I can make it use +crypt or fcrypt if available instead. + +Explicit checks for the emergency user were placed into the cookie_validate +routines. I suspect this may have been the cause of some grief with people +doing weird things like trying to make it the root auth folder. + +Changes for 0.7.7 + +Some Auth sources had problems coping with no roles being selected when +a user was created from the management interface, the stock ones were fixed. + +I screwed up some of the DTML, and forgot to change the loading of two of +the methods from the dtml directory. + +NO MORE TRACEBACKS ON LOGIN FORMS, there is a little redirector dtml file +dtml/docLoginRedirect that redirects to acl_users/docLogin with destination +set to take them back to where they were going. If you have a custom loginPage +change the redirector dtml to point to your new page. + +standard_html swapped for manage_page on Management Pages. Hopefully +this doesn't break someone with an old copy of Zope. + +Credential Caching is now available by default for all Authentication Sources, +upgrading installs will get this defaulted to 0 for no caching. You can alter +the cache level from the Parameters Tab. Authors of external sources should +remove any internal auth caching they're doing, and allow the user to decide +how long to cache the credentials for. + + +Changes for 0.7.6 + +smbAuthSource included. Doesn't require any external libraries, or compiling. +Uses pySMB from Micheal Teo + +Changes for 0.7.5 +The Management Interface now batches the user list by 10. This isn't +configurable at the moment (just change the dtml). + +The code was re-organised slightly, with all the DTML moving into its +own directory for core. + +radiusAuthSource added, but, is so far untested. It is a direct port of +ZRadius for GUF, but, I haven't had a chance to setup a RADIUS server to +test it out. + +You can add properties to a user from the management interface. + +List Properties on users can be added and edited, if I can work out a decent +way to edit Dicts/Mappings, I'll add that feature in. + +This paves the way for defining a set of properties in the Membership +source, so it can create a Signup and Edit page for you automatically. +You will also be able to specify which properties the user can edit, or +roles required to edit a property, this will be in a later release though. + +pgPropSource was updated to take into account non-scalar types, and now +pickles all data going into the database, this means ints will stay as ints, +et al. +There is code in there to cope with older properties coming out as strings. +The Schema remains the same. + +Changes for 0.7.2 +Changes to make it work with older version of python +Some minor bug fixes for membership. + +Changes for 0.7.1 +DTML Change for cmfPropSource + +Changes for 0.7.0 +exUserFolder was a little too liberal in removing its cruft, this is now +fixed. + +cmfPropSource was provided by Alan Runyan which is a layer around the CMF +property stuff. It's conditionally imported, so if you don't have CMF +installed you don't need to worry that'll it'll break. + +Property Sources are optional, and there is a NULL Property Source for this +purpose. + +Membership hooks, and a rough start at membership (basicMemberSource), +which has some usable functionality (you MUST read README.Membership before +using this). + +Membership Sources are optional and there is a NULL Membership Source for +this purpose. + + +Changes for 0.6.2 +exUserFolder was leaving cruft around when it was being deleted from +Folders. The cruft should now be obliterated if you delete an exUserFolder. + +Changes for 0.6.1 +Ownership tab enabled, for those sick monkeys that want to use it as a root +Folder (there are some). + +fcrypt got the __init__.py that was missing from the 0.6.0 release +zodbAuthSource updated to pull in fcrypt if crypt was missing. + +Changes for 0.6.0 + +Updated for 2.4.1 / Python 2.1 +Bug in pgPropSource not deleting users from the property cache fixed. +Bug with Local Roles not getting what it expected fixed. +Alex Verstraeten provided zodbAuthSource, there's a README.zodbAuthSource, +and the same README inside the zodbAuthSource directory. +fcrypt is now included and used if crypt cannot be imported. More information +on fcrypt can be found at http://home.clear.net.nz/pages/c.evans/sw/. This +should help particularly Windows users a lot. +Rudimentary API doc included. + +Changes for 0.5.0 + +A serious bug in zodbPropSource was fixed. + +There is now the option of providing a 'Remote Auth' function for +validating. This allows things like IMAP/LDAP auth sources to do their +authentication, since they don't return passwords you can use in general. + +There's already a 3rd Party solution that provides IMAP/POP3 authentication, +using the new API. + +Changes for 0.4.6 + +Minor dtml hacks + +Changes for 0.4.5 + +Hooks for 'editing' Authentication and Property Sources were added, along +with the relevant methods in each of the sources. + +The management interfaces got a little overhaul, just to make them +a little different (yes I know everything I do looks the same). The two +I didn't want to mess with still have the acquired management interfaces. + +A fix for the ZODB Property Source which was missing a few methods. + +Changes for 0.4.0 + +Based on an idea from Martin von Loewis, I added in support for defining +roles for etcAuthSource. This basically uses the current Prop source to +store a 'roles' property. The default role is still there as well for +those of you who might be using it. + +Changes for 0.3.0 + +Adrien Hernot noticed that properties for new users using zodbPropSource +were causing havoc, and that the version.txt file was completely wrong. +Andreas also noticed the version.txt was wrong. + +I've been bugged enough by the pair of them to change the single += +into 1.5.2 compliant syntax. + +I don't make any claims about it working under 1.5.2 though. + +Changes for 0.2.0 + +Even more embarassment... + +Andreas Heckel provided fixes for some stupid things I left out including; + +o Fixing the way I was handling multiple roles coming out of the database +o The wrong icon in the user display +o Alerting me to the fact that pgPropSource didn't actually have a + deleteUsers hook +o Providing a schema for automatically deleting properties in postgres + if you delete a user from the auth source (you have to be using both + pg sources for this to work, and they'd have to be in the same database) + I've put Andreas schema into the distribution, if you want to use + exUserFolder as a straight pgUserFolder, you'll also need to edit + exUserFolder.py and comment out the line indicated in deleteUsers() + +Changes for 0.1.0 + +Pretty embarassing really. + +M. Adam Kendall (DaJoker) found some stupid things in the 0.0.0 release +including the fact you couldn't edit user properties, or update them, +or actually change a user in anyway. + +I also discovered I was resetting the password to empty if you left it +empty.. diff --git a/ZopeProducts/exUserFolder/CryptoSources/__init__.py b/ZopeProducts/exUserFolder/CryptoSources/__init__.py new file mode 100644 index 00000000..49ebc6d4 --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/__init__.py @@ -0,0 +1,4 @@ +import pass_crypt +import pass_md5 +import pass_sha +import pass_plain diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/.cvsignore b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/ChangeLog b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/ChangeLog new file mode 100644 index 00000000..70299bed --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/ChangeLog @@ -0,0 +1,35 @@ +2001-05-05 Carey Evans + + * fcrypt.py: Add module doc string for pydoc, and other globals + for pydoc as well. Add __all__ for Python 2.1, and add + underscores to the front of private variables and functions. + (_set_key): Remove overly clever copying of globals into default + parameters, explicitly copying _shift2 and _skb before the loop. + (_body): Copy _SPtrans explicitly, as above. Remove CR_ENCRYPT + inline function, and reroll unrolled loop using the contents of + this function. Result: more readable code, and a 400% speedup! + (crypt): Add doc string for pydoc and doctest. + (_test): New function for doctest. + + * setup.py: Add fields for PKG-INFO metadata. + + * README: Add URL of distutils installation manual. + + * LICENSE: Add note about license on fcrypt.py being the union of + my license on the Python code and Eric Young's on the original C. + +2001-03-24 Carey Evans + + * setup.py: Move license to separate file. Change email address + to SpamCop forwardder. Update version to 1.1. + + * fcrypt.py: Update license text and email address. + (crypt): Fix bug where passwords longer than eight characters were + not truncated. + + * README: Update crypt module URL. Remove license text, and add + pointer to LICENSE file. Update email address. + + * MANIFEST.in: Add LICENSE, ChangeLog and MANIFEST.in. + + * LICENSE: New file. diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/LICENSE b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/LICENSE new file mode 100644 index 00000000..c26e615d --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/LICENSE @@ -0,0 +1,77 @@ + fcrypt.py copyrights and license + -------------------------------- + + +The Python code by Carey Evans has the following license, which is the +original Python license with the serial numbers filed off, and the +restrictions on advertising removed. + + Copyright (C) 2001, 2001 Carey Evans + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation. + + CAREY EVANS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + EVENT SHALL CAREY EVANS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + + +The original C code on which this module was based has the following +more restrictive license, so the source for fcrypt.py should be +considered to be covered by the union of my license and Eric Young's. + + This library is free for commercial and non-commercial use as long as + the following conditions are aheared to. The following conditions + apply to all code found in this distribution, be it the RC4, RSA, + lhash, DES, etc., code; not just the SSL code. The SSL documentation + included with this distribution is covered by the same copyright terms + except that the holder is Tim Hudson (tjh@mincom.oz.au). + + Copyright remains Eric Young's, and as such any Copyright notices in + the code are not to be removed. + If this package is used in a product, Eric Young should be given attribution + as the author of the parts of the library used. + This can be in the form of a textual message at program startup or + in documentation (online or textual) provided with the package. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + "This product includes cryptographic software written by + Eric Young (eay@mincom.oz.au)" + The word 'cryptographic' can be left out if the rouines from the library + being used are not cryptographic related :-). + 4. If you include any Windows specific code (or a derivative thereof) from + the apps directory (application code) you must include an acknowledgement: + "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + + THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + The licence and distribution terms for any publically available version or + derivative of this code cannot be changed. i.e. this code cannot simply be + copied and put under another distribution licence + [including the GNU Public Licence.] diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/MANIFEST.in b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/MANIFEST.in new file mode 100644 index 00000000..a42d250c --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/MANIFEST.in @@ -0,0 +1 @@ +include LICENSE ChangeLog MANIFEST.in diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/PKG-INFO b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/PKG-INFO new file mode 100644 index 00000000..c35737e0 --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 1.0 +Name: fcrypt +Version: 1.2 +Summary: The Unix password crypt function. +Home-page: http://home.clear.net.nz/pages/c.evans/sw/ +Author: Carey Evans +Author-email: careye@spamcop.net +License: BSD +Description: A pure Python implementation of the Unix DES password crypt function, + based on Eric Young's fcrypt.c. It works with any version of Python + from version 1.5 or higher, and because it's pure Python it doesn't + need a C compiler to install it. +Platform: UNKNOWN diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/README b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/README new file mode 100644 index 00000000..168500ff --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/README @@ -0,0 +1,33 @@ + fcrypt.py + --------- + +This is a pure Python implementation of the Unix DES password crypt +function. It was ported from C code by Eric Young (eay@mincom.oz.au). +See the file LICENSE for copyright and license details. + +This module is packaged with Distutils. If you have this installed, +or it came with your version of Python, you can install it by typing: + + python setup.py install + +If not, you can just copy `fcrypt.py' into a directory on your Python +library path, or into the same directory as the program that wants to +use it. + +For more information, see the documentation for Python's built-in +crypt module at: + + http://www.python.org/doc/current/lib/module-crypt.html + +Eric Young's fcrypt.c is available from: + + ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/ + +For more Distutils information, see: + + http://www.python.org/doc/current/inst/inst.html + http://www.python.org/sigs/distutils-sig/ + +-- +Carey Evans +5 May 2001 diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/__init__.py b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/__init__.py new file mode 100644 index 00000000..97f6031c --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/__init__.py @@ -0,0 +1 @@ +import fcrypt diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/fcrypt.py b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/fcrypt.py new file mode 100644 index 00000000..760347cb --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/fcrypt.py @@ -0,0 +1,602 @@ +# fcrypt.py + +"""Unix crypt(3) password hash algorithm. + +This is a port to Python of the standard Unix password crypt function. +It's a single self-contained source file that works with any version +of Python from version 1.5 or higher. The code is based on Eric +Young's optimised crypt in C. + +Python fcrypt is intended for users whose Python installation has not +had the crypt module enabled, or whose C library doesn't include the +crypt function. See the documentation for the Python crypt module for +more information: + + http://www.python.org/doc/current/lib/module-crypt.html + +The crypt() function is a one-way hash function, intended to hide a +password such that the only way to find out the original password is +to guess values until you get a match. If you need to encrypt and +decrypt data, this is not the module for you. + +There are at least two packages providing Python cryptography support: +M2Crypto at , and amkCrypto at +. + +Functions: + + crypt() -- return hashed password +""" + +__author__ = 'Carey Evans ' +__version__ = '1.2' +__date__ = '6 May 2001' +__credits__ = '''michal j wallace for inspiring me to write this. +Eric Young for the C code this module was copied from.''' + +__all__ = ['crypt'] + + +# Copyright (C) 2000, 2001 Carey Evans +# +# Permission to use, copy, modify, and distribute this software and +# its documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and +# that both that copyright notice and this permission notice appear in +# supporting documentation. +# +# CAREY EVANS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL CAREY EVANS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +# Based on C code by Eric Young (eay@mincom.oz.au), which has the +# following copyright. Especially note condition 3, which imposes +# extra restrictions on top of the standard Python license used above. +# +# The fcrypt.c source is available from: +# ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/ + +# ----- BEGIN fcrypt.c LICENSE ----- +# +# This library is free for commercial and non-commercial use as long as +# the following conditions are aheared to. The following conditions +# apply to all code found in this distribution, be it the RC4, RSA, +# lhash, DES, etc., code; not just the SSL code. The SSL documentation +# included with this distribution is covered by the same copyright terms +# except that the holder is Tim Hudson (tjh@mincom.oz.au). +# +# Copyright remains Eric Young's, and as such any Copyright notices in +# the code are not to be removed. +# If this package is used in a product, Eric Young should be given attribution +# as the author of the parts of the library used. +# This can be in the form of a textual message at program startup or +# in documentation (online or textual) provided with the package. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# "This product includes cryptographic software written by +# Eric Young (eay@mincom.oz.au)" +# The word 'cryptographic' can be left out if the rouines from the library +# being used are not cryptographic related :-). +# 4. If you include any Windows specific code (or a derivative thereof) from +# the apps directory (application code) you must include an acknowledgement: +# "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" +# +# THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# The licence and distribution terms for any publically available version or +# derivative of this code cannot be changed. i.e. this code cannot simply be +# copied and put under another distribution licence +# [including the GNU Public Licence.] +# +# ----- END fcrypt.c LICENSE ----- + + +import string, struct + + +_ITERATIONS = 16 + +_SPtrans = ( + # nibble 0 + [ 0x00820200, 0x00020000, 0x80800000, 0x80820200, + 0x00800000, 0x80020200, 0x80020000, 0x80800000, + 0x80020200, 0x00820200, 0x00820000, 0x80000200, + 0x80800200, 0x00800000, 0x00000000, 0x80020000, + 0x00020000, 0x80000000, 0x00800200, 0x00020200, + 0x80820200, 0x00820000, 0x80000200, 0x00800200, + 0x80000000, 0x00000200, 0x00020200, 0x80820000, + 0x00000200, 0x80800200, 0x80820000, 0x00000000, + 0x00000000, 0x80820200, 0x00800200, 0x80020000, + 0x00820200, 0x00020000, 0x80000200, 0x00800200, + 0x80820000, 0x00000200, 0x00020200, 0x80800000, + 0x80020200, 0x80000000, 0x80800000, 0x00820000, + 0x80820200, 0x00020200, 0x00820000, 0x80800200, + 0x00800000, 0x80000200, 0x80020000, 0x00000000, + 0x00020000, 0x00800000, 0x80800200, 0x00820200, + 0x80000000, 0x80820000, 0x00000200, 0x80020200 ], + + # nibble 1 + [ 0x10042004, 0x00000000, 0x00042000, 0x10040000, + 0x10000004, 0x00002004, 0x10002000, 0x00042000, + 0x00002000, 0x10040004, 0x00000004, 0x10002000, + 0x00040004, 0x10042000, 0x10040000, 0x00000004, + 0x00040000, 0x10002004, 0x10040004, 0x00002000, + 0x00042004, 0x10000000, 0x00000000, 0x00040004, + 0x10002004, 0x00042004, 0x10042000, 0x10000004, + 0x10000000, 0x00040000, 0x00002004, 0x10042004, + 0x00040004, 0x10042000, 0x10002000, 0x00042004, + 0x10042004, 0x00040004, 0x10000004, 0x00000000, + 0x10000000, 0x00002004, 0x00040000, 0x10040004, + 0x00002000, 0x10000000, 0x00042004, 0x10002004, + 0x10042000, 0x00002000, 0x00000000, 0x10000004, + 0x00000004, 0x10042004, 0x00042000, 0x10040000, + 0x10040004, 0x00040000, 0x00002004, 0x10002000, + 0x10002004, 0x00000004, 0x10040000, 0x00042000 ], + + # nibble 2 + [ 0x41000000, 0x01010040, 0x00000040, 0x41000040, + 0x40010000, 0x01000000, 0x41000040, 0x00010040, + 0x01000040, 0x00010000, 0x01010000, 0x40000000, + 0x41010040, 0x40000040, 0x40000000, 0x41010000, + 0x00000000, 0x40010000, 0x01010040, 0x00000040, + 0x40000040, 0x41010040, 0x00010000, 0x41000000, + 0x41010000, 0x01000040, 0x40010040, 0x01010000, + 0x00010040, 0x00000000, 0x01000000, 0x40010040, + 0x01010040, 0x00000040, 0x40000000, 0x00010000, + 0x40000040, 0x40010000, 0x01010000, 0x41000040, + 0x00000000, 0x01010040, 0x00010040, 0x41010000, + 0x40010000, 0x01000000, 0x41010040, 0x40000000, + 0x40010040, 0x41000000, 0x01000000, 0x41010040, + 0x00010000, 0x01000040, 0x41000040, 0x00010040, + 0x01000040, 0x00000000, 0x41010000, 0x40000040, + 0x41000000, 0x40010040, 0x00000040, 0x01010000 ], + + # nibble 3 + [ 0x00100402, 0x04000400, 0x00000002, 0x04100402, + 0x00000000, 0x04100000, 0x04000402, 0x00100002, + 0x04100400, 0x04000002, 0x04000000, 0x00000402, + 0x04000002, 0x00100402, 0x00100000, 0x04000000, + 0x04100002, 0x00100400, 0x00000400, 0x00000002, + 0x00100400, 0x04000402, 0x04100000, 0x00000400, + 0x00000402, 0x00000000, 0x00100002, 0x04100400, + 0x04000400, 0x04100002, 0x04100402, 0x00100000, + 0x04100002, 0x00000402, 0x00100000, 0x04000002, + 0x00100400, 0x04000400, 0x00000002, 0x04100000, + 0x04000402, 0x00000000, 0x00000400, 0x00100002, + 0x00000000, 0x04100002, 0x04100400, 0x00000400, + 0x04000000, 0x04100402, 0x00100402, 0x00100000, + 0x04100402, 0x00000002, 0x04000400, 0x00100402, + 0x00100002, 0x00100400, 0x04100000, 0x04000402, + 0x00000402, 0x04000000, 0x04000002, 0x04100400 ], + + # nibble 4 + [ 0x02000000, 0x00004000, 0x00000100, 0x02004108, + 0x02004008, 0x02000100, 0x00004108, 0x02004000, + 0x00004000, 0x00000008, 0x02000008, 0x00004100, + 0x02000108, 0x02004008, 0x02004100, 0x00000000, + 0x00004100, 0x02000000, 0x00004008, 0x00000108, + 0x02000100, 0x00004108, 0x00000000, 0x02000008, + 0x00000008, 0x02000108, 0x02004108, 0x00004008, + 0x02004000, 0x00000100, 0x00000108, 0x02004100, + 0x02004100, 0x02000108, 0x00004008, 0x02004000, + 0x00004000, 0x00000008, 0x02000008, 0x02000100, + 0x02000000, 0x00004100, 0x02004108, 0x00000000, + 0x00004108, 0x02000000, 0x00000100, 0x00004008, + 0x02000108, 0x00000100, 0x00000000, 0x02004108, + 0x02004008, 0x02004100, 0x00000108, 0x00004000, + 0x00004100, 0x02004008, 0x02000100, 0x00000108, + 0x00000008, 0x00004108, 0x02004000, 0x02000008 ], + + # nibble 5 + [ 0x20000010, 0x00080010, 0x00000000, 0x20080800, + 0x00080010, 0x00000800, 0x20000810, 0x00080000, + 0x00000810, 0x20080810, 0x00080800, 0x20000000, + 0x20000800, 0x20000010, 0x20080000, 0x00080810, + 0x00080000, 0x20000810, 0x20080010, 0x00000000, + 0x00000800, 0x00000010, 0x20080800, 0x20080010, + 0x20080810, 0x20080000, 0x20000000, 0x00000810, + 0x00000010, 0x00080800, 0x00080810, 0x20000800, + 0x00000810, 0x20000000, 0x20000800, 0x00080810, + 0x20080800, 0x00080010, 0x00000000, 0x20000800, + 0x20000000, 0x00000800, 0x20080010, 0x00080000, + 0x00080010, 0x20080810, 0x00080800, 0x00000010, + 0x20080810, 0x00080800, 0x00080000, 0x20000810, + 0x20000010, 0x20080000, 0x00080810, 0x00000000, + 0x00000800, 0x20000010, 0x20000810, 0x20080800, + 0x20080000, 0x00000810, 0x00000010, 0x20080010 ], + + # nibble 6 + [ 0x00001000, 0x00000080, 0x00400080, 0x00400001, + 0x00401081, 0x00001001, 0x00001080, 0x00000000, + 0x00400000, 0x00400081, 0x00000081, 0x00401000, + 0x00000001, 0x00401080, 0x00401000, 0x00000081, + 0x00400081, 0x00001000, 0x00001001, 0x00401081, + 0x00000000, 0x00400080, 0x00400001, 0x00001080, + 0x00401001, 0x00001081, 0x00401080, 0x00000001, + 0x00001081, 0x00401001, 0x00000080, 0x00400000, + 0x00001081, 0x00401000, 0x00401001, 0x00000081, + 0x00001000, 0x00000080, 0x00400000, 0x00401001, + 0x00400081, 0x00001081, 0x00001080, 0x00000000, + 0x00000080, 0x00400001, 0x00000001, 0x00400080, + 0x00000000, 0x00400081, 0x00400080, 0x00001080, + 0x00000081, 0x00001000, 0x00401081, 0x00400000, + 0x00401080, 0x00000001, 0x00001001, 0x00401081, + 0x00400001, 0x00401080, 0x00401000, 0x00001001 ], + + # nibble 7 + [ 0x08200020, 0x08208000, 0x00008020, 0x00000000, + 0x08008000, 0x00200020, 0x08200000, 0x08208020, + 0x00000020, 0x08000000, 0x00208000, 0x00008020, + 0x00208020, 0x08008020, 0x08000020, 0x08200000, + 0x00008000, 0x00208020, 0x00200020, 0x08008000, + 0x08208020, 0x08000020, 0x00000000, 0x00208000, + 0x08000000, 0x00200000, 0x08008020, 0x08200020, + 0x00200000, 0x00008000, 0x08208000, 0x00000020, + 0x00200000, 0x00008000, 0x08000020, 0x08208020, + 0x00008020, 0x08000000, 0x00000000, 0x00208000, + 0x08200020, 0x08008020, 0x08008000, 0x00200020, + 0x08208000, 0x00000020, 0x00200020, 0x08008000, + 0x08208020, 0x00200000, 0x08200000, 0x08000020, + 0x00208000, 0x00008020, 0x08008020, 0x08200000, + 0x00000020, 0x08208000, 0x00208020, 0x00000000, + 0x08000000, 0x08200020, 0x00008000, 0x00208020 ] ) + +_skb = ( + # for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 + [ 0x00000000, 0x00000010, 0x20000000, 0x20000010, + 0x00010000, 0x00010010, 0x20010000, 0x20010010, + 0x00000800, 0x00000810, 0x20000800, 0x20000810, + 0x00010800, 0x00010810, 0x20010800, 0x20010810, + 0x00000020, 0x00000030, 0x20000020, 0x20000030, + 0x00010020, 0x00010030, 0x20010020, 0x20010030, + 0x00000820, 0x00000830, 0x20000820, 0x20000830, + 0x00010820, 0x00010830, 0x20010820, 0x20010830, + 0x00080000, 0x00080010, 0x20080000, 0x20080010, + 0x00090000, 0x00090010, 0x20090000, 0x20090010, + 0x00080800, 0x00080810, 0x20080800, 0x20080810, + 0x00090800, 0x00090810, 0x20090800, 0x20090810, + 0x00080020, 0x00080030, 0x20080020, 0x20080030, + 0x00090020, 0x00090030, 0x20090020, 0x20090030, + 0x00080820, 0x00080830, 0x20080820, 0x20080830, + 0x00090820, 0x00090830, 0x20090820, 0x20090830 ], + + # for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 + [ 0x00000000, 0x02000000, 0x00002000, 0x02002000, + 0x00200000, 0x02200000, 0x00202000, 0x02202000, + 0x00000004, 0x02000004, 0x00002004, 0x02002004, + 0x00200004, 0x02200004, 0x00202004, 0x02202004, + 0x00000400, 0x02000400, 0x00002400, 0x02002400, + 0x00200400, 0x02200400, 0x00202400, 0x02202400, + 0x00000404, 0x02000404, 0x00002404, 0x02002404, + 0x00200404, 0x02200404, 0x00202404, 0x02202404, + 0x10000000, 0x12000000, 0x10002000, 0x12002000, + 0x10200000, 0x12200000, 0x10202000, 0x12202000, + 0x10000004, 0x12000004, 0x10002004, 0x12002004, + 0x10200004, 0x12200004, 0x10202004, 0x12202004, + 0x10000400, 0x12000400, 0x10002400, 0x12002400, + 0x10200400, 0x12200400, 0x10202400, 0x12202400, + 0x10000404, 0x12000404, 0x10002404, 0x12002404, + 0x10200404, 0x12200404, 0x10202404, 0x12202404 ], + + # for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 + [ 0x00000000, 0x00000001, 0x00040000, 0x00040001, + 0x01000000, 0x01000001, 0x01040000, 0x01040001, + 0x00000002, 0x00000003, 0x00040002, 0x00040003, + 0x01000002, 0x01000003, 0x01040002, 0x01040003, + 0x00000200, 0x00000201, 0x00040200, 0x00040201, + 0x01000200, 0x01000201, 0x01040200, 0x01040201, + 0x00000202, 0x00000203, 0x00040202, 0x00040203, + 0x01000202, 0x01000203, 0x01040202, 0x01040203, + 0x08000000, 0x08000001, 0x08040000, 0x08040001, + 0x09000000, 0x09000001, 0x09040000, 0x09040001, + 0x08000002, 0x08000003, 0x08040002, 0x08040003, + 0x09000002, 0x09000003, 0x09040002, 0x09040003, + 0x08000200, 0x08000201, 0x08040200, 0x08040201, + 0x09000200, 0x09000201, 0x09040200, 0x09040201, + 0x08000202, 0x08000203, 0x08040202, 0x08040203, + 0x09000202, 0x09000203, 0x09040202, 0x09040203 ], + + # for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 + [ 0x00000000, 0x00100000, 0x00000100, 0x00100100, + 0x00000008, 0x00100008, 0x00000108, 0x00100108, + 0x00001000, 0x00101000, 0x00001100, 0x00101100, + 0x00001008, 0x00101008, 0x00001108, 0x00101108, + 0x04000000, 0x04100000, 0x04000100, 0x04100100, + 0x04000008, 0x04100008, 0x04000108, 0x04100108, + 0x04001000, 0x04101000, 0x04001100, 0x04101100, + 0x04001008, 0x04101008, 0x04001108, 0x04101108, + 0x00020000, 0x00120000, 0x00020100, 0x00120100, + 0x00020008, 0x00120008, 0x00020108, 0x00120108, + 0x00021000, 0x00121000, 0x00021100, 0x00121100, + 0x00021008, 0x00121008, 0x00021108, 0x00121108, + 0x04020000, 0x04120000, 0x04020100, 0x04120100, + 0x04020008, 0x04120008, 0x04020108, 0x04120108, + 0x04021000, 0x04121000, 0x04021100, 0x04121100, + 0x04021008, 0x04121008, 0x04021108, 0x04121108 ], + + # for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 + [ 0x00000000, 0x10000000, 0x00010000, 0x10010000, + 0x00000004, 0x10000004, 0x00010004, 0x10010004, + 0x20000000, 0x30000000, 0x20010000, 0x30010000, + 0x20000004, 0x30000004, 0x20010004, 0x30010004, + 0x00100000, 0x10100000, 0x00110000, 0x10110000, + 0x00100004, 0x10100004, 0x00110004, 0x10110004, + 0x20100000, 0x30100000, 0x20110000, 0x30110000, + 0x20100004, 0x30100004, 0x20110004, 0x30110004, + 0x00001000, 0x10001000, 0x00011000, 0x10011000, + 0x00001004, 0x10001004, 0x00011004, 0x10011004, + 0x20001000, 0x30001000, 0x20011000, 0x30011000, + 0x20001004, 0x30001004, 0x20011004, 0x30011004, + 0x00101000, 0x10101000, 0x00111000, 0x10111000, + 0x00101004, 0x10101004, 0x00111004, 0x10111004, + 0x20101000, 0x30101000, 0x20111000, 0x30111000, + 0x20101004, 0x30101004, 0x20111004, 0x30111004 ], + + # for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 + [ 0x00000000, 0x08000000, 0x00000008, 0x08000008, + 0x00000400, 0x08000400, 0x00000408, 0x08000408, + 0x00020000, 0x08020000, 0x00020008, 0x08020008, + 0x00020400, 0x08020400, 0x00020408, 0x08020408, + 0x00000001, 0x08000001, 0x00000009, 0x08000009, + 0x00000401, 0x08000401, 0x00000409, 0x08000409, + 0x00020001, 0x08020001, 0x00020009, 0x08020009, + 0x00020401, 0x08020401, 0x00020409, 0x08020409, + 0x02000000, 0x0A000000, 0x02000008, 0x0A000008, + 0x02000400, 0x0A000400, 0x02000408, 0x0A000408, + 0x02020000, 0x0A020000, 0x02020008, 0x0A020008, + 0x02020400, 0x0A020400, 0x02020408, 0x0A020408, + 0x02000001, 0x0A000001, 0x02000009, 0x0A000009, + 0x02000401, 0x0A000401, 0x02000409, 0x0A000409, + 0x02020001, 0x0A020001, 0x02020009, 0x0A020009, + 0x02020401, 0x0A020401, 0x02020409, 0x0A020409 ], + + # for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 + [ 0x00000000, 0x00000100, 0x00080000, 0x00080100, + 0x01000000, 0x01000100, 0x01080000, 0x01080100, + 0x00000010, 0x00000110, 0x00080010, 0x00080110, + 0x01000010, 0x01000110, 0x01080010, 0x01080110, + 0x00200000, 0x00200100, 0x00280000, 0x00280100, + 0x01200000, 0x01200100, 0x01280000, 0x01280100, + 0x00200010, 0x00200110, 0x00280010, 0x00280110, + 0x01200010, 0x01200110, 0x01280010, 0x01280110, + 0x00000200, 0x00000300, 0x00080200, 0x00080300, + 0x01000200, 0x01000300, 0x01080200, 0x01080300, + 0x00000210, 0x00000310, 0x00080210, 0x00080310, + 0x01000210, 0x01000310, 0x01080210, 0x01080310, + 0x00200200, 0x00200300, 0x00280200, 0x00280300, + 0x01200200, 0x01200300, 0x01280200, 0x01280300, + 0x00200210, 0x00200310, 0x00280210, 0x00280310, + 0x01200210, 0x01200310, 0x01280210, 0x01280310 ], + + # for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 + [ 0x00000000, 0x04000000, 0x00040000, 0x04040000, + 0x00000002, 0x04000002, 0x00040002, 0x04040002, + 0x00002000, 0x04002000, 0x00042000, 0x04042000, + 0x00002002, 0x04002002, 0x00042002, 0x04042002, + 0x00000020, 0x04000020, 0x00040020, 0x04040020, + 0x00000022, 0x04000022, 0x00040022, 0x04040022, + 0x00002020, 0x04002020, 0x00042020, 0x04042020, + 0x00002022, 0x04002022, 0x00042022, 0x04042022, + 0x00000800, 0x04000800, 0x00040800, 0x04040800, + 0x00000802, 0x04000802, 0x00040802, 0x04040802, + 0x00002800, 0x04002800, 0x00042800, 0x04042800, + 0x00002802, 0x04002802, 0x00042802, 0x04042802, + 0x00000820, 0x04000820, 0x00040820, 0x04040820, + 0x00000822, 0x04000822, 0x00040822, 0x04040822, + 0x00002820, 0x04002820, 0x00042820, 0x04042820, + 0x00002822, 0x04002822, 0x00042822, 0x04042822 ] ) + +_shifts2 = (0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0) + +_con_salt = [ + 0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9, + 0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1, + 0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, + 0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1, + 0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9, + 0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01, + 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, + 0x0A,0x0B,0x05,0x06,0x07,0x08,0x09,0x0A, + 0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12, + 0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, + 0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22, + 0x23,0x24,0x25,0x20,0x21,0x22,0x23,0x24, + 0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C, + 0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C, + 0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44 ] + +_cov_2char = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + + +def _HPERM_OP(a): + """Clever bit manipulation.""" + t = ((a << 18) ^ a) & 0xcccc0000 + return a ^ t ^ ((t >> 18) & 0x3fff) + +def _PERM_OP(a,b,n,m): + """Cleverer bit manipulation.""" + t = ((a >> n) ^ b) & m + b = b ^ t + a = a ^ (t << n) + return a,b + + +def _set_key(password): + """Generate DES key schedule from ASCII password.""" + + c,d = struct.unpack('> 16) | ((c >> 4) & 0x0f000000)) + c = c & 0x0fffffff + + # Copy globals into local variables for loop. + shifts2 = _shifts2 + skbc0, skbc1, skbc2, skbc3, skbd0, skbd1, skbd2, skbd3 = _skb + + k = [0] * (_ITERATIONS * 2) + + for i in range(_ITERATIONS): + # Only operates on top 28 bits. + if shifts2[i]: + c = (c >> 2) | (c << 26) + d = (d >> 2) | (d << 26) + else: + c = (c >> 1) | (c << 27) + d = (d >> 1) | (d << 27) + c = c & 0x0fffffff + d = d & 0x0fffffff + + s = ( skbc0[ c & 0x3f ] | + skbc1[((c>> 6) & 0x03) | ((c>> 7) & 0x3c)] | + skbc2[((c>>13) & 0x0f) | ((c>>14) & 0x30)] | + skbc3[((c>>20) & 0x01) | + ((c>>21) & 0x06) | ((c>>22) & 0x38)] ) + + t = ( skbd0[ d & 0x3f ] | + skbd1[((d>> 7) & 0x03) | ((d>> 8) & 0x3c)] | + skbd2[((d>>15) & 0x3f) ] | + skbd3[((d>>21) & 0x0f) | ((d>>22) & 0x30)] ) + + k[2*i] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff + s = (s >> 16) | (t & 0xffff0000) + + # Top bit of s may be 1. + s = (s << 4) | ((s >> 28) & 0x0f) + k[2*i + 1] = s & 0xffffffff + + return k + + +def _body(ks, E0, E1): + """Use the key schedule ks and salt E0, E1 to create the password hash.""" + + # Copy global variable into locals for loop. + SP0, SP1, SP2, SP3, SP4, SP5, SP6, SP7 = _SPtrans + + inner = range(0, _ITERATIONS*2, 2) + l = r = 0 + for j in range(25): + l,r = r,l + for i in inner: + t = r ^ ((r >> 16) & 0xffff) + u = t & E0 + t = t & E1 + u = u ^ (u << 16) ^ r ^ ks[i] + t = t ^ (t << 16) ^ r ^ ks[i+1] + t = ((t >> 4) & 0x0fffffff) | (t << 28) + + l,r = r,(SP1[(t ) & 0x3f] ^ SP3[(t>> 8) & 0x3f] ^ + SP5[(t>>16) & 0x3f] ^ SP7[(t>>24) & 0x3f] ^ + SP0[(u ) & 0x3f] ^ SP2[(u>> 8) & 0x3f] ^ + SP4[(u>>16) & 0x3f] ^ SP6[(u>>24) & 0x3f] ^ l) + + l = ((l >> 1) & 0x7fffffff) | ((l & 0x1) << 31) + r = ((r >> 1) & 0x7fffffff) | ((r & 0x1) << 31) + + r,l = _PERM_OP(r, l, 1, 0x55555555) + l,r = _PERM_OP(l, r, 8, 0x00ff00ff) + r,l = _PERM_OP(r, l, 2, 0x33333333) + l,r = _PERM_OP(l, r, 16, 0x0000ffff) + r,l = _PERM_OP(r, l, 4, 0x0f0f0f0f) + + return l,r + + +def crypt(password, salt): + """Generate an encrypted hash from the passed password. If the password +is longer than eight characters, only the first eight will be used. + +The first two characters of the salt are used to modify the encryption +algorithm used to generate in the hash in one of 4096 different ways. +The characters for the salt must be alphanumeric, '.' or '/'. + +The returned hash begins with the two characters of the salt, and +should be passed as the salt to verify the password. + +Example: + + >>> from fcrypt import crypt + >>> password = 'AlOtBsOl' + >>> salt = 'cE' + >>> hash = crypt(password, salt) + >>> hash + 'cEpWz5IUCShqM' + >>> crypt(password, hash) == hash + 1 + >>> crypt('IaLaIoK', hash) == hash + 0 + +In practice, you would read the password using something like the +getpass module, and generate the salt randomly: + + >>> import random, string + >>> saltchars = string.letters + string.digits + './' + >>> salt = random.choice(saltchars) + random.choice(saltchars) +""" + + if len(salt) < 2: + salt = salt + 'AA' + + Eswap0 = _con_salt[ord(salt[0])] + Eswap1 = _con_salt[ord(salt[1])] << 4 + + ks = _set_key((password + '\0\0\0\0\0\0\0\0')[:8]) + out1,out2 = _body(ks, Eswap0, Eswap1) + + # Convert numbers to big-endian... + be1, be2 = struct.unpack('>ii', struct.pack('> 8) & 0xffffff, + ((be1 << 16) & 0xff0000) | ((be2 >> 16) & 0xffff), + (be2 << 8) & 0xffff00] + + # Convert to ASCII encoding, 4 characters for each 24 bits. + res = [salt[0], salt[1]] + for b in b24: + for i in range(18, -6, -6): + res.append(_cov_2char[(b >> i) & 0x3f]) + + return string.join(res[:13], '') + +def _test(): + """Run doctest on fcrypt module.""" + import doctest, fcrypt + return doctest.testmod(fcrypt) + +if __name__ == '__main__': + _test() diff --git a/ZopeProducts/exUserFolder/CryptoSources/fcrypt/setup.py b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/setup.py new file mode 100644 index 00000000..33ccb6d3 --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/fcrypt/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# distutils setup script for fcrypt. +# +# Copyright (C) 2000, 2001 Carey Evans + +from distutils.core import setup + +setup( name = 'fcrypt', + version = '1.2', + description = 'The Unix password crypt function.', + author = 'Carey Evans', + author_email = 'careye@spamcop.net', + url = 'http://home.clear.net.nz/pages/c.evans/sw/', + licence = 'BSD', + long_description = """\ +A pure Python implementation of the Unix DES password crypt function, +based on Eric Young's fcrypt.c. It works with any version of Python +from version 1.5 or higher, and because it's pure Python it doesn't +need a C compiler to install it.""", + + py_modules = ['fcrypt'] ) diff --git a/ZopeProducts/exUserFolder/CryptoSources/pass_crypt.py b/ZopeProducts/exUserFolder/CryptoSources/pass_crypt.py new file mode 100644 index 00000000..ef1d2954 --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/pass_crypt.py @@ -0,0 +1,44 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: pass_crypt.py,v 1.3 2004/11/18 09:24:46 akm Exp $ + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import CryptoPluginRegister + +try: + from crypt import crypt +except: + from fcrypt.fcrypt import crypt + + +def cryptPassword(authSource, username, password): + u = authSource.listOneUser(username) + if not u: + salt = username[:2] + else: + salt=u[0]['password'][:2] + + secret = crypt(password, salt) + return secret + + +CryptPlugin=CryptoPluginRegister('Crypt', 'crypt', 'Crypt', cryptPassword) +exUserFolder.cryptoSources['Crypt']=CryptPlugin diff --git a/ZopeProducts/exUserFolder/CryptoSources/pass_md5.py b/ZopeProducts/exUserFolder/CryptoSources/pass_md5.py new file mode 100644 index 00000000..c67af140 --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/pass_md5.py @@ -0,0 +1,47 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: pass_md5.py,v 1.1 2004/11/10 14:15:52 akm Exp $ + +import md5, base64, string + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import CryptoPluginRegister + +# Simple digest +def cryptPassword(authSource, username, password): + digest = md5.new() + digest.update(password) + digest = digest.digest() + secret = string.strip(base64.encodestring(digest)) + return secret + +# Digest includes username +# So two passwords for different users hash differently +def cryptPassword2(authSource, username, password): + newPass = username+':'+password + return cryptPassword(authSource, username, newPass) + + +MD5Plugin1=CryptoPluginRegister('MD51', 'MD5', 'MD5 Password Only', cryptPassword) +exUserFolder.cryptoSources['MD51']=MD5Plugin1 + +MD5Plugin2=CryptoPluginRegister('MD52', 'MD5', 'MD5 Username + Password', cryptPassword2) +exUserFolder.cryptoSources['MD52']=MD5Plugin2 diff --git a/ZopeProducts/exUserFolder/CryptoSources/pass_plain.py b/ZopeProducts/exUserFolder/CryptoSources/pass_plain.py new file mode 100644 index 00000000..2d29c78d --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/pass_plain.py @@ -0,0 +1,31 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: pass_plain.py,v 1.1 2004/11/10 14:15:52 akm Exp $ + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import CryptoPluginRegister + +# Simple digest +def cryptPassword(authSource, username, password): + return password + +PlainPlugin=CryptoPluginRegister('Plaintext', 'Plaintext', 'No Encryption', cryptPassword) +exUserFolder.cryptoSources['Plaintext']=PlainPlugin diff --git a/ZopeProducts/exUserFolder/CryptoSources/pass_sha.py b/ZopeProducts/exUserFolder/CryptoSources/pass_sha.py new file mode 100644 index 00000000..a4bc8fd3 --- /dev/null +++ b/ZopeProducts/exUserFolder/CryptoSources/pass_sha.py @@ -0,0 +1,41 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: pass_sha.py,v 1.1 2004/11/10 14:15:52 akm Exp $ + +import sha +from base64 import encodestring + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import CryptoPluginRegister + + +def cryptPassword(authSource, username, password): + return encodestring(sha.new(password).digest()) + +def cryptPassword2(authSource, username, password): + newPass = username+':'+password + return cryptPassword(authSource, username, newPass) + +SHAPlugin1=CryptoPluginRegister('SHA1', 'SHA', 'SHA Password Only', cryptPassword) +exUserFolder.cryptoSources['SHA1']=SHAPlugin1 + +SHAPlugin2=CryptoPluginRegister('SHA2', 'SHA', 'SHA Username + Password', cryptPassword2) +exUserFolder.cryptoSources['SHA2']=SHAPlugin2 diff --git a/ZopeProducts/exUserFolder/Extensions/.cvsignore b/ZopeProducts/exUserFolder/Extensions/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/Extensions/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/Extensions/getOldGroups.py b/ZopeProducts/exUserFolder/Extensions/getOldGroups.py new file mode 100644 index 00000000..9fa8088e --- /dev/null +++ b/ZopeProducts/exUserFolder/Extensions/getOldGroups.py @@ -0,0 +1,20 @@ +# This script interrogates the old-skool NuxUserGroups_support_branch +# group structure and outputs a tab-delimited file you can send to +# loadOldGroups. Just in case anyone is using it. :-) +# +# Matt Behrens + +def getOldGroups(self): + "Reconstruct a group list from the old-style _groups property" + from string import join + props = self.currentPropSource.userProperties + groups = {} + for username in props.keys(): + for groupname in props[username].getProperty('_groups', ()): + if not groups.has_key(groupname): + groups[groupname] = [] + groups[groupname].append(username) + out = '' + for groupname in groups.keys(): + out = out + '%s %s\n' % (groupname, join(groups[groupname], ' ')) + return out diff --git a/ZopeProducts/exUserFolder/Extensions/loadOldGroups.py b/ZopeProducts/exUserFolder/Extensions/loadOldGroups.py new file mode 100644 index 00000000..b7f2f702 --- /dev/null +++ b/ZopeProducts/exUserFolder/Extensions/loadOldGroups.py @@ -0,0 +1,26 @@ +# This takes 'old_groups.txt' from var (create it using getOldGroups) +# and sets up all the groups therein using NuxUserGroups calls. This +# will load a group source if you need to do such a thing. +# +# Matt Behrens + +def loadOldGroups(self): + from os.path import join as pathJoin + from string import split, strip + + groups_file = open(pathJoin(CLIENT_HOME, 'old_groups.txt'), 'r') + out = '' + for group_line in groups_file.readlines(): + group_line_elements = split(strip(group_line), ' ') + group_name = group_line_elements[0] + group_members = group_line_elements[1:] + + if self.getGroupById(group_name, default=None) is None: + out = out + 'adding group %s\n' % group_name + self.userFolderAddGroup(group_name) + + out = out + 'setting group %s membership to %s\n' % (group_name, group_members) + self.setUsersOfGroup(group_members, group_name) + + return out + diff --git a/ZopeProducts/exUserFolder/Extensions/usAuthSourceMethods.py b/ZopeProducts/exUserFolder/Extensions/usAuthSourceMethods.py new file mode 100644 index 00000000..bb43162d --- /dev/null +++ b/ZopeProducts/exUserFolder/Extensions/usAuthSourceMethods.py @@ -0,0 +1,140 @@ +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: usAuthSourceMethods.py,v 1.3 2001/12/01 08:40:04 akm Exp $ +# +######################################################################## +# +# This is an example of an Extension Module to provide User Supplied +# Authentication Methods. +# +# It mimics the behaviour of the pgAuthSource Module, and the sql queries +# Used here would be added as ZSQLMethods in the usAuthSource Folder. +# (you can basically cut and paste them from the bottom of this .py file +# into the ZSQL Method Template Area +# +# It's not complete, but, you do get the idea... +# +# Each function becomes usFunctionName +# +# e.g. listOneUser -> usListOneUser +# +import string +from crypt import crypt + +def listOneUser(self,username): + users = [] + result=self.sqlListOneUser(username=username) + for n in result: + username=sqlattr(n,'username') + password=sqlattr(n,'password') + roles=string.split(sqlattr(n,'roles')) + N={'username':username, 'password':password, 'roles':roles} + users.append(N) + return users + +def listUsers(self): + """Returns a list of user names or [] if no users exist""" + users = [] + result=self.sqlListUsers() + for n in result: + username=sqlattr(n,'username') + N={'username':username} + users.append(N) + return users + +def getUsers(self): + """Return a list of user objects or [] if no users exist""" + data=[] + try: items=self.listusers() + except: return data + for people in items: + roles=string.split(people['roles'],',') + user=User(people['username'], roles, '') + data.append(user) + return data + +def cryptPassword(self, username, password): + salt =username[:2] + secret = crypt(password, salt) + return secret + +def deleteUsers(self, userids): + for uid in userids: + self.sqlDeleteOneUser(userid=uid) + + +# Helper Functions... +from string import upper, lower +import Missing +mt=type(Missing.Value) + +def typeconv(val): + if type(val)==mt: + return '' + return val + +def sqlattr(ob, attr): + name=attr + if hasattr(ob, attr): + return typeconv(getattr(ob, attr)) + attr=upper(attr) + if hasattr(ob, attr): + return typeconv(getattr(ob, attr)) + attr=lower(attr) + if hasattr(ob, attr): + return typeconv(getattr(ob, attr)) + raise NameError, name + + +######################################################################## +# SQL METHODS USED ABOVE +# PASTE INTO ZSQL METHODS +# take note of what parameters are used in each query +######################################################################## + +_sqlListUsers=""" +SELECT * FROM passwd +""" + +_sqlListOneUser=""" +SELECT * FROM passwd +where username= +""" + +_sqlDeleteOneUser=""" +DELETE FROM passwd +where uid= +""" + +_sqlInsertUser=""" +INSERT INTO passwd (username, password, roles) +VALUES (, + , + ) +""" + +_sqlUpdateUserPassword=""" +UPDATE passwd set password= +WHERE username= +""" + +_sqlUpdateUser=""" +UPDATE passwd set roles= +WHERE username= +""" + diff --git a/ZopeProducts/exUserFolder/GroupSource/.cvsignore b/ZopeProducts/exUserFolder/GroupSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/GroupSource/GroupSource.py b/ZopeProducts/exUserFolder/GroupSource/GroupSource.py new file mode 100644 index 00000000..478ab0b3 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSource/GroupSource.py @@ -0,0 +1,32 @@ +# +# Extensible User Folder +# +# Null Group Source for exUserFolder +# +# Author: Brent Hendricks +# $Id: GroupSource.py,v 1.1 2002/12/02 23:20:49 bmh Exp $ +from Globals import DTMLFile + + +manage_addGroupSourceForm=DTMLFile('manage_addGroupSourceForm', globals(), __name__='manage_addGroupSourceForm') + + +def manage_addGroupSource(dispatcher, REQUEST): + """ Add a Group Source """ + + # Get the XUF object we're being added to + xuf = dispatcher.Destination() + + groupId = REQUEST.get('groupId', None) + if groupId: + # Invoke the add method for this plugin + xuf.groupSources[groupId].manage_addMethod(xuf, REQUEST) + else: + raise "BadRequest", "Required parameter 'groupId' omitted" + + dispatcher.manage_main(dispatcher, REQUEST) + + +class GroupSource: + pass + diff --git a/ZopeProducts/exUserFolder/GroupSource/__init__.py b/ZopeProducts/exUserFolder/GroupSource/__init__.py new file mode 100644 index 00000000..dc5d3a81 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2002/12/02 23:20:49 bmh Exp $ +import GroupSource diff --git a/ZopeProducts/exUserFolder/GroupSource/manage_addGroupSourceForm.dtml b/ZopeProducts/exUserFolder/GroupSource/manage_addGroupSourceForm.dtml new file mode 100644 index 00000000..3e6605a0 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSource/manage_addGroupSourceForm.dtml @@ -0,0 +1,33 @@ + + + + + + + + + + +
      + + + + + + + + + +
      + Group Source + + +

      Add ">
      +
      + +
      + \ No newline at end of file diff --git a/ZopeProducts/exUserFolder/GroupSources/__init__.py b/ZopeProducts/exUserFolder/GroupSources/__init__.py new file mode 100644 index 00000000..4f36cdc5 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/__init__.py @@ -0,0 +1,31 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: __init__.py,v 1.1 2004/11/10 14:15:53 akm Exp $ + +import nullGroupSource + +# If this fails due to NUG being absent, just skip it +try: + import zodbGroupSource +except ImportError: + pass + + diff --git a/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/.cvsignore b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/__init__.py b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/__init__.py new file mode 100644 index 00000000..b8943369 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2004/11/10 14:15:53 akm Exp $ +import nullGroupSource diff --git a/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/manage_addNullPluginSourceForm.dtml b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/manage_addNullPluginSourceForm.dtml new file mode 100644 index 00000000..f57a2c3a --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/manage_addNullPluginSourceForm.dtml @@ -0,0 +1,21 @@ + +
      + + + + + + + + + "> + + + + + +This Group Source has no configuration Items
      +
      +Add"> +
      + diff --git a/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/nullGroupSource.py b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/nullGroupSource.py new file mode 100644 index 00000000..cee50ff9 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/nullGroupSource/nullGroupSource.py @@ -0,0 +1,34 @@ +# +# Extensible User Folder +# +# Null Group Source for exUserFolder +# +# Author: Brent Hendricks +# $Id: nullGroupSource.py,v 1.1 2004/11/10 14:15:53 akm Exp $ +from Globals import HTMLFile, INSTANCE_HOME + +from OFS.Folder import Folder + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister +from Products.exUserFolder.nullPlugin import nullPlugin + +def manage_addNullGroupSource(self, REQUEST): + """ Add a Group Source """ + self.currentGroupSource=None + return '' + + +manage_addNullGroupSourceForm=HTMLFile('manage_addNullPluginSourceForm',globals()) +manage_editNullGroupSourceForm=None + + +nullGroupReg=PluginRegister('nullGroupSource', + 'Null Group Source', + nullPlugin, + manage_addNullGroupSourceForm, + manage_addNullGroupSource, + manage_editNullGroupSourceForm) + +exUserFolder.groupSources['nullGroupSource']=nullGroupReg + diff --git a/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/.cvsignore b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/__init__.py b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/__init__.py new file mode 100644 index 00000000..c2e979ba --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2004/11/10 14:15:54 akm Exp $ +import zodbGroupSource diff --git a/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_addzodbGroupSourceForm.dtml b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_addzodbGroupSourceForm.dtml new file mode 100644 index 00000000..2d618cf5 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_addzodbGroupSourceForm.dtml @@ -0,0 +1,21 @@ + + +
      + + + + + + + + + "> + + + + + +This group source requires no user configuration items at this time.
      +NEXT "> +
      + diff --git a/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_editzodbGroupSourceForm.dtml b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_editzodbGroupSourceForm.dtml new file mode 100644 index 00000000..3c04422e --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/manage_editzodbGroupSourceForm.dtml @@ -0,0 +1,7 @@ + + +
      +This group source requires no user configuration items at this time.
      +OK "> +
      + diff --git a/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/zodbGroupSource.py b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/zodbGroupSource.py new file mode 100644 index 00000000..78b441b6 --- /dev/null +++ b/ZopeProducts/exUserFolder/GroupSources/zodbGroupSource/zodbGroupSource.py @@ -0,0 +1,177 @@ +# +# Extensible User Folder +# +# ZODB Group Source for exUserFolder +# +# Author: Brent Hendricks +# $Id: zodbGroupSource.py,v 1.1 2004/11/10 14:15:54 akm Exp $ +from Globals import HTMLFile, MessageDialog, INSTANCE_HOME,Acquisition, PersistentMapping + +from OFS.Folder import Folder + +from Products.ZSQLMethods.SQL import SQL + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister +from Products.NuxUserGroups.UserFolderWithGroups import Group, _marker + +import time +import zLOG +import sys + +manage_addGroupSourceForm=HTMLFile('manage_addzodbGroupSourceForm', globals()) + +def manage_addzodbGroupSource(self, REQUEST): + """ Add a ZODB Group Source """ + + o = zodbGroupSource() + self._setObject('zodbGroupSource', o, None, None, 0) + o = getattr(self, 'zodbGroupSource') + + # Allow Prop Source to setup default users... + if hasattr(o, 'postInitialisation'): + o.postInitialisation(REQUEST) + self.currentGroupSource=o + +manage_addzodbGroupSourceForm=HTMLFile('manage_addzodbGroupSourceForm', globals()) +manage_editzodbGroupSourceForm=HTMLFile('manage_editzodbGroupSourceForm', globals()) + +# +# Very very simple thing, used as an example of how to write a property source +# Not recommended for large scale production sites... +# + +class zodbGroupSource(Folder): + """ Store Group Data inside ZODB, the simplistic way """ + + meta_type='Group Source' + title='Simplistic ZODB Groups' + icon ='misc_/exUserFolder/exUserFolderPlugin.gif' + manage_editForm=manage_editzodbGroupSourceForm + manage_tabs=Acquisition.Acquired + + def __init__(self): + self.id='zodbGroupSource' + self.groups=PersistentMapping() + + + def addGroup(self, groupname, title='', users=(), **kw): + """Creates a group""" + if self.groups.has_key(groupname): + raise ValueError, 'Group "%s" already exists' % groupname + a = 'before: groupname %s groups %s' % (groupname, self.groups) + group = apply(Group, (groupname,), kw) + group.setTitle(title) + group._setUsers(users) + self.groups[groupname] = group + + + def getGroup(self, groupname, default=_marker): + """Returns the given group""" + try: + group = self.groups[groupname] + except KeyError: + if default is _marker: raise + return default + return group + + + def delGroup(self, groupname): + """Deletes the given group""" + usernames = self.groups[groupname].getUsers() + #self.delUsersFromGroup(usernames, groupname) + del self.groups[groupname] + + + def listGroups(self): + """Returns a list of group names""" + return tuple(self.groups.keys()) + + + def getGroupsOfUser(self, username): + "Get a user's groups" + groupnames = [] + allnames = self.listGroups() + groupnames = filter(lambda g, u=username, self=self: u in self.groups[g].getUsers(), allnames) + return tuple(groupnames) + + + def setGroupsOfUser(self, groupnames, username): + "Set a user's groups" + oldGroups = self.getGroupsOfUser(username) + self.delGroupsFromUser(oldGroups, username) + self.addGroupsToUser(groupnames, username) + + + def addGroupsToUser(self, groupnames, username): + "Add groups to a user" + for name in groupnames: + group = self.groups[name] + if not username in group.getUsers(): + group._addUsers([username]) + + + def delGroupsFromUser(self, groupnames, username): + "Delete groups from a user" + for name in groupnames: + group = self.groups[name] + if username in group.getUsers(): + group._delUsers([username]) + + + def getUsersOfGroup(self, groupname): + "Get the users in a group" + return self.groups[groupname].getUsers() + + + def setUsersOfGroup(self, usernames, groupname): + "Set the users in a group" + # uniquify + dict = {} + for u in usernames: dict[u] = None + usernames = dict.keys() + + self.groups[groupname]._setUsers(usernames) + + + def addUsersToGroup(self, usernames, groupname): + "Add users to a group" + # uniquify + dict = {} + for u in usernames: dict[u] = None + usernames = dict.keys() + + self.groups[groupname]._addUsers(usernames) + + + def delUsersFromGroup(self, usernames, groupname): + "Delete users from a group" + # uniquify + dict = {} + for u in usernames: dict[u] = None + usernames = dict.keys() + + self.groups[groupname]._delUsers(usernames) + + + def deleteUsers(self, usernames): + "Delete a list of users" + for user in usernames: + groups = self.getGroupsOfUser(user) + self.delGroupsFromUser(groups, user) + + + def postInitialisation(self, REQUEST): + pass + + + def manage_beforeDelete(self, item, container): + # Notify the exUserFolder that it doesn't have a group source anymore + container.currentGroupSource=None + + +zodbGroupReg=PluginRegister('zodbGroupSource','Simplistic ZODB Group Source', + zodbGroupSource, manage_addzodbGroupSourceForm, + manage_addzodbGroupSource, + manage_editzodbGroupSourceForm) +exUserFolder.groupSources['zodbGroupSource']=zodbGroupReg diff --git a/ZopeProducts/exUserFolder/I18N/ZBabelDictionary_txt.zexp b/ZopeProducts/exUserFolder/I18N/ZBabelDictionary_txt.zexp new file mode 100644 index 0000000000000000000000000000000000000000..be6e17c2c0eea657d458e27f3931101ddc8200b0 GIT binary patch literal 38015 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 Xf35$a|2vAPt^Yf3IUoMtCx88aBqzjV literal 0 HcmV?d00001 diff --git a/ZopeProducts/exUserFolder/LICENSE b/ZopeProducts/exUserFolder/LICENSE new file mode 100644 index 00000000..3a969486 --- /dev/null +++ b/ZopeProducts/exUserFolder/LICENSE @@ -0,0 +1,91 @@ +XUF as a whole is covered by the BSD License, however it uses software +covered by other compatible licenses (see below) + +------------------------------------------------------------------------ + +All of the documentation and software included in the exUserFolder +Releases is copyrighted by The Internet (Aust) Pty Ltd and contributors +ACN: 082 081 472 ABN: 83 082 081 472 + +Copyright 2001, 2002 The Internet (Aust) Pty Ltd + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------------ + +This product includes software developed by Digital Creations for use in +the Z Object Publishing Environment (http://www.zope.org/) + +Portions of smbAuthSource Copyright (C) 2001 Michael Teo + +Portions of radiusAuthSource Copyright (C) 1999 Stuart Bishop + +fcrypt is Copyright (C) 2001, 2001 Carey Evans + +This product includes cryptographic software written by Eric Young +(eay@mincom.oz.au) + +------------------------------------------------------------------------ + +Brief discussion of what the license means to you, not meant to be +all encompassing, but, to give you the general idea. This editorial does +not need to be distributed d8) + +If you want to incorporate this product (or parts of it) into a commercial +product that's fine. + +If you want to modify this product that's fine. + +If you want to modify and distribute this product that's fine (even in +commercial products). + +If you want to incorporate this into a larger work that's fine (even +if that work has a different license). + +None of the previous items place any obligation of notification, compensation, +or return of code to us. In fact we don't care if you do these things. Go +forth and prosper. Basically as long as you recognise that this doesn't +belong to you, you can do what you want with it even charge money for it. + +Note: If you do distribute this as source, then the XUF components are +removable and distributable independently of your license as a whole +(although that's a lot of trouble to go to when they could just download it +from the same place you did). + +What you can't do, is claim it's yours, and this one thing encompasses a lot +of things, here's a few. + +If it's not yours you can't; + +Change the license even if you change the code since the copyright +of the modified files remains with the original copyright holders. + +Use bits of it inside products that require the license to change, because +only the copyright holders have the right to modify the license (not a +concern for commercial projects, only some other Free/Open Source licenses). + +Assign the copyright or other IP to any other party of the whole or any +part (even if you change the code), because it's not yours to give away or +sell to a 3rd party. + +If the fact you can almost do whatever you want with this code isn't +liberal enough for you, contact us and we'll see what we can arrange. diff --git a/ZopeProducts/exUserFolder/LoginRequiredMessages.py b/ZopeProducts/exUserFolder/LoginRequiredMessages.py new file mode 100644 index 00000000..1bee9173 --- /dev/null +++ b/ZopeProducts/exUserFolder/LoginRequiredMessages.py @@ -0,0 +1,27 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: LoginRequiredMessages.py,v 1.2 2001/12/01 08:40:03 akm Exp $ + +LoginRequiredMessages={ + 'session_expired':'Your Session has Expired', + 'unauthorized':'Please Login', + 'login_failed':'Login Failed', + } diff --git a/ZopeProducts/exUserFolder/MembershipSources/__init__.py b/ZopeProducts/exUserFolder/MembershipSources/__init__.py new file mode 100644 index 00000000..9dc1d5ff --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/__init__.py @@ -0,0 +1,25 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2005 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: + +import basicMemberSource +import nullMemberSource + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/.cvsignore b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/PasswordForm.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/PasswordForm.dtml new file mode 100644 index 00000000..98bf3777 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/PasswordForm.dtml @@ -0,0 +1,22 @@ + +
      + + + + + + + + + + + + + + + + +
      Old Password
      Password
      Confirm Password
      Password Hint
      +Change Password "> +
      + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/SignupForm.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/SignupForm.dtml new file mode 100644 index 00000000..98ae04ef --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/SignupForm.dtml @@ -0,0 +1,31 @@ + +
      + + + + + + + + + + + + + + + + + + + + + + + + + +
      Username
      Password
      Confirm Password
      Password Hint
      Real Name
      Email
      +Signup "> +
      + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/__init__.py b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/__init__.py new file mode 100644 index 00000000..a774af80 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2004/11/10 14:15:55 akm Exp $ +import basicMemberSource diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/basicMemberSource.py b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/basicMemberSource.py new file mode 100644 index 00000000..7be098a5 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/basicMemberSource.py @@ -0,0 +1,629 @@ +# +# Extensible User Folder +# +# Basic Membership Source for exUserFolder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: basicMemberSource.py,v 1.1 2004/11/10 14:15:55 akm Exp $ + +# +# Basically membership is a layer between the signup/login form, and +# the authentication layer, it uses the prop source of the users to +# store additional information about a user i.e. doesn't impact on the +# authentication source. +# +# Some membership features imply some extra properties for the user will +# be available; specifically at this time an email property. +# +# You also need a MailHost setup and ready to go for emailing stuff to users +# + +import string,Acquisition +from random import choice + + +from Globals import HTMLFile, INSTANCE_HOME + +from OFS.Folder import Folder +from OFS.DTMLMethod import DTMLMethod + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister + +from base64 import encodestring +from urllib import quote + +import zLOG + +""" +Password Policy enforcement (min/max length, caps etc) +Create Password, or User Chooses. +Timing out of passwords... +Empty Password force change on login... +Create Home Directory +Copy files from Skelton Directory +EMail password hint to user (forgot my password) +Reset password and email user (needs plugin?) +Redirect on login to fixed or varying per username location. +Automatically add users, or manually approve of users. +""" + +# Stupid little things for making a password +# Don't hassle me, it's supposed to be basic. + +nouns=['ace', 'ant', 'arc', 'arm', 'axe', + 'bar', 'bat', 'bee', 'bib', 'bin', + 'can', 'cap', 'car', 'cat', 'cob', + 'day', 'den', 'dog', 'dot', 'dux', + 'ear', 'eel', 'egg', 'elf', 'elk', + 'fad', 'fan', 'fat', 'fig', 'fez', + 'gag', 'gas', 'gin', 'git', 'gum', + 'hag', 'hat', 'hay', 'hex', 'hub'] + +pastConjs = [ 'did', 'has', 'was' ] +suffixes = [ 'ing', 'es', 'ed', 'ious', 'ily'] + +def manage_addBasicMemberSource(self, REQUEST): + """ Add a Membership Source """ + + pvfeatures=[] + minLength=0 + passwordPolicy='' + createHomedir=0 + homeRoot='' + copyFilesFrom='' + postLogin='' + postSignup='' + forgottenPasswords='' + defaultRoles=[] + usersCanChangePasswords=0 + baseURL='' + loginPage='' + signupPage='' + passwordPage='' + mailHost='' + fixedDest='' + + if REQUEST.has_key('basicmember_pvfeatures'): + pvfeatures=REQUEST['basicmember_pvfeatures'] + + if REQUEST.has_key('basicmember_roles'): + defaultRoles=REQUEST['basicmember_roles'] + + if not defaultRoles: + defaultRoles=['Member'] + + if 'minlength' in pvfeatures: + minLength=REQUEST['basicmember_minpasslen'] + + if REQUEST.has_key('basicmember_passwordpolicy'): + passwordPolicy=REQUEST['basicmember_passwordpolicy'] + + if REQUEST.has_key('basicmember_createhomedir'): + homeRoot=REQUEST['basicmember_homeroot'] + createHomedir=1 + + if REQUEST.has_key('basicmember_copyfiles'): + copyFilesFrom=REQUEST['basicmember_copyfiles'] + + if REQUEST.has_key('basicmember_changepasswords'): + usersCanChangePasswords=1 + + if REQUEST.has_key('basicmember_fixeddest'): + fixedDest='' + + forgottenPasswords=REQUEST['basicmember_forgottenpasswords'] + postLogin=REQUEST['basicmember_postlogin'] + + baseURL=REQUEST['basicmember_baseurl'] + loginPage=REQUEST['basicmember_loginpage'] + signupPage=REQUEST['basicmember_signuppage'] + passwordPage=REQUEST['basicmember_passwordpage'] + siteEmail=REQUEST['basicmember_siteemail'] + siteName=REQUEST['basicmember_sitename'] + + mailHost=REQUEST['basicmember_mailhost'] + + # postSignup=REQUEST['basicmember_postsignup'] + + # + # Yep this is obscene + # + o = BasicMemberSource(pvfeatures, minLength, passwordPolicy, + createHomedir, copyFilesFrom, postLogin, + homeRoot, forgottenPasswords, defaultRoles, + usersCanChangePasswords, baseURL, loginPage, + signupPage, passwordPage, mailHost, + siteName, siteEmail, fixedDest) + + self._setObject('basicMemberSource', o, None, None, 0) + o = getattr(self, 'basicMemberSource') + + if hasattr(o, 'postInitialisation'): + o.postInitialisation(REQUEST) + + self.currentMembershipSource=o + return '' + + +manage_addBasicMemberSourceForm=HTMLFile('manage_addBasicMemberSourceForm', + globals()) +manage_editBasicMemberSourceForm=HTMLFile('manage_editBasicMemberSourceForm', + globals()) + +# +# Crap, I don't know why I called this basic, I'd hate to see a +# complicated one. +# +class BasicMemberSource(Folder): + """ Provide High Level User Management """ + meta_type="Membership Source" + title="Basic Membership Source" + icon ='misc_/exUserFolder/exUserFolderPlugin.gif' + manage_tabs=Acquisition.Acquired + manage_editForm=manage_editBasicMemberSourceForm + + # Ugh... + def __init__(self, pvFeatures=[], minLength=0, passwordPolicy='', + createHomeDir=0, copyFilesFrom='', postLogin='', homeRoot='', + forgottenPasswords='', defaultRoles=[], usersCanChangePasswords=0, + baseURL='', loginPage='', signupPage='', passwordPage='', + mailHost='', siteName='', siteEmail='', fixedDest=''): + + self.id='basicMemberSource' + self.pvFeatures=pvFeatures + self.minLength=int(minLength) + self.passwordPolicy=passwordPolicy + self.createHomeDir=createHomeDir + self.copyFilesFrom=copyFilesFrom + self.postLogin=postLogin + self.homeRoot=homeRoot + self.forgottenPasswords=forgottenPasswords + self.defaultRoles=defaultRoles + self.usersCanChangePasswords=usersCanChangePasswords + self.baseURL=baseURL + self.loginPage=loginPage + self.signupPage=signupPage + self.passwordPage=passwordPage + self.siteName=siteName + self.siteEmail=siteEmail + self.fixedDest=fixedDest + + _SignupForm=HTMLFile('SignupForm', globals()) + SignupForm=DTMLMethod() + SignupForm.manage_edit(data=_SignupForm, title='Signup Form') + self._setObject('SignupForm', SignupForm) + + _PasswordForm=HTMLFile('PasswordForm', globals()) + PasswordForm=DTMLMethod() + PasswordForm.manage_edit(data=_PasswordForm, + title='Change Password') + self._setObject('PasswordForm', PasswordForm) + + self.mailHost=mailHost + + _newPasswordEmail=HTMLFile('newPasswordEmail', globals()) + newPasswordEmail=DTMLMethod() + newPasswordEmail.manage_edit(data=_newPasswordEmail, + title='Send New Password') + self._setObject('newPasswordEmail', newPasswordEmail) + + _forgotPasswordEmail=HTMLFile('forgotPasswordEmail', globals()) + forgotPasswordEmail=DTMLMethod() + forgotPasswordEmail.manage_edit(data=_forgotPasswordEmail, + title='Send Forgotten Password') + self._setObject('forgotPasswordEmail', forgotPasswordEmail) + + _passwordHintEmail=HTMLFile('passwordHintEmail', globals()) + passwordHintEmail=DTMLMethod() + passwordHintEmail.manage_edit(data=_passwordHintEmail, + title='Send Forgotten Password Hint') + self._setObject('passwordHintEmail', passwordHintEmail) + + def postInitialisation(self, REQUEST): + if self.createHomeDir and self.homeRoot: + self.findHomeRootObject() + else: + self.homeRootObj=None + + if self.copyFilesFrom: + self.findSkelRootObject() + else: + self.homeSkelObj=None + + # The nice sendmail tag doesn't allow expressions for + # the mailhost + self.mailHostObject=getattr(self, self.mailHost) + + def manage_editMembershipSource(self, REQUEST): + """ Edit a basic Membership Source """ + if REQUEST.has_key('pvfeatures'): + self.pvFeatures=REQUEST['pvfeatures'] + else: + self.pvFeatures=[] + + if REQUEST.has_key('minpasslength'): + self.minLength=REQUEST['minpasslength'] + + if REQUEST.has_key('createhomedir'): + createHomeDir=1 + else: + createHomeDir=0 + + if createHomeDir: + self.copyFilesFrom=REQUEST['copyfiles'] + if self.copyFilesFrom: + self.findSkelRootObject() + else: + self.homeRoot=REQUEST['homeroot'] + self.findHomeRootObject() + + if REQUEST.has_key('memberroles'): + self.defaultRoles=REQUEST['memberroles'] + if REQUEST.has_key('changepasswords'): + self.usersCanChangePasswords=1 + else: + self.usersCanChangePasswords=0 + + self.postLogin=REQUEST['postlogin'] + if REQUEST.has_key('fixeddest'): + self.fixedDest=REQUEST['fixeddest'] + + self.baseURL=REQUEST['baseurl'] + self.loginPage=REQUEST['loginpage'] + self.signupPage=REQUEST['signuppage'] + self.passwordPage=REQUEST['passwordpage'] + self.siteName=REQUEST['sitename'] + self.siteEmail=REQUEST['siteemail'] + return self.MessageDialog(self, + title ='Updated!', + message="Membership was Updated", + action ='manage_editMembershipSourceForm', + REQUEST=REQUEST) + + + + def forgotPassword(self, REQUEST): + username=REQUEST['username'] + curUser=self.getUser(username) + if not curUser: + return self.MessageDialog(self, + title ='No such user', + message="No users matching that username were found.", + action ='%s/%s'%(self.baseURL, self.loginPage), + REQUEST=REQUEST) + + + userEmail=curUser.getProperty('email') + userName=curUser.getProperty('realname') + if self.forgottenPasswords == "hint": + passwordHint=curUser.getProperty('passwordhint') + self.passwordHintEmail(self, + REQUEST=REQUEST, + username=username, + hint=passwordHint, + realname=userName, + email=userEmail) + else: + # make a new password, and mail it to the user + password = self.generatePassword() + curCrypt=self.currentAuthSource.cryptPassword(username,password) + + # Update the user + bogusREQUEST={} + #bogusREQUEST['username']=username + bogusREQUEST['password']=password + bogusREQUEST['password_confirm']=password + bogusREQUEST['roles']=curUser.roles + self.manage_editUser(username, bogusREQUEST) + + self.forgotPasswordEmail(self, + REQUEST=REQUEST, + username=username, + password=password, + realname=userName, + email=userEmail) + return self.MessageDialog(self, + title ='Sent!', + message="Password details have been emailed to you", + action ='%s/%s'%(self.baseURL, self.loginPage), + REQUEST=REQUEST) + + + def changeProperties(self, REQUEST): + + curUser=self.listOneUser(REQUEST['AUTHENTICATED_USER'].getUserName()) + curUser=curUser[0] + if not curUser: + return self.MessageDialog(self, + title ='Erm!', + message="You don't seem to be logged in", + action ='%s/%s'%(self.baseURL, self.passwordPage), + REQUEST=REQUEST) + + + + self.currentPropSource.updateUser(curUser['username'],REQUEST) + + return self.MessageDialog(self, + title ='Properties updated', + message="Your properties have been updated", + action =self.baseURL, + REQUEST=REQUEST, + ) + + + def changePassword(self, REQUEST): + if not self.usersCanChangePasswords: + return '' + + curUser=self.listOneUser(REQUEST['AUTHENTICATED_USER'].getUserName()) + curUser=curUser[0] + if not curUser: + return self.MessageDialog( + title ='Erm!', + message="You don't seem to be logged in", + action ='%s/%s'%(self.baseURL, self.passwordPage), + REQUEST=REQUEST) + + curCrypt=self.currentAuthSource.cryptPassword(curUser['username'],REQUEST['current_password']) + if curCrypt != curUser['password']: + return self.MessageDialog(self, + title ='Password Mismatch', + message="Password is incorrect", + action ='%s/%s'%(self.baseURL, self.passwordPage), + REQUEST=REQUEST) + + if REQUEST['password'] != REQUEST['password_confirm']: + return self.MessageDialog(self, + title ='Password Mismatch', + message="Passwords do not match", + action ='%s/%s'%(self.baseURL, self.passwordPage), + REQUEST=REQUEST) + + # OK the old password matches the one the user provided + # Both new passwords match... + # Time to validate against our normal set of rules... + # + if not self.validatePassword(REQUEST['password'], curUser['username']): + return self.MessageDialog(self, + title ='Password problem', + message="Your password is invalid, please choose another", + action ='%s/%s'%(self.baseURL, self.passwordPage), + REQUEST=REQUEST) + + if self.passwordPolicy=='hint': + if not hasattr(REQUEST,'user_passwordhint'): + return self.MessageDialog(self, + title ='Password requires hint', + message='You must choose a password hint', + action ='%s/%s'%(self.baseURL, self.passwordPage), + REQUEST=REQUEST) + + bogusREQUEST={} + + bogusREQUEST['password']=REQUEST['password'] + bogusREQUEST['password_confirm']=REQUEST['password'] + bogusREQUEST['roles']=curUser['roles'] + self.manage_editUser(curUser['username'],bogusREQUEST) + # update the cookie so he doesnt have to re-login: + if self.cookie_mode: + token='%s:%s' %(curUser['username'], REQUEST['password']) + token=encodestring(token) + token=quote(token) + REQUEST.response.setCookie('__ac', token, path='/') + REQUEST['__ac']=token + + return self.MessageDialog(self, + title ='Password updated', + message="Your password has been updated", + action =self.baseURL, + REQUEST=REQUEST) + + + def goHome(self, REQUEST, RESPONSE): + redirectstring="%s/%s/%s/manage_main"%(self.baseURL, self.homeRoot, REQUEST.AUTHENTICATED_USER.getUserName()) + RESPONSE.redirect(redirectstring) + return '' + + # Tell exUserFolder where we want to go... + def getLoginDestination(self, REQUEST): + script='' + pathinfo='' + querystring='' + redirectstring='' + if self.postLogin=="destination": + script=REQUEST['SCRIPT_NAME'] + pathinfo=REQUEST['PATH_INFO'] + elif self.postLogin=="varied": + script=self.baseURL + pathinfo="/acl_users/goHome" + + elif self.postLogin=="fixed": + pathinfo="%s"%(self.fixedDest) + + if REQUEST.has_key('QUERY_STRING'): + querystring='?'+REQUEST['QUERY_STRING'] + + redirectstring=script+pathinfo + if querystring: + redirectstring=redirectstring+querystring + + return redirectstring + + def validatePassword(self, password, username): + if 'minlength' in self.pvFeatures: + if len(password) < self.minLength: + return 0 + + if 'mixedcase' in self.pvFeatures: + lower = 0 + upper = 0 + for c in password: + if c in string.lowercase: + lower = 1 + if c in string.uppercase: + upper = 1 + if not upper and lower: + return 0 + + if 'specialchar' in self.pvFeatures: + special = 0 + for c in password: + if c in string.punctuation: + special = 1 + break + elif c in string.digits: + special = 1 + break + if not special: + return 0 + + # + # XXX Move this somewhere else + # + + if 'notstupid' in self.pvFeatures: + email='' + # We try some permutations here... + curUser=self.getUser(username) + if curUser: + email = curUser.getProperty('email') + elif hasattr(self, 'REQUEST'): + if self.REQUEST.has_key('user_email'): # new signup + email=self.REQUEST['user_email'] + elif self.REQUEST.has_key('email'): + email=self.REQUEST['email'] + + if ((string.find(password, username)>=0) or + ( email and + (string.find(password, + string.split(email,'@')[0]) >=0))): + return 0 + return 1 + + # These next two look the same (and they are for now), but, the reason I + # Don't use one single method, is I think that SkelObj might migrate to + # using full paths, not relative paths. + + def findSkelRootObject(self): + # Parent should be acl_users + parent = getattr(self, 'aq_parent') + + # This should be the root... + root = getattr(parent, 'aq_parent') + searchPaths = string.split(self.copyFilesFrom, '/') + for o in searchPaths: + if not getattr(root, o): + break + root = getattr(root, o) + + self.homeSkelObj=root + + def findHomeRootObject(self): + # Parent should be acl_users + parent = getattr(self, 'aq_parent') + + # This should be the root... + root = getattr(parent, 'aq_parent') + + searchPaths = string.split(self.homeRoot, '/') + for o in searchPaths: + if o not in root.objectIds(): + root.manage_addFolder(id=o, title=o, createPublic=0, createUserF=0) + root = getattr(root, o) + + self.homeRootObj=root + + def makeHomeDir(self, username): + if not self.homeRootObj: + return + + self.homeRootObj.manage_addFolder(id=username, title=username, createPublic=0, createUserF=0) + home = getattr(self.homeRootObj, username) + + + # Allow user to be in charge of their own destiny + # XXXX WARNING THIS IS A NORMAL FOLDER *SO USERS CAN ADD ANYTHING* + # YOU NEED TO CHANGE THE TYPE OF OBJECT ADDED FOR A USER UNLESS + # THIS IS WHAT YOU WANT TO HAPPEN + home.manage_addLocalRoles(userid=username, roles=['Manager']) + + if self.copyFilesFrom and self.homeSkelObj and self.homeSkelObj.objectIds(): + cp=self.homeSkelObj.manage_copyObjects( + self.homeSkelObj.objectIds()) + home.manage_pasteObjects(cp) + + # Fix it so the user owns their stuff + curUser=self.getUser(username).__of__(self.aq_parent) + home.changeOwnership(curUser, recursive=1) + + def generatePassword(self): + password = (choice(nouns) + choice(pastConjs) + + choice(nouns) + choice(suffixes)) + return password + + def createUser(self, REQUEST): + if self.passwordPolicy == 'user': + if not self.validatePassword(REQUEST['password'], REQUEST['username']): + return self.MessageDialog(self, + title ='Password problem', + message='Your password is invalid, please choose another', + action ='%s/%s'%(self.baseURL, self.signupPage), + REQUEST=REQUEST) + + if self.passwordPolicy=='hint': + if not hasattr(REQUEST,'user_passwordhint'): + return self.MessageDialog(self, + title ='Password requires hint', + message='You must choose a password hint', + action ='%s/%s'%(self.baseURL, self.signupPage), + REQUEST=REQUEST) + + elif self.passwordPolicy == 'system': + REQUEST['password']=self.generatePassword() + REQUEST['password_confirm']=REQUEST['password'] + + # Email the password. + self.newPasswordEmail(self, REQUEST) + + zLOG.LOG("exUserFolder.basicMemberSource", zLOG.BLATHER, + "Creating user", + "Passed all tests -- creating [%s]" % REQUEST['username']) + REQUEST['roles']=self.defaultRoles + self.manage_addUser(REQUEST) # Create the User... + if self.createHomeDir: + self.makeHomeDir(REQUEST['username']) + + return self.MessageDialog(self, + title ='You have signed up', + message='You have been signed up succesfully', + action ='%s'%(self.baseURL), + REQUEST=REQUEST) + + + +basicMemberReg=PluginRegister('basicMemberSource', + 'Basic Membership Source', + BasicMemberSource, + manage_addBasicMemberSourceForm, + manage_addBasicMemberSource, + manage_editBasicMemberSourceForm) +exUserFolder.membershipSources['basicMemberSource']=basicMemberReg + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/forgotPasswordEmail.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/forgotPasswordEmail.dtml new file mode 100644 index 00000000..1dc67b2b --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/forgotPasswordEmail.dtml @@ -0,0 +1,15 @@ + +To: <> +From: <> +Subject: You forgot your password for + +Dear , + +Your username is and your password is now +. + +You should have tested this first, and now that you've tested it, you'll +see you need to customise this method. + + + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_addBasicMemberSourceForm.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_addBasicMemberSourceForm.dtml new file mode 100644 index 00000000..434e1f09 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_addBasicMemberSourceForm.dtml @@ -0,0 +1,143 @@ + +Membership requires a valid property source, you cannot use this with NULL Property Source +
      + + + + + + + + + "> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Site Name (used in emails) +
      Site Email (used for emails) +
      Mail Host + +
      Site Base"> +
      Relative Path (from base) of Login Page +
      Relative Path (from base) of Signup Page +
      Relative Path (from base) of Change Password Page +
      Password Validation Features + +
      Minimum Length (0 if not required) + +
      Password Policy + +
      Forgotten Passwords + +
      Allow users to change passwords + Yes +
      Create 'Home Directory' + Yes +
      Relative Path to 'Home Directory' Root + +
      Copy initial 'Home Directory' files from...(empty=No Copy) + "> +
      After login.... + +
      Fixed Destination + +
      Default Roles + +
      +Add"> +
      + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_editBasicMemberSourceForm.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_editBasicMemberSourceForm.dtml new file mode 100644 index 00000000..c7092b32 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/manage_editBasicMemberSourceForm.dtml @@ -0,0 +1,116 @@ + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Site Name (used in emails) +
      Site Email (used for emails) +
      Mail Host + +
      Site Base +
      Relative Path (from base) of Login Page +
      Relative Path (from base) of Signup Page +
      Relative Path (from base) of Change Password Page +
      Password Validation Features + +
      Minimum Length (if required) + +
      Allow users to change passwords + checked>Yes +
      Create 'Home Directory' + checked>Yes +
      Path to 'Home Directory' Root + +
      Copy initial 'Home Directory' files from...(empty=No Copy) + +
      After login.... + +
      Fixed Destination + +
      Default Roles + +
      +Update "> +
      +
      + + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/newPasswordEmail.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/newPasswordEmail.dtml new file mode 100644 index 00000000..4de7dc6b --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/newPasswordEmail.dtml @@ -0,0 +1,16 @@ + +To: <> +From: <> +Subject: Welcome to + +Dear , + +Welcome to . + +Your username is and your password is . + +You should have tested this first, and now that you've tested it, you'll +see you need to customise this method. + + + diff --git a/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/passwordHintEmail.dtml b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/passwordHintEmail.dtml new file mode 100644 index 00000000..75c7cca9 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/basicMemberSource/passwordHintEmail.dtml @@ -0,0 +1,15 @@ + +To: <> +From: <> +Subject: Hint for + +Dear , + +Your username is and your password hint was; +. + +You should have tested this first, and now that you've tested it, you'll +see you need to customise this method. + + + diff --git a/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/.cvsignore b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/__init__.py b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/__init__.py new file mode 100644 index 00000000..9eb51622 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2004/11/10 14:15:55 akm Exp $ +import nullMemberSource diff --git a/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/manage_addNullPluginSourceForm.dtml b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/manage_addNullPluginSourceForm.dtml new file mode 100644 index 00000000..4ae809bf --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/manage_addNullPluginSourceForm.dtml @@ -0,0 +1,21 @@ + +
      + + + + + + + + + "> + + + + + +This Membership Source has no configuration Items
      +
      +Add"> +
      + diff --git a/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/nullMemberSource.py b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/nullMemberSource.py new file mode 100644 index 00000000..49f26a50 --- /dev/null +++ b/ZopeProducts/exUserFolder/MembershipSources/nullMemberSource/nullMemberSource.py @@ -0,0 +1,49 @@ +# +# Extensible User Folder +# +# Null Membership Source for exUserFolder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: nullMemberSource.py,v 1.1 2004/11/10 14:15:55 akm Exp $ +from Globals import HTMLFile, INSTANCE_HOME + +from OFS.Folder import Folder + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister +from Products.exUserFolder.nullPlugin import nullPlugin + +def manage_addNullMemberSource(self, REQUEST): + """ Add a Membership Source """ + self.currentMembershipSource=None + return '' + + +manage_addNullMemberSourceForm=HTMLFile('manage_addNullPluginSourceForm',globals()) +manage_editNullMemberSourceForm=None + + +nullMemberReg=PluginRegister('nullMemberSource', + 'Null Membership Source', + nullPlugin, + manage_addNullMemberSourceForm, + manage_addNullMemberSource, + manage_editNullMemberSourceForm) +exUserFolder.membershipSources['nullMemberSource']=nullMemberReg + diff --git a/ZopeProducts/exUserFolder/Plugins.py b/ZopeProducts/exUserFolder/Plugins.py new file mode 100644 index 00000000..58cdc2ef --- /dev/null +++ b/ZopeProducts/exUserFolder/Plugins.py @@ -0,0 +1,46 @@ +# +# +# (C) Copyright 2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: Plugins.py,v 1.5 2004/11/10 14:15:33 akm Exp $ + +import App, Globals, OFS +import string +import time + +from Globals import ImageFile, HTMLFile, HTML, MessageDialog, package_home +from OFS.Folder import Folder + +class PluginRegister: + def __init__(self, name, description, pluginClass, + pluginStartForm, pluginStartMethod, + pluginEditForm=None, pluginEditMethod=None): + self.name=name #No Spaces please... + self.description=description + self.plugin=pluginClass + self.manage_addForm=pluginStartForm + self.manage_addMethod=pluginStartMethod + self.manage_editForm=pluginEditForm + self.manage_editMethod=pluginEditMethod + +class CryptoPluginRegister: + def __init__(self, name, crypto, description, pluginMethod): + self.name = name #No Spaces please... + self.cryptoMethod = crypto + self.description = description + self.plugin = pluginMethod diff --git a/ZopeProducts/exUserFolder/PropSources/__init__.py b/ZopeProducts/exUserFolder/PropSources/__init__.py new file mode 100644 index 00000000..e11388e4 --- /dev/null +++ b/ZopeProducts/exUserFolder/PropSources/__init__.py @@ -0,0 +1,28 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: __init__.py,v 1.1 2004/11/10 14:15:55 akm Exp $ + + +import nullPropSource +# aucune autre prop source pour ScoDoc + + + diff --git a/ZopeProducts/exUserFolder/PropSources/nullPropSource/.cvsignore b/ZopeProducts/exUserFolder/PropSources/nullPropSource/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/PropSources/nullPropSource/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/PropSources/nullPropSource/__init__.py b/ZopeProducts/exUserFolder/PropSources/nullPropSource/__init__.py new file mode 100644 index 00000000..14ecc014 --- /dev/null +++ b/ZopeProducts/exUserFolder/PropSources/nullPropSource/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.1 2004/11/10 14:15:56 akm Exp $ +import nullPropSource diff --git a/ZopeProducts/exUserFolder/PropSources/nullPropSource/manage_addNullPluginSourceForm.dtml b/ZopeProducts/exUserFolder/PropSources/nullPropSource/manage_addNullPluginSourceForm.dtml new file mode 100644 index 00000000..a1979b0e --- /dev/null +++ b/ZopeProducts/exUserFolder/PropSources/nullPropSource/manage_addNullPluginSourceForm.dtml @@ -0,0 +1,21 @@ + +
      + + + + + + + + + "> + + + + + +This Property Source has no configuration Items
      +
      +Add"> +
      + diff --git a/ZopeProducts/exUserFolder/PropSources/nullPropSource/nullPropSource.py b/ZopeProducts/exUserFolder/PropSources/nullPropSource/nullPropSource.py new file mode 100644 index 00000000..ab269b03 --- /dev/null +++ b/ZopeProducts/exUserFolder/PropSources/nullPropSource/nullPropSource.py @@ -0,0 +1,50 @@ +# +# Extensible User Folder +# +# Null Membership Source for exUserFolder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: nullPropSource.py,v 1.1 2004/11/10 14:15:56 akm Exp $ +from Globals import HTMLFile, INSTANCE_HOME + +from OFS.Folder import Folder + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister +from Products.exUserFolder.nullPlugin import nullPlugin + +def manage_addNullPropSource(self, REQUEST): + """ Add a Property Source """ + self.currentPropSource=None + return '' + + +manage_addNullPropSourceForm=HTMLFile('manage_addNullPluginSourceForm',globals()) +manage_editNullPropSourceForm=None + + +nullPropReg=PluginRegister('nullPropSource', + 'Null Property Source', + nullPlugin, + manage_addNullPropSourceForm, + manage_addNullPropSource, + manage_editNullPropSourceForm) + +exUserFolder.propSources['nullPropSource']=nullPropReg + diff --git a/ZopeProducts/exUserFolder/PropertyEditor/.cvsignore b/ZopeProducts/exUserFolder/PropertyEditor/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/PropertyEditor/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/PropertyEditor/PropertyEditor.py b/ZopeProducts/exUserFolder/PropertyEditor/PropertyEditor.py new file mode 100644 index 00000000..711d4a0f --- /dev/null +++ b/ZopeProducts/exUserFolder/PropertyEditor/PropertyEditor.py @@ -0,0 +1,122 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: PropertyEditor.py,v 1.3 2002/01/29 17:42:02 alex_zxc Exp $ + +from Globals import DTMLFile, MessageDialog, INSTANCE_HOME +from string import join,strip,split,lower,upper,find +from urllib import quote, unquote + + +def editStringProperty( name, value): + """ """ + return('\n'%(name, value)) + +def viewStringProperty( name, value): + """ """ + return('
      %s
      \n'%(value, value)) + + +def editIntegerProperty( name, value): + """ """ + return('\n'%(name, value or 0)) + +def viewIntegerProperty( name, value): + """ """ + return('
      %d
      \n'%(value or 0 , value or 0)) + + +def editLongProperty( name, value): + """ """ + return('\n'%(name, value or 0)) + +def viewLongProperty( name, value): + """ """ + return('
      %d
      \n'%(value or 0, value or 0)) + + +def editFloatProperty( name, value): + """ """ + return('\n'%(name, value)) + +def viewFloatProperty( name, value): + """ """ + return('
      %f
      \n'%(value, value)) + + +def editListProperty( name, value): + a='' + if value: + a = a + 'Select Items to keep
      \n' + a = a + '\n
      ' + a = a + 'Add an item\n
      ' + a = a + ''%(name) + return(a) + +def viewListProperty( name, value): + a='' + if value: + for i in value: + a = a + ( + '\n'%(i)) + a = a + '%s\n
      '%(i) + return(a) + + +def editDictProperty( name, value): + """ """ + a='' + if value and value.keys(): + for i in value.keys(): + a = a + '%s : \n
      '%(i, name, i, value[i]) + return a + + +def viewDictProperty( name, value): + """ """ + a='' + if value and value.keys(): + for i in value.keys(): + a = a + '%s : \n
      '%(i, name, i, value[i]) + a = a + '%s\n
      '%(value[i]) + return a + +EditMethods={'String':editStringProperty, + 'Integer':editIntegerProperty, + 'Long':editLongProperty, + 'Float':editFloatProperty, + 'List':editListProperty, + 'Dict':editDictProperty} + + +ViewMethods={'String':viewStringProperty, + 'Integer':viewIntegerProperty, + 'Long':viewLongProperty, + 'Float':viewFloatProperty, + 'List':viewListProperty, + 'Dict':viewDictProperty} + + + + diff --git a/ZopeProducts/exUserFolder/PropertyEditor/__init__.py b/ZopeProducts/exUserFolder/PropertyEditor/__init__.py new file mode 100644 index 00000000..4189a84d --- /dev/null +++ b/ZopeProducts/exUserFolder/PropertyEditor/__init__.py @@ -0,0 +1,22 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: __init__.py,v 1.2 2001/12/01 08:40:04 akm Exp $ +import PropertyEditor diff --git a/ZopeProducts/exUserFolder/README.Upgrading b/ZopeProducts/exUserFolder/README.Upgrading new file mode 100644 index 00000000..384001a4 --- /dev/null +++ b/ZopeProducts/exUserFolder/README.Upgrading @@ -0,0 +1,14 @@ +If you are upgrading an existing site from < 0.50 + +I have restructured the source tree. This will make this version +incompatible with previous versions, as the classes have moved. This +breaks upgrading existing installs unless you keep the old classes +around. If you only use external Auth/Prop/Group sources, you will +probably be unaffected. + +This means for those of you using SQL or LDAP or any non-ZODB sources, +you can remove and then re-add your XUF acl_users to get going again. + +If you are using a ZODB source, then you need to keep the old classes +and the old paths around (e.g. symlink zodbAuthSource to +AuthSources/zodbAuthSource). diff --git a/ZopeProducts/exUserFolder/User.py b/ZopeProducts/exUserFolder/User.py new file mode 100644 index 00000000..7b55e0f9 --- /dev/null +++ b/ZopeProducts/exUserFolder/User.py @@ -0,0 +1,243 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: User.py,v 1.10 2004/12/14 05:30:29 akm Exp $ + +############################################################################## +# +# Zope Public License (ZPL) Version 0.9.4 +# --------------------------------------- +# +# Copyright (c) Digital Creations. All rights reserved. +# +# Redistribution and use in source and binary forms, with or +# without modification, are permitted provided that the following +# conditions are met: +# +# 1. Redistributions in source code must retain the above +# copyright notice, this list of conditions, and the following +# disclaimer. +# +# 6. Redistributions of any form whatsoever must retain the +# following acknowledgment: +# +# "This product includes software developed by Digital +# Creations for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# Disclaimer +# +# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND +# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## +from AccessControl.User import BasicUser +from string import join,strip,split,lower,upper,find + +class XUFUser(BasicUser): + + icon='misc_/exUserFolder/exUser.gif' + + # cacheable is a dict that must contain at least name, password, + # roles, and domains -- unless you're working with your own User class, + # in which case you need to override __init__ and define it yourself. + def __init__(self, cacheable, propSource, cryptPassword, authSource, + groupSource=None): + self.name =cacheable['name'] + self.__ =cacheable['password'] + if cacheable['roles']: + self.roles = filter(None, cacheable['roles']) + else: + self.roles = [] + # domains may be passed as a string or a list + if type(cacheable['domains']) == type(''): + self.domains=filter(None, map(strip, + split(cacheable['domains'], ','))) + else: + self.domains=cacheable['domains'] + self._authSource=authSource + self._propSource=propSource + self._groupSource=groupSource + self.cryptPassword=cryptPassword + + def getUserName(self): + return self.name + + def _getPassword(self): + return self.__ + + def getRoles(self): + return tuple(self.roles) + ('Authenticated',) + + def getDomains(self): + return self.domains + + # Ultra generic way of getting, checking and setting properties + def getProperty(self, property, default=None): + if self._propSource: + return self._propSource.getUserProperty(property, self.name, default) + + def hasProperty(self, property): + if self._propSource: + return self._propSource.hasProperty(property) + + def setProperty(self, property, value): + if property[0]=='_': + return + if self._propSource: + return self._propSource.setUserProperty(property, self.name, value) + + def setTempProperty(self, property, value): + if property[0]=='_': + return + if self._propSource: + return self._propSource.setTempProperty(property, value) + + def flushTempProperties(self): + if self._propSource: + return self._propSource.flushTempProperties() + + def delProperty(self, property): + if property[0]=='_': + return + if self._propSource: + return self._propSource.delUserProperty(property, self.name) + + def listProperties(self): + if self._propSource: + return self._propSource.listUserProperties(self.name) + + # Try to allow User['property'] -- won't work for password d;) + def __getitem__(self, key): + # Don't return 'private' keys + if key[0] != '_': + if hasattr(self, key): + return getattr(self, key) + if self._propSource and self._propSource.hasProperty(key): + + return self._propSource.getUserProperty(key, self.name) + raise KeyError, key + + def __setitem__(self, key, value): + if key[0]=='_': + return + if self._propSource: + self._propSource.setUserProperty(key, self.name, value) + + # List one user is supplied by the Auth Source... + + def authenticate(self, listOneUser, password, request, remoteAuth=None): + result=listOneUser(username=self.name) + for people in result: + if remoteAuth: + return remoteAuth(self.name, password) + else: + secret=self.cryptPassword(self.name, password) + return secret==people['password'] + return None + + # You can set logout times or whatever here if you want to, the + # property source is still active. + def notifyCacheRemoval(self): + if self._propSource: + self._propSource.flushTempProperties() + + # You must override this and __init__ if you are subclassing + # the user object, or your user object may not be reconstructed + # properly! All values in this dict must be non-Persistent objects + # or types, and may not hold any references to Persistent objects, + # or the cache will break. + def _getCacheableDict(self): + return {'name': self.name, + 'password': self.__, + 'roles': self.roles, + 'domains': self.domains} + + def getGroups(self): + if self._groupSource: + return self._groupSource.getGroupsOfUser(self.name) + else: + return () + + + def _setGroups(self, groupnames): + if self._groupSource: + return self._groupSource.setGroupsOfUser(groupnames, self.name) + + + def _addGroups(self, groupnames): + if self._groupSource: + return self._groupSource.addGroupsToUser(groupnames, self.name) + + def _delGroups(self, groupnames): + if self._groupSource: + return self._groupSource.delGroupsFromUser(groupnames, self.name) + + def getId(self): + if self._propSource and self._propSource.getUserProperty('userid', self.name): + return self._propSource.getUserProperty('userid', self.name) + return self.name + + +# +# An Anonymous User for session tracking... +# Can set and get properties just like a normal user. +# +# These objects live in the cache, so, we have a __del__ method to +# clean ourselves up. +# + +class XUFAnonUser(XUFUser): + def __init__(self, name, roles, propSource): + self.name =name + self.__ ='' + self.roles =filter(None, roles) + self._propSource=propSource + + def getRoles(self): + return tuple(self.roles) + ('Anonymous',) + + def authenticate(self, listOneUser, password, request, remoteAuth=None): + return 1 + + def notifyCacheRemoval(self): + if self._propSource: + self._propSource.deleteUsers([self.name,]) + +# We now set up a dummy classes so that people can extend the User objects +# or override stuff with much less pain --akm + +class User(XUFUser): + pass + +class AnonUser(XUFAnonUser): + pass + diff --git a/ZopeProducts/exUserFolder/UserCache/.cvsignore b/ZopeProducts/exUserFolder/UserCache/.cvsignore new file mode 100644 index 00000000..57c4bccb --- /dev/null +++ b/ZopeProducts/exUserFolder/UserCache/.cvsignore @@ -0,0 +1,4 @@ +*.pyc +*.pyo +*~ +.*.swp diff --git a/ZopeProducts/exUserFolder/UserCache/UserCache.py b/ZopeProducts/exUserFolder/UserCache/UserCache.py new file mode 100644 index 00000000..b3b6a942 --- /dev/null +++ b/ZopeProducts/exUserFolder/UserCache/UserCache.py @@ -0,0 +1,409 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: UserCache.py,v 1.16 2003/07/10 21:11:26 akm Exp $ + +# Module Level Caches for Various User Operations + +from time import time +from BTrees.OOBTree import OOBTree +import threading +from Acquisition import aq_inner +from Products.exUserFolder.User import User + +class UserCacheItem: + lastAccessed=0 + def __init__(self, username, password, cacheable): + self.username=username + self.password=password + self.cacheable=cacheable + self.lastAccessed=time() + + def touch(self): + self.lastAccessed=time() + + def __repr__(self): + return self.username + +class NegativeUserCacheItem(UserCacheItem): + def __init__(self, username): + self.username=username + self.lastAccessed=time() + +class AdvancedCookieCacheItem(UserCacheItem): + lastAccessed=0 + def __init__(self, username, password): + self.username=username + self.password=password + self.lastAccessed=time() + +class SessionExpiredException(Exception): + 'User Session Expired' + + +class UserCache: + def __init__(self, sessionLength): + self.sessionLength=sessionLength + self.cache=OOBTree() + self.hits=0 + self.fail=0 + self.nouser=0 + self.attempts=0 + self.timeouts=0 + self.cacheStarted=time() + self.lock=threading.Lock() + + def addToCache(self, username, password, User): + self.lock.acquire() + try: + if not self.sessionLength: + return + + try: + u = self.cache.items(username) + if u: + for x in self.cache[username]: + self.cache.remove(x) + except: + pass + + u = UserCacheItem(username, password, User._getCacheableDict()) + self.cache[username]=u + finally: + self.lock.release() + + def getUser(self, caller, username, password, checkpassword=1): + self.lock.acquire() + try: + if not self.sessionLength: + return None + + self.attempts=self.attempts+1 + + u = None + try: + u = self.cache[username] + except KeyError: + self.nouser=self.nouser+1 + return None + + now = time() + if u: + if checkpassword and (u.password != password): + self.fail=self.fail+1 + del self.cache[u.username] + elif self.sessionLength and ( + (now - u.lastAccessed) > self.sessionLength): + del self.cache[u.username] + self.timeouts=self.timeouts+1 + user_object=User(u.cacheable, + caller.currentPropSource, + caller.cryptPassword, + caller.currentAuthSource, + caller.currentGroupSource) + user_object.notifyCacheRemoval() + del u + raise SessionExpiredException + else: + u.touch() + self.hits=self.hits+1 + return User(u.cacheable, + caller.currentPropSource, + caller.cryptPassword, + caller.currentAuthSource, + caller.currentGroupSource) + + self.nouser=self.nouser+1 + return None + finally: + self.lock.release() + + def removeUser(self, username): + self.lock.acquire() + try: + if not self.sessionLength: + return + try: + if self.cache[username]: + del self.cache[username] + except: + pass + finally: + self.lock.release() + + def getCacheStats(self): + self.lock.acquire() + try: + return ( + {'attempts':self.attempts, + 'hits':self.hits, + 'fail':self.fail, + 'misses':self.nouser, + 'cachesize':len(self.cache), + 'time':self.cacheStarted, + 'timeouts':self.timeouts, + 'length':self.sessionLength}) + finally: + self.lock.release() + + def getCurrentUsers(self, caller): + self.lock.acquire() + try: + x=[] + now = time() + for z in self.cache.keys(): + u = self.cache[z] + if self.sessionLength and ( + (now - u.lastAccessed) > self.sessionLength): + del self.cache[u.username] + self.timeouts=self.timeouts+1 + user_object=User(u.cacheable, + caller.currentPropSource, + caller.cryptPassword, + caller.currentAuthSource, + caller.currentGroupSource) + user_object.notifyCacheRemoval() + del u + else: + x.append({'username':u.username, + 'lastAccessed':u.lastAccessed}) + return x + finally: + self.lock.release() + +class NegativeUserCache: + def __init__(self, sessionLength): + self.sessionLength=sessionLength + self.cache=OOBTree() + self.hits=0 + self.cacheStarted=time() + self.lock=threading.Lock() + + def addToCache(self, username): + self.lock.acquire() + try: + if not self.sessionLength: + return + + try: + u = self.cache.items(username) + if u: + for x in self.cache[username]: + self.cache.remove(x) + except: + pass + + u = NegativeUserCacheItem(username) + self.cache[username]=u + finally: + self.lock.release() + + def getUser(self, username): + self.lock.acquire() + try: + if not self.sessionLength: + return 0 + + u = None + try: + u = self.cache[username] + except KeyError: + return 0 + + now = time() + if u: + if self.sessionLength and ( + (now - u.lastAccessed) > self.sessionLength): + del self.cache[u.username] + else: + # We don't touch negative user caches + # u.touch() + self.hits=self.hits+1 + return 1 + return 0 + finally: + self.lock.release() + + def removeUser(self, username): + self.lock.acquire() + try: + if not self.sessionLength: + return + try: + del self.cache[username] + except: + pass + finally: + self.lock.release() + +class CookieCache: + def __init__(self, sessionLength): + self.sessionLength=sessionLength + self.cache=OOBTree() + self.hits=0 + self.cacheStarted=time() + self.lock=threading.Lock() + + def addToCache(self, username, password, key): + self.lock.acquire() + try: + if not self.sessionLength: + return + + try: + u = self.cache.items(key) + if u: + for x in self.cache[key]: + self.cache.remove(x) + except: + pass + u = AdvancedCookieCacheItem(username, password) + self.cache[key]=u + finally: + self.lock.release() + + def getUser(self, key): + self.lock.acquire() + try: + if not self.sessionLength: + return None + + u = None + try: + u = self.cache[key] + except KeyError: + return None + + now = time() + if u: + if self.sessionLength and ( + (now - u.lastAccessed) > self.sessionLength): + del self.cache[key] + else: + # We don't touch negative user caches + # u.touch() + self.hits=self.hits+1 + return u.username, u.password + return None + finally: + self.lock.release() + + def removeUser(self, key): + self.lock.acquire() + try: + if not self.sessionLength: + return + try: + del self.cache[key] + except: + pass + finally: + self.lock.release() + +class GlobalUserCache: + caches={} + def __init__(self): + self.lock = threading.Lock() + + def createCache(self, who, sessionLength): + self.lock.acquire() + try: + self.caches[who]=UserCache(sessionLength) + return self.caches[who] + finally: + self.lock.release() + + def getCache(self, who): + self.lock.acquire() + try: + if self.caches.has_key(who): + return self.caches[who] + else: + return None + finally: + self.lock.release() + + def deleteCache(self, who): + self.lock.acquire() + try: + del self.caches[who] + finally: + self.lock.release() + +class GlobalNegativeUserCache: + caches={} + def __init__(self): + self.lock = threading.Lock() + + def createCache(self, who, sessionLength): + self.lock.acquire() + try: + self.caches[who]=NegativeUserCache(sessionLength) + return self.caches[who] + finally: + self.lock.release() + + def getCache(self, who): + self.lock.acquire() + try: + if self.caches.has_key(who): + return self.caches[who] + else: + return None + finally: + self.lock.release() + + def deleteCache(self, who): + self.lock.acquire() + try: + del self.caches[who] + finally: + self.lock.release() + +class GlobalAdvancedCookieCache: + caches={} + def __init__(self): + self.lock = threading.Lock() + + def createCache(self, who, sessionLength): + self.lock.acquire() + try: + self.caches[who]=CookieCache(sessionLength) + return self.caches[who] + finally: + self.lock.release() + + def getCache(self, who): + self.lock.acquire() + try: + if self.caches.has_key(who): + return self.caches[who] + else: + return None + finally: + self.lock.release() + + def deleteCache(self, who): + self.lock.acquire() + try: + del self.caches[who] + finally: + self.lock.release() + diff --git a/ZopeProducts/exUserFolder/UserCache/__init__.py b/ZopeProducts/exUserFolder/UserCache/__init__.py new file mode 100644 index 00000000..5a484ebd --- /dev/null +++ b/ZopeProducts/exUserFolder/UserCache/__init__.py @@ -0,0 +1,22 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: __init__.py,v 1.2 2001/12/01 08:40:04 akm Exp $ +import UserCache diff --git a/ZopeProducts/exUserFolder/__init__.py b/ZopeProducts/exUserFolder/__init__.py new file mode 100644 index 00000000..49f0cbcc --- /dev/null +++ b/ZopeProducts/exUserFolder/__init__.py @@ -0,0 +1,85 @@ +# +# Extensible User Folder +# +# (C) Copyright 2000-2005 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: __init__.py,v 1.18 2004/11/10 14:15:33 akm Exp $ + +import exUserFolder + +import CryptoSources +import AuthSources +import PropSources +import MembershipSources +import GroupSources + +from GroupSource import GroupSource + +from App.ImageFile import ImageFile +import OFS + +# +# Install a dummy ZBabel setup if we don't have ZBabel installed. +# +import dummyZBabelTag + +# Methods we need access to from any ObjectManager context +legacy_methods = ( + ('manage_addexUserFolderForm', exUserFolder.manage_addexUserFolderForm), + ('manage_addexUserFolder', exUserFolder.manage_addexUserFolder), + ('getAuthSources', exUserFolder.getAuthSources), + #('getPropSources', exUserFolder.getPropSources), + ('getCryptoSources', exUserFolder.getCryptoSources), + ('getMembershipSources', exUserFolder.getMembershipSources), + ('getGroupSources', exUserFolder.getGroupSources), + ('doAuthSourceForm', exUserFolder.doAuthSourceForm), + #('doPropSourceForm', exUserFolder.doPropSourceForm), + ('doMembershipSourceForm', exUserFolder.doMembershipSourceForm), + # ('doGroupSourceForm', exUserFolder.doGroupSourceForm), + ('getVariableType', exUserFolder.getVariableType), + ('DialogHeader', exUserFolder.exUserFolder.DialogHeader), + ('DialogFooter', exUserFolder.exUserFolder.DialogFooter), + #('MailHostIDs', exUserFolder.MailHostIDs), + ) + +# Image files to place in the misc_ object so they are accesible from misc_/exUserFolder +misc_={'exUserFolder.gif': ImageFile('exUserFolder.gif', globals()), + 'exUserFolderPlugin.gif': ImageFile('exUserFolderPlugin.gif', globals()), + 'exUser.gif': ImageFile('exUser.gif', globals()), + } + + +def initialize(context): + """ + Register base classes + """ + context.registerClass(exUserFolder.exUserFolder, + meta_type="ex User Folder", + permission="Add exUser Folder", + constructors=(exUserFolder.manage_addexUserFolderForm, + exUserFolder.manage_addexUserFolder,), + legacy=legacy_methods, + icon="exUserFolder.gif") + + context.registerClass(GroupSource.GroupSource, + meta_type="ex User Folder Group Source", + permission="Add exUser Folder", + constructors=(GroupSource.manage_addGroupSourceForm, + GroupSource.manage_addGroupSource,), + icon="exUserFolderPlugin.gif") + diff --git a/ZopeProducts/exUserFolder/common/DialogFooter.dtml b/ZopeProducts/exUserFolder/common/DialogFooter.dtml new file mode 100644 index 00000000..00add5fe --- /dev/null +++ b/ZopeProducts/exUserFolder/common/DialogFooter.dtml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ZopeProducts/exUserFolder/common/DialogHeader.dtml b/ZopeProducts/exUserFolder/common/DialogHeader.dtml new file mode 100644 index 00000000..fad53794 --- /dev/null +++ b/ZopeProducts/exUserFolder/common/DialogHeader.dtml @@ -0,0 +1,7 @@ +","
      + + + + + + ","
      diff --git a/ZopeProducts/exUserFolder/common/MessageDialog.dtml b/ZopeProducts/exUserFolder/common/MessageDialog.dtml new file mode 100644 index 00000000..0d0bc555 --- /dev/null +++ b/ZopeProducts/exUserFolder/common/MessageDialog.dtml @@ -0,0 +1,41 @@ + +
      TARGET=""> + + + + + + + + + "> + + + + + + + + + + + + +
      +
      +
      !
      +
      +

      +
      + +
      +
      + +
      + Ok "> +
      +
      + + + + diff --git a/ZopeProducts/exUserFolder/common/manage_tabs.dtml b/ZopeProducts/exUserFolder/common/manage_tabs.dtml new file mode 100644 index 00000000..c6c66c2e --- /dev/null +++ b/ZopeProducts/exUserFolder/common/manage_tabs.dtml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
         + ">
       href="&dtml-action;"href="&dtml-URL1;" target="&dtml-target;"> +   href="&dtml-action;"href="&dtml-URL1;" target="&dtml-target;"> +  
      +
      + + +
      + + + + + + + + + + + + + + + +
      +
      + + &dtml-meta_type; + + + + + &dtml-meta_type; + + &dtml-meta_type; + + + Object + + at + + + + This item has been modified in this version + + This item has been modified in another version + () + + + + This item has been locked by WebDAV + +
      +
      +
      + +
      +
      +
      + +
      +
      + + +
      +You are currently working in version +
      +
      +
      + + +
      + +() +
      +
      + +
      diff --git a/ZopeProducts/exUserFolder/common/text_manage_tabs.dtml b/ZopeProducts/exUserFolder/common/text_manage_tabs.dtml new file mode 100644 index 00000000..62922a0e --- /dev/null +++ b/ZopeProducts/exUserFolder/common/text_manage_tabs.dtml @@ -0,0 +1,19 @@ + + + +
      + + + + +
      + + href="" + href="" + target=""> + []  + +
      +
      +
      +
      diff --git a/ZopeProducts/exUserFolder/doc/FAQ.txt b/ZopeProducts/exUserFolder/doc/FAQ.txt new file mode 100644 index 00000000..d2a0be5d --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/FAQ.txt @@ -0,0 +1,119 @@ +Frequently Asked Questions + +1. Why shouldn't I use Core Session Tracking + Login Manager? +XUF serves a different set of users to the combination above. XUF +aims to be a simple out of the box solution. Login Manager allows +for very complex authorisation schemes that can query multiple user +sources. We don't do that. + +2. Why use XUF at all? +In its simplest configuration, XUF provides the same functionality +as the standard User Folder, but, is more secure. Passwords are +stored encrypted, which is not the case for the standard User Folder. +So even if you don't want to set properties on users, or any +membership facilities, there is a benefit to running XUF. + +3. Do I have to have all this other stuff? +No. The only thing you need to enable is authentication. There is +a null property source, and a null membership source. Everything +other than authentication is optional. + +4. Can I use it as a root folder? +Some people have reported success in doing so. We don't recommend +it for various reasons. The main one is that the internal Zope API +can change without warning, which could break XUF and lock you out +of your Zope. This can happen with any User Folder product. We +recommend you look at VHM and other Site Access methods to allow +you to store your main site in a sub-folder. + +5. When will XUF support authentication against XYZ system? +That depends. First the active developers need to have an interest +in it, and more importantly they need to be able to test it. Writing +your authentication method is very simple, so if you understand +what you want to authenticate against, and know some python you +could write one in an hour. You can also use the usAuthSource to +write one using PythonScripts, ExternalMethods, DTML, or any other +callable method that Zope supports. + +6. I wrote this cool authentication source can I get it into the main + distribution? +Yes and No. If your authentication is Open Source, and has a +compatible license with XUF, and doesn't require any external +libraries, odds are it'll go into the main distribution. If it +depends on external libraries, it's possible it can conditionally +go into the main distribution. The nice thing about XUF is that +Authentication, Property, and Membership sources are all packagable +as independent products, so you can distribute it as a standalone +product, and it'll work (without having to have the code drop into +the XUF directory either). + +7. Is XUF going to be part of the Core Zope? +No idea. At the moment (0.10.5) XUF is probably not at a level that +Zope Corporation would consider mature enough for core inclusion +anyway. + +Actually the answer now, is probably not. At a minimum smbAuthSource, +and radiusAuthSource would have to be stripped and distributed +seperately. Over and above that, I would have to assign Zope Corp +co-ownership rights on the IP, which amongst other things gives +them or anyone that buys them unlimited access to future derived +works. I refuse to do this on principle, the liberal licensing of +the product should be more than adequate for any (especially open +source) endeavour. + +8. What's with the Management Screens? +It's a joke on the Zope World. + +9. But they're really ugly I want to change them. +That's fine, you do that, that's the point. + +10. Can I send you patches to put them back to standard Management + Screens? +You can put patches into the tracker at Source Forge if you want to. + +11. HELP!!!! I tried to install XUF as my root folder, without + reading the FAQ, or really knowing what I'm doing, and now I'm + hosed!!! +That's a shame. + +12. Will XUF work with ZEO? +Unknown. However, it's almost certain that in its current form +credential caching will not work across ZEO -- you will get a +seperate User Cache for each Zope Client (which isn't really all +that bad). However, it means that if you want to use Session Tracking, +you need to lock users to one particular server. Most commercial +Load Balancers do this for you anyhow. A persistent cache will form +part of an upcoming release which will allow all of this to work +transparently across ZEO farms. + +13. Shouldn't it be EUF? +No, it should be XUF :-P + +14. How can I log in a user via a form submission? +Yes, the key is sending the __ac_name and __ac_password (yes that's +two underscores in front) as form variables to any object covered +by the XUF. + +This form will take your users to the /index_html and log them in. +You can place this anywhere in your site including /index_html. + +
      +Name:
      +Password: + +
      + +15. That Dialog box sure is ugly! How can I customize it so it looks + right in my site? +Under the contents tab add an object called MessageDialog , it can +be a dtml document, method, or even a page template. Make it look +how you want, it will acquire all objects from the folder the XUF +is in so you can use the standard_html_header, call scripts, etc. + +16. Why can't I change the default crypto method? +Because someone will change it, and all the existing passwords will cease +to work. Then I'll get lots of hate-mail, or get slagged off on other mailing +lists :-) + +17. Where is the Zen Master's Guide to exUserFolder? +Everywhere and nowhere. diff --git a/ZopeProducts/exUserFolder/doc/README.API b/ZopeProducts/exUserFolder/doc/README.API new file mode 100644 index 00000000..ab6e8ebd --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.API @@ -0,0 +1,171 @@ +User Sources +------------ + +This is the list of functions your auth source should provide. + +createUser(self, username, password, roles) +Create a User to authenticate against username, password and roles, +should all be obvious. + + +updateUser(self, username, password, roles) +Update a user's roles and password. +An empty password means do not change passwords, so at least at this time +it's not possible to have passwordless accounts. + + +cryptPassword(self, username, password) +Encrypt a password +If no 'crypt' method is supplied return the Password -- +i.e. plaintext password. + + +deleteUsers(self, userids) +Delete a set of users, userids is a list of usernames to delete. + + +listUserNames(self) +returns a list of usernames. + +listOneUser(self,username) +Return one user matching the username +Should be a dictionary; + {'username':username, 'password':cryptedPassword, 'roles':list_of_roles} +Once again, you can provide more information than this if you're customising +the forms. + +listUsers(self) +Return the list of users in the same format as listOneUser + + +remoteAuthMethod(self, username, password) +You can define this to go off and do the authentication instead of +using the basic one inside the User Object. Useful for IMAP/LDAP auth +type methods where the authentication is handled elsewhere and we just want +to know success or failure. If you don't want to do this you should have; + +remoteAuthMethod=None + +in your AuthSource (to explicitly tell the exUserFolder that you don't). + +------------------------------------------------------------------------ +This is a skeleton class; + +manage_addfooAuthSourceForm=HTMLFile('manage_addfooAuthSourceForm', globals()) +manage_editfooAuthSourceForm=HTMLFile('manage_editfooAuthSourceForm', globals()) + +class fooAuthSource(Folder): + + meta_type='Authentication Source' + title='User Supplied Authentication' + icon ='misc_/exUserFolder/exUserFolderPlugin.gif' + manage_editForm=manage_editfooAuthSourceForm + + def __init__(self): + self.id='fooAuthSource' + + # Create a User to authenticate against + # username, password and roles + def createUser(self, username, password, roles): + """ Add A Username """ + pass + + # Update a user's roles and password + # An empty password means do not change passwords... + def updateUser(self, username, password, roles): + pass + + # Encrypt a password + # If no 'crypt' method is supplied return the + # Password -- i.e. plaintext password + def cryptPassword(self, username, password): + pass + + # Delete a set of users + def deleteUsers(self, userids): + pass + + # Return a list of usernames + def listUserNames(self): + pass + + # Return a list of user dictionaries with + # {'username':username} can be extended to pass back other + # information, but, we don't do that just now + def listUsers(self): + pass + + # Return one user matching the username + # Should be a dictionary; + # {'username':username, 'password':cryptedPassword, 'roles':list_of_roles} + def listOneUser(self,username): + pass + + # + # Return a list of users, dictionary format as for listOneUser + # + def getUsers(self): + pass + + # + # You can define this to go off and do the authentication instead of + # using the basic one inside the User Object + # + remoteAuthMethod=None + +## def remoteAuthMethod(self, username, password): +## pass + + def postInitialisation(self, REQUEST): + pass + +fooAuthReg=PluginRegister('fooAuthSource', 'User Supplied Authentication Source', + fooAuthSource, manage_addfooAuthSourceForm, + manage_addfooAuthSource, + manage_editfooAuthSourceForm) +exUserFolder.authSources['fooAuthSource']=fooAuthReg + +------------------------------------------------------------------------ + +Property Sources +---------------- + +Property Sources have only a few things they need to provide; + +hasProperty(self, key) +Returns true if the current user has that property + +setProperty(self, key, value) +Sets a property for the current user + + +setUserProperty(self, key, username, value) +Sets a property for the given user. + + +getProperty(self, key, default=None) +Returns the requested property or the default for the current user. + + +getUserProperty(self, key, username, default=None) +Returns the requested property or the default for the named user. + + +listProperties(self) +Returns a list of properties (just the properties not their values). + + +listUserProperties(self, username) +Returns a list of properties for the named user. + +createUser(self, username, REQUEST) +Creates a new user, and adds in the properties in the REQUEST. +New properties are preceded with "user_KEYNAME", so strip user_ to +set the property. + +deleteUsers(self, userids) +Delete the list of users (and their properties) contained within userids. + +updateUser(self, username, REQUEST) +Change the list of properties for a user, the variables are formatted as for +createUser. diff --git a/ZopeProducts/exUserFolder/doc/README.LDAPAuthSource b/ZopeProducts/exUserFolder/doc/README.LDAPAuthSource new file mode 100644 index 00000000..fa3bc004 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.LDAPAuthSource @@ -0,0 +1,20 @@ +This is a reimplementation of the auth_ldap auth source for apache, written by +Dave Carrigan and others. + +You can find the original auth_ldap Apache code at +http://www.rudedog.org/auth_ldap/ + +This auth source is covered by the Apache license; + +Copyright (C) 1998, 1999 Enbridge Pipelines Inc. +Copyright (C) 1999-2001 Dave Carrigan +Copyright (C) 2003 The Internet (Aust) Pty Ltd. +All rights reserved. + +This module is free software; you can redistribute it and/or modify +it under the same terms as Apache itself. This module 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. The copyright holder of this +module can not be held liable for any general, special, incidental +or consequential damages arising out of the use of the module. diff --git a/ZopeProducts/exUserFolder/doc/README.Membership b/ZopeProducts/exUserFolder/doc/README.Membership new file mode 100644 index 00000000..17937f39 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.Membership @@ -0,0 +1,144 @@ +There are now membership hooks defined, and a basic membership +source defined for exUserFolder. This is a first stab at this, so +please be careful :-) + +Membership adds a level of complexity to everything, and basically is a +controlled way of breaching your security. + +You will need to prepare a few things before trying to add an exUserFolder +with Membership support; + +a) You will need a MailHost object available +b) You will need to define some methods for Membership to use + i) a Login Page + ii) a Change Password Page + iii) a Signup Page + iv) a Forgot My Password Page (optional) + v) a change Properties Page (optional) + These should live at the same level as your acl_user (i.e. not inside). + These should be fairly simple, I've included some examples below. + These should just wrap the ones below acl_users. There will be methods + you can edit to get all your fields and layout done. +c) If you want the results pages from Signup, Change Password, etc to fit + your site, you'll need to add a MessageDialog document in the contents + of the XUF. See FAQ 15 for more + + +When you see the creation form, obviously some of the options are +mutually exclusive. + +e.g. You can't choose system defined passwords, and have the system + email a hint, if they forgot their password. So try to pick sane + combinations of options. + + + + + +If you choose to have Home Directories, basicMemberSource will create +the path you provide, so you don't need to do that in advance. + +If you want to have skeleton files copied to their homedir you'll need +to have that directory (it can be empty) setup and ready to go, before +the first user signs up. + +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +If you get basicMembershipSource to create Home Directories for your +users, it will create a 'Folder' and it will give the user management +permissions on that Folder. This means that they will be able to add +any object you can, just at a level below this. You should create/have +a proper HomeFolder object that is restricted in what is available +for adding, and change makeHomeDir() in basicMemberSource.py to create +one of those. + +I will look at creating a restricted HomeDirectory Object in a later +release, and allow you to add and remove meta_types from it. +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + +======================================================================== + + + +------------------------------------------------------------------------ +LoginForm +------------------------------------------------------------------------ + + + +------------------------------------------------------------------------ + + + +------------------------------------------------------------------------ +ChangePasswordForm +------------------------------------------------------------------------ + + + + + + + +------------------------------------------------------------------------ + + + +------------------------------------------------------------------------ +SignupForm +------------------------------------------------------------------------ + + + + + + + + + +------------------------------------------------------------------------ +ForgotMyPassword +------------------------------------------------------------------------ + +
      +Username: +
      + + + + +------------------------------------------------------------------------ +ChangePropertiesForm +------------------------------------------------------------------------ + + +

      Changing Properties for

      + + +
      + + +
      +

      Properties

      + + + + + + + + + +
      Property NameValue
      ">
      + + +
      + +
      + +------------------------------------------------------------------------ diff --git a/ZopeProducts/exUserFolder/doc/README.httpsAuthSource b/ZopeProducts/exUserFolder/doc/README.httpsAuthSource new file mode 100644 index 00000000..21697df3 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.httpsAuthSource @@ -0,0 +1,28 @@ +This plugin implements authentication from an https service. + +Upon installation, the mangament forms allow you to configure: +* the url to the service, +* the parameter that will contain the username +* the parameter that will contain the password +* The expected authorization response regex (returned from the authorization service). +* The default role that authorized users will be assinged upon their first login + +The https auth source posts a request over https to the named service with the username and +passowrd passed according to the parameters defined in the configuration. It will attempt +to match the authorization pattern specified, and if the pattern is found, the user will be +authenticated. + +Once a user has logged in, they will appear in xuf user list, and their roles can be updated. + +This auth source has been developed using the zodbBTreeProps plugin, and stores the user's +roles in this property tool. + +A typical use case for this authorization service might be to authenticate against +a legacy user directory for which no xuf auth plugin currently exists. Hopefully, the +development of a the auth service on the other end will be trivial, or better yet, +already exist. + +IMPORTANT NOTE: In order to use this plugin you must compile your python to include +ssl support. The python that ships with zope 2.X does not have this enabled by default. + +Thanks to akm, bcsaller, runyaga for all their help. diff --git a/ZopeProducts/exUserFolder/doc/README.pgAuthSource-Alternate b/ZopeProducts/exUserFolder/doc/README.pgAuthSource-Alternate new file mode 100644 index 00000000..8652263c --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.pgAuthSource-Alternate @@ -0,0 +1,20 @@ +This alternate pgAuthSource was developed to allow Zope and jakarta-tomcat to +share common PostGreSQL auth tables. It's really just a mod of the original +pgAuthSource, with changes to the original kept to a minimum. This should help +when it comes to cross porting improvements / maintenence changes between the +two versions. + +The only thing that's new is the table schema. This auth source uses: +A user table + Username, password + +A role table: + rolename + +and a associative userrole table for relating the two: + username, rolename + +ps. Use the Source, Luke! +If you dig a little you will find a couple of different ways of crypting +passwords commented out (plain and MD5). + diff --git a/ZopeProducts/exUserFolder/doc/README.radiusAuthSource b/ZopeProducts/exUserFolder/doc/README.radiusAuthSource new file mode 100644 index 00000000..60b940a5 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.radiusAuthSource @@ -0,0 +1,12 @@ +I have converted ZRadius to work with exUserFolder as an AuthSource +it should function at least as well as it functioned with GUF d8) +-- akm + +Radius + + This product implements simple Radius authentication. If you need + to authenticate Zope users from a Radius server, this product will + plug into the GenericUserFolder product. + + © Copywrite 1999 Stuart Bishop <zen@cs.rmit.edu.au> + diff --git a/ZopeProducts/exUserFolder/doc/README.smbAuthSource b/ZopeProducts/exUserFolder/doc/README.smbAuthSource new file mode 100644 index 00000000..ba4e52ab --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.smbAuthSource @@ -0,0 +1,41 @@ + +SMB Authentication Source for exUserFolder + +(C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +ACN: 082 081 472 ABN: 83 082 081 472 +All Rights Reserved + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +This smbAuthSource uses pySMB by Michael Teo whose copyright is as follows. + +Copyright (C) 2001 Michael Teo + +This software is provided 'as-is', without any express or implied warranty. +In no event will the author be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice cannot be removed or altered from any source distribution. + diff --git a/ZopeProducts/exUserFolder/doc/README.txt b/ZopeProducts/exUserFolder/doc/README.txt new file mode 100644 index 00000000..3152aa79 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.txt @@ -0,0 +1,127 @@ +A lot of this stuff is now covered in the Unenlightened Zopistas Guide to +exUserFolder, so this document is slightly redundant (but not completely). +It's also shockingly out of date... + +A note on the version numbering... there's none of that odd/even +numbering nonsense you find in Lin*x land. The numbers are based on + +Major.Minor.Micro + +A bump in Major means a milestone or set of milestones has been reached. +A bump in Minor means some piece of functionality has been added, or +a major bug was fixed. +A bump in Micro usually means bug fixes. + +These numbers go from 0-99, and are not necessarily continuous or +monotonically increasing (but they are increasing). + +What you consider major and what I consider major are probably two +different things. + +Release candidates before a Major bump start at 99.. so; + +0.99.0 is the first Release Candidate prior to 1.0.0 +0.99.1 is the next Release Candidate +1.0.0 is the 'final' release. + +It's possible that there will be no changes between final release candidate +and release. + +Sometimes due to the nature of the changes a release will be marked +development. This usually means some core functionality was changed. + +Extensible User Folder + +Extensible User Folder is a user folder that requires the authentication +of users to be removed from the storage of properties for users. + +Writing new authentication or property sources is (almost) a trivial operation +and require no authentication knowledge to write, most simply return lists +of attributes, or dictionaries. You don't need to incorporate them into +the base exUserFolder code, they can be written as normal Zope Products, +(i.e. you can distribute new sources independently of exUserFolder). + +There are three authentication sources provided OOTB; + +o pgAuthSource -- Postgresql Authentication Source +Actually this is pretty generic, and could be used for most SQL databases, +the schema isn't all that advanced either. + +This source allows you to specify the table, and the name of each of the +columns (username, password, roles), so you can use an existing database. + + +All ZSQL Methods are available inside the pgAuthSource folder for editing. +You need to have a DB Connection already in place for use. + +o usAuthSource -- User Supplied Authentication +This is similar to Generic User Folder, or Generic User Sources for +Login Manager. You provide a set of methods; + +createUser -- if you want to create users. +cryptPassword -- if you want to encrypt passwords. +listUsers -- return a list of userIds. +listOneUser -- return a dictionary containing the username, password, and + a list of roles +getUsers -- return a list of users ala listOneUser but lots of them. + +that's it. listOneUser is mandatory. + +There is an example of ExternalMethods you could use to create an 'sql' +user source in the Extensions directory. + +o etcAuthSource -- File Based Authentication +This is etcUserFolder reworked to be a plugin for this. Since I used +etcUserFolder as a base for this product, I might as well add the functionality +in. + +Each of the plugins has a 'manage_addForm' that is called when the User Folder +is added, so that parameters can be garnered from the user (pg*Source e.g. +get the dbconnection) + +etcAuthSource doesn't allow roles to be set, although it does ask for +a default role to be assigned to a user (which it dutifully does). + + +There are two property sources provided: + +o pgPropertySource -- Postgresql Property Source +A very naive sql implementation for properties, works fine, I wouldn't want +to load it up too high though. + +o zodbProperySource -- ZODB Property Source +This is a very simple property keeper, and is more available as an example +of what functionality needs to be provided. + +There is a postUserCreate method which you can replace with anything really, +if you want to do something once a user is created (send an email to someone, +page someone...) + +You can mix-n-match authentication methods and property methods. + +You can have cookie or standard (Basic) authentication. + +docLogin and docLogout methods are present in the ZODB for editing, because +you will hate the way the default login and logout pages look d8) + +The various plugins need some more configurable options atm, but, it +shouldn't be that much of a drama to add them in soon. + +Arbitrary properties can be set on a user (at least if the PropertySource +is written correctly they can). + + + + + + +Will all work (assuming the user is logged in). + +When creating a new user any fields with user_ will be set as properties. +So 'user_email' field will create an 'email' property. You just have to +provide the input form, and exUserFolder will do the rest. + +This has only been lightly tested, but, it seems to work just fine. + + + diff --git a/ZopeProducts/exUserFolder/doc/README.zodbAuthSource b/ZopeProducts/exUserFolder/doc/README.zodbAuthSource new file mode 100644 index 00000000..492e7f3f --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/README.zodbAuthSource @@ -0,0 +1,13 @@ +ZODB Authentication Source for exUserFolder + +This is an auth source that works pretty much like the standard user folder provided by zope. +It stores the usernames, roles and passwords on a ZODB persistent dictionary. + +It doesn't require any configuration at all, just select it as your auth source and you're +ready to add user accounts. + + +Author: Alex Verstraeten (aka: zxc) +Email: alex@quad.com.ar + + diff --git a/ZopeProducts/exUserFolder/doc/UnenlightenedZopistasGuide.txt b/ZopeProducts/exUserFolder/doc/UnenlightenedZopistasGuide.txt new file mode 100644 index 00000000..547bdc20 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/UnenlightenedZopistasGuide.txt @@ -0,0 +1,671 @@ +The Unenlightened Zopistas Guide to exUserFolder. +(C) 2001-2003 Andrew Milton + +0. INTRODUCTION. + +exUserFolder is an extensible authentication product for the Zope +Application Server. It allows a user to choose from a number of +methods of authenticating their users, and allows the setting and +fetching of arbitrary properties on a User object. + +Authentication methods, and Property methods do not have to use the +same backing store, so it is possible to use legacy user sources, and +still have configuration information stored about a user. + +exUserFolder supports HTTP Basic Authentication, and the so called +Cookie Authentication schemes popular with most webservers. + +0.1 Audience. + +Everybody, and Nobody. If we build our product correctly, we shouldn't +need user documentation, that's why Nobody. For some reason, normal +sane people seem to lose all of their common sense when sitting in +front of a computer. To the point where plain messages e.g. "Please +Insert a Floppy", evoke a response of "What does that mean?" So that's +why this document is for Everybody. + +This is not a guide for writing your own authentication, property, or +membership sources. You need the Zen Masters Guide to exUserFolder +document for that. + + +1. GETTING STARTED + +exUserFolder requires Python 2.1 or above (may work with older +pythons, but, this is not supported). And has been tested on Zope +2.3.0 and above (including 2.4.3). + +exUserFolder comes as a source tarball, and it does not rely on any +outside dependencies to be work. Some items may require additional +products to be installed in your Zope tree, but, simply will not +appear if these are not available. Some items may also require +external products to add functionality to them, but, at no time should +the Product not install because of these dependencies. + + +1.1 Installing exUserFolder. + +Unpack exUserFolder in your Products directory. This can be achieved by +executing the following on most UNIX systems. + +gzip -d -c exUserFolder-Ma_Mi_u.tgz | tar xvf - + +where exUserFolder-Ma_Mi_u.tgz is the version of exUserFolder you have +downloaded. + +On systems that have the GNU tar this can be shortened to; + +tar zxvf exUserFolder-Ma_Mi_u.tgz + +You should restart Zope after unpacking. Installing the product will not +affect your Zope installation in anyway. + +If you go to Folder inside your Zope Management Interface, you should see +exUserFolder as a dropdown item near the bottom somewhere. + +Congratulations, it's installed. + + +2. AUTHENTICATION SOURCES AND YOU. + +The only mandatory component of an exUserFolder installation, is +choosing an Authentication Source. There are six Authentication +Sources to choose from in the default install. There are other add-on +Sources available from other parties. + +Each Authentication Source is different, and assumes at least some +knowledge of that type of authentication scheme. + +Most if not all sources store the password encrypted in some +manner. This means that discovering other people's passwords is going +to be more difficult, than with the standard user folder that comes +with Zope. + +By default crypt or fcrypt are used, which is are DES encryption +methods. While this is not the strongest, fastest, choose another +superlative, of encryption techniques, it is certainly adequate for +protecting webpages. + +In a later release exUserFolder will allow you to choose what method +is used for password hashing. + +Some Authentication Sources can list the users that are available, +some cannot (or will not). Some allow you to add users, and others do +not. What features are availble depend on the individual +Authentication Source. + + +2.1 ZODB Authentication Source + +The ZODB Authentication Source operates just like a normal User +Folder. It stores its authentication items in the ZODB as the name +suggests. This is the simplest folder to setup, requiring no +parameters. + +Choosing ZODB Authentication Source is recommended for testing your install. + + +2.2 File Based Authentication. + +File Based Authentication allows you to have a fixed set of users in a +file with their encrypted passwords. The prerequisites for this are +somewhat convoluted. + +In the root of your Zope Installation, on the actual file system (not +in the ZODB), create a directory called exUsers. Make sure that Zope +has access to that directory. + +This is the directory where you will create your files. + +This is a read only Authentication Source. You will not be able to +create users, or modify their passwords. You can change their roles if you +choose a Property Source. + +There are two parameters asked for; + +2.2.1 Password File + +This is the name of the file that contains your users and passwords. +It should be of the format; + +username:cryptedPassword +user2:otherCryptedPasswd + +I can contain other fields after the password also delimited by : but these +will not be ussed. + +This file should exist inside the exUsers directory. + +2.2.2 Default Role + +This is the role that all users should be given when the log in. Because this +is a Read Only authentication source, you may not be able to add Roles at a +later date. + + +2.3 Postgresql Authentication Source + +Postgresql Authentication source is an RDBMS backed user store. You +can add, change, and list users. It requires a Postgresql Database +Connection to be created before creating the User Folder. + +You should be familiar with databases, and with your schema before +using this Authentication Source. If you don't already have a table +structure in place, a default schema is provided called 'pgScheme.sql' +in the exUserFolder distribution. + +The required schema is very simple. You need to store usernames, +passwords, and roles. If your existing schema doesn't support a roles +column you will have to add one. + +The configuration scheme looks daunting, but, it is setup to use the +defaults for 'pgScheme.sql' so if you're using this you can safely +continue. + +We will run through the items. + +2.3.1 Database Connection + +If you have any database connections, they will be listed in the drop +down box. Choose the one that represents your connection to your users +table. + +2.3.2 Table Name + +This is the name of the table containing your users. If you have a +different table to the default, you should change it here. + +2.3.3 Username Column + +This is the name of the column inside your table that contains the +usernames or logins of your users. This should contain exactly what +the user needs to type in as their username. + + +2.3.4 Password Column + +This is the name of the column inside your table that contains the +encrypted passwords of your users. + + +2.3.5 Roles Column + +This is where the roles are stored. These are used to provide access +to items in Zope. + + +2.4 User Supplied Authentication Source + +This allows you to create your methods in DTML, PythonScripts, +External Methods, or any other callable Zope item for listing, +authenticating, adding and changing your users. + +It is beyond the scope of this guide to describe how to do this, but, +the API is quite well defined inside the source, and also in the +README.API document. + +This Authentication Source has no configuration parameters. + + +2.5 RADIUS Authentication Source + +This allows you to authenticate your users against a RADIUS server. If +you don't know what this means, then this User Source is not for you +:-) + +You will require a RADIUS server to be operating, and for the server +that Zope is running on to have access to it. You will also need to +know the secret key to access the RADIUS server. + +2.5.1 Host + +This is the host your RADIUS server is running on. + +2.5.2 Port + +This is the port your RADIUS server is running on. Older installs may +require this to be 1645. The new 'blessed' port by IANA is 1812, and +this is now the default port. + +2.5.3 Secret + +Every remote host has a secret key it has to share with the server in +order to gain access to the authentication server. You need to know +this. + +2.5.4 Retries + +Because this is a networked authentication service, errors can +occur. This sets the number of times it will try to authenticate +before giving up. + +2.5.5 Timeout + +This is how long the RADIUS authenticator will wait for a +response. Because RADIUS operates over UDP, which is a connectionless +protocol, answers may never come back, or never reach their +destination in the first place. + +The default is 5 seconds which is actually quite a long time. + + +2.6 SMB Authentication Source + +This source allows you to authenticate your users in a Microsoft +Environment, using the SMB protocols. This is not the same as +authenticating via Directory Services. + +If your SMB server requires passwords to be encrypted in transit, you'll +need to install mxCrypto. + +2.6.1 Host + +This is the host that your Authentication service is on, this is +normally an NT or Win2K server, but, it can also be a UNIX box running +Samba. This should be the NetBIOS name of the server. + +2.6.2 Domain + +This is the NT/Windows DOMAIN that the user is to authenticate +against. + +2.6.3 WINS Server IP Address (optional) + +If provided, this should be the IP address of the WINS server to be +queried to locate your auth host (see 2.5.1 above). + +If you leave this field empty, the location of the authentication host +will be queried by broadcast, which works just fine if the Zope +machine is on the same subnet as your auth host but not if the auth +host is across a subnet link or if it's in the same machine as Zope +(don't ask. Apparently, some braindmamaged creature at M$ decided that +a machine shouldn't answer to its own broadcasts no matter what) + +Fill in this field if you are getting "NetBIOSTimeout" errors but you +are sure that your auth host was specified correctly, or if Windows +machines in your subnet also use a WINS server. + + +2.7 LDAP Authentication + +This source allows you to authenticate your users against an LDAP +server. This code is based on the auth_ldap module for Apache. The +documentation for these parameters is unashamedly lifted directly from +the documentation of the Apache directives for auth_ldap. + +See: http://www.rudedog.org/auth_ldap/ + +You must choose a property source when using LDAP Authentication, all +of the properties associated with the LDAP user entry are stored as +properties when they authenticate. Items with multiple entries are +stored as a list of items. + +You will need the pyLDAP module installed to use this authsource. +If you don't have it installed, you will not see an LDAP Auth Source available +for use. + +2.7.1 URL + +An RFC 2255 URL which specifies the LDAP search parameters to use. The syntax +of the URL is +ldap://host:port/basedn?attribute?scope?filter + +ldap For regular ldap, use the string ldap. For secure LDAP, use ldaps + instead. Secure LDAP is only available if auth_ldap was compiled with + SSL support. +host:port The name/port of the ldap server (defaults to localhost:389 for ldap, + and localhost:636 for ldaps). + + Once a connection has been made to a server, that connection remains + active for the life of the Zope process, or until the LDAP server + goes down. + + If the LDAP server goes down and breaks an existing connection, + the Auth Source will attempt to re-connect + +basedn The DN of the branch of the directory where all searches should start + from. At the very least, this must be the top of your directory tree, + but could also specify a subtree in the directory. + +attribute The attribute to search for. Although RFC 2255 allows a + comma-separated list of attributes, only the first attribute will be + used, no matter how many are provided. If no attributes are provided, + the default is to use uid. It's a good idea to choose an attribute + that will be unique across all entries in the subtree you will be + using. + +scope The scope of the search. Can be either one or sub. Note that a scope + of base is also supported by RFC 2255, but is not supported by this + module. If the scope is not provided, or if base scope is specified, + the default is to use a scope of sub. + +filter A valid LDAP search filter. If not provided, defaults to (objectClass + =*), which will search for all objects in the tree. + +When doing searches, the attribute, filter and username passed by the HTTP +client are combined to create a search filter that looks like (&(filter) +(attribute=username)). + +For example, consider an URL of ldap://ldap.xuf.com/o=XUF?cn?sub?(posixid +=*). When a client attempts to connect using a username of The Jester, the +resulting search filter will be (&(posixid=*)(cn=The Jester)). + + +2.7.2 Bind DN + +An optional Distinguished Name user to bind to the server when searching +for entries. If not provided an Anonymous bind will be used. + +2.7.3 Bind Password. +A bind password to use in conjunction with the bind DN. Note that the bind +password is probably sensitive data, and should be properly protected. You +should only use the Bind DN and Bind Password if you absolutely +need them to search the directory. + +2.7.4 Cert DB Path + +Specifies in which directory LDAP Auth Source should look for the certificate +authorities database. There should be a file named cert7.db in that directory. + +2.7.5 Compare DN On Server + +When set, LDAP Auth Source will use the LDAP server to compare the +DNs. This is the only foolproof way to compare DNs. LDAP Auth Source +will search the directory for the DN specified with the require dn +directive, then, retrieve the DN and compare it with the DN retrieved +from the user entry. If this directive is not set, LDAP Auth Source +simply does a string comparison. It is possible to get false negatives +with this approach, but it is much faster. Note the LDAP Auth Source cache +can speed up DN comparison in most situations. + +2.7.6 Dereference Aliases + +This directive specifies when LDAP Auth Source will de-reference +aliases during LDAP operations. The default is always. + +2.7.7 Group Attribute is DN + +When set, this directive says to use the distinguished name of the +client username when checking for group membership. Otherwise, the +username will be used. For example, assume that the client sent the +username tjester, which corresponds to the LDAP DN cn=The Jester, +o=XUF. If this directive is set, LDAP Auth Source will check if the +group has cn=The Jester, o=XUF as a member. If this directive is not +set, then LDAP Auth Source will check if the group has tjester as a +member. + +2.7.8 Compare Cache Size + +This specifies the size of the cache used to cache LDAP compare +operations. The default is 1024 entries. Setting it to 0 disables +operation caching. + +2.7.9 Compare Cache TTL + +Specifies the time (in seconds) that entries in the operation cache +remain valid. The default is 600 seconds. + +2.7.10 Start TLS + +If this is set to Yes, LDAP Auth Source will start a secure TLS +session after connecting to the LDAP server. This requires your LDAP +server to support TLS. + +2.7.11 Require User (one per line) + +The require user directive specifies what usernames can access the +resource. Once LDAP Auth Source has retrieved a unique DN from the +directory, it does an LDAP compare operation using the username +specified in the require user to see if that username is part of the +just-fetched LDAP entry. Multiple users can be granted access by +putting multiple usernames in the box, separated with newlines. For +example, with a AuthLDAPURL of ldap://ldap/o=XUF?cn (i.e., cn is used +for searches), the following require entries could be used to restrict +access: The Jester Fred User Joe Manager + +Because of the way that LDAP Auth Source handles this directive, The +Jester could sign on as The Jester, Zen Jester or any other cn that he +has in his LDAP entry. Only the single require user line is needed to +support all values of the attribute in the user's entry. + +If the uid attribute was used instead of the cn attribute in the URL +above, the above three lines could be; + +tj +fred_u +jmanager + +2.7.12 Require Group (one per line) + +This directive specifies an LDAP group whose members are allowed +access. It takes the distinguished name of the LDAP group. For +example, assume that the following entry existed in the LDAP +directory: + +dn: cn=Administrators, o=XUF +objectClass: groupOfUniqueNames +uniqueMember: cn=The Jester, o=XUF +uniqueMember: cn=Fred User, o=XUF + +The following directive would grant access to both Fred and Jester: + +require group cn=Administrators, o=XUF + +Behavior of this directive is modified by the Group Attribute and +Group Attribute Is DN options. + +2.7.13 Require DN + +The require dn option allows the administrator to grant access based +on distinguished names. It specifies a DN that must match for access +to be granted. If the distinguished name that was retrieved from the +directory server matches the distinguished name in the require dn, +then authorization is granted. + +The following directive would grant access to a specific DN: +require dn cn=The Jester, o=XUF + +Behavior of this directive is modified by the Compare DN On Server option. + +2.7.14 Default Manager + +This allows you to specify the username of the Manager for this area. +The manager will still need to meet auth requirements above, but, if +they do they will get the 'Manager' role added to their list of roles. + +2.7.15 Default Role + +This is a role to be assigned to users when they auth correctly. This +is to differentiate them from merely being 'authenticated'. + +2.7.16 Examples + + * Grant access to anyone who exists in the LDAP directory, using their UID + for searches. + URL ldap://ldap1.zope.com:389/ou=People, o=XUF?uid?sub?(objectClass=*) + + * The next example is similar to the previous one, but is uses the common + name instead of the UID. Note that this could be problematical if multiple + people in the directory share the same cn, because a search on cn must + return exactly one entry. That's why this approach is not recommended: it's + a better idea to choose an attribute that is guaranteed unique in your + directory, such as uid. + URL ldap://ldap.zope.com/ou=People, o=XUF?cn + + * Grant access to anybody in the Administrators group. The users must + authenticate using their UID. + URL ldap://ldap.zope.com/o=XUF?uid + require group: + cn=Administrators, o=XUF + + * The next example assumes that everyone at XUF who carries an + alphanumeric pager will have an LDAP attribute of qpagePagerID. The example + will grant access only to people (authenticated via their UID) who have + alphanumeric pagers: + URL: ldap://ldap.zope.com/o=XUF?uid??(qpagePagerID=*) + + * The next example demonstrates the power of using filters to accomplish + complicated administrative requirements. Without filters, it would have + been necessary to create a new LDAP group and ensure that the group's + members remain synchronized with the pager users. This becomes trivial with + filters. The goal is to grant access to anyone who has a filter, plus grant + access to Joe Manager, who doesn't have a pager, but does need to access + the same resource: + URL ldap://ldap.zope.com/o=XUF?uid??(|(qpagePagerID=*)(uid=jmanager)) + + This last may look confusing at first, so it helps to evaluate what the + search filter will look like based on who connects, as shown below. + If Fred User connects as fuser, the filter would look like + + (&(|(qpagePagerID=*)(uid=jmanager))(uid=fuser)) + + The above search will only succeed if fuser has a pager. When Joe Manager + connects as jmanager, the filter looks like + + (&(|(qpagePagerID=*)(uid=jmanager))(uid=jmanager)) + + The above search will succeed whether jmanager has a pager or not. + + +2.8 General Items. + +You can choose to use standard auth, or cookie auth, and you can +decide how long you want to cache the users credentials before +retrying. + +2.8.1 Authentication Type + +2.8.1.1 Standard Authentication + +This method causes the browser to pop up a dialog box to ask for the +username and password. + +2.8.1.2 Cookie Authentication + +This method allows you to use a normal HTML form to get the username +and password from the user. It also will present the default form to +the user if they try to access an unauthorised area. + + +2.8.1.3 Secure Cookie based Authentication + +This method, like Cookie Authentication allows you to use a HTML form +to get the user details. However, the cookie it uses does not contain +any login information. It is internally checked against a cache of +hashes and the information is derived from that. This cache disappears +if you restart Zope, so this is not a good option for people who want +to persistently cache logins across sessions. + + +2.8.2 Credential Cache Timeout in Seconds + +exUserFolder by default caches credential information, so that the +authorisation source isn't hit *for every object and page* that has to +be fetched. For remote authentication services this can slow things +down quite considerably. Even setting this to a modest setting will +quicken response times. + +Setting this too long could cause problems if you want to lock out a +troublesome user. The credential cache is flushed if someone provides +a password that doesn't match the one in the cache. + + +2.8.3 Negative Credential Cache Timeout in Seconds + +exUserFolder allows you to cache login failures for users that do not +exist. This means you don't have to go out to your auth source when +you know for certain this user is never going to be able to +authenticate. + +Due to the way some auth sources are designed, this doesn't work for +auth sources like SMB Auth Source that lie initially about the user +existing (because you can't verify the existence of a user without +authenticating them), and then check the credentials later. + +It's possible to create a hybrid auth source that lets this work +correctly for auth sources that can't list the users. + + +2.8.4 Log out users who expire from cache? + +If you've got caching turned on, then this will force any user who has +their session expire to login again. Some people like to do this. + + +2.8.5 Activate Session Tracking for anoymous users? + +For any anonymous user, a new temporary user is created. This allows +you to set/get properties for anonymous users too. Currently +experimental. + + +3.0 PROPERTY SOURCES + +4.0 MEMBERSHIP SOURCES + +5.0 TIPS FOR THE UNWARY + +Generally these things apply to Cookie Authentication models, since +there is additional access required to present the login form. + +5.1 Too much protection. + +A lot of people try to protect a folder by placing an exUserFolder +inside. They then change the permissions on this folder to only allow +Authenticated or some Local Role to have permission. + +5.1.1 The problem + +When you try to access the folder, instead of getting the form, you +get a popup box, even though you chose Cookie Authentication. Even +when you enter a username and password it doesn't work. + + +5.1.2 What happened + +You tried to access an area you don't have access to. Zope found the +closest user folder to the object you were trying to access. The user +folder decided you were not authorised and tried to display the login +form. You don't have access to view the login form, so Zope finds the +nearest user folder to the login form, which is the user folder above +the protected directory. It pops up the authentication dialog. If you +put in a valid username and password for this top level, then lower +level then displays the login form. + +5.1.3 Solution 1 (preferred). + +Place the user folder one level *above* the folder you want to protect, +that is in the unprotected area. Everything should work fine. + +5.1.4. Solution 2 (not so preferred). + +Set the 'View' permission on the docLogin form inside the acl_users +folder. You can get there by Choosing 'Contents' on docLogin and +scrolling down to the bottom. + +6.0 MISCELLANY + +6.1 Adding an exUserFolder from a product. + +You can add an exUserFolder from a Python product fairly easily, if +not a tad messily. + + +from Products.exUserFolder.exUserFolder import manage_addexUserFolder, eUserFolder + +manage_addexUserFolder(authId='zodbAuthSource', propId='zodbPropSource', + memberId='basicMemberSource', + cookie_mode=1, session_length=600, REQUEST) + +Obviously change authId, propId, and memberId to what you want. +However, you'll need to ram in the appropriate form fields for the various +source constructors into your REQUEST. + +6.2 Session Tracking. + +Session tracking (currently) relies on having the credential cache +active, and a property source active. Your trackable user will only +last as long as they are not expired from the cache. You should set +the cache expiry length to be somewhat longer than normal if you plan +to use Session Tracking, and you should also be prepared to check that +the current session is valid. diff --git a/ZopeProducts/exUserFolder/doc/mysql.sql b/ZopeProducts/exUserFolder/doc/mysql.sql new file mode 100644 index 00000000..b2bf5f82 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/mysql.sql @@ -0,0 +1,18 @@ +DROP TABLE IF EXISTS passwd; +CREATE TABLE passwd ( + username varchar(64) NOT NULL PRIMARY KEY, + password varchar(64) NOT NULL, + roles varchar(255) +); + +DROP TABLE IF EXISTS UserProperties; +CREATE TABLE UserProperties ( + username varchar(64) NOT NULL, + prop_key varchar(128) NOT NULL, + value text NOT NULL, + istemporary int +); + +CREATE UNIQUE INDEX username_prop_idx on UserProperties(username,prop_key ); +CREATE INDEX username_idx on UserProperties(username); + diff --git a/ZopeProducts/exUserFolder/doc/pgAndreasScheme.sql b/ZopeProducts/exUserFolder/doc/pgAndreasScheme.sql new file mode 100644 index 00000000..57fd21a4 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/pgAndreasScheme.sql @@ -0,0 +1,14 @@ +CREATE TABLE "passwd" ( + "username" character varying(64) UNIQUE NOT NULL, + "password" character varying(64) NOT NULL, + "roles" character varying(255), + Constraint "passwd_pkey" Primary Key ("username") +); + +CREATE TABLE "userproperties" ( + "username" character varying(64) NOT NULL REFERENCES passwd (username) ON DELETE CASCADE ON UPDATE CASCADE, + "key" character varying(128) NOT NULL, + "value" text NOT NULL +); + +CREATE INDEX "username_idx" on "userproperties" using btree ( "username" "varchar_ops" ); diff --git a/ZopeProducts/exUserFolder/doc/pgScheme.sql b/ZopeProducts/exUserFolder/doc/pgScheme.sql new file mode 100644 index 00000000..45e5e1a0 --- /dev/null +++ b/ZopeProducts/exUserFolder/doc/pgScheme.sql @@ -0,0 +1,46 @@ +CREATE SEQUENCE "passwd_userid_seq" start 1 increment 1 maxvalue 2147483647 minvalue 1 cache 1 ; + +-- +-- TOC Entry ID 6 (OID 24949) +-- +-- Name: passwd Type: TABLE Owner: akm +-- + +CREATE TABLE "passwd" ( + "userid" integer DEFAULT nextval('"passwd_userid_seq"'::text) NOT NULL, + "username" character varying(64) NOT NULL, + "password" character varying(64) NOT NULL, + "roles" character varying(255), + Constraint "passwd_pkey" Primary Key ("userid") +); + +-- +-- TOC Entry ID 4 (OID 24965) +-- +-- Name: userproperties_propertyid_seq Type: SEQUENCE Owner: akm +-- + +CREATE SEQUENCE "userproperties_propertyid_seq" start 1 increment 1 maxvalue 2147483647 minvalue 1 cache 1 ; + +-- +-- TOC Entry ID 7 (OID 24984) +-- +-- Name: userproperties Type: TABLE Owner: akm +-- + +CREATE TABLE "userproperties" ( + "propertyid" integer DEFAULT nextval('"userproperties_propertyid_seq"'::text) NOT NULL, + "username" character varying(64) NOT NULL, + "key" character varying(128) NOT NULL, + "value" text NOT NULL, + "istemporary" integer, + Constraint "userproperties_pkey" Primary Key ("propertyid") +); + +-- +-- TOC Entry ID 8 (OID 24984) +-- +-- Name: "username_idx" Type: INDEX Owner: akm +-- + +CREATE INDEX "username_idx" on "userproperties" using btree ( "username" "varchar_ops" ); diff --git a/ZopeProducts/exUserFolder/dtml/docLogin.dtml b/ZopeProducts/exUserFolder/dtml/docLogin.dtml new file mode 100644 index 00000000..87e30194 --- /dev/null +++ b/ZopeProducts/exUserFolder/dtml/docLogin.dtml @@ -0,0 +1,49 @@ + +
      + + + + + + +

      + +

      + + + + + Added by Emmanuel for ScoDoc + + + + + + + + + + + + + + + +
      + Name + + +
      + Password + + +
      +
      +
      +Ok "> +
      + +
      + +
      + diff --git a/ZopeProducts/exUserFolder/dtml/docLoginRedirect.dtml b/ZopeProducts/exUserFolder/dtml/docLoginRedirect.dtml new file mode 100644 index 00000000..30ed97b4 --- /dev/null +++ b/ZopeProducts/exUserFolder/dtml/docLoginRedirect.dtml @@ -0,0 +1,26 @@ + + +Logging In + +"> +&authFailedCode=&dtml-authFailedCode;
      "> + + + + + diff --git a/ZopeProducts/exUserFolder/dtml/docLogout.dtml b/ZopeProducts/exUserFolder/dtml/docLogout.dtml new file mode 100644 index 00000000..5210fc74 --- /dev/null +++ b/ZopeProducts/exUserFolder/dtml/docLogout.dtml @@ -0,0 +1,16 @@ + +

      +

      +

      Vous êtes déconnecté de ScoDoc. +

      + +

      ">revenir Ă  l'accueil

      + +
      +

      (Attention: si vous êtes administrateur, vous ne pouvez vous déconnecter complètement qu'en relançant votre navigateur) +

      +
      + + + + diff --git a/ZopeProducts/exUserFolder/dtml/mainUser.dtml b/ZopeProducts/exUserFolder/dtml/mainUser.dtml new file mode 100644 index 00000000..e1188165 --- /dev/null +++ b/ZopeProducts/exUserFolder/dtml/mainUser.dtml @@ -0,0 +1,55 @@ + + + +
      + +

      +The following users have been defined. Click on the name of a +user to edit that user. +

      + + + + + + + + + + + + + + + + +
      + + + +
        + +
      + + +
      +
      + +

      +There are no users defined. +

      + +

      +

      + +
      +

      +
      +
      + + + diff --git a/ZopeProducts/exUserFolder/dummyZBabelTag.py b/ZopeProducts/exUserFolder/dummyZBabelTag.py new file mode 100644 index 00000000..fc45c244 --- /dev/null +++ b/ZopeProducts/exUserFolder/dummyZBabelTag.py @@ -0,0 +1,164 @@ +try: + from Products.ZBabel import ZBabelTag +except: + + from DocumentTemplate.DT_String import String + from DocumentTemplate.DT_Util import render_blocks, Eval, ParseError + import string, zLOG + + + # fake Babel/Fish Tags + + class ZBabelTag: + '''ZBabel Tag class - The cool translation tag''' + + # define the name of the tag; also tell the system it is a doublet + name = 'babel' + blockContinuations = () + + + def __init__(self, blocks): + '''__init__(self, blocks) --> Initialize tag object; return None''' + (tname, args, section,) = blocks[0] + + self.section = section + + + def render(self, md): + '''render(self, md) --> Do the Translation; return string''' + return render_blocks(self.section.blocks, md) + __call__=render + # register the DTML-BABEL tag + String.commands['babel'] = ZBabelTag + + class FishTag: + '''Fish Tag class - Short-Cut version of the cool translation tag (babel) + + This tag is used to quickly translate menu-like text snippets, similar to + the KDE translation.''' + + # define the tag name + name = 'fish' + + # define additional variables + literal = 1 + + src = 'label' + attrs = {'dst': None, 'label': '', 'data': None, 'towerId': None} + + def __init__(self, args): + '''__init__(self, blocks) --> Initialize tag object; return None''' + self.section = None + args = parseTagParameters(args, tag=self.name) + self.args = self.validateArguments(args) + + for attr in self.attrs.keys(): + setattr(self, attr, self.attrs[attr]) + + def validateArguments(self, args): + '''validateArguments(self, args) --> try to evaluate the passed expression or try to get an object from the passed id; if all this fails, leave the string, it is probably cool!; return tuple of (name, value)''' + # I stole this from dtml-let... + # SR: Like he said: Always copy existing code to make you life easier (evben though + # I changed some variables around + for count in range(len(args)): + (name, expr,) = args[count] + if ((expr[:1] == '"') and ((expr[-1:] == '"') and (len(expr) > 1))): + expr = expr[1:-1] + try: + + args[count] = (name, Eval(expr).eval) + + except SyntaxError, v: + (m, (huh, l, c, src,),) = v + raise ParseError, (('Expression (Python) Syntax error:' + + '
      \012%s\012
      \012' % v[0]), 'babel') + + elif ((expr[:1] == "'") and ((expr[-1:] == "'") and (len(expr) > 1))): + expr = expr[1:-1] + args[count] = (name, expr) + + return args + + def render(self, md): + '''render(self, md) --> Do the Translation; return string''' + data = None + for name, expr in self.args: + if type(expr) is type(''): + try: + data = md[expr] + except: + data = expr + else: + data = expr(md) + + #zLOG.LOG("exUserFolder", zLOG.INFO, "rendering name=%s expr=%s data=%s"%(name,expr,data)) + + print data + return str(data) + + __call__=render + + + # register the DTML-FISH tag + String.commands['fish'] = FishTag + + + +try: + import re + parmre=re.compile('''([\000- ]*([^\000- ="']+)=([^\000- ="']+))''');#")) + dqparmre=re.compile('([\000- ]*([^\000- ="]+)="([^"]*)")') + sqparmre=re.compile('''([\000- ]*([^\000- =']+)='([^']*)')''') +except: + import regex + parmre=regex.compile('''([\000- ]*([^\000- ="']+)=([^\000- ="']+))''');#")) + dqparmre=regex.compile('([\000- ]*([^\000- ="]+)="([^"]*)")') + sqparmre=regex.compile('''([\000- ]*([^\000- =']+)='([^']*)')''') + + + +def parseTagParameters(paramText, result = None, tag = 'babel', + parmre=parmre, + dqparmre=dqparmre, + sqparmre=sqparmre, + **parms): + result = (result or []) + + parsedParam = parmre.match(paramText) + dqParsedParam = dqparmre.match(paramText) + sqParsedParam = sqparmre.match(paramText) + + # Parse parameters of the form: name=value + if parsedParam is not None: + name = parsedParam.group(2) + value = parsedParam.group(3) + length = len(parsedParam.group(1)) + + # Parse parameters of the form: name="value" + elif dqParsedParam is not None: + name = dqParsedParam.group(2) + value = ('"%s"' % dqParsedParam.group(3)) + length = len(dqParsedParam.group(1)) + + # Parse parameters of the form: name='value' + elif sqParsedParam is not None: + name = sqParsedParam.group(2) + value = ('''"'%s'"''' % sqParsedParam.group(3)) + length = len(sqParsedParam.group(1)) + + else: + # There are no more parameters to parse + if ((not paramText) or (not string.strip(paramText))): + return result + raise ParseError, (('invalid parameter: "%s"' % paramText), tag) + + # add the parameter/value pait to the results + result.append((name, value)) + + # remove the found parameter from the paramText + paramText = string.strip(paramText[length:]) + + if paramText: + return apply(parseTagParameters, (paramText, result, tag), parms) + else: + return result diff --git a/ZopeProducts/exUserFolder/exUser.gif b/ZopeProducts/exUserFolder/exUser.gif new file mode 100644 index 0000000000000000000000000000000000000000..c7f852b0ef2cac8ade0d72cbcdb558d3ba5facbf GIT binary patch literal 169 zcmZ?wbhEHb6krfwc+3C-5+VW?Dh3`B8V*3xz#vgUBT31iQbVCKv$9u3VyTG0QWb@5 zo3`D1_U`}x|B63Z7#SG&8FWB8L1r+pgnc;axw^J(k+!H4Q)6NOAq%<5g@<19wjZ@Q ztz@yP;ALBqR!itng$NsmCJD|L(HfQuTAd6ON}XDsUosGgWbS3SG^xNurq$r1qrwZV Q-jAY&4CL@oG;_2jve2j^2tQBwR zH@fxvZ>SdHYPtV}_C=YQbC|cF6cmnDc(K II}3v~034V|O#lD@ literal 0 HcmV?d00001 diff --git a/ZopeProducts/exUserFolder/exUserFolder.py b/ZopeProducts/exUserFolder/exUserFolder.py new file mode 100644 index 00000000..1c330c96 --- /dev/null +++ b/ZopeProducts/exUserFolder/exUserFolder.py @@ -0,0 +1,1380 @@ +# Zope User Folder for ScoDoc +# Adapte de l'Extensible User Folder +# simplifie pour les besoins de ScoDoc. +# Emmanuel Viennet 2013 + +# +# Extensible User Folder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: exUserFolder.py,v 1.93 2004/11/10 14:15:33 akm Exp $ + +############################################################################## +# +# Zope Public License (ZPL) Version 0.9.4 +# --------------------------------------- +# +# Copyright (c) Digital Creations. All rights reserved. +# +# Redistribution and use in source and binary forms, with or +# without modification, are permitted provided that the following +# conditions are met: +# +# 1. Redistributions in source code must retain the above +# copyright notice, this list of conditions, and the following +# disclaimer. +# +# 6. Redistributions of any form whatsoever must retain the +# following acknowledgment: +# +# "This product includes software developed by Digital +# Creations for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# Disclaimer +# +# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND +# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +############################################################################## + +# Portions Copyright (c) 2002 Nuxeo SARL , +# Copyright (c) 2002 Florent Guillaume . +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import Globals, App.Undo, socket, os, string, sha, random, sys, zLOG + +from Globals import DTMLFile, PersistentMapping +from string import join,strip,split,lower,upper,find + +from OFS.Folder import Folder +from OFS.CopySupport import CopyContainer + +from base64 import decodestring, encodestring +from urllib import quote, unquote + +from Acquisition import aq_base +from AccessControl import ClassSecurityInfo +from AccessControl.Role import RoleManager +from AccessControl.User import BasicUser, BasicUserFolder, readUserAccessFile +from AccessControl.PermissionRole import PermissionRole +from AccessControl.ZopeSecurityPolicy import _noroles +from OFS.DTMLMethod import DTMLMethod +from time import time +from OFS.ObjectManager import REPLACEABLE +from Persistence import Persistent + +from PropertyEditor import * + +from User import User, AnonUser +from UserCache.UserCache import GlobalUserCache, GlobalNegativeUserCache, GlobalAdvancedCookieCache, SessionExpiredException + +from LoginRequiredMessages import LoginRequiredMessages + +from AccessControl import Unauthorized + +class LoginRequired(Exception): + """Login required""" + pass + + +# If there is no NUG Product just define a dummy class +try: + from Products.NuxUserGroups.UserFolderWithGroups import BasicGroupFolderMixin, _marker +except ImportError: + class BasicGroupFolderMixin: + pass + _marker = None + +# Little function to create temp usernames +def createTempName(): + t=time() + t1=time() + t2=time() + t3 = 0.0 + t3 = (t + t1 + t2) / 3 + un = "Anonymous %.0f"%(t3) + return(un) + + +manage_addexUserFolderForm=DTMLFile('dtml/manage_addexUserFolder', globals(), __name__='manage_addexUserFolderForm') + + + +def manage_addexUserFolder(self, authId, propId, memberId, + cookie_mode=0, session_length=0, + not_session_length=0, + sessionTracking=None, idleTimeout=None, + REQUEST={}, groupId=None, cryptoId=None): + """ """ + if hasattr(self.aq_base, 'acl_users'): + return Globals.MessageDialog(self,REQUEST, + title ='Item Exists', + message='This object already contains a User Folder', + action ='%s/manage_main' % REQUEST['URL1']) + ob=exUserFolder(authId, propId, memberId, groupId, cryptoId, cookie_mode, + session_length, sessionTracking, idleTimeout, + not_session_length) + + self._setObject('acl_users', ob, None, None, 0) + self.__allow_groups__=self.acl_users + ob=getattr(self, 'acl_users') + ob.postInitialisation(REQUEST) + + if REQUEST: + return self.manage_main(self, REQUEST) + return '' + +# +# Module level caches +# +XUFUserCache=GlobalUserCache() +XUFNotUserCache=GlobalNegativeUserCache() +XUFCookieCache=GlobalAdvancedCookieCache() + +class exUserFolder(Folder,BasicUserFolder,BasicGroupFolderMixin, + CopyContainer): + """ """ + + # HACK! We use this meta_type internally so we can be pasted into + # the root. We registered with 'exUserFolder' meta_type however, so + # our constructors work. + meta_type='User Folder' + id ='acl_users' + title ='Extensible User Folder' + icon ='misc_/exUserFolder/exUserFolder.gif' + + isPrincipiaFolderish=1 + isAUserFolder=1 + __allow_access_to_unprotected_subobjects__=1 + authSources={} + propSources={} + cryptoSources={} + membershipSources={} + groupSources={} # UNUSED by ScoDoc + + manage_options=( + {'label':'Users', 'action':'manage_main'}, + {'label':'Groups', 'action':'manage_userGroups'}, + {'label':'Parameters', 'action':'manage_editexUserFolderForm'}, + {'label':'Authentication Source','action':'manage_editAuthSourceForm'}, + {'label':'Properties Source','action':'manage_editPropSourceForm'}, + {'label':'Membership Source', 'action':'manage_editMembershipSourceForm'}, + {'label':'Cache Data', 'action':'manage_showCacheData'}, + {'label':'Security', 'action':'manage_access'}, + {'label':'Contents', 'action':'manage_contents'}, + {'label':'Ownership', 'action':'manage_owner'}, + {'label':'Undo', 'action':'manage_UndoForm'}, + ) + + __ac_permissions__=( + ('View management screens', ('manage','manage_menu','manage_main', + 'manage_copyright', 'manage_tabs', + 'manage_properties', 'manage_UndoForm', + 'manage_edit', 'manage_contents', + 'manage_cutObjects','manage_copyObjects', + 'manage_pasteObjects', + 'manage_renameForm', + 'manage_renameObject', + 'manage_renameObjects', ), + ('Manager',)), + + ('Undo changes', ('manage_undo_transactions',), + ('Manager',)), + + ('Change permissions', ('manage_access',), + ('Manager',)), + + ('Manage users', ('manage_users', 'manage_editUserForm', + 'manage_editUser', 'manage_addUserForm', + 'manage_addUser', 'manage_userActions', + 'userFolderAddGroup', + 'userFolderDelGroups', + 'getGroupNames', + 'getGroupById', + 'manage_userGroups', + 'manage_addGroup', + 'manage_showGroup',), + ('Manager',)), + + ('Change exUser Folders', ('manage_edit',), + ('Manager',)), + + ('View', ('manage_changePassword', + 'manage_forgotPassword', 'docLogin','docLoginRedirect', + 'docLogout', 'logout', 'DialogHeader', + 'DialogFooter', 'manage_signupUser', + 'MessageDialog', 'redirectToLogin','manage_changeProps'), + ('Anonymous', 'Authenticated', 'Manager')), + + ('Manage properties', ('manage_addProperty', + 'manage_editProperties', + 'manage_delProperties', + 'manage_changeProperties', + 'manage_propertiesForm', + 'manage_propertyTypeForm', + 'manage_changePropertyTypes', + ), + ('Manager',)), + ('Access contents information', ('hasProperty', 'propertyIds', + 'propertyValues','propertyItems', + 'getProperty', 'getPropertyType', + 'propertyMap', 'docLogin','docLoginRedirect', + 'DialogHeader', 'DialogFooter', + 'MessageDialog', 'redirectToLogin',), + ('Anonymous', 'Authenticated', 'Manager')), + ) + manage_access=DTMLFile('dtml/access',globals()) + manage_tabs=DTMLFile('common/manage_tabs',globals()) + manage_properties=DTMLFile('dtml/properties', globals()) + manage_main=DTMLFile('dtml/mainUser', globals()) + manage_contents=Folder.manage_main + manage_showCacheData=DTMLFile('dtml/manage_showCacheData', globals()) + + # This is going away soon... + docLoginRedirect=DTMLFile('dtml/docLoginRedirect', globals()) + + # Stupid crap + try: + manage_contents._setName('manage_contents') + except AttributeError: + pass + + + MessageDialog=DTMLFile('common/MessageDialog', globals()) + MessageDialog.__replaceable__ = REPLACEABLE + + manage_addUserForm=DTMLFile('dtml/manage_addUserForm',globals()) + manage_editUserForm=DTMLFile('dtml/manage_editUserForm',globals()) + + DialogHeader__roles__=() + DialogHeader=DTMLFile('common/DialogHeader',globals()) + DialogFooter__roles__=() + DialogFooter=DTMLFile('common/DialogFooter',globals()) + + manage_editAuthSourceForm=DTMLFile('dtml/manage_editAuthSourceForm',globals()) + manage_editPropSourceForm=DTMLFile('dtml/manage_editPropSourceForm',globals()) + manage_editMembershipSourceForm=DTMLFile('dtml/manage_editMembershipSourceForm', globals()) + + manage_addPropertyForm=DTMLFile('dtml/manage_addPropertyForm', globals()) + manage_createPropertyForm=DTMLFile('dtml/manage_createPropertyForm', globals()) + manage_editUserPropertyForm=DTMLFile('dtml/manage_editUserPropertyForm', globals()) + + manage_editexUserFolderForm=DTMLFile('dtml/manage_editexUserFolderForm', globals()) + + manage_userGroups=DTMLFile('dtml/mainGroup',globals()) + + + # Use pages from NUG if it's there, otherwise no group support + try: + manage_addGroup = BasicGroupFolderMixin.manage_addGroup + manage_showGroup = BasicGroupFolderMixin.manage_showGroup + except: + manage_addGroup = None + manage_showGroup = None + + # No more class globals + + # sessionLength=0 # Upgrading users should get no caching. + # notSessionLength=0 # bad cache limit + # cookie_mode=0 + # sessionTracking=None # Or session tracking. + # idleTimeout=0 + + def __init__(self, authId, propId, memberId, groupId, cryptoId, + cookie_mode=0, session_length=0, sessionTracking=None, + idleTimeout=0, not_session_length=0): + self.cookie_mode=cookie_mode + self.sessionLength=session_length + self.notSessionLength=not_session_length + self.sessionTracking=sessionTracking + self.idleTimeout=idleTimeout + + _docLogin=DTMLFile('dtml/docLogin',globals()) + _docLogout=DTMLFile('dtml/docLogout',globals()) + + docLogin=DTMLMethod(__name__='docLogin') + docLogin.manage_edit(data=_docLogin, title='Login Page') + self._setObject('docLogin', docLogin, None, None, 0) + + docLogout=DTMLMethod(__name__='docLogout') + docLogout.manage_edit(data=_docLogout, title='Logout Page') + self._setObject('docLogout', docLogout, None, None, 0) + + postUserCreate=DTMLMethod(__name__='postUserCreate') + postUserCreate.manage_edit(data=_postUserCreate, title='Post User Creation methods') + self._setObject('postUserCreate', postUserCreate, None, None, 0) + + self.manage_addAuthSource=self.authSources[authId].manage_addMethod + self.manage_addPropSource=self.propSources[propId].manage_addMethod + self.manage_addMembershipSource=self.membershipSources[memberId].manage_addMethod + + self.manage_addGroupSource=None # UNUSED by ScoDoc + self.currentGroupsSource=None + + if cryptoId: + self.cryptoId = cryptoId + else: + self.cryptoId = 'Crypt' + + def __setstate__(self, state): + Persistent.__setstate__(self, state) + if not hasattr(self, 'currentGroupSource'): + self.currentGroupSource = None + if not hasattr(self, 'sessionLength'): + self.sessionLength = 0 + if not hasattr(self, 'notSessionLength'): + self.notSessionLength = 0 + if not hasattr(self, 'cookie_mode'): + self.cookie_mode = 0 + if not hasattr(self, 'sessionTraining'): + self.sessionTracking = None + if not hasattr(self, 'idleTimeout'): + self.idleTimeout=0 + + def manage_beforeDelete(self, item, container): + zLOG.LOG("exUserFolder", zLOG.BLATHER, "Attempting to delete an exUserFolder instance") + if item is self: + try: + self.cache_deleteCache() + self.xcache_deleteCache() + zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- Caches deleted") + except: + #pass + zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- Cache deletion failed") + + try: + del container.__allow_groups__ + zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- container.__allow_groups_ deleted") + except: + #pass + zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- container.__allow_groups_ deletion failed") + + + def manage_afterAdd(self, item, container): + zLOG.LOG("exUserFolder", zLOG.BLATHER, "Adding an exUserFolder") + + if item is self: + if hasattr(self, 'aq_base'): self=self.aq_base + container.__allow_groups__=self + + def manage_editPropSource(self, REQUEST): + """ Edit Prop Source """ + if self.currentPropSource: + self.currentPropSource.manage_editPropSource(REQUEST) + return self.manage_main(self, REQUEST) + + def manage_editAuthSource(self, REQUEST): + """ Edit Auth Source """ + self.currentAuthSource.manage_editAuthSource(REQUEST) + return self.manage_main(self, REQUEST) + + def manage_editMembershipSource(self, REQUEST): + """ Edit Membership Source """ + if self.currentMembershipSource: + return self.currentMembershipSource.manage_editMembershipSource(REQUEST) + + def postInitialisation(self, REQUEST): + self.manage_addAuthSource(self=self,REQUEST=REQUEST) + self.manage_addPropSource(self=self,REQUEST=REQUEST) + self.manage_addMembershipSource(self=self,REQUEST=REQUEST) + self.currentGroupSource = None + + def addAuthSource(self, REQUEST={}): + return self.manage_addAuthSourceForm(self, REQUEST) + + def addPropSource(self, REQUEST={}): + return self.manage_addPropSourceForm(self, REQUEST) + + def addMembershipSource(self, REQUEST={}): + return self.manage_editMembershipSourceForm(self, REQUEST) + + def listUserProperties(self, username): + if self.currentPropSource: + return self.currentPropSource.listUserProperties(username=username) + + def getUserProperty(self, username, key): + if self.currentPropSource: + return self.currentPropSource.getUserProperty(key=key, username=username) + + def reqattr(self, request, attr, default=None): + try: return request[attr] + except: return default + + def getAuthFailedMessage(self, code): + """ Return a code """ + if LoginRequiredMessages.has_key(code): + return LoginRequiredMessages[code] + return 'Login Required' + + # Called when we are deleted + def cache_deleteCache(self): + pp = string.join(self.getPhysicalPath(), '/') + XUFUserCache.deleteCache(pp) + + def cache_addToCache(self, username, password, user): + if not self.sessionLength: + return + # fix by emmanuel + if username == self._emergency_user.getUserName(): + return + # /fix + pp = string.join(self.getPhysicalPath(), '/') + x = XUFUserCache.getCache(pp) + if not x: + x = XUFUserCache.createCache(pp, self.sessionLength) + x.addToCache(username, password, user) + + def cache_getUser(self, username, password, checkpassword=1): + if not self.sessionLength: + return None + pp = string.join(self.getPhysicalPath(), '/') + x = XUFUserCache.getCache(pp) + if not x: + return None + u = x.getUser(self, username, password, checkpassword) + if u is not None: + u = u.__of__(self) + return u + + def cache_removeUser(self, username): + if not self.sessionLength: + return + pp = string.join(self.getPhysicalPath(), '/') + x = XUFUserCache.getCache(pp) + if x: + x.removeUser(username) + + def cache_getCacheStats(self): + pp = string.join(self.getPhysicalPath(), '/') + x = XUFUserCache.getCache(pp) + if not x: + x = XUFUserCache.createCache(pp, self.sessionLength) + if x: + return x.getCacheStats() + + def cache_getCurrentUsers(self): + pp = string.join(self.getPhysicalPath(), '/') + x = XUFUserCache.getCache(pp) + if x: + return x.getCurrentUsers(self) + + # negative cache functions + def xcache_deleteCache(self): + pp = string.join(self.getPhysicalPath(), '/') + XUFNotUserCache.deleteCache(pp) + + def xcache_addToCache(self, username): + if not self.notSessionLength: + return + pp = string.join(self.getPhysicalPath(), '/') + x = XUFNotUserCache.getCache(pp) + if not x: + x = XUFNotUserCache.createCache(pp, self.notSessionLength) + x.addToCache(username) + + def xcache_getUser(self, username): + if not self.notSessionLength: + return None + pp = string.join(self.getPhysicalPath(), '/') + x = XUFNotUserCache.getCache(pp) + if not x: + return None + return x.getUser(username) + + def xcache_removeUser(self, username): + #zLOG.LOG('exUserFolder', zLOG.PANIC, 'xcache_removeUser(%s)' % username) + if not self.notSessionLength: + return + pp = string.join(self.getPhysicalPath(), '/') + x = XUFNotUserCache.getCache(pp) + if x: + #zLOG.LOG('exUserFolder', zLOG.PANIC, 'xcache_removeUser removing') + x.removeUser(username) + + # Cookie Cache Functions + def cache_deleteCookieCache(self): + pp = string.join(self.getPhysicalPath(), '/') + XUFCookieCache.deleteCache(pp) + + def cache_addToCookieCache(self, username, password, key): + pp = string.join(self.getPhysicalPath(), '/') + c = XUFCookieCache.getCache(pp) + if not c: + c = XUFCookieCache.createCache(pp, 86400) + c.addToCache(username, password, key) + + def cache_getCookieCacheUser(self, key): + pp = string.join(self.getPhysicalPath(), '/') + c = XUFCookieCache.getCache(pp) + if not c: + return None + return c.getUser(key) + + def cache_removeCookieCacheUser(self, key): + pp = string.join(self.getPhysicalPath(), '/') + c = XUFCookieCache.getCache(pp) + if c: + c.removeUser(key) + + def manage_editUser(self, username, REQUEST={}): # UNUSED by ScoDoc + """ Edit a User """ + # username=self.reqattr(REQUEST,'username') + password=self.reqattr(REQUEST,'password') + password_confirm=self.reqattr(REQUEST,'password_confirm') + roles=self.reqattr(REQUEST,'roles', []) + groups=self.reqattr(REQUEST, 'groupnames', []) + + if not username: + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='A username must be specified', + action ='manage_main') + + if (password or password_confirm) and (password != password_confirm): + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='Password and confirmation do not match', + action ='manage_main') + + self._doChangeUser(username, password, roles, domains='', groups=groups, REQUEST=REQUEST) + + return self.MessageDialog(self,REQUEST=REQUEST, + title = 'User Updated', + message= 'User %s was updated.'%(username), + action = 'manage_main') + + + # Methode special pour ScoDoc: evite le code inutile dans notre contexte + # et accede a la BD via le curseur psycopg2 fourni + # (facilitera la separation de Zope) + def scodoc_editUser(self, cursor, username, password=None, roles=[]): + """Edit a ScoDoc user""" + roles = list(roles) + rolestring= ','.join(roles) + # Don't change passwords if it's null + if password: + secret=self.cryptPassword(username, password) + # Update just the password: + # self.sqlUpdateUserPassword(username=username, password=secret) + cursor.execute("UPDATE sco_users SET passwd=%(secret)s WHERE user_name=%(username)s", + { 'secret':secret, 'username': username } ) + + #self.sqlUpdateUser(username=username, roles=rolestring) + cursor.execute("UPDATE sco_users SET roles=%(rolestring)s WHERE user_name=%(username)s", + { 'rolestring':rolestring, 'username': username } ) + + if hasattr(self.currentAuthSource, '_v_lastUser'): + # Specific for pgAuthSource: + self.currentAuthSource._v_lastUser={} # clear pg user cache + + # We may have updated roles or passwords... flush the user... + self.cache_removeUser(username) + self.xcache_removeUser(username) + + # + # Membership helper + # + def goHome(self, REQUEST, RESPONSE): + """ Go to home directory """ + if self.currentMembershipSource: + self.currentMembershipSource.goHome(REQUEST, RESPONSE) + + + # + # Membership method of changing user properties + # + + def manage_changeProps(self, REQUEST): + """ Change Properties """ + if self.currentMembershipSource: + return self.currentMembershipSource.changeProperties(REQUEST) + else: + + return self.MessageDialog(self,REQUEST, + title = 'This is a test', + message= 'This was a test', + action = '..') + + + # + # Membership method of adding a new user. + # If everything goes well the membership plugin calls manage_addUser() + # + + def manage_signupUser(self, REQUEST): + """ Signup a new user """ + """ This is seperate so you can add users using the normal """ + """ interface w/o going through membership policy """ + + username=self.reqattr(REQUEST,'username') + roles=self.reqattr(REQUEST,'roles') + + if not username: + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='A username must be specified', + action ='manage_main') + + if (self.getUser(username) or + (self._emergency_user and + username == self._emergency_user.getUserName())): + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='A user with the specified name already exists', + action ='manage_main') + + if self.currentMembershipSource: + return self.currentMembershipSource.createUser(REQUEST) + + # + # Membership method of changing passwords + # + def manage_changePassword(self, REQUEST): + """ Change a password """ + if self.currentMembershipSource: + return self.currentMembershipSource.changePassword(REQUEST) + + # + # User says they can't remember their password + # + def manage_forgotPassword(self, REQUEST): + """ So something about forgetting your password """ + if self.currentMembershipSource: + return self.currentMembershipSource.forgotPassword(REQUEST) + + def __creatable_by_emergency_user__(self): return 1 + + def manage_addUser(self, REQUEST): + """ Add a New User """ + username=self.reqattr(REQUEST,'username') + password=self.reqattr(REQUEST,'password') + password_confirm=self.reqattr(REQUEST,'password_confirm') + roles=self.reqattr(REQUEST,'roles') + groups=self.reqattr(REQUEST, 'groupnames', []) + + if not username: + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='A username must be specified', + action ='manage_main') + + if not password or not password_confirm: + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='Password and confirmation must be specified', + action ='manage_main') + + if (self.getUser(username) or + (self._emergency_user and + username == self._emergency_user.getUserName())): + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='A user with the specified name already exists', + action ='manage_main') + + if (password or password_confirm) and (password != password_confirm): + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Illegal value', + message='Password and confirmation do not match', + action ='manage_main') + + self._doAddUser(username, password, roles, domains='', groups=groups, REQUEST=REQUEST) + # + # Explicitly check our contents, do not just acquire postUserCreate + # + if 'postUserCreate' in self.objectIds(): + self.postUserCreate(self, REQUEST) + + return self.MessageDialog(self,REQUEST=REQUEST, + title = 'User Created', + message= 'User %s was created.'%(username), + action = 'manage_main') + + def _doAddUser(self, name, password, roles, domains='', groups=(), **kw): + """ For programatically adding simple users """ + self.currentAuthSource.createUser(name, password, roles) + if self.currentPropSource: + # copy items not in kw from REQUEST + REQUEST = kw.get('REQUEST', self.REQUEST) + map(kw.setdefault, REQUEST.keys(), REQUEST.values()) + self.currentPropSource.createUser(name, kw) + + def _doChangeUser(self, name, password, roles, domains='', groups=(), **kw): + self.currentAuthSource.updateUser(name, password, roles) + if self.currentPropSource: + # copy items not in kw from REQUEST + REQUEST = kw.get('REQUEST', self.REQUEST) + map(kw.setdefault, REQUEST.keys(), REQUEST.values()) + self.currentPropSource.updateUser(name, kw) + # We may have updated roles or passwords... flush the user... + self.cache_removeUser(name) + self.xcache_removeUser(name) + + def _doDelUsers(self, names): + self.deleteUsers(names) + + def _createInitialUser(self): + if len(self.getUserNames()) <= 1: + info = readUserAccessFile('inituser') + if info: + name, password, domains, remote_user_mode = info + self._doAddUser(name, password, ('Manager',), domains) + + + def getUsers(self): + """Return a list of user objects or [] if no users exist""" + data=[] + try: + items=self.listUsers() + for people in items: + user=User({'name': people['username'], + 'password': people['password'], + 'roles': people['roles'], + 'domains': ''}, + self.currentPropSource, + self.cryptPassword, + self.currentAuthSource, + self.currentGroupSource) + data.append(user) + except: + import traceback + traceback.print_exc() + pass + + return data + + getUsers__roles__=('Anonymous','Authenticated') + + def getUser(self, name): + """Return the named user object or None if no such user exists""" + user = self.cache_getUser(name, '', 0) + #zLOG.LOG('exUserFolder.getUser', zLOG.PANIC, 'cache_getUser(%s)=%s' % (name,user)) + if user: + return user + try: + items=self.listOneUser(name) + #zLOG.LOG('exUserFolder.getUser', zLOG.PANIC, 'listOneUser=%s' % items) + except: + zLOG.LOG("exUserFolder", zLOG.ERROR, + "error trying to list user %s" % name, + '', + sys.exc_info()) + return None + + if not items: + return None + + for people in items: + user = User({'name': people['username'], + 'password':people['password'], + 'roles': people['roles'], + 'domains': ''}, + self.currentPropSource, + self.cryptPassword, + self.currentAuthSource, + self.currentGroupSource) + return user + return None + + def manage_userActions(self, submit=None, userids=None, REQUEST={}): + """ Do things to users """ + if submit==' Add ': + if hasattr(self.currentAuthSource,'manage_addUserForm'): + return self.currentAuthSource.manage_addUserForm(self, REQUEST) + else: + return self.manage_addUserForm(self, REQUEST) + if submit==' Delete ': + self.deleteUsers(userids) + return self.MessageDialog(self,REQUEST=REQUEST, + title ='Users Deleted', + message='Selected Users have been deleted', + action =REQUEST['URL1']+'/manage_main', + target ='manage_main') + + if REQUEST: + return self.manage_main(self,REQUEST) + return '' + + def identify(self, auth): + # Identify the username and password. This is where new modes should + # be called from, and if pluggable modes ever take shape, here ya go! + + if self.cookie_mode and not auth: + # The identify signature does not include the request, sadly. + # I think that's dumb. + request = self.REQUEST + response = request.RESPONSE + + if request.has_key('__ac_name') and request.has_key('__ac_password'): + return request['__ac_name'], request['__ac_password'] + elif request.has_key('__ac') and self.cookie_mode == 1: + return self.decodeBasicCookie(request, response) + elif request.has_key('__aca') and self.cookie_mode == 2: + return self.decodeAdvancedCookie(request, response) + + if auth and lower(auth[:6]) == 'basic ': + return tuple(split(decodestring(split(auth)[-1]), ':', 1)) + + return None, None + + def decodeUserCookie(self, request, response): + return self.identify('') + + def validate(self, request, auth='', roles=_noroles): + """ + Perform identification, authentication, and authorization. + """ + # Called at each web request + #zLOG.LOG('exUserFolder', zLOG.PANIC, 'validate') + v = request['PUBLISHED'] + a, c, n, v = self._getobcontext(v, request) + + name, password = self.identify(auth) # decode cookie, and raises LoginRequired if no ident info + # password is the cleartext passwd + # zLOG.LOG('exUserFolder', zLOG.DEBUG, 'identify returned %s, %s' % (name, password)) + + response = request.RESPONSE + if name is not None: + try: + xcached_user = self.xcache_getUser(name) + #zLOG.LOG('exUserFolder.validate', zLOG.PANIC, 'xcached_user=%s' % xcached_user) + if xcached_user: + #zLOG.LOG('exUserFolder.validate', zLOG.PANIC, 'returning None') + return None + except: + zLOG.LOG('exUserFolder', zLOG.ERROR, + "error while looking up '%s' on the xcache" % name, + '', + sys.exc_info()) + + user = self.authenticate(name, password, request) + #zLOG.LOG('exUserFolder.validate', zLOG.PANIC, 'user=%s' % user) + if user is None: + # If it's none, because there's no user by that name, + # don't raise a login, allow it to go higher... + # This kinda breaks for people putting in the wrong username + # when the Folder above uses a different auth method. + # But it doesn't lock Manager users out inside Zope. + # Perhaps this should be a tunable. + + # modified by Emmanuel + try: + lou = self.listOneUser(name) + except: + lou = None + if lou: + self.challenge(request, response, 'login_failed', auth) + return None + self.remember(name, password, request) + self.cache_addToCache(name, password, user) + emergency = self._emergency_user + if emergency and user is emergency: + if self._isTop(): + return emergency.__of__(self) + else: + return None + if self.authorize(user, a, c, n, v, roles): + return user.__of__(self) + if self._isTop() and self.authorize(self._nobody, a, c, n, v, roles): + return self._nobody.__of__(self) + self.challenge(request, response, 'unauthorized') + return None + else: + if self.sessionTracking and self.currentPropSource: + user = self.createAnonymousUser(request, response) + if self.authorize(user, a, c, n, v, roles): + return user.__of__(self) + if self.authorize(self._nobody, a, c, n, v, roles): + if self._isTop(): + return self._nobody.__of__(self) + else: + return None + else: + self.challenge(request, response, None, auth) + return None + + def authenticate(self, name, password, request): + #zLOG.LOG('exUserFolder.authenticate', zLOG.PANIC, '%s %s' % (name, password)) + emergency = self._emergency_user + if emergency and name == emergency.getUserName(): + return emergency + try: + user = self.cache_getUser(name, password) + #zLOG.LOG('exUserFolder.authenticate', zLOG.PANIC, 'cache_getUser=%s' % user) + if user: + return user + except SessionExpiredException: + if self.idleTimeout: + self.logout(request) + self.challenge(request, request.RESPONSE, 'session_expired') + return None + user = self.getUser(name) + #zLOG.LOG('exUserFolder.authenticate', zLOG.PANIC, 'getUser=%s' % user) + if user is not None: + if user.authenticate(self.currentAuthSource.listOneUser, + password, + request, + self.currentAuthSource.remoteAuthMethod): + return user + return None + + def challenge(self, request, response, reason_code='unauthorized', + auth=''): + # Give whatever mode we're in a chance to challenge the validation + # failure. We do this to preserve LoginRequired behavior. The + # other thing we could do is let the None propagate on up and patch + # the request's unauthorized method to + + if self.cookie_mode and not auth: + zLOG.LOG('exUserFolder', zLOG.DEBUG, 'raising LoginRequired for %s' % reason_code) + if reason_code == 'login_failed': + response.expireCookie('__ac', path='/') + response.expireCookie('__aca', path='/') + if reason_code: + request.set('authFailedCode', reason_code) + raise LoginRequired(self.docLogin(self, request)) + else: + zLOG.LOG('exUserFolder', zLOG.DEBUG, 'not raising LoginRequired for %s' % reason_code) + + def remember(self, name, password, request): + response = request.RESPONSE + if self.cookie_mode == 1: + self.setBasicCookie(name, password, request, response) + elif self.cookie_mode == 2: + self.setAdvancedCookie(name, password, request, response) + + if self.cookie_mode: + try: + del request.form['__ac_name'] + del request.form['__ac_password'] + except KeyError: + pass + + def makeRedirectPath(self): + REQUEST=self.REQUEST + if not REQUEST.has_key('destination'): + script=REQUEST['SCRIPT_NAME'] + pathinfo=REQUEST['PATH_INFO'] + redirectstring=script+pathinfo + if REQUEST.has_key('QUERY_STRING'): + querystring='?'+quote(REQUEST['QUERY_STRING']) + redirectstring=redirectstring+querystring + + REQUEST['destination']=redirectstring + + def redirectToLogin(self, REQUEST): + """ Allow methods to call from Web """ + script='' + pathinfo='' + querystring='' + redirectstring='' + authFailedCode='' + + if not REQUEST.has_key('destination'): + if self.currentMembershipSource: + redirectstring = self.currentMembershipSource.getLoginDestination(REQUEST) + else: + script=REQUEST['SCRIPT_NAME'] + pathinfo=REQUEST['PATH_INFO'] + redirectstring=script+pathinfo + if REQUEST.has_key('QUERY_STRING'): + querystring='?'+REQUEST['QUERY_STRING'] + redirectstring=redirectstring+querystring + + REQUEST['destination']=redirectstring + + + if REQUEST.has_key('authFailedCode'): + authFailedCode='&authFailedCode='+REQUEST['authFailedCode'] + + + + if self.currentMembershipSource and self.currentMembershipSource.loginPage: + try: + REQUEST.RESPONSE.redirect('%s/%s?destination=%s%s'%(self.currentMembershipSource.baseURL, self.currentMembershipSource.loginPage,REQUEST['destination'],authFailedCode)) + return + except: + pass + return self.docLogin(self,REQUEST) + + def decodeBasicCookie(self, request, response): + c=request['__ac'] + c=unquote(c) + try: + c=decodestring(c) + except: + response.expireCookie('__ac', path='/') + raise LoginRequired(self.docLogin(self, request)) + + name,password=tuple(split(c, ':', 1)) + return name, password + + def decodeAdvancedCookie(self, request, response): + c = '' + try: + c = request['__aca'] + c = unquote(c) + except: + response.expireCookie('__aca', path='/') + response.expireCookie('__ac', path='/') # Precaution + response.flush() + raise LoginRequired(self.docLogin(self, request)) + + u = self.cache_getCookieCacheUser(c) + if u: + return u + + response.expireCookie('__aca', path='/') + response.expireCookie('__ac', path='/') # Precaution + response.flush() + raise LoginRequired(self.docLogin(self, request)) + + def setBasicCookie(self, name, password, request, response): + token='%s:%s' % (name, password) + token=encodestring(token) + token=quote(token) + response.setCookie('__ac', token, path='/') + request['__ac']=token + + def setAdvancedCookie(self, name, password, request, response): + xufid = self._p_oid + hash = encodestring(sha.new('%s%s%f%f%s'%( + name, password, time(), random.random(), str(request))).digest()) + token=quote(hash) + response.setCookie('__aca', token, path='/') + response.flush() + request['__aca']=token + self.cache_addToCookieCache(name, password, hash) + + def setAnonCookie(self, name, request, resp): + token='%s:%s' % (name, '') + token=encodestring(token) + token=quote(token) + resp.setCookie('__ac', token, path='/') + request['__ac']=token + + def createAnonymousUser(self, request, resp): + aName=createTempName() + bogusREQUEST={} + bogusREQUEST['user_realname']='Guest User' + self.currentPropSource.createUser(aName, bogusREQUEST) + ob = AnonUser(aName, [], self.currentPropSource) + ob = ob.__of__(self) + self.cache_addToCache(aName, '', ob) + self.setAnonCookie(aName, request, resp) + return ob + + def manage_edit(self, cookie_mode, session_length, sessionTracking=None, + idleTimeout=0, not_session_length=0, + title=None, + REQUEST=None): + """Change properties""" + + self.cookie_mode=cookie_mode + self.sessionLength=session_length + self.notSessionLength=not_session_length + self.sessionTracking=sessionTracking + self.idleTimeout=idleTimeout + if title: + self.title = title + + if REQUEST: + return self.MessageDialog(self,REQUEST=REQUEST, + title ='exUserFolder Changed', + message='exUserFolder properties have been updated', + action =REQUEST['URL1']+'/manage_main', + target ='manage_main') + + def logout(self, REQUEST): + """Logout""" + try: + self.cache_removeUser(REQUEST['AUTHENTICATED_USER'].getUserName()) + except: + pass + + REQUEST['RESPONSE'].expireCookie('__ac', path='/') + REQUEST.cookies['__ac']='' + try: + acc = REQUEST['__aca'] + self.cache_removeCookieCacheUser(acc) + REQUEST.cookies['__aca']='' + except: + pass + REQUEST['RESPONSE'].expireCookie('__aca', path='/') + + + + return self.docLogout(self, REQUEST) + + # + # Methods to be supplied by Auth Source + # + def deleteUsers(self, userids): + self.currentAuthSource.deleteUsers(userids) + + # Comment out to use Andreas' pgSchema + if self.currentPropSource: + self.currentPropSource.deleteUsers(userids) + + if self.currentGroupSource: + self.currentGroupSource.deleteUsers(userids) + + + def listUsers(self): + return self.currentAuthSource.listUsers() + + def user_names(self): + return self.currentAuthSource.listUserNames() + + def getUserNames(self): + return self.currentAuthSource.listUserNames() + + def listOneUser(self,username): + return self.currentAuthSource.listOneUser(username) + + def cryptPassword(self, username, password): + if hasattr(aq_base(self.currentAuthSource), 'cryptPassword'): + return self.currentAuthSource.cryptPassword(username, password) + + if hasattr(self, 'cryptoId'): + return self.cryptoSources[self.cryptoId].plugin(self, username, password) + return self.cryptoSources['Crypt'].plugin(self, username, password) + + def PropertyEditor(self): + """ """ + if self.REQUEST.has_key(self.REQUEST['propName']): + return PropertyEditor.EditMethods[self.REQUEST['propType']](self.REQUEST['propName'], self.REQUEST[self.REQUEST['propName']]) + return PropertyEditor.EditMethods[self.REQUEST['propType']](self.REQUEST['propName'], None) + + def PropertyView(self): + """ """ + if self.REQUEST.has_key(self.REQUEST['propName']): + return PropertyEditor.ViewMethods[self.REQUEST['propType']](self.REQUEST['propName'], self.REQUEST[self.REQUEST['propName']]) + return PropertyEditor.ViewMethods[self.REQUEST['propType']](self.REQUEST['propName'], None) + + def manage_addUserProperty(self, username, propName, propValue, REQUEST): + """ add a new property """ + self.currentPropSource.setUserProperty(propName, username, propValue) + if hasattr(self.currentAuthSource,'manage_editUserForm'): + return self.currentAuthSource.manage_editUserForm(self, REQUEST) + else: + return self.manage_editUserForm(self,REQUEST) + + def getUserCacheStats(self): + """ Stats """ + if self.sessionLength: + if self.cache_getCacheStats()['attempts']: + return self.cache_getCacheStats() + return None + + def getUserCacheUsers(self): + """ Current Users """ + if self.sessionLength: + return self.cache_getCurrentUsers() + return None + + def userFolderAddGroup(self, groupname, title='', **kw): + """Creates a group""" + if self.currentGroupSource: + apply(self.currentGroupSource.addGroup, (groupname, title), kw) + + def userFolderDelGroups(self, groupnames): + """Deletes groups""" + if self.currentGroupSource: + for groupname in groupnames: + self.currentGroupSource.delGroup(groupname) + + def getGroupNames(self): + """Returns a list of group names""" + if self.currentGroupSource: + return self.currentGroupSource.listGroups() + else: + return [] + + + def getGroupById(self, groupname, default=_marker): + """Returns the given group""" + if self.currentGroupSource: + group = self.currentGroupSource.getGroup(groupname, default) + if group: + return group.__of__(self) + else: + return None + + def setUsersOfGroup(self, usernames, groupname): + """Sets the users of the group""" + if self.currentGroupSource: + return self.currentGroupSource.setUsersOfGroup(usernames, groupname) + + def addUsersToGroup(self, usernames, groupname): + """Adds users to a group""" + if self.currentGroupSource: + return self.currentGroupSource.addUsersToGroup(usernames, groupname) + + def delUsersFromGroup(self, usernames, groupname): + """Deletes users from a group""" + if self.currentGroupSource: + return self.currentGroupSource.delUsersFromGroup(usernames, groupname) + + def setGroupsOfUser(self, groupnames, username): + """Sets the groups of a user""" + if self.currentGroupSource: + return self.currentGroupSource.setGroupsOfUser(groupnames, username) + + def addGroupsOfUser(self, groupnames, username): + """Add groups to a user""" + if self.currentGroupSource: + return self.currentGroupSource.addGroupsToUser(groupnames, username) + + def delGroupsOfUser(self, groupnames, username): + """Deletes groups from a user""" + if self.currentGroupSource: + return self.currentGroupSource.delGroupsFromUser(groupnames, username) + + # We lie. + def hasUsers(self): + return 1 + + +def doAuthSourceForm(self,authId): + """ la de da """ + return exUserFolder.authSources[authId].manage_addForm + +def doPropSourceForm(self,propId): + """ la de da """ + return exUserFolder.propSources[propId].manage_addForm + +def doMembershipSourceForm(self, memberId): + """ doot de doo """ + return exUserFolder.membershipSources[memberId].manage_addForm + +#def doGroupSourceForm(self,groupId): +# """ la de da """ +# return exUserFolder.groupSources[groupId].manage_addForm + +def getAuthSources(self): + """ Hrm I need a docstring """ + l=[] + for o in exUserFolder.authSources.keys(): + l.append( + exUserFolder.authSources[o] + ) + return l + +def getPropSources(self): + """ Hrm I need a docstring """ + l=[] + for o in exUserFolder.propSources.keys(): + l.append( + exUserFolder.propSources[o] + ) + return l + +def getMembershipSources(self): + """ Hrm I need a docstring """ + l=[] + for o in exUserFolder.membershipSources.keys(): + l.append( + exUserFolder.membershipSources[o] + ) + return l + +def getGroupSources(self): + """ Hrm I need a docstring """ + return [] # UNUSED by ScoDoc: empty + +def getCryptoSources(self): + """ Doc String """ + l = [] + for o in exUserFolder.cryptoSources.keys(): + l.append( + exUserFolder.cryptoSources[o] + ) + return l + +def MailHostIDs(self): + """Find SQL database connections in the current folder and above + + This function return a list of ids. + """ + return [] # UNUSED BY SCODOC + +from types import ListType, IntType, LongType, FloatType, NoneType, DictType, StringType + +def getVariableType(self, o): + + if type(o) == ListType: + return 'List' + if type(o) == IntType: + return 'Int' + if type(o) == LongType: + return 'Long' + if type(o) == FloatType: + return 'Float' + if type(o) == NoneType: + return 'None' + if type(o) == DictType: + return 'Dict' + if type(o) == StringType: + return 'String' + return 'Unknown or Restricted' + +_postUserCreate=''' + +Replace this method with whatever you want to do +when a user is created, you can use a Python Script, +or External Method, or keep it as a DTML Method if you +want to + +''' diff --git a/ZopeProducts/exUserFolder/exUserFolderPlugin.gif b/ZopeProducts/exUserFolder/exUserFolderPlugin.gif new file mode 100644 index 0000000000000000000000000000000000000000..dd8e2c9f736567a421b9e96dae3313469c071b5c GIT binary patch literal 402 zcmWlUKS/>B<*q6W^8yKt01NxgvDrOG$7CDL&BH4SRFcNUjdgwUm2>;+X3?9{Ja=lZ8; zyCOJkwu070v(3*FR=zzZd=-DH{jBO1pjH_)DWE$2J#l z>gBbi?aEoYIU!x%ex1y3{5>fR-c6^JfvJ;E7ZcIG)YB(cPnX)8DhT;mQMVK+swG6B zJe#oMLsEBjWmU6cuN7UA3vy(3PO+Yw+Hm}P{gbx literal 0 HcmV?d00001 diff --git a/ZopeProducts/exUserFolder/nullPlugin/__init__.py b/ZopeProducts/exUserFolder/nullPlugin/__init__.py new file mode 100644 index 00000000..b0d9e576 --- /dev/null +++ b/ZopeProducts/exUserFolder/nullPlugin/__init__.py @@ -0,0 +1,2 @@ +# $Id: __init__.py,v 1.4 2004/11/10 14:15:57 akm Exp $ +import nullPlugin diff --git a/ZopeProducts/exUserFolder/nullPlugin/nullPlugin.py b/ZopeProducts/exUserFolder/nullPlugin/nullPlugin.py new file mode 100644 index 00000000..dd9cd526 --- /dev/null +++ b/ZopeProducts/exUserFolder/nullPlugin/nullPlugin.py @@ -0,0 +1,39 @@ +# +# Extensible User Folder +# +# Null Plugin for exUserFolder +# +# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd +# ACN: 082 081 472 ABN: 83 082 081 472 +# All Rights Reserved +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Author: Andrew Milton +# $Id: nullPlugin.py,v 1.5 2004/11/10 14:15:57 akm Exp $ +import string,Acquisition + +from Globals import HTMLFile, INSTANCE_HOME + +from OFS.Folder import Folder + +from Products.exUserFolder.exUserFolder import exUserFolder +from Products.exUserFolder.Plugins import PluginRegister + +class NullPlugin(Folder): + + def __init__(self): + pass + + def postInitialisation(self, REQUEST): + pass diff --git a/ZopeProducts/exUserFolder/version.txt b/ZopeProducts/exUserFolder/version.txt new file mode 100644 index 00000000..af0accc3 --- /dev/null +++ b/ZopeProducts/exUserFolder/version.txt @@ -0,0 +1 @@ +exUserFolder-0-50-1 diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..97c50056 --- /dev/null +++ b/__init__.py @@ -0,0 +1,75 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +from ZScolar import ZScolar, manage_addZScolarForm, manage_addZScolar + +# from ZNotes import ZNotes, manage_addZNotesForm, manage_addZNotes + +from ZScoDoc import ZScoDoc, manage_addZScoDoc + +# from sco_zope import * +# from notes_log import log +# log.set_log_directory( INSTANCE_HOME + '/log' ) + + +__version__ = "1.0.0" + + +def initialize(context): + """initialize the Scolar products""" + # called at each startup (context is a ProductContext instance, basically useless) + + # --- ZScolars + context.registerClass( + ZScolar, + constructors=( + manage_addZScolarForm, # this is called when someone adds the product + manage_addZScolar, + ), + icon="static/icons/sco_icon.png", + ) + + # context.registerHelp() + # context.registerHelpTitle("ZScolar") + + # --- ZScoDoc + context.registerClass( + ZScoDoc, constructors=(manage_addZScoDoc,), icon="static/icons/sco_icon.png" + ) + + # --- ZNotes + # context.registerClass( + # ZNotes, + # constructors = ( + # manage_addZNotesForm, + # manage_addZNotes + # ), + # icon = 'static/icons/notes_icon.png' + # ) + + # context.registerHelp() + # context.registerHelpTitle("ZNotes") diff --git a/bonus_sport.py b/bonus_sport.py new file mode 100644 index 00000000..c002ff59 --- /dev/null +++ b/bonus_sport.py @@ -0,0 +1,422 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +from operator import mul +import pprint + + +def bonus_iutv(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport, culture), règle IUT Villetaneuse + + Les Ă©tudiants de l'IUT peuvent suivre des enseignements optionnels + de l'UniversitĂ© Paris 13 (sports, musique, deuxième langue, + culture, etc) non rattachĂ©s Ă  une unitĂ© d'enseignement. Les points + au-dessus de 10 sur 20 obtenus dans chacune des matières + optionnelles sont cumulĂ©s et 5% de ces points cumulĂ©s s'ajoutent Ă  + la moyenne gĂ©nĂ©rale du semestre dĂ©jĂ  obtenue par l'Ă©tudiant. + """ + sumc = sum(coefs) # assumes sum. coefs > 0 + note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondĂ©rĂ©e + bonus = sum([(x - 10) / 20.0 for x in notes_sport if x > 10]) + return bonus + + +def bonus_iut_stdenis(notes_sport, coefs, infos=None): + """Semblable Ă  bonus_iutv mais sans coefficients et total limitĂ© Ă  0.5 points. + """ + points = sum([x - 10 for x in notes_sport if x > 10]) # points au dessus de 10 + bonus = points * 0.05 # ou / 20 + return min(bonus, 0.5) # bonus limitĂ© Ă  1/2 point + + +def bonus_colmar(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport, culture), règle IUT Colmar. + + Les Ă©tudiants de l'IUT peuvent suivre des enseignements optionnels + de l'U.H.A. (sports, musique, deuxième langue, culture, etc) non + rattachĂ©s Ă  une unitĂ© d'enseignement. Les points au-dessus de 10 + sur 20 obtenus dans chacune des matières optionnelles sont cumulĂ©s + dans la limite de 10 points. 5% de ces points cumulĂ©s s'ajoutent Ă  + la moyenne gĂ©nĂ©rale du semestre dĂ©jĂ  obtenue par l'Ă©tudiant. + + """ + # les coefs sont ignorĂ©s + points = sum([x - 10 for x in notes_sport if x > 10]) + points = min(10, points) # limite total Ă  10 + bonus = points / 20.0 # 5% + return bonus + + +def bonus_iutva(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport, culture), règle IUT Ville d'Avray + + Les Ă©tudiants de l'IUT peuvent suivre des enseignements optionnels + de l'UniversitĂ© Paris 10 (C2I) non rattachĂ©s Ă  une unitĂ© d'enseignement. + Si la note est >= 10 et < 12, bonus de 0.1 point + Si la note est >= 12 et < 16, bonus de 0.2 point + Si la note est >= 16, bonus de 0.3 point + Ce bonus s'ajoute Ă  la moyenne gĂ©nĂ©rale du semestre dĂ©jĂ  obtenue par + l'Ă©tudiant. + """ + sumc = sum(coefs) # assumes sum. coefs > 0 + note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondĂ©rĂ©e + if note_sport >= 16.0: + return 0.3 + if note_sport >= 12.0: + return 0.2 + if note_sport >= 10.0: + return 0.1 + return 0 + + +# XXX InutilisĂ© (mai 2020) ? Ă  confirmer avant suppression XXX +# def bonus_iut1grenoble_v0(notes_sport, coefs, infos=None): +# """Calcul bonus sport IUT Grenoble sur la moyenne gĂ©nĂ©rale +# +# La note de sport de nos Ă©tudiants va de 0 Ă  5 points. +# Chaque point correspond Ă  un % qui augmente la moyenne de chaque UE et la moyenne gĂ©nĂ©rale. +# Par exemple : note de sport 2/5 : chaque UE sera augmentĂ©e de 2%, ainsi que la moyenne gĂ©nĂ©rale. +# +# Calcul ici du bonus sur moyenne gĂ©nĂ©rale et moyennes d'UE non capitalisĂ©es. +# """ +# # les coefs sont ignorĂ©s +# # notes de 0 Ă  5 +# points = sum([x for x in notes_sport]) +# factor = (points / 4.0) / 100.0 +# bonus = infos["moy"] * factor +# # Modifie les moyennes de toutes les UE: +# for ue_id in infos["moy_ues"]: +# ue_status = infos["moy_ues"][ue_id] +# if ue_status["sum_coefs"] > 0: +# # modifie moyenne UE ds semestre courant +# ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] * (1.0 + factor) +# if not ue_status["is_capitalized"]: +# # si non capitalisee, modifie moyenne prise en compte +# ue_status["moy"] = ue_status["cur_moy_ue"] +# +# # open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' ) +# return bonus + + +def bonus_iut1grenoble_2017(notes_sport, coefs, infos=None): + """Calcul bonus sport IUT Grenoble sur la moyenne gĂ©nĂ©rale (version 2017) + + La note de sport de nos Ă©tudiants va de 0 Ă  5 points. + Chaque point correspond Ă  un % qui augmente la moyenne de chaque UE et la moyenne gĂ©nĂ©rale. + Par exemple : note de sport 2/5 : la moyenne gĂ©nĂ©rale sera augmentĂ©e de 2%. + + Calcul ici du bonus sur moyenne gĂ©nĂ©rale + """ + # les coefs sont ignorĂ©s + # notes de 0 Ă  5 + points = sum([x for x in notes_sport]) + factor = (points / 4.0) / 100.0 + bonus = infos["moy"] * factor + + return bonus + + +def bonus_lille(notes_sport, coefs, infos=None): + """calcul bonus modules optionels (sport, culture), règle IUT Villeneuve d'Ascq + + Les Ă©tudiants de l'IUT peuvent suivre des enseignements optionnels + de l'UniversitĂ© Lille 1 (sports,etc) non rattachĂ©s Ă  une unitĂ© d'enseignement. Les points + au-dessus de 10 sur 20 obtenus dans chacune des matières + optionnelles sont cumulĂ©s et 4% (2% avant aout 2010) de ces points cumulĂ©s s'ajoutent Ă  + la moyenne gĂ©nĂ©rale du semestre dĂ©jĂ  obtenue par l'Ă©tudiant. + """ + sumc = sum(coefs) # assumes sum. coefs > 0 + note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondĂ©rĂ©e + if ( + infos["sem"]["date_debut_iso"] > "2010-08-01" + ): # changement de regle en aout 2010. + return sum([(x - 10) / 25.0 for x in notes_sport if x > 10]) + return sum([(x - 10) / 50.0 for x in notes_sport if x > 10]) + + +# Fonction Le Havre, par Dom. Soud. +def bonus_iutlh(notes_sport, coefs, infos=None): + """Calcul bonus sport IUT du Havre sur moyenne gĂ©nĂ©rale et UE + + La note de sport de nos Ă©tudiants va de 0 Ă  20 points. + m2=m1*(1+0.005*((10-N1)+(10-N2)) + m2 : Nouvelle moyenne de l'unitĂ© d'enseignement si note de sport et/ou de langue supĂ©rieure Ă  10 + m1 : moyenne de l'unitĂ© d'enseignement avant bonification + N1 : note de sport si supĂ©rieure Ă  10 + N2 : note de seconde langue si supĂ©rieure Ă  10 + Par exemple : sport 15/20 et langue 12/20 : chaque UE sera multipliĂ©e par 1+0.005*7, ainsi que la moyenne gĂ©nĂ©rale. + Calcul ici de la moyenne gĂ©nĂ©rale et moyennes d'UE non capitalisĂ©es. + """ + # les coefs sont ignorĂ©s + points = sum([x - 10 for x in notes_sport if x > 10]) + points = min(10, points) # limite total Ă  10 + factor = 1.0 + (0.005 * points) + # bonus nul puisque les moyennes sont directement modifiĂ©es par factor + bonus = 0 + # Modifie la moyenne gĂ©nĂ©rale + infos["moy"] = infos["moy"] * factor + # Modifie les moyennes de toutes les UE: + for ue_id in infos["moy_ues"]: + ue_status = infos["moy_ues"][ue_id] + if ue_status["sum_coefs"] > 0: + # modifie moyenne UE ds semestre courant + ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] * factor + if not ue_status["is_capitalized"]: + # si non capitalisee, modifie moyenne prise en compte + ue_status["moy"] = ue_status["cur_moy_ue"] + + # open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' ) + return bonus + + +# Bonus sport IUT Tours +def bonus_tours(notes_sport, coefs, infos=None): + """Calcul bonus sport & culture IUT Tours sur moyenne generale + + La note de sport & culture de nos etudiants est applique sur la moyenne generale. + """ + return min(1.0, sum(notes_sport)) # bonus maximum de 1 point + + +def bonus_iutr(notes_sport, coefs, infos=None): + """Calcul du bonus , regle de l'IUT de Roanne (contribuĂ©e par Raphael C., nov 2012) + + Le bonus est compris entre 0 et 0.35 point. + cette procĂ©dure modifie la moyenne de chaque UE capitalisable. + + """ + # modifie les moyennes de toutes les UE: + # le bonus est le minimum entre 0.35 et la somme de toutes les bonifs + bonus = min(0.35, sum([x for x in notes_sport])) + for ue_id in infos["moy_ues"]: + # open('/tmp/log','a').write( ue_id + infos['moy_ues'] + '\n\n' ) + ue_status = infos["moy_ues"][ue_id] + if ue_status["sum_coefs"] > 0: + # modifie moyenne UE dans semestre courant + ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] + bonus + if not ue_status["is_capitalized"]: + ue_status["moy"] = ue_status["cur_moy_ue"] + return bonus + + +def bonus_iutam(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport), regle IUT d'Amiens. + Les etudiants de l'IUT peuvent suivre des enseignements optionnels. + Si la note est de 10.00 a 10.49 -> 0.50% de la moyenne + Si la note est de 10.50 a 10.99 -> 0.75% + Si la note est de 11.00 a 11.49 -> 1.00% + Si la note est de 11.50 a 11.99 -> 1.25% + Si la note est de 12.00 a 12.49 -> 1.50% + Si la note est de 12.50 a 12.99 -> 1.75% + Si la note est de 13.00 a 13.49 -> 2.00% + Si la note est de 13.50 a 13.99 -> 2.25% + Si la note est de 14.00 a 14.49 -> 2.50% + Si la note est de 14.50 a 14.99 -> 2.75% + Si la note est de 15.00 a 15.49 -> 3.00% + Si la note est de 15.50 a 15.99 -> 3.25% + Si la note est de 16.00 a 16.49 -> 3.50% + Si la note est de 16.50 a 16.99 -> 3.75% + Si la note est de 17.00 a 17.49 -> 4.00% + Si la note est de 17.50 a 17.99 -> 4.25% + Si la note est de 18.00 a 18.49 -> 4.50% + Si la note est de 18.50 a 18.99 -> 4.75% + Si la note est de 19.00 a 20.00 -> 5.00% + Ce bonus s'ajoute a la moyenne generale du semestre de l'etudiant. + """ + # une seule note + note_sport = notes_sport[0] + if note_sport < 10.0: + return 0 + prc = min((int(2 * note_sport - 20.0) + 2) * 0.25, 5) + bonus = infos["moy"] * prc / 100 + return bonus + + +def bonus_saint_etienne(notes_sport, coefs, infos=None): + """IUT de Saint-Etienne (jan 2014) + Nous avons diffĂ©rents types de bonification + bonfication Sport / Associations + coopĂ©ratives de dĂ©partement / Bureau Des Étudiants + / engagement citoyen / Langues optionnelles + Nous ajoutons sur le bulletin une bonification qui varie entre 0,1 et 0,3 ou 0,35 pour chaque item + la bonification totale ne doit pas excĂ©der les 0,6 point. + Sur le bulletin nous ne mettons pas une note sur 20 mais directement les bonifications. + + + Dans ScoDoc: on a dĂ©clarer une UE "sport&culture" dans laquelle on aura des modules + pour chaque activitĂ© (Sport, Associations, ...) + avec Ă  chaque fois une note (ScoDoc l'affichera comme une note sur 20, mais en fait ce sera la + valeur de la bonification: entrer 0,1/20 signifiera un bonus de 0,1 point la moyenne gĂ©nĂ©rale) + """ + bonus = min(0.6, sum([x for x in notes_sport])) # plafonnement Ă  0.6 points + + return bonus + + +def bonus_iutTarbes(notes_sport, coefs, infos=None): + """Calcul bonus modules optionnels + (sport, Langues, action sociale, Théâtre), règle IUT Tarbes + Les coefficients ne sont pas pris en compte, + seule la meilleure note est prise en compte + le 1/30ème des points au-dessus de 10 sur 20 est retenu et s'ajoute Ă  + la moyenne gĂ©nĂ©rale du semestre dĂ©jĂ  obtenue par l'Ă©tudiant. + """ + bonus = max([(x - 10) / 30.0 for x in notes_sport if x > 10] or [0.0]) + return bonus + + +def bonus_iutSN(notes_sport, coefs, infos=None): + """Calcul bonus sport IUT Saint-Nazaire sur moyenne gĂ©nĂ©rale + + La note de sport de nos Ă©tudiants va de 0 Ă  5 points. + La note de culture idem, + Elles sont cumulables, + Chaque point correspond Ă  un % qui augmente la moyenne gĂ©nĂ©rale. + Par exemple : note de sport 2/5 : la moyenne gĂ©nĂ©rale sera augmentĂ©e de 2%. + + Calcul ici du bonus sur moyenne gĂ©nĂ©rale et moyennes d'UE non capitalisĂ©es. + """ + # les coefs sont ignorĂ©s + # notes de 0 Ă  5 + points = sum([x for x in notes_sport]) + factor = points / 100.0 + bonus = infos["moy"] * factor + return bonus + + +def bonus_iutBordeaux1(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport, culture), règle IUT Bordeaux 1, sur moyenne gĂ©nĂ©rale et UE + + Les Ă©tudiants de l'IUT peuvent suivre des enseignements optionnels + de l'UniversitĂ© Bordeaux 1 (sport, théâtre) non rattachĂ©s Ă  une unitĂ© d'enseignement. + En cas de double activitĂ©, c'est la meilleure des 2 notes qui compte. + Chaque point au-dessus de 10 sur 20 obtenus dans cet enseignement correspond Ă  un % + qui augmente la moyenne de chaque UE et la moyenne gĂ©nĂ©rale. + Formule : le % = points>moyenne / 2 + Par exemple : sport 13/20 : chaque UE sera multipliĂ©e par 1+0,015, ainsi que la moyenne gĂ©nĂ©rale. + + Calcul ici du bonus sur moyenne gĂ©nĂ©rale et moyennes d'UE non capitalisĂ©es. + """ + # open('/tmp/log','a').write( '\n---------------\n' + pprint.pformat(infos) + '\n' ) + # les coefs sont ignorĂ©s + # on rĂ©cupère la note maximum et les points au-dessus de la moyenne + sport = max(notes_sport) + points = max(0, sport - 10) + # on calcule le bonus + factor = (points / 2.0) / 100.0 + bonus = infos["moy"] * factor + # Modifie les moyennes de toutes les UE: + for ue_id in infos["moy_ues"]: + ue_status = infos["moy_ues"][ue_id] + if ue_status["sum_coefs"] > 0: + # modifie moyenne UE ds semestre courant + ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] * (1.0 + factor) + if not ue_status["is_capitalized"]: + # si non capitalisee, modifie moyenne prise en compte + ue_status["moy"] = ue_status["cur_moy_ue"] + + # open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' ) + return bonus + + +def bonus_iuto(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport, culture), règle IUT Orleans + * Avant aout 2013 + Un bonus de 2,5% de la note de sport est accordĂ© Ă  chaque UE sauf + les UE de Projet et Stages + * Après aout 2013 + Un bonus de 2,5% de la note de sport est accordĂ© Ă  la moyenne gĂ©nĂ©rale + """ + sumc = sum(coefs) # assumes sum. coefs > 0 + note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondĂ©rĂ©e + bonus = note_sport * 2.5 / 100 + if ( + infos["sem"]["date_debut_iso"] > "2013-08-01" + ): # changement de regle en aout 2013. + return bonus + coefs = 0 + coefs_total = 0 + for ue_id in infos["moy_ues"]: + ue_status = infos["moy_ues"][ue_id] + coefs_total = coefs_total + ue_status["sum_coefs"] + # Extremement spĂ©cifique (et n'est plus utilisĂ©) + if ue_status["ue"]["ue_code"] not in { + "ORA14", + "ORA24", + "ORA34", + "ORA44", + "ORB34", + "ORB44", + "ORD42", + "ORE14", + "ORE25", + "ORN44", + "ORO44", + "ORP44", + "ORV34", + "ORV42", + "ORV43", + }: + if ue_status["sum_coefs"] > 0: + coefs = coefs + ue_status["sum_coefs"] + # modifie moyenne UE ds semestre courant + ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] + bonus + if not ue_status["is_capitalized"]: + # si non capitalisee, modifie moyenne prise en compte + ue_status["moy"] = ue_status["cur_moy_ue"] + return bonus * coefs / coefs_total + + +def bonus_iutbethune(notes_sport, coefs, infos=None): + """Calcul bonus modules optionels (sport), règle IUT Bethune + + Les points au dessus de la moyenne de 10 apportent un bonus pour le semestre. + Ce bonus est Ă©gal au nombre de points divisĂ© par 200 et multipliĂ© par la + moyenne gĂ©nĂ©rale du semestre de l'Ă©tudiant. + """ + # les coefs sont ignorĂ©s + points = sum([x - 10 for x in notes_sport if x > 10]) + points = min(10, points) # limite total Ă  10 + bonus = int(infos["moy"] * points / 2) / 100.0 # moyenne-semestre x points x 0,5% + return bonus + + +def bonus_demo(notes_sport, coefs, infos=None): + """Fausse fonction "bonus" pour afficher les informations disponibles + et aider les dĂ©veloppeurs. + Les informations sont placĂ©es dans le fichier /tmp/scodoc_bonus.log + qui est ECRASE Ă  chaque appel. + *** Ne pas utiliser en production !!! *** + """ + f = open("/tmp/scodoc_bonus.log", "w") # mettre 'a' pour ajouter en fin + f.write("\n---------------\n" + pprint.pformat(infos) + "\n") + # Statut de chaque UE + # for ue_id in infos['moy_ues']: + # ue_status = infos['moy_ues'][ue_id] + # #open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' ) + + return 0.0 diff --git a/config/README b/config/README new file mode 100644 index 00000000..7d51341f --- /dev/null +++ b/config/README @@ -0,0 +1,56 @@ + + CONFIGURATION DE SCODOC + ----------------------- + +Emmanuel Viennet, juin 2008, mar 2017 + + + +0) INSTALL de base: +- prerequis: apache2, utilisateur www-data +- detarer ScoDoc.tgz (== Zope + produits + scodoc) + + +1) Creation de la base utilisateurs (initialement vide) + +------- + +Sept 2013: ScoDoc 7 : python2.7, Debian 7, Zope 2.13.21 + + +Pour la construction de Zope: +attention: on veut bĂ©nĂ©ficier des paquets python Debian ! +donc + +apt-get install python-dev +apt-get install python-virtualenv +apt-get install gcc +virtualenv --system-site-packages /opt/zope213 +cd zope213/ +bin/easy_install -i http://download.zope.org/Zope2/index/2.13.21 Zope2 +# ...long... + +bin/easy_install Products.ZSQLMethods +bin/easy_install ZPsycopgDA +# Avec Debian 8: probleme install de ZPsycopgDA +# essai en le copiant de la version Debian 7. Semble ok. + +# Problemes persistant avec pydot (voir commentaire dans script install) + +# Si besoin, crĂ©ation de l'instance Zope: +bin/mkzopeinstance -d /opt/inst + +(XXX admin / admin) + + +2) Migration bases de donnĂ©es +Sur ScoDoc6: + pg_dumpall > scodoc.dump.txt + + + +passage en UTF-8 +- sources Python +- locale systeme (scrip install) +- creation des bases (users et depts) +- recodage du dump sql diff --git a/config/anonymize_db.py b/config/anonymize_db.py new file mode 100755 index 00000000..dd3911ff --- /dev/null +++ b/config/anonymize_db.py @@ -0,0 +1,151 @@ +#!/opt/zope213/bin/python +# -*- coding: utf-8 -*- +# -*- mode: python -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2019 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + + +"""Anonymize une base de donnĂ©es ScoDoc + +Runned as "www-data" with scodoc and postgresql up. + +E. Viennet, Jan 2019 +""" + +import sys, os, traceback, psycopg2 + +def log(msg): + sys.stdout.flush() + sys.stderr.write(msg+'\n') + sys.stderr.flush() + + +# --- Fonctions d'Anonymisation, en SQL + +anonymize_name = "random_text_md5(8)" +anonymize_date = "'1970-01-01'" +anonymize_question_str = "'?'" +anonymize_null = "NULL" + +# aggregate_length = lambda column, _: 'length({})'.format(column) + + +# --- Champs Ă  anonymiser (cette configuration pourrait ĂŞtre placĂ© dans un fichier sĂ©parĂ© +# et le code serait alors gĂ©nĂ©rique pour tout base posgresql, aux donnĂ©es de connection +# près) +# On essaie de retirer les donnĂ©es personnelles des Ă©tudiants et des entreprises +# L'identitĂ© (login) des enseignants n'est pas modifiĂ©e +# (on ne sait rien d'autre sur eux dans cette base, et changer le login ets compliquĂ© +# car c'est la clĂ© avec SCOUSERS) +# +ANONYMIZED_FIELDS = { + 'identite.nom' : anonymize_name, + 'identite.prenom' : anonymize_name, + 'identite.date_naissance' : anonymize_date, + 'identite.lieu_naissance' : anonymize_question_str, + 'identite.nationalite' : anonymize_question_str, + 'identite.foto' : anonymize_null, + 'identite.code_nip' : anonymize_null, + 'identite.code_ine' : anonymize_null, + 'identite.nom_usuel' : anonymize_null, + + 'adresse.email' : "'ano@nyme.fr'", + 'adresse.emailperso' : anonymize_null, + 'adresse.domicile' : anonymize_null, + 'adresse.telephone' : anonymize_null, + 'adresse.telephonemobile' : anonymize_null, + 'adresse.fax' : anonymize_null, + + 'billet_absence.description' : anonymize_null, + 'etud_annotations.comment' : anonymize_name, + + 'entreprises.nom' : anonymize_name, + 'entreprises.adresse' : anonymize_null, + 'entreprises.ville' : anonymize_null, + 'entreprises.codepostal' : anonymize_null, + 'entreprises.pays' : anonymize_null, + 'entreprises.contact_origine' : anonymize_null, + 'entreprises.secteur' : anonymize_null, + 'entreprises.note' : anonymize_null, + 'entreprises.privee' : anonymize_null, + 'entreprises.localisation' : anonymize_null, + + 'entreprise_correspondant.nom' : anonymize_name, + 'entreprise_correspondant.prenom' : anonymize_name, + 'entreprise_correspondant.phone1' : anonymize_null, + 'entreprise_correspondant.phone2' : anonymize_null, + 'entreprise_correspondant.mobile' : anonymize_null, + 'entreprise_correspondant.mail1' : anonymize_null, + 'entreprise_correspondant.mail2' : anonymize_null, + 'entreprise_correspondant.note' : anonymize_null, + 'entreprise_correspondant.fax' : anonymize_null, + + 'entreprise_contact.description' : anonymize_null, + 'entreprise_contact.enseignant' : anonymize_null, + + 'notes_appreciations.comment' : anonymize_name, + } + +def anonymize_column(cursor, tablecolumn): + """Anonymise une colonne + tablecolumn est de la forme nom_de_table.nom_de_colonne, par exemple "identite.nom" + key_name est le nom de la colonne (clĂ©) Ă  utiliser pour certains remplacements + (cette clĂ© doit ĂŞtre anonyme et unique). Par exemple, un nom propre pourrait ĂŞtre + remplacĂ© par nom_valeur_de_la_clĂ©. + """ + table, column = tablecolumn.split('.') + anonymization = ANONYMIZED_FIELDS[ tablecolumn ] + log('processing {}'.format(tablecolumn)) + cursor.execute("UPDATE {table} SET {column} = {value};".format( + table=table, + column=column, + value=anonymization(column, key_name) if callable(anonymization) else anonymization + )) + +def anonymize_db(cursor): + """Traite, une Ă  une, les colonnes indiquĂ©es dans ANONYMIZED_FIELDS + """ + for tablecolumn in ANONYMIZED_FIELDS: + anonymize_column(cursor, tablecolumn) + + +dbname = sys.argv[1] + +log('\nAnonymizing database %s' % dbname) +cnx_string = 'dbname=' + dbname +try: + cnx = psycopg2.connect( cnx_string ) +except: + log("\n*** Error: can't connect to database %s ***\n" % dbname) + log('connexion string was "%s"' % cnx_string) + traceback.print_exc() + +cnx.set_session(autocommit=False) +cursor = cnx.cursor() + +anonymize_db(cursor) + +cnx.commit() +cnx.close() diff --git a/config/config.sh b/config/config.sh new file mode 100644 index 00000000..4b8368b8 --- /dev/null +++ b/config/config.sh @@ -0,0 +1,61 @@ + +# Version majeure de Debian (..., 9, 10) +debian_version=$(cat /etc/debian_version) +debian_version=${debian_version%%.*} + + +# Fix path +export PATH="${PATH}":/usr/sbin:/sbin + +# ScoDoc: environment variables +umask 0022 + +export SCODOC_DIR=${PWD%/*} +# normalement: /opt/scodoc/Products/ScoDoc +export SCODOC_VAR_DIR=$(realpath "$SCODOC_DIR/../../var/scodoc") +# = /opt/scodoc/var/scodoc +export SCODOC_VERSION_DIR="${SCODOC_VAR_DIR}/config/version" +export SCODOC_LOGOS_DIR="${SCODOC_VAR_DIR}/config/logos" +# Postgresql superuser: +export POSTGRES_SUPERUSER=postgres + +# Postgresql normal user: (by default, same a zope==www-data) +# IMPORTANT: must match SCO_DEFAULT_SQL_USER defined in sco_utils.py +export POSTGRES_USER=www-data + +# psql command: if various versions installed, force the one we want: +if [ ${debian_version} = "10" ] +then + PSQL=/usr/lib/postgresql/11/bin/psql +elif [ ${debian_version} = "9" ] +then + PSQL=/usr/lib/postgresql/9.6/bin/psql +elif [ ${debian_version} = "8" ] +then + PSQL=/usr/lib/postgresql/9.4/bin/psql +elif [ ${debian_version} = "7" ] +then + PSQL=/usr/lib/postgresql/9.1/bin/psql +elif [ ${debian_version} = "5" ] + then + PSQL=/usr/lib/postgresql/8.3/bin/psql + elif [ ${debian_version} = "6" ] + then + PSQL=/usr/lib/postgresql/8.4/bin/psql + else + PSQL=/usr/lib/postgresql/8.1/bin/psql +fi + + +# tcp port for SQL server (under Debian 4, 5432 or 5433 for 8.1 if 7.4 also installed !) +# Important note: if changed, you should probably also change it in +# sco_utils.py (SCO_DEFAULT_SQL_PORT). +export POSTGRES_PORT=5432 + +# Utilise par le script de reset du mot de passe: +if [ ${debian_version} -ge "7" ] +then + export ZOPE_VERSION=2.13 +else + export ZOPE_VERSION=2.11.0 +fi diff --git a/config/create_database.sh b/config/create_database.sh new file mode 100755 index 00000000..3485b216 --- /dev/null +++ b/config/create_database.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Create database for a ScoDoc instance +# This script must be executed as postgres user +# +# $db_name is passed ias an environment variable + +source config.sh +source utils.sh + +echo 'Creating postgresql database' + +# --- +echo 'Creating postgresql database ' $db_name +createdb -E UTF-8 -p $POSTGRES_PORT -O $POSTGRES_USER $db_name + diff --git a/config/create_dept.sh b/config/create_dept.sh new file mode 100755 index 00000000..4b39b628 --- /dev/null +++ b/config/create_dept.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# +# ScoDoc: creation initiale d'un departement +# +# Ce script prend en charge la creation de la base de donnees +# et doit ĂŞtre lancĂ© par l'utilisateur unix root dans le repertoire .../config +# ^^^^^^^^^^^^^^^^^^^^^ +# E. Viennet, Juin 2008 +# + + +source config.sh +source utils.sh + +check_uid_root $0 + + +echo -n "Nom du departement (un mot sans ponctuation, exemple \"Info\"): " +read DEPT + +if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]] +then + echo 'Nom de departement invalide !' + exit 1 +fi + +export DEPT + +export db_name=SCO$(to_upper "$DEPT") + +cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg + +if [ -e $cfg_pathname ] +then + echo 'Erreur: Il existe deja une configuration pour "'$DEPT'"' + exit 1 +fi + +# --- Ensure postgres user www-data exists +init_postgres_user + +# ----------------------- Create database +su -c ./create_database.sh $POSTGRES_SUPERUSER + +# ----------------------- Create tables +# POSTGRES_USER == regular unix user (www-data) +su -c ./initialize_database.sh $POSTGRES_USER + +# ----------------------- Enregistre fichier config +echo "dbname="$db_name > $cfg_pathname + +# ----------------------- Force mise Ă  jour +echo -n "Voulez vous mettre a jour ScoDoc (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + (cd "$SCODOC_DIR/config"; ./upgrade.sh) +fi + +# ----------------------- +echo +echo " Departement $DEPT cree" +echo +echo " Attention: la base de donnees n'a pas de copies de sauvegarde" +echo +echo " Maintenant, vous pouvez ajouter le departement via l'application web" +echo " en suivant le lien \"Administration de ScoDoc\" sur la page d'accueil." +echo diff --git a/config/create_user_db.sh b/config/create_user_db.sh new file mode 100755 index 00000000..8790cd5d --- /dev/null +++ b/config/create_user_db.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# +# ScoDoc: creation de la base de donnees d'utilisateurs +# +# Ce script prend en charge la creation de la base de donnees +# et doit ętre lancé par l'utilisateur unix root dans le repertoire .../config +# ^^^^^^^^^^^^^^^^^^^^^ +# E. Viennet, Juin 2008 +# + +source config.sh +source utils.sh + +check_uid_root $0 + +# --- Ensure postgres user www-data exists +init_postgres_user + +db_name=SCOUSERS + +echo 'Creating postgresql database ' $db_name + +su -c "createdb -E UTF-8 -O $POSTGRES_USER -p $POSTGRES_PORT $db_name" $POSTGRES_SUPERUSER + +echo 'Initializing tables in database ' $db_name +echo su -c "$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name < $SCODOC_DIR/misc/create_user_table.sql" $POSTGRES_USER +su -c "$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name < $SCODOC_DIR/misc/create_user_table.sql" $POSTGRES_USER diff --git a/config/default-etapes.txt b/config/default-etapes.txt new file mode 100644 index 00000000..80e037d1 --- /dev/null +++ b/config/default-etapes.txt @@ -0,0 +1,20 @@ +# Etapes Apogee par defaut: a adapter a votre cas +# Les etapes sont d'abord demandees au portail +# et en cas d'echec lues dans ce fichier +# +# E. Viennet, 2007 + +# Format: Departement : Code_etape_apogee : nom de la formation +# Le code departement est celui indiquĂ© par la propriĂ©tĂ© +# "portal_dept_name" du dĂ©partement. + +# Attention: le codage de ce fichier doit etre le mĂŞme que +# celui de ScoDoc (utf-8) + +rt : V1RT : DUT RĂ©seaux et TĂ©lĂ©communications 1 +rt : V2RT2 : DUT RĂ©seaux et TĂ©lĂ©communications 2 +rt : V3ASR3 : Licence Pro R&T ASUR (FC) +rt : V3ASR2 : Licence Pro R&T ASUR (Apprentissage) +rt : V3ON : Licence pro. Electronique, Optique et Nanotechnologies +gea: V1GE : DUT Gestion des Entreprises et Administration 1 +gea: V2GE : DUT Gestion des Entreprises et Administration 2 diff --git a/config/delete_dept.sh b/config/delete_dept.sh new file mode 100755 index 00000000..9a88a70c --- /dev/null +++ b/config/delete_dept.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# +# ScoDoc: suppression d'un departement +# +# Ce script supprime la base de donnees ScoDoc d'un departement +# *** le departement doit au prealable avoir ďż˝tďż˝ supprime via l'interface web ! *** +# +# Ne fonctionne que pour les configurations "standards" (dbname=xxx) +# +# Il doit ďż˝tre lancďż˝ par l'utilisateur unix root dans le repertoire .../config +# ^^^^^^^^^^^^^^^^^^^^^ +# E. Viennet, Sept 2008 +# + + +source config.sh +source utils.sh + +check_uid_root $0 + +echo +echo "Ce script supprime la base de donnees ScoDoc d'un departement" +echo +echo "Attention: le departement doit au prealable avoir ete supprime via l'interface web !" +echo "faites le AVANT d'executer ce script !!!" +echo +echo -n "Nom du departement a supprimer (un mot sans ponctuation, exemple \"Info\"): " +read DEPT + +if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]] +then + echo "Nom de departement invalide !" + exit 1 +fi + +export DEPT + +cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg + +if [ -e $cfg_pathname ] +then + # arret de ScoDoc + /etc/init.d/scodoc stop + # suppression de la base postgres + db_name=$(cat $cfg_pathname | sed '/^dbname=*/!d; s///;q') + echo "suppression de la base postgres $db_name" + su -c "dropdb $db_name" $POSTGRES_SUPERUSER || terminate "ne peux supprimer base de donnees $db_name" + # suppression du fichier de config + /bin/rm -f $cfg_pathname || terminate "ne peux supprimer $cfg_pathname" + # relance ScoDoc + echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]" + read ans + if [ "$(norm_ans "$ans")" = 'Y' ] + then + /etc/init.d/scodoc start + fi + exit 0 +else + echo 'Erreur: pas de configuration trouvee pour "'$DEPT'"' + exit 1 +fi diff --git a/config/diagnostic.sh b/config/diagnostic.sh new file mode 100755 index 00000000..fc0fb959 --- /dev/null +++ b/config/diagnostic.sh @@ -0,0 +1,256 @@ +#!/bin/bash + +# Rassemble informations sur le systeme et l'installation ScoDoc pour +# faciliter le support a distance. +# +# Avec option: +# -a : sauve aussi les bases de donnĂ©es +# +DEST_ADDRESS=emmanuel.viennet@univ-paris13.fr + +INSTANCE_DIR=/opt/scodoc + +TMP=/tmp/scodoc-$(date +%F-%s) + +DEPTS_TO_SAVE="" +SAVE_USERS=0 +SEND_BY_MAIL=1 + +# ------------------------------------- +# Arguments +# ------------------------------------- + +function join_by { local IFS="$1"; shift; echo "$*"; } + +while getopts ":d:aunh" opt; do + case $opt in + a) + # Liste des noms des departements, a partir des bases SQL SCO* + DEPTS_TO_SAVE=$( (su postgres -c "psql -l") | grep SCO | grep -v SCOUSERS | awk '{ gsub("SCO", "", $1); print $1}' ) + SAVE_USERS=1 + ;; + u) + SAVE_USERS=1 + ;; + n) + SEND_BY_MAIL=0 + ;; + d) + DEPTS_TO_SAVE=$( join_by ' ' $DEPTS_TO_SAVE $OPTARG ) + ;; + h) + echo "Diagnostic installation ScoDoc" + echo "Rassemble informations sur le systeme et l'installation ScoDoc" + echo "Usage: $0 [-h] [-n] [-a] [-u] [-d dept]" + echo " -h cette aide" + echo " -n pas d'envoi par mail" + echo " -a enregistre aussi toutes les bases de donnees" + echo " -u enregistre la base utilisateurs" + echo " -d dept enregistre la base du departement dept" + exit 0 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; + esac +done + + +# ------------------------------------- +# Configuration +# ------------------------------------- + +# needed for uuencode +if [ ! -e /usr/bin/uuencode ] +then + apt-get install sharutils +fi + +mkdir $TMP + +# Files to copy: +FILES="/etc/hosts /etc/debian_version /etc/apt /etc/apache2" + + +echo "ScoDoc diagnostic: informations about your system will be " +if [ "${SEND_BY_MAIL}" = "1" ] +then + echo "sent to ${DEST_ADDRESS}" + echo -n "and " +fi +echo "left in ${TMP}" + + +# ------------------------------------- +# Logs +# ------------------------------------- + +copy_log() { + if [ -e $1 ] + then + cp $1 $TMP/scodoc_logs/ + fi +} +mkdir $TMP/scodoc_logs/ +copy_log /opt/scodoc/instance/log/event.log +copy_log /opt/scodoc/instance/log/event.log.1 +copy_log /opt/scodoc/instance/log/notes.log +copy_log /opt/scodoc/instance/log/notes.log.1 + + +# ------------------------------------- +# Linux System Configuration +# ------------------------------------- + +iptables -L > $TMP/iptables.out +ip a > $TMP/ifconfig.out +ps auxww > $TMP/ps.out +df -h > $TMP/df.out +dpkg -l > $TMP/dpkg.lst + +(cd /opt/scodoc/instance/Products/ScoDoc; svn status > $TMP/svn.status) +(cd /opt/scodoc/instance/Products/ScoDoc; svn diff > $TMP/svn.diff) + +(cd /opt/scodoc/instance/Products/ScoDoc; svnversion > $TMP/svn.version) +ls -laR /opt/scodoc/instance/Products/ScoDoc > $TMP/ls-laR + + +# ------------------------------------- +# Databases configurations +# ------------------------------------- +(su postgres -c "psql -l") > "${TMP}/psql-l.out" +for dept in "${INSTANCE_DIR}"/var/scodoc/config/depts/*.cfg +do + cnx=$(cat $dept) + (su postgres -c "echo '\dt' | psql -d $cnx") > "${TMP}/psql-$(basename ${dept%%.*}).out" +done + + +# ------------------------------------- +# Other system configuration files +# ------------------------------------- +# copy files: +for f in $FILES +do + cp -R $f $TMP +done + + +# ------------------------------------- +# Optionally save dept(s) database(s) +# ------------------------------------- +DEPTS_TO_SAVE=$(echo ${DEPTS_TO_SAVE} | tr ' ' '\n' | sort | uniq) + +# Dump database of a dept (eg "RT") +function dump_dept_db { + dept=$1 + DB=$2 + echo "Dumping database ${DB}..." + mkdir -p "${TMP}/depts/${dept}" + chmod -R a+wr "${TMP}/depts/" + (su postgres -c "pg_dump --create ${DB}") | gzip > "${TMP}/depts/${dept}/${DB}.dump.gz" + # may add archives ? (but probably too big) +} + +for dept in ${DEPTS_TO_SAVE} +do + dump_dept_db "${dept}" "SCO${dept}" +done + + +# ------------------------------------- +# Optionally saveUSERS db +# ------------------------------------- +if [ "${SAVE_USERS}" = "1" ] +then + dump_dept_db "USERS" "SCOUSERS" +fi + + +# ------------------------------------- +# Archive all stuff to /tmp +# ------------------------------------- + +tar cfz $TMP.tgz $TMP + +echo +echo "Fichier de diagnostic: $TMP.tgz" +echo + +# If no mail, stop here +if [ "${SEND_BY_MAIL}" = "0" ] +then + exit 0 +fi + +# ------------------------------------- +# Send by e-mail +# ------------------------------------- + + +# Code below found on http://www.zedwood.com/article/103/bash-send-mail-with-an-attachment + +#requires: basename,date,md5sum,sed,sendmail,uuencode +function fappend { + echo "$2">>$1; +} +YYYYMMDD=`date +%Y%m%d` + +# CHANGE THESE +TOEMAIL=$DEST_ADDRESS +FREMAIL="scodoc-diagnostic@none.org"; +SUBJECT="ScoDoc diagnostic - $YYYYMMDD"; +MSGBODY="ScoDoc diagnostic sent by diagnostic.sh"; +ATTACHMENT="$TMP.tgz" +MIMETYPE="application/gnutar" #if not sure, use http://www.webmaster-toolkit.com/mime-types.shtml + + +# DON'T CHANGE ANYTHING BELOW +TMP="/tmp/tmpfil_123"$RANDOM; +BOUNDARY=`date +%s|md5sum` +BOUNDARY=${BOUNDARY:0:32} +FILENAME=`basename $ATTACHMENT` + +rm -rf $TMP; +cat $ATTACHMENT|uuencode --base64 $FILENAME>$TMP; +sed -i -e '1,1d' -e '$d' $TMP;#removes first & last lines from $TMP +DATA=`cat $TMP` + +rm -rf $TMP; +fappend $TMP "From: $FREMAIL"; +fappend $TMP "To: $TOEMAIL"; +fappend $TMP "Reply-To: $FREMAIL"; +fappend $TMP "Subject: $SUBJECT"; +fappend $TMP "Content-Type: multipart/mixed; boundary=\""$BOUNDARY"\""; +fappend $TMP ""; +fappend $TMP "This is a MIME formatted message. If you see this text it means that your"; +fappend $TMP "email software does not support MIME formatted messages."; +fappend $TMP ""; +fappend $TMP "--$BOUNDARY"; +fappend $TMP "Content-Type: text/plain; charset=ISO-8859-1; format=flowed"; +fappend $TMP "Content-Transfer-Encoding: 7bit"; +fappend $TMP "Content-Disposition: inline"; +fappend $TMP ""; +fappend $TMP "$MSGBODY"; +fappend $TMP ""; +fappend $TMP ""; +fappend $TMP "--$BOUNDARY"; +fappend $TMP "Content-Type: $MIMETYPE; name=\"$FILENAME\""; +fappend $TMP "Content-Transfer-Encoding: base64"; +fappend $TMP "Content-Disposition: attachment; filename=\"$FILENAME\";"; +fappend $TMP ""; +fappend $TMP "$DATA"; +fappend $TMP ""; +fappend $TMP ""; +fappend $TMP "--$BOUNDARY--"; +fappend $TMP ""; +fappend $TMP ""; +#cat $TMP>out.txt +cat $TMP|sendmail -t -f none@example.com; +rm $TMP; + diff --git a/config/distrib.sh b/config/distrib.sh new file mode 100644 index 00000000..0958fb10 --- /dev/null +++ b/config/distrib.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# Pense bete pour tout nettoyer avant de faire une distribution... +# +# +# E. Viennet, jul 2008 + +source config.sh +source utils.sh + +if [ "$UID" != "0" ] +then + echo "Erreur: le script $0 doit etre lance par root" + exit 1 +fi + + +echo "Changing to directory " $SCODOC_DIR/config +cd $SCODOC_DIR/config + +echo "Stopping ScoDoc..." +/etc/init.d/scodoc stop + +# DROITS +echo -n "Verification des droits: proprietaire www-data ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo 'changing owner to www-data' + chown -R www-data.www-data .. +fi + +echo -n 'Suppression des backups des sources (*~) ? (y/n) [y] ' +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + /bin/rm -f ../*~ ../*/*~ +fi + + +# SVN +echo -n "svn update ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo 'Updating from SVN...' + (cd ..; svn update) +fi + + +# DEPARTEMENTS (maintenant inutile car dans /var) +echo -n "Supprimer les (anciennes) configs de departements ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "moving " depts/*.cfg "to /tmp" + mv depts/*.cfg /tmp +fi + +# .../var/ +echo -n "Supprimer et recrĂ©er .../var (archives, photos, configs, ...) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "moving ../../../var/scodoc to /tmp" + mv ../../../var/scodoc /tmp + mkdir -p ../../../var/scodoc/config/depts + mkdir ../../../var/scodoc/photos + mkdir ../../../var/scodoc/tmp + mkdir ../../../var/scodoc/archives + chown -R www-data.www-data ../../../var/scodoc/ +fi + +# LOGS ZOPE +echo -n "Effacer les logs de Zope et ScoDoc ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + (cd ../../../log/; ./purge) +fi + +# IMAGE Data.fs +echo -n "Recopier le Data.fs original ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "moving Data.fs to /tmp" + mv ../../../var/Data.fs ../../../var/Data.fs.index /tmp + DATAFS=../../../var/Data.fs.ok-to-distrib-545 + echo "copying $DATAFS to Data.fs" + cp -p $DATAFS ../../../var/Data.fs +fi + +# +echo +echo "OK, vous pouvez archiver la distribution !" +echo diff --git a/config/doc_poursuites_etudes/README.txt b/config/doc_poursuites_etudes/README.txt new file mode 100644 index 00000000..6e2449c6 --- /dev/null +++ b/config/doc_poursuites_etudes/README.txt @@ -0,0 +1,16 @@ +Documents pour l'Ă©dition des avis de poursuite d'Ă©tude + +Ces documents sont joints au zip renvoyĂ© par la fonction de synthèse des avis de poursuite d'Ă©tude: + +- images (logos) +- classe LaTeX +- modele pour frise chronologique en LaTeX + + +Tous les documents et rĂ©pertoires placĂ©s dans le rĂ©pertoire distrib/ ou local/ seront ajoutĂ©s. +Si un document du mĂŞme nom est prĂ©sent dans le rĂ©pertoire local/, c'est lui qui sera +utilisĂ© Ă  la place. +Cela permet des adaptations locales, sans gĂ©nĂ©rer de conflits lors des mises Ă  jour de ScoDoc +(pensez Ă  sauvegarder votre rĂ©pertoire local/) + + diff --git a/config/doc_poursuites_etudes/distrib/README.txt b/config/doc_poursuites_etudes/distrib/README.txt new file mode 100644 index 00000000..ea19fd3d --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/README.txt @@ -0,0 +1,11 @@ + + Avis de poursuites d'Ă©tude gĂ©nĂ©rĂ©s par ScoDoc + + +Pour compiler et gĂ©nĂ©rer le document pdf (avec les avis de tous les Ă©tudiants): + + pdflatex avis.txt + + + +(si vous souhaitez modifier les fichiers sur le serveur, faites le dans le rĂ©pertoire ../local/) diff --git a/config/doc_poursuites_etudes/distrib/avis.tex b/config/doc_poursuites_etudes/distrib/avis.tex new file mode 100755 index 00000000..524c590b --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/avis.tex @@ -0,0 +1,29 @@ +%% Original : ClĂ©o BARAS +%% +%% Version 1.0 +%% +%% Ce fichier est distribuĂ© par ScoDoc +%% +%% Si vous voulez le modifier sur le serveur, copiez le dans +%% ../local/ +%% et Ă©ditez votre copie dans local. +%% + + +\documentclass[12pt]{avisPE} + +\usepackage[T1]{fontenc} +\usepackage[utf8x]{inputenc} +\usepackage{frcursive} +\usepackage{graphics,graphicx} +\usepackage{tikz} +\usepackage{colortbl} +\usepackage[francais]{babel} % Date francisĂ©e + + +\begin{document} +\input{modeles/parcourstimeline.tex} + +\input{avis_poursuite} + +\end{document} diff --git a/config/doc_poursuites_etudes/distrib/avisPE.cls b/config/doc_poursuites_etudes/distrib/avisPE.cls new file mode 100755 index 00000000..eacc1b8d --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/avisPE.cls @@ -0,0 +1,126 @@ +%% Original : ClĂ©o BARAS +%% +%% Version 1.0 +%% +%% Ce fichier est distribuĂ© par ScoDoc +%% +%% Si vous voulez le modifier sur le serveur, copiez le dans +%% ../local/ +%% et Ă©ditez votre copie dans local. +%% + +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{avisPE} + [2017/05/06 v0.1 Modele avis PE] + +\LoadClass[12pt]{article} +\RequirePackage{ifthen} +\RequirePackage{array} +\RequirePackage[paper=a4paper,textwidth=190mm]{geometry} +\RequirePackage{bookman} +\RequirePackage{xcolor} +\RequirePackage{fontawesome} + + %%%%%%%%%%%%%%%%%%%%%%%%%%% + % Mise en page par dĂ©faut % + %%%%%%%%%%%%%%%%%%%%%%%%%%% + +\setlength{\textwidth}{190mm} %% Largeur de la zone texte +\setlength{\textheight}{300mm} %% Hauteur de la zone texte +\setlength{\topmargin}{-25mm} +\setlength{\evensidemargin}{-15mm} +\setlength{\oddsidemargin}{-15mm} + +\definecolor{ugared}{rgb}{0.94,0.02,0.02} +\definecolor{ugadarkgray}{rgb}{0.72,0.72,0.72} +\definecolor{ugagray}{rgb}{0.42,0.42,0.42} +\definecolor{ugablack}{rgb}{0,0,0} + +\pagestyle{empty} % pour ne pas indiquer de numĂ©ro de page... + +% On dĂ©finit les pieds de page comme des \stretch de force 1, +% soit 4 plus fort que celui qui est en bas du chapeau +% (Cf. ci-dessous). De cette façon, un avis qui ne remplit pas toute la +% page a un bel espacement. +\makeatletter +\def\@textbottom{\vspace*{\stretch{1}}} +\makeatother + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Paramètres rĂ©glables % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% L'utilisateur peut les redĂ©finir ceux-ci «\renewcommand» si besoin : +\newcommand{\espaceInterRubrique}{\medskip\vspace{\stretch{0.1}}} + +\newlength{\rubriquedureeparindent} +\setlength{\rubriquedureeparindent}{-16pt} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Macros pratiques % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\er{$^{\hbox{er}}$} +\def\ere{$^{\hbox{ère}}$} +\def\eme{$^{\hbox{ème}}$} + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % L'environnement "rubrique" % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Usage : \begin{rubrique}[Indentation]{Titre} [...] \end{rubrique} +% On met le texte qu'on veut Ă  l'intĂ©rieur. + +\newenvironment{nom} % "rubrique" prend un seul argument, le titre +{ +\relax% +\noindent +\medskip\bfseries \LARGE}{% +\normalsize\normalfont +\espaceInterRubrique +} + +\newenvironment{rubrique}[1] % "rubrique" prend un seul argument, le titre +{ +\relax% +\noindent +\tikz{\fill[ugagray] (0,0) rectangle (0.5cm,0.5cm); } +%\faArrowCircleRight +\medskip\hspace{0.2cm}\bfseries \color{ugagray} \Large{#1}\par +\noindent\tikz{\fill[white] (0,0) rectangle (0.5cm,0.5cm); +\draw[ugagray] (0.8cm,1cm)--(19cm,1cm); }\par + \color{ugablack}\normalsize\normalfont}{% le contenu +\espaceInterRubrique +} + +\newenvironment{sousrubrique}[1] % "rubrique" prend un seul argument, le titre +{\espaceInterRubrique\relax% + \color{ugagray}{\faCaretSquareORight~\textbf{\large{#1}}} + \color{ugablack}\normalsize\normalfont\par% + \espaceInterRubrique + }{} + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % L'en-tĂŞte de l'avis % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% en paramètre: le logo +\newenvironment{entete}[1]{% + \def\ligne{\rule[2pt]{24pt}{1pt}} % Ligne sĂ©paratrice + \noindent + \begin{minipage}{\linewidth}%\hspace{-2cm}% + \begin{minipage}{5.5cm} + \includegraphics[height=3.5cm,keepaspectratio=true]{#1} + \end{minipage} + \hfill + \begin{minipage}{13cm} + \color{ugagray} +} +{ \color{ugablack} +\relax\vspace*{\fill} +\end{minipage} +\end{minipage} +\vspace{\stretch{0.25}}} diff --git a/config/doc_poursuites_etudes/distrib/make_avis.bat b/config/doc_poursuites_etudes/distrib/make_avis.bat new file mode 100755 index 00000000..ceb8ac31 --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/make_avis.bat @@ -0,0 +1,3 @@ + +echo "Compilation des avis latex" +pdflatex avis.tex diff --git a/config/doc_poursuites_etudes/distrib/modeles/parcourstimeline.tex b/config/doc_poursuites_etudes/distrib/modeles/parcourstimeline.tex new file mode 100755 index 00000000..fec03f65 --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/modeles/parcourstimeline.tex @@ -0,0 +1,67 @@ +%% Original : ClĂ©o BARAS +%% +%% Version 1.0 +%% +%% Ce fichier est distribuĂ© par ScoDoc +%% +%% Si vous voulez le modifier sur le serveur, copiez le dans +%% ../local/ +%% et Ă©ditez votre copie dans local. +%% + +\definecolor{ugagray}{rgb}{0.42,0.42,0.42} + +% define global counters +\newcounter{yearcount} + + +\newcounter{leftcount} + +% env parcourstimeline (d'après copyright (c) 2016 Jan KĂĽster) +% param 1: annĂ©e de dĂ©but +% param 2: annĂ©e de fin +% param 3: nombre de semestre +% param 4: largeur totale +\newenvironment{parcourstimeline}[4]{ + + % param 1: no + % param 2: Nom + \newcommand{\parcoursevent}[3] { + \pgfmathparse{\largeur*(##1-1)+\largeur/2+ \marge}\let\startexp\pgfmathresult + + \node[draw,color=white,] (Ev##1) at (\startexp,0.7){ + \textcolor{black}{##2} + }; + \node[draw,color=white,] (Ev##1) at (\startexp,-0.6){ + \textcolor{black}{##3} + }; +} + + %------------------------------------------------------------ + % BEGIN + %------------------------------------------------------------ + + \begin{tikzpicture} + + %calc pas = number of years + \pgfmathparse{(#4/#3)}\let\pas\pgfmathresult + \pgfmathparse{\pas*0.9}\let\largeur\pgfmathresult + \pgfmathparse{(#4-\largeur*#3)/2}\let\marge\pgfmathresult + \draw[draw=ugagray,line width=2pt] (0,0) -- (#4,0) ; %the timeline + + %for each year put a horizontal line in place + \setcounter{yearcount}{1} + \whiledo{\value{yearcount} < #3}{ % \fullrange}{ + \draw[fill=white,draw=ugagray, line width=2pt] (\largeur*\value{yearcount}+\marge+0.,0) circle (0.1); + \stepcounter{yearcount} + } + + %start year + \node[draw,color=ugagray,rectangle,line width=2pt,fill=ugagray!20,rounded corners=3pt] (Yd) at(0,0){\textcolor{black}{\textbf{\small#1}}}; + + %end year + \node[draw,color=ugagray,rectangle,line width=2pt,fill=ugagray!20,rounded corners=3pt] (Yd) at(#4,0){\textcolor{black}{\textbf{\small#2}}}; + + +}%end begin part of newenv +{\end{tikzpicture}} \ No newline at end of file diff --git a/config/doc_poursuites_etudes/distrib/modeles/un_avis.tex b/config/doc_poursuites_etudes/distrib/modeles/un_avis.tex new file mode 100644 index 00000000..05a6320f --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/modeles/un_avis.tex @@ -0,0 +1,164 @@ +%% Original : ClĂ©o BARAS +%% +%% Template LaTeX des avis +%% +%% Version 1.0 +%% +%% Ce fichier est distribuĂ© par ScoDoc +%% +%% Si vous voulez le modifier sur le serveur, copiez le dans +%% ../local/ +%% et Ă©ditez votre copie dans local. +%% + + +% Ce document est n'est pas utilisĂ© si le paramètre eTemplate LaTeX des avis" est rempli. + +% ************************************************************ +% En-tĂŞte de l'avis +% ************************************************************ +\begin{entete}{logos/logo_header} + \textbf{\Huge{Avis de Poursuites d'Etudes}} \\ + \ligne \\ + \normalsize{DĂ©partement **DeptFullName**} \\ + \normalsize{**InstituteName**} \\ +\end{entete} + +\begin{nom} +**sexe** **prenom** **nom** (**age** ans) +\end{nom} + +% ************************************************************ +% Parcours scolaire de l'Ă©tudiant +% ************************************************************ + +\begin{rubrique}{Parcours scolaire} + +% TIMELINE +**parcourstimeline** + +{ +\scriptsize +\textbf{Guide de lecture} : +$\bullet$ Les options de la formation: FI = initiale, UFA = alternance (contrat pro), ENEPS = École nationale de l'enseignement professionnel supĂ©rieur +} +\end{rubrique} + +% ************************************************************ +% Bilan scolaire +% ************************************************************ + +\begin{rubrique}{Bilan scolaire synthĂ©tique} + + +\begin{tabular}{|p{0.08\textwidth}|p{0.11\textwidth}|*{8}{p{0.07\textwidth}|}} +% Titres des colonnes +\hline & \textbf{Resultats} & \multicolumn{4}{c|}{\textbf{Dans le groupe}\textdagger} + & \multicolumn{4}{c|}{\textbf{Dans la promo}\textdagger} \\ + & & Class. & Min & Moy & Max + & Class. & Min & Moy & Max \\ +\hline +\hline S1 & **S1:groupe:dut:note** & **S1:groupe:dut:rang** + & {\scriptsize **S1:groupe:dut:min**} + & {\scriptsize **S1:groupe:dut:moy**} + & {\scriptsize **S1:groupe:dut:max**} + & **S1:promo:dut:rang** + & {\scriptsize **S1:promo:dut:min**} + & {\scriptsize **S1:promo:dut:moy**} + & {\scriptsize **S1:promo:dut:max**} \\ +\hline S2 & **S2:groupe:dut:note** & **S2:groupe:dut:rang** + & {\scriptsize **S2:groupe:dut:min**} + & {\scriptsize **S2:groupe:dut:moy**} + & {\scriptsize **S2:groupe:dut:max**} + & **S2:promo:dut:rang** + & {\scriptsize **S2:promo:dut:min**} + & {\scriptsize **S2:promo:dut:moy**} + & {\scriptsize **S2:promo:dut:max**} \\ +\hline +\hline \cellcolor{black!20} 1A & **1A:groupe:dut:note** & **1A:groupe:dut:rang** + & {\scriptsize **1A:groupe:dut:min**} + & {\scriptsize **1A:groupe:dut:moy**} + & {\scriptsize **1A:groupe:dut:max**} + & **1A:promo:dut:rang** + & {\scriptsize **1A:promo:dut:min**} + & {\scriptsize **1A:promo:dut:moy**} + & {\scriptsize **1A:promo:dut:max**} \\ +\hline +\hline S3 & **S3:groupe:dut:note** & **S3:groupe:dut:rang** + & {\scriptsize **S3:groupe:dut:min**} + & {\scriptsize **S3:groupe:dut:moy**} + & {\scriptsize **S3:groupe:dut:max**} + & **S3:promo:dut:rang** + & {\scriptsize **S3:promo:dut:min**} + & {\scriptsize **S3:promo:dut:moy**} + & {\scriptsize **S3:promo:dut:max**} \\ +\hline S4* & **S4:groupe:dut:note** & **S4:groupe:dut:rang** + & {\scriptsize **S4:groupe:dut:min**} + & {\scriptsize **S4:groupe:dut:moy**} + & {\scriptsize **S4:groupe:dut:max**} + & **S4:promo:dut:rang** + & {\scriptsize **S4:promo:dut:min**} + & {\scriptsize **S4:promo:dut:moy**} + & {\scriptsize **S4:promo:dut:max**} \\ +\hline +\hline \cellcolor{black!20} 2A* & **2A:groupe:dut:note** & **2A:groupe:dut:rang** + & {\scriptsize **2A:groupe:dut:min**} + & {\scriptsize **2A:groupe:dut:moy**} + & {\scriptsize **2A:groupe:dut:max**} + & **2A:promo:dut:rang** + & {\scriptsize **2A:promo:dut:min**} + & {\scriptsize **2A:promo:dut:moy**} + & {\scriptsize **2A:promo:dut:max**} \\ +\hline +\hline DUT* & **4S:groupe:dut:note** & **4S:groupe:dut:rang** + & {\scriptsize **4S:groupe:dut:min**} + & {\scriptsize **4S:groupe:dut:moy**} + & {\scriptsize **4S:groupe:dut:max**} + & **4S:promo:dut:rang** + & {\scriptsize **4S:promo:dut:min**} + & {\scriptsize **4S:promo:dut:moy**} + & {\scriptsize **4S:promo:dut:max**} \\ +\hline +\end{tabular} + +\begin{minipage}{\textwidth} +\scriptsize +\textbf{Guide de lecture} : * Sur notes disponibles ; \textdagger~Le groupe est l'ensemble des Ă©tudiants ayant suivi le mĂŞme semestre que **prenom** **nom** alors que la promo est l'ensemble des Ă©tudiants recevant le diplĂ´me de DUT Ă  la mĂŞme date que **prenom** **nom** (ayant donc des parcours potentiellement diffĂ©rents) +\end{minipage} + +\end{rubrique} + +% ************************************************************ +% Avis de poursuite d'Ă©tudes manuscrit ou saisi dans les +% annotations de l'Ă©tudiant +% ************************************************************ + +\begin{rubrique}{Avis du jury de Poursuites d'Etudes} +**annotation** +\end{rubrique} + +\newpage % Saut de page +% ------------------------------------------------------------ + +% ************************************************************ +% Bilan automatisĂ© par matières +% ************************************************************ + +\begin{rubrique}{Bilan par matières} +**bilanParTag** +\end{rubrique} + +% ************************************************************ +% Signature +% ************************************************************ + +\begin{rubrique}{Cachet de l'Ă©tablissement} +\begin{flushright} +Le~\today,\\ +**NomResponsablePE**~\\ +\textit{Responsable des Poursuites d'Études du dĂ©partement **DeptName**} +\end{flushright} +\vspace{0.5cm} +\end{rubrique} + +% Le pied de page (indiquĂ© dans les paramètres ScoDoc) vient après cette ligne diff --git a/config/doc_poursuites_etudes/distrib/un_footer.tex b/config/doc_poursuites_etudes/distrib/un_footer.tex new file mode 100644 index 00000000..ba4ff162 --- /dev/null +++ b/config/doc_poursuites_etudes/distrib/un_footer.tex @@ -0,0 +1,7 @@ +\begin{minipage}{\textwidth} \scriptsize +%IUT exemple - Dpt RT, 3 rue xxx, France + +% \textbf{SecrĂ©tariat Poursuite d'Etudes}: Xxx Yyy, Tel 01.02.03.04.05, xxx.yyy@example.com + +% \textbf{Responsables Poursuite d'Etudes RT}: Zzz Wwww, Tel 01.02.03.04.05, zzz.www@example.com +\end{minipage} \ No newline at end of file diff --git a/config/etablissements.csv b/config/etablissements.csv new file mode 100644 index 00000000..8cd2ceaf --- /dev/null +++ b/config/etablissements.csv @@ -0,0 +1,5317 @@ +COD_ETB;LIB_OFF_ETB;LIB_AD1_ETB;COD_POS_ADR_ETB;LIB_COM;LAT;LNG +0400786M;Afasec Ecole Des Courses Ctre Des Lads Et Jockeys;Hippodrome 164 Rue Geroges Pela;40000;Mont De Marsan;43.893485;-0.499782 +0753239N;Agence Nat Valorisation Recher;43 Rue De Caumartin;75009;Paris 09;48.8727925;2.3281692 +0290324V;Agrotech Formation;Le Cleusmeur;29260;Lesneven;48.5782643;-4.3214093 +0410629L;Annexe Du Legta De Vendome LycĂ©e Horticole De Blois;5 7 R Des Grands Champs;41029;Blois;47.5660138;1.3272299 +0910831J;Annexe Ecole Agricole PrivĂ©e St Nicolas;10 Avenue Division Leclerc;91430;Igny;48.7405146;2.229604 +0602005D;Annexe LycĂ©e Agricole;40 Rue Villebois Mareuil;60000;Beauvais;49.4368684;2.0892144 +0593462T;Annexe LycĂ©e G. Et T. Agricole Legta Du Nord;52 Rue Jean-baptiste Lebas;59177;Sains Du Nord;50.0905687;4.0044665 +0595540B;Annexe LycĂ©e G. Et T. Agricole Legta Du Nord;17 Rue Des Tilleuls;59530;Le Quesnoy;50.245023;3.6509335 +0422266K;Ant Du Lap E Gautier De Nandax;219 Route De Fleury;42190;Charlieu;46.1618988;4.1710338 +0421168S;Ant Du Legta De Precieux;Le Creux Du Balay;42660;St Genest Malifaux;45.3482709;4.431769 +0421858S;Antenne Du Legta Roanne Cherve;37 Rue De La Republique;42440;Noiretable;45.8193741;3.764883 +0011056T;Antenne Du Lppr De L'ain;160 Rue De La Poype;01330;Villars Les Dombes;46.0035471;5.0313797 +0772151V;Ass Les Sinoplies Ctre De Formation;12 Rue Joseph Bodin Boismortier;77680;Roissy En Brie;48.8016537;2.6384353 +0410980T;C F P A;Domaine Des Frileuses;41150;Chaumont Sur Loire;47.480012;1.182876 +0133440U;C.n.r.s.;31 Chemin Joseph Aiguier;13009;Marseille 09;43.25633;5.4049604 +0130231F;Centre D Enseignement Et De Recherche De L Ensam;2 Cours Des Arts Et Metiers;13617;Aix En Provence;43.5302287;5.454456 +0490067G;Centre D Enseignement Et De Recherche De L Ensam D Angers;2 Bd Du Ronceray;49035;Angers;47.4751627;-0.5593541 +0761366X;Centre De Formation Le Havre Formation Entreprise;10 R Mal Delattre De Tassigny;76600;Le Havre;49.4914767;0.1139103 +0831023L;Centre De Formation Technique Drh De La Deleg.gener.armement;Rte Tour Royale Le Mourillon;83000;Toulon;43.1200697;5.9383291 +0831024M;Centre De Formation Technique Drh De La Deleg.gener.armement;Rte Tour Royale Le Mourillon;83000;Toulon;43.1200697;5.9383291 +0312020C;Centre D'etudes Superieures Industrielles Midi-pyrenees;Rue Magellan Innopole;31315;Labege;43.530582;1.529754 +0291758D;Centre D'instruction Naval Ecole De Maistrance;;29240;Brest;48.4472219;-4.421667 +0332373A;Centre Enseignement Medoc Hauteville;1 Impasse Lamire Hauteville;33250;Pauillac;45.1985079;-0.753281 +0831521C;Centre Etud Sup Techn Indust;Pl G.pompidou Qua Mayol;83000;Toulon;43.1200697;5.9383291 +0451467A;Centre Nat Rech Scientifique;3e Av Recherche Scientifique;45071;Orleans;47.8396941;1.9431002 +0451502N;Centre Nat Rech Scientifique;3e Av Recherche Scientifique;45071;Orleans;47.8396941;1.9431002 +0341860M;Centre National De La Recherche Scientif;1919 Route De Mende;34093;Montpellier;43.6388093;3.8653614 +0383049H;Centre National De La Recherche Scientif;;38042;Grenoble;45.195057;5.732322 +0753639Y;Centre National De La Recherche Scientif;15 Quai Anatole France;75007;Paris 07;48.86134;2.3230543 +0251189G;Centre National De Recherche;32 Avenue De L Observatoire;25030;Besancon;47.2478005;5.9896566 +0802092P;Centre Regional De Formation Aux Metiers Du Football;Rue Du Chapitre;80016;Amiens;49.8922653;2.2592521 +0171423E;Centre Sup Technique PrivĂ© Dietetique Esthetique Optique;5 Ter Avenue Kennedy;17044;La Rochelle;46.1680031;-1.1851484 +0332984P;Cesi Aquitaine;60 Rue De Maurian;33290;Blanquefort;44.9197812;-0.630999 +0542260N;Cesi Nancy;2 Bis Rue De La Credence;54600;Villers Les Nancy;48.6552886;6.1174951 +0622384E;Cesi Nord;7 Rue Diderot;62000;Arras;50.2971954;2.7335494 +0922455U;Cesi Paris;116 Avenue Aristide Briand;92220;Bagneux;48.7985634;2.322656 +0691696U;Cesi Rhone Alpes Auvergne;19 Avenue Guy De Collongue;69131;Dardilly;45.7832503;4.7658188 +0762969P;Cesi Rouen;Parc Vatine 1 Rue Marconi;76130;Mont St Aignan;49.459778;1.079798 +0442292C;Cesi St Nazaire;Boulevard De L Universite;44603;St Nazaire;47.253238;-2.264146 +0341564R;Cnearc- Eitarc Montpellier;1101 Avenue Agropolis;34033;Montpellier;43.6477471;3.868611 +0352342F;Cnrs;74e Rue De Paris Ctre Oberthur;35000;Rennes;48.1173421;-1.7075198 +0332865K;Cnrs Et Organisme Associe;Esplanade Des Arts Metiers;33405;Talence;44.8052072;-0.6012446 +0753480A;College De France;11 Pl Marcelin Berthelot;75231;Paris 05;48.8493963;2.3447743 +9840018X;College PrivĂ© College Pomare Iv;Papeete;98714;Papeete;-17.535022;-149.569594 +0672866B;Compagnie De Formation Pigier;15 Rue Des Magasins;67000;Strasbourg;48.5900534;7.7400921 +0753471R;Conservatoire National Des Arts Et MĂ©tiers (paris);292,rue Saint Martin;75141;Paris 03;48.8667317;2.3542533 +0312733C;Cours Assistance Toulouse;269 Route De Narbonne;31400;Toulouse;43.5536414;1.4680912 +0922297X;Cours Complementaire PrivĂ© Assurmath;44 Avenue Maurice Labrousse;92160;Antony;48.7555554;2.2959254 +0831447X;Cours PrivĂ© Cours Sigma;Chemin De La Buge;83110;Sanary Sur Mer;43.1207576;5.8079614 +0211551X;Cours PrivĂ© Saint Dominique;Rue De Velard;21320;Pouilly En Auxois;47.266633;4.5539379 +0312150U;Cours PrivĂ© Voltaire;57 Bd Armand Duportal;31000;Toulouse;43.6095732;1.4347543 +0411021M;Cours Promotion Sociale Coiffure;5 Rue De La Picardiere;41000;Blois;47.5884358;1.2950781 +0541559B;Ctr.form.prof.agricole Jeunes;3 Rue Drouas;54200;Toul;48.6725691;5.8924462 +0291759E;Ctre De Formation De Brest;Porte De Carpon;29240;Brest;48.390394;-4.486076 +0311230U;Ctre D'ecologie;29 Rue J Marvig;31055;Toulouse;43.5779576;1.4639799 +0930604B;Ctre Etud Sup Tech Industriel;3 Rue Fernand Hainaut;93407;St Ouen;48.9192718;2.3327857 +0751700R;Ctre Nat Exploitation Oceans;39 Avenue D Iena;75116;Paris 16;48.8687074;2.2955003 +0312339Z;Ctre Nat Rech Scientifique;16 Avenue Edouard Belin;31054;Toulouse;43.5631811;1.480326 +0631814E;Ctre Nat.rech.scientifique;34 Avenue Carnot;63000;Clermont Ferrand;45.7762666;3.0916043 +0110942P;Ctre Polyvalent Formation Prof;La Rouatiere;11400;Souilhanels;43.355982;1.902977 +0941958T;Deleg Regionale;27 Rue Paul Bert;94200;Ivry Sur Seine;48.8181225;2.3709464 +0941957S;Deleg Regionale Cnrs;Tour Europa 126 Belle Epine;94320;Thiais;48.760344;2.387405 +0672674T;Delegation Regionale Cnrs;23 Rue Du Loess;67037;Strasbourg;48.6042226;7.7115393 +0542276F;Delegation Regionale Du Cnrs;39 Rue Gambetta;54000;Nancy;48.691842;6.1794993 +0595987M;Delegation Regionale Du Cnrs;2 Rue Des Cannonniers;59046;Lille;50.6401336;3.0691392 +0411013D;E T I C Ecole Des Tech Communication;2 Rue Jehan De Saveuse;41000;Blois;47.5882653;1.3381421 +0611020C;E.s.a.t.;;61210;Giel Courteilles;48.7645229;-0.196656 +0951623Y;Ec Inter Sc Trait Information;Avenue Du Parc;95011;Cergy;49.0337852;2.072182 +0180910S;Ec Nat Sup Ingenieurs Bourges;10 Boulevard Lahitolle;18020;Bourges;47.0830802;2.417787 +0330203S;Ec Nle Ing Des Travaux Agricol;1 Cours Du Gl De Gaulle;33175;Gradignan;44.774267;-0.618945 +0641923T;Ec Sup Des Techno Indust Avanc;;64102;Bayonne;43.493181;-1.4749344 +0801911T;Ec Sup Ing Electr Et Electrote;14 Quai De La Somme;80083;Amiens;49.8996519;2.2914433 +0142182W;Ec Sup Ing Travaux Constr Caen;1 Rue Pierre Et Marie Curie;14610;Epron;49.2154076;-0.3662197 +0590353N;Ec Sup Tech Indust Textiles;52 Allee Lakanal;59654;Villeneuve D Ascq;50.6409021;3.1377931 +0332880B;Ec Tech Pr Esthetique Cosmet. Cecile Briat;21 Cours Marechal Foch;33000;Bordeaux;44.8470956;-0.575386 +0060693W;Ec.secondaire Et Techn.privee Cours Cannois-guy Furet;134 Boulevard De La Republique;06400;Cannes;43.5631735;7.0210192 +0782128P;Ec.secondaire Et Techn.privee Cours Du Prieure;27 Rue Diderot;78100;St Germain En Laye;48.8923567;2.0910716 +0730768N;Ec.secondaire Et Techn.privee D'altitude Le Val D'arly;Le Val D'arly;73590;St Nicolas La Chapelle;45.808921;6.501631 +0781947T;Ec.secondaire Et Techn.privee Foot;Domaine De Montjoye;78120;Clairefontaine En Yvelines;48.61237;1.908489 +0920925F;Ec.secondaire Et Techn.privee Marymount School;72 Boulevard De La Saussaye;92200;Neuilly Sur Seine;48.8928396;2.2700496 +0941954N;Ec.sup.ing.trav.const.;28 Ave Du President Wilson;94234;Cachan;48.7934331;2.3305073 +0332911K;Ec.tech.pr.coiffure Ec.tech.pr.a.berge;8 Place Tartas;33000;Bordeaux;44.8408689;-0.590832 +0952044F;Ecole 2d Degre General PrivĂ©e;9 Bis Rue Saint Flaive;95120;Ermont;48.9881778;2.2598042 +0061991G;Ecole 2d Degre General PrivĂ©e Alliance/apeda;22 Rue Michelet;06100;Nice;43.714606;7.2590664 +0752180M;Ecole 2d Degre General PrivĂ©e Beausejour;23 Boulevard Beausejour;75016;Paris 16;48.8574466;2.2702555 +0952134D;Ecole 2d Degre General PrivĂ©e Beith Yaacov;14 Avenue Charles Peguy;95200;Sarcelles;48.9811624;2.3754349 +0694066V;Ecole 2d Degre General PrivĂ©e Beth Hanna;293 Rue Francis De Pressense;69100;Villeurbanne;45.7681543;4.8969586 +0952126V;Ecole 2d Degre General PrivĂ©e Beth Israel;9 Ruelle Des Jardins;95360;Montmagny;48.9769243;2.3448886 +0132965C;Ecole 2d Degre General PrivĂ©e Beth-myriam;60 Che Vallon De Toulouse;13010;Marseille 10;43.2664233;5.4263163 +0881749Y;Ecole 2d Degre General PrivĂ©e Bienheureux Frassati;50 Rue Monseigneur Rodhain;88800;Mandres Sur Vair;48.2234792;5.891083 +0754083F;Ecole 2d Degre General PrivĂ©e Carnot;98 Rue Pierre Demours;75017;Paris 17;48.8835651;2.2995698 +9720871P;Ecole 2d Degre General PrivĂ©e Centre Saint Raphael;Quartier Bel Air;97233;Schoelcher;0.0;0.0 +0753829E;Ecole 2d Degre General PrivĂ©e Claude Bernard;34 Rue De La Clef;75005;Paris 05;48.8416993;2.3526449 +0754672W;Ecole 2d Degre General PrivĂ©e Colbert;5 Rue De La Sante;75013;Paris 13;48.8378098;2.342051 +0940872M;Ecole 2d Degre General PrivĂ©e Cours Henri Iv;6 Avenue Du Reservoir;94100;St Maur Des Fosses;48.8081416;2.4967768 +0391047B;Ecole 2d Degre General PrivĂ©e Cours N.d. De L'annonciation;Chateau;39270;Cressia;46.5223129;5.483452 +0690557F;Ecole 2d Degre General PrivĂ©e Cours Pascal;21 Rue Longue;69001;Lyon 01;45.7654048;4.8337143 +0754971W;Ecole 2d Degre General PrivĂ©e Cours PrivĂ© Progress;41 Rue De Turenne;75003;Paris 03;48.8571717;2.364311 +0342019K;Ecole 2d Degre General PrivĂ©e Ec Pr Bilingue Internationale;Le Chateau Domaine De Massane;34670;Baillargues;43.650414;4.003782 +0753857K;Ecole 2d Degre General PrivĂ©e Ecole Du Champ De Mars;87 Avenue De La Bourdonnais;75007;Paris 07;48.8559885;2.3026302 +0133785U;Ecole 2d Degre General PrivĂ©e Fides;6 Cours Sexctius;13100;Aix En Provence;43.5275763;5.4433404 +0753859M;Ecole 2d Degre General PrivĂ©e Fides;10 Avenue De La Bourdonnais;75007;Paris 07;48.8600406;2.2962319 +0492344G;Ecole 2d Degre General PrivĂ©e Foyer Saint Thomas;Couvent Haye Aux Bonshommes;49240;Avrille;47.505335;-0.590629 +0651036Y;Ecole 2d Degre General PrivĂ©e Gambetta;2 Rue De L'harmonie;65000;Tarbes;43.232008;0.0723895 +0755193M;Ecole 2d Degre General PrivĂ©e Gaston Tenoudji;2 Rue Emile Borel;75017;Paris 17;48.8988905;2.3223466 +0752456M;Ecole 2d Degre General PrivĂ©e Geoffroy Saint-hilaire;24 Rue Charcot;75013;Paris 13;48.8308116;2.3712295 +0753870Z;Ecole 2d Degre General PrivĂ©e Hattemer;52 Rue De Londres;75008;Paris 08;48.8784036;2.3254588 +0430967T;Ecole 2d Degre General PrivĂ©e Institut Alcuin;;43270;Ceaux D Allegre;45.181719;3.746936 +0611249B;Ecole 2d Degre General PrivĂ©e Institut Croix Des Vents;55 Rue D Argentre;61500;Sees;48.6058338;0.1799387 +0940873N;Ecole 2d Degre General PrivĂ©e Institut Francais De Grignon;19 Rue Jean-francois Marmontel;94320;Thiais;48.7530756;2.3962989 +0490950S;Ecole 2d Degre General PrivĂ©e Institution Bois Robert;;49370;Becon Les Granits;47.503413;-0.80139 +0752803P;Ecole 2d Degre General PrivĂ©e International School Of Paris;6 Rue Beethoven;75016;Paris 16;48.8584557;2.2873279 +0062013F;Ecole 2d Degre General PrivĂ©e International School Of Sophia;60 Rue Du Vallon;06560;Valbonne;43.6263899;7.0532296 +0753377N;Ecole 2d Degre General PrivĂ©e Ipecom Prepa Sciences;8 Rue Benjamin Godard;75016;Paris 16;48.8664881;2.275755 +0755278E;Ecole 2d Degre General PrivĂ©e Jcours Cours PrivĂ©s;6 Rue Mayran;75009;Paris 09;48.8772948;2.3454524 +0312170R;Ecole 2d Degre General PrivĂ©e Jean-jacques Rousseau;22 Rue Ingres;31000;Toulouse;43.614223;1.4426803 +0940890G;Ecole 2d Degre General PrivĂ©e Jeanne D'albret;3 Place Moreau David;94120;Fontenay Sous Bois;48.8431774;2.4652823 +0312527D;Ecole 2d Degre General PrivĂ©e La Clairiere;8 Impasse Marestan;31100;Toulouse;43.5649875;1.4068322 +0320682T;Ecole 2d Degre General PrivĂ©e Le Carouet;;32230;Monlezun;43.500014;0.213362 +0753911U;Ecole 2d Degre General PrivĂ©e L'ecole;24 R Ingenieur Robert Keller;75015;Paris 15;48.8472783;2.2838185 +0750026W;Ecole 2d Degre General PrivĂ©e L'enseign Par Petits Groupes;17 Avenue Felix Faure;75015;Paris 15;48.8421981;2.2908869 +0672863Y;Ecole 2d Degre General PrivĂ©e Michael;2c Rue Du Schnokeloch;67200;Strasbourg;48.5784653;7.7138219 +0753895B;Ecole 2d Degre General PrivĂ©e Moliere;2 Boulevard Soult;75012;Paris 12;48.8360654;2.4076243 +0753845X;Ecole 2d Degre General PrivĂ©e Montaigne;1 Rue Dupin;75006;Paris 06;48.8502773;2.324821 +0693316E;Ecole 2d Degre General PrivĂ©e Multilingue Ombrosa;95 Quai Clemenceau;69300;Caluire Et Cuire;45.807247;4.8438576 +0753060U;Ecole 2d Degre General PrivĂ©e Ohr Joseph;29 Rue De Thionville;75019;Paris 19;48.8904226;2.3857306 +0753146M;Ecole 2d Degre General PrivĂ©e Prepasup;38 Rue Des Blancs Manteaux;75004;Paris 04;48.8597021;2.3551971 +0753818T;Ecole 2d Degre General PrivĂ©e Rene Reaumur;81 Rue Reaumur;75002;Paris 02;48.866918;2.3486024 +0030906U;Ecole 2d Degre General PrivĂ©e Rudolf Steiner;Chateau De La Mhotte;03210;St Menoux;46.572769;3.115683 +0595489W;Ecole 2d Degre General PrivĂ©e Saint Benoit;116 Boulevard D'armentieres;59100;Roubaix;50.7010617;3.1628397 +0142219L;Ecole 2d Degre General PrivĂ©e Sainte Catherine De Sienne;;14740;St Manvieu Norrey;49.180678;-0.501961 +0753109X;Ecole 2d Degre General PrivĂ©e Saint-john Perse;14 Rue Cavallotti;75018;Paris 18;48.8866068;2.327952 +0753376M;Ecole 2d Degre General PrivĂ©e Saint-john Perse;3 Rue De L'eure;75014;Paris 14;48.8324435;2.3221673 +0942025R;Ecole 2d Degre General PrivĂ©e Sophia;5 Rue Colonel P.brossolette;94480;Ablon Sur Seine;48.7249353;2.4195636 +0753894A;Ecole 2d Degre General PrivĂ©e Spinoza;22 Rue Du Sergent Bauchat;75012;Paris 12;48.845549;2.3930299 +0861216E;Ecole 2d Degre General PrivĂ©e St Thomas D Aquin;Le Parc;86700;Romagne;46.290382;0.3073 +0441850X;Ecole 2d Degre General PrivĂ©e Ste Catherine De Sienne;32 Rue De La Bastille;44000;Nantes;47.2185622;-1.5664552 +0753878H;Ecole 2d Degre General PrivĂ©e Verlaine;37 Rue Des Martyrs;75009;Paris 09;48.879285;2.3398549 +0754676A;Ecole 2d Degre General PrivĂ©e Yechiva Chaare Thora;3 5 Rue Henri Turot;75019;Paris 19;48.876668;2.3725952 +0851557F;Ecole 2d Degre International Chavagnes;96 Rue Du Calvaire;85250;Chavagnes En Paillers;46.8935629;-1.2523775 +0261430N;Ecole 2d Degre Polyval PrivĂ©e Formacom;3 Place Marx Dormoy;26200;Montelimar;44.5549839;4.7495125 +0450771U;Ecole 2d Degre Polyval PrivĂ©e Formation Entreprise - Cecam;28 Rue Du Fbg De Bourgogne;45000;Orleans;47.9011332;1.9212467 +0731504N;Ecole 2d Degre Polyval PrivĂ©e Univeria;725 Faubourg Montmelian;73000;Chambery;45.5665104;5.9347242 +0312579K;Ecole 2d Degre Prof PrivĂ©e Dynameca;75 Avenue De Grande Bretagne;31300;Toulouse;43.5999437;1.4151779 +0171382K;Ecole 2d Degre Prof PrivĂ©e Esthetique Coiffure De Sainton;4 Rue De La Comedie;17100;Saintes;45.7454397;-0.6320982 +0171434S;Ecole 2d Degre Prof PrivĂ©e Inst Reg Techn Image Et Son;25 Avenue Dassault;17304;Rochefort;45.945147;-0.9578502 +0171416X;Ecole 2d Degre Prof PrivĂ©e J. Eudes;8 Boul. Du Commandant Charcot;17443;Aytre;46.1351019;-1.1261536 +0861245L;Ecole 2d Degre Prof PrivĂ©e Skhole D'art;2 Et 4 Place Jean De Berry;86000;Poitiers;46.5903977;0.3396993 +0141992P;Ecole 2d Degre Prof. PrivĂ©e Arcade Caen;9 Rue Du Colonel Remy;14000;Caen;49.202484;-0.3878073 +0593022P;Ecole 2d Degre Prof. PrivĂ©e Coupe Couture;38 Rue De Roubaix;59800;Lille;50.6388879;3.0697772 +0251070C;Ecole 2d Degre Prof. PrivĂ©e Cours Hotelier De Besancon;98 Grande Rue;25000;Besancon;47.2357282;6.0272858 +0593076Y;Ecole 2d Degre Prof. PrivĂ©e Cte Formation Perfectionnement;Rue Des Cligneux;59330;Hautmont;50.24159;3.895865 +0251674J;Ecole 2d Degre Prof. PrivĂ©e Ecole De Coiffure Cordier;11 Bis Rue Nicolas Bruand;25000;Besancon;47.2493423;6.0227103 +0900039D;Ecole 2d Degre Prof. PrivĂ©e Ecole De Commerce Kisel;7 Faubourg De Lyon;90000;Belfort;47.6343043;6.850814 +0251523V;Ecole 2d Degre Prof. PrivĂ©e Ecole Du Fc Sochaux;20 Rue De La Cote;25230;Seloncourt;47.4588815;6.8458273 +0900035Z;Ecole 2d Degre Prof. PrivĂ©e Ecole Ema / Imt;11-13 Rue Aristide Briand;90000;Belfort;47.6327477;6.8553195 +0622169W;Ecole 2d Degre Prof. PrivĂ©e Ecole Europeenne D'esthetique;114 Rue Saint Aubert;62000;Arras;50.2924651;2.7692463 +0596339V;Ecole 2d Degre Prof. PrivĂ©e Ecole PrivĂ©e De Coiffure;74 Boulevard De La Liberte;59800;Lille;50.633826;3.0576975 +0596351H;Ecole 2d Degre Prof. PrivĂ©e Efficom-nord;144/146 Rue Nationale;59000;Lille;50.6335348;3.0541112 +0595721Y;Ecole 2d Degre Prof. PrivĂ©e Lp PrivĂ© Cours Auber-icam;6 Rue Auber;59046;Lille;50.6302567;3.0417529 +0595828P;Ecole 2d Degre Prof. PrivĂ©e Pigier I.t.m.-i.c.a.;14 Rue Des Fusiliers Marins;59140;Dunkerque;51.0329454;2.3721355 +0593019L;Ecole 2d Degre Prof. PrivĂ©e Schanfelaer-normand;20 Avenue Charles Saint Venant;59800;Lille;50.634035;3.0703394 +0594621C;Ecole 2d Degre Prof. PrivĂ©e Soins Esthetiques;9-11 Rue Leon Trulin;59800;Lille;50.6384147;3.0663107 +0593098X;Ecole 2d Degre Prof. PrivĂ©e Sueur Delabre Ctr Format PrivĂ©;Parks Service Unite1;59406;Cambrai;50.173538;3.236633 +0251908N;Ecole 2d Degre Prof.priv. Afco Ass. Franc-comt. Form.coiffur;16 Rue De Belfort;25000;Besancon;47.2469149;6.026832 +0360846Z;Ecole 2d Degre Prof.privee;8-10 Rue Guimon Latouche;36000;Chateauroux;46.8126291;1.6942297 +0580977X;Ecole 2d Degre Prof.privee;5 Avenue Claude Delisse;58360;St Honore Les Bains;46.8962966;3.8561571 +0711892W;Ecole 2d Degre Prof.privee;Charly;71250;Mazille;46.396423;4.610709 +0610799M;Ecole 2d Degre Prof.privee A.d.f.i.;40 Rue Du Puit Au Verrier;61001;Alencon;48.4363865;0.0876791 +0142359N;Ecole 2d Degre Prof.privee Acad Coif Esthet Morriss Partn;177 Rue D Auge;14000;Caen;49.1747803;-0.3407445 +0596780Z;Ecole 2d Degre Prof.privee Academie PrivĂ©e Michel Dervyn;344 Avenue De La Marne;59700;Marcq En Baroeul;50.6798793;3.1150119 +0672904T;Ecole 2d Degre Prof.privee Academy Serge Comtesse;11 Rue Boston;67000;Strasbourg;48.5766769;7.7707505 +0333122P;Ecole 2d Degre Prof.privee Alienor D Esthetique;55 57 Rue Jules Ferry;33200;Bordeaux;44.842255;-0.6034116 +0851559H;Ecole 2d Degre Prof.privee Animation Et Petite Enfance;Zi Montifaut;85700;Pouzauges;46.782648;-0.837243 +0440432F;Ecole 2d Degre Prof.privee Anjorrant;80 Rue Du General Buat;44009;Nantes;47.2305106;-1.5393662 +0422052C;Ecole 2d Degre Prof.privee Ass Forezienne Ec Production;10 Rue Scheurer Kestner;42000;St Etienne;45.453516;4.3906874 +0721300X;Ecole 2d Degre Prof.privee Assoc Formation Personnel;47 Avenue Olivier Messiaen;72018;Le Mans;48.0146373;0.1594294 +0383299E;Ecole 2d Degre Prof.privee A-tel Communication;21 Avenue De La Plaine Fleurie;38240;Meylan;45.20604;5.7576666 +0693134G;Ecole 2d Degre Prof.privee Atelier Apprent Gorge De Loup;105 Avenue Sidoine Apollinaire;69009;Lyon 09;45.7672746;4.7977054 +0691474C;Ecole 2d Degre Prof.privee Ateliers D'apprentissage;La Giraudiere;69690;Brussieu;45.752063;4.547178 +0133909D;Ecole 2d Degre Prof.privee Ateliers De L'image Et Du Son;40 Rue Borde;13008;Marseille 08;43.2788168;5.3910828 +0693444U;Ecole 2d Degre Prof.privee Athena;22 Rue Pizay;69202;Lyon 01;45.7671314;4.836327 +0422109P;Ecole 2d Degre Prof.privee Avenir Formation;10 Rue Emile Noirot;42302;Roanne;46.0386546;4.0683222 +0611282M;Ecole 2d Degre Prof.privee Beaute Coiffure Et Formation;93 Avenue Rhin Et Danube;61000;Alencon;48.4183713;0.0973169 +0492317C;Ecole 2d Degre Prof.privee Beaute Coiffure Formation;51 Place Allain Targe;49400;Saumur;47.2570769;-0.0691588 +0171586G;Ecole 2d Degre Prof.privee Campus Des Metiers;Rue Du Chateau Site Du Prieure;17000;La Rochelle;48.7991114;-1.3941003 +0761772N;Ecole 2d Degre Prof.privee Catherine Lorene;52 Rue Jean Lecanuet;76000;Rouen;49.4454094;1.0882638 +0754129F;Ecole 2d Degre Prof.privee Ccip Ecoles Gregoire-ferrandi;28 Rue De L'abbe Gregoire;75006;Paris 06;48.8474723;2.3254989 +0660876U;Ecole 2d Degre Prof.privee Centre Enseignement Formation;Route De Thuir;66011;Perpignan;42.6820032;2.8646257 +0755207C;Ecole 2d Degre Prof.privee Chapellerie Modiste;8 Passage Abel Leblanc;75012;Paris 12;48.8459871;2.3814297 +0501212W;Ecole 2d Degre Prof.privee Coiff'ecole;26 Rue De La Paix;50100;Cherbourg Octeville;49.643053;-1.6270152 +0195038Y;Ecole 2d Degre Prof.privee Coiffure;4 Place Saint Pierre;19100;Brive La Gaillarde;45.1594566;1.5344809 +0721515F;Ecole 2d Degre Prof.privee Coiffure Esthet Espace Bel Air;200 Avenue Georges Durand;72100;Le Mans;47.9754992;0.2147089 +0442345K;Ecole 2d Degre Prof.privee Coiffure Et Esthetique;44 Quai Malakoff;44000;Nantes;47.2147689;-1.5414584 +0011255J;Ecole 2d Degre Prof.privee Coiffure Europeenne De L'ain;30 Avenue Alphonse Baudin;01000;Bourg En Bresse;46.200325;5.216353 +0111011P;Ecole 2d Degre Prof.privee Coiffure K L;3 Square Gambetta;11000;Carcassonne;43.2116828;2.357763 +0133683H;Ecole 2d Degre Prof.privee Coiffure Leader;441 Avenue Du Prado;13008;Marseille 08;43.266863;5.3830066 +0851437A;Ecole 2d Degre Prof.privee Coiffure Morriss;12 Rue Jean Jaures;85000;La Roche Sur Yon;46.6696044;-1.4289695 +0421913B;Ecole 2d Degre Prof.privee Createc;12 Rue Michel Rondet;42000;St Etienne;45.4380867;4.3854206 +0754168Y;Ecole 2d Degre Prof.privee Ctre Marcel Lamy Coiffure-esth;15-17 Rue Des Fillettes;75018;Paris 18;48.8946669;2.3633961 +0754338H;Ecole 2d Degre Prof.privee Ctre Pri Ens Soins Esthetiques;9 Rue Volney;75002;Paris 02;48.8696803;2.3301585 +0311202N;Ecole 2d Degre Prof.privee D'esthetique Cosmetique;23 Rue Gabriel Peri;31000;Toulouse;43.6060897;1.4534456 +0752123A;Ecole 2d Degre Prof.privee Ec Francaise D'ens Technique;110 Rue De Picpus;75012;Paris 12;48.8385284;2.4013752 +0754872N;Ecole 2d Degre Prof.privee Ec Internat Esthetiq Francaise;5 Avenue Bertie Albrecht;75008;Paris 08;48.8756935;2.3011723 +0754108H;Ecole 2d Degre Prof.privee Ec Internationale De Coiffure;76 Rue Reaumur;75002;Paris 02;48.8664449;2.3517469 +0342216Z;Ecole 2d Degre Prof.privee Ec Pr Esthetique Millenaire;1350 Av Albert Einstein;34000;Montpellier;43.6101676;3.9133879 +0341752V;Ecole 2d Degre Prof.privee Ec Sup Metiers Aern Esma;Aerp Montpellier Mediterranee;34137;Mauguio;43.5788389;3.959328 +0922623B;Ecole 2d Degre Prof.privee Ecofac;72 Avenue De La Republique;92320;Chatillon;48.8019714;2.2959133 +0754169Z;Ecole 2d Degre Prof.privee Ecole Active De Coiffure;203 205 Rue Lafayette;75010;Paris 10;48.8810547;2.3636236 +0754112M;Ecole 2d Degre Prof.privee Ecole De Bijouterie-joaillerie;58 Rue Du Louvre;75002;Paris 02;48.8658847;2.3434157 +0750115T;Ecole 2d Degre Prof.privee Ecole De Coiffure;60 Rue Saint Lazare;75009;Paris 09;48.8768576;2.3333014 +0754114P;Ecole 2d Degre Prof.privee Ecole Dentaire Francaise;3 Rue De L'est;75020;Paris 20;48.8708623;2.3944941 +0180939Y;Ecole 2d Degre Prof.privee Ecole Des Metiers;145-151 Rue De Turly;18000;Bourges;47.1015295;2.4242279 +0681962N;Ecole 2d Degre Prof.privee Ecole Des Metiers Artistiques;2 Rue Sainte-catherine;68100;Mulhouse;47.7426617;7.3390234 +0251890U;Ecole 2d Degre Prof.privee Ecole Ema 25 Besancon;13 Rue Du Balcon;25000;Besancon;47.2476688;6.0251837 +0624303R;Ecole 2d Degre Prof.privee Ecole Europeenne Esthetique Ii;112 Rue Saint Aubert;62000;Arras;50.2924167;2.7693128 +0442412H;Ecole 2d Degre Prof.privee Ecole Nantaise De Coiffure;6 Place Montaigne;44000;Nantes;47.2201576;-1.5615212 +0011294B;Ecole 2d Degre Prof.privee Ecole Technique Du Bois;560 B Rue Du Marais;01110;Cormaranche En Bugey;45.957123;5.602139 +0212188P;Ecole 2d Degre Prof.privee Eisec;35 Rue Charles Dumont;21000;Dijon;47.3123064;5.0399242 +0624372R;Ecole 2d Degre Prof.privee Elit Academy;50 60 Rue De Folkestone;62200;Boulogne Sur Mer;50.7279326;1.5994104 +0062052Y;Ecole 2d Degre Prof.privee Elysees Cannes;22 Bd De La Republique;06400;Cannes;43.5545307;7.0250715 +0672997U;Ecole 2d Degre Prof.privee Elysees Marbeuf;26 Rue Des Magasins;67000;Strasbourg;48.591175;7.7408775 +0212187N;Ecole 2d Degre Prof.privee Ema;15 Rue De L'arquebuse;21000;Dijon;47.3190199;5.031125 +0831653W;Ecole 2d Degre Prof.privee Ema;1349 Boulevard Jean Moulin;83700;St Raphael;43.4279147;6.7900075 +0292256V;Ecole 2d Degre Prof.privee Emb Pigier;31 Rue De Douarnenez;29000;Quimper;47.9980478;-4.1098548 +0121234B;Ecole 2d Degre Prof.privee Emilie De Rodat;11 Rue Seguy;12000;Rodez;44.3519011;2.5755888 +0460637T;Ecole 2d Degre Prof.privee Enseignement Commercial;17 Rue De La Prefecture;46000;Cahors;44.4475229;1.441989 +0421947N;Ecole 2d Degre Prof.privee Epec-darfeuille;5 Place Jean Jaures;42000;St Etienne;45.4405716;4.3855444 +0580978Y;Ecole 2d Degre Prof.privee Estec;2/4 Rue Saint-benin;58000;Nevers;46.9862229;3.1492371 +0875054W;Ecole 2d Degre Prof.privee Esthetiq Cosmetiq Coiffure;3 Allee St Alexis;87000;Limoges;45.8253606;1.2591981 +0573543S;Ecole 2d Degre Prof.privee Esthetique Coiffure;9 Rue Ausone;57000;Metz;49.1087379;6.1725896 +0820777Z;Ecole 2d Degre Prof.privee Esthetique Cosmet Skhole D'art;3 R Bataille De Dunkerque 1940;82000;Montauban;44.0068309;1.3461514 +0011256K;Ecole 2d Degre Prof.privee Esthetique M C Dumonceau;30 Avenue Alphonse Baudin;01000;Bourg En Bresse;46.200325;5.216353 +0753082T;Ecole 2d Degre Prof.privee Ets - Iris;63 Rue Ampere;75017;Paris 17;48.885201;2.3009995 +0752124B;Ecole 2d Degre Prof.privee F.morice Esthetique-cosmetique;27 Rue Vernet;75008;Paris 08;48.8720338;2.2978549 +0241248B;Ecole 2d Degre Prof.privee Formafoot;87 Route De Bordeaux;24430;Marsac Sur L Isle;45.1841986;0.6576263 +0442617F;Ecole 2d Degre Prof.privee Formation Corps Et Beaute;24 Rue De L'ile De France;44600;St Nazaire;47.2845926;-2.209649 +0754465W;Ecole 2d Degre Prof.privee Formul?abc;105 Boulevard De Magenta;75010;Paris 10;48.878965;2.3535647 +0492028N;Ecole 2d Degre Prof.privee Francaise Coiffure Esthetique;Place Du Chapeau De Gendarme;49000;Angers;47.4511367;-0.5553704 +0492343F;Ecole 2d Degre Prof.privee Francaise Coiffure Esthetique;192 Rue Nationale;49300;Cholet;47.0618334;-0.8842425 +0133825M;Ecole 2d Degre Prof.privee Giorgifont;2a Rue De Rome;13001;Marseille 01;43.2957629;5.3782139 +0133567G;Ecole 2d Degre Prof.privee Groupe Pluralis;16 Bd Theodore Thurner;13006;Marseille 06;43.2918566;5.383141 +0422027A;Ecole 2d Degre Prof.privee Hair School;35 Rue D'arcole;42000;St Etienne;45.43977;4.3804548 +0912185F;Ecole 2d Degre Prof.privee I P F C;93 Bd Decauville;91024;Evry;48.6332379;2.4421065 +0301757X;Ecole 2d Degre Prof.privee Ifc Gard;125 Rue De L Hostellerie;30000;Nimes;43.8139025;4.3459059 +0693368L;Ecole 2d Degre Prof.privee Ifosupd;181 Avenue Jean Jaures;69348;Lyon 07;45.7368155;4.836897 +0142172K;Ecole 2d Degre Prof.privee Inst Form Esth Et Coiff Ifec;1 Et 3 Rue De L'arquette;14000;Caen;49.1748513;-0.3556685 +0341674K;Ecole 2d Degre Prof.privee Inst Sup PrivĂ© Sup Exup;Aerp Montpellier Mediterranee;34137;Mauguio;43.5788389;3.959328 +0851584K;Ecole 2d Degre Prof.privee Inst Sup Prothese Assist Dent;Route De La Goriandiere;85504;Les Herbiers;46.872157;-0.999918 +0841042B;Ecole 2d Degre Prof.privee Inst. PrivĂ© Format.commerciale;250 Rue Du 12e Reg. De Zouaves;84094;Avignon;43.949317;4.805528 +0721610J;Ecole 2d Degre Prof.privee Instit K Form Esthetique;200 Avenue Georges Durand;72100;Le Mans;47.9754992;0.2147089 +0492342E;Ecole 2d Degre Prof.privee Institut De Bijouterie;Square Balzac;49412;Saumur;47.2519481;-0.0800867 +0754163T;Ecole 2d Degre Prof.privee Institut Europeen De Coiffure;28 Rue De Trevise;75009;Paris 09;48.8746401;2.3454931 +0851599B;Ecole 2d Degre Prof.privee Iseca;Village D'entreprises;85700;Pouzauges;46.782648;-0.837243 +0840077C;Ecole 2d Degre Prof.privee Isfap;11 Avenue Des Sources;84000;Avignon;43.9413462;4.8139846 +0573734Z;Ecole 2d Degre Prof.privee Isfec;15 Rue Des Charpentiers;57070;Metz;49.1114889;6.2334259 +0061888V;Ecole 2d Degre Prof.privee Jeune Aiglon;177 Route De Grenoble;06200;Nice;43.6810228;7.1990378 +0624366J;Ecole 2d Degre Prof.privee Joe's Art Concept;3 Rue Bayard;62300;Lens;50.4298166;2.8333087 +0312693J;Ecole 2d Degre Prof.privee La Pradette;12 Chemin De La Pradette;31600;Muret;43.4624447;1.3224734 +0690636S;Ecole 2d Degre Prof.privee Le C.e.p.a.j;Chemin De Bernicot;69564;St Genis Laval;45.6933714;4.771675 +0673004B;Ecole 2d Degre Prof.privee L'inst. Coiffure Et Esthetique;10 Rue Paul Eluard;67200;Strasbourg;48.5882779;7.6973786 +0301777U;Ecole 2d Degre Prof.privee L'institut;4 Bis Rue Julien Trelis;30100;Ales;44.127204;4.083352 +0631974D;Ecole 2d Degre Prof.privee Maestris;9/12 Rue Gilbert Romme;63000;Clermont Ferrand;45.7675847;3.101037 +0596791L;Ecole 2d Degre Prof.privee Maestris Beaute;679 Avenue De La Republique;59000;Lille;50.6562547;3.0875691 +0596794P;Ecole 2d Degre Prof.privee Maestris Beaute;9 Bis Rue De Roubaix;59400;Cambrai;50.1811372;3.2380149 +0133780N;Ecole 2d Degre Prof.privee Maestris Marseille;54 Traverse Maridet;13012;Marseille 12;43.3096761;5.4199628 +0133787W;Ecole 2d Degre Prof.privee Maestris Pigier;2 Rue Le Corbusier;13090;Aix En Provence;43.5233286;5.4325772 +0422190C;Ecole 2d Degre Prof.privee Maison De La Coiffure;38 Rue Des Passementiers;42100;St Etienne;45.4198431;4.396064 +0754242D;Ecole 2d Degre Prof.privee Marbeuf Esthetique Elysees;27 Rue Marbeuf;75008;Paris 08;48.8691291;2.3042693 +0312251D;Ecole 2d Degre Prof.privee Marge Verlair;5 Impasse Saint Aubin;31000;Toulouse;43.6037233;1.4532092 +0440425Y;Ecole 2d Degre Prof.privee Matile Esthetique Manucurie;24 Rue Crebillon;44000;Nantes;47.2132721;-1.5619479 +0721546P;Ecole 2d Degre Prof.privee Metiers De La Musique;71 Avenue Olivier Messiaen;72000;Le Mans;48.0154161;0.1544436 +0492333V;Ecole 2d Degre Prof.privee Morriss Partner;70 Rue Du Grand Launay;49000;Angers;47.4678294;-0.5932746 +0442196Y;Ecole 2d Degre Prof.privee Morriss-partner;5 Allee Baco;44002;Nantes;47.2146182;-1.5479843 +0061926L;Ecole 2d Degre Prof.privee N?1 Ctre Perf Maitres Coiffeur;1 Place Massena;06000;Nice;43.6974679;7.2702352 +0596406T;Ecole 2d Degre Prof.privee Patricia Houzet;12 Rue Sadi Carnot;59290;Wasquehal;50.668925;3.1303161 +0660070T;Ecole 2d Degre Prof.privee Pigier;49 Bd Clemenceau 7 Rue P Courty;66000;Perpignan;42.698684;2.8958719 +0694088U;Ecole 2d Degre Prof.privee Pigier;110 Place De La Gare;69400;Villefranche Sur Saone;45.9845849;4.7202289 +0841119K;Ecole 2d Degre Prof.privee Pigier;40 Bis Chemin Du Lavarin;84000;Avignon;43.9295739;4.8047154 +0442547E;Ecole 2d Degre Prof.privee Pigier Coiffure;16 Rue De Vauzelles;44110;Chateaubriant;47.712001;-1.3750486 +0492230H;Ecole 2d Degre Prof.privee Pigier Coiffure Esthetique;30 Rue Bodinier;49000;Angers;47.4726409;-0.5544881 +0720829K;Ecole 2d Degre Prof.privee Pigier Coiffure Esthetique;41 Rue Du Docteur Leroy;72005;Le Mans;48.0016328;0.1969314 +0301749N;Ecole 2d Degre Prof.privee Pigier Formanimes;105 Rue Claude Nicolas Ledoux;30900;Nimes;43.8150783;4.3471676 +0754686L;Ecole 2d Degre Prof.privee R.ferrere Esthetiq-cosmetique;14 Rue Du Faubourg St Honore;75008;Paris 08;48.8687211;2.3223202 +0751593Z;Ecole 2d Degre Prof.privee Saint-louis Union Academie;113 115 Rue Du Fg Poissonniere;75009;Paris 09;48.8779515;2.3490733 +0341831F;Ecole 2d Degre Prof.privee Sarl Ecole Sonia Delaunay;Route De Boujan;34500;Beziers;43.3663036;3.2315349 +0721645X;Ecole 2d Degre Prof.privee Sarth'esthetique Formations;185 Rue Henri Champion;72100;Le Mans;47.9827235;0.232009 +0690696G;Ecole 2d Degre Prof.privee Silvya Terrade;53 Place De La Republique;69002;Lyon 02;45.7606843;4.8360085 +0693974V;Ecole 2d Degre Prof.privee Studio M;565 Rue Du Sans Souci;69760;Limonest;45.805516;4.7733391 +0333187K;Ecole 2d Degre Prof.privee Visagisme Coiffure Developpeme;81 Avenue De L'epinette;33500;Libourne;44.9106614;-0.2289695 +0752435P;Ecole 2d Degre Prof.privee Y.rocher Esthetique-cosmetique;62 Avenue D'iena;75016;Paris 16;48.8687256;2.2959626 +0751698N;Ecole 2d Degree Prof. PrivĂ©e Pigier;67 69 Rue De Douai;75009;Paris 09;48.883762;2.3287504 +0132665B;Ecole 2nd Degre General PrivĂ©e Cours Du Parc Breteuil;7 Rue Lacedemone;13006;Marseille 06;43.2814125;5.3771657 +0133446A;Ecole 2nd Degre General PrivĂ©e Hamaskaine;60 Boulevard Pinatel;13012;Marseille 12;43.3148118;5.4413806 +0133148B;Ecole 2nd Degre General PrivĂ©e I.b.s. Of Provence;500 Petite Rte De Bouc Bel Air;13080;Aix En Provence;43.4513324;5.429245 +0131938L;Ecole 2nd Degre General PrivĂ©e Institut Leschi;58 Cours Julien;13006;Marseille 06;43.2943303;5.383053 +0133242D;Ecole 2nd Degre General PrivĂ©e St Pierre;24 Av St Eloi;13790;Peynier;43.4467555;5.6397325 +0131669U;Ecole 2nd Degre General PrivĂ©e Val Saint Andre;19 Avenue Malacrida;13100;Aix En Provence;43.5165876;5.4668556 +0430842G;Ecole Agricole PrivĂ©e;36 Rue Du Colonel De Turenne;43200;Yssingeaux;45.1426639;4.1220723 +0601404A;Ecole Agricole PrivĂ©e;;60120;Rouvroy Les Merles;49.647906;2.358818 +0601575L;Ecole Agricole PrivĂ©e;Place De L'hotel De Ville;60190;Estrees St Denis;49.426115;2.639508 +0611018A;Ecole Agricole PrivĂ©e;Saint Gervais;61220;Briouze;48.704021;-0.369682 +0611021D;Ecole Agricole PrivĂ©e;;61240;Nonant Le Pin;48.7069199;0.217466 +0631560D;Ecole Agricole PrivĂ©e;;63580;Vernet La Varenne;45.472891;3.451612 +0631561E;Ecole Agricole PrivĂ©e;1 Route De Riom;63720;Ennezat;45.8977695;3.2206849 +0601576M;Ecole Agricole PrivĂ©e (filles);39 Route Des Flandres;60490;Orvillers Sorel;49.5786617;2.7084991 +0332376D;Ecole Agricole PrivĂ©e Centre D Enseignement Agricole;44 Crs Gambetta;33210;Langon;44.5532316;-0.2524309 +0840746E;Ecole Agricole PrivĂ©e Centre De Formation;19 Quai Pasteur;84110;Vaison La Romaine;44.2401186;5.0726233 +0595121W;Ecole Agricole PrivĂ©e Centre D'enseignement Agricole;110 Route De Gravelines;59630;Bourbourg;50.9522873;2.1920151 +0501576S;Ecole Agricole PrivĂ©e Centre D'etudes Rurales;Rue De Monteglise;50720;Barenton;48.6026445;-0.8315884 +0595122X;Ecole Agricole PrivĂ©e Centre Education Et Promotion;15 Rue Du College;59940;Estaires;50.6462778;2.7208128 +0141724Y;Ecole Agricole PrivĂ©e Centre Horticole;Rue Du Prieure;14480;St Gabriel Brecy;49.2780979;-0.5664422 +0840747F;Ecole Agricole PrivĂ©e Centre Saint Dominique;Rue Des Ursulines;84601;Valreas;44.385702;4.989613 +0030888Z;Ecole Agricole PrivĂ©e Claude Mercier;Route De Lapalisse;03250;Le Mayet De Montagne;46.0750346;3.6671551 +0261265J;Ecole Agricole PrivĂ©e Ctr D'etudes Forestieres Agr.;103 Av De Rochemaure;26200;Montelimar;44.5657532;4.735804 +0611175W;Ecole Agricole PrivĂ©e Ctre Form Technicien Agricole;25 Rue Pierre Neveu;61600;La Ferte Mace;48.5935429;-0.3524704 +0771738W;Ecole Agricole PrivĂ©e Ctre Formation Prof Agr Et Hor;Domaine De Morfonde;77270;Villeparisis;48.947136;2.638554 +0511646M;Ecole Agricole PrivĂ©e Ctre Hort Foyer De L'enfance;33 Rue Du Clair Logis;51000;Chalons En Champagne;48.9740922;4.3620971 +0501793C;Ecole Agricole PrivĂ©e Ctre Nat.form.lads Et Jockeys;Graignes;50620;Graignes Mesnil Angot;49.2381699;-1.19717 +0931654T;Ecole Agricole PrivĂ©e Ec Horticulture Fenelon;1 Rue De Montauban;93410;Vaujours;48.9301569;2.5695529 +0881452A;Ecole Agricole PrivĂ©e Ecole D Horticulture Et De Pay;6 Rue Du College;88700;Roville Aux Chenes;48.3861293;6.6027303 +0840788A;Ecole Agricole PrivĂ©e Et Reg Form Prof Agric Forest;;84240;La Bastide Des Jourdans;43.784867;5.634354 +0781068M;Ecole Agricole PrivĂ©e Horticole;43 Rue Du General De Gaulle;78490;Le Tremblay Sur Mauldre;48.7703215;1.8781482 +0133500J;Ecole Agricole PrivĂ©e I.c.o.p.;1 Allee Des Pinsons;13012;Marseille 12;43.3136607;5.4380651 +0595012C;Ecole Agricole PrivĂ©e Inst Enseignement Technologiqu;Route De Warhem;59492;Hoymille;50.972972;2.461305 +0641545G;Ecole Agricole PrivĂ©e Inst.secondaire Jean Errecart;Route Aicirits;64120;St Palais;43.327408;-1.032999 +0595120V;Ecole Agricole PrivĂ©e Institut Agricole;19 Rue De L'epee;59400;Cambrai;50.1735274;3.2293037 +0595124Z;Ecole Agricole PrivĂ©e Institut Agricole;69 Rue Du Violon D'or;59190;Hazebrouck;50.7219064;2.5298525 +0623280D;Ecole Agricole PrivĂ©e Institut Agricole;22 Ancienne Route Nationale;62690;Savy Berlette;50.3497901;2.557783 +0623279C;Ecole Agricole PrivĂ©e Institut Agricole Saint Joseph;Route Nationale 43;62137;Coulogne;50.905437;1.9459807 +0593257V;Ecole Agricole PrivĂ©e Institut De Genech;Rue De La Liberation;59242;Genech;50.5300906;3.2117313 +0501578U;Ecole Agricole PrivĂ©e Institut L'abbaye;Route De Quineville;50310;Montebourg;49.4967815;-1.3685693 +0141723X;Ecole Agricole PrivĂ©e Institut Lemonier;Rue D Herouville;14013;Caen;49.1935596;-0.3500571 +0030925P;Ecole Agricole PrivĂ©e Institut Rural;;03110;Escurolles;46.143095;3.26744 +0400753B;Ecole Agricole PrivĂ©e Ireo;Cassen;40360;Castelnau Chalosse;43.679287;-0.846823 +0382374Z;Ecole Agricole PrivĂ©e La Maison;La Bonte;38620;St Geoire En Valdaine;45.4581366;5.6351566 +0133005W;Ecole Agricole PrivĂ©e Lads Et Jockeys;Les Plaines De L Arbois;13480;Cabries;43.44489;5.320507 +0601613C;Ecole Agricole PrivĂ©e Lads-jockeys;5 Rue Du Chauffour;60270;Gouvieux;49.1894825;2.4274189 +0641542D;Ecole Agricole PrivĂ©e Lap Frantses Enia;Av Du Jai Alai;64220;St Jean Pied De Port;43.1663684;-1.2335137 +0781066K;Ecole Agricole PrivĂ©e Le Logis;5 Rue De Port Royal;78470;St Lambert;48.7332837;2.0194432 +0150661M;Ecole Agricole PrivĂ©e Letrt St Vincent;2 Rue Marcellin Boudet;15100;St Flour;45.0348129;3.0871023 +0623276Z;Ecole Agricole PrivĂ©e LycĂ©e Agricole Sainte Marie;52 Rue D'isbergues;62120;Aire Sur La Lys;50.6386462;2.4046454 +0593256U;Ecole Agricole PrivĂ©e LycĂ©e PrivĂ© D'anchin;Abbaye D'anchin;59146;Pecquencourt;50.375524;3.212056 +0595119U;Ecole Agricole PrivĂ©e LycĂ©e PrivĂ© Rural;5 Rue De La Chaussee Brunehaut;59570;Bavay;50.2871242;3.7776055 +0831109E;Ecole Agricole PrivĂ©e LycĂ©e Professionnel Agricole;Route De Barjols;83470;St Maximin La Ste Baume;43.472847;5.869149 +0623278B;Ecole Agricole PrivĂ©e LycĂ©e Rural PrivĂ© Catholique;Rue Druet;62116;Bucquoy;50.137547;2.70619 +0781022M;Ecole Agricole PrivĂ©e Notre-dame De La Roche;Boite Postale 10;78320;Levis St Nom;48.722222;1.949444 +0150658J;Ecole Agricole PrivĂ©e Saint Joseph;Avenue De La Gare;15600;Maurs;44.7084459;2.1994338 +0911551S;Ecole Agricole PrivĂ©e St Antoine;Chateau;91460;Marcoussis;48.645942;2.220813 +0762284V;Ecole Agricole PrivĂ©e St Joseph;Chateau De Mesnieres;76270;Mesnieres En Bray;49.7649649;1.380615 +0781069N;Ecole Agricole PrivĂ©e Sully;22 Avenue De L'europe;78200;Magnanville;48.9665663;1.6800698 +0781023N;Ecole Agricole PrivĂ©e Tecomah;Parc De Jouy;78350;Jouy En Josas;48.764757;2.163188 +0690194L;Ecole Catholique D'arts Et Metiers;40 Montee Saint Barthelemy;69321;Lyon 05;45.7624909;4.8246771 +0590349J;Ecole Centrale De Lille;Cite Scientifique;59651;Villeneuve D Ascq;50.623334;3.145 +0690187D;Ecole Centrale De Lyon;36 Avenue Guy De Collongue;69131;Dardilly;45.782627;4.7662635 +0440100V;Ecole Centrale De Nantes;1 Rue De La Noe;44321;Nantes;47.2482417;-1.5499091 +0754431J;Ecole Centrale D'electronique;53 Rue De Grenelle;75007;Paris 07;48.8545505;2.3256016 +0921225G;Ecole Centrale Des Arts Et Manufactures, Paris;Grande Voie Des Vignes;92295;Chatenay Malabry;48.7660651;2.2865429 +0951807Y;Ecole Commerciale PrivĂ©e Fayol;4 Rue Revert;95300;Pontoise;49.052526;2.0952513 +0251480Y;Ecole D'agriculture PrivĂ© Saint Joseph;Place Cretin;25270;Levier;46.954174;6.116989 +0951820M;"ecole De Biologie Industrielle ""ebi""";32 Boulevard Du Port;95094;Cergy;49.0322975;2.0640348 +0501669T;Ecole De Formation Technique Ecole Des Fourriers;;50460;Querqueville;49.664767;-1.695441 +0595781N;Ecole De Formation Technique Ecole Reedu Prof Andre Maginot;35 Rue Du General Sarail;59056;Roubaix;50.6915213;3.1716011 +0130230E;Ecole De L'air;Base Aerienne 701;13300;Salon De Provence;43.603333;5.106389 +0440429C;Ecole De Metiers Ctre Formation Gaz De France;La Croix Gaudin;44360;St Etienne De Montluc;47.2834179;-1.7417426 +0741433F;Ecole De Production PrivĂ©e E.c.a.u.t;301 Route De Bregny;74250;Viuz En Sallaz;46.147207;6.393383 +0470064P;Ecole De Tersac Ac;Tersac;47180;Meilhan Sur Garonne;44.495752;0.06094 +0312188K;Ecole Dentaire Francaise;20 Rue St Antoine Du T;31000;Toulouse;43.603794;1.4473808 +0251950J;Ecole Des Arts Et Techniques;11 Rue Nicolas Bruand;25000;Besancon;47.2493423;6.0227103 +0753742K;Ecole Des Hautes Etudes En Sciences Sociales;54 Boulevard Raspail;75006;Paris 06;48.8501959;2.3269388 +EHESS;Ecole Des Hautes Etudes En Sciences Sociales;54, 96,105 Boulevard Raspail;75005;Paris 05;48.8461933;2.3281331 +0750043P;Ecole D'ingenieurs De La Ville De Paris;57 Boulevard Saint Germain;75240;Paris 05;48.8504483;2.3463367 +0623921A;Ecole D'ingenieurs Du Pas De Calais;Campus De La Malassise;62967;Longuenesse;50.736744;2.243632 +0171435T;Ecole D'ingenieurs En Genie Des Systemes Industriels;26 Rue Vaux De Foletier;17011;La Rochelle;46.1398628;-1.1537103 +0171550T;Ecole Francaise Coiffure Et Esthetique;17-19 Avenue De Mulhouse;17000;La Rochelle;0.0;0.0 +0791162J;Ecole Francaise Coiffure Et Esthetique;7-27 Rue Marcel Paul;79000;Niort;0.0;0.0 +0753592X;Ecole Francaise D'electronique Et D'informatique;10 Rue Amyot;75005;Paris 05;48.8434614;2.3468998 +0941934S;Ecole Francaise D'electronique Et D'informatique;3 Rue Victor Hugo;94800;Villejuif;48.78874;2.3650286 +0951678H;Ecole Gen Et Technol PrivĂ©e Compta-sup Marketing;1 Av De La Div Leclerc;95350;St Brice Sous Foret;48.9922397;2.3583183 +0830177S;Ecole Gen. Et Technol.privee Cours Renaissance;Le Mayol Rue Leon Reboul;83000;Toulon;43.1185381;5.937864 +0831372R;Ecole Gen. Et Technol.privee Guy Furet;702 Avenue Du 15e Corps;83600;Frejus;43.4394351;6.7435339 +0752593L;Ecole Gen.et Technol. PrivĂ© Nation Bauchat Ecole D La Lune;22 Rue Du Sergent Bauchat;75012;Paris 12;48.845549;2.3930299 +0753372H;Ecole Gen.et Technol. PrivĂ©e Beth Sefer Yad Mordekhai;145 Rue Saint Maur;75011;Paris 11;48.8679559;2.375428 +0753819U;Ecole Gen.et Technol. PrivĂ©e Inst Marais-charlemagne-polles;9 Rue Dieu;75010;Paris 10;48.8703187;2.3644784 +0061990F;Ecole Gen.et Technol.privee As Cannes Football;Bp 179;06152;Cannes;43.552847;7.017369 +0133566F;Ecole Gen.et Technol.privee Bac 2000;13 Rue De La Poudriere;13090;Aix En Provence;43.52038;5.4449743 +0752382G;Ecole Gen.et Technol.privee Clapeyron Europe Math;23 Rue Clapeyron;75008;Paris 08;48.8823236;2.3234815 +0755261L;Ecole Gen.et Technol.privee Cours Legendre;25 Rue Du Petit Musc;75004;Paris 04;48.8528745;2.3643336 +0693366J;Ecole Gen.et Technol.privee Cours Montesquieu;11 13 Rue Auguste Lacroix;69003;Lyon 03;45.7549248;4.8451646 +0781946S;Ecole Gen.et Technol.privee Cours Versaillais;28 Rue Albert Joly;78000;Versailles;48.8102821;2.139174 +9830416K;Ecole Gen.et Technol.privee Da Costa;Imm.galaxie 71 Av Benebig;98800;Noumea;0.0;0.0 +0311758T;Ecole Gen.et Technol.privee De Photo Et D'audiovisuel;7 Rue Eugene Labiche;31200;Toulouse;43.6283648;1.4348721 +0596808E;Ecole Gen.et Technol.privee Des Met Du Sport Professionnel;Grand Rue;59780;Camphin En Pevele;50.5934675;3.2630624 +9711174Z;Ecole Gen.et Technol.privee Ecole Jean Roumain;15 Rue De La Liberte;97150;St Martin;43.1913245;0.12305 +9830634X;Ecole Gen.et Technol.privee Epsilon Etudes;74 Bis Rue Rene Milliards;98800;Noumea;-22.2758;166.458 +0101094K;Ecole Gen.et Technol.privee Estac;11 Rue Marie Curie;10000;Troyes;48.2716992;4.0753397 +0442618G;Ecole Gen.et Technol.privee Etu.di.a;Plaine De Jeux De La Joneliere;44240;La Chapelle Sur Erdre;47.299576;-1.550638 +0752904Z;Ecole Gen.et Technol.privee Lafayette;16 Rue Du Mail;75002;Paris 02;48.8668132;2.3427 +0530936E;Ecole Gen.et Technol.privee Moliere;6 Bis Rue Ambroise Pare;53000;Laval;48.0703895;-0.7677541 +0783355Y;Ecole Gen.et Technol.privee Perceval;5 Av D Epremesnil;78400;Chatou;48.8872505;2.1584939 +0301767H;Ecole Gen.et Technol.privee Sports Etudes Concept;Chemin Des Hauts De Nimes;30900;Nimes;43.8632132;4.2957487 +0352337A;Ecole Louis De Broglie;Campus De Ker Lann;35170;Bruz;48.0477814;-1.7425205 +0171157R;Ecole Militaire Enseignement Technique;Base Aerienne 722 Saintes Air;17136;Saintes;45.744175;-0.633389 +0161158X;Ecole Militaire Preparatoire;Base Aerienne 709;16109;Cognac;45.657776;-0.3136109 +0171333G;Ecole Militaire Preparatoire;Base Aerienne 721;17133;Rochefort;44.5163889;4.8611111 +0131596P;Ecole Militaire Preparatoire Poilus (bd Des);13 Boulevard Des Poilus;13617;Aix En Provence;43.5267509;5.4595152 +0610055D;Ecole Nat Prof Des Haras Le Pin Au Haras;Le Pin Au Haras;61310;Le Pin Au Haras;48.738069;0.149685 +0755204Z;Ecole Nationale De Commerce;70 Bd Bessières;75017;Paris 17;0.0;0.0 +0312069F;Ecole Nationale De La Meteorologie;42 Avenue Gaspard Coriolis;31057;Toulouse;43.5775472;1.376741 +0350095N;Ecole Nationale De La Sante Publique;Av Du Professeur Leon Bernard;35043;Rennes;48.117507;-1.7000516 +0311256X;Ecole Nationale De L'aviation Civile De Toulouse;7 Avenue Edouard Belin;31055;Toulouse;43.565156;1.479281 +0753478Y;Ecole Nationale Des Chartes;19 Rue De La Sorbonne;75005;Paris 05;48.8486428;2.3430165 +0753493P;Ecole Nationale Des Mines De Paris;60 Bld Saint-michel;75006;Paris 06;48.8455131;2.339794 +0753501Y;Ecole Nationale Des Ponts Et Chaussees;28 Rue Des Saints Peres;75007;Paris 07;48.8556079;2.3310429 +0772517T;Ecole Nationale Des Ponts Et Chaussees;6 Av B Pascal Cite Descartes;77420;Champs Sur Marne;48.852775;2.602651 +0772496V;Ecole Nationale Des Sciences Geographiques;6-8 Av B Pascal Cite Descartes;77455;Champs Sur Marne;48.852775;2.602651 +0692587M;Ecole Nationale Des Travaux Maritimes;Rue Maurice Audin;69518;Vaulx En Velin;45.7790055;4.9221344 +0692566P;Ecole Nationale Des Travaux Publics De L'etat;Rue Maurice Audin;69518;Vaulx En Velin;45.7790055;4.9221344 +0290119X;Ecole Nationale D'ingenieurs De Brest;Technopole Brest Iroise;29608;Brest;48.3651848;-4.5489314 +0420093Y;Ecole Nationale D'ingenieurs De Saint-etienne;58 Rue Jean Parot;42023;St Etienne;45.430467;4.4237117 +0650048Z;Ecole Nationale D'ingenieurs De Tarbes;47 Avenue D'azereix;65016;Tarbes;43.224813;0.0526893 +0341652L;Ecole Nationale Du Genie Rural Des Eaux Et Des Forets;648 Rue Jean-francois Breton;34093;Montpellier;43.6462081;3.8767159 +0540137F;Ecole Nationale Du Genie Rural Des Eaux Et Des Forets;14 Rue Girardet;54042;Nancy;48.6937927;6.1878434 +0631920V;Ecole Nationale Du Genie Rural Des Eaux Et Des Forets;24 Avenue Des Landais;63172;Aubiere;45.7640856;3.1123259 +0753503A;Ecole Nationale Du Genie Rural Des Eaux Et Des Forets;19 Avenue Du Maine;75732;Paris 15;48.843847;2.320594 +0910684Z;Ecole Nationale Sup Des Industries Agricoles Et Alimentaires;1 Rue Des Olympiades;91300;Massy;48.732702;2.2953659 +0340131H;Ecole Nationale Superieure Agronomique De Montpellier;2 Place Pierre Viala;34060;Montpellier;43.6170101;3.8548718 +0350087E;Ecole Nationale Superieure Agronomique De Rennes;65 Rue De Saint Brieuc;35042;Rennes;48.1136527;-1.7053288 +0753472S;Ecole Nationale SupĂ©rieure D'art Et MĂ©tiers, Paris;151, Bld De L'hopital;75013;Paris 13;48.8339145;2.3573451 +0753237L;École Nationale SupĂ©rieure D'arts Et MĂ©tiers Paris;151, Bd De L'hopital;75013;Paris 13;48.8339145;2.3573451 +0410981U;Ecole Nationale Superieure De La Nature Et Du Paysage;5-7 Rue Des Grands Champs;41029;Blois;47.5660138;1.3272299 +0590339Y;Ecole Nationale Superieure Des Arts Et Metiers De Lille;8 Boulevard Louis Xiv;59046;Lille;50.6282777;3.0711917 +0420094Z;Ecole Nationale Superieure Des Mines De Saint Etienne;158 Cours Fauriel;42023;St Etienne;45.422755;4.408854 +0751878J;Ecole Nationale SupĂ©rieure Des Techniques AvancĂ©es Paris;32 Bld Victor;75015;Paris 15;48.8342821;2.2835648 +0753510H;Ecole Nationale SupĂ©rieure Des TĂ©lĂ©communications Paris;46 Rue Barrault;75634;Paris 13;48.8262704;2.3464541 +0290124C;Ecole Navale;Lanveoc Poulmic;29160;Lanveoc;48.279167;-4.448333 +0753455Y;Ecole Normale Superieure;45, Rue D'ulm;75005;Paris 05;48.8418453;2.3440345 +0940607Z;Ecole Normale SupĂ©rieure De Cachan;61, Avenue Du PrĂ©sident Wilson;94230;Cachan;0.0;0.0 +0920812H;Ecole Normale Superieure De Fontenay Saint-cloud;31, Avenue Lombard;92266;Fontenay Aux Roses;48.789776;2.287181 +0693259T;Ecole Normale Superieure De Sciences Lyon;47 Allee D'italie;69364;Lyon 07;45.7298048;4.8280041 +0951819L;Ecole Phys Math Industrielles;32 Boulevard Du Port;95092;Cergy;49.0322975;2.0640348 +0911568K;Ecole Polytechnique;;91128;Palaiseau;48.7057547;2.219477 +0920674H;Ecole Polytechnique Feminine;3 Bis Rue Lakanal;92330;Sceaux;48.7786376;2.3035209 +0260111E;Ecole Pr D'esthestique/coiffur Epec Beatrice Auger;13 Rue Mirabel Chambaud;26000;Valence;44.9310141;4.8878778 +0333124S;Ecole Pr Fc Girondins Bx Fc Girondins Bordeaux;Rue Joliot Curie;33187;Le Haillan;44.8776541;-0.672552 +0133147A;Ecole Pr Internat. Esthetique Francoise Morice;28 Rue Pierre Et Marie Curie;13100;Aix En Provence;43.5324645;5.4480192 +0331574G;Ecole Pr Pigier;23 Quai De Paludate;33800;Bordeaux;44.8292492;-0.5550825 +0753486G;Ecole Pratique Des Hautes Etudes;45 Rue Des Ecoles;75005;Paris 05;48.8497216;2.3442935 +0352267Z;Ecole PrivĂ©e Coif Esthetique Academy Jacques Bedfert;24 Boulevard Solferino;35000;Rennes;48.1042709;-1.6691883 +0861398C;Ecole PrivĂ©e D Esthetique Agnes Briat;12 Passage Boncenne;86000;Poitiers;46.5835089;0.3405425 +0371561W;Ecole Prof. De Coiffure;213 Rue Febvotte;37000;Tours;47.3754237;0.6818723 +0511678X;Ecole Prof. Ouvriere Agricole;1 Rue Du Levant;51600;Somme Suippe;49.1144052;4.5829985 +0693500E;Ecole Reconversion Prof. Georges Guynemer;37 Rue Challemel Lacour;69364;Lyon 07;45.7299676;4.836691 +0875057Z;Ecole Reg. Metiers De Coiffure;26 Bis Rue Ferdinand Buisson;87000;Limoges;45.8251395;1.2561492 +0022101Y;Ecole Secondaire Haute Picardi Haute Picardie;9 Rue Dachery;02100;St Quentin;49.842455;3.2941865 +0672656Y;Ecole Secondaire PrivĂ©e Beth Hannah;59 Rue Du Faubourg De Pierre;67000;Strasbourg;48.5896482;7.7450122 +0780812J;Ecole Secondaire PrivĂ©e British School Of Paris;38 Quai De L Ecluse;78290;Croissy Sur Seine;48.8737663;2.126996 +0671616T;Ecole Secondaire PrivĂ©e Centre Eshel;19 Rue Schweighaeuser;67000;Strasbourg;48.5862803;7.7635035 +0061636W;Ecole Secondaire PrivĂ©e Cours Albert Camus;3 Avenue Paderi;06200;Nice;43.6894342;7.2347465 +0061541T;Ecole Secondaire PrivĂ©e Cours Du Prado;4 Avenue Prince De Galles;06400;Cannes;43.5583829;7.022587 +0061243U;Ecole Secondaire PrivĂ©e Cours Frederic Mistral Ii;2 Boulevard Marechal Juin;06800;Cagnes Sur Mer;43.6624639;7.1505143 +0060687P;Ecole Secondaire PrivĂ©e Cours Henri Iv;5 Rue Gustave Deloye;06000;Nice;43.6993942;7.2696539 +0332756S;Ecole Secondaire PrivĂ©e Cours Polles;47 Rue Nicot;33300;Bordeaux;44.8337782;-0.5865275 +0831317F;Ecole Secondaire PrivĂ©e Cours PrivĂ© Mirabeau;25 Rue Mirabeau;83000;Toulon;43.1274443;5.9298803 +0830102K;Ecole Secondaire PrivĂ©e Cours Saint Dominique;Quartier Pre Tuillieres;83170;La Celle;43.393994;6.041015 +0672582T;Ecole Secondaire PrivĂ©e Cours Saint-thomas D'aquin;Le Mullerhof Muhlbach/bruche;67280;Urmatt;48.52264;7.312944 +0332759V;Ecole Secondaire PrivĂ©e Crs Secondaire PrivĂ© Peret;43 Bis Rue Du Perigord;33160;St Medard En Jalles;44.8992335;-0.6985045 +0920901E;Ecole Secondaire PrivĂ©e D'aguesseau;16 Rue D Aguesseau;92100;Boulogne Billancourt;48.8443346;2.2363708 +0781901T;Ecole Secondaire PrivĂ©e Dali;45 Rue De Montval;78160;Marly Le Roi;48.8738951;2.0902186 +0371442S;Ecole Secondaire PrivĂ©e Du Perreux;Chateau Du Perreux;37530;Nazelles Negron;47.4127;0.950414 +0332808Y;Ecole Secondaire PrivĂ©e Dwight Moody;247 Boulevard Marechal Leclerc;33000;Bordeaux;44.8279564;-0.5935838 +0911496G;Ecole Secondaire PrivĂ©e E.s.e.d.;88 Rue De La Division Leclerc;91160;Saulx Les Chartreux;48.6897758;2.2623625 +0681792D;Ecole Secondaire PrivĂ©e Ecole Mathias Grunewald;4 Rue Herzog;68124;Wintzenheim;48.0854575;7.3187032 +0061117G;Ecole Secondaire PrivĂ©e Institut Mediterraneen D'etude;21 Rue Meyerbeer;06000;Nice;43.6967782;7.2605294 +0881710F;Ecole Secondaire PrivĂ©e Institution St Dominique;184 Rue Du Clos Mariotte;88460;La Baffe;48.1596058;6.576138 +0831326R;Ecole Secondaire PrivĂ©e Institution St Joseph;269 Avenue Alphonse Daudet;83300;Draguignan;43.5415395;6.4538543 +0061393G;Ecole Secondaire PrivĂ©e International School Of Nice;15 Avenue Claude Debussy;06200;Nice;43.6854715;7.1997893 +0332919U;Ecole Secondaire PrivĂ©e Jacques Prevert;173 Rue Du Jardin Public;33300;Bordeaux;44.8562027;-0.5732713 +0781902U;Ecole Secondaire PrivĂ©e Jeanne D'arc;35 Rue Remilly;78000;Versailles;48.8115151;2.1400778 +0110840D;Ecole Secondaire PrivĂ©e La Clarte-dieu;Le Cammazou;11270;Fanjeaux;43.195433;2.049444 +0573122J;Ecole Secondaire PrivĂ©e L'etoile Du Matin;112 Route De Waldeck;57230;Eguelshardt;49.0251882;7.5131059 +0060692V;Ecole Secondaire PrivĂ©e LycĂ©e PrivĂ© Michelet;48 Rue Gioffredo;06000;Nice;43.6992502;7.272996 +0950757G;Ecole Secondaire PrivĂ©e Montaigne;2-4 Rue Etienne Fourmont;95220;Herblay;48.9916461;2.1607171 +0061392F;Ecole Secondaire PrivĂ©e Mougins School;615 Avenue Docteur Donat;06250;Mougins;43.6112691;7.0172605 +0022006V;Ecole Secondaire PrivĂ©e Notre Dame Des Victoires;Rue Du Chateau;02120;Le Herie La Vieville;49.824039;3.647624 +0332601Y;Ecole Secondaire PrivĂ©e Notre Dame Du Rosaire;Les Cordeliers;33490;St Macaire;44.561713;-0.231533 +0061229D;Ecole Secondaire PrivĂ©e Or Torah;2 Av Villebois Mareuil;06000;Nice;43.7096465;7.272545 +0572029W;Ecole Secondaire PrivĂ©e Pilatre De Rozier;6 Rue De L'ermitage;57160;Lessy;49.1192846;6.0963774 +0110912G;Ecole Secondaire PrivĂ©e Saint Joseph Des Carmes;Les Carmes;11290;Montreal;43.2145699;2.1848932 +0922221P;Ecole Secondaire PrivĂ©e St Bernard;1 Pl Des Trois Freres Rocquigny;92400;Courbevoie;48.8965949;2.2595784 +0782131T;Ecole Secondaire PrivĂ©e St Dominique;18 20 Avenue Charles De Gaulle;78230;Le Pecq;48.8874253;2.1020683 +0360671J;Ecole Secondaire PrivĂ©e St Michel;5 Rue Du Chateau;36250;Niherne;46.8363903;1.5639221 +0920930L;Ecole Secondaire PrivĂ©e St Pie X;19 Rue Des Ecoles;92210;St Cloud;48.8417127;2.2178743 +0561404X;Ecole Secondaire PrivĂ©e St Thomas D'aquin;Pontcalec;56240;Berne;47.99546;-3.393066 +0352236R;Ecole Secondaire PrivĂ©e Ste Marie;Le Bois Martin;35430;St Pere;48.5885182;-1.9175387 +0910844Y;Ecole Secondaire Prof PrivĂ©e Mg Formation - Escade;98 Allee Des Champs Elysees;91042;Evry;48.63019;2.4243841 +0021486E;Ecole Secondaire Prof.privee;3 Rue De La Mairie;02850;Courtemont Varennes;49.054245;3.5755585 +0910847B;Ecole Secondaire Prof.privee Air-france;Centre D Instruction Vilgenis;91300;Massy;48.730756;2.27137 +0060810Y;Ecole Secondaire Prof.privee Balzac;53 Boulevard Carnot;06400;Cannes;43.5586532;7.0166352 +0110063J;Ecole Secondaire Prof.privee Beaute Et Coiffure;10 Rue Du Luxembourg;11100;Narbonne;43.1809088;3.0021156 +0133404E;Ecole Secondaire Prof.privee C.n.a.t. (coiffure Esth.);Rue Paul Marcel Les Rayettes;13500;Martigues;43.404811;5.053728 +0512015N;Ecole Secondaire Prof.privee Capucine;28 Bis Rue De Courcelles;51100;Reims;49.2603658;4.0195699 +0780026E;Ecole Secondaire Prof.privee Ccip;25 Avenue Des Robaresses;78570;Andresy;48.9871366;2.0579322 +0711717F;Ecole Secondaire Prof.privee Chatelet Coiffure;6 Et 15 Rue St Georges;71100;Chalon Sur Saone;46.7818229;4.8552658 +0061637X;Ecole Secondaire Prof.privee Coiffure Ecole Balzac;28 Boulevard Carabacel;06000;Nice;43.7044827;7.2738384 +0133514Z;Ecole Secondaire Prof.privee Coiffure Esthetique A.f.q.a.;1 Rue Oswald Ortis;13500;Martigues;43.4018692;5.0578334 +0022026S;Ecole Secondaire Prof.privee Coiffure Et Esthetique;19 Bis Boulevard Leon Blum;02100;St Quentin;49.8412959;3.292847 +0133229P;Ecole Secondaire Prof.privee Coiffure Et Esthetique;3 Impasse De L Escouniere;13127;Vitrolles;43.450964;5.2466691 +0332588J;Ecole Secondaire Prof.privee Coiffure Et Esthetique;161 Rue Guillaume Leblanc;33000;Bordeaux;44.8312081;-0.5964205 +0831412J;Ecole Secondaire Prof.privee Coiffure Et Esthetique;1 Rue D Entraigues;83170;Brignoles;43.4059946;6.0612509 +0831393N;Ecole Secondaire Prof.privee Coiffure Performances;80 Avenue Marechal Foch;83000;Toulon;43.1265235;5.9242973 +0060701E;Ecole Secondaire Prof.privee Cours Azur;56 Av Marechal Gallieni;06400;Cannes;43.5586412;7.0176078 +0142159W;Ecole Secondaire Prof.privee Ctre Formation Stade Malherbe;23 Boulevard Georges Pompidou;14064;Caen;49.1808568;-0.3974758 +0371413K;Ecole Secondaire Prof.privee Ctre Promotion De La Coiffure;4 Rue Rene Besnard;37000;Tours;47.3891021;0.699764 +0731207R;Ecole Secondaire Prof.privee Ctre Tech Hotel L'arlequin;Route Nationale 6;73802;Montmelian;45.5068086;6.0799376 +0021592V;Ecole Secondaire Prof.privee D Hotellerie-dosnon;2 Rue De L Eglise;02220;Couvrelles;49.3389541;3.4900193 +0781692R;Ecole Secondaire Prof.privee De Coiffure Des Yvelines;Rue A Honegger Rce Des Forets;78100;St Germain En Laye;48.898908;2.093761 +0110958G;Ecole Secondaire Prof.privee De Prothese Dentaire;2 Rue Simon Castan;11100;Narbonne;43.1849396;2.9975438 +0110981G;Ecole Secondaire Prof.privee D'esthetique;10 Rue Du Luxembourg;11100;Narbonne;43.1809088;3.0021156 +0340927Y;Ecole Secondaire Prof.privee Duclaux;4 Avenue Georges Clemenceau;34000;Montpellier;43.6049943;3.8743094 +0190070Y;Ecole Secondaire Prof.privee Ec Application Travaux Pub;Av Des Papes Limousins;19300;Egletons;45.40101;2.0529799 +0331557N;Ecole Secondaire Prof.privee Ec Tech Esthetique;1 Allee De Chartres;33000;Bordeaux;44.8474789;-0.5712593 +0672682B;Ecole Secondaire Prof.privee Ec.privee Carrieres De La Mode;1 Bis Rue De La Course;67000;Strasbourg;48.5832604;7.7377959 +0332720C;Ecole Secondaire Prof.privee Ec.tech.privee Coiffure;71 Crs Anatole France;33000;Bordeaux;44.8350088;-0.587269 +0641807S;Ecole Secondaire Prof.privee Ecole Coiffure Centre Jorlis;Centre Jorlis;64600;Anglet;43.481402;-1.514699 +0840087N;Ecole Secondaire Prof.privee Ecole Des Arts De La Coiffure;6 Boulevard Saint Michel;84000;Avignon;43.942479;4.8093831 +0061766M;Ecole Secondaire Prof.privee Ecole Europeenne Esthetique;23 Boulevard Dubouchage;06000;Nice;43.7017687;7.2716067 +0681616M;Ecole Secondaire Prof.privee Ecole Vendome;5 Avenue De Colmar;68200;Mulhouse;47.7504281;7.3386179 +0381725U;Ecole Secondaire Prof.privee Elag;31 Rue De La Bajatiere;38100;Grenoble;45.1792916;5.7385752 +0301527X;Ecole Secondaire Prof.privee Espace Robert Bourgier;65 Avenue Jean Jaures;30900;Nimes;43.8276135;4.3533655 +0660756N;Ecole Secondaire Prof.privee Esthet.coiffure Giorgifont Ii;24 24bis Avenue Louis Torcatis;66000;Perpignan;42.7040138;2.890867 +0190834D;Ecole Secondaire Prof.privee Esthetique;22 Rue Andre Devaux;19100;Brive La Gaillarde;45.159555;1.533937 +0341651K;Ecole Secondaire Prof.privee Esthetique Coiffure Giorgifont;16 18 Rue Durand;34000;Montpellier;43.6048143;3.8783242 +0641900T;Ecole Secondaire Prof.privee Esthetique Cosmetique;5bis Et 45 Rue Marechal Joffre;64000;Pau;43.2952632;-0.3731234 +0870913V;Ecole Secondaire Prof.privee Esthetique Cosmetique;2 Bis Rue Gustave Nadaud;87000;Limoges;45.8351859;1.2538931 +0110820G;Ecole Secondaire Prof.privee Esthetique Cosmetique Cybele;8 Boulevard Commandant Roumens;11000;Carcassonne;43.2104305;2.3544704 +0371411H;Ecole Secondaire Prof.privee Etc Esthetique;49 Et 51 Rue Laponneraye;37000;Tours;47.3819579;0.6901009 +0340922T;Ecole Secondaire Prof.privee Etec Formatep;24 26 Avenue Du Pont Juvenal;34000;Montpellier;43.6062402;3.8851375 +0671634M;Ecole Secondaire Prof.privee Europeenne De Beaute;3 Rue De Turenne;67300;Schiltigheim;48.6126471;7.7301404 +0133218C;Ecole Secondaire Prof.privee Football Club;31 Chemin De Paradis;13500;Martigues;43.4077601;5.0489976 +0641911E;Ecole Secondaire Prof.privee Formation Coiffure;Rue Louis Barthou;64000;Pau;43.2944096;-0.3675756 +0211836G;Ecole Secondaire Prof.privee Futura;6 Rue Du Cap Vert;21800;Quetigny;47.3105834;5.0934395 +0382818G;Ecole Secondaire Prof.privee Grenoble Football 38;18 Chemin De La Poterne;38100;Grenoble;45.1726906;5.7475301 +0332868N;Ecole Secondaire Prof.privee I.p.s.o.;77 Rue Lecocq;33000;Bordeaux;44.834317;-0.5862338 +0370759Z;Ecole Secondaire Prof.privee I.s.t.;8 Rue Du Cygne;37000;Tours;47.3955648;0.6912651 +0951707P;Ecole Secondaire Prof.privee Igesa;23 Rue Du General Leclerc;95780;La Roche Guyon;49.0823097;1.6318458 +0131518E;Ecole Secondaire Prof.privee Igesec Ipec;64 Rue Seneque;13300;Salon De Provence;43.6422595;5.0935196 +0190071Z;Ecole Secondaire Prof.privee Inst Brivis Sup Adm Et Commerc;1 Rue Ernest Rupin;19100;Brive La Gaillarde;45.161126;1.5284506 +0831554N;Ecole Secondaire Prof.privee Inst Var Form Metiers Football;Avenue Aristide Briand;83200;Toulon;43.1263887;5.8994285 +0040496T;Ecole Secondaire Prof.privee Institut Avenir Provence;456 Bd St Joseph Z.i.st Joseph;04100;Manosque;43.835744;5.790916 +0542235L;Ecole Secondaire Prof.privee Institut Superieur Decoration;12 Rue De Tivoli;54400;Longwy;49.5293194;5.7649789 +0541921V;Ecole Secondaire Prof.privee Int Esthetique Haute Coiffure;19 Rue Saint Lambert;54000;Nancy;48.687104;6.1665792 +0131426E;Ecole Secondaire Prof.privee Jeanne Michaud;5 Rue Pisancon;13001;Marseille 01;43.2940239;5.3780345 +0370755V;Ecole Secondaire Prof.privee La Chaumette;39 Rue De La Chaumette;37304;Joue Les Tours;47.3579712;0.6500568 +0840090S;Ecole Secondaire Prof.privee Les Cigales;670 Rue Meyne Claire;84100;Orange;44.127041;4.8278237 +0741410F;Ecole Secondaire Prof.privee Maestris Beaute;2 Bis Avenue Zanaroli;74600;Seynod;45.8947154;6.1133921 +0830120E;Ecole Secondaire Prof.privee Objectif 3;154 Avenue Philippe Lebon;83000;Toulon;43.1238546;5.9401956 +0930960N;Ecole Secondaire Prof.privee Ort;39-45 Rue Raspail;93107;Montreuil;48.8528323;2.424645 +0100072Z;Ecole Secondaire Prof.privee Pariset;1 Rue Victorien Sardou;10000;Troyes;48.3001866;4.0506345 +0133211V;Ecole Secondaire Prof.privee Prothesistes Dentaires;11 Av Du General Brosset;13009;Marseille 09;43.2660279;5.4062018 +0341882L;Ecole Secondaire Prof.privee Ruffel;5 Rue Gabriel Peri;34200;Sete;43.4052616;3.6958817 +0341123L;Ecole Secondaire Prof.privee Sarl La Coquille;Route De Boujan;34500;Beziers;43.3663036;3.2315349 +0030867B;Ecole Secondaire Prof.privee Soins Esthetiques;3 Rue President Roosevelt;03200;Vichy;46.1245309;3.4216715 +0332935L;Ecole Secondaire Prof.privee Solange Lourie;8 Cours 30 Juillet;33000;Bordeaux;44.8432096;-0.5743866 +0331597G;Ecole Secondaire Prof.privee St Francois Xavier;181 Rue St F Xavier;33173;Gradignan;44.7753179;-0.5896844 +0341691D;Ecole Secondaire Prof.privee Sup Odontologique;13 Rue Claude Chappe;34000;Montpellier;43.6108708;3.9010094 +0780745L;Ecole Secondaire Tech.privee La Maison;1 Rue Louis Massotte;78530;Buc;48.7737404;2.1264434 +0753574C;Ecole Speciale De Mecanique Et D'electricite;4 Rue Blaise Desgoffe;75006;Paris 06;48.8459266;2.3242783 +0560068V;Ecole Speciale Militaire De Saint Cyr;St Cyr Coetquidan;56381;Guer;47.9394226;-2.1330897 +0772219U;Ecole Sup Infor Et Genie Telec;1 Rue Du Port De Valvins;77215;Avon;48.4247422;2.7446464 +0171523N;Ecole Sup. Innov. Concep. Simu Esics Formation;3 R Alfred Kastler Les Minimes;17000;La Rochelle;46.160329;-1.151139 +0180888T;Ecole Sup.esthetique Du Centre Estec;8d Cours Des Jacobins;18000;Bourges;47.0825143;2.3953601 +0492246A;Ecole Superieure Angevine D'informatique Et De Productique;18 Rue Du 8 Mai 1945;49180;St Barthelemy D Anjou;47.4636468;-0.4970921 +0753560M;Ecole Superieure D Informatique Electronique Automatique;9 Rue Vesale;75005;Paris 05;48.8379228;2.352546 +0490072M;Ecole Superieure D'agriculture D'angers;55 Rue Rabelais;49007;Angers;47.4586985;-0.5450618 +0310154Z;Ecole Superieure D'agriculture De Purpan Toulouse;75 Voie Du Toec;31076;Toulouse;43.6019615;1.4036581 +0951803U;Ecole Superieure De Chimie Organique Et Minerale Escom;13 Boulevard De L'hautil;95092;Cergy;49.03288;2.0788717 +0693623N;Ecole Superieure De Chimie Physique Electronique De Lyon;43 Bd Du 11 Novembre 1918;69616;Villeurbanne;45.7791704;4.8728736 +0920672F;Ecole Superieure De Fonderie;12 Rue Berthelot La Defense;92400;Courbevoie;48.8978987;2.2180596 +0595714R;Ecole Superieure De Metrologie;941 Rue Charles Bourseul;59508;Douai;50.3757844;3.0671778 +0753429V;Ecole Superieure De Physique Et De Chimie Industrielles;10 Rue Vauquelin;75005;Paris 05;48.8413208;2.3477955 +0011293A;Ecole Superieure De Plasturgie D'oyonnax;85 Rue Henri Becquerel;01100;Bellignat;46.242107;5.629371 +0351781W;Ecole Superieure D'electricite;Av De La Boulaie;35511;Cesson Sevigne;48.1230581;-1.6253834 +0911494E;Ecole Superieure D'electricite PrivĂ©e Supelec;Plateau Du Moulon;91192;Gif Sur Yvette;48.7082355;2.1628765 +0573492L;Ecole Superieure D'electricite Supelec De Metz;2 Rue Edouard Belin;57078;Metz;49.1046825;6.2201426 +0490075R;Ecole Superieure D'electronique De L'ouest;4 Rue Merlet De La Boulaye;49009;Angers;47.478419;-0.563166 +0642012P;Ecole Superieure Des Affaires Groupe Esa Ipa;Rue Jean Zay;64000;Pau;43.3185461;-0.3226804 +0721575W;Ecole Superieure Des Geometres Topographes;1 Rue Pythagore;72000;Le Mans;48.0181117;0.1551443 +0941875C;Ecole Superieure Des Industries Du Caoutchouc;60 Rue Auber;94408;Vitry Sur Seine;48.8026632;2.3746261 +0880077F;Ecole Superieure Des Industries Textiles D'epinal;85 Rue D'alsace;88025;Epinal;48.166412;6.4466764 +0762378X;Ecole Superieure D'ingenieurs En Genie Electrique;1 Rue Du Marechal Juin;76131;Mont St Aignan;49.4630688;1.0638285 +0910725U;Ecole Superieure D'optique;Ctre Scientifique Bat 503;91403;Orsay;48.7068033;2.1741725 +0672881T;Ecole Superieure D'optique;26 Rue Des Magasins;67000;Strasbourg;48.591175;7.7408775 +0442278M;Ecole Superieure Du Bois;Rue Christian Pauc;44306;Nantes;47.2828202;-1.5157572 +0932078D;Ecole Superieure Du Soudage Et De Ses Applications;90 Rue Des Vanesses;95942;Roissy En France;48.9736945;2.5064275 +0492202C;Ecole Superieure Et D Application Du Genie;106 Rue Elbe;49041;Angers;47.4558692;-0.5656686 +0351842M;Ecole Superieure Et D'application Des Transmissions;Avenue De La Touraudais;35510;Cesson Sevigne;48.1212814;-1.6302067 +0921929X;Ecole Superieure PrivĂ©e;3 Rue Jules Verne;92300;Levallois Perret;48.894939;2.2989649 +0133885C;Ecole Superieure PrivĂ©e Studio M;29 Boulevard Charles Nedelec;13003;Marseille 03;43.3031966;5.3785285 +0312745R;Ecole Tech Priv D'esthetique Esther Mario;28 Rue De Metz;31000;Toulouse;43.6002386;1.4434844 +0561654U;Ecole Tech PrivĂ©e Coif Estheti Scotto Di Cesare;2 Allee De Laroiseau;56000;Vannes;47.668463;-2.7838366 +0332918T;Ecole Tech PrivĂ©e Creasud;8 Rue Du General Cheyron;33100;Bordeaux;44.8398802;-0.5563023 +0292186U;Ecole Techn PrivĂ©e De Coiffure Chantal Conan;59 Rue De Brest;29800;Landerneau;48.4509184;-4.2574215 +0731479L;Ecole Technique De Coiffure E.c.m. Chambery;357 Faubourg Montmelian;73000;Chambery;45.5663336;5.9304253 +0501515A;Ecole Technique Preparatoire;Ec Tech Armt De La Marine;50100;Cherbourg Octeville;49.633998;-1.613426 +0781858W;Ecole Technique PrivĂ© Iscg De Commerce Et De Gestion;76 Rue Du Marechal Lyautey;78100;St Germain En Laye;48.8949513;2.0959937 +0290204P;Ecole Technique PrivĂ©e Academie Bretagne Coiffure;52 Ter Rue De Douarnenez;29000;Quimper;47.9990273;-4.110778 +0383053M;Ecole Technique PrivĂ©e Academy;21 Rue Boucher De Perthes;38000;Grenoble;45.1842117;5.7060419 +0271601U;Ecole Technique PrivĂ©e Alm Evreux Basket;1 Avenue Aristide Briard;27040;Evreux;49.0270129;1.151361 +0292261A;Ecole Technique PrivĂ©e Anaho Pigier;51 Rue Traverse;29200;Brest;48.3868325;-4.4923533 +0561862V;Ecole Technique PrivĂ©e Anaho Pigier;87 Rue De Lanveur;56100;Lorient;47.7439993;-3.3914865 +0541973B;Ecole Technique PrivĂ©e Asnl;Parc De Haye;54840;Velaine En Haye;48.704136;6.069881 +0352178C;Ecole Technique PrivĂ©e Association Odorico;6 Rue Du Moulin Du Comte;35000;Rennes;48.1084383;-1.7099738 +0530074T;Ecole Technique PrivĂ©e Beaute Coiffure Formation;38-40 Rue De Paris;53000;Laval;48.0712391;-0.7628592 +0261436V;Ecole Technique PrivĂ©e Centre De Formation De La Coif;25 Rue Frederic Chopin;26000;Valence;44.9258887;4.9229792 +0791083Y;Ecole Technique PrivĂ©e Chamois Niortais Football Club;66 Rue H Sellier;79001;Niort;46.3166545;-0.4930179 +0350805K;Ecole Technique PrivĂ©e Chantal Le Cozic;52 Bd Villebois Mareuil;35000;Rennes;48.105558;-1.6507469 +0430988R;Ecole Technique PrivĂ©e Coiffure Du Velay;99 Avenue Charles Dupuy;43700;Brives Charensac;45.0487048;3.9209091 +0541361L;Ecole Technique PrivĂ©e Compagnie De Formation-pigier;43 Cours Leopold;54000;Nancy;48.6972591;6.1737517 +0281111F;Ecole Technique PrivĂ©e De La Coiffure;15 Rue D'etampes;28000;Chartres;48.4471028;1.5129974 +0602084P;Ecole Technique PrivĂ©e Des Jeunes Sportifs De L'oise;171 Avenue Marcel Dassault;60000;Beauvais;49.4489162;2.0963607 +0811312L;Ecole Technique PrivĂ©e Des Lgt Et Lp PrivĂ©s Notre Dam;Avenue D'hauterive;81101;Castres;43.599188;2.244541 +0312711D;Ecole Technique PrivĂ©e Du Stade Toulousain;114 Rue Des Troenes;31022;Toulouse;43.620776;1.416174 +0672119P;Ecole Technique PrivĂ©e Ec Internationle Tunon;15 Rue Du Fosse Des Treize;67000;Strasbourg;48.5879967;7.7473844 +0060907D;Ecole Technique PrivĂ©e Ec Tech D'esthetique Gontard;47 Rue Hotel Des Postes;06000;Nice;43.6991792;7.2705291 +0331579M;Ecole Technique PrivĂ©e Ec Tech Jamet Buffereau;67 Cours Pasteur;33800;Bordeaux;44.8326055;-0.5738464 +0641386J;Ecole Technique PrivĂ©e Ec.d Esthetique;80 Rue De Madrid;64201;Biarritz;43.4673441;-1.5713779 +0261286G;Ecole Technique PrivĂ©e Epseco;Le Forum 7 Avenue De Verdun;26000;Valence;44.9382727;4.8994976 +0131515B;Ecole Technique PrivĂ©e Esthetique Parfumerie;149 Rue De Rome;13006;Marseille 06;43.2889789;5.3821494 +0382050X;Ecole Technique PrivĂ©e Faugier Hays;38 Rue D'alembert;38000;Grenoble;45.1880988;5.7110881 +0881383A;Ecole Technique PrivĂ©e Formapole-epinal;19 Rue Paul Oulmont;88005;Epinal;48.163398;6.4506308 +0710134K;Ecole Technique PrivĂ©e Fpt-powertrain Technologies Fr;79, Avenue Puzenat;71140;Bourbon Lancy;46.6261932;3.7495169 +0161183Z;Ecole Technique PrivĂ©e Hair Styl'mak'up Academy;8 Route De Bordeaux;16000;Angouleme;45.6522967;0.1584877 +0352550G;Ecole Technique PrivĂ©e Iffdec;8 Quai Robinot De St Cyr;35000;Rennes;48.1055424;-1.717577 +0060777M;Ecole Technique PrivĂ©e Ilec;12 Boulevard Dubouchage;06000;Nice;43.702245;7.2722329 +0060818G;Ecole Technique PrivĂ©e Ilec;10 Rue Mozart;06400;Cannes;43.5581488;7.0181123 +0580083A;Ecole Technique PrivĂ©e Institut Superieur Techniciens;5 Rue Des Francs Bourgeois;58000;Nevers;46.9912616;3.1626878 +0571998M;Ecole Technique PrivĂ©e Isct;2 Rue Des Parmentiers;57000;Metz;49.1159983;6.177431 +0451468B;Ecole Technique PrivĂ©e Loiret Orleans Judo;Rue Fernand Pelloutier;45016;Orleans;47.920072;1.888645 +0261392X;Ecole Technique PrivĂ©e Maestris;19 Avenue Victor Hugo;26000;Valence;44.9294864;4.890829 +0830662U;Ecole Technique PrivĂ©e Maestris;10 Rue Truguet;83000;Toulon;43.1250211;5.9349305 +0631039M;Ecole Technique PrivĂ©e Michelin;15 Place Des Carmes;63040;Clermont Ferrand;45.7767324;3.0881041 +0561854L;Ecole Technique PrivĂ©e Morriss 56;1 Bis Rue Francois D'argouges;56000;Vannes;47.6620426;-2.7567142 +0891118G;Ecole Technique PrivĂ©e Moulin De Preuilly;Route De Vaux;89000;Auxerre;47.7833102;3.593685 +0301500T;Ecole Technique PrivĂ©e Nimes Olympique Association;123 Avenue De La Bouvine;30023;Nimes;43.8173886;4.358485 +0292208T;Ecole Technique PrivĂ©e Oscelorn;59 Rue De Brest;29800;Landerneau;48.4509184;-4.2574215 +0382170C;Ecole Technique PrivĂ©e Paul Louis Merlin;41 Rue Henri Wallon;38400;St Martin D Heres;45.1738193;5.7608852 +0383347G;Ecole Technique PrivĂ©e Pigier;5 Avenue Marcellin Berthelot;38000;Grenoble;45.1799156;5.7316907 +0381730Z;Ecole Technique PrivĂ©e Pro' Style Formation;2 Rue Henri Chatelier;38000;Grenoble;45.1872052;5.7117155 +0783465T;Ecole Technique PrivĂ©e Psa Peugeot Citroen Et3pc;1 A 3 Bd De L Europe;78300;Poissy;48.9347739;2.0451355 +0660655D;Ecole Technique PrivĂ©e Rive Gauche;11 Rue Du Docteur Pous;66000;Perpignan;42.6992586;2.889932 +0340937J;Ecole Technique PrivĂ©e Ruffel;Route Nationale 113;34290;Servian;43.397764;3.3692659 +0133369S;Ecole Technique PrivĂ©e Saint Francois De Sales;20 Bd Madeleine Remuzat;13384;Marseille 13;43.296482;5.36978 +0131499J;Ecole Technique PrivĂ©e Soins Esthet Et Coiffure;1 Boulevard Dugommier;13001;Marseille 01;43.2976149;5.3808487 +0341793P;Ecole Technique PrivĂ©e Studio M;3320 Boulevard Paul Valery;34070;Montpellier;43.5907556;3.8548311 +0132309P;Ecole Technique PrivĂ©e Susini Esthetique;Le California Bat D;13090;Aix En Provence;43.5388336;5.4044268 +0382896S;Ecole Technique PrivĂ©e Univeria;27 Rue De Turenne;38100;Grenoble;45.1838167;5.720223 +0711856G;Ecole Technique PrivĂ©e Univeria;71 Rue Jean Mace;71000;Macon;46.3163972;4.8245756 +0383383W;Ecole Technologique PrivĂ©e;13 Place Du Triforium;38080;L Isle D Abeau;45.6208808;5.2221078 +0741692M;Ecole Technologique PrivĂ©e;166 Route De Livron;74100;Vetraz Monthoux;46.1744216;6.2574083 +0731532U;Ecole Technologique PrivĂ©e Agfp;25 Rue Croix D'or;73000;Chambery;45.5647917;5.9239496 +0693401X;Ecole Technologique PrivĂ©e Arts Appliques Bellecour;3 Place Bellecour;69002;Lyon 02;45.7575832;4.8320746 +0750106H;Ecole Technologique PrivĂ©e Ccip Ctre Format Industrielles;247 Avenue Gambetta;75020;Paris 20;48.8755533;2.4057617 +0383349J;Ecole Technologique PrivĂ©e Charmilles Isf;8 Rue Du Tour De L Eau;38400;St Martin D Heres;45.1865579;5.7726517 +0651026M;Ecole Technologique PrivĂ©e Concept Formation;54 Avenue Bertrand Barere;65000;Tarbes;43.2373153;0.0712894 +0693451B;Ecole Technologique PrivĂ©e De Conde;5 Place Gensoul;69002;Lyon 02;45.7517842;4.8243867 +0740283F;Ecole Technologique PrivĂ©e De Poisy;;74330;Poisy;45.921423;6.063557 +0061542U;Ecole Technologique PrivĂ©e E.s.c.c.o.m.;22 Rue El Nouzah;06000;Nice;43.7077627;7.2773541 +0754101A;Ecole Technologique PrivĂ©e Ec Dessin Techn Artist Sornas;108 Rue Saint Honore;75001;Paris 01;48.8615803;2.3418634 +0931906S;Ecole Technologique PrivĂ©e Ecofih;79 Rue De Paris;93000;Bobigny;48.898946;2.4435126 +0840083J;Ecole Technologique PrivĂ©e Ecole Hoteliere D Avignon;Allee Des Fenaisons;84032;Avignon;43.9272966;4.8473351 +0596455W;Ecole Technologique PrivĂ©e Ecole Superieure De Tourisme;70 Rue De Bouvines;59000;Lille;50.6356752;3.0878986 +0160080A;Ecole Technologique PrivĂ©e Estac;29 Rue Montalembert;16000;Angouleme;45.6465899;0.1677237 +0831631X;Ecole Technologique PrivĂ©e Esthetique Veronique Harlaut;146 Boulevard Marechal Foch;83000;Toulon;43.1268203;5.9234125 +0763365V;Ecole Technologique PrivĂ©e Fc Rouen;48 Avenue Des Canadiens;76140;Le Petit Quevilly;49.4115983;1.072111 +0312346G;Ecole Technologique PrivĂ©e Gamma;1 Rue Felix Debax;31700;Blagnac;43.6323187;1.3975413 +0596770N;Ecole Technologique PrivĂ©e I.s.c.o.m;7 Av De L'architecte Cordonnier;59000;Lille;50.6355544;3.0440254 +0595877T;Ecole Technologique PrivĂ©e Institut De Gestion Hoteliere;17 Place Charles Roussel;59200;Tourcoing;50.7215161;3.1568082 +0062035E;Ecole Technologique PrivĂ©e International Business School;1240 Route Des Dolines;06560;Valbonne;43.6252096;7.0403607 +0061992H;Ecole Technologique PrivĂ©e Ispecc;21 Boulevard Grosso;06000;Nice;43.6958857;7.2516559 +0731527N;Ecole Technologique PrivĂ©e Itcc;Place Maurice Mollard;73100;Aix Les Bains;45.688374;5.915667 +0755057P;Ecole Technologique PrivĂ©e Itecom Inst Techn Communic;12 Rue Du 4 Septembre;75002;Paris 02;48.8695094;2.3374631 +0593035D;Ecole Technologique PrivĂ©e Kienz;39 Avenue De Flandre;59700;Marcq En Baroeul;50.6653766;3.1073796 +0133283Y;Ecole Technologique PrivĂ©e Leschi;34 Avenue Sainte Victoire;13100;Aix En Provence;43.5320321;5.4547984 +0383350K;Ecole Technologique PrivĂ©e Lodima Rhone Alpes;25 Rue De Sassenage;38600;Fontaine;45.1974483;5.6830524 +0752877V;Ecole Technologique PrivĂ©e Met De La Creation-la Ruche;14 Rue Lally Tollendal;75019;Paris 19;48.8830729;2.3742869 +0062012E;Ecole Technologique PrivĂ©e Orbicom;2780 Route Nationale 7;06270;Villeneuve Loubet;43.6253366;7.131598 +0311189Z;Ecole Technologique PrivĂ©e Ort Maurice Grynfogel;14 Rue Etienne Collongues;31770;Colomiers;43.601951;1.333155 +0383393G;Ecole Technologique PrivĂ©e Performances Concept;;38590;St Etienne De St Geoirs;45.339389;5.34384 +0711884M;Ecole Technologique PrivĂ©e Pigier;33 Rue De Lyon;71000;Macon;46.3002379;4.8268272 +0741690K;Ecole Technologique PrivĂ©e Pigier;2 Rue De La Cesiere;74600;Seynod;45.8868104;6.1117354 +0783504K;Ecole Technologique PrivĂ©e Sportifs De Haut Niveau;4 Bis Avenue Kennedy;78100;St Germain En Laye;48.9146897;2.0854407 +0383369F;Ecole Technologique PrivĂ©e Sup Formation;22 Cours Senozan;38500;Voiron;45.3654833;5.5909482 +0690693D;Ecole Technologique PrivĂ©e Supdemod;10 Rue Des Marronniers;69002;Lyon 02;45.7565575;4.8344883 +0312303K;Ecole Technologique PrivĂ©e Toulouse Arts Appliques;24 Rue Ingres;31000;Toulouse;43.6142705;1.4428243 +0311768D;Ecole Technologique PrivĂ©e Tunon;3 Rue Albert Lautmann;31000;Toulouse;43.6065141;1.4390729 +0590342B;Emd Douai;941 Rue Charles Bourseul;59508;Douai;50.3757844;3.0671778 +0670189S;Engees Strasbourg;1 Quai Koch;67070;Strasbourg;48.584972;7.7580286 +0492248C;Enihp Angers;2 Rue Le Notre;49045;Angers;47.4791574;-0.6058396 +0631786Z;Enita Clermont-ferrand;Rn 89 Marmilhat;63370;Lempdes;45.7796779;3.17838 +0441679L;Enitiaa Nantes;Rue De La Geraudiere;44322;Nantes;47.264745;-1.56533 +0590338X;Ens Arts Indust Textil Roubaix;2 Pl Martyrs De La Resistance;59070;Roubaix;50.6750202;3.1456381 +0670190T;Ens Arts Industries Strasbourg;24 Boulevard De La Victoire;67084;Strasbourg;48.5822995;7.7649338 +0870862P;Ens Ceramique Iindust Limoges;47 A 73 Av Albert Thomas;87065;Limoges;45.8365048;1.2425704 +0951376E;Ens Electroniq Applicat Cergy;6 Av Du Ponceau;95014;Cergy;49.039012;2.0713216 +0693817Z;Ens Lettres Lyon;15 Parvis Rene Descartes;69007;Lyon 07;0.0;0.0 +0311587G;Ensae Toulouse;10 Avenue Edouard Belin;31055;Toulouse;43.5665729;1.4751039 +0330201P;Ensam Centre D Enseignement Et De Recherche De Bordeaux;Esplanade Des Arts & Metiers;33405;Talence;44.802614;-0.588054 +0710091N;Ensam Centre D Enseignement Et De Recherche De Cluny;Rue Porte De Paris;71250;Cluny;46.434167;4.662638 +0510083N;Ensam Cer Chalons;Rue Saint Dominique;51006;Chalons En Champagne;48.9564418;4.3572928 +0573513J;Ensam Cer Metz;4 Rue Augustin Fresnel;57078;Metz;49.0963218;6.2255916 +9711101V;Enseignement Sup. Technique Formates;4, Rue Achille Rene Boisneuf;97110;Pointe A Pitre;0.0;0.0 +0492247B;Enshap Angers;2 Rue Le Notre;49045;Angers;47.4791574;-0.6058396 +0590341A;Ensiaa Cycle D'etudes En Industries Alimentaires;41 Rue Du Port;59046;Lille;50.6319132;3.0461578 +0310146R;Ensica Toulouse;1 Place Emile Blouin;31056;Toulouse;43.604652;1.444209 +0290125D;Ensieta Brest;2 Rue Francois Verny;29806;Guipavas;48.4198889;-4.4721985 +0920815L;Enspmr Rueil Malmaison;228 232 Av Napoleon Bonaparte;92506;Rueil Malmaison;48.8771709;2.1729069 +0692459Y;Enssib;17 21 Bd Du 11 Novembre 1918;69623;Villeurbanne;45.7795294;4.8611273 +0291811L;Enst Bretagne;Technopole Brest Iroise;29285;Brest;48.3651848;-4.5489314 +0352402W;Enst Bretagne;Rue De La Chataigneraie;35512;Cesson Sevigne;48.1209253;-1.6286523 +0300063F;Enstima Ales;6 Avenue De Clavieres;30319;Ales;44.1331218;4.0883578 +0811200P;Enstimac Albi-carmaux;Campus Jarlart Rte De Teillet;81013;Albi;43.9124307;2.1939809 +0442205H;Enstimn Nantes;4 Rue A Kastler La Chantrerie;44307;Nantes;47.218371;-1.553621 +0100735V;Eplefpa Ets Pub Local Enseig Form Prof Agric;Route De Vielaines;10120;St Pouange;48.221263;4.039121 +0641772D;Epseco;15 Rue Vauban;64100;Bayonne;43.4956281;-1.4801189 +0530939H;Esiea Laval;38 Rue Drs Calmette Et Guerin;53000;Laval;48.077863;-0.770138 +0932019P;Esiee Noisy Le Grand;2 Bd Blaise Pascal Cite Descart;93162;Noisy Le Grand;48.848579;2.55261 +0132396J;Esim Marseille;Technopole De Chateau Gombert;13451;Marseille 13;43.3417;5.43649 +0573389Z;Esitc Metz;6 Rue Marconi Technopole 2000;57070;Metz;49.1005629;6.2173198 +0271338H;Esitpa Rouen;Rue Grande;27106;Val De Reuil;49.2721551;1.2138012 +0753607N;Estp Paris;57 Boulevard Saint Germain;75240;Paris 05;48.8504483;2.3463367 +0783529M;Etab Ens Second PrivĂ© Sfef-completude;22 Rue Henri De Regnier;78000;Versailles;48.7934699;2.125999 +0212024L;Etab.national Enseignement Sup;26 Bd Dr Petitjean;21036;Dijon;47.3111605;5.0659413 +0755182A;Etablissement Experimental;48 Avenue Des Gobelins;75013;Paris 13;48.8342909;2.3533682 +0932377D;Etablissement Experimental Auto Ecole;12 Rue De La Liberte;93200;St Denis;48.9438959;2.3653605 +0754401B;Etablissement Experimental LycĂ©e Autogere;393 Rue De Vaugirard;75015;Paris 15;48.8348375;2.2918472 +0772586T;Etablissement Experimental Micro LycĂ©e De Senart;Rue Du LycĂ©e;77552;Moissy Cramayel;0.0;0.0 +0932375B;Etablissement Experimental Nouvelles Chances;25 Rue Des Trois Noyers;93220;Gagny;48.8874111;2.5425454 +0932376C;Etablissement Experimental Nouvelles Chances;146 Av Henri Barbusse;93000;Bobigny;48.9083628;2.42744 +0932396Z;Etablissement Experimental Nouvelles Chances;10 Ave Charles De Gaulle;93152;Le Blanc Mesnil;48.9438125;2.4679399 +0870581J;Ets Public Local Enseignement Agricole Vaseix;Les Vaseix;87430;Verneuil Sur Vienne;45.836771;1.172854 +0762917H;Formavenir Formavenir;28 Place Saint Marc;76000;Rouen;49.4385493;1.1018603 +0341704T;Gpt Interet Public Reclus;17 Rue Abbe De L Epee;34000;Montpellier;43.621583;3.8741726 +0590348H;Hautes Etudes Industrielles;13 Rue De Toul;59046;Lille;50.6339335;3.0449176 +0352422T;I E S I E L;65 Rue De St Brieuc;35042;Rennes;48.1136527;-1.7053288 +0753488J;I.n.a.l.c.o. De Paris;2 Rue De Lille;75343;Paris 07;48.8577011;2.3325963 +0753483D;Inst Fr Froid Ind Genie Climat;292 Rue Saint Martin;75141;Paris 03;48.8667317;2.3542533 +0341013S;Inst Nat Recherche Agronomique;2 Place Pierre Viala;34060;Montpellier;43.6170101;3.8548718 +0332282B;Inst Sces Nature Agroalimentai De Bordeaux;Rue St Jean;33140;Villenave D Ornon;44.795055;-0.5725998 +0753558K;Inst Scien Math Et Econ Appli;14 Rue Corvisart;75013;Paris 13;48.833382;2.346573 +0161165E;Inst Sup Format Alternance Con Isfac;Z I 3 Espace Victor Hugo;16340;L Isle D Espagnac;45.663854;0.199192 +0171501P;Inst Sup Format Alternance Con Isfac;17 Rue Jean Perrin;17000;La Rochelle;46.1405865;-1.1584497 +0520939N;Inst. Rural D'educ. Et D'orien;Rue Du Baron De Beine;52000;Buxieres Les Villiers;48.106388;5.0360569 +0942034A;Inst.nat.rech.transp.securite;2 Av Du Gal Malleret Joinville;94114;Arcueil;48.805173;2.3442021 +0332473J;Instit Rurale Educ Orientation;Domaine De Lagron;33870;Vayres;44.8964799;-0.319015 +0110678C;Institut Agricole PrivĂ© Saint Joseph;Av Andre Chenier La Raque;11303;Limoux;43.053289;2.218142 +0691678Z;Institut Agricole PrivĂ© Sandar;392 Chemin De La Sabliere;69579;Limonest;45.8348979;4.7678854 +0590345E;Institut Catholique D'arts Et Metiers;6 Rue Auber;59046;Lille;50.6302567;3.0417529 +0442185L;Institut Catholique D'arts Et Metiers De Nantes;35 Av Champ De Manoeuvre;44470;Carquefou;47.2748684;-1.5086799 +0312421N;Institut Catholique Des Arts Et Metiers De Toulouse;75 Avenue De Grande Bretagne;31300;Toulouse;43.5999437;1.4151779 +0753431X;Institut D'etudes Politiques De Paris;27 Rue Saint Guillaume;75341;Paris 07;48.854072;2.3283597 +0912141H;Institut D'informatique D'entreprise;18 Allee Jean Rostand;91025;Evry;48.6269332;2.4313463 +0870997L;Institut D'ingenierie Informatique De Limoges;43 Rue Ste Anne;87015;Limoges;45.8189643;1.2712663 +0631833A;Institut FranÇais De Mecanique Avancee De Clermont-ferrand;Campus Des Cezeaux;63175;Aubiere;45.7690647;3.1153321 +0753465J;Institut National Agronomique Paris Grignon;16 Rue Claude Bernard;75005;Paris 05;48.839766;2.347641 +0690192J;Institut National Des Sciences Appliquees De Lyon;20 Avenue Albert Einstein;69621;Villeurbanne;45.7824151;4.8777701 +0350097R;Institut National Des Sciences Appliquees De Rennes;20 Avenue Des Buttes De Coesmes;35043;Rennes;48.1225975;-1.6377774 +0760165S;Institut National Des Sciences Appliquees De Rouen;Place Emile Blondel;76131;Mont St Aignan;49.458089;1.069146 +0310152X;Institut National Des Sciences Appliquees De Toulouse;135 Avenue De Rangueil;31077;Toulouse;43.5703224;1.4679205 +0910685A;Institut National Des Sciences Et Techniques Nucleaires;Centre D Etudes De Saclay;91191;Gif Sur Yvette;48.7099264;2.164881 +0911781S;Institut National Des Telecommunications;9 Rue Fourier;91000;Evry;48.6254354;2.4432663 +0381912X;Institut National Polytechnique De Grenoble;46 Avenue Felix Viallet;38031;Grenoble;45.1907112;5.7174034 +0541564G;Institut National Polytechnique De Nancy;2 Avenue De La Foret De Haye;54501;Vandoeuvre Les Nancy;48.6543335;6.1342988 +0311381H;Institut National Polytechnique De Toulouse;Place Des Hauts Murats;31006;Toulouse;43.5945193;1.4471792 +0352347L;Institut National Superieur De Formation Agro-alimentaire;65 Rue De St Brieuc;35042;Rennes;48.1136527;-1.7053288 +0900384D;Institut Polytechnique De Sevenans;Rue Du Chateau;90010;Belfort;47.6410762;6.8386557 +0763212D;Institut Rural De Hte Normandi Totes;11 Rue Du General Leclerc;76890;Totes;49.6775732;1.050514 +0611136D;Institut Sup De Plasturgie;Montfoulon;61250;Damigny;48.4450915;0.0590041 +0600071B;Institut Superieur Agricole De Beauvais;19 Rue Pierre Waguet;60026;Beauvais;49.4611762;2.0685274 +0753559L;Institut Superieur D Electronique De Paris;28 Rue Notre Dame Des Champs;75006;Paris 06;48.845356;2.328294 +0590343C;Institut Superieur D'agriculture;41 Rue Du Port;59046;Lille;50.6319132;3.0461578 +0692353H;Institut Superieur D'agriculture Rhone Alpes;31 Place Bellecour;69288;Lyon 02;45.7575832;4.8320746 +0292125C;Institut Superieur D'electronique De Bretagne;20 Rue Cuirasse Bretagne;29604;Brest;48.4066763;-4.4959757 +0831458J;Institut Superieur D'electronique De La Mediterranee;Place Georges Pompidou;83000;Toulon;43.120804;5.937864 +0590347G;Institut Superieur D'electronique Du Nord;41 Boulevard Vauban;59046;Lille;50.6342668;3.0487049 +0721484X;Institut Superieur Des Materiaux Du Mans;44 Av Bartholdi;72000;Le Mans;48.0199119;0.1574886 +0930603A;Institut Superieur Des Materiaux Et Construction Mecanique;3, Rue Fernand Hainaut;93430;Villetaneuse;48.9192718;2.3327857 +0061874E;Institut Superieur D'informatique Et D'automatique;Rue Claude Daunesse;06904;Valbonne;43.6156514;7.051952 +0130238N;Institut Superieur Du Beton Arme-ccimp Groupe Esim Imt;Imt Technopole Chateau Gombert;13451;Marseille 13;43.296482;5.36978 +0693364G;Institut Textile Et Chimique De Lyon;87 Chemin Des Mouilles;69134;Dardilly;45.7863735;4.7690268 +0171552V;Int Rur Educat Et Orientation De La Saintonge Et De L Aunis;15 Avenue De Saintes;17240;St Genis De Saintonge;45.4831161;-0.5674628 +0351780V;Ipssa;Les Hairies;35370;Etrelles;48.055495;-1.1761569 +0351949D;Ipssa;12 Avenue Du Mail;35130;La Guerche De Bretagne;47.9402114;-1.2314923 +0351963U;Ipssa;2 Allee De La Hodeyere;35504;Vitre;48.119326;-1.2031957 +0133347T;Ismea Marseille;Technopole De Chateau Gombert;13451;Marseille 13;43.3417;5.43649 +9830289X;Itfm Nouvelle Caledonie;Rue P Sauvan Anse Vata;98807;Noumea;-22.2976224;166.4471083 +0801885P;Iufm;49 Boulevard De Chateaudun;80044;Amiens;49.8826199;2.2835252 +0133393T;Iufm Aix Marseille;32 Rue Eugene Cas;13004;Marseille 04;43.3129449;5.3982588 +9710939U;Iufm Antilles Guyane;Morne Ferret;97159;Pointe A Pitre;16.241111;-61.533056 +0332826T;Iufm Aquitaine;160 Av De Verdun;33705;Merignac;44.8393097;-0.6294005 +0251762E;Iufm Besancon;Fort Griffon;25042;Besancon;47.242863;6.019874 +0142158V;Iufm Caen;186 Rue De La Delivrande;14053;Caen;49.1997374;-0.3587701 +0631821M;Iufm Clermont Ferrand;20 Avenue Raymond Bergougnan;63039;Clermont Ferrand;45.7833384;3.0703968 +7200164R;Iufm Corse;2 Rue De L'eglise;20250;Corte;42.3048203;9.1497992 +0941936U;Iufm Creteil;Route De Brevannes;94388;Bonneuil Sur Marne;48.7656805;2.5144979 +0211960S;Iufm Dijon;Maison Univ.esplanade Erasme;21009;Dijon;47.3141252;5.0663861 +9830491S;Iufm Du Pacifique;15 Rue De Verdun,bp Mga1;98802;Noumea;-22.2758;166.458 +0382955F;Iufm Grenoble;30 Avenue Marcelin Berthelot;38000;Grenoble;45.1763086;5.7317449 +9741061K;Iufm La Reunion;Allee Des Aigues Marines;97487;St Denis;-20.8939781;55.4425462 +0595851P;Iufm Lille;2 Bis Rue Parmentier;59650;Villeneuve D Ascq;50.6399807;3.1349929 +0871012C;Iufm Limoges;209 Bd De Vanteaux;87036;Limoges;45.823034;1.2270492 +0693480H;Iufm Lyon;5 Rue Anselme;69317;Lyon 04;45.77362;4.8210967 +0341818S;Iufm Montpellier;2 Place Marcel Godechot;34092;Montpellier;43.6198978;3.8692638 +0542255H;Iufm Nancy Metz;5 Rue Paul Richard;54320;Maxeville;48.7104439;6.1644578 +0442199B;Iufm Nantes;4 Chemin Launay Violette;44322;Nantes;47.2497938;-1.5579682 +0061758D;Iufm Nice;89 Avenue Georges V;06046;Nice;43.7135641;7.2699222 +0451482S;Iufm Orleans Tours;72 R Du Faubourg De Bourgogne;45044;Orleans;47.9022511;1.9264231 +0754445Z;Iufm Paris;10 Rue Molitor;75016;Paris 16;48.8452554;2.2652208 +0861249R;Iufm Poitiers;22 Rue De La Tranchee;86000;Poitiers;46.5764592;0.3333732 +0511935B;Iufm Reims;23 Rue Clement Ader;51685;Reims;49.2325574;4.0695783 +0352291A;Iufm Rennes;153 Rue De St Malo;35043;Rennes;48.1215596;-1.6831814 +0762952W;Iufm Rouen;2 Rue Du Tronquet;76131;Mont St Aignan;49.4703815;1.0767393 +0672635A;Iufm Strasbourg;200 Avenue De Colmar;67100;Strasbourg;48.558093;7.7482963 +0312299F;Iufm Toulouse;56 Avenue De L'urss;31078;Toulouse;43.5806199;1.4494941 +0781938H;Iufm Versailles;45 Avenue Des Etats Unis;78000;Versailles;48.8106931;2.1485567 +0470817H;L Professionnel Ste Genevieve Ctr.etu.fem.rur.ste Genevieve;19 Rue Felix;47220;Astaffort;44.0641604;0.6531217 +0441783Z;L.e.p.agricole PrivĂ© Lp Agric Pr La Marchanderie;;44150;Ancenis;47.365464;-1.177491 +0180098J;La Compagnie De Formation Pigier;29 Boulevard Gambetta;18000;Bourges;47.0880089;2.3899259 +0311429K;Laboratoire Automatique;7 Avenue Du Colonel Roche;31400;Toulouse;43.5637248;1.4765815 +0311430L;Laboratoire Ctre D'hematologie;Place Du Docteur Baylac;31300;Toulouse;43.6078649;1.3973901 +0311473H;Laboratoire De Biochimie Et Genetique Ce;118 Route De Narbonne;31062;Toulouse;43.5631577;1.4628392 +0311431M;Laboratoire De Pharmacologie Toxicologie;205 Route De Narbonne;31078;Toulouse;43.5564738;1.4656272 +0311274S;Laboratoire D'optique;29 Rue Jeanne Marvig;31055;Toulouse;43.5779576;1.4639799 +0090467J;Laboratoire Souterrain;;09200;Moulis;42.961685;1.091185 +0580055V;Legta De Nevers;243 Route De Lyon;58000;Challuy;46.9475361;3.1579337 +0331493U;Lg Pr Lyc Des Metiers St Genes;160 Rue De Saint Genes;33081;Bordeaux;44.8249282;-0.5813679 +0120024L;Lgt LycĂ©e Des Metiers Alexis Monteil;14 Rue Carnus;12034;Rodez;44.3559366;2.5738937 +0671509B;Lgt LycĂ©e Des Metiers Alphonse Heinrich;123 Route De Strasbourg;67504;Haguenau;48.8001884;7.7656679 +0730029K;Lgt LycĂ©e Des Metiers Ambroise Croizat;244 Avenue De La Liberation;73604;Moutiers;45.4828426;6.5291499 +0601864A;Lgt LycĂ©e Des Metiers Andre Malraux;1 Place Nelson Mandela;60160;Montataire;49.2597525;2.425907 +0060037H;Lgt LycĂ©e Des Metiers Beau Site;38 Avenue D'estienne D'orves;06050;Nice;43.701575;7.245204 +0800001S;Lgt LycĂ©e Des Metiers Boucher De Perthes;1 Rue Paul Delique;80142;Abbeville;50.1023384;1.8422737 +0460010L;Lgt LycĂ©e Des Metiers Champollion;13 15 Av Fernand Pezet;46106;Figeac;44.6090478;2.0281224 +0460007H;Lgt LycĂ©e Des Metiers Clement Marot;59 Rue Des Augustins;46005;Cahors;44.4499879;1.4392957 +0020050U;Lgt LycĂ©e Des Metiers Condorcet;Rond Point Joliot Curie;02100;St Quentin;49.861713;3.293589 +0640044A;Lgt LycĂ©e Des Metiers De La Chimie Albert Camus;Avenue Pierre Angot;64150;Mourenx;43.3703832;-0.6251249 +0570319M;Lgt LycĂ©e Des Metiers De L'industrie;Les Grands Bois;57703;Hayange;49.3168954;6.0671076 +0641779L;Lgt LycĂ©e Des Metiers Du Pays De Soule;Avenue Jean Monnet;64130;Cheraute;43.2265756;-0.8742488 +0570087K;Lgt LycĂ©e Des Metiers Et Des Techno Innov. C. Jully;59 Rue Marechal Foch;57501;St Avold;49.1057612;6.6927566 +0880153N;Lgt LycĂ©e Des Metiers Filiere Bois Andre Malraux;Rue De L'epinette;88204;Remiremont;48.021875;6.5831674 +0020031Y;Lgt LycĂ©e Des Metiers Frederic Et Irene Joliot Curie;1 Place Du Pigeon Blanc;02500;Hirson;49.9200919;4.084192 +0570099Y;Lgt LycĂ©e Des Metiers Henri Nomine;60 R Du Marechal Foch;57215;Sarreguemines;49.1165272;7.0802942 +0470003Y;Lgt LycĂ©e Des Metiers Jean Baptiste De Baudre;5 Allee Pierre Pomarede;47916;Agen;44.203142;0.616363 +0600040T;Lgt LycĂ©e Des Metiers Jean Calvin;Mont Saint Simeon;60400;Noyon;49.580638;3.019981 +0030038A;Lgt LycĂ©e Des Metiers Jean Monnet;39 Place Jules Ferry;03401;Yzeure;46.5660374;3.3552285 +0801853E;Lgt LycĂ©e Des Metiers Jean Racine;541 Rue Pasteur;80500;Montdidier;49.6533011;2.5807174 +0110007Y;Lgt LycĂ©e Des Metiers Jules Fil;Boulevard Joliot Curie;11021;Carcassonne;43.2148527;2.3675002 +0630021F;Lgt LycĂ©e Des Metiers La Fayette;21 Boulevard Robert Schuman;63002;Clermont Ferrand;45.7626229;3.1315173 +0320067Z;Lgt LycĂ©e Des Metiers Le Garros;1 Bis Rue Darwin;32021;Auch;43.6317386;0.5885775 +0260113G;Lgt LycĂ©e Des Metiers Les Catalins;24 Avenue Des Catalins;26216;Montelimar;44.5696163;4.7576998 +0060075Z;Lgt LycĂ©e Des Metiers Les Eucalyptus;7 Avenue Des Eucalyptus;06200;Nice;43.6762904;7.2225405 +0810004P;Lgt LycĂ©e Des Metiers Louis Rascol;10 Rue De La Republique;81012;Albi;43.9318293;2.1542966 +0600020W;Lgt LycĂ©e Des Metiers Marie Curie;Boulevard Pierre De Coubertin;60180;Nogent Sur Oise;49.266627;2.464235 +0730016W;Lgt LycĂ©e Des Metiers Monge;1 Avenue Marius Berroir;73000;Chambery;45.5660699;5.9341975 +0382203N;Lgt LycĂ©e Des Metiers Pablo Neruda;35 Rue Henri Wallon;38400;St Martin D Heres;45.1728602;5.7587385 +0310017A;Lgt LycĂ©e Des Metiers Paul Mathou;Avenue De Luchon;31210;Gourdan Polignan;43.0674443;0.5872643 +0880021V;Lgt LycĂ©e Des Metiers Pierre Mendes France;2 Rue Du Haut Des Etages;88000;Epinal;48.186392;6.455177 +0230025C;Lgt LycĂ©e Des Metiers Raymond Loewy;Place Filderstadt;23300;La Souterraine;46.239087;1.482986 +0630020E;Lgt LycĂ©e Des Metiers Sidoine Apollinaire;20 Rue Jean Richepin;63037;Clermont Ferrand;45.7818105;3.0868666 +0310028M;Lgt LycĂ©e Des Metiers Vincent Auriol;36 Route De Soreze;31250;Revel;43.4563296;2.0116845 +9830537S;Lp Agricole Do Neva (asee-agri);Ecole De Do Neva;98816;Houailou;0.0;0.0 +0672118N;Lp Agricole PrivĂ© Schattenmann;88 Grand'rue;67330;Bouxwiller;48.8272341;7.4782172 +0470823P;Lp Des Metiers Vie Rurale Ctr.etu.fem.rur.l Oustal;Rue Paul Sabatier;47300;Villeneuve Sur Lot;44.393685;0.733397 +0741689J;Lp Leap;Site De Chavanod;74650;Chavanod;45.890053;6.03928 +0330018R;Lp LycĂ©e Des Metiers;24 Rue Du Clg Technique;33294;Blanquefort;44.9122244;-0.6202443 +0410718H;Lp LycĂ©e Des Metiers;2 Avenue Jean Magnon;41110;St Aignan;47.266268;1.3718823 +0801534H;Lp LycĂ©e Des Metiers;541 Rue Pasteur;80500;Montdidier;49.6533011;2.5807174 +0950688G;Lp LycĂ©e Des Metiers;71 Avenue De Ceinture;95880;Enghien Les Bains;48.97192;2.2940159 +0421606T;Lp LycĂ©e Des Metiers Adrien Testud;33 Boulevard D'auvergne;42502;Le Chambon Feugerolles;45.3930502;4.3204203 +0370040T;Lp LycĂ©e Des Metiers Albert Bayet;50 Boulevard Preuilly;37058;Tours;47.3929843;0.6707489 +0010001W;Lp LycĂ©e Des Metiers Alexandre Berard;223 Rue Alexandre Berard;01505;Amberieu En Bugey;45.972793;5.3518732 +0910630R;Lp LycĂ©e Des Metiers Alexandre Denis;Ch Montmirault Av Carnot;91590;Cerny;48.483799;2.3326528 +0120037A;Lp LycĂ©e Des Metiers Alexis Monteil;14 Rue Carnus;12034;Rodez;44.3559366;2.5738937 +0660026V;Lp LycĂ©e Des Metiers Alfred Sauvy;Chateau Lagrange;66740;Villelongue Dels Monts;42.526599;2.9025059 +0050005D;Lp LycĂ©e Des Metiers Alpes Et Durance;Quartier De La Robeyere;05200;Embrun;44.5604975;6.4881374 +0332445D;Lp LycĂ©e Des Metiers Alphonse Beau De Rochas;Rue Jean Hameau;33300;Bordeaux;44.8667377;-0.5677874 +0730030L;Lp LycĂ©e Des Metiers Ambroise Croizat;244 Avenue De La Liberation;73604;Moutiers;45.4828426;6.5291499 +0630024J;Lp LycĂ©e Des Metiers Amedee Gasquet;12 Rue Jean Baptiste Torrilhon;63037;Clermont Ferrand;45.7782828;3.0776281 +0410031L;Lp LycĂ©e Des Metiers Andre Ampere;2 Rue Ampere;41107;Vendome;47.8033433;1.082092 +0573211F;Lp LycĂ©e Des Metiers Andre Citroen;5 Rue De 11eme D'aviation;57155;Marly;49.059828;6.154795 +0601870G;Lp LycĂ©e Des Metiers Andre Malraux;1 Place Nelson Mandela;60160;Montataire;49.2597525;2.425907 +0670024M;Lp LycĂ©e Des Metiers Andre Siegfried;12 Rue Des Dominicains;67504;Haguenau;48.8156319;7.7946128 +0870058R;Lp LycĂ©e Des Metiers Antone De Saint Exupery;Route Du Palais;87000;Limoges;45.8499103;1.2977815 +0772244W;Lp LycĂ©e Des Metiers Antonin Careme;1 Place Gustave Courbet;77176;Savigny Le Temple;48.5955639;2.578176 +0011120M;Lp LycĂ©e Des Metiers Arbez Carme;1 Rue Pierre Et Marie Curie;01100;Bellignat;46.2508923;5.6331189 +0940138P;Lp LycĂ©e Des Metiers Armand Guillaumin;Rue Pierre Corneille;94310;Orly;48.749033;2.404065 +0340069R;Lp LycĂ©e Des Metiers Auto Cycles Moto Jacques Brel;15 Avenue De La Gare;34220;St Pons De Thomieres;43.4879072;2.7645737 +0560001X;Lp LycĂ©e Des Metiers B. Du Guesclin;50 Rue Pierre Allio;56406;Auray;47.6840168;-2.9983391 +0820001F;Lp LycĂ©e Des Metiers Batiment Et Topographie;578 Avenue De Gascogne;82500;Beaumont De Lomagne;43.8767017;0.9780313 +0310053P;Lp LycĂ©e Des Metiers Bayard;150 Route De Launaguet;31021;Toulouse;43.6387232;1.4392188 +0060908E;Lp LycĂ©e Des Metiers Beau Site;38 Avenue Estienne D Orves;06050;Nice;43.7002115;7.2489081 +0350102W;Lp LycĂ©e Des Metiers Beaumont;10 Rue Du LycĂ©e;35605;Redon;0.0;0.0 +0770943G;Lp LycĂ©e Des Metiers Benjamin Franklin;Rue De La Foret La Rochette;77012;Melun;48.508725;2.663036 +0800063J;Lp LycĂ©e Des Metiers Boucher De Perthes;1 Rue Paul Delique;80142;Abbeville;50.1023384;1.8422737 +0460032K;Lp LycĂ©e Des Metiers Champollion;13 Avenue Fernand Pezet;46106;Figeac;44.6090478;2.0281224 +0600041U;Lp LycĂ©e Des Metiers Charles De Bovelles;Mont Saint Simeon;60402;Noyon;49.580638;3.019981 +0312217S;Lp LycĂ©e Des Metiers Charles De Gaulle;24 Avenue Charles De Gaulle;31604;Muret;43.4689165;1.3215619 +0340078A;Lp LycĂ©e Des Metiers Charles De Gaulle;38 Rue Robespierre;34200;Sete;43.4110658;3.6790691 +0830661T;Lp LycĂ©e Des Metiers Claret;202 Boulevard Trucy;83000;Toulon;43.1340433;5.9256355 +0930136T;Lp LycĂ©e Des Metiers Claude-nicolas Ledoux;Avenue Du Quatorze Juillet;93320;Les Pavillons Sous Bois;48.9117268;2.4912496 +0460051F;Lp LycĂ©e Des Metiers Clement Marot;59 Rue Des Augustins;46005;Cahors;44.4499879;1.4392957 +0020079A;Lp LycĂ©e Des Metiers Condorcet;Rond Point Joliot Curie;02100;St Quentin;49.861713;3.293589 +0930130L;Lp LycĂ©e Des Metiers Condorcet;31 Rue Desire Chevalier;93105;Montreuil;48.857903;2.446193 +0510069Y;Lp LycĂ©e Des Metiers Croix Cordier;40 Rue Croix Cordier;51434;Tinqueux;49.2460676;3.985723 +0300047N;Lp LycĂ©e Des Metiers D'art Georges Guynemer;Place De Verdun;30703;Uzes;44.0145529;4.4206567 +0240006B;Lp LycĂ©e Des Metiers De L Alba;4 Rue Ch Gonthier;24100;Bergerac;44.8430899;0.493039 +0400019D;Lp LycĂ©e Des Metiers De L Auto Frederic Esteve;Quart. St Medard Rue F.esteve;40010;Mont De Marsan;43.893485;-0.499782 +0640026F;Lp LycĂ©e Des Metiers De L Habitat;25 Rue Louis Barthou;64110;Gelos;43.2838439;-0.3738321 +0470040N;Lp LycĂ©e Des Metiers De L Habitat Louis Couffignal;Rue Du Rooy;47300;Villeneuve Sur Lot;44.396361;0.712915 +0310088C;Lp LycĂ©e Des Metiers De L'ameublement;Plaine Du Laudot;31250;Revel;43.458611;2.004573 +0171571R;Lp LycĂ©e Des Metiers De L'atlantique;2 Rue De Montreal;17200;Royan;45.6336048;-1.0180228 +0220059V;Lp LycĂ©e Des Metiers Des Metiers Jean Moulin;3 Rue Du Vau Gicquel;22022;St Brieuc;48.5255564;-2.7908495 +0690048C;Lp LycĂ©e Des Metiers Diderot;41 Cours General Giraud;69283;Lyon 01;45.7710133;4.8167669 +0120096P;Lp LycĂ©e Des Metiers Du Batiment;2 Avenue Du LycĂ©e;12110;Aubin;0.0;0.0 +0230019W;Lp LycĂ©e Des Metiers Du Batiment;Les Granges Route D'aubussson;23500;Felletin;45.884543;2.173878 +0290130J;Lp LycĂ©e Des Metiers Du Batiment;Rue De Kervern;29190;Pleyben;48.2217709;-3.970686 +0101022G;Lp LycĂ©e Des Metiers Edouard Herriot;Rue De La Maladiere;10302;Ste Savine;48.2935455;4.0271421 +0280864M;Lp LycĂ©e Des Metiers Elsa Triolet;2 Rue Des Ecoles;28110;Luce;48.439743;1.4632135 +0690105P;Lp LycĂ©e Des Metiers Emile Bejuit (automobile);282 Route De Genas;69675;Bron;45.7514458;4.9054389 +0560008E;Lp LycĂ©e Des Metiers Emile James;56 Rue Emile James;56410;Etel;47.6631024;-3.2049371 +0560070X;Lp LycĂ©e Des Metiers Emile Zola;30 Rue Emile Zola;56704;Hennebont;47.8259374;-3.2514182 +0421489R;Lp LycĂ©e Des Metiers Etienne Legrand;8 Boulevard Charles Gallet;42124;Le Coteau;46.0226751;4.0940381 +0080010T;Lp LycĂ©e Des Metiers Etion;Rue Jean De La Fontaine;08000;Charleville Mezieres;49.7789551;4.701555 +0220071H;Lp LycĂ©e Des Metiers Eugene Freyssinet;32 Rue Mansart;22023;St Brieuc;48.5195608;-2.777683 +0510038P;Lp LycĂ©e Des Metiers Europe;71 Avenue De L'europe;51673;Reims;49.2495206;4.0658188 +0110013E;Lp LycĂ©e Des Metiers Francois Andreossy;1 Rue Saint Francois;11494;Castelnaudary;43.3190962;1.9585015 +0370032J;Lp LycĂ©e Des Metiers Francois Clouet;8 Rue Lepage;37081;Tours;47.4285682;0.6913718 +0630012W;Lp LycĂ©e Des Metiers Francois Rabelais;13 Avenue De Charbonnier;63570;Brassac Les Mines;45.4134991;3.3218257 +0451304Y;Lp LycĂ©e Des Metiers Francoise Dolto;125 Rue Claude Debussy;45160;Olivet;47.865765;1.8794481 +0020089L;Lp LycĂ©e Des Metiers Frederic Et Irene Joliot Curie;1 Place Du Pigeon Blanc;02500;Hirson;49.9200919;4.084192 +0300058A;Lp LycĂ©e Des Metiers Frederic Mistral;457 Ancienne Route De Generac;30913;Nimes;43.823195;4.3589827 +0640098J;Lp LycĂ©e Des Metiers Gabriel Haure-place;6 Av Carmel Lasportes;64800;Coarraze;43.177514;-0.2400054 +0830960T;Lp LycĂ©e Des Metiers Gallieni;Rue Marechal Lyautey;83600;Frejus;43.4454017;6.7551626 +0450064A;Lp LycĂ©e Des Metiers Gaudier-brzeska;40 Avenue Denis Papin;45800;St Jean De Braye;47.9115438;1.9462399 +0730039W;Lp LycĂ©e Des Metiers General Ferrie;39 Avenue De La Republique;73140;St Michel De Maurienne;45.2190734;6.4710134 +0010020S;Lp LycĂ©e Des Metiers Georges Charpak;Avenue Charles De Gaulle;01400;Chatillon Sur Chalaronne;46.1254895;4.9607151 +0830058M;Lp LycĂ©e Des Metiers Georges Cisson;272 Rue Andre Chenier;83100;Toulon;43.1292577;5.9614162 +0691626T;Lp LycĂ©e Des Metiers Georges Lamarque;10 Route De Geneve;69140;Rillieux La Pape;45.8037404;4.8823896 +0510036M;Lp LycĂ©e Des Metiers Gustave Eiffel;34 Rue De Neufchatel;51066;Reims;49.275193;4.0269257 +0380036H;Lp LycĂ©e Des Metiers Guynemer;56 Avenue Marcelin Berthelot;38037;Grenoble;45.1705531;5.731918 +0310057U;Lp LycĂ©e Des Metiers Helene Boucher;1 Rue Lucien Lafforgue;31901;Toulouse;43.6132367;1.4345703 +0931193S;Lp LycĂ©e Des Metiers Helene Boucher;70 Avenue Gilbert Berger;93290;Tremblay En France;48.9502883;2.5784449 +0380104G;Lp LycĂ©e Des Metiers Henri Fabre;35 Rue Henri Wallon;38400;St Martin D Heres;45.1728602;5.7587385 +0570123Z;Lp LycĂ©e Des Metiers Henri Nomine;60 R Marechal Foch;57215;Sarreguemines;49.1165272;7.0802942 +0630041C;Lp LycĂ©e Des Metiers Henri Sainte-claire Deville;Chemin Des Croizettes;63504;Issoire;45.5401316;3.2576825 +0421736J;Lp LycĂ©e Des Metiers Hotelier;18 Rue Francois Gillet;42405;St Chamond;45.4546377;4.5045484 +0811144D;Lp LycĂ©e Des Metiers Hotelier;45 Rue Lapeyrouse;81207;Mazamet;43.5031335;2.3830315 +0261148G;Lp LycĂ©e Des Metiers Hotelier De L'hermitage;Rue Jean Monnet;26600;Tain L Hermitage;45.06968;4.860383 +0460529A;Lp LycĂ©e Des Metiers Hoteliers Quercy-perigord;Avenue Roger Couderc;46200;Souillac;44.8950338;1.4700737 +0332194F;Lp LycĂ©e Des Metiers Hotellerie Tourisme E Services;1 Av Roland Dorgeles;33120;Arcachon;44.6510118;-1.1604617 +0301270T;Lp LycĂ©e Des Metiers Industriel Jules Raimu;12 Rue Jules Raimu;30908;Nimes;43.8195221;4.3290667 +0600003C;Lp LycĂ©e Des Metiers J.b. Corot - Batiment;4 Et 6 Rue Henri Lebesgue;60000;Beauvais;49.42964;2.081875 +0332441Z;Lp LycĂ©e Des Metiers Jacques Brel;Rue Jean Lurcat;33305;Lormont;44.8758255;-0.5124773 +0940141T;Lp LycĂ©e Des Metiers Jacques Brel;90-100 Avenue D'alfortville;94600;Choisy Le Roi;48.7737868;2.4151804 +0690043X;Lp LycĂ©e Des Metiers Jacques De Flesselles;15 Rue De Flesselles;69283;Lyon 01;45.7710983;4.8266437 +0771997C;Lp LycĂ©e Des Metiers Jacques Prevert;7 Avenue Jean Jaures;77385;Combs La Ville;48.6273907;2.5860068 +0382271M;Lp LycĂ©e Des Metiers Jean Claude Aubry;Chemin De Rosiere;38303;Bourgoin Jallieu;45.5958819;5.2972999 +0360026H;Lp LycĂ©e Des Metiers Jean D Alembert;8 Rue De La Limoise;36105;Issoudun;46.9567081;2.0000772 +0180009M;Lp LycĂ©e Des Metiers Jean De Berry;;18026;Bourges;47.060833;2.372222 +0650041S;Lp LycĂ©e Des Metiers Jean Dupuy;1 Rue Aristide Berges;65016;Tarbes;43.229533;0.064581 +0400097N;Lp LycĂ©e Des Metiers Jean Garnier;24 Rue Henri Barbusse;40110;Morcenx;44.0401193;-0.9135882 +0180025E;Lp LycĂ©e Des Metiers Jean Guehenno;Rue Des Sables;18200;St Amand Montrond;46.733637;2.5082521 +0430024T;Lp LycĂ©e Des Metiers Jean Monnet;45 Boulevard Bertrand;43000;Le Puy En Velay;45.0368885;3.8870656 +0290001U;Lp LycĂ©e Des Metiers Jean Moulin;27 Rue De La Republique;29780;Plouhinec;48.0182717;-4.5302967 +0400027M;Lp LycĂ©e Des Metiers Jean Taris;Avenue J Dupaya;40300;Peyrehorade;43.5493036;-1.0898504 +0080047H;Lp LycĂ©e Des Metiers Jean-baptiste Clement;11 Rue J Jaures;08200;Sedan;49.6998064;4.9329191 +0931233K;Lp LycĂ©e Des Metiers Jean-baptiste Clement;25 Rue Des Trois Noyers;93220;Gagny;48.8874111;2.5425454 +0880072A;Lp LycĂ©e Des Metiers Jean-charles Pellerin;44 Rue Abel Ferry;88021;Epinal;48.1665516;6.4548634 +0511430C;Lp LycĂ©e Des Metiers Joliot-curie;4 Rue Joliot-curie;51096;Reims;49.2313467;4.0037269 +0420045W;Lp LycĂ©e Des Metiers Joseph Haubtmann;20 Rue Burdeau;42000;St Etienne;45.4457783;4.4108791 +0090006H;Lp LycĂ©e Des Metiers Joseph Marie Jacquard;Rue Jacquard;09300;Lavelanet;42.923053;1.840868 +0670058Z;Lp LycĂ©e Des Metiers Jules Verne;31 Rue Saint Nicolas;67703;Saverne;48.7345469;7.3714457 +0221595P;Lp LycĂ©e Des Metiers La Fontaine Des Eaux;La Fontaine Des Eaux;22102;Dinan;48.4614665;-2.0384413 +0320068A;Lp LycĂ©e Des Metiers Le Garros;1 Bis Rue Darwin;32021;Auch;43.6317386;0.5885775 +0870748R;Lp LycĂ©e Des Metiers Le Mas Jambost;7 Allee Du Marechal Fayolle;87065;Limoges;45.833096;1.222058 +0021477V;Lp LycĂ©e Des Metiers Leonard De Vinci;1 Espace Jean Guerland;02331;Soissons;49.3726602;3.3073808 +0341385W;Lp LycĂ©e Des Metiers Leonard De Vinci;Rue Professeur Blayac;34085;Montpellier;43.6246951;3.8272737 +0693045K;Lp LycĂ©e Des Metiers Les Canuts;2 Rue Ho Chi Minh;69120;Vaulx En Velin;45.7797002;4.9205183 +0260114H;Lp LycĂ©e Des Metiers Les Catalins;24 Avenue Des Catalins;26216;Montelimar;44.5696163;4.7576998 +0060015J;Lp LycĂ©e Des Metiers Les Coteaux;4/6 Che Morgon Av Des Coteaux;06400;Cannes;43.5662949;7.022452 +0060082G;Lp LycĂ©e Des Metiers Les Eucalyptus;7 Avenue Des Eucalyptus;06200;Nice;43.6762904;7.2225405 +0080953T;Lp LycĂ©e Des Metiers Louis Armand;;08440;Vivier Au Court;49.733206;4.830856 +0561507J;Lp LycĂ©e Des Metiers Louis Armand;3 Rue Jean Moulin;56500;Locmine;47.8792681;-2.8294521 +0920171L;Lp LycĂ©e Des Metiers Louis Bleriot;67 Rue De Verdun;92150;Suresnes;48.8742683;2.2303113 +0771171E;Lp LycĂ©e Des Metiers Louis Lumiere;32 Avenue De L'europe;77500;Chelles;48.882983;2.6085512 +0810046K;Lp LycĂ©e Des Metiers Louis Rascol;10 Rue De La Republique;81012;Albi;43.9318293;2.1542966 +0931735F;Lp LycĂ©e Des Metiers Louise Michel;77 Rue De Saint Gratien;93800;Epinay Sur Seine;48.9620907;2.2997924 +0450066C;Lp LycĂ©e Des Metiers Mal Leclerc De Hauteclocque;85 Avenue Georges Clemenceau;45140;St Jean De La Ruelle;47.8960395;1.8605698 +0190027B;Lp LycĂ©e Des Metiers Marcel Barbanceys;Rue De L'artisanat;19160;Neuvic;45.382449;2.272544 +0630054S;Lp LycĂ©e Des Metiers Marie Laurencin;1 Avenue Jean Monnet;63201;Riom;45.9006673;3.1039393 +0560027A;Lp LycĂ©e Des Metiers Marie Le Franc;128 Boulevard Leon Blum;56321;Lorient;47.7456044;-3.3806542 +0370054H;Lp LycĂ©e Des Metiers Martin Nadaud;67 Rue Jeanne Labourbe;37700;St Pierre Des Corps;47.394938;0.7418599 +0870004G;Lp LycĂ©e Des Metiers Martin Nadaud;30 Avenue De La Liberation;87300;Bellac;46.1224081;1.0612588 +0280022X;Lp LycĂ©e Des Metiers Maurice Viollette;Rue Pablo Neruda;28101;Dreux;48.736652;1.345303 +0340043M;Lp LycĂ©e Des Metiers Mendes France;49 Bis Av Georges Clemenceau;34007;Montpellier;43.602191;3.8701314 +0020051V;Lp LycĂ©e Des Metiers Metiers De L Ameublement;Rue Fleming;02100;St Quentin;49.8638229;3.303156 +0751710B;Lp LycĂ©e Des Metiers Nicolas Louis Vauquelin;13-21 Avenue Boutroux;75013;Paris 13;48.8220969;2.3749517 +0060040L;Lp LycĂ©e Des Metiers Pasteur;25 Rue Du Professeur Delvalle;06000;Nice;43.722971;7.2839949 +0670050R;Lp LycĂ©e Des Metiers Paul Emile Victor;1a Avenue De Gail;67210;Obernai;48.4632396;7.4912864 +0310089D;Lp LycĂ©e Des Metiers Paul Mathou;Avenue De Luchon;31210;Gourdan Polignan;43.0674443;0.5872643 +0670006T;Lp LycĂ©e Des Metiers Philippe-charles Goulden;2 Rue De La Piscine;67241;Bischwiller;48.7749993;7.8502476 +0631480S;Lp LycĂ©e Des Metiers Pierre Boulanger;85 Chemin Des Palisses;63430;Pont Du Chateau;45.792027;3.2478132 +0470015L;Lp LycĂ©e Des Metiers Porte Du Lot;Porte Du Lot;47320;Clairac;44.358916;0.375807 +0180049F;Lp LycĂ©e Des Metiers Rene Cassin;41 Bis Rue Charles Hurvoy;18108;Vierzon;47.2279543;2.0673471 +0420029D;Lp LycĂ©e Des Metiers Rene Cassin;Le Marthoret;42800;Rive De Gier;45.522087;4.605514 +0400020E;Lp LycĂ©e Des Metiers Robert Wlerick;6 Rue Jean Mace -;40005;Mont De Marsan;43.8969788;-0.5144499 +0630022G;Lp LycĂ©e Des Metiers Roger Claustres;127 Rue Docteur Hospital;63039;Clermont Ferrand;45.795454;3.0877122 +0310052N;Lp LycĂ©e Des Metiers Roland Garros;32 Rue Mathaly;31200;Toulouse;43.6290045;1.4298117 +0941604H;Lp LycĂ©e Des Metiers Samuel De Champlain;61-91 Rue Des Bordes;94430;Chennevieres Sur Marne;48.7987853;2.5577377 +0281021H;Lp LycĂ©e Des Metiers Sully;8 Rue Des Viennes;28404;Nogent Le Rotrou;48.3245558;0.8218301 +0690010L;Lp LycĂ©e Des Metiers Tony Garnier;235 Boulevard Pinel;69676;Bron;45.7261136;4.8871026 +0770919F;Lp LycĂ©e Des Metiers Uruguay France;1 Avenue Des Marronniers;77211;Avon;48.4103009;2.7355078 +0100004A;Lp LycĂ©e Des Metiers Val More;Avenue Bernard Pieds;10110;Bar Sur Seine;48.1121812;4.3867353 +0631668W;Lp LycĂ©e Des Metiers Vercingetorix;4 Rue De Laubize;63540;Romagnat;45.737373;3.106057 +0391131T;Lp PrivĂ© Institut Europeen De Form Compagnons Tour De France;2 Rue Leopold Alixant;39330;Mouchard;46.976214;5.797567 +9830401U;Lp PrivĂ© St Pierre Chanel La Conception (ddec);Rue Beautemps Beaupre;98874;Le Mont Dore;0.0;0.0 +0331740M;Lpa Viticulture Oenologie La Tour Blanche;Le Bourg;33210;Bommes;44.5613178;-0.3551972 +0731198F;Lpa Costa De Beauregard Site De Chambery Le Bocage;340 Rue Costa De Beauregard;73000;Chambery;45.5633945;5.9311119 +0731199G;Lpa Costa De Beauregard Site De La Ravoire Charmilles;148 Rue L Costa De Beauregard;73490;La Ravoire;45.5639798;5.9610297 +0400141L;Lpa De Sabres;Route De Luglon;40630;Sabres;44.1457175;-0.7411813 +0631669X;Lpo LycĂ©e Des Metiers;Voie Romaine;63400;Chamalieres;45.7782726;3.0496304 +0240026Y;Lpo LycĂ©e Des Metiers Albert Claveille;80 Rue V Hugo;24001;Perigueux;45.1894956;0.7130103 +0031043T;Lpo LycĂ©e Des Metiers Albert Einstein;Rue Albert Einstein;03105;Montlucon;46.3508329;2.604608 +0670087F;Lpo LycĂ©e Des Metiers Alexandre Dumas (hotelier);75 Rte Du Rhin;67404;Illkirch Graffenstaden;48.5734116;7.7971354 +0770938B;Lpo LycĂ©e Des Metiers Andre Malraux;Avenue Du LycĂ©e;77130;Montereau Fault Yonne;0.0;0.0 +0932129J;Lpo LycĂ©e Des Metiers Application De L'enna;Place Du 8 Mai 1945;93203;St Denis;48.939438;2.355926 +0410001D;Lpo LycĂ©e Des Metiers Augustin Thierry;13 Avenue De Chateaudun;41018;Blois;47.5966852;1.3290092 +0750681H;Lpo LycĂ©e Des Metiers Boulle Esaa;9 Rue Pierre Bourdan;75012;Paris 12;48.8468966;2.3914855 +0560016N;Lpo LycĂ©e Des Metiers Broceliande;Bellevue;56383;Guer;47.9401893;-2.1337777 +0640001D;Lpo LycĂ©e Des Metiers Cantau;1 Allee De Cantau;64600;Anglet;43.4758828;-1.5121686 +0521032P;Lpo LycĂ©e Des Metiers Charles De Gaulle;Avenue Christian Pineau;52903;Chaumont;48.0738585;5.1462257 +0681801N;Lpo LycĂ©e Des Metiers Charles De Gaulle;14 Rte De Ruelisheim;68840;Pulversheim;47.8328666;7.3074998 +0772228D;Lpo LycĂ©e Des Metiers Charles De Gaulle;6 Place Jean Mermoz;77230;Longperrier;49.0483392;2.6684576 +0430020N;Lpo LycĂ©e Des Metiers Charles Et Adrien Dupuy;3 Avenue Docteur Durand;43003;Le Puy En Velay;45.0371683;3.8938789 +0560026Z;Lpo LycĂ©e Des Metiers Colbert;117 Boulevard Leon Blum;56101;Lorient;47.7456671;-3.3807475 +0781578S;Lpo LycĂ©e Des Metiers De Hotellerie Et De Tourisme;Place Francois Rabelais;78042;Guyancourt;48.762895;2.075323 +0332870R;Lpo LycĂ©e Des Metiers De La Mer;Port De La Barbotiere;33470;Gujan Mestras;44.635731;-1.071189 +0470020S;Lpo LycĂ©e Des Metiers De La Plasturgie V De Garonne;22 Rue Ejea De Los Caballeros;47207;Marmande;44.504404;0.1562185 +0290044R;Lpo LycĂ©e Des Metiers De L'elorn;10 Place St Houardon;29207;Landerneau;48.4489257;-4.2496666 +0542262R;Lpo LycĂ©e Des Metiers Du Batiment Et Tp E. Here;86 Blv Marechal Foch;54525;Laxou;48.6827791;6.1514542 +0260023J;Lpo LycĂ©e Des Metiers Du Dauphine;Boulevard Remy Roure;26103;Romans Sur Isere;45.0500065;5.0436672 +0771658J;Lpo LycĂ©e Des Metiers Du Gue A Tresmes;Domaine Du Gue A Tresmes;77440;Congis Sur Therouanne;49.007185;2.97453 +0951728M;Lpo LycĂ©e Des Metiers Edmond Rostand;75 Rue De Paris St Ouen L'aumon;95312;St Ouen L Aumone;48.9004041;2.3043039 +0690128P;Lpo LycĂ©e Des Metiers Edouard Branly;25 Rue De Tourvielle;69322;Lyon 05;45.7527217;4.7911202 +0681809X;Lpo LycĂ©e Des Metiers Ettore Bugatti;8 Rue Des Jonquilles;68110;Illzach;47.7622552;7.3652658 +0932119Y;Lpo LycĂ©e Des Metiers Eugene Henaff;55 Avenue Raspail;93170;Bagnolet;48.8746864;2.4310918 +0772312V;Lpo LycĂ©e Des Metiers Flora Tristan;12 Avenue Du 8 Mai 1945;77130;Montereau Fault Yonne;48.3765466;2.9451169 +0511565Z;Lpo LycĂ©e Des Metiers Francois Arago;1 Rue Francois Arago;51095;Reims;49.2288823;4.0051108 +0080008R;Lpo LycĂ©e Des Metiers Francois Bazin;145 Avenue Charles De Gaulle;08013;Charleville Mezieres;49.7734776;4.7050401 +0940585A;Lpo LycĂ©e Des Metiers Francois Mansart;25 Avenue De La Banque;94211;St Maur Des Fosses;48.7903039;2.4971131 +0932126F;Lpo LycĂ©e Des Metiers Francois Rabelais;Rue Francois Rabelais;93440;Dugny;48.949219;2.417634 +0750695Y;Lpo LycĂ©e Des Metiers Fresnel;31 Boulevard Pasteur;75015;Paris 15;48.8431925;2.3126765 +0921156G;Lpo LycĂ©e Des Metiers Galilee;79 Avenue Chandon;92230;Gennevilliers;48.9207477;2.3027836 +0912142J;Lpo LycĂ©e Des Metiers Gaspard Monge;1 Place Gaspard Monge;91600;Savigny Sur Orge;48.6851259;2.349282 +0460493L;Lpo LycĂ©e Des Metiers Gaston Monnerville;Rue George Sand;46005;Cahors;44.463535;1.455082 +0772311U;Lpo LycĂ©e Des Metiers Georges Cormier;6 Rue Des Templiers;77527;Coulommiers;48.824111;3.093208 +0470038L;Lpo LycĂ©e Des Metiers Georges Leygues;Avenue D Agen;47307;Villeneuve Sur Lot;44.3939869;0.7092418 +0572590F;Lpo LycĂ©e Des Metiers Gustave Eiffel;Cite Technique;57525;Talange;49.235;6.168168 +0681810Y;Lpo LycĂ©e Des Metiers Gustave Eiffel;Rue Gustave Eiffel;68704;Cernay;47.805144;7.18261 +0672616E;Lpo LycĂ©e Des Metiers Gutenberg;22 Rue Lixenbuhl;67404;Illkirch Graffenstaden;48.5294488;7.7224172 +0401002X;Lpo LycĂ©e Des Metiers Haroun Tazieff;Bld St Vincent De Paul;40993;St Paul Les Dax;43.7296115;-1.0383382 +0672534R;Lpo LycĂ©e Des Metiers Haut-barr;4 Rue Jean De Manderscheid;67703;Saverne;48.7317063;7.3513877 +0690040U;Lpo LycĂ©e Des Metiers Hector Guimard;23 Rue Claude Veyron;69361;Lyon 07;45.7456711;4.8579449 +0220196U;Lpo LycĂ©e Des Metiers Henri Avril;7 Et 9 Rue De Dinard;22402;Lamballe;48.4765006;-2.5018486 +0180036S;Lpo LycĂ©e Des Metiers Henri Brisson;25 Avenue Henri Brisson;18100;Vierzon;47.2279808;2.0612978 +0410899E;Lpo LycĂ©e Des Metiers Hotel.et Tourisme Val De Loire;174 Rue Albert 1er;41033;Blois;47.5720598;1.3057101 +0332192D;Lpo LycĂ©e Des Metiers Hotel.tourisme Gascogne;15 Rue Francois Rabelais;33405;Talence;44.7970282;-0.5983577 +0171405K;Lpo LycĂ©e Des Metiers Hotelier;Avenue Des Minimes;17030;La Rochelle;46.143788;-1.162168 +0871030X;Lpo LycĂ©e Des Metiers Hotelier Jean Monnet;12 Rue Louis Armstrong;87065;Limoges;45.8526566;1.2911805 +0573320Z;Lpo LycĂ©e Des Metiers Hotelier Raymond Mondon;2 Boulevard De La Defense;57070;Metz;49.1054834;6.2095119 +0132974M;Lpo LycĂ©e Des Metiers Hotelier Regional;114 Avenue Zenatti;13266;Marseille 08;43.2473062;5.3818559 +0060034E;Lpo LycĂ©e Des Metiers Hotelier Tourisme-paul Augier;163 Boulevard Rene Cassin;06203;Nice;43.6688186;7.212126 +0310046G;Lpo LycĂ©e Des Metiers Hotellerie Et Tourisme;1 Rue De L'abbe Jules Lemire;31026;Toulouse;43.596917;1.4068498 +0831453D;Lpo LycĂ©e Des Metiers Hotellerie Et Tourisme Tpm;Place Vatel;83098;Toulon;43.124228;5.928 +0020012C;Lpo LycĂ©e Des Metiers Jean De La Fontaine;2 Av De Champagne;02401;Chateau Thierry;49.0525511;3.3881731 +0022008X;Lpo LycĂ©e Des Metiers Jean Mace;7 Rue Andre Ternynck;02300;Chauny;49.6216079;3.2145425 +0940129E;Lpo LycĂ©e Des Metiers Jean Mace;Rue Jules Ferry;94407;Vitry Sur Seine;48.8016614;2.4003992 +0783431F;Lpo LycĂ©e Des Metiers Jules Verne;2 Rue De La Constituante;78500;Sartrouville;48.9427537;2.160041 +0221571N;Lpo LycĂ©e Des Metiers Kerraoul;Kerraoul;22502;Paimpol;48.779782;-3.048828 +0770920G;Lpo LycĂ©e Des Metiers Lafayette;Place Des Celestins;77811;Moret Sur Loing;48.4041358;2.7973142 +0681817F;Lpo LycĂ©e Des Metiers Lazare De Schwendi;19 Rue De Turckheim;68040;Ingersheim;48.0776787;7.3464456 +0672198A;Lpo LycĂ©e Des Metiers Le Corbusier;15 Rue Lixenbuhl;67404;Illkirch Graffenstaden;48.5303263;7.7218261 +0382440W;Lpo LycĂ©e Des Metiers Leonard De Vinci;Boulevard De Villefontaine;38091;Villefontaine;45.615892;5.1484454 +0770934X;Lpo LycĂ©e Des Metiers Leonard De Vinci;2 Bis Rue Edouard Branly;77011;Melun;48.5510247;2.6601886 +0932046U;Lpo LycĂ©e Des Metiers Leonard De Vinci;115 Route Des Petits Ponts;93290;Tremblay En France;48.9668896;2.5735809 +0771336J;Lpo LycĂ©e Des Metiers Les Pannevelles;Route De Chalautre;77487;Provins;48.5559405;3.3040064 +0380035G;Lpo LycĂ©e Des Metiers Lesdiguieres;15 Avenue Beaumarchais;38034;Grenoble;45.1679451;5.708607 +0931779D;Lpo LycĂ©e Des Metiers L'horticulture;16 Rue Paul Doumer;93512;Montreuil;48.8591808;2.4567758 +0940118T;Lpo LycĂ©e Des Metiers Louis Armand;173 Boulevard De Strasbourg;94130;Nogent Sur Marne;48.8465135;2.4912988 +0670085D;Lpo LycĂ©e Des Metiers Louis Couffignal;11 Route De La Federation;67025;Strasbourg;48.5621702;7.7496898 +0460028F;Lpo LycĂ©e Des Metiers Louis Vicat;1 Rue Pierre Bourthoumieux;46200;Souillac;44.8945267;1.4803757 +0940112L;Lpo LycĂ©e Des Metiers Louise Michel;7 Rue Pierre Marie Derrien;94507;Champigny Sur Marne;48.8170061;2.4949002 +0932074Z;Lpo LycĂ©e Des Metiers Marcel Cachin;11-13 Rue Marcel Cachin;93400;St Ouen;48.9174351;2.3385731 +0171455P;Lpo LycĂ©e Des Metiers Marcel Dassault;40 Avenue Marcel Dassault;17300;Rochefort;45.9469735;-0.9578103 +0561641E;Lpo LycĂ©e Des Metiers Marcelin Berthelot;Avenue Roland Garros;56230;Questembert;47.6628885;-2.4619313 +0501850P;Lpo LycĂ©e Des Metiers Maurice Marland;159 Rue Des LycĂ©es;50400;Granville;0.0;0.0 +0940126B;Lpo LycĂ©e Des Metiers Maximilien Perret;Place San Benedetto Del Tronto;94142;Alfortville;48.7871379;2.4256714 +0942130E;Lpo LycĂ©e Des Metiers Montaleau;2 Bis Rue Pierre Semard;94372;Sucy En Brie;48.7698796;2.5168592 +0932291K;Lpo LycĂ©e Des Metiers Nicolas-joseph Cugnot;55 Boulevard Louis Armand;93330;Neuilly Sur Marne;48.8620632;2.5203576 +0830923C;Lpo LycĂ©e Des Metiers Paul Langevin;Boulevard De L'europe;83514;La Seyne Sur Mer;43.1141507;5.8566865 +0190018S;Lpo LycĂ©e Des Metiers Pierre Caraminot;28 Avenue De Ventadour;19300;Egletons;45.4072622;2.0561062 +0350030T;Lpo LycĂ©e Des Metiers Pierre Mendes France;34 Rue Bahon Rault;35069;Rennes;48.1336823;-1.6840215 +0180008L;Lpo LycĂ©e Des Metiers Pierre-emile Martin;1 Avenue De Gionne;18026;Bourges;47.0736177;2.4059603 +0750691U;Lpo LycĂ©e Des Metiers Raspail;5 Bis Avenue Maurice D Ocagne;75014;Paris 14;48.8246549;2.3135463 +0910620E;Lpo LycĂ©e Des Metiers Robert Doisneau;95 Bd Jean Jaures;91107;Corbeil Essonnes;48.612295;2.4605384 +0382099A;Lpo LycĂ©e Des Metiers Roger Deschaux;5 Rue Des Pies;38360;Sassenage;45.1992827;5.6681035 +0410030K;Lpo LycĂ©e Des Metiers Ronsard;Rue Joliot Curie;41100;Vendome;47.807054;1.057515 +0922276Z;Lpo LycĂ©e Des Metiers Santos Dumont;39 Rue Pasteur;92210;St Cloud;48.8414385;2.1996884 +0740047Z;Lpo LycĂ©e Des Metiers Savoie Leman;40 Boulevard Carnot;74203;Thonon Les Bains;46.3726701;6.4752202 +0782587N;Lpo LycĂ©e Des Metiers Viollet-le-duc;1 Route De Septeuil;78640;Villiers St Frederic;48.8143515;1.875185 +0290071V;Lpo LycĂ©e Des Metiers Yves Thepot;28 Avenue Yves Thepot;29104;Quimper;47.9882623;-4.0884842 +0352072M;Lpo PrivĂ© Des Metiers Institution St Malo-providence;2 Rue Du College;35409;St Malo;48.6503421;-2.0254142 +0290340M;Lpo PrivĂ© LycĂ©e Des Metiers Estran Fenelon;Ker Stears;29287;Brest;48.390394;-4.486076 +0220117H;Lpo PrivĂ© Sacre Coeur;2 Bd St Jean-bapt De La Salle;22003;St Brieuc;0.0;0.0 +0230018V;Lt LycĂ©e Des Metiers Des Metiers Du Batiment;Les Granges Route D'aubusson;23500;Felletin;45.889542;2.177806 +0800011C;Lt LycĂ©e Des Metiers Edouard Gand;70 Bd De St Quentin;80098;Amiens;49.8791607;2.2965051 +0350005R;Lt LycĂ©e Des Metiers Hotellerie Et Restauration;33 Rue Des Ecoles;35803;Dinard;48.6254023;-2.0585228 +0650027B;Lt LycĂ©e Des Metiers Jean Dupuy;1 Rue Aristide Berges;65016;Tarbes;43.229533;0.064581 +0021476U;Lt LycĂ©e Des Metiers Leonard De Vinci;1 Espace Jean Guerland;02331;Soissons;49.3726602;3.3073808 +0641548K;Lyc Agricole PrivĂ© N Dame;Route D Oraas;64390;Sauveterre De Bearn;43.399781;-0.942935 +0211173L;Lyc Enst Gen Et Techno Agricol Felix Kir;85 Rue De Velars;21370;Plombieres Les Dijon;47.3416891;4.9660479 +0340128E;Lyc Enst Gen.techno Agricole Frederic Bazille - Agropolis;3224 Route De Mende;34093;Montpellier;43.6481537;3.864392 +0110857X;Lyc Enst PrivĂ© Agric Form Prof;8 Boulevard Des Pins;11170;Pezens;43.255745;2.2655503 +0660632D;Lyc Enst Prof Agricole PrivĂ© Beausoleil;Rue Beau Soleil;66403;Ceret;42.4881192;2.7500287 +0383208F;Lyc Gen Tech Pr Itec Boisfleur Itec Boisfleury Europe;118 Avenue De L'eygala;38700;Corenc;45.2122675;5.7577651 +0671639T;Lyc Gen Tech PrivĂ© Des Metiers Institution Sainte Clotilde;19 Rue De Verdun;67083;Strasbourg;48.5874674;7.7724234 +0801742J;Lyc Gen Tech PrivĂ© Des Metiers La Providence;146 Bd De Saint Quentin;80094;Amiens;49.8789518;2.3007807 +0021869W;Lyc Gen Tech PrivĂ© Des Metiers Saint Joseph;4 Place Thiers;02402;Chateau Thierry;49.0428865;3.4009902 +0801888T;Lyc Gen Tech PrivĂ© Des Metiers Saint Martin;68 Rue Delpech;80043;Amiens;49.8842672;2.29491 +0801479Y;Lyc Gen Tech PrivĂ© Des Metiers Saint Remi;4 Rue Des Sergents;80006;Amiens;49.8942788;2.2987524 +0740092Y;Lyc Gen Tech PrivĂ© Des Metiers Sainte Famille;Avenue Des Voirons;74805;La Roche Sur Foron;46.0773767;6.2971227 +0290168A;Lyc Gen Tech PrivĂ© Des Metiers St Gabriel;Rue Jean Lautredou;29125;Pont L Abbe;47.8619569;-4.216529 +0022108F;Lyc Gen Tech PrivĂ© Des Metiers St Vincent De Paul;13 Avenue De Reims;02205;Soissons;49.3754463;3.3353105 +0430113P;Lyc Gen Techno Prof Agricole;Domaine De Choumouroux;43200;Yssingeaux;45.142667;4.123734 +0430112N;Lyc Gen Techno Prof Agricole Bonnefont;;43100;Fontannes;45.288694;3.422626 +0110669T;Lyc Gen Techno Prof Agricole Charlemagne;Route De Saint Hilaire;11000;Carcassonne;43.1941938;2.3440721 +0780004F;Lyc Gen Techno Prof Agricole De Chambourcy;Rte Forestiere Des Princesses;78100;St Germain En Laye;48.898908;2.093761 +0150037J;Lyc Gen Techno Prof Agricole Enil - Georges Pompidou;21 Rue De Salers;15005;Aurillac;44.9356765;2.4448977 +0630984C;Lyc Gen Techno Prof Agricole Louis Pasteur;Marmilhat;63370;Lempdes;45.7796779;3.17838 +0300139N;Lyc Gen Techno Prof Agricole Marie Durand;Domaine De Donadille;30230;Rodilhan;43.8269904;4.4476196 +0110677B;Lyc Gen Techno Prof Agricole Pierre Paul Riquet;935 Avenue Du Docteur Laennec;11491;Castelnaudary;43.3077647;1.9414051 +0210036A;Lyc Gen Techno Prof Agricole Viticole;16 Av Charles Jaffelin;21206;Beaune;47.0243711;4.8287285 +0420976H;Lyc Gen.et Technol.prive Sainte Barbe;10 Rue Franklin;42028;St Etienne;45.4285207;4.3845892 +0300125Y;Lyc Polyval PrivĂ© Des Metiers De La Salle;17 Place Henri Barbusse;30106;Ales;44.1232736;4.0805697 +0560180S;Lyc Polyval PrivĂ© Des Metiers Des Metiers N.dame De La Paix;Beg Er Lann;56275;Ploemeur;47.7506666;-3.3973939 +0371532P;Lyc Polyval PrivĂ© Des Metiers Esthetique De Touraine;49 Et 51 Rue Laponneraye;37000;Tours;47.3819579;0.6901009 +0480680D;Lyc Polyval PrivĂ© Des Metiers Hotellerie Sacre-coeur;43 Avenue De La Gare;48200;St Chely D Apcher;44.8001253;3.273392 +0350795Z;Lyc Polyval PrivĂ© Des Metiers Jean-baptiste De La Salle;5 Rue De La Motte Brulon;35702;Rennes;48.125487;-1.6682656 +0931797Y;Lyc Polyval PrivĂ© Des Metiers Jean-baptiste De La Salle;6-8 Place De La Resistance;93200;St Denis;48.932793;2.3549513 +0350797B;Lyc Polyval PrivĂ© Des Metiers Jeanne D'arc;61 Rue La Fontaine;35708;Rennes;48.1186302;-1.6535782 +0560182U;Lyc Polyval PrivĂ© Des Metiers Jeanne D'arc-st Ivy;29 Rue Abbe Martin;56306;Pontivy;48.0716012;-2.9614094 +0693786R;Lyc Polyval PrivĂ© Des Metiers Jehanne De France;6 Rue De La Fraternelle;69009;Lyon 09;45.7693693;4.8036475 +0560105K;Lyc Polyval PrivĂ© Des Metiers La Mennais;2 Rue General Dubreton;56801;Ploermel;47.9316915;-2.3986823 +0422158T;Lyc Polyval PrivĂ© Des Metiers La Salesienne;35 Rue De La Richelandiere;42100;St Etienne;45.4387009;4.4019391 +0290186V;Lyc Polyval PrivĂ© Des Metiers Le Paraclet;35 Avenue Des Glenan;29018;Quimper;47.9955627;-4.1146056 +0860789R;Lyc Polyval PrivĂ© Des Metiers Le Porteau;62 Rue Du Porteau;86001;Poitiers;46.601507;0.3392301 +0350791V;Lyc Polyval PrivĂ© Des Metiers Marcel Callo;21 Avenue Etienne Gascon;35603;Redon;47.6544207;-2.0810479 +0772275E;Lyc Polyval PrivĂ© Des Metiers Maurice Rondeau;1 Place Du Clos Saint Georges;77600;Bussy St Georges;48.8420023;2.7042405 +0560200N;Lyc Polyval PrivĂ© Des Metiers Notre Dame Le Menimur;71 Rue De Metz;56000;Vannes;47.6669014;-2.7622223 +0370749N;Lyc Polyval PrivĂ© Des Metiers Sainte Marguerite;86 Avenue De Grammont;37041;Tours;47.3825627;0.6921553 +0860774Z;Lyc Polyval PrivĂ© Des Metiers St Jacques De Compostelle;2 Avenue De La Revolution;86036;Poitiers;46.5861427;0.3817243 +0511146U;Lyc Polyval PrivĂ© Des Metiers St J-b De La Salle;20 Rue De Contrai;51066;Reims;49.2510721;4.0364069 +0560181T;Lyc Polyval PrivĂ© Des Metiers St Joseph;42 Rue De Kerguestenen;56109;Lorient;47.751421;-3.3928125 +0560198L;Lyc Polyval PrivĂ© Des Metiers St Joseph;39 Boulevard Des Iles;56010;Vannes;47.642256;-2.7821005 +0341470N;Lyc Pr Tech Agric Hortic Paysa;Route De Pezenas;34150;Gignac;43.6459898;3.5428841 +0332380H;Lyc Prof Horticole Ville De Bordeaux;Domaine De Bel Air Cedex 1052;33185;Le Haillan;44.873167;-0.677914 +0440980B;Lyc Techn Et General Agric Pr;Briace;44430;Le Landreau;47.202598;-1.317968 +0260074P;Lyc Technol PrivĂ© Des Metiers Montplaisir;75 Rue Montplaisir;26000;Valence;44.9312156;4.9083899 +0731405F;Lyc Technol PrivĂ© Des Metiers Sainte Anne;39 Montee St Jean;73292;La Motte Servolex;45.5973189;5.8780976 +0022022M;Lyc Technol PrivĂ© Des Metiers Sainte Sophie;22 Rue Curie;02110;Bohain En Vermandois;49.9830328;3.4565714 +0331590Z;Lyc Technol PrivĂ© Des Metiers St Vincent De Paul;47 A 51 Rue Des Sablieres;33074;Bordeaux;44.825364;-0.5753274 +0331556M;Lyc Technol PrivĂ© Des Metiers Ste Famille Saintonge;12 Rue De Saintonge;33023;Bordeaux;44.8321381;-0.5798944 +0260791U;Lyc.gen.tech.agricole De Romans Sur Isere;Route De Tain;26105;Romans Sur Isere;45.0451381;5.0138158 +0561454B;LycĂ©e Agric Et Horticole PrivĂ© De Kerplouz;Kerplouz;56404;Auray;47.657435;-2.971875 +0351047Y;LycĂ©e Agric Et Horticole PrivĂ© La Lande Du Breil;Rue Fernand Robert;35042;Rennes;48.1322242;-1.7079189 +0801825Z;LycĂ©e Agricole De La Haute Som;Rue De La Poste;80800;Ribemont Sur Ancre;49.9585849;2.566318 +0641547J;LycĂ©e Agricole Pr St Christoph;Le Bourg;64310;St Pee Sur Nivelle;43.31283;-1.58032 +0120936C;LycĂ©e Agricole PrivĂ©;Boulevard Penevayre;12200;Villefranche De Rouergue;44.3552133;2.0415109 +0421676U;LycĂ©e Agricole PrivĂ©;1 Rue Du Petit Lavoir;42450;Sury Le Comtal;45.5340512;4.1820966 +0100734U;LycĂ©e Agricole PrivĂ©;105 Route De Mery-sur-seine;10150;Ste Maure;48.349943;4.0570357 +0360686A;LycĂ©e Agricole PrivĂ©;St Cyran Du Jambot;36700;St Cyran Du Jambot;47.017235;1.139101 +0280944Z;LycĂ©e Agricole PrivĂ© De Nermont;2 Rue De Nermont;28202;Chateaudun;48.0643465;1.3301401 +0100891P;LycĂ©e Agricole PrivĂ© Des Cordeliers;29 Rue Des Cordeliers;10700;Arcis Sur Aube;48.5349144;4.1402841 +0421090G;LycĂ©e Agricole PrivĂ© Etienne Gautier;Ressins;42720;Nandax;46.096594;4.189123 +0080943G;LycĂ©e Agricole PrivĂ© L.e.t.p.a.p;18 Rue Du Chateau;08260;Maubert Fontaine;49.8696137;4.4309129 +0560858D;LycĂ©e Agricole PrivĂ© La Touche;La Touche;56801;Ploermel;47.9491412;-2.4040591 +0241033T;LycĂ©e Agricole PrivĂ© Le Cluzeau;Le Cluzeau;24240;Sigoules;44.764981;0.419171 +0281101V;LycĂ©e Agricole PrivĂ© Notre Dame (orph.app.auteuil);Chateau De Vaux;28240;La Loupe;47.786628;0.936139 +0511645L;LycĂ©e Agricole PrivĂ© Reims-thillois;4 Rue Des Ecoles;51370;Thillois;49.2565382;3.9547396 +9741060J;LycĂ©e Agricole PrivĂ© Sainte Suzanne;Chemin Des Trois Freres;97441;Ste Suzanne;-20.90902;55.6055231 +0271382F;LycĂ©e Agricole PrivĂ© Tourville Sur Pont-audemer;Route De Lisieux;27504;Pont Audemer;49.3501675;0.5151331 +0721009F;LycĂ©e Agricole PrivĂ© Val De Sarthe;Route Du Mans;72303;Sable Sur Sarthe;47.838713;-0.304241 +0641538Z;LycĂ©e Agricole Rural Pr Ois;;64130;Mauleon Licharre;43.220131;-0.8895 +0131690S;LycĂ©e Agricole Tech. PrivĂ© Institut De Fontlongue;Bd Theodore Aubanel;13148;Miramas;43.5881724;4.9977599 +0631162W;LycĂ©e Agro-environ PrivĂ© Saint Joseph;Chateau De Saint Quentin;63340;Le Breuil Sur Couze;45.460253;3.273918 +0251477V;LycĂ©e Agrotechnologique PrivĂ© Francois Xavier;5 Rue Du Chapitre;25000;Besancon;47.2333772;6.0305139 +0380097Z;LycĂ©e Climatique Et Sportif Jean Prevost;470 Rue De Tintaine;38250;Villard De Lans;45.0619796;5.5561287 +0660057D;LycĂ©e Climatique Et Sportif Pierre De Coubertin;5 Avenue Pierre De Coubertin;66120;Font Romeu Odeillo Via;42.5131055;2.0447502 +0050004C;LycĂ©e Climatique Honore Romane;Route De Caleyere;05202;Embrun;44.5702302;6.4862717 +0650001Y;LycĂ©e Climatique Rene Billeres;6 Avenue Marcel Lemettre;65402;Argeles Gazost;43.0017111;-0.0948255 +0390042J;LycĂ©e Climatique Victor Considerant;Route D'ornans;39110;Salins Les Bains;46.9525457;5.8851415 +0671636P;LycĂ©e D'ens. General Et Techn. Ort;14 Rue Sellenick;67083;Strasbourg;48.5906831;7.7508928 +0671685T;LycĂ©e D'ens. General Et Technologique Agricole;44 Bld D'europe;67212;Obernai;48.46226;7.48168 +0431000D;LycĂ©e D'enseig. Agricole PrivĂ© Site De Vals De L'isvt;72 Avenue De Vals;43750;Vals Pres Le Puy;45.0335241;3.8797025 +0520893N;LycĂ©e D'enseignement Agricole PrivĂ©;Rue De La Motte;52220;Droyes;48.5130399;4.703696 +0021818R;LycĂ©e D'enseignement Technolog Robert Schuman;10 Route D'ugny;02300;Chauny;49.6265626;3.1986091 +0280947C;LycĂ©e Ens Prof Agri PrivĂ© Du Perche;35 Rue De La Touche;28400;Nogent Le Rotrou;48.3111788;0.8100851 +0180758B;LycĂ©e Ens Prof PrivĂ© Agricole Bengy/lignieres;2 Rue Du Chanoine Volton;18520;Bengy Sur Craon;47.0017201;2.7487466 +0180757A;LycĂ©e Ens Prof PrivĂ© Agricole Site De Lignieres;10 Rue Raymond Chassagne;18160;Lignieres;46.7505044;2.1756904 +0410855G;LycĂ©e Ens Prof Rural PrivĂ© Boissay;;41120;Fougeres Sur Bievre;47.4478099;1.3411169 +0360710B;LycĂ©e Ens Prof Rural PrivĂ© De Lignac;Rue Alienor D Aquitaine;36370;Lignac;46.4667777;1.2176906 +0410854F;LycĂ©e Ens Prof Rural PrivĂ© Ste Cecile;12 Rue Saint Laurent;41800;Montoire Sur Le Loir;47.7521229;0.8632596 +0190056H;LycĂ©e Ens. General PrivĂ© Inst Jeanne D Arc;Rue Du Jardin Public;19400;Argentat;45.093215;1.940286 +0230040U;LycĂ©e Ens. General PrivĂ© Notre Dame;5 Av Du Docteur Manouvrier;23008;Gueret;46.167861;1.8743524 +0441787D;LycĂ©e Ensei Agricole PrivĂ© Saint-exupery;9 Rue Pierre Landais;44430;Le Loroux Bottereau;47.2379078;-1.3457308 +0280967Z;LycĂ©e Enseig Agricole PrivĂ© Efagrir;2 Rue Des Fleurs;28630;Mignieres;48.3595403;1.4288729 +0561463L;LycĂ©e Enseignement Agric PrivĂ© Sts Anges Kerlebost;St Thuriau;56306;Pontivy;48.0565733;-2.9246647 +0870864S;LycĂ©e Enseignement General Pri St Jean;26 Rue Eugene Varlin;87016;Limoges;45.8263601;1.2749309 +0400080V;LycĂ©e Enseignement Prof. Jean Cassaigne;Avenue Kennedy;40280;St Pierre Du Mont;43.869494;-0.489719 +0781070P;LycĂ©e Enseigt PrivĂ© Agricole Lepa Le Buat;21 Rue Du Buat;78580;Maule;48.906077;1.8409898 +0110984K;LycĂ©e Enst Agricole PrivĂ©;8 Avenue Georges Clemenceau;11200;Lezignan Corbieres;43.200028;2.7645461 +0341477W;LycĂ©e Enst Agricole PrivĂ© Bonne Terre;Route De Beziers;34120;Pezenas;43.448806;3.4123228 +0341474T;LycĂ©e Enst Agricole PrivĂ© La Gardiole;2 Rue Du Couvent;34770;Gigean;43.4990181;3.7125009 +0660631C;LycĂ©e Enst Agricole PrivĂ© Le Mas Blanc;Av Emmanuel Brousse;66760;Bourg Madame;42.4391763;1.9445521 +0341471P;LycĂ©e Enst Agricole PrivĂ© Les Buissonnets;1 Avenue De Nissan;34310;Capestang;43.3272313;3.0496255 +0480502K;LycĂ©e Enst Agricole PrivĂ© Terre Nouvelle;2 Av Martyrs De La Resistance;48100;Marvejols;44.554723;3.2925132 +0301324B;LycĂ©e Enst Prof Agric PrivĂ©;9 Route De Bezouce;30840;Meynes;43.8791144;4.5539775 +0221676C;LycĂ©e Enst Prof Agricole PrivĂ© Xavier Grall;8 Rue Lavergne;22605;Loudeac;48.1797422;-2.7566822 +0221680G;LycĂ©e Enst Prof PrivĂ© Rural;La Guerche;22100;St Helen;48.468966;-1.986689 +0758513W;LycĂ©e Etranger PrivĂ© Leonard De Vinci;12 Rue Sedillot;75007;Paris 07;48.8586491;2.3017117 +0592922F;LycĂ©e Europeen PrivĂ© Therese D'avila;254 Rue Nationale;59003;Lille;50.6306982;3.0475752 +0791005N;LycĂ©e Gen Et Tech Agricole;22 Rue De La Baritauderie;79302;Bressuire;46.838952;-0.4751457 +0160006V;LycĂ©e Gen Et Tech Agricole Angouleme-l Oisellerie;Logis De L'oisellerie;16400;La Couronne;45.625025;0.103934 +0170087C;LycĂ©e Gen Et Tech Agricole Georges Desclaude;Rue Georges Desclaude;17119;Saintes;45.7559394;-0.6430047 +0170393K;LycĂ©e Gen Et Tech Agricole Ind.laitiere Agro-alimentaire;Avenue Francois Miterrand;17700;Surgeres;46.104913;-0.751411 +0791057V;LycĂ©e Gen Et Tech Agricole Inst.rur.educ.orient.;47 Avenue De Poitiers;79170;Brioux Sur Boutonne;46.1527195;-0.2107398 +0790768F;LycĂ©e Gen Et Tech Agricole Jacques Bujault;Route De La Roche;79500;Melle;46.2272777;-0.1514867 +0790706N;LycĂ©e Gen Et Tech Agricole Les Sicaudieres;Boulevard De Nantes;79308;Bressuire;46.843654;-0.519819 +0491807Y;LycĂ©e Gen Et Tech Agricole Pr La Verzee;1 Rue Du Chevalier D'avoynes;49520;Combree;47.705533;-1.025685 +0160980D;LycĂ©e Gen Et Tech Agricole Roc Fleuri;6 Bd Des Grands Rocs;16700;Ruffec;46.0309545;0.2026405 +0171265H;LycĂ©e Gen Et Tech Agricole Saint Antoine;2 Allee Saint Antoine;17240;Bois;45.476325;-0.5915032 +0860718N;LycĂ©e Gen Et Tech Agricole Xavier Bernard;Venours;86480;Rouille;46.400326;0.072145 +0331683A;LycĂ©e Gen Et Techno Agricole;Avenue De La Republique;33430;Bazas;44.4336384;-0.2244781 +0470662P;LycĂ©e Gen Et Techno Agricole;Route De Francescas;47600;Nerac;44.1092632;0.3702156 +0780235G;LycĂ©e Gen Et Technol Agricole Centre D Enseign Zootechnique;Parc Du Chateau;78120;Rambouillet;48.6748927;1.807541 +0290018M;LycĂ©e Gen Et Technol Agricole De Brehoulou;Brehoulou;29170;Fouesnant;47.888817;-4.012919 +0290341N;LycĂ©e Gen Et Technol Agricole De L'aulne;Rocade Du Parc De Bihan;29150;Chateaulin;48.195389;-4.094207 +0290126E;LycĂ©e Gen Et Technol Agricole De Suscinio;Ploujean;29600;Morlaix;48.605845;-3.833374 +0120937D;LycĂ©e Gen Et Technol Agricole Epla De Rodez;La Roque;12033;Rodez;44.3824285;2.5858486 +0220470S;LycĂ©e Gen Et Technol Agricole Kernilien;Plouisy;22204;Guingamp;48.5763113;-3.1825986 +0560013K;LycĂ©e Gen Et Technol Agricole Le Gros Chene;;56308;Pontivy;48.0565733;-2.9246647 +0221031B;LycĂ©e Gen Et Technol Agricole Public;6 Rue Du Porhoet;22230;Merdrignac;48.190409;-2.4262933 +0350700W;LycĂ©e Gen Et Technol Agricole Theodore Monod;55 Avenue De La Bouvardiere;35651;Le Rheu;48.0998905;-1.8040923 +0290181P;LycĂ©e Gen Et Technol PrivĂ© Brest - Rive Droite - Javouhey;4 Rue Des Remparts;29238;Brest;48.3832192;-4.5023223 +0220094H;LycĂ©e Gen Et Technol PrivĂ© Cordeliers-n-d De La Victoire;1 Place Des Cordeliers;22102;Dinan;48.4538527;-2.0443642 +0220102S;LycĂ©e Gen Et Technol PrivĂ© Jacques Bossuet;4 Rue De La Bienfaisance;22304;Lannion;48.7287135;-3.4562299 +0220106W;LycĂ©e Gen Et Technol PrivĂ© Jean Xxiii;1 Rue Du Seminaire;22800;Quintin;48.3991817;-2.9154743 +0290335G;LycĂ©e Gen Et Technol PrivĂ© La Croix Rouge;2 Rue Mirabeau;29229;Brest;48.4075125;-4.4894481 +0290170C;LycĂ©e Gen Et Technol PrivĂ© Le Likes;20 Place De La Tourbie;29196;Quimper;47.9996048;-4.1024423 +0290177K;LycĂ©e Gen Et Technol PrivĂ© Nd Du Kreisker;2 Rue Cadiou;29250;St Pol De Leon;48.6825255;-3.9870981 +0220097L;LycĂ©e Gen Et Technol PrivĂ© Notre Dame;21 Rue Des Capucins;22205;Guingamp;48.5613277;-3.1433799 +0220107X;LycĂ©e Gen Et Technol PrivĂ© Notre Dame De Campostal;5 Place Du Bourg Coz;22110;Rostrenen;48.235163;-3.3159261 +0350769W;LycĂ©e Gen Et Technol PrivĂ© Notre Dame Des Marais;9 Bis Rue Eugene Pacory;35304;Fougeres;48.3569757;-1.2046288 +0290164W;LycĂ©e Gen Et Technol PrivĂ© Notre Dame Du Mur;10 Rue Guy Le Normand;29671;Morlaix;48.5731563;-3.8200636 +0560098C;LycĂ©e Gen Et Technol PrivĂ© Notre Dame Du Voeu;Rue Du Champ De Foire;56704;Hennebont;47.8067327;-3.2789444 +0950759J;LycĂ©e Gen Et Technol PrivĂ© Notre-dame De Bury;1 Av Georges Pompidou;95580;Margency;49.0026782;2.2838011 +0920928J;LycĂ©e Gen Et Technol PrivĂ© Passy Buzenval;50 Avenue Otis Mygatt;92508;Rueil Malmaison;48.8591268;2.1832515 +0910808J;LycĂ©e Gen Et Technol PrivĂ© St Charles;2 Rue G.anthonioz De Gaulle;91200;Athis Mons;48.7091074;2.3900057 +0781974X;LycĂ©e Gen Et Technol PrivĂ© St Exupery;11 Rue Michael Faraday;78180;Montigny Le Bretonneux;48.7952039;2.0497303 +0221589H;LycĂ©e Gen Et Technol PrivĂ© St Joseph;42 Rue De La Cheze;22600;Loudeac;48.174469;-2.7502399 +0291654R;LycĂ©e Gen Et Technol PrivĂ© St Joseph;61 Rue Bayard;29187;Concarneau;47.8740713;-3.9214211 +0290156M;LycĂ©e Gen Et Technol PrivĂ© St Louis;63 Grand'rue;29150;Chateaulin;48.1965682;-4.0868452 +0560101F;LycĂ©e Gen Et Technol PrivĂ© St Louis;5 Rue Perault;56102;Lorient;47.7448536;-3.3636004 +0350777E;LycĂ©e Gen Et Technol PrivĂ© St Martin;31 Rue D'antrain;35706;Rennes;48.1166798;-1.6785836 +0950762M;LycĂ©e Gen Et Technol PrivĂ© St Martin De France;1 Avenue De Verdun;95300;Pontoise;49.042589;2.0905789 +0560117Y;LycĂ©e Gen Et Technol PrivĂ© St Paul;12 Allee Gabriel Deshayes;56017;Vannes;47.6502167;-2.7624641 +0220111B;LycĂ©e Gen Et Technol PrivĂ© St Pierre;16 Rue St Pierre;22015;St Brieuc;48.5128487;-2.7660968 +0350774B;LycĂ©e Gen Et Technol PrivĂ© St Sauveur;16 Place St Sauveur;35603;Redon;47.6505318;-2.0844461 +0290159R;LycĂ©e Gen Et Technol PrivĂ© St Sebastien;4 Rue H De Guebriant;29208;Landerneau;48.4510307;-4.2578666 +0350783L;LycĂ©e Gen Et Technol PrivĂ© Ste Genevieve;14 Rue Ginguene;35006;Rennes;48.0990499;-1.6853943 +0560096A;LycĂ©e Gen Et Technol PrivĂ© Ste Jeanne D'arc;2 Rue Penantraon;56110;Gourin;48.1392054;-3.6092159 +0290171D;LycĂ©e Gen Et Technol PrivĂ© Ste Therese;56 Avenue De Remscheid;29337;Quimper;47.9811832;-4.0962165 +0920937U;LycĂ©e Gen Et Technol PrivĂ© Suger;11 Sente De L Abbe Suger;92420;Vaucresson;48.84389;2.1624469 +0470019R;LycĂ©e Gen Et Technol.agricole Etienne Restat;Route De Casseneuil;47110;Ste Livrade Sur Lot;44.4222789;0.6061423 +0594278E;LycĂ©e Gen. Et Tech. PrivĂ© LycĂ©e E.p.i.d.;20 Rue De Lille;59140;Dunkerque;51.0277456;2.3724543 +0381817U;LycĂ©e Gen. Et Tech.agricole Grenoble St-ismier;1 Chemin Charvinieres;38332;St Ismier;45.2413573;5.8229581 +0271107G;LycĂ©e Gen. Et Techn. Agricole;6 Rue Georges Politzer;27032;Evreux;49.0072785;1.150413 +0271016H;LycĂ©e Gen. Et Techn. Agricole De Chambray;Gouville;27240;Gouville;48.8505833;0.9909485 +0500116E;LycĂ©e Gen. Et Technol. PrivĂ© Germain;73 Rue D'ilkley;50208;Coutances;49.0608464;-1.4492908 +0520679F;LycĂ©e Gen. Et Technol. PrivĂ© Immaculee Conception;1 Bis Rue Du Mal De Lattre;52115;St Dizier;48.6385221;4.9486945 +0500112A;LycĂ©e Gen. Et Technol. PrivĂ© Notre Dame De La Providence;9 Rue Du Chanoine Beranger;50303;Avranches;48.686819;-1.3690481 +0070069M;LycĂ©e Gen. Et Technol. PrivĂ© Sacre Coeur - Notre Dame;6 Boulevard De Vernon;07000;Privas;44.7359923;4.6003035 +0730763H;LycĂ©e Gen. Et Technol. PrivĂ© Saint Ambroise;2 Rue Burdin;73025;Chambery;45.5703402;5.9246533 +0370737A;LycĂ©e Gen. Et Technol. PrivĂ© Saint Denis;19 Av Du Gal De Gaulle;37601;Loches;47.127138;0.9929978 +0141164P;LycĂ©e Gen. Et Technol. PrivĂ© Sainte Marie;8 Avenue De La Croix Guerin;14000;Caen;49.190183;-0.3565417 +0070066J;LycĂ©e Gen. Et Technol. PrivĂ© Vernet;21 Avenue Notre Dame;07700;Bourg St Andeol;44.3730123;4.6450101 +0212082Z;LycĂ©e Gen.et Tech. Agricole;7 Rue Du Champ De Foire;21140;Semur En Auxois;47.4953913;4.3360813 +0460727R;LycĂ©e Gen.et Tech. Agricole;;46120;Lacapelle Marival;44.72784;1.925505 +0861392W;LycĂ©e Gen.et Tech. Agricole Annexe Du LycĂ©e De Venours;45 Avenue Maurice Bailly;86400;Civray;46.1358095;0.287391 +0480514Y;LycĂ©e Gen.et Tech. Agricole Francois Rabelais;Civergols;48200;St Chely D Apcher;44.807858;3.302736 +0050012L;LycĂ©e Gen.et Tech. Agricole Hautes-alpes (des);Les Emeyeres 127 Rte Valserres;05000;Gap;44.5416045;6.0907911 +0480657D;LycĂ©e Gen.et Tech. Agricole Louis Pasteur;Chemin Du Fraissinet;48500;La Canourgue;44.4348979;3.242143 +0900246D;LycĂ©e Gen.et Tech. Agricole Lucien Quelet;95 Rue De Turenne;90300;Valdoie;47.6785817;6.8404106 +0030094L;LycĂ©e Gen.et Tech. Agricole Tourret;Neuville;03000;Neuvy;46.546982;3.287709 +0530081A;LycĂ©e Gen.et Tech.agricole;321 Route De St Nazaire;53013;Laval;48.0476546;-0.7976282 +0850151C;LycĂ©e Gen.et Tech.agricole Bel Air;1 Boulevard Hoche;85205;Fontenay Le Comte;46.4612234;-0.7962385 +0771436T;LycĂ©e Gen.et Tech.agricole Bougainville;Domaine De Sansalle Rn 19;77257;Brie Comte Robert;48.6912267;2.6103697 +0442061B;LycĂ©e Gen.et Tech.agricole Jules Rieffel;5 Rue De La Syonniere;44817;St Herblain;47.2468499;-1.6477727 +0720010V;LycĂ©e Gen.et Tech.agricole La Germiniere;;72700;Rouillon;48.00385;0.135487 +0850152D;LycĂ©e Gen.et Tech.agricole Lucon-petre;;85400;Ste Gemme La Plaine;46.483266;-1.113742 +0850144V;LycĂ©e Gen.et Tech.prof.agricol LycĂ©e Nature;Allee Des Druides;85035;La Roche Sur Yon;46.6641852;-1.3811329 +0890851S;LycĂ©e Gen.et Techn.agricole Annexe;3 Rte De Champcevrais;89350;Champignelles;47.7807971;3.0679892 +0741219Y;LycĂ©e Gen.et Techn.agricole H.b.de Saussure;;74920;Combloux;45.896937;6.641306 +0601700X;LycĂ©e Gen.et Technol PrivĂ© Sevigne;20 Rue De La Sous Prefecture;60200;Compiegne;49.415931;2.8299869 +0382373Y;LycĂ©e Gen.et Technol. Agricole Du Guiers Val D'ainan;6 Place Du Marche;38480;Le Pont De Beauvoisin;45.5367798;5.6698057 +0740082M;LycĂ©e Gen.et Technol. PrivĂ© Saint Michel;27 Faubourg Des Balmettes;74007;Annecy;45.8918267;6.1237631 +0230030H;LycĂ©e Gen.et Technol.agricole;Le Chaussadis;23150;Ahun;46.093558;2.030514 +0421088E;LycĂ©e Gen.et Technol.agricole;Le Bourg;42600;Precieux;45.568298;4.081896 +0640220S;LycĂ©e Gen.et Technol.agricole;Route De Pau;64121;Montardon;43.3616096;-0.3613466 +0690279D;LycĂ©e Gen.et Technol.agricole;234 Route De Charly;69230;St Genis Laval;45.6727335;4.7911863 +0711067Z;LycĂ©e Gen.et Technol.agricole;Poncetys;71960;Davaye;46.3067223;4.7375335 +0080863V;LycĂ©e Gen.et Technol.agricole;27 Rue Du Muguet;08090;St Laurent;49.7642188;4.7790123 +0180636U;LycĂ©e Gen.et Technol.agricole;Route De Barlieu;18260;Vailly Sur Sauldre;47.4622514;2.6472006 +0331424U;LycĂ©e Gen.et Technol.agricole;84 Av Du General De Gaulle;33294;Blanquefort;44.9172959;-0.6343214 +0381819W;LycĂ©e Gen.et Technol.agricole;57 Avenue Charles De Gaulle;38260;La Cote St Andre;45.3874636;5.2629127 +0400750Y;LycĂ©e Gen.et Technol.agricole;Heugas;40180;Heugas;43.6302977;-1.0735488 +0501213X;LycĂ©e Gen.et Technol.agricole;Route De Montmartin;50200;Coutances;49.047808;-1.4452609 +0601782L;LycĂ©e Gen.et Technol.agricole;;60600;Airion;49.425303;2.416204 +0610790C;LycĂ©e Gen.et Technol.agricole;Rue Du 11 Novembre 1918;61500;Sees;48.5959227;0.1730679 +0680003J;LycĂ©e Gen.et Technol.agricole;8 Aux Remparts;68250;Rouffach;47.9555409;7.3007998 +0711068A;LycĂ©e Gen.et Technol.agricole;10 Lieu Dit La Platiere;71150;Fontaines;46.859713;4.771907 +0820559M;LycĂ©e Gen.et Technol.agricole;Dom Capou 1915 Rte De Bordeaux;82000;Montauban;44.0324358;1.3154167 +0881070K;LycĂ©e Gen.et Technol.agricole;Avenue De Lattre De Tassigny;88500;Mirecourt;48.2920391;6.1305618 +0060793E;LycĂ©e Gen.et Technol.agricole Agricole Et Horticole;1285 Avenue Jules Grec;06600;Antibes;43.5991017;7.1129973 +0830077H;LycĂ©e Gen.et Technol.agricole Agricole Et Horticole;Carrefour Du LycĂ©e;83408;Hyeres;0.0;0.0 +0381886U;LycĂ©e Gen.et Technol.agricole Agrotec;Montee Bon Accueil;38206;Vienne;45.5465829;4.8648632 +0890045R;LycĂ©e Gen.et Technol.agricole Auxerre-la-brosse;La Brosse;89290;Venoy;47.7928286;3.6535285 +0320039U;LycĂ©e Gen.et Technol.agricole Beaulieu-lavacant;Route De Tarbes;32020;Auch;43.6233119;0.5770361 +0251263M;LycĂ©e Gen.et Technol.agricole Besancon Granvelle;2 Rue Des Chanets;25410;Dannemarie Sur Crete;47.2020212;5.873984 +0190244M;LycĂ©e Gen.et Technol.agricole Brive-objat;23 Rue Murat;19130;Voutezac;45.2831337;1.4250998 +0593395V;LycĂ©e Gen.et Technol.agricole Communaute Urbaine De Lille;Rue De La Mitterie;59463;Lille;50.6487941;2.9947205 +9710804X;LycĂ©e Gen.et Technol.agricole Convenance;Convenance;97122;Baie Mahault;16.2370417;-61.5986037 +9720616M;LycĂ©e Gen.et Technol.agricole Croix Rivail;Croix Rivail;97232;Le Lamentin;14.6131612;-60.9523142 +0511196Y;LycĂ©e Gen.et Technol.agricole De Chalons En Champagne;;51460;Somme Vesle;48.985722;4.590599 +0020060E;LycĂ©e Gen.et Technol.agricole De Crezancy;1 Rue De Paris;02650;Crezancy;49.0498047;3.5155052 +0730813M;LycĂ©e Gen.et Technol.agricole De La Motte Servolex;Domaine Reinach;73290;La Motte Servolex;45.596488;5.874112 +0080804F;LycĂ©e Gen.et Technol.agricole De Rethel;Route De Novion;08300;Rethel;49.5285468;4.3743447 +0211135V;LycĂ©e Gen.et Technol.agricole Dijon-quetigny;21 Bd Olivier De Serres;21800;Quetigny;47.3091823;5.1138121 +0593255T;LycĂ©e Gen.et Technol.agricole Du Nord;458 Rue De La Motte Julien;59507;Douai;50.3892001;3.0651718 +0622257S;LycĂ©e Gen.et Technol.agricole Du Pas-de-calais;Route De Cambrai;62217;Tilloy Les Mofflaines;50.274208;2.83736 +0681222J;LycĂ©e Gen.et Technol.agricole Du Pflixbourg;2 Lieu Dit Saint Gilles;68920;Wintzenheim;48.073122;7.289782 +0251202W;LycĂ©e Gen.et Technol.agricole Ecole D'industrie Laitiere;Grande Rue;25620;Mamirolle;47.196804;6.158412 +0390812W;LycĂ©e Gen.et Technol.agricole Ecole D'industrie Laitiere;Rue De Versailles;39801;Poligny;46.83417;5.705002 +0390810U;LycĂ©e Gen.et Technol.agricole Edgar Faure;614 Avenue Edgar Faure;39570;Montmorot;46.689511;5.5099358 +0010059J;LycĂ©e Gen.et Technol.agricole Edouard Herriot;Domaine De Cibeins;01600;Miserieux;45.9973531;4.8236968 +9740078S;LycĂ©e Gen.et Technol.agricole Emile Boyer De La Giroday;165 Route De Mafate;97460;St Paul;0.0;0.0 +0810121S;LycĂ©e Gen.et Technol.agricole Epla D Albi;Fonlabour Route De Toulouse;81000;Albi;43.9185017;2.1151666 +0312354R;LycĂ©e Gen.et Technol.agricole Epla D Ondes;Rue Castelnau;31330;Ondes;43.782461;1.316476 +0460490H;LycĂ©e Gen.et Technol.agricole Epla De Figeac;La Vinadie;46100;Figeac;44.573023;2.028841 +0090022A;LycĂ©e Gen.et Technol.agricole Epla De Pamiers;Route De Belpech;09100;Pamiers;43.12866;1.6549179 +0311262D;LycĂ©e Gen.et Technol.agricole Epla De Toulouse Auzeville;2 Rte De Narbonne;31326;Castanet Tolosan;43.5348091;1.4844659 +0120621K;LycĂ©e Gen.et Technol.agricole Epla Villefranche De Rouergue;Beauregard Cote Mas De Bonnet;12204;Villefranche De Rouergue;44.349746;2.035381 +0240023V;LycĂ©e Gen.et Technol.agricole Et Agro-alimentaire;Avenue Winston Churchill;24660;Coulounieix Chamiers;45.1872222;0.6922222 +0700827U;LycĂ©e Gen.et Technol.agricole Etienne Munier;Le Grand Montmarin;70014;Vesoul;47.619788;6.15428 +0660699B;LycĂ©e Gen.et Technol.agricole Federico Garcia Lorca;Route Nationale 114;66200;Theza;42.6020137;2.9695758 +0100687T;LycĂ©e Gen.et Technol.agricole Forestier De Crogny;Rue Des Etangs;10210;Les Loges Margueron;48.0863362;4.0991274 +0740927F;LycĂ©e Gen.et Technol.agricole Industrie Laitiere;;74805;La Roche Sur Foron;46.0666904;6.3112656 +0623277A;LycĂ©e Gen.et Technol.agricole Institut Agricole Saint Eloi;36 Rue Marcellin Gaudefroy;62452;Bapaume;50.1027923;2.8464795 +0650139Y;LycĂ©e Gen.et Technol.agricole Jean Monnet Epla Vic-en-big;11 Bis Promenade Des Acacias;65500;Vic En Bigorre;43.3823572;0.0457979 +0211217J;LycĂ©e Gen.et Technol.agricole La Barotte;La Barotte Rte De Langres;21403;Chatillon Sur Seine;47.85948;4.614945 +0690250X;LycĂ©e Gen.et Technol.agricole La Bruyere;26 Chemin De La Bruyere;69570;Dardilly;45.813486;4.769397 +0450094H;LycĂ©e Gen.et Technol.agricole Le Chesnoy;Le Chesnoy;45200;Amilly;47.971965;2.7392439 +0801272Y;LycĂ©e Gen.et Technol.agricole Le Paraclet;Le Paraclet Route De Cottenchy;80440;Boves;49.8249135;2.3942367 +0260765R;LycĂ©e Gen.et Technol.agricole Le Valentin;Route Nationale 7;26500;Bourg Les Valence;44.9965985;4.8429604 +0180585N;LycĂ©e Gen.et Technol.agricole Legta;Rue Theophile Gauthier;18028;Bourges;47.1059678;2.412406 +0280706R;LycĂ©e Gen.et Technol.agricole Legta;La Saussaye;28630;Sours;48.406038;1.556544 +0360017Y;LycĂ©e Gen.et Technol.agricole Legta;Route De Velles;36018;Chateauroux;46.7822046;1.6898082 +0360600G;LycĂ©e Gen.et Technol.agricole Legta;16 Rue H De Latouche;36400;La Chatre;46.5790648;1.9898776 +0370781Y;LycĂ©e Gen.et Technol.agricole Legta;La Petite Plaine;37230;Fondettes;47.399712;0.6381559 +0410018X;LycĂ©e Gen.et Technol.agricole Legta;Rue De La Valle Du Loir;41106;Vendome;47.8004267;1.0920294 +0451535Z;LycĂ©e Gen.et Technol.agricole Les Barres;Les Barres;45290;Nogent Sur Vernisson;47.838247;2.756901 +0010819K;LycĂ©e Gen.et Technol.agricole Les Sardieres;79 Avenue De Jasseron;01000;Bourg En Bresse;46.2116792;5.2565142 +0570112M;LycĂ©e Gen.et Technol.agricole LycĂ©e Agricole Departemental;40 Route De Strasbourg;57170;Chateau Salins;48.8187729;6.515628 +0570086J;LycĂ©e Gen.et Technol.agricole Metz-courcelles-chaussy;1 3 Av D Urville;57530;Courcelles Chaussy;49.1086404;6.38191 +0331863W;LycĂ©e Gen.et Technol.agricole Montagne;Le Bourg;33570;Montagne;44.971429;-0.141059 +0541207U;LycĂ©e Gen.et Technol.agricole Nancy-pixerecourt;Pixerecourt;54220;Malzeville;48.7304099;6.1804006 +0071125K;LycĂ©e Gen.et Technol.agricole Olivier De Serres;;07205;Aubenas;44.2136489;3.999401 +0550752U;LycĂ©e Gen.et Technol.agricole Philippe De Vilmorin;Chemin De Popey;55000;Bar Le Duc;48.7618197;5.1849786 +0623723K;LycĂ©e Gen.et Technol.agricole Radinghem;Rue Neuve;62310;Radinghem;50.55078;2.117642 +0421078U;LycĂ©e Gen.et Technol.agricole Roanne-cherve;;42120;Perreux;46.0376199;4.122751 +6200190M;LycĂ©e Gen.et Technol.agricole Sartene;Route De Levie;20100;Sartene;41.621822;8.97472 +0500060U;LycĂ©e Gen.et Technol.agricole St Lo There;;50620;Le Hommet D Arthenay;49.189776;-1.188463 +0950786N;LycĂ©e Gen.et Technol.prive St Rosaire;53 Rue Pierre Brossolette;95200;Sarcelles;48.9919938;2.3747125 +9830504F;LycĂ©e Gen.et Technol.prive Apollinaire Anova (ddec);24 C Rte Bretelle Savexpress;98890;Paita;0.0;0.0 +0690539L;LycĂ©e Gen.et Technol.prive Assomption Bellevue;39 Quai Jean Jacques Rousseau;69350;La Mulatiere;45.7310895;4.8137961 +0690522T;LycĂ©e Gen.et Technol.prive Aux Lazaristes;24 Montee Saint Barthelemy;69321;Lyon 05;45.7638385;4.826226 +0940822H;LycĂ©e Gen.et Technol.prive Batiment Et Travaux Publics;18 Rue De Belfort;94307;Vincennes;48.8501369;2.4311329 +0880099E;LycĂ©e Gen.et Technol.prive Beau Jardin;9 Rue Du Beau Jardin;88104;St Die Des Vosges;48.2892768;6.9523158 +0870081R;LycĂ©e Gen.et Technol.prive Beaupeyrat;9 Ter Rue Petiniaud Beaupeyrat;87036;Limoges;45.8280651;1.2550534 +0300077W;LycĂ©e Gen.et Technol.prive Bellevue;14 Rue Alfred De Musset;30100;Ales;44.1324998;4.0852321 +0690536H;LycĂ©e Gen.et Technol.prive Belmont-capdepon;43 Rue Pasteur;69007;Lyon 07;45.7526828;4.8397656 +0641907A;LycĂ©e Gen.et Technol.prive Bernat Etxepare;12 Bis Av Mounede;64100;Bayonne;43.4986058;-1.4580377 +9830261S;LycĂ©e Gen.et Technol.prive Blaise Pascal (ddec);22 Rue B. Pascal A. Vata;98807;Noumea;-22.2980465;166.4424651 +0783282U;LycĂ©e Gen.et Technol.prive Blanche De Castille;1 Avenue De La Breteche;78150;Le Chesnay;48.8295148;2.1317755 +0772324H;LycĂ©e Gen.et Technol.prive Campus Sainte Therese;Av Erasme - Rond Pt De L'europe;77330;Ozoir La Ferriere;48.763827;2.671739 +9741256X;LycĂ©e Gen.et Technol.prive Catholique Levavasseur;4 Rue Fenelon;97400;St Denis;48.7876977;2.5414736 +0442083A;LycĂ©e Gen.et Technol.prive Cens;La Joneliere;44244;La Chapelle Sur Erdre;47.2794482;-1.5401117 +0430056C;LycĂ©e Gen.et Technol.prive Cevenol;;43400;Le Chambon Sur Lignon;45.061316;4.3021269 +0490952U;LycĂ©e Gen.et Technol.prive Champ Blanc;Champ Blanc;49710;Le Longeron;47.016606;-1.067741 +0691654Y;LycĂ©e Gen.et Technol.prive Champagnat;250 Rue Chanoine Pavailler;69590;St Symphorien Sur Coise;45.6291908;4.4521899 +0542408Z;LycĂ©e Gen.et Technol.prive Charles De Foucauld;1 Rue Jeannot;54000;Nancy;48.6894923;6.1881706 +0690514J;LycĂ©e Gen.et Technol.prive Charles De Foucauld;6 Rue Bara;69003;Lyon 03;45.7514974;4.871623 +0754086J;LycĂ©e Gen.et Technol.prive Charles De Foucauld;5 Rue De La Madone;75018;Paris 18;48.8927022;2.3612027 +0753890W;LycĂ©e Gen.et Technol.prive Charles Peguy;54 Avenue De La Republique;75011;Paris 11;48.8649983;2.375791 +0530049R;LycĂ©e Gen.et Technol.prive D'avesnieres;51 Rue D Avesnieres;53012;Laval;48.0625624;-0.7676675 +0441982R;LycĂ©e Gen.et Technol.prive De Bretagne;15 Rue Du Landreau;44300;Nantes;47.2324995;-1.5206435 +0570311D;LycĂ©e Gen.et Technol.prive De La Salle;2 Rue St Maximin;57070;Metz;49.1048183;6.1935928 +0690542P;LycĂ©e Gen.et Technol.prive Des Chassagnes;13 Chemin Des Chassagnes;69600;Oullins;45.7227891;4.8097245 +0541309E;LycĂ©e Gen.et Technol.prive Des Recollets;44 Rue Du General Pershing;54400;Longwy;49.5170129;5.7657012 +0060773H;LycĂ©e Gen.et Technol.prive Don Bosco;40 Place Don Bosco;06046;Nice;43.7086006;7.2813297 +0211068X;LycĂ©e Gen.et Technol.prive Du Saint Coeur;3 Rue Du Faubourg St Nicolas;21200;Beaune;47.0279594;4.8395949 +0672050P;LycĂ©e Gen.et Technol.prive Ecole Commerciale PrivĂ©e;19 Rue De Wissembourg;67000;Strasbourg;48.591122;7.741682 +0300080Z;LycĂ©e Gen.et Technol.prive Emmanuel D Alzon;11 Rue Sainte Perpetue;30020;Nimes;43.834299;4.3718 +0010069V;LycĂ©e Gen.et Technol.prive Ensemble Scolaire Lamartine;41 Rue Georges Girerd;01302;Belley;45.7612095;5.6848602 +0941719H;LycĂ©e Gen.et Technol.prive Epin;19 Avenue Eugene Pelletan;94400;Vitry Sur Seine;48.7942096;2.3847676 +9730010Y;LycĂ©e Gen.et Technol.prive Externat Saint Joseph;15 Rue Lallouette;97326;Cayenne;4.9393005;-52.3308137 +0690521S;LycĂ©e Gen.et Technol.prive Externat Sainte Marie;4 Montee Saint Barthelemy;69321;Lyon 05;45.7655825;4.8268167 +0060674A;LycĂ©e Gen.et Technol.prive Fenelon;7 Avenue Yves Emmanuel Baudouin;06130;Grasse;43.6589192;6.9208425 +0761708U;LycĂ©e Gen.et Technol.prive Fenelon;2 Rue D'alsace;76501;Elbeuf;49.29007;1.0141247 +0932036H;LycĂ©e Gen.et Technol.prive Francoise Cabrini;20 Rue Du Docteur Sureau;93167;Noisy Le Grand;48.8461618;2.5491228 +0593007Y;LycĂ©e Gen.et Technol.prive Frederic Ozanam;50 Rue Saint Gabriel;59045;Lille;50.6393334;3.0867728 +0440175B;LycĂ©e Gen.et Technol.prive Gabriel Deshayes;Route De Redon;44530;St Gildas Des Bois;47.5223759;-2.044929 +0631068U;LycĂ©e Gen.et Technol.prive Godefroy De Bouillon;14 Rue Godefroy De Bouillon;63037;Clermont Ferrand;45.781122;3.0929055 +0370727P;LycĂ©e Gen.et Technol.prive Grand St Gregoire;3 Quai Paul Bert;37081;Tours;47.4014833;0.685655 +0940821G;LycĂ©e Gen.et Technol.prive Gregor Mendel;205 Rue De Fontenay;94300;Vincennes;48.8488616;2.4272778 +0601702Z;LycĂ©e Gen.et Technol.prive Guynemer;8 Bis Av De La Foret;60205;Compiegne;49.4087315;2.840105 +0910823A;LycĂ©e Gen.et Technol.prive Ile De France;7 Rue Du Baron De Niviere;91140;Villebon Sur Yvette;48.7000351;2.2570051 +0640130U;LycĂ©e Gen.et Technol.prive Immac.concept.beau Frene;Bd Edouard Herriot;64051;Pau;43.3050247;-0.3782249 +0530048P;LycĂ©e Gen.et Technol.prive Immaculee Conception;15 Rue Crossardiere;53000;Laval;48.0734452;-0.7680567 +0592965C;LycĂ©e Gen.et Technol.prive Industriel Et Commercial PrivĂ©;27 Rue Du Dragon;59202;Tourcoing;50.7191345;3.1488557 +9720070U;LycĂ©e Gen.et Technol.prive Institut Martiniquais D'etudes;74 Rue Du Pr Raymond Garcin;97200;Fort De France;14.609371;-61.07256 +0595657D;LycĂ©e Gen.et Technol.prive Institut Saint Vincent De Paul;6 Rue Du Marechal Joffre;59120;Loos;50.6162933;3.0052777 +0500120J;LycĂ©e Gen.et Technol.prive Institut Saint-lo;18 Rue De L'oratoire;50180;Agneaux;49.1184709;-1.1060995 +0681917P;LycĂ©e Gen.et Technol.prive Institution Don Bosco;60 Rue D'ensisheim;68272;Wittenheim;47.8136036;7.3394336 +0671610L;LycĂ©e Gen.et Technol.prive Institution Notre Dame;6 Rue Des Bonnes Gens;67082;Strasbourg;48.5880447;7.7433479 +0671602C;LycĂ©e Gen.et Technol.prive Institution Ste Philomene;19a Boulevard Hanauer;67504;Haguenau;48.8113152;7.7888563 +0622150A;LycĂ©e Gen.et Technol.prive Jean Bosco;19 Place Du Marechal Foch;62340;Guines;50.869395;1.8697706 +0761721H;LycĂ©e Gen.et Technol.prive Jean Xxiii;16 Rue De La Gare;76191;Yvetot;49.6223882;0.7592408 +0850076W;LycĂ©e Gen.et Technol.prive Jean Xxiii;Avenue Des Sables;85505;Les Herbiers;46.8650254;-1.0292923 +0690671E;LycĂ©e Gen.et Technol.prive Jean-baptiste De La Salle;1 Rue Neyret;69283;Lyon 01;45.7715543;4.8280213 +0761715B;LycĂ©e Gen.et Technol.prive Jean-baptiste De La Salle;84 Rue Saint-gervais;76042;Rouen;49.4481293;1.08442 +0541320S;LycĂ©e Gen.et Technol.prive Jean-baptiste Vatelot;6 Rue De La Republique;54203;Toul;48.6729066;5.8919085 +0132300E;LycĂ©e Gen.et Technol.prive Jeanne D Arc;5 Chemin Saint Roch;13632;Arles;43.6743556;4.6432771 +0610694Y;LycĂ©e Gen.et Technol.prive Jeanne D Arc;10 Rue Du College;61203;Argentan;48.7461287;-0.0164951 +0680151V;LycĂ©e Gen.et Technol.prive Jeanne D Arc;15 Rue Du Chanoine Brun;68090;Mulhouse;47.7434603;7.3370328 +0910815S;LycĂ©e Gen.et Technol.prive Jeanne D Arc;11 Bd Henri Iv;91150;Etampes;48.4358438;2.1576582 +0920918Y;LycĂ©e Gen.et Technol.prive Jeanne D Arc;44 Rue Gabriel Peri;92120;Montrouge;48.8187323;2.3185306 +0921663H;LycĂ©e Gen.et Technol.prive Jeanne D Arc;9 Boulevard De Valmy;92700;Colombes;48.9248684;2.2526402 +0390091M;LycĂ©e Gen.et Technol.prive Jeanne D'arc;10 Rue Du Sauget;39303;Champagnole;46.7456228;5.9058307 +0710112L;LycĂ©e Gen.et Technol.prive Jeanne D'arc;17 Rue Pasteur;71600;Paray Le Monial;46.4526677;4.1219752 +0880097C;LycĂ©e Gen.et Technol.prive Jeanne D'arc;6 Rue Du Canton;88202;Remiremont;48.0192476;6.5872824 +0690535G;LycĂ©e Gen.et Technol.prive Jeanne De Lestonnac;132 Rue Vendome;69006;Lyon 06;45.7648477;4.8456527 +0761716C;LycĂ©e Gen.et Technol.prive Join-lambert;39 Rue De L'avalasse;76044;Rouen;49.4489928;1.0980641 +0070064G;LycĂ©e Gen.et Technol.prive Jules Froment;3 Rue Albert Seibel;07201;Aubenas;44.6172525;4.3886254 +0331495W;LycĂ©e Gen.et Technol.prive L Assomption;370 Bd President Wilson;33000;Bordeaux;44.8520362;-0.5944037 +0783283V;LycĂ©e Gen.et Technol.prive L Ermitage;18 Rue Des Cotes;78600;Maisons Laffitte;48.9430565;2.1455384 +0430063K;LycĂ©e Gen.et Technol.prive La Bruyere;1 Boulevard Des Passementiers;43140;St Didier En Velay;45.2992188;4.2760259 +0132828D;LycĂ©e Gen.et Technol.prive La Cadenelle;134 Bd Des Liberateurs;13012;Marseille 12;43.3020176;5.4572235 +0430055B;LycĂ©e Gen.et Technol.prive La Chartreuse;Rue Du Pont De La Chartreuse;43700;Brives Charensac;45.0529219;3.9138113 +0690520R;LycĂ©e Gen.et Technol.prive La Favorite Sainte Therese;62 Rue De La Favorite;69322;Lyon 05;45.7578457;4.8120645 +0601703A;LycĂ©e Gen.et Technol.prive La Maison Francaise;La Chesnoye;60350;Cuise La Motte;49.372412;2.996137 +9710062R;LycĂ©e Gen.et Technol.prive La Perseverance Mixte;Boissard;97182;Pointe A Pitre;16.2386048;-61.5386617 +0021872Z;LycĂ©e Gen.et Technol.prive La Providence;2 Rue Clerjot;02001;Laon;49.5636589;3.6226864 +0761707T;LycĂ©e Gen.et Technol.prive La Providence;Chemin Des Vertus;76371;Dieppe;49.905225;1.074298 +0761713Z;LycĂ©e Gen.et Technol.prive La Providence;6 Rue De Neuvillette;76240;Le Mesnil Esnard;49.4064428;1.1403376 +0763112V;LycĂ©e Gen.et Technol.prive La Providence;35 Rue Queue De Renard;76400;Fecamp;49.7565044;0.3888415 +0920916W;LycĂ©e Gen.et Technol.prive La Source;11 Rue Ernest Renan;92190;Meudon;48.8188276;2.2268149 +0340871M;LycĂ©e Gen.et Technol.prive La Trinite;6 Avenue Jean Moulin;34503;Beziers;43.3467234;3.219544 +0640111Y;LycĂ©e Gen.et Technol.prive Largente;42 Avenue Jacques Loeb;64115;Bayonne;43.4776655;-1.4841692 +0541305A;LycĂ©e Gen.et Technol.prive L'assomption;1 Rue Foch;54150;Briey;49.2499091;5.9385613 +0783297K;LycĂ©e Gen.et Technol.prive Le Bon Sauveur;6 Rue Henri Cloppet;78110;Le Vesinet;48.8873186;2.1463532 +0040034R;LycĂ©e Gen.et Technol.prive Le Sacre Coeur;2 Avenue Des Thermes;04000;Digne Les Bains;44.0917238;6.2407469 +0251024C;LycĂ©e Gen.et Technol.prive Les Augustins;1 Rue Du Fbg St Etienne;25300;Pontarlier;46.9020595;6.3592476 +0783293F;LycĂ©e Gen.et Technol.prive Les Chataigniers;11 Bis Avenue Jean Jaures;78000;Versailles;48.8174674;2.1442539 +0860755D;LycĂ©e Gen.et Technol.prive Les Feuillants;15 Rue Des Feuillants;86035;Poitiers;46.5844278;0.3491711 +0753820V;LycĂ©e Gen.et Technol.prive Les Francs Bourgeois;21 Rue Saint Antoine;75004;Paris 04;48.8535159;2.3657551 +9710991A;LycĂ©e Gen.et Technol.prive Les Perseverants;28 Rue Lardenoy;97100;Basse Terre;15.9932345;-61.7242003 +0753815P;LycĂ©e Gen.et Technol.prive Les Petits Champs;19 Rue Voltaire;75011;Paris 11;48.8525604;2.3915183 +0840059H;LycĂ©e Gen.et Technol.prive Louis Pasteur;13 Rue Du Pont Trouca;84071;Avignon;43.9478109;4.8132766 +0754965P;LycĂ©e Gen.et Technol.prive Lucien De Hirsch;70 Avenue Secretan;75019;Paris 19;48.8797122;2.3764183 +9840017W;LycĂ©e Gen.et Technol.prive LycĂ©e-college La Mennais;Papeete;98714;Papeete;-17.535022;-149.569594 +0921365J;LycĂ©e Gen.et Technol.prive Madeleine Danielou;61 63 Rue Du General Miribel;92500;Rueil Malmaison;48.8614432;2.1711774 +9710054G;LycĂ©e Gen.et Technol.prive Maitrise De Massabielle;29 Faubourg Victor Hugo;97157;Pointe A Pitre;16.241111;-61.533056 +0592980U;LycĂ©e Gen.et Technol.prive Marie Noel;31 Rue De Renaix;59200;Tourcoing;50.7228956;3.1689614 +0801743K;LycĂ©e Gen.et Technol.prive Montalembert;3 Route D Amiens;80600;Doullens;50.153554;2.3391028 +0331506H;LycĂ©e Gen.et Technol.prive Montesquieu;31 Rue Jules Simon;33500;Libourne;44.9133394;-0.2462284 +0490828J;LycĂ©e Gen.et Technol.prive N-d De Bonnes Nouvelles;3 Rue Mongazon;49600;Beaupreau;47.1982814;-0.9966793 +0850079Z;LycĂ©e Gen.et Technol.prive Nd De La Tourteliere;Rue Buffon;85700;Pouzauges;46.773501;-0.835248 +0720834R;LycĂ©e Gen.et Technol.prive Nd De Ste Croix;25 Rue Antoine De St Exupery;72000;Le Mans;48.0000944;0.2133997 +0950761L;LycĂ©e Gen.et Technol.prive N-dame De La Compassion;8 Place Nicolas Flamel;95300;Pontoise;49.0520594;2.0991487 +0920919Z;LycĂ©e Gen.et Technol.prive N-dame De Ste Croix;30 Avenue Du Roule;92200;Neuilly Sur Seine;48.8819566;2.2806369 +0783351U;LycĂ©e Gen.et Technol.prive N-dame Du Grandchamp;97 Rue Royale;78000;Versailles;48.7933549;2.1239896 +0280665W;LycĂ©e Gen.et Technol.prive Notre Dame;2 Avenue Bethouart;28000;Chartres;48.4511272;1.4921613 +0720837U;LycĂ©e Gen.et Technol.prive Notre Dame;23 Av Francois Mitterrand;72000;Le Mans;48.0038785;0.1997462 +0783288A;LycĂ©e Gen.et Technol.prive Notre Dame;3 Rue De Temara;78100;St Germain En Laye;48.8940298;2.0754316 +0783289B;LycĂ©e Gen.et Technol.prive Notre Dame;5 Rue De La Sangle;78200;Mantes La Jolie;48.9886407;1.7215203 +0783344L;LycĂ©e Gen.et Technol.prive Notre Dame;106 Grande Rue;78480;Verneuil Sur Seine;48.9826866;1.9733268 +0830100H;LycĂ©e Gen.et Technol.prive Notre Dame;29 Boulevard Abbe Duploye;83100;Toulon;43.1279722;5.956097 +0920897A;LycĂ©e Gen.et Technol.prive Notre Dame;1 Avenue Charles De Gaulle;92100;Boulogne Billancourt;48.8467426;2.2368542 +0880101G;LycĂ©e Gen.et Technol.prive Notre Dame - Saint Joseph;23 Rue Thiers;88012;Epinal;48.1701687;6.4513835 +0693267B;LycĂ©e Gen.et Technol.prive Notre Dame De Bel Air;5 Avenue Des Belges;69170;Tarare;45.8904997;4.4380599 +0660059F;LycĂ©e Gen.et Technol.prive Notre Dame De Bon Secours;39 Avenue Julien Panchot;66028;Perpignan;42.6915814;2.8827875 +0340881Y;LycĂ©e Gen.et Technol.prive Notre Dame De La Merci;62 Cours Gambetta;34965;Montpellier;43.6082615;3.8686748 +0570313F;LycĂ©e Gen.et Technol.prive Notre Dame De La Providence;22 Place Notre Dame;57126;Thionville;49.3576246;6.1602375 +0950753C;LycĂ©e Gen.et Technol.prive Notre Dame De La Providence;7 Boulevard Sadi Carnot;95880;Enghien Les Bains;48.967214;2.3122446 +0601701Y;LycĂ©e Gen.et Technol.prive Notre Dame De La Tilloye;1 Avenue De La Liberation;60204;Compiegne;49.4062037;2.8305249 +0170103V;LycĂ©e Gen.et Technol.prive Notre Dame De Recouvrance;88 Cours Genet;17100;Saintes;45.7486442;-0.65005 +0910816T;LycĂ©e Gen.et Technol.prive Notre Dame De Sion;1 Avenue De Ratisbonne;91000;Evry;48.6437303;2.4357094 +0900029T;LycĂ©e Gen.et Technol.prive Notre Dame Des Anges;46 Bis Faubourg De Montbeliard;90000;Belfort;47.6334827;6.8570422 +0410675L;LycĂ©e Gen.et Technol.prive Notre Dame Des Aydes;7 Rue Franciade;41000;Blois;47.5897279;1.3293085 +0381683Y;LycĂ©e Gen.et Technol.prive Notre Dame Des Victoires;1 Rue De La Terrasse;38506;Voiron;45.367053;5.59632 +0430058E;LycĂ©e Gen.et Technol.prive Notre Dame Du Chateau;Montee Du Prince;43120;Monistrol Sur Loire;45.292432;4.172183 +0470063N;LycĂ©e Gen.et Technol.prive Notre Dame La Compassion;1 Ter Rue De Langeot;47200;Marmande;44.4978288;0.1723321 +0480025S;LycĂ©e Gen.et Technol.prive Notre-dame;Quartier Fontanilles;48000;Mende;44.517611;3.501873 +0920906K;LycĂ©e Gen.et Technol.prive Notre-dame;65 Avenue Du General Leclerc;92340;Bourg La Reine;48.7817469;2.3162608 +0920917X;LycĂ©e Gen.et Technol.prive Notre-dame;24 Rue Alexandre Guilmant;92190;Meudon;48.8157744;2.2403627 +0950785M;LycĂ©e Gen.et Technol.prive Notre-dame;106 Bd Charles De Gaulle;95110;Sannois;48.9730488;2.2543252 +0592916Z;LycĂ©e Gen.et Technol.prive Notre-dame De Grace;Quai Des Nerviens;59602;Maubeuge;50.2764329;3.9688679 +0753842U;LycĂ©e Gen.et Technol.prive Notre-dame De Sion;61 R Notre Dame Des Champs;75006;Paris 06;48.84375;2.3310368 +0593036E;LycĂ©e Gen.et Technol.prive Notre-dame Du Tilleul;48 Place De L'industrie;59600;Maubeuge;50.2706855;3.9481957 +0951940T;LycĂ©e Gen.et Technol.prive Ozar Hatorah;1 Rue Jean Lurcat;95200;Sarcelles;48.9827985;2.3890294 +0754666P;LycĂ©e Gen.et Technol.prive Passy-saint-honore;117 Avenue Victor Hugo;75016;Paris 16;48.8682833;2.282365 +9710055H;LycĂ©e Gen.et Technol.prive Pensionnat De Versailles;8 Rue Victor Hugues;97100;Basse Terre;16.1985639;-61.5901551 +0341521U;LycĂ©e Gen.et Technol.prive Pierre Rouge;85 Rue Lunaret;34090;Montpellier;43.6207134;3.8837999 +0381666E;LycĂ©e Gen.et Technol.prive Pierre Termier;5 Bis Rue Joseph Fourier;38028;Grenoble;45.1889828;5.7349988 +0341765J;LycĂ©e Gen.et Technol.prive Rabelais;36 Rue Des Aiguerelles;34000;Montpellier;43.6026039;3.883383 +0920898B;LycĂ©e Gen.et Technol.prive Rambam;11 Rue Des Abondances;92100;Boulogne Billancourt;48.8431743;2.2294952 +0761718E;LycĂ©e Gen.et Technol.prive Rey;15 Rue Verte;76000;Rouen;49.4479795;1.0934318 +0941720J;LycĂ©e Gen.et Technol.prive Robert Schuman;5-6 Rue De L'eglise;94340;Joinville Le Pont;48.8192804;2.4664616 +0381678T;LycĂ©e Gen.et Technol.prive Robin;Place Saint Pierre;38204;Vienne;45.5234014;4.8708709 +0430065M;LycĂ©e Gen.et Technol.prive Sacre Coeur;11 Place Charles De Gaulle;43200;Yssingeaux;45.1433694;4.1235441 +0492015Z;LycĂ©e Gen.et Technol.prive Sacre Coeur;2 Rue Millet;49101;Angers;47.4727929;-0.556212 +0801207C;LycĂ©e Gen.et Technol.prive Sacre Coeur;1 Rue De L Oratoire;80007;Amiens;49.8933658;2.3044821 +0801226Y;LycĂ©e Gen.et Technol.prive Sacre Coeur;36 Boulevard Des Anglais;80200;Peronne;49.9264177;2.9342576 +0910824B;LycĂ©e Gen.et Technol.prive Sacre Coeur;Passage De Graville;91620;La Ville Du Bois;48.6609531;2.2674756 +0131862D;LycĂ©e Gen.et Technol.prive Sacre Coeur (le);29 Rue Manuel;13100;Aix En Provence;43.5282729;5.453657 +0761719F;LycĂ©e Gen.et Technol.prive Sacre-coeur;31 32 Rue Blaise Pascal;76176;Rouen;49.4297216;1.0808737 +0631075B;LycĂ©e Gen.et Technol.prive Saint Alyre;20 Rue Sainte George;63037;Clermont Ferrand;45.7834986;3.0818986 +0030084A;LycĂ©e Gen.et Technol.prive Saint Benoit;4 Rue Achille Roche;03008;Moulins;46.5632283;3.3286114 +0690563M;LycĂ©e Gen.et Technol.prive Saint Bruno-saint Louis;16 Rue Des Chartreux;69283;Lyon 01;45.7719779;4.8230673 +0382854W;LycĂ©e Gen.et Technol.prive Saint Charles;1 Place Des Capucins;38217;Vienne;45.5275795;4.8766132 +0450110A;LycĂ©e Gen.et Technol.prive Saint Charles;24 Rue Des Grands Champs;45058;Orleans;47.9055842;1.8998948 +0790058J;LycĂ©e Gen.et Technol.prive Saint Charles;1 Rue Jules Michelet;79101;Thouars;46.9785341;-0.2100378 +0580062C;LycĂ©e Gen.et Technol.prive Saint Cyr;22 Rue Jeanne D Arc;58000;Nevers;46.991543;3.1530512 +0071126L;LycĂ©e Gen.et Technol.prive Saint Denis;1 Chemin De La Muette;07104;Annonay;45.2411669;4.6779477 +0601699W;LycĂ©e Gen.et Technol.prive Saint Esprit;68 Rue De Pontoise;60026;Beauvais;49.4192451;2.086629 +0593136N;LycĂ©e Gen.et Technol.prive Saint Jacques;58 Rue De La Sous Prefecture;59190;Hazebrouck;50.7232207;2.5301757 +0251028G;LycĂ©e Gen.et Technol.prive Saint Jean;1 Rue De L'esperance;25000;Besancon;47.2589558;6.0204846 +0133822J;LycĂ©e Gen.et Technol.prive Saint Jean De Garguier;Chemin Du Puits;13420;Gemenos;43.2845374;5.6190148 +0141173Z;LycĂ©e Gen.et Technol.prive Saint Jean Eudes;2 Avenue De La Gare;14503;Vire;48.8457455;-0.8844612 +0010075B;LycĂ©e Gen.et Technol.prive Saint Joseph;101 Rue Henri Grobon;01705;Miribel;45.825157;4.9512247 +0021871Y;LycĂ©e Gen.et Technol.prive Saint Joseph;2 Chaussee De Fontaine;02140;Fontaine Les Vervins;49.8365787;3.906547 +0030445T;LycĂ©e Gen.et Technol.prive Saint Joseph;11 Rue Du Fg De La Gironde;03100;Montlucon;46.3411588;2.6080734 +0371306U;LycĂ©e Gen.et Technol.prive Saint Joseph;1 Place Saint Mexme;37502;Chinon;47.167917;0.24509 +0740100G;LycĂ©e Gen.et Technol.prive Saint Joseph;5 Avenue Du Leman;74200;Thonon Les Bains;46.3774405;6.4835126 +0830098F;LycĂ©e Gen.et Technol.prive Saint Joseph;2229 Route De Faveyrolles;83190;Ollioules;43.139436;5.8845344 +0572340J;LycĂ©e Gen.et Technol.prive Saint Joseph-la Providence;2 Avenue Du General Passaga;57600;Forbach;49.1853591;6.9006303 +0592926K;LycĂ©e Gen.et Technol.prive Saint Jude;18 Rue Lamartine;59280;Armentieres;50.6867725;2.8756446 +0430053Z;LycĂ©e Gen.et Technol.prive Saint Julien;7 Place Du Valla;43101;Brioude;45.2911231;3.3878825 +0170098P;LycĂ©e Gen.et Technol.prive Saint Louis;73 Avenue Andre Malraux;17250;Pont L Abbe D Arnoult;45.829429;-0.8705604 +0550047C;LycĂ©e Gen.et Technol.prive Saint Louis;28 Rue Voltaire;55001;Bar Le Duc;48.7719202;5.1608526 +0160062F;LycĂ©e Gen.et Technol.prive Saint Paul;101 Rue De Beaulieu;16007;Angouleme;45.65054;0.1489427 +0251021Z;LycĂ©e Gen.et Technol.prive Saint Paul;8 Boulevard Diderot;25000;Besancon;47.2453023;6.0351931 +0622114L;LycĂ©e Gen.et Technol.prive Saint Paul;38 Route De La Bassee;62301;Lens;50.4365139;2.8216873 +0420973E;LycĂ©e Gen.et Technol.prive Saint Paul Forez;13 Rue Du College;42603;Montbrison;45.6084481;4.0659129 +0010070W;LycĂ©e Gen.et Technol.prive Saint Pierre;7 Rue Villeneuve;01001;Bourg En Bresse;46.1996249;5.2230636 +0030072M;LycĂ©e Gen.et Technol.prive Saint Pierre;26 Allee Pierre Berthomier;03304;Cusset;46.1342557;3.4580004 +0622110G;LycĂ©e Gen.et Technol.prive Saint Pierre;72 Rue Arago;62100;Calais;50.9413829;1.8641701 +0801206B;LycĂ©e Gen.et Technol.prive Saint Pierre;24 Place Clemenceau;80103;Abbeville;50.1087199;1.8347016 +0021874B;LycĂ©e Gen.et Technol.prive Saint Remy;8 Rue St Jean;02204;Soissons;49.3763018;3.3235831 +0801874C;LycĂ©e Gen.et Technol.prive Saint Riquier;50 Chaussee Jules Ferry;80094;Amiens;49.8829134;2.3258793 +0690543R;LycĂ©e Gen.et Technol.prive Saint Thomas D'aquin Veritas;56 Rue Du Perron;69600;Oullins;45.711866;4.8086923 +0601149Y;LycĂ©e Gen.et Technol.prive Saint Vincent;30 Rue De Meaux;60304;Senlis;49.203401;2.5889031 +0271053Y;LycĂ©e Gen.et Technol.prive Saint-adjutor;54 Rue De Marzelles;27200;Vernon;49.0859189;1.4575149 +0680149T;LycĂ©e Gen.et Technol.prive Saint-andre;19 Rue Rapp;68025;Colmar;48.0805738;7.3597557 +0271282X;LycĂ©e Gen.et Technol.prive Saint-anselme;11/13 R Leprevost De Beaumont;27300;Bernay;49.0942864;0.6045856 +0420974F;LycĂ©e Gen.et Technol.prive Sainte Anne;4 Rue Saint Alban;42300;Roanne;46.0363817;4.0633804 +0550049E;LycĂ©e Gen.et Technol.prive Sainte Anne;14 Rue Mautrote;55104;Verdun;49.1604635;5.3812032 +0801209E;LycĂ©e Gen.et Technol.prive Sainte Famille;13 Rue De Castille;80017;Amiens;49.8882835;2.3096003 +0550048D;LycĂ©e Gen.et Technol.prive Sainte Jeanne D'arc;23 Rue Poincare;55200;Commercy;48.7606796;5.5902284 +0592928M;LycĂ©e Gen.et Technol.prive Sainte Jeanne D'arc;157 Rue De L'hotel De Ville;59620;Aulnoye Aymeries;50.2001783;3.8323903 +0060670W;LycĂ©e Gen.et Technol.prive Sainte Marie;4 Avenue Windsor;06400;Cannes;43.5514185;7.0307566 +0390070P;LycĂ©e Gen.et Technol.prive Sainte Marie Fenelon;84 Rue St Desire;39000;Lons Le Saunier;46.6706079;5.5489595 +0030073N;LycĂ©e Gen.et Technol.prive Sainte Procule;22 Rue Des Augustins;03800;Gannat;46.0996289;3.1938761 +0631070W;LycĂ©e Gen.et Technol.prive Sainte Thecle;7 Rue Amelie Murat;63402;Chamalieres;45.7771931;3.0670184 +0592929N;LycĂ©e Gen.et Technol.prive Sainte Therese;4 Place Guillemin;59362;Avesnes Sur Helpe;50.1225956;3.9322081 +0753844W;LycĂ©e Gen.et Technol.prive Sainte-genevieve;64 Rue D'assas;75006;Paris 06;48.8458567;2.331337 +0753852E;LycĂ©e Gen.et Technol.prive Sainte-jeanne Elisabeth;8 Rue Maurice De La Sizeranne;75007;Paris 07;48.8473086;2.3147319 +0900030U;LycĂ©e Gen.et Technol.prive Sainte-marie;40 Fbg Des Ancetres;90006;Belfort;47.6405457;6.8573468 +0271045P;LycĂ©e Gen.et Technol.prive Saint-francois De Sales;8 Rue Portevin;27025;Evreux;49.0165487;1.1504096 +0761710W;LycĂ©e Gen.et Technol.prive Saint-joseph;207 Rue Felix Faure;76072;Le Havre;49.5005512;0.1056686 +0681793E;LycĂ©e Gen.et Technol.prive Saint-joseph De Cluny;53 Avenue Roger Salengro;68100;Mulhouse;47.7509394;7.347119 +0271049U;LycĂ©e Gen.et Technol.prive Saint-ouen;30 Rue Sadi Carnot;27500;Pont Audemer;49.3571648;0.5145026 +0060677D;LycĂ©e Gen.et Technol.prive Sasserno;1 Place Sasserno;06000;Nice;43.7026106;7.2711176 +9720829U;LycĂ©e Gen.et Technol.prive Seminaire College Ste Marie;27 Rue Martin Luther King;97247;Fort De France;14.604913;-61.075087 +0911935J;LycĂ©e Gen.et Technol.prive Senart-enseignement;9 Rue De L Industrie;91210;Draveil;48.6837846;2.4333977 +0421034W;LycĂ©e Gen.et Technol.prive Sevigne;29 Rue Michelet;42000;St Etienne;45.434245;4.3898936 +0631033F;LycĂ©e Gen.et Technol.prive Sevigne Saint Louis;2 Avenue De La Liberation;63500;Issoire;45.5411893;3.2487329 +0920907L;LycĂ©e Gen.et Technol.prive Sophie Barat;50 Rue Des Grillons;92290;Chatenay Malabry;48.7579109;2.2704913 +0470060K;LycĂ©e Gen.et Technol.prive St Caprais;8 Rue Raspail;47000;Agen;44.2073871;0.6188656 +0640131V;LycĂ©e Gen.et Technol.prive St Dominique;30 Av Fouchet;64000;Pau;43.3139559;-0.3757428 +0920921B;LycĂ©e Gen.et Technol.prive St Dominique;23 Quater Bd D Argenson;92200;Neuilly Sur Seine;48.8881037;2.2648464 +0131391S;LycĂ©e Gen.et Technol.prive St Eloi;9 Avenue Jules Isaac;13626;Aix En Provence;43.5348838;5.4513323 +0783286Y;LycĂ©e Gen.et Technol.prive St Erembert;5 Rue Salomon Reinach;78100;St Germain En Laye;48.8958802;2.0996683 +0781899R;LycĂ©e Gen.et Technol.prive St Francois D Assise;6 Place Paul Claudel;78180;Montigny Le Bretonneux;48.7790268;2.0410595 +0920894X;LycĂ©e Gen.et Technol.prive St Gabriel;21 Rue De La Lisette;92220;Bagneux;48.7925201;2.3011991 +0401016M;LycĂ©e Gen.et Technol.prive St Jacques De Compostelle;32 Rue Lahargou;40100;Dax;43.6951039;-1.057711 +0783350T;LycĂ©e Gen.et Technol.prive St Jean Hulst;26 R Mal De Lattre De Tassigny;78000;Versailles;48.8179183;2.1368041 +0021873A;LycĂ©e Gen.et Technol.prive St Jean La Croix;25 Rue Antoine Lecuyer;02105;St Quentin;49.85011;3.2855838 +0240079F;LycĂ©e Gen.et Technol.prive St Joseph;23 Avenue Georges Pompidou;24000;Perigueux;45.1889317;0.7230677 +0440149Y;LycĂ©e Gen.et Technol.prive St Joseph;66 Rue Du College;44153;Ancenis;47.3665584;-1.1794334 +0440151A;LycĂ©e Gen.et Technol.prive St Joseph;40 Rue Michel Grimault;44141;Chateaubriant;47.7156138;-1.3765267 +0440201E;LycĂ©e Gen.et Technol.prive St Joseph;14 Rue Des Capucins;44270;Machecoul;46.9932154;-1.8232442 +0541308D;LycĂ©e Gen.et Technol.prive St Joseph;413 Avenue De Boufflers;54524;Laxou;48.6934935;6.1381862 +0640137B;LycĂ©e Gen.et Technol.prive St Joseph;650 Rue Hiribehere;64480;Ustaritz;43.4045104;-1.462666 +0840072X;LycĂ©e Gen.et Technol.prive St Joseph;62 Rue Des Lices;84000;Avignon;43.9455461;4.8107194 +9720063L;LycĂ©e Gen.et Technol.prive St Joseph De Cluny;22 Route De Cluny;97200;Fort De France;14.627187;-61.0738508 +0131339K;LycĂ©e Gen.et Technol.prive St Joseph De La Madeleine;172 B Bd De La Liberation;13248;Marseille 04;43.3020176;5.3944529 +0331502D;LycĂ©e Gen.et Technol.prive St Joseph De Tivoli;40 Av D Eysines;33073;Bordeaux;44.8538365;-0.5966724 +0601831P;LycĂ©e Gen.et Technol.prive St Joseph Du Moncel;8 Place De L Eglise;60722;Pont Ste Maxence;49.3158812;2.6215379 +0131331B;LycĂ©e Gen.et Technol.prive St Joseph Les Maristes;24 Rue Sainte Victoire;13006;Marseille 06;43.2862953;5.382018 +0771237B;LycĂ©e Gen.et Technol.prive St Laurent-la Paix Notre Dame;43 Rue Alfred Brebion;77400;Lagny Sur Marne;48.8734226;2.7086692 +0490838V;LycĂ©e Gen.et Technol.prive St Louis;47 Rue D Alsace;49400;Saumur;47.2588834;-0.0854513 +0133314G;LycĂ©e Gen.et Technol.prive St Louis - Ste Marie;Allee Saint Louis - Rn 568;13180;Gignac La Nerthe;43.390916;5.233463 +0430059F;LycĂ©e Gen.et Technol.prive St Louis Notre Dame De France;5 Rue Latour Maubourg;43010;Le Puy En Velay;45.042305;3.8789494 +0910826D;LycĂ©e Gen.et Technol.prive St Louis St Clement;1 Rue Margot;91170;Viry Chatillon;48.6718555;2.372288 +0530046M;LycĂ©e Gen.et Technol.prive St Michel;5 Rue Henri Dunant;53204;Chateau Gontier;47.8312371;-0.7100198 +0922353H;LycĂ©e Gen.et Technol.prive St Nicolas;19 Rue Victor Hugo;92130;Issy Les Moulineaux;48.8264404;2.2758007 +0280667Y;LycĂ©e Gen.et Technol.prive St Pierre St Paul;16 Boulevard Dubois;28109;Dreux;48.7375877;1.3745466 +0640134Y;LycĂ©e Gen.et Technol.prive St Thomas D Aquin;Rue Biscarbidea;64500;St Jean De Luz;43.388051;-1.663055 +0920908M;LycĂ©e Gen.et Technol.prive St Thomas De Villeneuve;1646 Avenue Roger Salengro;92370;Chaville;48.8075482;2.1862517 +0131403E;LycĂ©e Gen.et Technol.prive St Vincent De Paul;30 Rue Stanislas Torrents;13006;Marseille 06;43.287572;5.3782713 +0060673Z;LycĂ©e Gen.et Technol.prive Stanislas;1 Place Stanislas;06403;Cannes;43.5468601;6.9368506 +0490819Z;LycĂ©e Gen.et Technol.prive Ste Agnes;7 Rue Volney;49000;Angers;47.464216;-0.5472721 +0920889S;LycĂ©e Gen.et Technol.prive Ste Genevieve;19 Rue De La Station;92600;Asnieres Sur Seine;48.9068047;2.2853198 +0132810J;LycĂ©e Gen.et Technol.prive Ste Marie;13 Rue Jeu De Ballon;13400;Aubagne;43.2916499;5.5711486 +0920875B;LycĂ©e Gen.et Technol.prive Ste Marie;24 Boulevard Victor Hugo;92200;Neuilly Sur Seine;48.8850578;2.2811906 +0331501C;LycĂ©e Gen.et Technol.prive Ste Marie De La Bastide;45 Rue De Dijon;33100;Bordeaux;44.8425946;-0.5526472 +0920904H;LycĂ©e Gen.et Technol.prive Ste Marie La Croix;2 Rue De L Abbaye;92160;Antony;48.7539117;2.2999629 +0180561M;LycĂ©e Gen.et Technol.prive Ste Marie St Dominique;38 Rue Jean Baffier;18020;Bourges;47.077094;2.4032838 +0240076C;LycĂ©e Gen.et Technol.prive Ste Marthe - St Front;74 Av Pasteur;24100;Bergerac;44.8601889;0.4951121 +0781664K;LycĂ©e Gen.et Technol.prive Ste Therese;7 Rue Beziel;78120;Rambouillet;48.6454729;1.8336261 +0370731U;LycĂ©e Gen.et Technol.prive Ste Ursule;26 Rue Emile Zola;37009;Tours;47.3938697;0.6906468 +0940878U;LycĂ©e Gen.et Technol.prive Teilhard De Chardin;2 Place D'armes;94100;St Maur Des Fosses;48.8125041;2.4720666 +0420979L;LycĂ©e Gen.et Technol.prive Tezenas Du Montcel;14 Place Girodet;42000;St Etienne;45.4480193;4.3809644 +0501299R;LycĂ©e Gen.et Technol.prive Thomas Helye;37 Rue Emile Zola;50100;Cherbourg Octeville;49.6370758;-1.6291084 +0951048Y;LycĂ©e Gen.et Technol.prive Torat Emet;14 Av Charles Peguy;95200;Sarcelles;48.9811624;2.3754349 +0860903P;LycĂ©e Gen.et Technol.prive Union Chretienne;2 Plan Sainte Croix;86011;Poitiers;46.5792977;0.3487512 +0951221L;LycĂ©e Gen.et Technol.prive Vauban;23 Place Du Petit Martroy;95300;Pontoise;49.0513181;2.0969748 +0671611M;LycĂ©e Gen.prive Instit.la Doctrine Chretienne;14 Rue Brulee;67000;Strasbourg;48.584854;7.752502 +9830484J;LycĂ©e Gen.tech.prof.agricole De Pouembout (agri);Route Munipale 2;98825;Pouembout;0.0;0.0 +0580584V;LycĂ©e Gen.techn.agricole Les Cottereaux;66 Rue Jean Monnet;58200;Cosne Cours Sur Loire;47.3910652;2.9324174 +0190609J;LycĂ©e Gen.technolo Agricole Ecole Forestiere;Meymac;19250;Meymac;45.5362265;2.14547 +0190087S;LycĂ©e Gen.technolo. Agricole Henri Queuille;;19160;Neuvic;45.382449;2.272544 +0422132P;LycĂ©e General;16 Rue Arquilliere;42130;Boen;45.745278;4.004247 +0694069Y;LycĂ©e General;Les Grands Champs;69210;Sain Bel;45.822538;4.599975 +0922615T;LycĂ©e General;13 17 Avenue D Alsace;92400;Courbevoie;48.8908791;2.2503094 +0590212K;LycĂ©e General;80 Boulevard Gambetta;59208;Tourcoing;50.7133268;3.1588623 +0140004D;LycĂ©e General Alain Chartier;Place De La Lombarderie;14402;Bayeux;49.278971;-0.712644 +0180005H;LycĂ©e General Alain Fournier;50 Rue Stephane Mallarme;18016;Bourges;47.1000872;2.4108852 +0590063Y;LycĂ©e General Albert Chatelet;357 Rue Marceline;59508;Douai;50.3738413;3.0855523 +0260022H;LycĂ©e General Albert Triboulet;55 Avenue Gambetta;26102;Romans Sur Isere;45.0453925;5.0568298 +0140043W;LycĂ©e General Andre Maurois;10 Boulevard Cornuche;14800;Deauville;49.3628042;0.0747539 +0290069T;LycĂ©e General Auguste Brizeux;6 Rue Bourg Les Bourgs;29191;Quimper;47.9930436;-4.1116997 +0590182C;LycĂ©e General Baudelaire;23 Avenue Le Notre;59100;Roubaix;50.6804598;3.1673908 +0720030S;LycĂ©e General Bellevue;2 Rue Abbaye St Vincent;72001;Le Mans;48.0121822;0.2021824 +0810005R;LycĂ©e General Bellevue;98 Rue Du Roc;81011;Albi;43.9221442;2.1574435 +0870045B;LycĂ©e General Bernard Palissy;11 R Leon Jouhaux;87400;St Leonard De Noblat;45.8344936;1.4931518 +0240024W;LycĂ©e General Bertran De Born;1 Rue Ch Mangold;24001;Perigueux;45.1802367;0.7199966 +0030044G;LycĂ©e General Blaise De Vigenere;51 Avenue Pasteur;03500;St Pourcain Sur Sioule;46.3112153;3.2927033 +0630018C;LycĂ©e General Blaise Pascal;36 Avenue Carnot;63037;Clermont Ferrand;45.7762478;3.0934525 +0750693W;LycĂ©e General Buffon;16 Boulevard Pasteur;75015;Paris 15;48.8434549;2.3117609 +0101028N;LycĂ©e General Camille Claudel;28 Rue Des Terrasses;10026;Troyes;48.2918918;4.0787778 +0860035W;LycĂ©e General Camille Guerin;33 Rue De La Gibauderie;86022;Poitiers;46.5687695;0.3635822 +0760093N;LycĂ©e General Camille Saint-saens;22 Rue Saint-lo;76005;Rouen;49.4422597;1.0937285 +0750694X;LycĂ©e General Camille See;11 Rue Leon Lhermitte;75015;Paris 15;48.8430219;2.2972982 +0260035X;LycĂ©e General Camille Vernet;160 Rue Faventines;26021;Valence;44.9243431;4.9036578 +0060011E;LycĂ©e General Carnot;90 Boulevard Carnot;06408;Cannes;43.5636838;7.0164947 +0750704H;LycĂ©e General Carnot;145 Boulevard Malesherbes;75017;Paris 17;48.8845308;2.3078305 +0380027Y;LycĂ©e General Champollion;Cours La Fontaine;38026;Grenoble;45.1878714;5.7248743 +0080006N;LycĂ©e General Chanzy;13 Rue Delvincourt;08000;Charleville Mezieres;49.7725131;4.730864 +0480007X;LycĂ©e General Chaptal;Avenue Paulin Daude;48001;Mende;44.520214;3.508123 +0570106F;LycĂ©e General Charlemagne;17 Avenue Clemenceau;57100;Thionville;49.3603724;6.1679017 +0750652B;LycĂ©e General Charlemagne;14 Rue Charlemagne;75004;Paris 04;48.8541627;2.3609691 +0142107P;LycĂ©e General Charles De Gaulle;39 Rue D'hastings;14000;Caen;49.1844909;-0.3794536 +0211928G;LycĂ©e General Charles De Gaulle;25 Av General Touzet Du Vigier;21000;Dijon;47.3495084;5.0418001 +0390012B;LycĂ©e General Charles Nodier;6 Grande Rue;39107;Dole;47.0907375;5.4933764 +0693446W;LycĂ©e General Cite Scolaire Internationale;2 Place De Montreal;69361;Lyon 07;0.0;0.0 +0420041S;LycĂ©e General Claude Fauriel;28 Avenue De La Liberation;42007;St Etienne;45.4373521;4.3929933 +0880020U;LycĂ©e General Claude Gellee;44 Rue Abel Ferry;88021;Epinal;48.1665516;6.4548634 +0740003B;LycĂ©e General Claude Louis Berthollet;9 Boulevard Du LycĂ©e;74008;Annecy;0.0;0.0 +0750683K;LycĂ©e General Claude Monet;1 Rue Du Docteur Magnan;75013;Paris 13;48.8274487;2.3622395 +0511901P;LycĂ©e General Colbert;56 Rue Du Dr Schweitzer;51100;Reims;49.2803364;4.0213284 +0750673Z;LycĂ©e General Colbert;27 Rue De Chateau Landon;75010;Paris 10;48.88268;2.3634216 +0750667T;LycĂ©e General Condorcet;8 Rue Du Havre;75009;Paris 09;48.8747904;2.3269255 +0190011J;LycĂ©e General D Arsonval;Place Du 15 Aout 1944;19100;Brive La Gaillarde;45.1605553;1.530861 +0320025D;LycĂ©e General D'artagnan;Avenue Des Pyrenees;32110;Nogaro;43.754576;-0.029898 +0490001K;LycĂ©e General David D Angers;1 Rue Paul Langevin;49035;Angers;47.4653426;-0.5453201 +0290007A;LycĂ©e General De Kerichen;Rue Prince De Joinville;29801;Brest;48.4043354;-4.4826021 +0290010D;LycĂ©e General De L'harteloire;1 Rue Du Guesclin;29213;Brest;48.4284698;-4.5611577 +0290009C;LycĂ©e General De L'iroise;7 Place De Strasbourg;29223;Brest;48.4018349;-4.4663852 +0332846P;LycĂ©e General Des Graves;238 Cours Du Gl De Gaulle;33173;Gradignan;44.774267;-0.618945 +0590024F;LycĂ©e General Des Nerviens;Place Charles De Gaulle;59570;Bavay;50.2991014;3.7947585 +0370035M;LycĂ©e General Descartes;10 Rue Des Minimes;37010;Tours;47.3914217;0.6900424 +0090018W;LycĂ©e General Du Couserans;Esplanade Mendes France;09201;St Girons;42.9780405;1.1514912 +0260008T;LycĂ©e General Du Diois;Rue Maurice Faure;26150;Die;44.757877;5.367689 +0690026D;LycĂ©e General Du Parc;1 Boulevard Anatole France;69458;Lyon 06;45.7714439;4.8568367 +0490040C;LycĂ©e General Duplessis Mornay;1 Rue Duruy;49408;Saumur;47.255425;-0.077033 +0940117S;LycĂ©e General Edouard Branly;8 Rue Bauyn De Perreuse;94130;Nogent Sur Marne;48.8349733;2.4828212 +0150646W;LycĂ©e General Emile Duclaux;16 Avenue Henri Mondor;15005;Aurillac;44.9269037;2.439407 +0260034W;LycĂ©e General Emile Loubet;2 Rue Du LycĂ©e;26021;Valence;0.0;0.0 +0141112H;LycĂ©e General Et Tech Agricole Le Robillard;Le Robillard;14170;St Pierre Sur Dives;48.98834;-0.005216 +0810068J;LycĂ©e General Et Techno. PrivĂ© D'amboise;13 Boulevard Carnot;81000;Albi;43.9227213;2.1450617 +0741469V;LycĂ©e General Et Techno. PrivĂ© Demotz De La Salle;2 Rue Du College;74150;Rumilly;45.867922;5.9444106 +0121438Y;LycĂ©e General Et Techno. PrivĂ© Francois D'estaing;22 Boulevard Denys Puech;12056;Rodez;44.3491284;2.5776399 +0820044C;LycĂ©e General Et Techno. PrivĂ© Institut Familial;1 Allee De Mortarieu;82005;Montauban;44.0151243;1.356479 +0120051R;LycĂ©e General Et Techno. PrivĂ© Jeanne D'arc;3 Place Du Mandarous;12104;Millau;44.0996885;3.0784006 +0810079W;LycĂ©e General Et Techno. PrivĂ© Jeanne D'arc;23 Rue De La Vanne;81207;Mazamet;43.4847739;2.3737673 +0320051G;LycĂ©e General Et Techno. PrivĂ© Oratoire Sainte-marie;50 Bis Rue Victor Hugo;32002;Auch;43.6505336;0.5776322 +0312355S;LycĂ©e General Et Techno. PrivĂ© Ort Maurice Grynfogel;14 Rue Etienne Collongues;31770;Colomiers;43.601951;1.333155 +0311147D;LycĂ©e General Et Techno. PrivĂ© Sainte-marie De Nevers;10 Rue Du Perigord;31070;Toulouse;43.6072684;1.4432532 +0311146C;LycĂ©e General Et Techno. PrivĂ© Sainte-marie Des Champs;169 Avenue Jean Rieux;31506;Toulouse;43.5912753;1.4691653 +0460038S;LycĂ©e General Et Techno. PrivĂ© Saint-etienne;49 Rue Des Soubirous;46000;Cahors;44.4504769;1.4412933 +0120059Z;LycĂ©e General Et Techno. PrivĂ© Saint-gabriel;23 Rue Lamartine;12402;St Affrique;43.956919;2.890886 +0120061B;LycĂ©e General Et Techno. PrivĂ© Saint-joseph;Avenue Etienne Soulie;12200;Villefranche De Rouergue;44.357108;2.0390698 +0120104Y;LycĂ©e General Et Techno. PrivĂ© Saint-joseph;1 Rue Sarrus;12000;Rodez;44.3451427;2.5715885 +0311145B;LycĂ©e General Et Techno. PrivĂ© Saint-joseph;85 Rue De Limayrac;31079;Toulouse;43.593452;1.4734636 +0311133N;LycĂ©e General Et Technol PrivĂ© Le Caousou;42 Avenue Camille Pujol;31079;Toulouse;43.6010623;1.4610488 +0810103X;LycĂ©e General Et Technol PrivĂ© Notre-dame;Avenue D'hauterive;81101;Castres;43.599188;2.244541 +0312744P;LycĂ©e General Et Technologique;Chemin De La Cepette;31860;Pins Justaret;43.4754398;1.3911496 +9750001C;LycĂ©e General Et Technologique;Rm Bonin;97500;St Pierre;0.0;0.0 +0090013R;LycĂ©e General Et Technologique;Route De Limoux;09500;Mirepoix;43.0849235;1.8953166 +0120006S;LycĂ©e General Et Technologique;Avenue Leo Lagrange;12300;Decazeville;44.5676272;2.2523499 +0670007U;LycĂ©e General Et Technologique;4 Place Du Chateau;67330;Bouxwiller;48.8259746;7.4840954 +0910625K;LycĂ©e General Et Technologique;2 Place De L Europe;91230;Montgeron;48.7050057;2.4537264 +9720694X;LycĂ©e General Et Technologique Acajou 1;Quartier Acajou;97285;Le Lamentin;0.0;0.0 +0610001V;LycĂ©e General Et Technologique Alain;27 Boulevard Mezeray;61014;Alencon;48.438637;0.0813898 +0782568T;LycĂ©e General Et Technologique Alain;25 Route De La Cascade;78110;Le Vesinet;48.8913223;2.1225849 +0260015A;LycĂ©e General Et Technologique Alain Borne;10 Place Du Theatre;26216;Montelimar;44.5872248;4.7021409 +0580753D;LycĂ©e General Et Technologique Alain Colas;Rue D'estutt De Tracy;58000;Nevers;46.98518;3.1285249 +0560051B;LycĂ©e General Et Technologique Alain Rene Lesage;20 Rue Winston Churchill;56017;Vannes;47.6477281;-2.7703646 +0060031B;LycĂ©e General Et Technologique Albert Calmette;5 Avenue Marechal Foch;06050;Nice;43.7040719;7.2711532 +0300023M;LycĂ©e General Et Technologique Albert Camus;51 Avenue Georges Pompidou;30911;Nimes;43.8363398;4.3463446 +0420013L;LycĂ©e General Et Technologique Albert Camus;32 Bis Rue De La Loire;42704;Firminy;45.3883922;4.2830196 +0440288Z;LycĂ©e General Et Technologique Albert Camus;11 Rue Etienne Coutan;44100;Nantes;47.2051598;-1.6079868 +0692517L;LycĂ©e General Et Technologique Albert Camus;Avenue Des Nations;69140;Rillieux La Pape;45.8212079;4.9071706 +0920132U;LycĂ©e General Et Technologique Albert Camus;131 Rue Pierre Joigneaux;92270;Bois Colombes;48.9106181;2.2595527 +0620166U;LycĂ©e General Et Technologique Albert Chatelet;Rue Cassin;62165;St Pol Sur Ternoise;50.38609;2.3445988 +0911346U;LycĂ©e General Et Technologique Albert Einstein;Av De La Liberte;91706;Ste Genevieve Des Bois;48.6400356;2.3486662 +0680031P;LycĂ©e General Et Technologique Albert Schweitzer;8 Bld De La Marne;68068;Mulhouse;47.7459702;7.3241297 +0930830X;LycĂ©e General Et Technologique Albert Schweitzer;11 Allee Valere Lefebvre;93340;Le Raincy;48.898921;2.5108023 +0420033H;LycĂ©e General Et Technologique Albert Thomas;20 Rue Albert Thomas;42328;Roanne;46.0440474;4.0740983 +0442309W;LycĂ©e General Et Technologique Alcide D'orbigny;Place De L Edit De Nantes;44830;Bouaye;47.2154657;-1.5678387 +0920801W;LycĂ©e General Et Technologique Alexandre Dumas;112 Bd De La Republique;92210;St Cloud;48.8493852;2.2138325 +0620161N;LycĂ©e General Et Technologique Alexandre Ribot;42 Rue Gambetta;62505;St Omer;50.7478277;2.2586747 +0061760F;LycĂ©e General Et Technologique Alexis De Tocqueville;22 Che De L Orme;06131;Grasse;43.6545971;6.9414071 +0500017X;LycĂ©e General Et Technologique Alexis De Tocqueville;34 Avenue Henri Poincare;50100;Cherbourg Octeville;49.6304753;-1.6077873 +0370016S;LycĂ©e General Et Technologique Alfred De Vigny;Rue Paul Delvaux;37600;Loches;47.1286731;0.976984 +0590060V;LycĂ©e General Et Technologique Alfred Kastler;123 Rue Paul Elie Casanova;59723;Denain;50.3293842;3.4072591 +0680015X;LycĂ©e General Et Technologique Alfred Kastler;5 Rue Du Luspel;68502;Guebwiller;47.9070545;7.2048104 +0850027T;LycĂ©e General Et Technologique Alfred Kastler;Boulevard Guitton;85020;La Roche Sur Yon;46.659238;-1.4360472 +0951399E;LycĂ©e General Et Technologique Alfred Kastler;26 Avenue De La Palette;95011;Cergy;49.0319888;2.0841331 +0540030P;LycĂ©e General Et Technologique Alfred Mezieres;3 Avenue Andre Malraux;54401;Longwy;49.5216573;5.7601943 +0860038Z;LycĂ©e General Et Technologique Alienor D Aquitaine;41 Rue Pierre De Coubertin;86034;Poitiers;46.5883777;0.3652348 +0130164H;LycĂ©e General Et Technologique Alphonse Daudet;1 Boulevard Jules Ferry;13150;Tarascon;43.8021567;4.6635779 +0300021K;LycĂ©e General Et Technologique Alphonse Daudet;3 Boulevard Victor Hugo;30039;Nimes;43.8358168;4.3578013 +0630077S;LycĂ©e General Et Technologique Ambroise Brugiere;44 Rue Des Planchettes;63039;Clermont Ferrand;45.7997921;3.1075266 +0530010Y;LycĂ©e General Et Technologique Ambroise Pare;17 Rue Du LycĂ©e;53013;Laval;0.0;0.0 +9740019C;LycĂ©e General Et Technologique Ambroise Vollard;3 Avenue De Soweto;97448;St Pierre;-21.3417856;55.4892561 +0060020P;LycĂ©e General Et Technologique Amiral De Grasse;20 Avenue Sainte Lorette;06130;Grasse;43.6523288;6.9209759 +9740471U;LycĂ©e General Et Technologique Amiral Pierre Bouvet;76 Rue Joseph Hubert;97470;St Benoit;0.0;0.0 +0290008B;LycĂ©e General Et Technologique Amiral Ronarc'h;3 Rue Mozart;29231;Brest;48.3959427;-4.5224841 +0690023A;LycĂ©e General Et Technologique Ampere;31 Rue De La Bourse;69289;Lyon 02;45.7650005;4.8368975 +0330010G;LycĂ©e General Et Technologique Anatole De Monzie;12 Cours Gambetta;33430;Bazas;44.4332382;-0.213939 +0620120U;LycĂ©e General Et Technologique Anatole France;64 Boulevard De Paris;62190;Lillers;50.5559013;2.4778684 +0381603L;LycĂ©e General Et Technologique Andre Argouges;61 Rue Leon Jouhaux;38029;Grenoble;45.1753906;5.7445124 +0931585T;LycĂ©e General Et Technologique Andre Boulloche;18 Boulevard Gutenberg;93190;Livry Gargan;48.9125431;2.5194358 +0271580W;LycĂ©e General Et Technologique Andre Malraux;59 Avenue Francois Mitterand;27607;Gaillon;49.160734;1.33341 +0620042J;LycĂ©e General Et Technologique Andre Malraux;314 Rue Jules Massenet;62408;Bethune;50.5200303;2.6516077 +0721548S;LycĂ©e General Et Technologique Andre Malraux;3 Rue De Beau Soleil;72700;Allonnes;47.9559769;0.1669132 +0670005S;LycĂ©e General Et Technologique Andre Maurois;1 Rue Du LycĂ©e;67242;Bischwiller;0.0;0.0 +0760029U;LycĂ©e General Et Technologique Andre Maurois;1 Rue De Lorraine;76503;Elbeuf;49.2906662;1.0159498 +0860009T;LycĂ©e General Et Technologique Andre Theuriet;;86400;Civray;46.148901;0.295258 +0352686E;LycĂ©e General Et Technologique Anita Conti;Esplanade Du LycĂ©e;35174;Bruz;0.0;0.0 +0690031J;LycĂ©e General Et Technologique Antoine De Saint Exupery;82 Rue Henon;69316;Lyon 04;45.7797287;4.8219853 +0570023R;LycĂ©e General Et Technologique Antoine De Saint-exupery;11 Avenue Saint Exupery;57290;Fameck;49.2470951;6.1087402 +9830507J;LycĂ©e General Et Technologique Antoine Kela;Rte Provinciale 3;98822;Poindimie;0.0;0.0 +9740787M;LycĂ©e General Et Technologique Antoine Roussin;25 Rue Leconte De Lisle;97450;St Louis;0.0;0.0 +0132733A;LycĂ©e General Et Technologique Antonin Artaud;25 Ch N D De La Consolation;13013;Marseille 13;43.3402529;5.4230519 +0750680G;LycĂ©e General Et Technologique Arago;4 Place De La Nation;75012;Paris 12;48.847931;2.3943586 +0011119L;LycĂ©e General Et Technologique Arbez Carme;1 Rue Pierre Et Marie Curie;01100;Bellignat;46.2508923;5.6331189 +0840026X;LycĂ©e General Et Technologique Arc (de L');346 Av Des Etudiants;84106;Orange;44.1415649;4.8019433 +0141274J;LycĂ©e General Et Technologique Arcisse De Caumont;3 Rue Baron Gerard;14402;Bayeux;49.2843359;-0.7097585 +0050007F;LycĂ©e General Et Technologique Aristide Briand;20 Avenue Commandant Dumont;05007;Gap;44.5662436;6.0836082 +0270016W;LycĂ©e General Et Technologique Aristide Briand;2 Rue Pierre Semard;27031;Evreux;49.0168441;1.1602102 +0440069L;LycĂ©e General Et Technologique Aristide Briand;10 Bd Pierre De Coubertin;44606;St Nazaire;47.2693774;-2.2334295 +0382780R;LycĂ©e General Et Technologique Aristides Berges;10 Avenue Aime Bouchayer;38171;Seyssinet Pariset;45.1846564;5.6945758 +0250058C;LycĂ©e General Et Technologique Armand Peugeot;30 Rue Des Carrieres;25702;Valentigney;47.4583749;6.8706804 +0240032E;LycĂ©e General Et Technologique Arnaud Daniel;Rue Couleau;24600;Riberac;45.245019;0.336231 +0132495S;LycĂ©e General Et Technologique Arthur Rimbaud;Quartier Des Salles;13808;Istres;43.513006;4.987968 +0595885B;LycĂ©e General Et Technologique Arthur Rimbaud;1075 Rue Paul Foucaut;59450;Sin Le Noble;50.3496401;3.1081053 +0540044E;LycĂ©e General Et Technologique Arthur Varoquaux;Rue Jean Moulin;54510;Tomblaine;48.6955865;6.2138163 +0590073J;LycĂ©e General Et Technologique Auguste Angellier;Bd Republique -fr. Mitterrand;59942;Dunkerque;51.0465854;2.4108807 +0620109G;LycĂ©e General Et Technologique Auguste Behal;6 Rue Paul Eluard;62300;Lens;50.4456289;2.8230467 +0610014J;LycĂ©e General Et Technologique Auguste Chevalier;7 Place Du Champ De Foire;61700;Domfront;48.5921466;-0.6422106 +0492061Z;LycĂ©e General Et Technologique Auguste Et Jean Renoir;15 Impasse Ampere;49035;Angers;47.4751203;-0.5682534 +0690035N;LycĂ©e General Et Technologique Auguste Et Louis Lumiere;50 Boulevard Des Etats Unis;69372;Lyon 08;45.7361542;4.8620526 +0220018A;LycĂ©e General Et Technologique Auguste Pavie;13 Rue Anatole Le Braz;22205;Guingamp;48.5585676;-3.146937 +0060009C;LycĂ©e General Et Technologique Auguste Renoir;18 Avenue Marcel Pagnol;06802;Cagnes Sur Mer;43.718958;7.1656502 +0870017W;LycĂ©e General Et Technologique Auguste Renoir;119 Rue Sainte-claire;87036;Limoges;45.8235722;1.2409963 +0920131T;LycĂ©e General Et Technologique Auguste Renoir;137 Rue Du Menil;92600;Asnieres Sur Seine;48.9221719;2.2821664 +0140014P;LycĂ©e General Et Technologique Augustin Fresnel;77 Rue Eustache Restout;14020;Caen;49.1638565;-0.3552215 +0270003G;LycĂ©e General Et Technologique Augustin Fresnel;14 Rue Kleber Mercier;27301;Bernay;49.0846704;0.5939843 +9710003B;LycĂ©e General Et Technologique Baimbridge;Boulevard Des Heros;97139;Les Abymes;16.2498118;-61.5230947 +0370036N;LycĂ©e General Et Technologique Balzac;36 Rue D Entraigues;37013;Tours;47.3878353;0.6872794 +0261099D;LycĂ©e General Et Technologique Barthelemy De Laffemas;Rue Barthelemy De Laffemas;26901;Valence;44.917059;4.915496 +0680007N;LycĂ©e General Et Technologique Bartholdi;9 Rue Du LycĂ©e;68025;Colmar;0.0;0.0 +0350022J;LycĂ©e General Et Technologique Beaumont;10 Rue Du LycĂ©e;35605;Redon;0.0;0.0 +0590093F;LycĂ©e General Et Technologique Beaupre;Avenue De Beaupre;59481;Haubourdin;50.6018;2.9906864 +0420018S;LycĂ©e General Et Technologique Beauregard;4 Avenue Paul Cezanne;42605;Montbrison;45.6137248;4.0570332 +0830050D;LycĂ©e General Et Technologique Beaussier;Qua Beaussier Place Galilee;83512;La Seyne Sur Mer;43.1000479;5.8780319 +9741046U;LycĂ©e General Et Technologique Bellepierre;Avenue Gaston Monnerville;97475;St Denis;48.830502;2.597217 +0170058W;LycĂ©e General Et Technologique Bellevue;1 Chemin Des Cotieres;17100;Saintes;45.7370327;-0.6435025 +9720003W;LycĂ©e General Et Technologique Bellevue;Rue Marie Therese Gertrude;97262;Fort De France;14.6040646;-61.084565 +0561534N;LycĂ©e General Et Technologique Benjamin Franklin;1 Rue De La Foret;56408;Auray;47.6712271;-2.9796708 +0170060Y;LycĂ©e General Et Technologique Bernard Palissy;1 Rue De Gascogne;17107;Saintes;45.7360174;-0.6152519 +0450029M;LycĂ©e General Et Technologique Bernard Palissy;9 Rue Du 32e R I;45502;Gien;47.699878;2.6321739 +0470001W;LycĂ©e General Et Technologique Bernard Palissy;164 Boulevard De La Liberte;47000;Agen;44.1992878;0.6215667 +0190038N;LycĂ©e General Et Technologique Bernart De Ventadour;Boulevard De La Jaloustre;19200;Ussel;45.541611;2.315461 +0350053T;LycĂ©e General Et Technologique Bertrand D'argentre;15 Rue Du College;35506;Vitre;48.1200837;-1.211593 +0490782J;LycĂ©e General Et Technologique Blaise Pascal;2 Rue Du LycĂ©e;49502;Segre;0.0;0.0 +0570030Y;LycĂ©e General Et Technologique Blaise Pascal;5 Rue Paul Ney;57608;Forbach;49.1806835;6.8958864 +0622803K;LycĂ©e General Et Technologique Blaise Pascal;Rue Roger Salengro;62967;Longuenesse;50.7396525;2.2532221 +0693518Z;LycĂ©e General Et Technologique Blaise Pascal;2 Avenue Bergeron;69751;Charbonnieres Les Bains;45.7751178;4.7452169 +0910626L;LycĂ©e General Et Technologique Blaise Pascal;18 A 20 Rue Alexandre Fleming;91406;Orsay;48.6959013;2.1868463 +0831243A;LycĂ©e General Et Technologique Bonaparte;Avenue W. Churchill;83097;Toulon;43.1249494;5.9263291 +0810959C;LycĂ©e General Et Technologique Borde Basse;Cite Scolaire;81108;Castres;43.606214;2.241295 +0320009L;LycĂ©e General Et Technologique Bossuet;42 Rue Jules Ferry;32100;Condom;43.9617419;0.3733974 +0820021C;LycĂ©e General Et Technologique Bourdelle;3 Boulevard Edouard Herriot;82003;Montauban;44.0128398;1.3704149 +0350028R;LycĂ©e General Et Technologique Brequigny;7 Avenue Georges Graff;35205;Rennes;48.0836213;-1.6920985 +0060013G;LycĂ©e General Et Technologique Bristol;10 Avenue St Nicolas;06405;Cannes;43.5554966;7.0188911 +0630052P;LycĂ©e General Et Technologique C. Et P. Virlogeux;1 Rue Du General Chapsal;63201;Riom;45.8868933;3.1143602 +0410959V;LycĂ©e General Et Technologique Camille Claudel;10 Rue Albert Camus;41018;Blois;47.5764443;1.3027224 +0442207K;LycĂ©e General Et Technologique Camille Claudel;14 Boulevard Jules Verne;44130;Blain;47.4812962;-1.7638862 +0590083V;LycĂ©e General Et Technologique Camille Claudel;Cite Scolaire Rue Paul Lafargue;59613;Fourmies;50.0115008;4.0428865 +0711137A;LycĂ©e General Et Technologique Camille Claudel;Route De Roanne;71160;Digoin;46.4693907;4.002237 +0772243V;LycĂ©e General Et Technologique Camille Claudel;Place Anyama;77347;Pontault Combault;48.801255;2.607598 +0911938M;LycĂ©e General Et Technologique Camille Claudel;17 Rue Robespierre;91120;Palaiseau;48.7189784;2.2340194 +0383069E;LycĂ©e General Et Technologique Camille Corot;454 Rue Paul Claudel;38510;Morestel;45.674976;5.474023 +0330023W;LycĂ©e General Et Technologique Camille Jullian;29 Rue De La Croix Blanche;33074;Bordeaux;44.8446226;-0.5901585 +0951922Y;LycĂ©e General Et Technologique Camille Saint-saens;18 Au 22 Rue Guynemer;95170;Deuil La Barre;48.9734465;2.3360722 +0680008P;LycĂ©e General Et Technologique Camille See;42 Avenue De L'europe;68025;Colmar;48.0804554;7.325755 +0441993C;LycĂ©e General Et Technologique Carcouet;115 Bd Du Massacre;44184;Nantes;47.2316043;-1.5925015 +0210015C;LycĂ©e General Et Technologique Carnot;16 Boulevard Thiers;21000;Dijon;47.3236983;5.0492348 +0420034J;LycĂ©e General Et Technologique Carnot;35 Avenue Carnot;42300;Roanne;46.0419603;4.0699904 +0600013N;LycĂ©e General Et Technologique Cassini;11 Rue Henri Breuil;60607;Clermont;49.3845904;2.4076904 +0891200W;LycĂ©e General Et Technologique Catherine Et Raymond Janot;1 Place Lech Walesa;89094;Sens;48.20065;3.28268 +0590121L;LycĂ©e General Et Technologique Cesar Baggio;Boulevard D'alsace;59000;Lille;50.6177376;3.0676499 +0220058U;LycĂ©e General Et Technologique Chaptal;6 Allee Chaptal;22015;St Brieuc;48.5125105;-2.7377027 +0750663N;LycĂ©e General Et Technologique Chaptal;45 Boulevard Des Batignolles;75008;Paris 08;48.8817015;2.3196312 +0160004T;LycĂ©e General Et Technologique Charles A Coulomb;Avenue Joachim Du Bellay;16016;Angouleme;45.6303964;0.1570913 +0561627P;LycĂ©e General Et Technologique Charles De Gaulle;23 Avenue Paul Cezanne;56017;Vannes;47.6764559;-2.772802 +0601863Z;LycĂ©e General Et Technologique Charles De Gaulle;Rue Jacques Daguerre;60321;Compiegne;49.387482;2.7840919 +0781898P;LycĂ©e General Et Technologique Charles De Gaulle;10 Rue Gustave Eiffel;78306;Poissy;48.9483064;2.0613496 +0932031C;LycĂ©e General Et Technologique Charles De Gaulle;102 Rue Lavoisier;93110;Rosny Sous Bois;48.8615917;2.4910337 +0400018C;LycĂ©e General Et Technologique Charles Despiau;637 Route Du Houga;40010;Mont De Marsan;43.8821356;-0.4870724 +0590049H;LycĂ©e General Et Technologique Charles Deulin;89 Rue De La Chaussiette;59163;Conde Sur L Escaut;50.4598956;3.5860573 +0500026G;LycĂ©e General Et Technologique Charles Francois Lebrun;2 Place Georges Davy;50207;Coutances;49.047808;-1.4452609 +0300046M;LycĂ©e General Et Technologique Charles Gide;Place Adolphe Bosc;30700;Uzes;44.016096;4.416498 +0771763Y;LycĂ©e General Et Technologique Charles Le Chauve;4 Rue J. Bodin De Boismortier;77680;Roissy En Brie;48.8016537;2.6384353 +0451526P;LycĂ©e General Et Technologique Charles Peguy;1 Cours Victor Hugo;45074;Orleans;47.8891246;1.897411 +0740017S;LycĂ©e General Et Technologique Charles Poncet;1 Avenue Charles Poncet;74302;Cluses;46.0625378;6.579908 +0692800U;LycĂ©e General Et Technologique Charlie Chaplin;13 Rue Francisco Ferrer;69152;Decines Charpieu;45.7737096;4.9761696 +0490003M;LycĂ©e General Et Technologique Chevrollier;2 Rue Adrien Recouvreur;49035;Angers;47.4543325;-0.5571808 +0370037P;LycĂ©e General Et Technologique Choiseul;78 Rue Des Douets;37095;Tours;47.435967;0.6806159 +0100022V;LycĂ©e General Et Technologique Chrestien De Troyes;3 Rue De Quebec;10009;Troyes;48.2688104;4.0777951 +0790036K;LycĂ©e General Et Technologique Cite Scolaire Jean Moulin;Rue Albert Buisson;79101;Thouars;46.9937436;-0.2131568 +0590149S;LycĂ©e General Et Technologique Cite Scolaire Pierre Forest;Boulevard Charles De Gaulle;59605;Maubeuge;50.2784762;3.9816647 +0860005N;LycĂ©e General Et Technologique Cite Technique Edouard Branly;2 Rue Edouard Branly;86106;Chatellerault;46.8021551;0.5431246 +0750698B;LycĂ©e General Et Technologique Claude Bernard;1 Avenue Du Parc Des Princes;75016;Paris 16;48.842485;2.254794 +0410017W;LycĂ©e General Et Technologique Claude De France;9 11 Avenue De Paris;41206;Romorantin Lanthenay;47.3693756;1.738879 +0420040R;LycĂ©e General Et Technologique Claude Lebois;8 Boulevard Alamagny;42403;St Chamond;45.4685576;4.5111391 +0760174B;LycĂ©e General Et Technologique Claude Monet;267 Rue Felix Faure;76085;Le Havre;49.5009436;0.110217 +0251711Z;LycĂ©e General Et Technologique Claude Nicolas Ledoux;14 Rue Alain Savary;25006;Besancon;47.2507592;5.9961549 +0820883P;LycĂ©e General Et Technologique Claude Nougaro;;82300;Monteils;44.1741667;1.5661111 +0440021J;LycĂ©e General Et Technologique Clemenceau;1 Rue Georges Clemenceau;44042;Nantes;47.2196647;-1.5456315 +0590214M;LycĂ©e General Et Technologique Colbert;2 Parvis Jean Baptiste Colbert;59208;Tourcoing;50.724993;3.16207 +0690042W;LycĂ©e General Et Technologique Colbert;20 Rue Louis Jouvet;69372;Lyon 08;45.7460272;4.8608427 +0720048L;LycĂ©e General Et Technologique Colbert De Torcy;Rue St Denis;72305;Sable Sur Sarthe;47.838785;-0.342691 +0601865B;LycĂ©e General Et Technologique Condorcet;Chemin Du Tour De Ville;60111;Meru;49.232327;2.140134 +0620108F;LycĂ©e General Et Technologique Condorcet;25 Rue Etienne Dolet;62303;Lens;50.4322132;2.836371 +0693478F;LycĂ©e General Et Technologique Condorcet;29 Rue Edmond Rostand;69802;St Priest;45.6943551;4.9411916 +0900002N;LycĂ©e General Et Technologique Condorcet;13 Avenue Roosevelt;90016;Belfort;47.6333787;6.8506415 +0930122C;LycĂ©e General Et Technologique Condorcet;31 Rue Desire Chevalier;93105;Montreuil;48.857903;2.446193 +0170042D;LycĂ©e General Et Technologique Cordouan;28 Rue Henri Dunant;17200;Royan;45.6214498;-1.0029599 +0940121W;LycĂ©e General Et Technologique D'arsonval;65 Rue Du Pont De Creteil;94107;St Maur Des Fosses;48.8032591;2.4718505 +0620143U;LycĂ©e General Et Technologique D'artois;Rue Jules Mousseron;62290;Noeux Les Mines;50.4729178;2.6598973 +0310032S;LycĂ©e General Et Technologique De Bagatelle;114 Avenue Francois Mitterrand;31806;St Gaudens;43.1122133;0.7456576 +0290098Z;LycĂ©e General Et Technologique De Cornouaille;8 Avenue Des Oiseaux;29191;Quimper;47.9922221;-4.1232942 +0290076A;LycĂ©e General Et Technologique De Kerneuzec;Kerneuzec;29391;Quimperle;47.88185;-3.552623 +0801900F;LycĂ©e General Et Technologique De L Authie;20 Rue De Routequeue;80600;Doullens;50.149672;2.3555172 +0573281G;LycĂ©e General Et Technologique De La Communication;3 Boulevard Arago;57070;Metz;49.1006107;6.2227012 +0762953X;LycĂ©e General Et Technologique De La Cote D'albatre;24 Bis Rue Du Noroit;76460;St Valery En Caux;49.8670293;0.6986804 +0011326L;LycĂ©e General Et Technologique De La Cotiere;270 Chemin Du Grand Casset;01120;La Boisse;45.8460955;5.0355377 +0011194T;LycĂ©e General Et Technologique De La Plaine De L'ain;Rue Leon Blum;01500;Amberieu En Bugey;45.9629402;5.3435213 +0741532N;LycĂ©e General Et Technologique De L'albanais;Rue Du LycĂ©e;74152;Rumilly;0.0;0.0 +0595809U;LycĂ©e General Et Technologique De L'escaut;1 Avenue De Saint Amand;59305;Valenciennes;50.3654932;3.5145784 +0590072H;LycĂ©e General Et Technologique De L'europe;809 Rue Du Banc Vert;59640;Dunkerque;51.0203846;2.354849 +0781949V;LycĂ©e General Et Technologique De Villaroy;2 Rue E Viollet Le Duc;78041;Guyancourt;48.7647055;2.0683153 +9741230U;LycĂ©e General Et Technologique De Vincendo;10 Route De La Marine;97480;St Joseph;-21.3766787;55.6686797 +9870026P;LycĂ©e General Et Technologique De Wallis;Mata Utu;98600;Uvea;-13.2825091;-176.1764475 +0110023R;LycĂ©e General Et Technologique Denis Diderot;2 Rue Jean Moulin;11100;Narbonne;43.1899207;3.01902 +0310044E;LycĂ©e General Et Technologique Deodat De Severac;26 Boulevard Deodat De Severac;31076;Toulouse;43.5869625;1.4260334 +0610018N;LycĂ©e General Et Technologique Des Andaines;3 Place Du General De Gaulle;61600;La Ferte Mace;48.5869648;-0.3589215 +0312267W;LycĂ©e General Et Technologique Des Arenes;Place Emile Male;31024;Toulouse;43.5912216;1.4186141 +9710882G;LycĂ©e General Et Technologique Des Droits De L'homme;Pointe A Bacchus;97170;Petit Bourg;16.2002975;-61.5883791 +0590101P;LycĂ©e General Et Technologique Des Flandres;2 Avenue Des Flandres;59522;Hazebrouck;50.7282931;2.5320417 +0781512V;LycĂ©e General Et Technologique Descartes;6 Boulevard Descartes;78180;Montigny Le Bretonneux;48.766296;2.0387934 +0920130S;LycĂ©e General Et Technologique Descartes;1 Avenue Lavoisier;92761;Antony;48.7484691;2.3136729 +0300026R;LycĂ©e General Et Technologique Dhuoda;17 Rue Dhuoda;30913;Nimes;43.8268494;4.356595 +0520021R;LycĂ©e General Et Technologique Diderot;21 Av Du Gal De Gaulle;52206;Langres;47.847086;5.3305042 +0620070P;LycĂ©e General Et Technologique Diderot;Avenue Montaigne;62220;Carvin;50.4909624;2.9457485 +0670071N;LycĂ©e General Et Technologique Docteur Koeberle;Bld Charlemagne;67604;Selestat;48.2633909;7.4554325 +0110022P;LycĂ©e General Et Technologique Docteur Lacroix;Rue Gay Lussac;11100;Narbonne;43.1783759;3.0009809 +0050006E;LycĂ©e General Et Technologique Dominique Villars;Place De Verdun;05010;Gap;-27.4251257;152.9935547 +0530011Z;LycĂ©e General Et Technologique Douanier Rousseau;7 Rue Des Archives;53013;Laval;48.0717168;-0.7606616 +0260019E;LycĂ©e General Et Technologique Dr. Gustave Jaume;Avenue Henri Becquerel;26702;Pierrelatte;44.380434;4.698 +0861228T;LycĂ©e General Et Technologique Du Bois D'amour;9 Rue De La Garenne;86034;Poitiers;46.5506752;0.3075189 +0010010F;LycĂ©e General Et Technologique Du Bugey;113 Rue Du 5eme Rtm;01306;Belley;45.7606158;5.68952 +0090015T;LycĂ©e General Et Technologique Du Castella;Place Du Mercadal;09104;Pamiers;43.114347;1.6083829 +0831407D;LycĂ©e General Et Technologique Du Coudon;Avenue Toulouse-lautrec;83957;La Garde;43.1328032;6.0180676 +9830557N;LycĂ©e General Et Technologique Du Grand Noumea;Avenue Victor Hugo;98830;Dumbea;-22.2131693;166.467175 +0731392S;LycĂ©e General Et Technologique Du Granier;185 Avenue Joseph Fontanet;73492;La Ravoire;45.5509773;5.9675821 +0292047T;LycĂ©e General Et Technologique Du Leon;25 Bd De La Republique;29406;Landivisiau;48.5083658;-4.0595902 +0595616J;LycĂ©e General Et Technologique Du Noordover;26 Avenue De Suwalki;59792;Grande Synthe;51.0111625;2.3014666 +0011276G;LycĂ©e General Et Technologique Du Val De Saone;220 Chemin D'arras;01606;Trevoux;45.9455462;4.7619654 +0801864S;LycĂ©e General Et Technologique Du Vimeu;3 Rue Denis Papin;80534;Friville Escarbotin;50.0832324;1.5461642 +0450062Y;LycĂ©e General Et Technologique Duhamel Du Monceau;16 Avenue De France;45300;Pithiviers;48.1809395;2.2549277 +0830053G;LycĂ©e General Et Technologique Dumont D Urville;212 Avenue Amiral Jaujard;83056;Toulon;43.1179515;5.9386824 +0590112B;LycĂ©e General Et Technologique Dupleix;10 Boulevard Des Resistants;59550;Landrecies;50.1229644;3.6895214 +0560025Y;LycĂ©e General Et Technologique Dupuy De Lome;Rue Le Coutaller;56100;Lorient;47.7478486;-3.3697288 +0450042B;LycĂ©e General Et Technologique Durzy;23 Rue Leonard De Vinci;45702;Villemandeur;48.0003639;2.7159501 +0380029A;LycĂ©e General Et Technologique Eaux Claires;7 Rue De Dunkerque;38030;Grenoble;45.1790269;5.7127172 +0010014K;LycĂ©e General Et Technologique Edgar Quinet;5 Avenue Jean Marie Verne;01011;Bourg En Bresse;46.1953623;5.225314 +0520844K;LycĂ©e General Et Technologique Edme Bouchardon;16 Rue Youri Gagarine;52903;Chaumont;48.104773;5.1478457 +0590065A;LycĂ©e General Et Technologique Edmond Labbe;817 Rue Charles Bourseul;59508;Douai;50.3764002;3.0685187 +0911961M;LycĂ©e General Et Technologique Edmond Michelet;2 Bd Abel Cornaton;91290;Arpajon;48.5870056;2.2468739 +0190032G;LycĂ©e General Et Technologique Edmond Perrier;6 Avenue Henri De Bournazel;19012;Tulle;45.2680719;1.768389 +0311334G;LycĂ©e General Et Technologique Edmond Rostand;2 Boulevard Charles De Gaulle;31110;Bagneres De Luchon;42.7929599;0.5975252 +0620052V;LycĂ©e General Et Technologique Edouard Branly;2 Rue De La Porte Gayole;62321;Boulogne Sur Mer;50.7198852;1.6152414 +0101016A;LycĂ©e General Et Technologique Edouard Herriot;Rue De La Maladiere;10302;Ste Savine;48.2935455;4.0271421 +0690027E;LycĂ©e General Et Technologique Edouard Herriot;6 Place Edgar Quinet;69455;Lyon 06;45.7657284;4.8446272 +0180035R;LycĂ©e General Et Technologique Edouard Vaillant;41 Bis Rue Charles Hurvoy;18100;Vierzon;47.2279543;2.0673471 +0332744D;LycĂ©e General Et Technologique Elie Faure;Rue Jules Ferry;33305;Lormont;44.8701779;-0.519413 +0160010Z;LycĂ©e General Et Technologique Elie Vinet;7 Avenue Pierre Mendes France;16300;Barbezieux St Hilaire;45.4741718;-0.1501908 +0750692V;LycĂ©e General Et Technologique Emile Dubois;14 Rue Emile Dubois;75014;Paris 14;48.8317647;2.3369673 +0130001F;LycĂ©e General Et Technologique Emile Zola;Av Arc De Meyran;13181;Aix En Provence;43.5118833;5.4584873 +0280015P;LycĂ©e General Et Technologique Emile Zola;26 Rue De Civry;28200;Chateaudun;48.0722541;1.3347452 +0350024L;LycĂ©e General Et Technologique Emile Zola;2 Avenue Janvier;35044;Rennes;48.1073742;-1.6736042 +0772294A;LycĂ©e General Et Technologique Emily Bronte;10 Bis Mail Le Corbusier;77185;Lognes;48.8392621;2.6392575 +0380032D;LycĂ©e General Et Technologique Emmanuel Mounier;6 Avenue Marcellin Berthelot;38029;Grenoble;45.1788938;5.7314999 +0492089E;LycĂ©e General Et Technologique Emmanuel Mounier;1 Boulevard Robert Schuman;49017;Angers;47.4895805;-0.5269842 +0920135X;LycĂ©e General Et Technologique Emmanuel Mounier;35 Rue Des Pres Hauts;92290;Chatenay Malabry;48.768766;2.2759475 +0130160D;LycĂ©e General Et Technologique Emperi (l');21 Montee Du Puech;13657;Salon De Provence;43.6383013;5.0983648 +0450040Z;LycĂ©e General Et Technologique En Foret;Route De Paucourt;45207;Montargis;48.0084469;2.750206 +0570081D;LycĂ©e General Et Technologique Erckmann Chatrian;13 Rue De L'arsenal;57372;Phalsbourg;48.7667374;7.260468 +0540034U;LycĂ©e General Et Technologique Ernest Bichat;4 Avenue Docteur Paul Kahn;54300;Luneville;48.5974249;6.5061312 +0590192N;LycĂ©e General Et Technologique Ernest Couteaux;37 Avenue Du College;59734;St Amand Les Eaux;50.4477034;3.4431281 +0790029C;LycĂ©e General Et Technologique Ernest Perochon;40 Rue Taillepied;79204;Parthenay;46.6423084;-0.2526283 +0220057T;LycĂ©e General Et Technologique Ernest Renan;2 Et 4 Bd Herault;22021;St Brieuc;48.517094;-2.75443 +0720021G;LycĂ©e General Et Technologique Estournelles De Constant;Domaine Bouchevereau;72205;La Fleche;47.69589;-0.074646 +0420046X;LycĂ©e General Et Technologique Etienne Mimard;32 Rue Etienne Mimard;42021;St Etienne;45.438102;4.3960568 +0510007F;LycĂ©e General Et Technologique Etienne Oehmichen;8 Avenue Du Mont Hery;51037;Chalons En Champagne;48.9761202;4.3588161 +0940116R;LycĂ©e General Et Technologique Eugene Delacroix;5 Rue Pierre Curie;94704;Maisons Alfort;48.8107477;2.426031 +0220060W;LycĂ©e General Et Technologique Eugene Freyssinet;32 Rue Mansart;22023;St Brieuc;48.5195608;-2.777683 +0230002C;LycĂ©e General Et Technologique Eugene Jamot;1 Rue Williams Dumazet;23200;Aubusson;45.9530125;2.1677732 +0620140R;LycĂ©e General Et Technologique Eugene Woillez;1 Rue Porte Becquerelle;62170;Montreuil;50.4644173;1.7663501 +0595867G;LycĂ©e General Et Technologique Europeen Montebello;196 Boulevard Montebello;59006;Lille;50.6206484;3.0469456 +9740597F;LycĂ©e General Et Technologique Evariste De Parny;Plateau Caillou 85 Rue A.vinson;97867;St Paul;0.0;0.0 +0782924E;LycĂ©e General Et Technologique Evariste Galois;87 Avenue De Tobrouk;78500;Sartrouville;48.9285342;2.1613742 +0100015M;LycĂ©e General Et Technologique F. Et I. Joliot Curie;1 Rue Guy Moquet;10100;Romilly Sur Seine;48.5155735;3.716231 +0570054Z;LycĂ©e General Et Technologique Fabert;12 Rue Saint Vincent;57045;Metz;49.1226525;6.1717052 +0590119J;LycĂ©e General Et Technologique Faidherbe;9 Rue Armand Carrel;59034;Lille;50.6165715;3.0739224 +9710774P;LycĂ©e General Et Technologique Faustin Fleret;Section Esperance;97111;Morne A L Eau;0.0;0.0 +9730001N;LycĂ©e General Et Technologique Felix Eboue;Rocade Sud;97306;Cayenne;4.9227;-52.3269 +0040010P;LycĂ©e General Et Technologique Felix Esclangon;Boulevard Martin Bret;04103;Manosque;43.836809;5.781223 +0600001A;LycĂ©e General Et Technologique Felix Faure;31 Boulevard De L Assaut;60000;Beauvais;49.4347804;2.0895545 +0220023F;LycĂ©e General Et Technologique Felix Le Dantec;Rue Des Cordiers;22303;Lannion;48.7310544;-3.446948 +0572022N;LycĂ©e General Et Technologique Felix Mayer;2 Square Georges Bastide;57150;Creutzwald;49.204987;6.6962469 +0590035T;LycĂ©e General Et Technologique Fenelon;Place Fenelon;59407;Cambrai;50.1764669;3.2299983 +0331760J;LycĂ©e General Et Technologique Fernand Daguin;15 Rue Gustave Flaubert;33698;Merignac;44.8366276;-0.6500899 +0620093P;LycĂ©e General Et Technologique Fernand Darchicourt;211 Rue Rene Cassin;62251;Henin Beaumont;50.410645;2.960295 +0490054T;LycĂ©e General Et Technologique Fernand Renaudeau;Rue La Tuilerie;49321;Cholet;47.0709727;-0.8903426 +6200001G;LycĂ©e General Et Technologique Fesch;5 Cours Grandval;20176;Ajaccio;41.9176744;8.7311055 +0931565W;LycĂ©e General Et Technologique Flora Tristan;27 Rue Des Hauts Roseaux;93166;Noisy Le Grand;48.8478226;2.5765564 +0951147F;LycĂ©e General Et Technologique Fragonard;Allee Verte;95290;L Isle Adam;49.1193445;2.2241912 +0510062R;LycĂ©e General Et Technologique Francois 1er;Fg De Vitry Le Brule;51308;Vitry Le Francois;48.729036;4.59224 +0660010C;LycĂ©e General Et Technologique Francois Arago;22 Avenue Paul Doumer;66028;Perpignan;42.698684;2.8958719 +0770926N;LycĂ©e General Et Technologique Francois Couperin;Route Hurtault;77305;Fontainebleau;48.3979083;2.6875719 +0330027A;LycĂ©e General Et Technologique Francois Mauriac;1 Rue Henri Dunant;33015;Bordeaux;44.8394554;-0.5560782 +0421976V;LycĂ©e General Et Technologique Francois Mauriac-forez;32 Rue Des Bullieux;42166;Andrezieux Boutheon;45.5374319;4.2959439 +0410002E;LycĂ©e General Et Technologique Francois Philibert Dessaigne;;41007;Blois;47.588402;1.335616 +0220056S;LycĂ©e General Et Technologique Francois Rabelais;8 Rue Rabelais;22022;St Brieuc;48.504309;-2.7467497 +0601823F;LycĂ©e General Et Technologique Francois Truffaut;4 Rue De Pontoise;60007;Beauvais;49.4236814;2.0846913 +0851346B;LycĂ©e General Et Technologique Francois Truffaut;8 Rue De La Cailletiere;85300;Challans;46.8538105;-1.8685351 +0451484U;LycĂ©e General Et Technologique Francois Villon;Avenue Pierre De Felice;45190;Beaugency;47.7856371;1.6185055 +0750690T;LycĂ©e General Et Technologique Francois Villon;10 16 Avenue Marc Sangnier;75014;Paris 14;48.826174;2.306449 +0780422K;LycĂ©e General Et Technologique Francois Villon;Rue Salvador Allende;78133;Les Mureaux;48.97137;1.922145 +0911021R;LycĂ©e General Et Technologique Francois-joseph Talma;1 Rue Des Cerfs;91805;Brunoy;48.6928052;2.5025791 +0510034K;LycĂ©e General Et Technologique Franklin Roosevelt;10 Rue Roosevelt;51096;Reims;49.2620465;4.0259878 +9720350Y;LycĂ©e General Et Technologique Frantz Fanon;Cite Scolaire Frantz Fanon;97220;La Trinite;0.0;0.0 +0540040A;LycĂ©e General Et Technologique Frederic Chopin;39 Rue Du Sergent Blandan;54042;Nancy;48.6804667;6.1664682 +0690103M;LycĂ©e General Et Technologique Frederic Fays;46 Rue Frederic Fays;69615;Villeurbanne;45.7625759;4.9047165 +0131549N;LycĂ©e General Et Technologique Frederic Joliot-curie;4 Avenue Des Goums;13400;Aubagne;43.2916597;5.5612548 +0680051L;LycĂ©e General Et Technologique Frederic Kirschleger;8 Rue Du Docteur Heid;68140;Munster;48.0385537;7.1326565 +0281047L;LycĂ©e General Et Technologique Fulbert;Rue Saint Cheron;28011;Chartres;48.4464583;1.5061027 +0220027K;LycĂ©e General Et Technologique Fulgence Bienvenue;Rue Eon De L'etoile;22606;Loudeac;48.1851796;-2.7600028 +0910687C;LycĂ©e General Et Technologique Fustel De Coulanges;11 Rue Des Migneaux;91300;Massy;48.7330082;2.2656033 +0090002D;LycĂ©e General Et Technologique Gabriel Faure;5 Rue Lt Paul Delpech;09008;Foix;42.964127;1.605232 +0740005D;LycĂ©e General Et Technologique Gabriel Faure;2 Avenue Du Rhone;74008;Annecy;45.8989205;6.1181581 +0720033V;LycĂ©e General Et Technologique Gabriel Touchard;8 Place Washington;72002;Le Mans;47.9956706;0.2030578 +0710071S;LycĂ©e General Et Technologique Gabriel Voisin;Rue Saint Jean;71700;Tournus;46.574065;4.907238 +0380083J;LycĂ©e General Et Technologique Galilee;124 Avenue General Leclerc;38209;Vienne;45.5052095;4.8559693 +0772127U;LycĂ©e General Et Technologique Galilee;Avenue Andre Malraux;77385;Combs La Ville;48.6574407;2.5584154 +0951637N;LycĂ©e General Et Technologique Galilee;11 Av Du Jour;95801;Cergy;49.0459761;2.0299018 +0620007W;LycĂ©e General Et Technologique Gambetta;25 Boulevard Carnot;62022;Arras;50.2874799;2.7750733 +0440030U;LycĂ©e General Et Technologique Gaspard Monge;2 Rue De La Fantaisie;44322;Nantes;47.2535027;-1.5719339 +0100003Z;LycĂ©e General Et Technologique Gaston Bachelard;5 Bis Rue Gaston Bachelard;10200;Bar Sur Aube;48.2298972;4.7045231 +0770922J;LycĂ©e General Et Technologique Gaston Bachelard;32 Avenue De L'europe;77505;Chelles;48.9146526;2.5976487 +0590258K;LycĂ©e General Et Technologique Gaston Berger;Avenue Gaston Berger Prolongee;59016;Lille;50.6143623;3.0807063 +0640052J;LycĂ©e General Et Technologique Gaston Febus;20 Av Georges Moutet;64301;Orthez;43.4914882;-0.7661496 +9730108E;LycĂ©e General Et Technologique Gaston Monnerville;Rue Du LycĂ©e;97387;Kourou;0.0;0.0 +0020014E;LycĂ©e General Et Technologique Gay Lussac;Boulevard Gambetta;02302;Chauny;49.613832;3.2214 +0670057Y;LycĂ©e General Et Technologique General Leclerc;8 Rue Poincare;67703;Saverne;48.7409019;7.360907 +0771663P;LycĂ©e General Et Technologique George Sand;Rue De La Mare Au Diable;77350;Le Mee Sur Seine;48.549902;2.636207 +0880152M;LycĂ©e General Et Technologique Georges Baumont;Rue De L'orme;88102;St Die Des Vosges;48.29796;6.946644 +0300950V;LycĂ©e General Et Technologique Georges Brassens;Avenue Vigan Braquet;30205;Bagnols Sur Ceze;44.1607643;4.6116424 +0594413B;LycĂ©e General Et Technologique Georges Bustin;13 Rue Du 8 Mai 1945;59690;Vieux Conde;50.4642251;3.569136 +0190010H;LycĂ©e General Et Technologique Georges Cabanis;6 Bd Henri De Jouvenel;19311;Brive La Gaillarde;45.1600815;1.5232644 +0340039H;LycĂ©e General Et Technologique Georges Clemenceau;31 Avenue Georges Clemenceau;34060;Montpellier;43.6031139;3.8721038 +0850006V;LycĂ©e General Et Technologique Georges Clemenceau;Place De La Republique;85111;Chantonnay;46.6849967;-1.0493349 +0930127H;LycĂ©e General Et Technologique Georges Clemenceau;130 Rue De Neuilly;93250;Villemomble;48.8772594;2.5239123 +0250030X;LycĂ©e General Et Technologique Georges Cuvier;1 Place Jean Monnet;25207;Montbeliard;47.510356;6.798466 +0540041B;LycĂ©e General Et Technologique Georges De La Tour;5 Rue De La Croix St Claude;54052;Nancy;48.6928494;6.1381574 +0572757M;LycĂ©e General Et Technologique Georges De La Tour;Place Maud'huy;57045;Metz;49.2036097;6.1574195 +0133525L;LycĂ©e General Et Technologique Georges Duby;200 Rue Georges Duby;13080;Aix En Provence;43.4805284;5.4133099 +0270044B;LycĂ©e General Et Technologique Georges Dumezil;Route D'ivry;27207;Vernon;49.0818358;1.4769566 +0921594H;LycĂ©e General Et Technologique Georges Pompidou;1 Avenue Georges Pompidou;92390;Villeneuve La Garenne;48.9419734;2.3246588 +0020059D;LycĂ©e General Et Technologique Gerard De Nerval;14 Rue Paul Deviolaine;02209;Soissons;49.3860844;3.3238462 +0950647M;LycĂ©e General Et Technologique Gerard De Nerval;Place De L Europe;95270;Luzarches;49.113738;2.441136 +9710002A;LycĂ©e General Et Technologique Gerville Reache;37 Rue Amedee Fengarol;97100;Basse Terre;16.2431386;-61.5372836 +7200009X;LycĂ©e General Et Technologique Giocante De Casabianca;Av.jean Zuccarelli;20293;Bastia;42.7033101;9.4445024 +0510068X;LycĂ©e General Et Technologique Godart Roger;8 Rue Godart Roger;51331;Epernay;49.0398693;3.9654465 +0330003Z;LycĂ©e General Et Technologique Grand Air;Avenue Dr Lorentz Monod;33311;Arcachon;44.6531506;-1.1636262 +0440012Z;LycĂ©e General Et Technologique Grand Air;77 Avenue Du Bois D Amour;44503;La Baule Escoublac;47.2848421;-2.3741177 +0370038R;LycĂ©e General Et Technologique Grandmont;Avenue De Sevigne;37204;Tours;47.3603558;0.7008859 +0160002R;LycĂ©e General Et Technologique Guez De Balzac;Place Beaulieu;16016;Angouleme;45.65111;0.146709 +0061763J;LycĂ©e General Et Technologique Guillaume Apollinaire;29 Bd Jean Baptiste Verany;06300;Nice;43.7155171;7.2826203 +0940123Y;LycĂ©e General Et Technologique Guillaume Apollinaire;42 Rue Du Pave De Grignon;94320;Thiais;48.7571462;2.4006742 +0760072R;LycĂ©e General Et Technologique Guillaume Le Conquerant;Allee De La Cote Blanche;76170;Lillebonne;49.521553;0.545717 +0900003P;LycĂ©e General Et Technologique Gustave Courbet;Av Gambiez;90020;Belfort;47.652328;6.856124 +0931272C;LycĂ©e General Et Technologique Gustave Eiffel;16 Chemin De La Renardiere;93220;Gagny;48.8887212;2.5413326 +0760096S;LycĂ©e General Et Technologique Gustave Flaubert;1 Rue Albert Dupuis;76044;Rouen;49.4508628;1.1226207 +0950644J;LycĂ©e General Et Technologique Gustave Monod;71 Avenue De Ceinture;95880;Enghien Les Bains;48.97192;2.2940159 +0860021F;LycĂ©e General Et Technologique Guy Chauvet;Rue De L Eperon;86206;Loudun;47.0060213;0.0850569 +0760035A;LycĂ©e General Et Technologique Guy De Maupassant;1575 Boulevard Nelson Mandela;76401;Fecamp;49.7442498;0.3892768 +0920137Z;LycĂ©e General Et Technologique Guy De Maupassant;52 Rue Robert Schuman;92701;Colombes;48.9273923;2.240501 +0440005S;LycĂ©e General Et Technologique Guy Moquet;1 Rue De L Europe;44146;Chateaubriant;47.729359;-1.3637892 +0940124Z;LycĂ©e General Et Technologique Hector Berlioz;106 Avenue De Paris;94307;Vincennes;48.8456885;2.4269693 +0491966W;LycĂ©e General Et Technologique Henri Bergson;85 Rue De La Barre;49036;Angers;47.4721055;-0.5929403 +0750711R;LycĂ©e General Et Technologique Henri Bergson;27 Rue Edouard Pailleron;75019;Paris 19;48.8810293;2.377875 +0500082T;LycĂ©e General Et Technologique Henri Cornat;36 Rue Henri Cornat;50700;Valognes;49.5071261;-1.4752266 +0620113L;LycĂ©e General Et Technologique Henri Darras;Chemin Des Manufactures;62803;Lievin;50.412424;2.766446 +0340009A;LycĂ©e General Et Technologique Henri Iv;1 Rue Ignace Brunel;34543;Beziers;43.3436459;3.2107736 +0540042C;LycĂ©e General Et Technologique Henri Loritz;29 Rue Des Jardiniers;54042;Nancy;48.6903847;6.1913797 +0061884R;LycĂ©e General Et Technologique Henri Matisse;101 Avenue Foch;06140;Vence;43.72337;7.1054579 +0312290W;LycĂ©e General Et Technologique Henri Matisse;Avenue Du Comminges;31270;Cugnaux;43.5335144;1.3592883 +0670041F;LycĂ©e General Et Technologique Henri Meck;10 Rue Henri Meck;67125;Molsheim;48.5361513;7.4912458 +0770930T;LycĂ©e General Et Technologique Henri Moissan;20 Cours De Verdun;77100;Meaux;48.9584066;2.8748634 +0710054Y;LycĂ©e General Et Technologique Henri Parriat;49 Rue De Gourdon;71307;Montceau Les Mines;46.6749138;4.3728594 +0540038Y;LycĂ©e General Et Technologique Henri Poincare;2 Rue De La Visitation;54042;Nancy;48.6918437;6.179348 +0590221V;LycĂ©e General Et Technologique Henri Wallon;16 Place De La Republique;59322;Valenciennes;50.3630096;3.5208558 +0930116W;LycĂ©e General Et Technologique Henri Wallon;146 Rue Des Cites;93300;Aubervilliers;48.9123093;2.3885117 +0130175V;LycĂ©e General Et Technologique Honore Daumier;46 Avenue Clot Bey;13008;Marseille 08;43.2585306;5.3838879 +0360024F;LycĂ©e General Et Technologique Honore De Balzac;8 Rue De La Limoise;36105;Issoudun;46.9567081;2.0000772 +0750705J;LycĂ©e General Et Technologique Honore De Balzac;118 Boulevard Bessieres;75849;Paris 17;48.8954075;2.316289 +0060033D;LycĂ©e General Et Technologique Honore D'estienne D'orves;13 Avenue D Estienne D Orves;06050;Nice;43.7036523;7.2446985 +0420042T;LycĂ©e General Et Technologique Honore D'urfe;1 Impasse Le Chatelier;42014;St Etienne;45.4143637;4.3895743 +0601826J;LycĂ©e General Et Technologique Hugues Capet;Avenue De Reims;60309;Senlis;49.1915037;2.5690088 +0510035L;LycĂ©e General Et Technologique Hugues Libergier;55 Rue Libergier;51095;Reims;49.2514521;4.0278114 +0161095D;LycĂ©e General Et Technologique Image Et Son;303 Avenue De Navarre;16022;Angouleme;45.6332536;0.1538142 +0570108H;LycĂ©e General Et Technologique Industriel;15 Route De La Briquerie;57100;Thionville;49.363583;6.1609848 +0010072Y;LycĂ©e General Et Technologique International;Avenue Des Sports;01216;Ferney Voltaire;46.2620007;6.1160646 +0061642C;LycĂ©e General Et Technologique International;190 Rue F. Mistral;06902;Valbonne;43.6211121;7.0416901 +0040543U;LycĂ©e General Et Technologique International Iter;116 Bd Regis Ryckebush;04100;Manosque;43.831469;5.7990491 +0312093G;LycĂ©e General Et Technologique International Victor Hugo;Boulevard Victor Hugo;31773;Colomiers;43.6169776;1.3132042 +0340076Y;LycĂ©e General Et Technologique Irene Et Frederic Joliot Curi;19 Boulevard Joliot Curie;34200;Sete;43.3972261;3.6659039 +0840017M;LycĂ©e General Et Technologique Ismael Dauphin;Rue Pierre Fabre;84300;Cavaillon;43.8245562;5.0387837 +0851401L;LycĂ©e General Et Technologique J.de Lattre De Tassigny;165 Rue Hubert Cailler;85021;La Roche Sur Yon;46.6754534;-1.4044832 +0420014M;LycĂ©e General Et Technologique Jacob Holtzer;5 Rue Michelet;42704;Firminy;45.3882874;4.2914303 +0750713T;LycĂ©e General Et Technologique Jacquard;2 Et 2 Bis Rue Bouret;75019;Paris 19;48.8805143;2.3765501 +0770933W;LycĂ©e General Et Technologique Jacques Amyot;6 Bis Rue Michelet;77000;Melun;48.5454598;2.6590212 +0060001U;LycĂ©e General Et Technologique Jacques Audiberti;Boulevard Wilson;06631;Antibes;43.578323;7.11987 +0692717D;LycĂ©e General Et Technologique Jacques Brel;7 Avenue D'oschatz;69200;Venissieux;45.6952773;4.8802134 +0931430Z;LycĂ©e General Et Technologique Jacques Brel;4-6 Rue Dulcie September;93120;La Courneuve;48.9284905;2.4004255 +0540070H;LycĂ©e General Et Technologique Jacques Callot;12 Rue Jacques Callot;54500;Vandoeuvre Les Nancy;48.6663588;6.1630763 +0180007K;LycĂ©e General Et Technologique Jacques Coeur;108 Rue Jean Baffier;18026;Bourges;47.0738989;2.409201 +0371418R;LycĂ©e General Et Technologique Jacques De Vaucanson;1 Rue Vedrines;37081;Tours;47.4229477;0.7057627 +0930120A;LycĂ©e General Et Technologique Jacques Feyder;10 Rue Henri Wallon;93806;Epinay Sur Seine;48.9551431;2.3208206 +0540058V;LycĂ©e General Et Technologique Jacques Marquette;Place Foch;54701;Pont A Mousson;48.90329;6.054868 +0451462V;LycĂ©e General Et Technologique Jacques Monod;7 Rue Leon Blum;45803;St Jean De Braye;47.9106684;1.9688004 +0641839B;LycĂ©e General Et Technologique Jacques Monod;10 Rue Du Parvis;64234;Lescar;43.332829;-0.4354594 +0921555R;LycĂ©e General Et Technologique Jacques Monod;46 Rue Du Fort;92140;Clamart;48.7956785;2.2724849 +0271431J;LycĂ©e General Et Technologique Jacques Prevert;30 Route De Saint-paul;27500;Pont Audemer;49.3509874;0.5239679 +0440077V;LycĂ©e General Et Technologique Jacques Prevert;17 Rue Joseph Malegue;44260;Savenay;47.364721;-1.9397342 +0911577V;LycĂ©e General Et Technologique Jacques Prevert;23 Rue Jules Ferry;91162;Longjumeau;48.6865888;2.2969624 +0920134W;LycĂ©e General Et Technologique Jacques Prevert;163 Rue De Billancourt;92100;Boulogne Billancourt;48.8333966;2.2376962 +0950651S;LycĂ©e General Et Technologique Jacques Prevert;23 Chemin Vert De Boissy;95150;Taverny;49.0137009;2.2123305 +0622276M;LycĂ©e General Et Technologique Jan Lavezzari;Avenue Du Phare;62602;Berck;50.3988519;1.5605537 +9710923B;LycĂ©e General Et Technologique Jardin D'essai;Route Des Abymes;97139;Les Abymes;16.2417208;-61.4735645 +0330020T;LycĂ©e General Et Technologique Jaufre Rudel;2 Rue Urbain Chasseloup;33394;Blaye;45.1239711;-0.660485 +0241137F;LycĂ©e General Et Technologique Jay De Beaufort;9 Rue Turenne;24007;Perigueux;45.1825357;0.7117974 +0830025B;LycĂ©e General Et Technologique Jean Aicard;Avenue Gallieni;83412;Hyeres;43.1188945;6.1262759 +0910627M;LycĂ©e General Et Technologique Jean Baptiste Corot;Pl Davout Le Chateau;91605;Savigny Sur Orge;48.6851259;2.349282 +0870050G;LycĂ©e General Et Technologique Jean Baptiste Darnet;28 Avenue De Perigueux;87500;St Yrieix La Perche;45.5048579;1.2020927 +0801700N;LycĂ©e General Et Technologique Jean Baptiste Delambre;Rue Montaigne;80084;Amiens;49.9121084;2.3220718 +0590071G;LycĂ©e General Et Technologique Jean Bart;1 Rue Du Nouvel Arsenal;59383;Dunkerque;51.0363738;2.3838308 +0492148U;LycĂ©e General Et Technologique Jean Bodin;Av De L Europe;49137;Les Ponts De Ce;47.4409485;-0.5286577 +0352318E;LycĂ©e General Et Technologique Jean Brito;Avenue Du Bois Greffier;35470;Bain De Bretagne;47.8470053;-1.6939432 +0400067F;LycĂ©e General Et Technologique Jean Cassaigne;Avenue Kennedy;40280;St Pierre Du Mont;43.869494;-0.489719 +0133195C;LycĂ©e General Et Technologique Jean Cocteau;Avenue Jean Cocteau;13141;Miramas;43.5872654;5.0088295 +0332747G;LycĂ©e General Et Technologique Jean Condorcet;89 Rue Condorcet;33030;Bordeaux;44.8590368;-0.5776445 +0170028N;LycĂ©e General Et Technologique Jean Dautet;18 Rue Delayant;17022;La Rochelle;46.1645196;-1.1540598 +0570098X;LycĂ©e General Et Technologique Jean De Pange;16 Rue Du LycĂ©e;57216;Sarreguemines;0.0;0.0 +0820004J;LycĂ©e General Et Technologique Jean De Prades;Route De Toulouse;82102;Castelsarrasin;44.0334192;1.1211267 +0500016W;LycĂ©e General Et Technologique Jean Francois Millet;1 Rue De Bougainville;50130;Cherbourg Octeville;49.6407942;-1.6467298 +0870003F;LycĂ©e General Et Technologique Jean Giraudoux;Avenue Charles De Gaulle;87300;Bellac;46.1255359;1.061553 +0350011X;LycĂ©e General Et Technologique Jean Guehenno;11 Rue Du Champ Rossignol;35305;Fougeres;48.3484995;-1.2000706 +0610021S;LycĂ©e General Et Technologique Jean Guehenno;16 Rue Pierre Huet;61105;Flers;48.744858;-0.552585 +0541270M;LycĂ©e General Et Technologique Jean Hanzelet;Place De Trey;54701;Pont A Mousson;48.9095605;6.0576284 +0510032H;LycĂ©e General Et Technologique Jean Jaures;17 Rue Ruinart De Brimont;51062;Reims;49.258353;4.0443359 +0810012Y;LycĂ©e General Et Technologique Jean Jaures;Route De Blaye;81400;Carmaux;44.0511877;2.1480562 +9741182S;LycĂ©e General Et Technologique Jean Joly;2 Chemin La Ouete;97899;St Louis;-21.2709804;55.4310343 +0132210G;LycĂ©e General Et Technologique Jean Lurcat;Chemin De St Macaire;13693;Martigues;43.4217515;5.0344096 +0880004B;LycĂ©e General Et Technologique Jean Lurcat;22 Av De Lattre De Tassigny;88600;Bruyeres;48.205641;6.7148648 +0350026N;LycĂ©e General Et Technologique Jean Mace;10 Rue Jean Mace;35704;Rennes;48.1173362;-1.6712538 +0790023W;LycĂ©e General Et Technologique Jean Mace;18-20 Rue Gustave Eiffel;79004;Niort;46.3310176;-0.4685901 +0390019J;LycĂ©e General Et Technologique Jean Michel;400 Rue Du Dr Michel;39015;Lons Le Saunier;46.6791715;5.5512839 +0150006A;LycĂ©e General Et Technologique Jean Monnet;10 Rue Du Docteur Chibret;15005;Aurillac;44.9354809;2.4517539 +0341736C;LycĂ©e General Et Technologique Jean Monnet;Rue De Malbosc;34088;Montpellier;43.6319486;3.8311411 +0420043U;LycĂ©e General Et Technologique Jean Monnet;16 Rue Portail Rouge;42014;St Etienne;45.4168613;4.4105286 +0601832R;LycĂ©e General Et Technologique Jean Monnet;10 Rue Des Cedres;60803;Crepy En Valois;49.2239895;2.8929225 +0670078W;LycĂ©e General Et Technologique Jean Monnet;2 Place A Schweitzer;67028;Strasbourg;48.6419332;7.7584307 +0741476C;LycĂ©e General Et Technologique Jean Monnet;1 Place De Lattre De Tassigny;74106;Annemasse;46.1828008;6.2460882 +0080040A;LycĂ©e General Et Technologique Jean Moulin;996 Avenue De La Cite Scolaire;08500;Revin;49.9269895;4.6510517 +0180024D;LycĂ©e General Et Technologique Jean Moulin;45 Rue Jean Moulin;18206;St Amand Montrond;46.7345343;2.5142771 +0290023T;LycĂ©e General Et Technologique Jean Moulin;10 Rue Du LycĂ©e;29150;Chateaulin;0.0;0.0 +0340011C;LycĂ©e General Et Technologique Jean Moulin;Av Des Martyrs De La Resistance;34521;Beziers;43.3429502;3.233546 +0340059E;LycĂ©e General Et Technologique Jean Moulin;1 Avenue Vidal De La Blache;34120;Pezenas;43.4590752;3.4162673 +0570029X;LycĂ©e General Et Technologique Jean Moulin;7 Rue Maurice Barres;57608;Forbach;49.183268;6.9061491 +0590185F;LycĂ©e General Et Technologique Jean Moulin;49 Bd Du General De Gaulle;59051;Roubaix;50.68501;3.17217 +0730005J;LycĂ©e General Et Technologique Jean Moulin;12 Rue Felix Chautemps;73202;Albertville;45.6734316;6.3903457 +0772120L;LycĂ©e General Et Technologique Jean Moulin;6-8 Av Jean Moulin - Torcy;77206;Torcy;48.8349124;2.6594968 +0830015R;LycĂ©e General Et Technologique Jean Moulin;Place De La Paix;83300;Draguignan;43.535376;6.469779 +0860028N;LycĂ©e General Et Technologique Jean Moulin;Avenue Jean Moulin;86501;Montmorillon;46.428122;0.876037 +0440062D;LycĂ©e General Et Technologique Jean Perrin;20 Rue Du Chateau De Reze;44400;Reze;47.1806714;-1.5600159 +0590110Z;LycĂ©e General Et Technologique Jean Perrin;2 Avenue Sakharov;59832;Lambersart;50.6476975;3.038367 +0690082P;LycĂ©e General Et Technologique Jean Perrin;48 Rue Pierre Baizet;69338;Lyon 09;45.7936129;4.8146401 +0760076V;LycĂ©e General Et Technologique Jean Prevost;Avenue Jean Prevost;76290;Montivilliers;49.5450214;0.177044 +0595786U;LycĂ©e General Et Technologique Jean Prouve;2 Rue De Lompret;59463;Lille;50.6533164;2.9946618 +0930118Y;LycĂ©e General Et Technologique Jean Renoir;11 Rue Fremin;93141;Bondy;48.9027563;2.473295 +0140017T;LycĂ©e General Et Technologique Jean Rostand;98 Route D'ifs;14054;Caen;49.1629104;-0.3522713 +0590184E;LycĂ©e General Et Technologique Jean Rostand;361 Grande Rue;59057;Roubaix;50.6954992;3.1964449 +0600009J;LycĂ©e General Et Technologique Jean Rostand;Place Georges Paquier;60501;Chantilly;49.181337;2.462574 +0120012Y;LycĂ©e General Et Technologique Jean Vigo;Le Puits De Cales;12100;Millau;44.090247;3.059129 +0301722J;LycĂ©e General Et Technologique Jean Vilar;616 Av Du Dr Paul Gache;30409;Villeneuve Les Avignon;43.9814603;4.788257 +0772229E;LycĂ©e General Et Technologique Jean Vilar;83 Avenue Salvador Allende;77100;Meaux;48.9547859;2.893935 +0780582J;LycĂ©e General Et Technologique Jean Vilar;1 Rue Jacques Prevert;78375;Plaisir;48.8240075;1.9601951 +0540076P;LycĂ©e General Et Technologique Jean Zay;2 Rue De La Tuilerie;54801;Jarny;49.1581264;5.8767019 +0630069H;LycĂ©e General Et Technologique Jean Zay;21 Rue Jean Zay;63300;Thiers;45.864148;3.5305398 +0572027U;LycĂ©e General Et Technologique Jean-baptiste Colbert;7 Impasse Colbert;57129;Thionville;49.366287;6.1478214 +0590064Z;LycĂ©e General Et Technologique Jean-baptiste Corot;133 Rue Saint Vaast;59508;Douai;50.3751343;3.0784266 +0601906W;LycĂ©e General Et Technologique Jean-baptiste Corot;Rue Henri Lebesgue;60000;Beauvais;49.42964;2.081875 +0300002P;LycĂ©e General Et Technologique Jean-baptiste Dumas;Place De La Belgique;30104;Ales;44.128435;4.0760861 +0750700D;LycĂ©e General Et Technologique Jean-baptiste Say;11 Bis Rue D'auteuil;75016;Paris 16;48.8474879;2.2677956 +0950648N;LycĂ©e General Et Technologique Jean-jacques Rousseau;20 Rue De Jaigny;95160;Montmorency;48.9902224;2.3179386 +0211909L;LycĂ©e General Et Technologique Jean-marc Boivin;4 Bis Route De Dijon;21800;Chevigny St Sauveur;47.3024948;5.127503 +0290034E;LycĂ©e General Et Technologique Jean-marie Le Bris;2 Place Du LycĂ©e;29172;Douarnenez;0.0;0.0 +0782132U;LycĂ©e General Et Technologique Jeanne D Albret;6 Rue Giraud Teulon;78101;St Germain En Laye;48.8936305;2.0989384 +0540039Z;LycĂ©e General Et Technologique Jeanne D Arc;16 Rue Pierre Fourier;54000;Nancy;48.6932069;6.1859085 +0760091L;LycĂ©e General Et Technologique Jeanne D'arc;Rue Sainte-genevieve Du Mont;76044;Rouen;49.4441008;1.1081523 +0601824G;LycĂ©e General Et Technologique Jeanne Hachette;31 Boulevard Amyot D Inville;60009;Beauvais;49.4354984;2.081916 +0693044J;LycĂ©e General Et Technologique Jean-paul Sartre;93 Avenue Francois Mitterrand;69675;Bron;45.7372573;4.9196386 +0920802X;LycĂ©e General Et Technologique Jean-pierre Vernant;21 Rue Du Dr Ledermann;92310;Sevres;48.8255916;2.211662 +0570085H;LycĂ©e General Et Technologique Jean-victor Poncelet;Rue Des Anglais;57501;St Avold;49.1018211;6.7061996 +0760023M;LycĂ©e General Et Technologique Jehan Ango;25 Rue Roger Lecoffre;76203;Dieppe;49.9217708;1.0652099 +0490002L;LycĂ©e General Et Technologique Joachim Du Bellay;1 Avenue Marie Talet;49105;Angers;47.4762607;-0.5475231 +0340038G;LycĂ©e General Et Technologique Joffre;150 Allee De La Citadelle;34060;Montpellier;43.6135477;3.8827869 +0311902Z;LycĂ©e General Et Technologique Jolimont;44 Chemin Cassaing;31079;Toulouse;43.6168618;1.4763001 +0350029S;LycĂ©e General Et Technologique Joliot-curie;144 Bd De Vitre;35703;Rennes;48.1220757;-1.6510693 +0920141D;LycĂ©e General Et Technologique Joliot-curie;92 Avenue Joliot Curie;92014;Nanterre;48.890764;2.2068759 +0790019S;LycĂ©e General Et Technologique Joseph Desfontaines;2 Rue Guillotiere;79500;Melle;46.2238133;-0.144721 +9720004X;LycĂ©e General Et Technologique Joseph Gaillard;Rue Marie Therese Gertrude;97261;Fort De France;14.6040646;-61.084565 +0560038M;LycĂ©e General Et Technologique Joseph Loth;4 Rue J M De Lamennais;56306;Pontivy;48.0662246;-2.9630909 +0220065B;LycĂ©e General Et Technologique Joseph Savina;5 Place De La Republique;22220;Treguier;48.7859048;-3.2335656 +0010016M;LycĂ©e General Et Technologique Joseph-marie Carriat;1 Rue De Crouy;01011;Bourg En Bresse;46.1981597;5.2225406 +0440001M;LycĂ©e General Et Technologique Joubert;160 Rue Du Pressoir Rouge;44154;Ancenis;47.3752742;-1.1822546 +0260036Y;LycĂ©e General Et Technologique Jules Algoud;Rue Barthelemy De Laffemas;26901;Valence;44.917059;4.915496 +0060014H;LycĂ©e General Et Technologique Jules Ferry;82 Boulevard De La Republique;06402;Cannes;43.5586684;7.0219303 +0770924L;LycĂ©e General Et Technologique Jules Ferry;4 Rue Henri Dunant;77527;Coulommiers;48.8240411;3.0905384 +0781845G;LycĂ©e General Et Technologique Jules Ferry;7 Rue Bouyssel;78700;Conflans Ste Honorine;48.9989596;2.0960496 +0880055G;LycĂ©e General Et Technologique Jules Ferry;48 Rue Saint Charles;88109;St Die Des Vosges;48.2877577;6.9551559 +0290013G;LycĂ©e General Et Technologique Jules Lesven;34 Rue Jules Lesven;29801;Brest;48.4056622;-4.4787824 +0590282L;LycĂ©e General Et Technologique Jules Mousseron;Boulevard Du 8 Mai 1945;59723;Denain;50.32402;3.396566 +0580031U;LycĂ©e General Et Technologique Jules Renard;11 Boulevard Saint Exupery;58002;Nevers;47.0034899;3.1609303 +0600021X;LycĂ©e General Et Technologique Jules Uhry;10 Rue Aristide Briand;60107;Creil;49.2580804;2.4755361 +0021939X;LycĂ©e General Et Technologique Jules Verne;;02401;Chateau Thierry;49.064633;3.479051 +0911983L;LycĂ©e General Et Technologique Jules Verne;49 Rue D'arpajon;91470;Limours;48.6420954;2.0892316 +0570146Z;LycĂ©e General Et Technologique Julie Daubie;Rue Joffre;57120;Rombas;49.2534261;6.0999961 +0690032K;LycĂ©e General Et Technologique Juliette Recamier;57 Rue De La Charite;69287;Lyon 02;45.7563614;4.8333688 +0500036T;LycĂ©e General Et Technologique Julliot De La Morandiere;Rue De La Crete;50406;Granville;48.8264841;-1.5782999 +0920144G;LycĂ©e General Et Technologique L Agora;120 Rue De Verdun;92816;Puteaux;48.8780243;2.2345512 +0782563M;LycĂ©e General Et Technologique La Bruyere;31 Avenue De Paris;78000;Versailles;48.8005385;2.1377792 +0440086E;LycĂ©e General Et Technologique La Coliniere;129 Rue Du Landreau;44319;Nantes;47.2345916;-1.5099978 +0801882L;LycĂ©e General Et Technologique La Hotoie;Rue Du Batonnier Mahiu;80016;Amiens;49.8964029;2.2741636 +0690038S;LycĂ©e General Et Technologique La Martiniere Duchere;300 Avenue Andrei Sakharov;69338;Lyon 09;45.7867171;4.7986751 +0690037R;LycĂ©e General Et Technologique La Martiniere Diderot;18 Place Gabriel Rambaud;69283;Lyon 01;45.767838;4.8294264 +0692866R;LycĂ©e General Et Technologique La Martiniere Monplaisir;41 Rue Antoine Lumiere;69372;Lyon 08;45.7399085;4.8673503 +9830002K;LycĂ©e General Et Technologique La Perouse;5 Rue G. Baudoux;98849;Noumea;-22.2811991;166.4425546 +0710023P;LycĂ©e General Et Technologique La Prat's;Rue Du 19 Mars 1962;71250;Cluny;46.4311567;4.6548977 +0650040R;LycĂ©e General Et Technologique La Serre De Sarsan;Rue St Exupery;65107;Lourdes;43.1082589;-0.0241297 +0021522U;LycĂ©e General Et Technologique La Thierache;Le Pont De Pierre;02140;Fontaine Les Vervins;49.832387;3.886854 +0740046Y;LycĂ©e General Et Technologique La Versoie;12 Avenue Du Forchat;74203;Thonon Les Bains;46.3652365;6.4698132 +0690029G;LycĂ©e General Et Technologique Lacassagne;93 Rue Antoine Charial;69425;Lyon 03;45.7577924;4.8703604 +6200002H;LycĂ©e General Et Technologique Laetitia Bonaparte;3, Ave Napoleon Iii;20192;Ajaccio;41.9266076;8.7346838 +0920145H;LycĂ©e General Et Technologique Lakanal;3 Av Pdt Franklin Roosevelt;92331;Sceaux;48.7772605;2.3077341 +0800007Y;LycĂ©e General Et Technologique Lamarck;Avenue Robert Solente;80301;Albert;49.9999141;2.6573268 +0710045N;LycĂ©e General Et Technologique Lamartine;Avenue Des Gaises;71018;Macon;46.317061;4.82604 +0240025X;LycĂ©e General Et Technologique Laure Gatet;25 Av G Pompidou;24003;Perigueux;45.189843;0.7240838 +0530016E;LycĂ©e General Et Technologique Lavoisier;281 Rue Du Pommier;53103;Mayenne;48.3058312;-0.628314 +0782546U;LycĂ©e General Et Technologique Le Corbusier;88 Rue De Villiers;78300;Poissy;48.9233505;2.0271586 +0930117X;LycĂ©e General Et Technologique Le Corbusier;44 Rue Leopold Rechossiere;93533;Aubervilliers;48.9145674;2.3910044 +0250033A;LycĂ©e General Et Technologique Le Grand Chenois;Rue Pierre Donzelot;25204;Montbeliard;47.496347;6.808773 +9741185V;LycĂ©e General Et Technologique Le Verger;1 Avenue Des Corossols;97438;Ste Marie;-20.9285865;55.4894457 +9740001H;LycĂ©e General Et Technologique Leconte De Lisle;Cite Scolaire Du Butor;97491;St Denis;48.936181;2.357443 +0370001A;LycĂ©e General Et Technologique Leonard De Vinci;Rue Du Clos Des Gardes;37402;Amboise;47.3993791;0.9837666 +0430947W;LycĂ©e General Et Technologique Leonard De Vinci;Le Mazel;43120;Monistrol Sur Loire;45.292432;4.172183 +0951753P;LycĂ©e General Et Technologique Leonard De Vinci;2 Rue Robquin;95470;St Witz;49.0876254;2.55935 +0170029P;LycĂ©e General Et Technologique Leonce Vieljeux;118 Rue Des Gonthieres;17028;La Rochelle;46.1774956;-1.1513475 +0271579V;LycĂ©e General Et Technologique Leopold Sedar Senghor;Rue Du Canada;27035;Evreux;49.0100928;1.1380869 +0760109F;LycĂ©e General Et Technologique Les Bruyeres;67 Avenue Des Canadiens;76300;Sotteville Les Rouen;49.4127725;1.0733177 +0701052N;LycĂ©e General Et Technologique Les Haberges;1 Rue Du Dr Jean Georges Girard;70014;Vesoul;47.630099;6.140727 +0721329D;LycĂ©e General Et Technologique Les Horizons;Rue Victor Duruy;72650;St Saturnin;48.056355;0.165149 +0100025Y;LycĂ©e General Et Technologique Les Lombards;12 Avenue Des Lombards;10000;Troyes;48.2747672;4.0725464 +0780515L;LycĂ©e General Et Technologique Les Sept Mares;13 Rue De La Beauce;78310;Maurepas;48.766684;1.9452384 +0261277X;LycĂ©e General Et Technologique Les Trois Sources;110 Chemin Du Valentin;26501;Bourg Les Valence;44.9493203;4.8967491 +0440029T;LycĂ©e General Et Technologique Livet;16 Rue Dufour;44042;Nantes;47.2252268;-1.5446675 +0830032J;LycĂ©e General Et Technologique Lorgues;1 Rue Emile Heraud;83510;Lorgues;43.4925907;6.3552396 +0693330V;LycĂ©e General Et Technologique Louis Aragon;12 Che De La Cote A Cailloux;69700;Givors;45.6012034;4.7609173 +0680034T;LycĂ©e General Et Technologique Louis Armand;3 Boulevard Des Nations;68058;Mulhouse;47.7321514;7.305391 +0731248K;LycĂ©e General Et Technologique Louis Armand;321 Rue Du Grand Champ;73020;Chambery;45.5947932;5.9203139 +0860037Y;LycĂ©e General Et Technologique Louis Armand;63 Rue De La Bugellerie;86022;Poitiers;46.5974421;0.3312595 +0541286E;LycĂ©e General Et Technologique Louis Bertrand;27 Av Albert De Briey;54154;Briey;49.2572689;5.9349594 +0620040G;LycĂ©e General Et Technologique Louis Blaringhem;Boulevard Victor Hugo;62408;Bethune;50.5308387;2.6424351 +0781861Z;LycĂ©e General Et Technologique Louis De Broglie;1 Avenue Jean Beranger;78160;Marly Le Roi;48.8668593;2.1005876 +0640011P;LycĂ©e General Et Technologique Louis De Foix;4 Av Jean Rostand;64103;Bayonne;43.4849353;-1.4754015 +0880019T;LycĂ©e General Et Technologique Louis Lapicque;5 Rue Nicolas Bellot;88021;Epinal;48.1749063;6.4408913 +0540066D;LycĂ©e General Et Technologique Louis Majorelle;16 Rue Porte De Metz;54200;Toul;48.6783902;5.8933465 +0250008Y;LycĂ©e General Et Technologique Louis Pasteur;4 Rue Du LycĂ©e;25043;Besancon;0.0;0.0 +0590117G;LycĂ©e General Et Technologique Louis Pasteur;1 Rue Des Urbanistes;59000;Lille;50.6417246;3.0706587 +0590207E;LycĂ©e General Et Technologique Louis Pasteur;151 Boulevard Louise Michel;59490;Somain;50.3493932;3.2826358 +0620095S;LycĂ©e General Et Technologique Louis Pasteur;800 Rue Leon Blum;62251;Henin Beaumont;50.4306013;2.9485013 +0670082A;LycĂ©e General Et Technologique Louis Pasteur;24 Rue Humann;67085;Strasbourg;48.5756269;7.7381955 +9741050Y;LycĂ©e General Et Technologique Louis Payen;329 Rue Saint Louis;97863;St Paul;-21.000214;55.2816619 +0250010A;LycĂ©e General Et Technologique Louis Pergaud;91 93 Boulevard Blum;25022;Besancon;47.263734;6.0453694 +0570058D;LycĂ©e General Et Technologique Louis Vincent;Rue De Verdun;57000;Metz;49.1064034;6.1678408 +0270026G;LycĂ©e General Et Technologique Louise Michel;47 Route De Dieppe;27140;Gisors;49.2850688;1.7823312 +0931613Y;LycĂ©e General Et Technologique Louise Michel;70 Avenue Jean Jaures;93000;Bobigny;48.9025957;2.440509 +0841093G;LycĂ©e General Et Technologique Lucie Aubrac;224 Rue Ernest Laffont;84500;Bollene;44.2939051;4.7496556 +0021946E;LycĂ©e General Et Technologique LycĂ©e Europeen;Avenue De Noue;02600;Villers Cotterets;49.2513352;3.0801771 +9741324W;LycĂ©e General Et Technologique Mahatma Gandhi;Zac Porte Des Salazes;97440;St Andre;0.0;0.0 +0240005A;LycĂ©e General Et Technologique Maine De Biran;108 Rue Valette;24108;Bergerac;44.8571395;0.4744008 +0140013N;LycĂ©e General Et Technologique Malherbe;14 Avenue Albert Sorel;14052;Caen;49.1758952;-0.3712852 +0783140P;LycĂ©e General Et Technologique Mansart;26 Rue Victorien Sardou;78210;St Cyr L Ecole;48.8044358;2.0638552 +0271582Y;LycĂ©e General Et Technologique Marc Bloch;1 Voie Bacheliere;27106;Val De Reuil;49.2680332;1.2021861 +0672604S;LycĂ©e General Et Technologique Marc Bloch;Allee Blaise Pascal;67803;Bischheim;48.6165342;7.7652418 +0511926S;LycĂ©e General Et Technologique Marc Chagall;60 Chaussee Saint Martin;51726;Reims;49.2416308;4.0282581 +0130037V;LycĂ©e General Et Technologique Marcel Pagnol;128 Boulevard De Saint Loup;13395;Marseille 10;43.2831428;5.4344926 +0910623H;LycĂ©e General Et Technologique Marcel Pagnol;Avenue De La Terrasse;91205;Athis Mons;48.6946547;2.3768362 +0690104N;LycĂ©e General Et Technologique Marcel Sembat;20 Boulevard Marcel Sembat;69694;Venissieux;45.7159355;4.8819736 +0760110G;LycĂ©e General Et Technologique Marcel Sembat;128 Rue Leon Salva;76300;Sotteville Les Rouen;49.4152133;1.0855593 +0310039Z;LycĂ©e General Et Technologique Marcelin Berthelot;59 Rue Achille Viadieu;31078;Toulouse;43.5883613;1.4444026 +0860003L;LycĂ©e General Et Technologique Marcelin Berthelot;1 Avenue Du President Wilson;86106;Chatellerault;46.8148527;0.5406476 +0930124E;LycĂ©e General Et Technologique Marcelin Berthelot;110 Avenue Jean Jaures;93500;Pantin;48.9063319;2.3950721 +0940120V;LycĂ©e General Et Technologique Marcelin Berthelot;6 Boulevard Maurice Berteaux;94100;St Maur Des Fosses;48.8100943;2.470758 +0810033W;LycĂ©e General Et Technologique Marechal Soult;Rue Du LycĂ©e;81207;Mazamet;0.0;0.0 +0590086Y;LycĂ©e General Et Technologique Marguerite De Flandre;15 Rue Pasteur;59147;Gondecourt;50.5443046;2.9799955 +0180006J;LycĂ©e General Et Technologique Marguerite De Navarre;50 Rue De Vauvert;18016;Bourges;47.0813154;2.3815361 +0610002W;LycĂ©e General Et Technologique Marguerite De Navarre;Avenue Du General Leclerc;61014;Alencon;48.4166835;0.0894658 +0160003S;LycĂ©e General Et Technologique Marguerite De Valois;Rue Louise Leriget;16017;Angouleme;45.6532678;0.1797211 +0470018P;LycĂ©e General Et Technologique Marguerite Filhol;Avenue Charles De Gaulle;47501;Fumel;44.498344;0.958251 +0721493G;LycĂ©e General Et Technologique Marguerite Yourcenar;2 Rue Du Miroir;72007;Le Mans;47.9895703;0.2007066 +0141555P;LycĂ©e General Et Technologique Marie Curie;16 Rue Octave Greard;14504;Vire;48.8401949;-0.8975237 +0382920T;LycĂ©e General Et Technologique Marie Curie;Avenue Du 8 Mai 1945;38435;Echirolles;45.1701009;5.7563365 +0650026A;LycĂ©e General Et Technologique Marie Curie;Rue Georges Ledormeur;65930;Tarbes;43.223345;0.07366 +0782567S;LycĂ©e General Et Technologique Marie Curie;70 Av De Paris;78002;Versailles;48.7987327;2.1414585 +0100023W;LycĂ©e General Et Technologique Marie De Champagne;2 Avenue Marie De Champagne;10026;Troyes;48.3040593;4.0617662 +0133244F;LycĂ©e General Et Technologique Marie Madeleine Fourcade;Avenue Du Groupe Manouchian;13120;Gardanne;43.455669;5.470649 +0383263R;LycĂ©e General Et Technologique Marie Reynoard;20 Rue Louis Neel;38190;Villard Bonnot;45.2511485;5.8974898 +0622949U;LycĂ©e General Et Technologique Mariette;69 Rue De Beaurepaire;62321;Boulogne Sur Mer;50.7321268;1.6119389 +0130038W;LycĂ©e General Et Technologique Marseilleveyre;83 Traverse Parangon;13285;Marseille 08;43.2463268;5.3798313 +0772292Y;LycĂ©e General Et Technologique Martin Luther King;21 Av Du General De Gaulle;77600;Bussy St Georges;48.8390621;2.7187791 +0350042F;LycĂ©e General Et Technologique Maupertuis;Rue Pierre De Coubertin;35407;St Malo;48.6400752;-2.0058975 +0911927A;LycĂ©e General Et Technologique Maurice Eliot;Rue De Provence;91860;Epinay Sous Senart;48.6992445;2.5232739 +0132410Z;LycĂ©e General Et Technologique Maurice Genevoix;Avenue General De Gaulle;13700;Marignane;43.4128456;5.2048951 +0790007D;LycĂ©e General Et Technologique Maurice Genevoix;36 Rue De Malabry;79301;Bressuire;46.8407623;-0.4847921 +0921399W;LycĂ©e General Et Technologique Maurice Genevoix;29 Avenue Du Fort;92120;Montrouge;48.810644;2.323172 +0170022G;LycĂ©e General Et Technologique Maurice Merleau-ponty;3 Rue Raymonde Maous;17304;Rochefort;45.9505449;-0.9713481 +0750715V;LycĂ©e General Et Technologique Maurice Ravel;89 Cours De Vincennes;75020;Paris 20;48.8476082;2.4082145 +0330088S;LycĂ©e General Et Technologique Max Linder;43 All Robert Boulin;33505;Libourne;44.9171154;-0.2403445 +0940580V;LycĂ©e General Et Technologique Maximilien Sorre;61 Avenue Du President Wilson;94230;Cachan;48.789111;2.325304 +0680032R;LycĂ©e General Et Technologique Michel De Montaigne;5 Rue De Metz;68090;Mulhouse;47.7489658;7.3421381 +0920149M;LycĂ©e General Et Technologique Michelet;5 Rue Jullien;92174;Vanves;48.8250185;2.287109 +0600015R;LycĂ©e General Et Technologique Mireille Grenet;13 Avenue De Huy;60321;Compiegne;49.4025055;2.8188505 +0740037N;LycĂ©e General Et Technologique Mme De Stael;16 Route De Thairy;74163;St Julien En Genevois;46.1409969;6.0704696 +0270017X;LycĂ©e General Et Technologique Modeste Leroy;32 Rue Pierre Brossolette;27016;Evreux;49.0216265;1.1618627 +0080027L;LycĂ©e General Et Technologique Monge;2 Avenue De St Julien;08000;Charleville Mezieres;49.7613023;4.7131743 +0210017E;LycĂ©e General Et Technologique Montchapet;36 Boulevard Francois Pompon;21000;Dijon;47.3343589;5.0174446 +0630068G;LycĂ©e General Et Technologique Montdory;Cite Pontel 7 Ter Av J.jaures;63304;Thiers;45.857907;3.545177 +0951723G;LycĂ©e General Et Technologique Montesquieu;Rue Emile Zola;95220;Herblay;48.9963505;2.152554 +9720727H;LycĂ©e General Et Technologique Montgerald;Quartier Montgerald;97290;Le Marin;0.0;0.0 +0130042A;LycĂ©e General Et Technologique Montgrand;13 Rue Montgrand;13291;Marseille 06;43.2915831;5.3783833 +0630034V;LycĂ©e General Et Technologique Murat;27 Boulevard Pasteur;63500;Issoire;45.5506873;3.2484695 +0610026X;LycĂ©e General Et Technologique Napoleon;15 Rue Des Sports;61306;L Aigle;48.7582148;0.6363548 +9710940V;LycĂ©e General Et Technologique Nord Basse Terre;La Ramee;97115;Ste Rose;45.9474919;4.838131 +0332081H;LycĂ©e General Et Technologique Odilon Redon;R Maquis Des Vignes Oudides;33250;Pauillac;45.1907714;-0.7495194 +0930123D;LycĂ©e General Et Technologique Olympe De Gouges;205 Rue De Brement;93130;Noisy Le Sec;48.8872318;2.4667557 +0310047H;LycĂ©e General Et Technologique Ozenne;9 Rue Merly;31070;Toulouse;43.610058;1.4428833 +0762169V;LycĂ©e General Et Technologique Pablo Neruda;Chemin Des Bruyeres;76204;Dieppe;49.900744;1.06796 +0620027T;LycĂ©e General Et Technologique Pablo Picasso;2 Boulevard Anatole France;62210;Avion;50.4005382;2.8354531 +0660014G;LycĂ©e General Et Technologique Pablo Picasso;120 Avenue General Jean Gilles;66028;Perpignan;42.6975235;2.9260966 +0941347D;LycĂ©e General Et Technologique Pablo Picasso;2 Avenue Pablo Picasso;94120;Fontenay Sous Bois;48.855411;2.4862776 +0332722E;LycĂ©e General Et Technologique Pape Clement;1 Rue Leo Lagrange;33605;Pessac;44.8086751;-0.6500731 +0690074F;LycĂ©e General Et Technologique Parc Chabrieres;9 Chemin Des Chassagnes;69600;Oullins;45.7224869;4.8100176 +0911251R;LycĂ©e General Et Technologique Parc Des Loges;Boulevard Des Champs Elysees;91012;Evry;48.6410335;2.4334713 +0060029Z;LycĂ©e General Et Technologique Parc Imperial;2 Avenue Paul Arene;06050;Nice;43.7038318;7.2505348 +0320002D;LycĂ©e General Et Technologique Pardailhan;Chemin De Baron;32008;Auch;43.6546824;0.5749589 +7200021K;LycĂ©e General Et Technologique Pascal Paoli;Av President Pierucci;20250;Corte;42.3041836;9.1512357 +0130002G;LycĂ©e General Et Technologique Paul Cezanne;Av J Et M Fontenaille;13100;Aix En Provence;43.5346979;5.467873 +0020032Z;LycĂ©e General Et Technologique Paul Claudel;Place Foch;02001;Laon;49.5655582;3.6097485 +0141275K;LycĂ©e General Et Technologique Paul Cornu;9 Rue Paul Cornu;14107;Lisieux;49.15711;0.2224269 +0590034S;LycĂ©e General Et Technologique Paul Duez;1 Boulevard Paul Bezin;59407;Cambrai;50.174142;3.2420519 +0790024X;LycĂ©e General Et Technologique Paul Guerin;19 Rue Des Fiefs;79004;Niort;46.3145227;-0.443402 +0590010R;LycĂ©e General Et Technologique Paul Hazard;1 Rue Paul Hazard;59426;Armentieres;50.6883173;2.8763804 +0730037U;LycĂ©e General Et Technologique Paul Heroult;307 Avenue Du Mont Cenis;73302;St Jean De Maurienne;45.2725564;6.3502605 +0600002B;LycĂ©e General Et Technologique Paul Langevin;3 Avenue Montaigne;60009;Beauvais;49.414897;2.1063143 +0920147K;LycĂ©e General Et Technologique Paul Langevin;2 Rue Payret Dortail;92150;Suresnes;48.877042;2.2221624 +0920138A;LycĂ©e General Et Technologique Paul Lapie;5 Bd Aristide Briand;92401;Courbevoie;48.899279;2.257718 +0110004V;LycĂ©e General Et Technologique Paul Sabatier;36 Rue Alfred De Musset;11802;Carcassonne;43.2222389;2.3451227 +0720055U;LycĂ©e General Et Technologique Paul Scarron;La Brunetiere;72140;Sille Le Guillaume;48.1924459;-0.118455 +0080039Z;LycĂ©e General Et Technologique Paul Verlaine;Rue Du Docteur Gobinet;08305;Rethel;49.5167514;4.3704596 +0370039S;LycĂ©e General Et Technologique Paul-louis Courier;2 Pl Gregoire De Tours;37012;Tours;47.3959502;0.6953666 +0441992B;LycĂ©e General Et Technologique Pays De Retz;Place Joseph Girard;44211;Pornic;47.1175499;-2.096457 +0130036U;LycĂ©e General Et Technologique Perier;270 Rue Paradis;13295;Marseille 08;43.2942989;5.3763908 +0720027N;LycĂ©e General Et Technologique Perseigne;Rue Jean Jaures;72600;Mamers;48.349964;0.363104 +0332835C;LycĂ©e General Et Technologique Philippe Cousteau;13 Rue Arnaudin;33240;St Andre De Cubzac;44.9914275;-0.4497814 +0840005Z;LycĂ©e General Et Technologique Philippe De Girard;138 Av De Tarascon;84082;Avignon;43.9250989;4.8091279 +0301552Z;LycĂ©e General Et Technologique Philippe Lamour;Rue De L Occitanie;30001;Nimes;43.8322264;4.3863175 +0080045F;LycĂ©e General Et Technologique Pierre Bayle;Rue Rogissart;08200;Sedan;49.6930223;4.9478574 +0383119J;LycĂ©e General Et Technologique Pierre Beghin;Rue De La Roche Brune;38430;Moirans;45.3211861;5.5864031 +0230020X;LycĂ©e General Et Technologique Pierre Bourdan;Place Moliere;23011;Gueret;46.171715;1.871472 +0690132U;LycĂ©e General Et Technologique Pierre Brossolette;161 Cours Emile Zola;69628;Villeurbanne;45.7698199;4.8784389 +0782822U;LycĂ©e General Et Technologique Pierre Corneille;Avenue Corneille;78170;La Celle St Cloud;48.8450625;2.1372043 +0600014P;LycĂ©e General Et Technologique Pierre D Ailly;136 Bd Des Etats Unis;60321;Compiegne;49.406211;2.8338602 +0310024H;LycĂ©e General Et Technologique Pierre D'aragon;14 Avenue Henri Peyrusse;31605;Muret;43.4478905;1.3276737 +0620062F;LycĂ©e General Et Technologique Pierre De Coubertin;320 Boulevard Du 8 Mai;62225;Calais;50.958627;1.837843 +0770931U;LycĂ©e General Et Technologique Pierre De Coubertin;Chaussee De Paris;77100;Meaux;48.9559126;2.869815 +0020049T;LycĂ©e General Et Technologique Pierre De La Ramee;1 Rue Jules Siegfried;02100;St Quentin;49.8511637;3.3005423 +0382270L;LycĂ©e General Et Technologique Pierre Du Terrail;Avenue Du LycĂ©e;38530;Pontcharra;0.0;0.0 +0060026W;LycĂ©e General Et Technologique Pierre Et Marie Curie;Avenue Du Doyen Jean Lepine;06500;Menton;43.777619;7.485669 +0360009P;LycĂ©e General Et Technologique Pierre Et Marie Curie;31 Rue P Et M Curie;36018;Chateauroux;46.809443;1.7081627 +0501219D;LycĂ©e General Et Technologique Pierre Et Marie Curie;377 Rue De L'exode;50010;St Lo;49.107496;-1.0893717 +0880040R;LycĂ©e General Et Technologique Pierre Et Marie Curie;Rue Victor Martin;88307;Neufchateau;48.358763;5.6969839 +0891168L;LycĂ©e General Et Technologique Pierre Larousse;6 Rue Des Montagnes;89130;Toucy;47.739374;3.2968476 +0800046R;LycĂ©e General Et Technologique Pierre Mendes France;Route De Saint Denis;80201;Peronne;49.9361685;2.9435387 +0772188K;LycĂ©e General Et Technologique Pierre Mendes-france;11 Avenue De L'europe;77176;Savigny Le Temple;48.5970024;2.576709 +0850025R;LycĂ©e General Et Technologique Pierre Mendes-france;Boulevard Arago;85021;La Roche Sur Yon;46.6704474;-1.4451322 +9740952S;LycĂ©e General Et Technologique Pierre Poivre;Rue Hippolyte Foucque;97480;St Joseph;-21.3746802;55.6251873 +0040490L;LycĂ©e General Et Technologique Pierre-gilles De Gennes;Quartier St Christophe;04000;Digne Les Bains;44.077491;6.187476 +0861223M;LycĂ©e General Et Technologique Pilote Innovant International;Teleport 5;86130;Jaunay Clan;46.6634435;0.3613592 +0781297L;LycĂ©e General Et Technologique Plaine De Neauphle;Avenue Salvador Allende;78190;Trappes;48.783188;1.98746 +0270042Z;LycĂ©e General Et Technologique Porte De Normandie;830 Chemin Des Poissonniers;27130;Verneuil Sur Avre;48.7405634;0.9156189 +0380089R;LycĂ©e General Et Technologique Portes De L'oisans;Avenue Aristide Briand;38220;Vizille;45.067413;5.769686 +0450049J;LycĂ©e General Et Technologique Pothier;2 Bis Rue Marcel Proust;45044;Orleans;47.908273;1.9068319 +0240035H;LycĂ©e General Et Technologique Pre De Cordy;5 Avenue Josephine Baker;24200;Sarlat La Caneda;44.8703019;1.2059934 +0390786T;LycĂ©e General Et Technologique Pre Saint Sauveur;Cite Scolaire Pre St Sauveur;39201;St Claude;46.387405;5.8677659 +0030051P;LycĂ©e General Et Technologique Presles;Boulevard Du 8 Mai 1945;03306;Cusset;46.131694;3.442323 +9710921Z;LycĂ©e General Et Technologique Providence;Morne L'epingle Providence;97139;Les Abymes;16.27395;-61.502615 +0920798T;LycĂ©e General Et Technologique Rabelais;6 Rue Georges Langrognet;92190;Meudon;48.8019343;2.2402667 +0750688R;LycĂ©e General Et Technologique Rabelais-paramedical Et Socia;9 Rue Francis De Croisset;75018;Paris 18;48.8994731;2.3455576 +0720012X;LycĂ©e General Et Technologique Racan;9 Avenue Du Mans;72500;Chateau Du Loir;47.6987575;0.4132499 +0750664P;LycĂ©e General Et Technologique Racine;20 Rue Du Rocher;75008;Paris 08;48.8765125;2.3224703 +0870118F;LycĂ©e General Et Technologique Raoul Dautry;14 Rue Du Puy Imbert;87036;Limoges;45.8387379;1.2785581 +0580032V;LycĂ©e General Et Technologique Raoul Follereau;9 Boulevard Saint Exupery;58000;Nevers;47.0040888;3.1563606 +0900004R;LycĂ©e General Et Technologique Raoul Follereau;3 Rue Louis Marchal;90016;Belfort;47.6383708;6.8462164 +0550002D;LycĂ©e General Et Technologique Raymond Poincare;1 Place Paul Lemagny;55012;Bar Le Duc;48.7772859;5.1572672 +0594424N;LycĂ©e General Et Technologique Raymond Queneau;Place Leon Blum;59650;Villeneuve D Ascq;50.6501468;3.1057879 +0120031U;LycĂ©e General Et Technologique Raymond Savignac;Rue Agnes Savignac;12203;Villefranche De Rouergue;44.3563661;2.0381734 +0530012A;LycĂ©e General Et Technologique Reaumur;39 Avenue Chanzy;53013;Laval;48.0669908;-0.7596169 +0280036M;LycĂ©e General Et Technologique Remi Belleau;33 Rue Bretonnerie;28400;Nogent Le Rotrou;48.3189144;0.8109948 +0352235P;LycĂ©e General Et Technologique Rene Cassin;Route De St Meen Le Grand;35162;Montfort Sur Meu;48.138879;-1.957605 +0690085T;LycĂ©e General Et Technologique Rene Cassin;75 Route De Saint Clement;69173;Tarare;45.8973407;4.4437914 +0911632E;LycĂ©e General Et Technologique Rene Cassin;17 Rue Jean Moulin;91294;Arpajon;48.580006;2.2446388 +0950646L;LycĂ©e General Et Technologique Rene Cassin;7 Avenue Francois Mitterrand;95500;Gonesse;48.985717;2.4325823 +0840935K;LycĂ©e General Et Technologique Rene Char;2 Rue Pierre A Renoir;84033;Avignon;43.9460637;4.8531827 +0351907H;LycĂ©e General Et Technologique Rene Descartes;Chemin De Ronde;35207;Rennes;48.0893228;-1.6435067 +0631861F;LycĂ©e General Et Technologique Rene Descartes;Avenue Jules Ferry;63801;Cournon D Auvergne;45.7436203;3.2103095 +0693654X;LycĂ©e General Et Technologique Rene Descartes;Avenue De Gadagne;69230;St Genis Laval;45.6945428;4.7859462 +0340023R;LycĂ©e General Et Technologique Rene Gosse;2 Rue Victor Hugo;34800;Clermont L Herault;43.6264775;3.4318207 +0170027M;LycĂ©e General Et Technologique Rene Josue Valin;Rue Henri Barbusse;17023;La Rochelle;46.155842;-1.137402 +0290062K;LycĂ©e General Et Technologique Rene Laennec;61 Rue Du LycĂ©e;29120;Pont L Abbe;0.0;0.0 +0730043A;LycĂ©e General Et Technologique Rene Perrin;41 Rue Rene Perrin;73400;Ugine;45.7514631;6.4161324 +0680060W;LycĂ©e General Et Technologique Ribeaupierre;12 Rue Du Chateau;68150;Ribeauville;48.1969038;7.3163765 +0920799U;LycĂ©e General Et Technologique Richelieu;64 Rue George Sand;92501;Rueil Malmaison;48.8669727;2.1806262 +0311323V;LycĂ©e General Et Technologique Rive Gauche;Avenue Jean Baylet;31081;Toulouse;43.5846573;1.3942102 +9710884J;LycĂ©e General Et Technologique Riviere Des Peres;Riviere Des Peres;97100;Basse Terre;16.008758;-61.7407286 +0801841S;LycĂ©e General Et Technologique Robert De Luzarches;4 Rue Le Mongnier;80097;Amiens;49.8856067;2.3204605 +0693619J;LycĂ©e General Et Technologique Robert Doisneau;5 Rue Du LycĂ©e;69511;Vaulx En Velin;0.0;0.0 +0570057C;LycĂ©e General Et Technologique Robert Schuman;4 Rue Monseigneur Pelt;57074;Metz;49.1122787;6.2034203 +0670020H;LycĂ©e General Et Technologique Robert Schuman;2 Quai Des Pecheurs;67504;Haguenau;48.8145686;7.7953074 +0760058A;LycĂ©e General Et Technologique Robert Schuman;51 Avenue Du 8 Mai;76610;Le Havre;49.5161566;0.1664377 +0620006V;LycĂ©e General Et Technologique Robespierre;Avenue Des Fusilles;62022;Arras;50.2867806;2.7650924 +0750696Z;LycĂ©e General Et Technologique Roger Verlomme;24 Rue Fondary;75015;Paris 15;48.8480367;2.2932272 +0950640E;LycĂ©e General Et Technologique Romain Rolland;1 Place Romain Rolland;95100;Argenteuil;48.9516193;2.2306041 +0280019U;LycĂ©e General Et Technologique Rotrou;Les Marchebeaux;28104;Dreux;48.734267;1.362417 +0260017C;LycĂ©e General Et Technologique Roumanille;Draye De Meyne;26111;Nyons;44.361204;5.139882 +0490055U;LycĂ©e General Et Technologique Sadi Carnot;25 Rue Marceau;49401;Saumur;47.250839;-0.0746699 +0130039X;LycĂ©e General Et Technologique Saint Charles;5 Rue Guy Fabre;13232;Marseille 01;43.3058152;5.3920359 +0130048G;LycĂ©e General Et Technologique Saint Exupery;529 Che De La Madrague Ville;13326;Marseille 15;43.3463387;5.3547981 +0690028F;LycĂ©e General Et Technologique Saint Just;21 Rue Des Farges;69321;Lyon 05;45.7575431;4.8217746 +0171418Z;LycĂ©e General Et Technologique Saint-exupery;Allee De Lattre De Tassigny;17028;La Rochelle;46.1569483;-1.1815663 +0641732K;LycĂ©e General Et Technologique Saint-john Perse;2 Rue J Ferry;64012;Pau;43.319586;-0.3639694 +0141796B;LycĂ©e General Et Technologique Salvador Allende;15 Rue Guyon De Guercheville;14200;Herouville St Clair;49.2033372;-0.3448826 +0941470M;LycĂ©e General Et Technologique Samuel De Champlain;61 Rue Des Bordes;94430;Chennevieres Sur Marne;48.7987853;2.5577377 +9740043D;LycĂ©e General Et Technologique Sarda Garriga;755 Rue De La Communaute;97440;St Andre;-20.9643338;55.6558272 +0850032Y;LycĂ©e General Et Technologique Savary De Mauleon;78 Avenue De Bretagne;85108;Les Sables D Olonne;46.5047869;-1.7776341 +0680073K;LycĂ©e General Et Technologique Scheurer Kestner;1 Rue Moschenross;68802;Thann;47.8011401;7.1139147 +0670002N;LycĂ©e General Et Technologique Schure;2 Rue Du LycĂ©e;67142;Barr;0.0;0.0 +0080007P;LycĂ©e General Et Technologique Sevigne;14 Rue Mme De Sevigne;08013;Charleville Mezieres;49.7710181;4.7172012 +0352304P;LycĂ©e General Et Technologique Sevigne;2 Rue De La Chalotais;35513;Cesson Sevigne;48.1647545;-1.5796421 +0590215N;LycĂ©e General Et Technologique Sevigne;151 Rue De La Malcense;59208;Tourcoing;50.730799;3.1650448 +0951766D;LycĂ©e General Et Technologique Simone De Beauvoir;171 Av De Stalingrad;95141;Garges Les Gonesse;48.9677071;2.4101572 +0420044V;LycĂ©e General Et Technologique Simone Weil;63 Avenue Albert Raimond;42272;St Priest En Jarez;45.4752;4.3691484 +0430021P;LycĂ©e General Et Technologique Simone Weil;22 Boulevard Marechal Joffre;43003;Le Puy En Velay;45.048158;3.8937244 +0750651A;LycĂ©e General Et Technologique Simone Weil;7 Rue De Poitou;75003;Paris 03;48.8613887;2.3639619 +0501839C;LycĂ©e General Et Technologique Sivard De Beaulieu;7 Bis Rue Sivard De Beaulieu;50500;Carentan;49.304491;-1.2397239 +0620063G;LycĂ©e General Et Technologique Sophie Berthelot;224 Boulevard Gambetta;62104;Calais;50.9466002;1.8418599 +0750653C;LycĂ©e General Et Technologique Sophie Germain;9 Rue De Jouy;75004;Paris 04;48.8548803;2.3581925 +0520027X;LycĂ©e General Et Technologique St Exupery;82 Rue Anatole France;52105;St Dizier;48.6430006;4.9626225 +0782539L;LycĂ©e General Et Technologique St Exupery;8 Rue Marcel Fouque;78201;Mantes La Jolie;48.9959726;1.6947006 +9710922A;LycĂ©e General Et Technologique Ste Anne;Poirier De Gissac;97180;Ste Anne;0.0;0.0 +0210012Z;LycĂ©e General Et Technologique Stephen Liegeard;1 Rue Stephen Liegeard;21220;Brochon;47.2390412;4.9716086 +0573326F;LycĂ©e General Et Technologique Teyssier;23 Rue Du Schellenthal;57230;Bitche;49.0565652;7.4161082 +0840004Y;LycĂ©e General Et Technologique Theodore Aubanel;14 Rue De La Palapharnerie;84025;Avignon;43.952567;4.8119249 +0061691F;LycĂ©e General Et Technologique Thierry Maulnier;2 Avenue Claude Debussy;06200;Nice;43.6854715;7.1997893 +0130040Y;LycĂ©e General Et Technologique Thiers;5 Place Du LycĂ©e;13232;Marseille 01;0.0;0.0 +0760005T;LycĂ©e General Et Technologique Thomas Corneille;Avenue Andre Maurois;76360;Barentin;49.5566128;0.9530339 +0311586F;LycĂ©e General Et Technologique Toulouse-lautrec;64 Bd Pierre Curie;31020;Toulouse;43.6251328;1.4415296 +0290051Y;LycĂ©e General Et Technologique Tristan Corbiere;16 Rue De Kerveguen;29671;Morlaix;48.5705251;-3.8221176 +0750647W;LycĂ©e General Et Technologique Turgot;69 Rue De Turbigo;75003;Paris 03;48.8660831;2.3583935 +0870056N;LycĂ©e General Et Technologique Turgot;6 Rue Paul Derignac;87031;Limoges;45.8284168;1.2486231 +0770918E;LycĂ©e General Et Technologique Uruguay France;1 Avenue Des Marronniers;77211;Avon;48.4103009;2.7355078 +0841117H;LycĂ©e General Et Technologique Vaison La Romaine;Vaison La Romaine;84110;Vaison La Romaine;44.2414859;5.0777653 +0761742F;LycĂ©e General Et Technologique Val De Seine;Avenue Georges Braque;76124;Le Grand Quevilly;49.402714;1.04095 +0062015H;LycĂ©e General Et Technologique Valbonne;1265 Route De Biot;06560;Valbonne;43.6435755;7.0381165 +0590122M;LycĂ©e General Et Technologique Valentine Labbe;41 Rue Paul Doumer;59110;La Madeleine;50.6473736;3.0721697 +0911913K;LycĂ©e General Et Technologique Vallee De Chevreuse;8 Rue De Madrid;91192;Gif Sur Yvette;48.7000948;2.0991226 +0762879S;LycĂ©e General Et Technologique Vallee Du Cailly;Rue Du Petit Aulnay;76250;Deville Les Rouen;49.462879;1.045217 +0771512A;LycĂ©e General Et Technologique Van Dongen;45 Rue Jean Mermoz;77400;Lagny Sur Marne;48.8677351;2.7191655 +0950645K;LycĂ©e General Et Technologique Van Gogh;Rue Du General Decaen;95123;Ermont;48.9828272;2.2525097 +0080018B;LycĂ©e General Et Technologique Vauban;15 Rue Andre Bouzy;08600;Givet;50.1333015;4.8290573 +0380033E;LycĂ©e General Et Technologique Vaucanson;27 Rue Anatole France;38030;Grenoble;45.1722442;5.7083809 +0130003H;LycĂ©e General Et Technologique Vauvenargues;60 Boulevard Carnot;13625;Aix En Provence;43.5284055;5.4549853 +0791062A;LycĂ©e General Et Technologique Venise Verte;71 Rue Laurent Bonnevay;79012;Niort;46.3171043;-0.4858123 +0440031V;LycĂ©e General Et Technologique Vial;12 Rue Du 14 Juillet;44042;Nantes;47.2182601;-1.5678263 +0400017B;LycĂ©e General Et Technologique Victor Duruy;Rue Noneres;40002;Mont De Marsan;43.898948;-0.499903 +0352009U;LycĂ©e General Et Technologique Victor Et Helene Basch;15 Avenue Charles Tillon;35083;Rennes;48.1268772;-1.6876718 +0501828R;LycĂ©e General Et Technologique Victor Grignard;12 R Guillaume Fouace;50100;Cherbourg Octeville;49.6385355;-1.6291646 +0130043B;LycĂ©e General Et Technologique Victor Hugo;3 Boulevard Gustave Desplaces;13003;Marseille 03;43.3051852;5.3805831 +0250007X;LycĂ©e General Et Technologique Victor Hugo;1 Rue Rembrandt;25052;Besancon;47.2183047;5.9777021 +0530004S;LycĂ©e General Et Technologique Victor Hugo;4 Rue Du Gal Lemonnier;53200;Chateau Gontier;47.828315;-0.6982177 +0561607T;LycĂ©e General Et Technologique Victor Hugo;26 Avenue Francois Mitterrand;56704;Hennebont;47.8091467;-3.2627756 +0840016L;LycĂ©e General Et Technologique Victor Hugo;139 Av Victor Hugo;84208;Carpentras;44.0515235;5.0474084 +0860034V;LycĂ©e General Et Technologique Victor Hugo;10 Rue Victor Hugo;86034;Poitiers;46.5805351;0.337763 +9720002V;LycĂ©e General Et Technologique Victor Schoelcher;Boulevard Robert Attuly;97262;Fort De France;14.6029213;-61.0763884 +0250032Z;LycĂ©e General Et Technologique Viette;1b Rue P Donzelot;25206;Montbeliard;47.4951765;6.8092234 +0930834B;LycĂ©e General Et Technologique Voillaume;136 Rue De Mitry;93604;Aulnay Sous Bois;48.9457777;2.5099926 +0450782F;LycĂ©e General Et Technologique Voltaire;3 Avenue Voltaire;45072;Orleans;47.8350087;1.937364 +0623915U;LycĂ©e General Et Technologique Voltaire;Place Bertin Ledoux;62410;Wingles;50.494578;2.852404 +0750675B;LycĂ©e General Et Technologique Voltaire;101 Avenue De La Republique;75011;Paris 11;48.8634943;2.3846316 +0590222W;LycĂ©e General Et Technologique Watteau;20 Boulevard Pater;59307;Valenciennes;50.3582668;3.5317714 +0932034F;LycĂ©e General Et Technologique Wolfgang Amadeus Mozart;10 Ave Charles De Gaulle;93152;Le Blanc Mesnil;48.9438125;2.4679399 +0640017W;LycĂ©e General Experimental A.malraux;Rue Du 8 Mai 1945;64203;Biarritz;43.485562;-1.545229 +0590116F;LycĂ©e General Fenelon;27 Rue Alexandre Leleux;59013;Lille;50.6315402;3.0572537 +0750660K;LycĂ©e General Fenelon;2 Rue De L'eperon;75006;Paris 06;48.8533839;2.3409528 +0340005W;LycĂ©e General Ferdinand Fabre;Boulevard Jean Moulin;34600;Bedarieux;43.613182;3.155169 +0120022J;LycĂ©e General Ferdinand Foch;1 Rue Vieussens;12000;Rodez;44.350556;2.5620421 +0312754A;LycĂ©e General Fonsorbes;Avenue Lucie Aubrac Fonsorbes;31470;Fonsorbes;43.535456;1.230645 +0783548H;LycĂ©e General Franco Allemand;5 Rue Collin Mamet;78530;Buc;48.7712546;2.1214749 +0760052U;LycĂ©e General Francois 1er;2 Rue Jean-paul Sartre;76066;Le Havre;49.4954384;0.1137507 +0770927P;LycĂ©e General Francois 1er;11 Rue Victor Hugo;77300;Fontainebleau;48.4091563;2.6957656 +0260006R;LycĂ©e General Francois Jean Armorin;35 Avenue H.grand;26402;Crest;44.731422;5.0164927 +0330026Z;LycĂ©e General Francois Magendie;10 Rue Des Treuils;33023;Bordeaux;44.8270751;-0.5854467 +0820016X;LycĂ©e General Francois Mitterrand;Boulevard Du Quercy;82201;Moissac;44.1012226;1.0931804 +0350710G;LycĂ©e General Francois Rene De Chateaubriand;136 Bd De Vitre;35703;Rennes;48.1220757;-1.6510693 +0840003X;LycĂ©e General Frederic Mistral;37 Rue D Annanelle;84023;Avignon;43.9467812;4.7999217 +0670049P;LycĂ©e General Freppel;25 Rue Du Gen Gouraud;67212;Obernai;48.4616378;7.4873303 +0670079X;LycĂ©e General Fustel De Coulanges;1 Place Du Chateau;67061;Strasbourg;48.5816654;7.7520269 +0750684L;LycĂ©e General Gabriel Faure;81 Avenue De Choisy;75013;Paris 13;48.823831;2.3615927 +0440024M;LycĂ©e General Gabriel Guisthau;3 Rue Marie Anne Du Boccage;44042;Nantes;47.2163672;-1.5683753 +0442112G;LycĂ©e General Galilee;16 Avenue Gustave Flaubert;44353;Guerande;47.3241762;-2.4171927 +0870015U;LycĂ©e General Gay Lussac;12 Boulevard Georges Perin;87031;Limoges;45.8308335;1.2627435 +0470028A;LycĂ©e General George Sand;Bd Pierre De Coubertin;47600;Nerac;44.137773;0.334861 +0754684J;LycĂ©e General Georges Brassens;40 Rue Manin;75019;Paris 19;48.8840069;2.3904607 +0510031G;LycĂ©e General Georges Clemenceau;46 Avenue Georges Clemenceau;51096;Reims;49.2516684;4.0457383 +0300008W;LycĂ©e General Gerard Philipe;17 Avenue Leon Blum;30205;Bagnols Sur Ceze;44.1625881;4.616978 +0240013J;LycĂ©e General Giraut De Borneil;10 Boulevard A Dupuy;24160;Excideuil;45.3397691;1.0460386 +0570107G;LycĂ©e General Helene Boucher;55 Boulevard Foch;57100;Thionville;49.358428;6.1612623 +0750714U;LycĂ©e General Helene Boucher;75 Cours De Vincennes;75020;Paris 20;48.8477131;2.4070344 +0750654D;LycĂ©e General Henri Iv;23 Rue Clovis;75005;Paris 05;48.8463061;2.347663 +0020048S;LycĂ©e General Henri Martin;1 Rue Gabriel Girodon;02100;St Quentin;49.8694377;3.2486887 +0782562L;LycĂ©e General Hoche;73 Avenue De St Cloud;78000;Versailles;48.806093;2.136819 +0783549J;LycĂ©e General International;Rue Du Fer A Cheval;78104;St Germain En Laye;48.8978284;2.0627911 +0383242T;LycĂ©e General International Europole;4 Place De Sfax;38012;Grenoble;45.1946007;5.7111866 +0890003V;LycĂ©e General Jacques Amyot;3 Rue De L'etang Saint Vigile;89015;Auxerre;47.799159;3.571808 +0750668U;LycĂ©e General Jacques Decour;12 Avenue Trudaine;75009;Paris 09;48.881468;2.3434282 +0750699C;LycĂ©e General Janson De Sailly;106 Rue De La Pompe;75016;Paris 16;48.8655035;2.2794818 +0750702F;LycĂ©e General Jean De La Fontaine;1 Place De La Porte Molitor;75016;Paris 16;48.8451877;2.2565988 +0360008N;LycĂ©e General Jean Giraudoux;31 Avenue Marcel Lemoine;36000;Chateauroux;46.8162727;1.696676 +0460026D;LycĂ©e General Jean Lurcat;Quai Jules Ferry;46400;St Cere;44.8580683;1.8929695 +0332745E;LycĂ©e General Jean Monnet;70 Av Gl De Gaulle;33294;Blanquefort;44.910084;-0.635244 +0371417P;LycĂ©e General Jean Monnet;45 Rue De La Gitonniere;37306;Joue Les Tours;47.3382097;0.6558827 +0331636Z;LycĂ©e General Jean Moulin;Bd Francois Mauriac;33210;Langon;44.5478647;-0.2565149 +0690030H;LycĂ©e General Jean Moulin;1 Place Des Minimes;69321;Lyon 05;45.757748;4.8209387 +0420031F;LycĂ©e General Jean Puy;Rue Jean Puy;42328;Roanne;46.0381422;4.0698154 +0630019D;LycĂ©e General Jeanne D'arc;40 Avenue De Grande Bretagne;63037;Clermont Ferrand;45.777262;3.0955677 +0320036R;LycĂ©e General Joseph Saverne;5 Avenue Claude Auge;32600;L Isle Jourdain;43.6128214;1.0853875 +0750669V;LycĂ©e General Jules Ferry;77 Boulevard De Clichy;75009;Paris 09;48.883866;2.3288165 +0640047D;LycĂ©e General Jules Supervielle;Boulevard F.mitterrand;64404;Oloron Ste Marie;43.184366;-0.621821 +0440022K;LycĂ©e General Jules Verne;1 Rue General Meusnier;44042;Nantes;47.2173915;-1.5617182 +0670080Y;LycĂ©e General Kleber;25 Place De Bordeaux;67082;Strasbourg;48.5950246;7.7579489 +0920143F;LycĂ©e General La Folie Saint James;41 Rue De Longchamp;92200;Neuilly Sur Seine;48.8825303;2.2557562 +0510053F;LycĂ©e General La Fontaine Du Ve;Avenue De La Fontaine Du Ve;51122;Sezanne;48.726156;3.7170255 +0880030E;LycĂ©e General La Haie Griselle;Boite Postale 109;88407;Gerardmer;48.070081;6.877292 +0010013J;LycĂ©e General Lalande;16 Rue Du LycĂ©e;01011;Bourg En Bresse;0.0;0.0 +0750670W;LycĂ©e General Lamartine;121 Rue Du Fg Poissonniere;75009;Paris 09;48.8784359;2.3490775 +0681761V;LycĂ©e General Lambert;73 Rue Josue Heilmann;68069;Mulhouse;47.7546303;7.3302166 +0810006S;LycĂ©e General Laperouse;2 Lices Georges Pompidou;81030;Albi;43.9302181;2.1472285 +0810030T;LycĂ©e General Las Cases;Place De La Resistance;81500;Lavaur;43.698135;1.8209846 +0620017G;LycĂ©e General Lavoisier;99 Rue Jean Jaures;62260;Auchel;50.5075071;2.4754481 +0750656F;LycĂ©e General Lavoisier;17 Rue Henri Barbusse;75005;Paris 05;48.8423289;2.3392448 +0500065Z;LycĂ©e General Le Verrier;7 Rue Le Verrier;50002;St Lo;49.114593;-1.085313 +0460013P;LycĂ©e General Leo Ferre;75 Avenue Cavaignac;46300;Gourdon;44.7341503;1.3771929 +0510015P;LycĂ©e General Leon Bourgeois;29 Avenue De Champagne;51331;Epernay;49.043149;3.9594547 +0870016V;LycĂ©e General Leonard Limosin;13 Rue Des Clairettes;87036;Limoges;45.8296501;1.2542492 +0640055M;LycĂ©e General Louis Barthou;2 Rue Louis Barthou;64015;Pau;43.2946775;-0.3652763 +0750655E;LycĂ©e General Louis Le Grand;123 Rue Saint Jacques;75005;Paris 05;48.8479761;2.3441552 +0920142E;LycĂ©e General Louis Pasteur;17 A 21 Bd D Inkermann;92200;Neuilly Sur Seine;48.8858222;2.2741732 +0800009A;LycĂ©e General Louis Thuillier;70 Boulevard De Saint Quentin;80098;Amiens;49.8791607;2.2965051 +9840001D;LycĂ©e General LycĂ©e D'uturoa;Uturoa;98735;Uturoa;-16.7331706;-151.4403326 +9840002E;LycĂ©e General LycĂ©e Paul Gauguin;Papeete;98714;Papeete;-17.535022;-149.569594 +0030025L;LycĂ©e General Madame De Stael;1 Rue Madame De Stael;03100;Montlucon;46.3388313;2.6097306 +0800010B;LycĂ©e General Madeleine Michelis;43 Rue Des Otages;80037;Amiens;49.8890443;2.3029357 +0280007F;LycĂ©e General Marceau;2 Rue Pierre Mendes France;28000;Chartres;48.4414461;1.4951576 +0140061R;LycĂ©e General Marcel Gambier;3 Rue General Leclerc;14107;Lisieux;49.1496609;0.2243572 +0670083B;LycĂ©e General Marie Curie;7 Rue De Leicester;67084;Strasbourg;48.5777746;7.7720751 +0920146J;LycĂ©e General Marie Curie;1 Rue Constant Pilate;92331;Sceaux;48.7778987;2.2870922 +0060030A;LycĂ©e General Massena;2 Avenue Felix Faure;06050;Nice;43.6991436;7.2762535 +0640065Y;LycĂ©e General Maurice Ravel;Av Du Pr Maranon;64502;St Jean De Luz;43.388051;-1.663055 +0590181B;LycĂ©e General Maxence Van Der Meersch;1 Av Maxence Van Der Meersch;59052;Roubaix;50.7014845;3.238885 +0330021U;LycĂ©e General Michel Montaigne;118 Cours Victor Hugo;33075;Bordeaux;44.834861;-0.5718881 +0130045D;LycĂ©e General Michelet;21 Avenue Marechal Foch;13248;Marseille 04;43.3009814;5.3993878 +0650012K;LycĂ©e General Michelet;148 Rue Michelet;65303;Lannemezan;43.1267858;0.3861872 +0820020B;LycĂ©e General Michelet;22 Faubourg Lacapelle;82004;Montauban;44.0133311;1.3611955 +0750703G;LycĂ©e General Moliere;71 Rue Du Ranelagh;75016;Paris 16;48.8550931;2.272105 +0750657G;LycĂ©e General Montaigne;17 Rue Auguste Comte;75006;Paris 06;48.8442327;2.3349031 +0330022V;LycĂ©e General Montesquieu;4 Et 5 Place De Longchamps;33081;Bordeaux;48.7535502;0.0030397 +0720029R;LycĂ©e General Montesquieu;1 Rue Montesquieu;72008;Le Mans;48.0099285;0.2002142 +0130010R;LycĂ©e General Montmajour;Che Des Moines Qua Du Trebon;13200;Arles;43.6017739;4.6243751 +0332724G;LycĂ©e General Nord Bassin;128 Avenue De Bordeaux;33510;Andernos Les Bains;44.7518058;-1.0797311 +0750689S;LycĂ©e General Paul Bert;7 Rue Huyghens;75014;Paris 14;48.8408655;2.328794 +0640046C;LycĂ©e General Paul Rey;6 Avenue Jean Seigneres;64800;Nay;43.1810505;-0.2540108 +0340075X;LycĂ©e General Paul Valery;55 Rue Paul Valery;34200;Sete;43.4013903;3.6930027 +0750679F;LycĂ©e General Paul Valery;38 Boulevard Soult;75012;Paris 12;48.8391381;2.4089398 +0442334Y;LycĂ©e General Pierre Abelard;Chemin Du Rouaud;44330;Vallet;47.1685954;-1.2643431 +0510006E;LycĂ©e General Pierre Bayen;22 Rue Du LycĂ©e;51037;Chalons En Champagne;0.0;0.0 +0312696M;LycĂ©e General Pierre Bourdieu;Avenue De Villaudric;31620;Fronton;43.834408;1.401836 +0760090K;LycĂ©e General Pierre Corneille;4 Rue Du Maulevrier;76044;Rouen;49.4453249;1.1001636 +0310036W;LycĂ©e General Pierre De Fermat;Parvis Des Jacobins;31068;Toulouse;43.604652;1.444209 +0650038N;LycĂ©e General Pierre Mendes France;Rue Du College;65501;Vic En Bigorre;43.388411;0.056435 +0710011B;LycĂ©e General Pontus De Tyard;13 Rue Des Gaillardons;71321;Chalon Sur Saone;46.7792939;4.8365859 +0596122J;LycĂ©e General PrivĂ© Active Bilingue Jeannine Manue;418 Bis Rue Albert Bailly;59700;Marcq En Baroeul;50.6905276;3.1198309 +0940880W;LycĂ©e General PrivĂ© Albert De Mun;12-14 Av Des Marronniers;94736;Nogent Sur Marne;48.8371754;2.470904 +0331491S;LycĂ©e General PrivĂ© Albert Le Grand;189 Rue De Saint Genes;33000;Bordeaux;44.8239861;-0.5819519 +0931813R;LycĂ©e General PrivĂ© Alliance;35 Allee Robert Estienne;93320;Les Pavillons Sous Bois;48.9066801;2.5114479 +0133555U;LycĂ©e General PrivĂ© Ami;47 Rue St Suffren;13006;Marseille 06;43.2871912;5.3813577 +0311132M;LycĂ©e General PrivĂ© Annonciation;7 Chemin De Percin;31840;Seilh;43.678854;1.3673649 +0350778F;LycĂ©e General PrivĂ© Assomption;18 Boulevard Painleve;35702;Rennes;48.1217772;-1.6560228 +0772648K;LycĂ©e General PrivĂ© Assomption;2 Rue De Salins;77130;Forges;48.418206;2.960877 +0810071M;LycĂ©e General PrivĂ© Barral;113 Rue Marcel Briguiboul;81100;Castres;43.593354;2.2327115 +0941909P;LycĂ©e General PrivĂ© Bernard Palissy;2 Rue Merciere;94470;Boissy St Leger;48.7508613;2.5140211 +0754325U;LycĂ©e General PrivĂ© Beth Hanna;49 Rue Petit;75019;Paris 19;48.8855041;2.3853435 +0910838S;LycĂ©e General PrivĂ© Beth Rivkah;43 49 Rue Raymond Poincare;91330;Yerres;48.711413;2.5006397 +0753384W;LycĂ©e General PrivĂ© Beth Yacov;50 Bis Rue Des Prairies;75020;Paris 20;48.8634435;2.401922 +0610700E;LycĂ©e General PrivĂ© Bignon;3 Rue De La Comedie;61400;Mortagne Au Perche;48.5222373;0.5449647 +0440154D;LycĂ©e General PrivĂ© Blanche De Castille;43-45 Boulevard Jules Verne;44319;Nantes;47.237147;-1.5333106 +0930974D;LycĂ©e General PrivĂ© Blanche De Castille;1 Place Charles De Gaulle;93250;Villemomble;48.9012637;2.4777022 +0753915Y;LycĂ©e General PrivĂ© Blomet;5 Rue Blomet;75015;Paris 15;48.8440336;2.3089779 +0753887T;LycĂ©e General PrivĂ© Bossuet-notre-dame;17 Rue Yves Toudic;75010;Paris 10;48.8704201;2.3632866 +0622107D;LycĂ©e General PrivĂ© Catho Haffreingue Chanlaire;67 Avenue Charles De Gaulle;62200;Boulogne Sur Mer;50.7302448;1.6189027 +0260064D;LycĂ©e General PrivĂ© Chabrillan;Route De Dieulefit;26207;Montelimar;44.552049;4.6923919 +0511131C;LycĂ©e General PrivĂ© Charles Peguy;2 Bis Allees Paul Doumer;51037;Chalons En Champagne;48.9525137;4.3710585 +0930962R;LycĂ©e General PrivĂ© Charles Peguy;216 Avenue Henri Barbusse;93000;Bobigny;48.9116357;2.434482 +0690574Z;LycĂ©e General PrivĂ© Chevreul;21 Rue Sala;69287;Lyon 02;45.7560734;4.8289023 +0131335F;LycĂ©e General PrivĂ© Chevreul Blancarde;1 Rue Saint Francois De Sales;13248;Marseille 04;43.3017559;5.4016838 +0932110N;LycĂ©e General PrivĂ© Chne Or;150 Rue Andre Karman;93300;Aubervilliers;48.9112912;2.3852031 +0680141J;LycĂ©e General PrivĂ© College Episcopal;5 Rue Du Seminaire;68720;Zillisheim;47.7002658;7.3057379 +0671609K;LycĂ©e General PrivĂ© College Episcopal St Etienne;2 Rue De La Pierre Large;67084;Strasbourg;48.583169;7.75531 +0131344R;LycĂ©e General PrivĂ© Cours Bastide;50 Rue De Lodi;13006;Marseille 06;43.2897281;5.386952 +0141165R;LycĂ©e General PrivĂ© Cours Notre-dame;Rue De L'arbalete;14440;Douvres La Delivrande;49.2954884;-0.3728552 +0440158H;LycĂ©e General PrivĂ© D'amboise-chavagnes;11 Rue Mondesir;44015;Nantes;47.2177089;-1.5694414 +0160065J;LycĂ©e General PrivĂ© De Beaulieu;23 Place Beaulieu;16105;Cognac;45.6944011;-0.3306255 +0753943D;LycĂ©e General PrivĂ© De La Tour;86 Rue De La Tour;75016;Paris 16;48.8615462;2.2788627 +0690529A;LycĂ©e General PrivĂ© Deborde;72 Rue Ney;69006;Lyon 06;45.7666327;4.8556717 +0271051W;LycĂ©e General PrivĂ© Des Roches;Avenue Edmond Demolins;27137;Verneuil Sur Avre;48.7386465;0.911939 +0753848A;LycĂ©e General PrivĂ© D'hulst;21 Rue De Varenne;75007;Paris 07;48.8536447;2.3250815 +0292137R;LycĂ©e General PrivĂ© Diwan;Kerampuil;29270;Carhaix Plouguer;48.273696;-3.552728 +0530052U;LycĂ©e General PrivĂ© Don Bosco;18 Bd Anatole France;53102;Mayenne;48.295993;-0.6158636 +0680134B;LycĂ©e General PrivĂ© Don Bosco;1 Rue Don Bosco;68440;Landser;47.6849042;7.3871869 +0331498Z;LycĂ©e General PrivĂ© Du Mirail;36 Rue Du Mirail;33000;Bordeaux;44.8333866;-0.5710599 +0070071P;LycĂ©e General PrivĂ© Du Sacre Coeur;7 Avenue De La Gare;07301;Tournon Sur Rhone;45.0637796;4.834587 +0752937K;LycĂ©e General PrivĂ© Ecole Active Bilingue Etoile;24 Bis Rue De Berri;75008;Paris 08;48.8732062;2.3056124 +0753874D;LycĂ©e General PrivĂ© Ecole Active Bilingue J.manuel;70 Rue Du Theatre;75015;Paris 15;48.847462;2.291572 +0753647G;LycĂ©e General PrivĂ© Ecole Alsacienne;109 R Notre Dame Des Champs;75006;Paris 06;48.8409608;2.3336956 +0671615S;LycĂ©e General PrivĂ© Ecole Aquiba;4 Rue Baldung Grien;67000;Strasbourg;48.5899787;7.7529847 +0753883N;LycĂ©e General PrivĂ© Edgar Poe;2 R Du Faubourg Poissonniere;75010;Paris 10;48.8709741;2.3480187 +0930961P;LycĂ©e General PrivĂ© Esperance;35 Rue Anatole France;93600;Aulnay Sous Bois;48.935783;2.4938629 +0290336H;LycĂ©e General PrivĂ© Estran Charles De Foucauld;32 Rue De Quimper;29287;Brest;48.4021619;-4.4602772 +0690533E;LycĂ©e General PrivĂ© Externat De La Trinite;31 Rue De Seze;69006;Lyon 06;45.7682665;4.8465112 +0440160K;LycĂ©e General PrivĂ© Externat Enfants Nantais;31 Avenue Camus;44042;Nantes;47.2189478;-1.5726768 +0381803D;LycĂ©e General PrivĂ© Externat Notre Dame;43 Avenue Marcellin Berthelot;38100;Grenoble;45.1737452;5.731904 +0421021G;LycĂ©e General PrivĂ© Externat Saint Michel;4 Rue Jules Valles;42030;St Etienne;45.4315189;4.3977734 +0631074A;LycĂ©e General PrivĂ© Fenelon;1 Cours Raymond Poincare;63037;Clermont Ferrand;45.7709705;3.0900322 +0830099G;LycĂ©e General PrivĂ© Fenelon;251 Rue Pourquoi Pas;83000;Toulon;43.1097419;5.9519764 +0931799A;LycĂ©e General PrivĂ© Fenelon;1 Rue De Montauban;93410;Vaujours;48.9301569;2.5695529 +0753873C;LycĂ©e General PrivĂ© Fenelon Sainte-marie;24 Rue Du General Foy;75008;Paris 08;48.8784763;2.3179146 +0941693E;LycĂ©e General PrivĂ© Foyer Des Ptt;36 Avenue Du President Wilson;94234;Cachan;48.7922449;2.3291571 +0753935V;LycĂ©e General PrivĂ© Georges Leven;30 Boulevard Carnot;75012;Paris 12;48.8424354;2.4129184 +0753941B;LycĂ©e General PrivĂ© Gerson;31 Rue De La Pompe;75116;Paris 16;48.8608035;2.2751876 +0771720B;LycĂ©e General PrivĂ© Guy Gasnier-sainte Bathilde;28 Rue Du Tir;77500;Chelles;48.8893418;2.5961092 +0755025E;LycĂ©e General PrivĂ© Heikhal Menahem Sinai;110 Boulevard De Menilmontant;75020;Paris 20;48.8655642;2.384824 +0930965U;LycĂ©e General PrivĂ© Henri Matisse;88 Bis Rue Jules Guesde;93100;Montreuil;48.8627932;2.4792419 +0120052S;LycĂ©e General PrivĂ© Immaculee Conception;Rue Auzuech;12500;Espalion;44.525222;2.769631 +0690554C;LycĂ©e General PrivĂ© Immaculee Conception;74 Place Grandclement;69613;Villeurbanne;45.7591491;4.8897359 +0840074Z;LycĂ©e General PrivĂ© Immaculee Conception;18 Rue Des Marins;84208;Carpentras;44.0539842;5.0490871 +0596335R;LycĂ©e General PrivĂ© Institut D Anchin;Abbaye D'anchin;59146;Pecquencourt;50.375524;3.212056 +0690564N;LycĂ©e General PrivĂ© Institution Des Chartreux;58 Rue Pierre Dupont;69283;Lyon 01;45.7718191;4.8226139 +0141169V;LycĂ©e General PrivĂ© Institution Fremont;12 Rue Paul Banaston;14100;Lisieux;49.1476683;0.2237108 +0141157G;LycĂ©e General PrivĂ© Institution Jeanne D'arc;10 Rue D'eterville;14403;Bayeux;49.2809816;-0.7065406 +6200650M;LycĂ©e General PrivĂ© Institution Saint-paul;Avenue Marechal Lyautey;20186;Ajaccio;41.9379483;8.7447941 +0671552Y;LycĂ©e General PrivĂ© Jan Amos Comenius;8 Place Des Etudiants;67060;Strasbourg;48.5842395;7.7489578 +0511130B;LycĂ©e General PrivĂ© Jean Xxiii;18 Rue Andrieux;51064;Reims;49.2592782;4.0360628 +0572341K;LycĂ©e General PrivĂ© Jean Xxiii;10 R Monseigneur Heintz;57958;Montigny Les Metz;49.0988857;6.1588903 +0110047S;LycĂ©e General PrivĂ© Jeanne D Arc;47 Rue De La Baffe;11400;Castelnaudary;43.3159746;1.957673 +0640127R;LycĂ©e General PrivĂ© Jeanne D Arc;47 Rue Moncade;64300;Orthez;43.4903576;-0.7717527 +0141161L;LycĂ©e General PrivĂ© Jeanne D'arc;27 Rue Claude Chappe;14000;Caen;49.1815244;-0.395588 +0381670J;LycĂ©e General PrivĂ© Jeanne D'arc;2 Rue Poincare;38550;Le Peage De Roussillon;45.3678002;4.7966512 +0490822C;LycĂ©e General PrivĂ© Jeanne D'arc;3 Rue Joubert;49103;Angers;47.4725034;-0.5449917 +0650062P;LycĂ©e General PrivĂ© Jeanne D'arc;17 Rue Massey;65000;Tarbes;43.2347802;0.0738099 +0730760E;LycĂ©e General PrivĂ© Jeanne D'arc;3 Place De L'eglise;73203;Albertville;45.6780169;6.3908431 +0771232W;LycĂ©e General PrivĂ© Jeanne D'arc Saint Aspais;18 Boulevard Andre Maginot;77300;Fontainebleau;48.4057991;2.6893858 +0260062B;LycĂ©e General PrivĂ© Jeunes Filles;77 Rue Geoffroy De Moirans;26330;Chateauneuf De Galaure;45.2345255;4.9553014 +0693769X;LycĂ©e General PrivĂ© Juif De Lyon;40 Rue Alexandre Boutin;69100;Villeurbanne;45.768352;4.868835 +0592935V;LycĂ©e General PrivĂ© La Croix Blanche;2 Rue De L'abbe Six;59588;Bondues;50.7198225;3.1094008 +0622116N;LycĂ©e General PrivĂ© La Malassise;Pension Saint Joseph;62968;Longuenesse;50.736744;2.243632 +0541986R;LycĂ©e General PrivĂ© La Malgrange;Avenue De La Malgrange;54140;Jarville La Malgrange;48.669525;6.199726 +0442226F;LycĂ©e General PrivĂ© La Mennais;24 Rue Jean Baptiste Legeay;44350;Guerande;47.3216018;-2.4317213 +0570201J;LycĂ©e General PrivĂ© La Misericorde;11 Rue Des Recollets;57000;Metz;49.1206035;6.1806771 +0440172Y;LycĂ©e General PrivĂ© La Perverie Sacre Coeur;63 Rue De La Perverie;44322;Nantes;47.2408522;-1.5621761 +0753851D;LycĂ©e General PrivĂ© La Rochefoucauld;90 Bis Rue Saint Dominique;75007;Paris 07;48.8594019;2.3057007 +0331504F;LycĂ©e General PrivĂ© La Sauque;Le Bourg;33650;La Brede;44.6707635;-0.4790839 +0693587Z;LycĂ©e General PrivĂ© La Xaviere;252 Route De Vienne;69008;Lyon 08;45.7282439;4.8537692 +0131324U;LycĂ©e General PrivĂ© Lacordaire;7 Boulevard Lacordaire;13013;Marseille 13;43.3197549;5.4057658 +0753849B;LycĂ©e General PrivĂ© L'alma;12 Avenue Bosquet;75007;Paris 07;48.8602799;2.3023934 +0753948J;LycĂ©e General PrivĂ© L'assomption;6 Rue De Lubeck;75116;Paris 16;48.8666108;2.2940415 +0311126F;LycĂ©e General PrivĂ© Le Ferradou;Route De Grenade;31700;Blagnac;43.6401295;1.3886897 +0850118S;LycĂ©e General PrivĂ© L'esperance;Bourdevaire;85110;Ste Cecile;46.711412;-1.113992 +0771228S;LycĂ©e General PrivĂ© Libre De Juilly;7 Rue Barre;77230;Juilly;49.0111848;2.7045603 +0131345S;LycĂ©e General PrivĂ© L'olivier - Robert Coffy;29 Avenue Des Caillols;13012;Marseille 12;43.3039572;5.4277833 +0753827C;LycĂ©e General PrivĂ© Louise De Marillac;32 R Geoffroy Saint Hilaire;75005;Paris 05;48.8408725;2.3561278 +0080081V;LycĂ©e General PrivĂ© Mabillon;23 Av Martyrs De La Resistance;08201;Sedan;49.7000092;4.930206 +0610702G;LycĂ©e General PrivĂ© Marie Immaculee;6 Rue Charles Forget;61500;Sees;48.6067391;0.1716333 +0141172Y;LycĂ©e General PrivĂ© Marie-joseph;Avenue De La Marniere;14360;Trouville Sur Mer;49.3710312;0.1146815 +0631847R;LycĂ©e General PrivĂ© Massillon;5 Rue Bansac;63037;Clermont Ferrand;45.7786092;3.091566 +0753824Z;LycĂ©e General PrivĂ© Massillon;2 Bis Quai Des Celestins;75004;Paris 04;48.851529;2.3607945 +0131328Y;LycĂ©e General PrivĂ© Melizan;55 Route Des Camoins;13011;Marseille 11;43.2971335;5.4945421 +0932303Y;LycĂ©e General PrivĂ© Merkaz Hatorah Filles;67 Boulevard Du Midi;93340;Le Raincy;48.8945151;2.5251787 +0931464L;LycĂ©e General PrivĂ© Merkaz Hatorah Garcons;92-94 Chemin Des Bourdons;93220;Gagny;48.8909288;2.5225507 +0631076C;LycĂ©e General PrivĂ© Monanges;7 Rue De Metz;63000;Clermont Ferrand;45.7807747;3.0980538 +0060668U;LycĂ©e General PrivĂ© Mont Saint Jean;Avenue Du Chataignier;06600;Antibes;43.5809119;7.1129044 +0940881X;LycĂ©e General PrivĂ© Montalembert;28 Boulevard Gambetta;94736;Nogent Sur Marne;48.838178;2.4713696 +0753823Y;LycĂ©e General PrivĂ© Moria - Diane Benvenuti;9 11 Rue Lekain;75116;Paris 16;48.8571388;2.277618 +0131319N;LycĂ©e General PrivĂ© Nativite (la);8 Rue Jean Andreani La Beauvale;13097;Aix En Provence;43.529742;5.447427 +0622118R;LycĂ©e General PrivĂ© Nazareth;84 Rue De Maquetra;62280;St Martin Boulogne;50.7287576;1.6255495 +0440171X;LycĂ©e General PrivĂ© Nd De L'abbaye;38 Bis Rue De L Abbaye;44100;Nantes;47.1998104;-1.5962954 +0440166S;LycĂ©e General PrivĂ© Nd De Toutes Aides;33 Boulevard Louis Millet;44319;Nantes;47.22695;-1.519285 +0421660B;LycĂ©e General PrivĂ© Nd De Valbenoite Le Rond Point;10 Place De L'abbaye;42030;St Etienne;45.4216919;4.3986281 +0440178E;LycĂ©e General PrivĂ© Nd D'esperance;15 Rue Du Bois Savary;44615;St Nazaire;47.2747567;-2.2080565 +0490834R;LycĂ©e General PrivĂ© Nd D'orveau;;49500;Nyoiseau;47.716305;-0.914938 +0211074D;LycĂ©e General PrivĂ© Notre Dame;97 Rue De Talant;21000;Dijon;47.3310729;5.0153828 +0260069J;LycĂ©e General PrivĂ© Notre Dame;91 Rue Montplaisir;26000;Valence;44.930458;4.9086288 +0331499A;LycĂ©e General PrivĂ© Notre Dame;45 Rue Du Palais Gallien;33000;Bordeaux;44.843183;-0.5815713 +0421025L;LycĂ©e General PrivĂ© Notre Dame;9 Rue Cacherat;42190;Charlieu;46.1614459;4.1732205 +0490837U;LycĂ©e General PrivĂ© Notre Dame;;49310;La Salle De Vihiers;47.15538;-0.63641 +0720833P;LycĂ©e General PrivĂ© Notre Dame;46 Rue De La Magdeleine;72205;La Fleche;47.7006579;-0.0802937 +0690540M;LycĂ©e General PrivĂ© Notre Dame De Bellegarde;22 Avenue Gambetta;69250;Neuville Sur Saone;45.870459;4.8419155 +0640122K;LycĂ©e General PrivĂ© Notre Dame De Betharram;1 Place Saint Michel;64800;Lestelle Betharram;43.125087;-0.212631 +0131333D;LycĂ©e General PrivĂ© Notre Dame De France;132 Rue Breteuil;13253;Marseille 06;43.2846524;5.378104 +0290174G;LycĂ©e General PrivĂ© Notre Dame De Kerbertrand;154 Rue De Pont Aven;29391;Quimperle;47.8710014;-3.5663631 +0940877T;LycĂ©e General PrivĂ© Notre Dame De La Providence;7 Avenue Gabriel Peri;94307;Vincennes;48.8472148;2.4409415 +0690552A;LycĂ©e General PrivĂ© Notre Dame De Mongre;276 Avenue Saint Exupery;69652;Villefranche Sur Saone;45.9909387;4.7116054 +0131341M;LycĂ©e General PrivĂ© Notre Dame De Sion;231 Rue Paradis;13006;Marseille 06;43.2823556;5.3815876 +0690519P;LycĂ©e General PrivĂ© Notre Dame Des Minimes;65 Rue Des Aqueducs;69322;Lyon 05;45.7594493;4.789366 +0941877E;LycĂ©e General PrivĂ© Notre Dame Des Missions;4 Rue Du President Kennedy;94220;Charenton Le Pont;48.8223455;2.404544 +0541318P;LycĂ©e General PrivĂ© Notre Dame Saint Sigisbert;19 21 Cours Leopold;54042;Nancy;48.695498;6.1750984 +0090036R;LycĂ©e General PrivĂ© Notre-dame;3 Place Des Cordeliers;09100;Pamiers;43.1175009;1.6124603 +0593141U;LycĂ©e General PrivĂ© Notre-dame;15 Rue Des Capucins;59308;Valenciennes;50.358955;3.5292575 +0753902J;LycĂ©e General PrivĂ© Notre-dame De France;63 Rue De La Sante;75013;Paris 13;48.833057;2.3413569 +0592938Y;LycĂ©e General PrivĂ© Notre-dame De Grace;31 Boulevard De La Liberte;59402;Cambrai;50.1705607;3.2302062 +0593109J;LycĂ©e General PrivĂ© Notre-dame De La Paix;14 Place Du Concert;59009;Lille;50.6427151;3.0616162 +0622120T;LycĂ©e General PrivĂ© Notre-dame De Sion;52 Rue Courteville;62501;St Omer;50.7540117;2.2558843 +0592913W;LycĂ©e General PrivĂ© Notre-dame Des Anges;4 Rue Du Bruille;59733;St Amand Les Eaux;50.4487482;3.4257749 +0593102B;LycĂ©e General PrivĂ© Notre-dame Des Dunes;60 Rue Du Sud;59140;Dunkerque;51.0341311;2.3836906 +0753946G;LycĂ©e General PrivĂ© Notre-dame Des Oiseaux;12 Rue Michel Ange;75016;Paris 16;48.8468059;2.2630177 +0511135G;LycĂ©e General PrivĂ© Notre-dame St Victor;13 Rue Jean Chandon Moet;51200;Epernay;49.0412476;3.9571882 +0931970L;LycĂ©e General PrivĂ© Ohr Sarah;6 Rue Saint Louis;93500;Pantin;48.8914438;2.418068 +0312356T;LycĂ©e General PrivĂ© Ozar Hatorah;33 Rue Jules Dalou;31500;Toulouse;43.6217711;1.4620342 +0754479L;LycĂ©e General PrivĂ© Ozar Hatorah;34 Rue Du Moulin Joly;75011;Paris 11;48.8701778;2.3779337 +0941711Z;LycĂ©e General PrivĂ© Ozar Hatorah Filles;65 Rue Saint Simon;94000;Creteil;48.7947133;2.4451292 +0941943B;LycĂ©e General PrivĂ© Ozar Hatorah Garcons;2 Voie Felix Eboue;94000;Creteil;48.798567;2.4507301 +0753919C;LycĂ©e General PrivĂ© Pascal;33 Boulevard Lannes;75116;Paris 16;48.8670712;2.2721096 +0753809H;LycĂ©e General PrivĂ© Paul Claudel;118 120 Rue De Grenelle;75007;Paris 07;48.8570302;2.319517 +7200073S;LycĂ©e General PrivĂ© Pensionnat Jeanne D Arc;15 Boulevard Benoite Danesi;20297;Bastia;42.6969548;9.4436937 +0940891H;LycĂ©e General PrivĂ© Petit Val;12 Avenue Albert Pleuvry;94370;Sucy En Brie;48.7688364;2.5165907 +0650058K;LycĂ©e General PrivĂ© Peyramale Saint-joseph;13 Avenue Du Marechal Joffre;65100;Lourdes;43.0948515;-0.0445345 +0690538K;LycĂ©e General PrivĂ© Pierre Termier;23 Rue Des Alouettes;69008;Lyon 08;45.7414256;4.8733393 +0740097D;LycĂ©e General PrivĂ© Presentation De Marie;10 Rue Monseigneur Paget;74163;St Julien En Genevois;46.1432999;6.0801747 +0596281G;LycĂ©e General PrivĂ© PrivĂ© De Genech;Rue De La Liberation;59242;Genech;50.5300906;3.2117313 +0261395A;LycĂ©e General PrivĂ© Protestant Du Cedre;La Cloitre;26750;Montmiral;45.1702732;5.1414735 +0131323T;LycĂ©e General PrivĂ© Provence (de);42 Boulevard Emile Sicard;13272;Marseille 08;43.2702033;5.3844699 +0753885R;LycĂ©e General PrivĂ© Rocroy Saint-leon;106 R Du Faubourg Poissonniere;75010;Paris 10;48.8786454;2.3492599 +0911844K;LycĂ©e General PrivĂ© Rudolf Steiner;62 Rue De Paris;91370;Verrieres Le Buisson;48.7391608;2.2441374 +0593139S;LycĂ©e General PrivĂ© Sacre Coeur;111 Rue De Lille;59334;Tourcoing;50.7229;3.149171 +0511142P;LycĂ©e General PrivĂ© Sacre-coeur;86 Rue De Courlancy;51096;Reims;49.2407341;4.0270779 +0592944E;LycĂ©e General PrivĂ© Saint Adrien;15 Rue St Jb De La Salle;59653;Villeneuve D Ascq;50.6288481;3.1487012 +0940885B;LycĂ©e General PrivĂ© Saint Andre;12 Avenue Leon Gourdault;94600;Choisy Le Roi;48.7634434;2.4057917 +0570211V;LycĂ©e General PrivĂ© Saint Antoine;Chemin Des Dames;57370;Phalsbourg;48.7699551;7.2698303 +0570194B;LycĂ©e General PrivĂ© Saint Augustin;56 Rue Jean Jacques Kieffer;57231;Bitche;49.044565;7.4272673 +0260063C;LycĂ©e General PrivĂ© Saint Bonnet;Saint Bonnet;26330;Chateauneuf De Galaure;45.218168;4.940543 +0021870X;LycĂ©e General PrivĂ© Saint Charles;1 Rue Du Brouage;02300;Chauny;49.6147218;3.2121038 +0541319R;LycĂ©e General PrivĂ© Saint Dominique;11 Rue Du Manege;54004;Nancy;48.690925;6.1880469 +0602070Z;LycĂ©e General PrivĂ© Saint Dominique;5,rue Gerard De Nerval;60128;Mortefontaine;49.1109074;2.6017471 +0301499S;LycĂ©e General PrivĂ© Saint Felix;90 Chemin Des Marguilliers;30300;Beaucaire;43.818271;4.636691 +0740104L;LycĂ©e General PrivĂ© Saint Francois;19 Rue Fernand David;74100;Ville La Grand;46.2049328;6.2495591 +0592940A;LycĂ©e General PrivĂ© Saint Jean;246 Rue Saint Jean;59506;Douai;50.3694281;3.0865161 +0690550Y;LycĂ©e General PrivĂ© Saint Joseph;7 Rue Lieutenant Audras;69160;Tassin La Demi Lune;45.7606452;4.7579506 +0740098E;LycĂ©e General PrivĂ© Saint Joseph;St Martin Sur Arve;74703;Sallanches;45.912929;6.689997 +0740099F;LycĂ©e General PrivĂ© Saint Joseph;Rue Bienheureux Pierre Favre;74230;Thones;45.8834922;6.3272261 +0541313J;LycĂ©e General PrivĂ© Saint Leon Ix;20 Rue St Leon;54000;Nancy;48.6891381;6.172546 +0421020F;LycĂ©e General PrivĂ© Saint Louis;22 Rue Desire Claude;42030;St Etienne;45.4290047;4.3889574 +0931812P;LycĂ©e General PrivĂ© Saint Louis-sainte Clotilde;37 Allee De La Fontaine;93340;Le Raincy;48.8928994;2.5177604 +0690571W;LycĂ©e General PrivĂ© Saint Marc;10 Rue Sainte Helene;69287;Lyon 02;45.7550807;4.8288541 +0260066F;LycĂ©e General PrivĂ© Saint Maurice;Rue Eugene Blain;26106;Romans Sur Isere;45.046612;5.042497 +0370728R;LycĂ©e General PrivĂ© Saint Medard Inst N-d La Riche;30 Rue Delperier;37058;Tours;47.3925342;0.6769264 +0593138R;LycĂ©e General PrivĂ© Saint Michel;13 Rue Emile Zola;59730;Solesmes;50.1838289;3.5030972 +0940882Y;LycĂ©e General PrivĂ© Saint Michel De Picpus;10 Ter Rue Jeanne D'arc;94165;St Mande;48.8403691;2.418619 +0421023J;LycĂ©e General PrivĂ© Saint Paul;15 Rue Bourgneuf;42308;Roanne;46.038829;4.0705744 +0421035X;LycĂ©e General PrivĂ© Saint Paul;11 Rue De La Paix;42016;St Etienne;45.4392601;4.385133 +0593114P;LycĂ©e General PrivĂ© Saint Paul;25 Bis Rue Colbert;59000;Lille;50.6278376;3.0477018 +0450108Y;LycĂ©e General PrivĂ© Saint Paul-bourdon Blanc;4 Rue Neuve St Aignan;45057;Orleans;47.8984756;1.9160429 +0593117T;LycĂ©e General PrivĂ© Saint Pierre;18 Rue Saint J-b De La Salle;59000;Lille;50.6284889;3.0392123 +0631032E;LycĂ©e General PrivĂ© Saint Pierre;;63120;Courpiere;45.754178;3.538632 +0570216A;LycĂ©e General PrivĂ© Saint Pierre Chanel;33 Rue Du Chardon;57109;Thionville;49.3635917;6.1516523 +0541312H;LycĂ©e General PrivĂ© Saint Pierre Fourier;14 Rue Des Benedictins;54300;Luneville;48.5874967;6.4975357 +0300083C;LycĂ©e General PrivĂ© Saint Stanislas;16 Rue Des Chassaintes;30900;Nimes;43.8371085;4.3525051 +0622106C;LycĂ©e General PrivĂ© Saint Vaast-saint Dominique;92 Rue De L'universite;62401;Bethune;50.5243179;2.6481843 +0260071L;LycĂ©e General PrivĂ© Saint Victor;3 Rue De La Cecile;26000;Valence;44.9228247;4.8883094 +0381660Y;LycĂ©e General PrivĂ© Sainte Cecile;Avenue Marechal Foch;38260;La Cote St Andre;45.3900283;5.2511309 +0772602K;LycĂ©e General PrivĂ© Sainte Celine;29 Rue Pierre Marx;77262;La Ferte Sous Jouarre;48.9494327;3.1265198 +0570214Y;LycĂ©e General PrivĂ© Sainte Chretienne;20 Rue Ste Croix;57204;Sarreguemines;49.111329;7.0675702 +0593113N;LycĂ©e General PrivĂ© Sainte Claire;8 Rue Des Augustins;59800;Lille;50.6343712;3.0691758 +0772290W;LycĂ©e General PrivĂ© Sainte Croix;1 Rue Des Jacobins;77160;Provins;48.5629412;3.2939093 +0741443S;LycĂ©e General PrivĂ© Sainte Croix Des Neiges;Chef Lieu;74360;Abondance;46.298615;6.789512 +0410676M;LycĂ©e General PrivĂ© Sainte Marie;33 Rue Du Bourgneuf;41013;Blois;47.5902427;1.3309065 +0592932S;LycĂ©e General PrivĂ© Sainte Marie;31 Rue De L'eglise;59134;Beaucamps Ligny;50.6028831;2.9202882 +0631034G;LycĂ©e General PrivĂ© Sainte Marie;3 Place Marinette Menut;63203;Riom;45.8932067;3.1086693 +0771247M;LycĂ©e General PrivĂ© Sainte Marie;41 Rue De Chaage;77109;Meaux;48.9658178;2.8806747 +0830095C;LycĂ©e General PrivĂ© Sainte Marie;1 Place Germain Loro;83501;La Seyne Sur Mer;43.1015838;5.8770157 +0421022H;LycĂ©e General PrivĂ© Sainte Marie La Grand'grange;15 Route Du Coin;42401;St Chamond;45.4714856;4.5209864 +0593106F;LycĂ©e General PrivĂ© Sainte Odile;244 Avenue De Dunkerque;59130;Lambersart;50.6368821;3.0253351 +0753916Z;LycĂ©e General PrivĂ© Sainte-elisabeth;112 Rue De Lourmel;75015;Paris 15;48.8428294;2.2860109 +0753947H;LycĂ©e General PrivĂ© Saint-jean De Passy;72 Rue Raynouard;75016;Paris 16;48.8544406;2.2785455 +0320054K;LycĂ©e General PrivĂ© Saint-joseph;1 Rue De L'abbe Tournier;32700;Lectoure;43.933604;0.623621 +0810075S;LycĂ©e General PrivĂ© Saint-joseph De L'apparition;10 Boulevard Gambetta;81600;Gaillac;43.8992922;1.8986894 +0753880K;LycĂ©e General PrivĂ© Saint-louis;50 Rue De Clichy;75009;Paris 09;48.8802553;2.3290393 +0753933T;LycĂ©e General PrivĂ© Saint-louis De Gonzague;12 Rue Benjamin Franklin;75116;Paris 16;48.8595496;2.2854729 +0660768B;LycĂ©e General PrivĂ© Saint-louis-de-gonzague;71 Av Du Docteur Schweitzer;66043;Perpignan;42.7182794;2.8790947 +0753897D;LycĂ©e General PrivĂ© Saint-michel De Picpus;53 Rue De La Gare De Reuilly;75012;Paris 12;48.8422799;2.3964787 +0753959W;LycĂ©e General PrivĂ© Saint-michel Des Batignolles;35 Avenue De Saint Ouen;75017;Paris 17;48.8897216;2.3263756 +0753898E;LycĂ©e General PrivĂ© Saint-pierre Fourier;13 Rue De Prague;75012;Paris 12;48.84972;2.3757534 +0753838P;LycĂ©e General PrivĂ© Saint-sulpice;68 Rue D'assas;75006;Paris 06;48.8455323;2.3316045 +0820046E;LycĂ©e General PrivĂ© Saint-theodard;12 Quai De Verdun;82008;Montauban;44.0217919;1.3480475 +0753850C;LycĂ©e General PrivĂ© Saint-thomas D'aquin;44 Rue De Grenelle;75007;Paris 07;48.8541469;2.3267057 +0671618V;LycĂ©e General PrivĂ© Seminaire De Jeunes;60 Grand'rue;67360;Walbourg;48.8873256;7.7852027 +0131348V;LycĂ©e General PrivĂ© Sevigne;1 Avenue Saint Jerome;13388;Marseille 13;43.3289588;5.4188772 +0500119H;LycĂ©e General PrivĂ© Sevigne;15 Bd Girard Desprairies;50400;Granville;48.840647;-1.5908235 +0753598D;LycĂ©e General PrivĂ© Sevigne;28 Rue Pierre Nicole;75005;Paris 05;48.8408072;2.3394515 +0753231E;LycĂ©e General PrivĂ© Sinai;2-6 Rue Tristan Tzara;75018;Paris 18;48.8946459;2.3657946 +0100047X;LycĂ©e General PrivĂ© St Bernard;8 Rue Du Palais De Justice;10041;Troyes;48.296588;4.0706044 +0220110A;LycĂ©e General PrivĂ© St Charles;2 Rue Cordiere;22021;St Brieuc;48.5101209;-2.7676223 +0131342N;LycĂ©e General PrivĂ© St Charles Camas;21 Rue Du Camas;13392;Marseille 05;43.2984501;5.3926775 +0440176C;LycĂ©e General PrivĂ© St Dominique;103 Avenue Cheverny;44807;St Herblain;47.2439329;-1.6042189 +0331488N;LycĂ©e General PrivĂ© St Elme;50 Boulevard Deganne;33120;Arcachon;44.6554765;-1.1525843 +0290198H;LycĂ©e General PrivĂ© St Esprit;3 Rue Emile Souvestre;29403;Landivisiau;48.5121112;-4.0797023 +0440167T;LycĂ©e General PrivĂ© St Felix;27 Rue Du Ballet;44001;Nantes;47.2316606;-1.560045 +0640124M;LycĂ©e General PrivĂ© St Francois;1 Rue De Belzunce;64130;Mauleon Licharre;43.2233966;-0.8850869 +0100046W;LycĂ©e General PrivĂ© St Francois De Sales;11 Rue General Saussier;10000;Troyes;48.2955822;4.0742386 +0290160S;LycĂ©e General PrivĂ© St Francois-notre Dame;1 Rue Des Recollets;29260;Lesneven;48.5708202;-4.3245065 +0560114V;LycĂ©e General PrivĂ© St Francois-xavier;3 Rue Thiers;56000;Vannes;47.6538433;-2.7595614 +0050035L;LycĂ©e General PrivĂ© St Joseph;2 Rue Des Pins;05000;Gap;44.5629426;6.0769717 +0352341E;LycĂ©e General PrivĂ© St Joseph;3 Av. Alphonse Legault;35171;Bruz;48.0262162;-1.7463371 +0490835S;LycĂ©e General PrivĂ© St Joseph;50 Rue De La Loire;49620;La Pommeraye;47.3601893;-0.8600084 +0511140M;LycĂ©e General PrivĂ© St Joseph;177 Rue Des Capucins;51095;Reims;49.2475072;4.0338348 +0640125N;LycĂ©e General PrivĂ© St Joseph;Avenue Des Abbes Dupont;64800;Nay;43.1720298;-0.2670033 +0640126P;LycĂ©e General PrivĂ© St Joseph;1 Rue Palou;64400;Oloron Ste Marie;43.188782;-0.6093897 +0840078D;LycĂ©e General PrivĂ© St Joseph;105 Rue Duplessis;84205;Carpentras;44.0524315;5.0457201 +0850083D;LycĂ©e General PrivĂ© St Joseph;40 Rue Victor Hugo;85035;La Roche Sur Yon;46.6732361;-1.426608 +0440161L;LycĂ©e General PrivĂ© St Joseph Du Loquidy;73 Boulevard Michelet;44322;Nantes;47.2388267;-1.5566766 +0440177D;LycĂ©e General PrivĂ© St Louis;10 Boulevard Albert 1er;44616;St Nazaire;47.2681941;-2.217917 +0840075A;LycĂ©e General PrivĂ© St Louis;Colline St Eutrope;84100;Orange;44.132831;4.808196 +0640115C;LycĂ©e General PrivĂ© St Louis Villa Pia;Avenue Marechal Soult;64100;Bayonne;43.4856105;-1.4920018 +0352471W;LycĂ©e General PrivĂ© St Magloire;2 Rue Du Chanoine Boursier;35120;Dol De Bretagne;48.5518844;-1.7542833 +0490824E;LycĂ©e General PrivĂ© St Martin;5 Cloitre Saint Martin;49100;Angers;47.491878;-0.5478767 +0720842Z;LycĂ©e General PrivĂ© St Michel Des Perrais;;72330;Parigne Le Polin;47.8496499;0.10935 +0721549T;LycĂ©e General PrivĂ© St Paul-notre Dame;54 Av De La Republique;72406;La Ferte Bernard;48.1880106;0.646915 +0910812N;LycĂ©e General PrivĂ© St Pierre;70 Rue De Montgeron;91800;Brunoy;48.6945366;2.4899797 +0080082W;LycĂ©e General PrivĂ© St Remi;6 Place Winston Churchill;08000;Charleville Mezieres;49.7718959;4.7217776 +0440163N;LycĂ©e General PrivĂ© St Stanislas;2 Rue Saint Stanislas;44009;Nantes;47.2222759;-1.5552563 +0131343P;LycĂ©e General PrivĂ© St Thomas D Aquin;23 Rue Dieude;13006;Marseille 06;43.2923588;5.3816291 +0350776D;LycĂ©e General PrivĂ© St Vincent-providence;57 Rue De Paris;35064;Rennes;48.1126265;-1.665047 +0060729K;LycĂ©e General PrivĂ© Stanislas;25 Av Bieckert;06008;Nice;43.7082584;7.2717928 +0753840S;LycĂ©e General PrivĂ© Stanislas;22 R Notre Dame Des Champs;75006;Paris 06;48.8457593;2.3277363 +0831551K;LycĂ©e General PrivĂ© Stanislas;2431 Bd Pierre Delli Zotti;83700;St Raphael;43.4356915;6.7948685 +0290338K;LycĂ©e General PrivĂ© Ste Anne;20 Rue Lamotte-picquet;29220;Brest;48.390394;-4.486076 +0470065R;LycĂ©e General PrivĂ© Ste Catherine;2 Chemin De Velours;47300;Villeneuve Sur Lot;44.4033113;0.7143613 +0131320P;LycĂ©e General PrivĂ© Ste Catherine De Sienne;20 Rue Mignet;13100;Aix En Provence;43.5304783;5.4508604 +0450757D;LycĂ©e General PrivĂ© Ste Croix St Euverte;28 Rue De L Etelon;45043;Orleans;47.9025554;1.9171035 +0290194D;LycĂ©e General PrivĂ© Ste Elisabeth;16 Rue Lamennais;29171;Douarnenez;48.0904856;-4.3274811 +0490831M;LycĂ©e General PrivĂ© Ste Marie;43 Rue St Bonaventure;49307;Cholet;47.0594586;-0.8778263 +0331503E;LycĂ©e General PrivĂ© Ste Marie Grand Lebrun;164 Av Charles De Gaulle;33021;Bordeaux;44.8502305;-0.6056323 +0131347U;LycĂ©e General PrivĂ© Ste Trinite;55 Av Lattre Tassigny Mazargue;13009;Marseille 09;43.2465459;5.4082558 +0754081D;LycĂ©e General PrivĂ© Ste Ursule-louise Bettignies;25 Rue Daubigny;75017;Paris 17;48.8858511;2.3075599 +0730759D;LycĂ©e General PrivĂ© Talmudique;50 Montee De La Reine Victoria;73105;Aix Les Bains;45.6922535;5.8982884 +0754860A;LycĂ©e General PrivĂ© Therese Chappuis;52 Rue Vaneau;75007;Paris 07;48.8510132;2.3191724 +0731040J;LycĂ©e General PrivĂ© Tomer Debora;9 Chemin De Saint Pol;73100;Aix Les Bains;45.6854748;5.9200229 +0131327X;LycĂ©e General PrivĂ© Tour Sainte (de);Av De Tour Sainte Ste Marthe;13014;Marseille 14;43.3450548;5.3873213 +0490823D;LycĂ©e General PrivĂ© Urbain Mongazon;1 Rue Du Colombier;49036;Angers;47.4592971;-0.5294038 +0131360H;LycĂ©e General PrivĂ© Viala Lacoste;16 Avenue Gaston Cabrier;13300;Salon De Provence;43.6407537;5.1006531 +0753834K;LycĂ©e General PrivĂ© Yabne;29 Avenue Leon Bollee;75013;Paris 13;48.8179276;2.3624561 +0132472S;LycĂ©e General PrivĂ© Yavne;44 Bd Barry St Just;13013;Marseille 13;43.3178816;5.4096639 +0330115W;LycĂ©e General Reclus;7 Avenue De Verdun;33220;Ste Foy La Grande;44.8382054;0.2152357 +0640010N;LycĂ©e General Rene Cassin;2 Rue Lasseguette;64100;Bayonne;43.4809394;-1.4805897 +0750682J;LycĂ©e General Rodin;19 Rue Corvisart;75013;Paris 13;48.8325019;2.3470622 +0360002G;LycĂ©e General Rollinat;Rue Du LycĂ©e;36200;Argenton Sur Creuse;0.0;0.0 +0940115P;LycĂ©e General Romain Rolland;17 Rue Lucien Nadaire;94200;Ivry Sur Seine;48.8015275;2.3935972 +0694026B;LycĂ©e General Rosa Parks;13 Rue Pollet;69582;Neuville Sur Saone;45.8763307;4.8483431 +0731507S;LycĂ©e General Saint Exupery;107 Rue Du Pre De Foire;73704;Bourg St Maurice;45.6188058;6.7651855 +0400046H;LycĂ©e General Saint-exupery;Av Du LycĂ©e Cite Scolaire;40160;Parentis En Born;0.0;0.0 +0750658H;LycĂ©e General Saint-louis;44 Boulevard Saint Michel;75006;Paris 06;48.848959;2.341577 +0310041B;LycĂ©e General Saint-sernin;3 Place St Sernin;31070;Toulouse;43.608488;1.440625 +0670081Z;LycĂ©e General Sections Internationales;1 Rue Des Pontonniers;67081;Strasbourg;48.5840973;7.7564613 +0380028Z;LycĂ©e General Stendhal;1 Bis Place Jean Achard;38816;Grenoble;45.189156;5.729899 +0470009E;LycĂ©e General Stendhal;Allees Charles De Gaulle;47190;Aiguillon;44.300538;0.3372336 +0400933X;LycĂ©e General Sud Des Landes;11 Voie Romaine;40231;St Vincent De Tyrosse;43.6667923;-1.2957191 +0332831Y;LycĂ©e General Sud Medoc La Boetie;2 Rue Du LycĂ©e;33320;Le Taillan Medoc;0.0;0.0 +0220099N;LycĂ©e General Technologi PrivĂ© St Joseph;Rue Mouexigne;22404;Lamballe;48.4631626;-2.5132448 +0030036Y;LycĂ©e General Theodore De Banville;12 Cours Vincent D'indy;03017;Moulins;46.5683421;3.3269656 +0650025Z;LycĂ©e General Theophile Gautier;15 Rue Abbe Torne;65016;Tarbes;43.2337724;0.0718967 +0080053P;LycĂ©e General Thomas Masaryk;35 Rue Bournizet;08400;Vouziers;49.3974951;4.6979019 +0730013T;LycĂ©e General Vaugelas;;73006;Chambery;46.874236;1.586684 +0750662M;LycĂ©e General Victor Duruy;33 Boulevard Des Invalides;75007;Paris 07;48.852408;2.3147672 +0142059M;LycĂ©e General Victor Hugo;16 Rue Defense Passive;14070;Caen;49.1989716;-0.3668609 +0750648X;LycĂ©e General Victor Hugo;27 Rue De Sevigne;75003;Paris 03;48.8572227;2.3629231 +0590143K;LycĂ©e General Yves Kernanec;91 Avenue Du Docteur Calmette;59700;Marcq En Baroeul;50.6619541;3.0871451 +0580661D;LycĂ©e Horticole Et Rural PrivĂ© Du Haut Nivernais;Chemin Des Plantes Froides;58210;Varzy;47.358735;3.388036 +0692681P;LycĂ©e Horticole PrivĂ© De Lyon Pressin;81 Chemin De Beaunant;69230;St Genis Laval;45.7087787;4.7789349 +0593040J;LycĂ©e Hotelier Notre-dame De La Providence;Rue Des Glycines;59358;Orchies;50.4806719;3.2420076 +0783441S;LycĂ©e International Montessori;5 Rue De Chaponval;78870;Bailly;48.8359161;2.0710087 +0780015T;LycĂ©e Militaire Militaire;2 Avenue Jean Jaures;78211;St Cyr L Ecole;48.7993005;2.0427068 +0711064W;LycĂ©e Militaire National;3 Rue Gaston Joliet;71404;Autun;46.9497566;4.3057409 +0720896H;LycĂ©e Militaire National Prytanee National Militaire;22 Rue Du College;72208;La Fleche;47.7003277;-0.0743896 +0930863H;LycĂ©e Ministere Justice Maison De La Legion D'honneur;5 Rue De La Legion D'honneur;93206;St Denis;48.9344018;2.3581156 +0290063L;LycĂ©e Naval;Avenue De L'ecole Navale;29240;Brest;48.3741391;-4.5258197 +0150747F;LycĂ©e Polyvalent;Avenue Raymond Cortat;15200;Mauriac;45.2120206;2.3397724 +0772685A;LycĂ©e Polyvalent;19 Rue Du Lion;77260;La Ferte Sous Jouarre;48.9433531;3.1318635 +0810023K;LycĂ©e Polyvalent;41 Rue Victor Hugo;81604;Gaillac;43.9032896;1.9009418 +9720695Y;LycĂ©e Polyvalent Acajou 2;Quartier Acajou;97232;Le Lamentin;0.0;0.0 +0941294W;LycĂ©e Polyvalent Adolphe Cherioux;195 Rue Julian Grimau;94408;Vitry Sur Seine;48.7804644;2.3734255 +0781983G;LycĂ©e Polyvalent Adrienne Bolland;62 64 Bd Devaux;78300;Poissy;48.9327605;2.0499354 +0693734J;LycĂ©e Polyvalent Aiguerande;Chemin Des Sablons;69823;Belleville;46.0990944;4.7468894 +0320023B;LycĂ©e Polyvalent Alain-fournier;Avenue Laplagne;32300;Mirande;43.517913;0.404015 +0831440P;LycĂ©e Polyvalent Albert Camus;560 Rue Henri Giraud;83600;Frejus;43.443681;6.7538626 +0140056K;LycĂ©e Polyvalent Albert Sorel;Avenue Du Labrador;14600;Honfleur;49.398803;0.236287 +0240021T;LycĂ©e Polyvalent Alcide Dusolier;Avenue Jules Ferry;24300;Nontron;45.534132;0.666243 +0040027H;LycĂ©e Polyvalent Alexandra David Neel;17 Avenue General Leclerc;04000;Digne Les Bains;43.0535129;2.2158732 +0330135T;LycĂ©e Polyvalent Alfred Kastler;2 Avenue De L Universite;33402;Talence;44.802672;-0.599039 +0550072E;LycĂ©e Polyvalent Alfred Kastler;1 Rue De Munnerstadt;55700;Stenay;49.487591;5.201371 +0911985N;LycĂ©e Polyvalent Alfred Kastler;Chemin Du Champ De Course;91410;Dourdan;48.5346237;1.9966259 +0932026X;LycĂ©e Polyvalent Alfred Nobel;130 Allee De Gagny;93390;Clichy Sous Bois;48.9014549;2.5470145 +0840021S;LycĂ©e Polyvalent Alphonse Benoit;Cours Victor Hugo;84803;L Isle Sur La Sorgue;43.92001;5.0478131 +0681888H;LycĂ©e Polyvalent Amelie Zurcher;30 Rue Jean Mermoz;68310;Wittelsheim;47.7978887;7.2389802 +0300052U;LycĂ©e Polyvalent Andre Chamson;Boulevard Pasteur;30123;Le Vigan;43.9901973;3.6016316 +0040003G;LycĂ©e Polyvalent Andre Honnorat;1 Rue Honnorat;04400;Barcelonnette;44.3881411;6.6525371 +0595884A;LycĂ©e Polyvalent Andre Lurcat;113 Rue D'haumont;59602;Maubeuge;50.267331;3.944058 +0932123C;LycĂ©e Polyvalent Andre Sabatier;140 Rue De La Republique;93000;Bobigny;48.9072448;2.4295277 +0740051D;LycĂ©e Polyvalent Anna De Noailles;2 Avenue Anna De Noailles;74500;Evian Les Bains;46.3992246;6.5798893 +0210047M;LycĂ©e Polyvalent Anna Judic;3 Rue Du Champ De Foire;21140;Semur En Auxois;47.4953913;4.3360813 +0830042V;LycĂ©e Polyvalent Antoine De Saint Exupery;270 Avenue De Valescure;83700;St Raphael;43.4500551;6.8063128 +0940114N;LycĂ©e Polyvalent Antoine De Saint Exupery;2-4 Rue Henri Matisse;94000;Creteil;48.7825757;2.460862 +0241125T;LycĂ©e Polyvalent Antoine De St Exupery;Les Plantes;24122;Terrasson Lavilledieu;45.131328;1.281654 +0781948U;LycĂ©e Polyvalent Antoine Lavoisier;40 A 60 Bd De La Republique;78440;Porcheville;48.9729155;1.7749472 +0660809W;LycĂ©e Polyvalent Aristide Maillol;73 Avenue Pau Casals;66044;Perpignan;42.7173923;2.884448 +0951787B;LycĂ©e Polyvalent Arthur Rimbaud;99 Av De La Div Leclerc;95140;Garges Les Gonesse;48.9697855;2.3963489 +0930933J;LycĂ©e Polyvalent Assomption;12 Avenue De Verdun;93140;Bondy;48.9059128;2.4892334 +0070004S;LycĂ©e Polyvalent Astier;Quartier Roqua;07205;Aubenas;44.620909;4.389863 +0850016F;LycĂ©e Polyvalent Atlantique;5 Rue Jean Jaures;85402;Lucon;46.4541495;-1.1606375 +0930126G;LycĂ©e Polyvalent Auguste Blanqui;54 Rue Charles Schmidt;93404;St Ouen;48.903085;2.3338269 +0131747D;LycĂ©e Polyvalent Auguste Et Louis Lumiere;Avenue Jules Ferry;13600;La Ciotat;43.1835597;5.6019241 +0340002T;LycĂ©e Polyvalent Auguste Loubatieres;Boulevard Des Hellenes;34304;Agde;43.299603;3.4826301 +0762975W;LycĂ©e Polyvalent Auguste Perret;Place Robert Schuman;76600;Le Havre;49.5138762;0.1616656 +0700009E;LycĂ©e Polyvalent Augustin Cournot;73 Grande Rue;70104;Gray;47.4462233;5.5896275 +9711046K;LycĂ©e Polyvalent Baimbridge 2 (ex Caraibes);Boulevard Des Heros;97001;Les Abymes;16.2498118;-61.5230947 +9741270M;LycĂ©e Polyvalent Bel Air;2 Rue Du LycĂ©e;97441;Ste Suzanne;0.0;0.0 +0310038Y;LycĂ©e Polyvalent Bellevue;135 Route De Narbonne;31031;Toulouse;43.567539;1.4578558 +0450051L;LycĂ©e Polyvalent Benjamin Franklin;21 Bis Rue Eugene Vignat;45010;Orleans;47.9107742;1.9118459 +9730235T;LycĂ©e Polyvalent Bertene Juminer;Route De Saint Maurice;97320;St Laurent Du Maroni;0.0;0.0 +0932048W;LycĂ©e Polyvalent Blaise Cendrars;12 Avenue Leon Jouhaux;93270;Sevran;48.9395961;2.538669 +0360043B;LycĂ©e Polyvalent Blaise Pascal;27 Bd Blaise Pascal;36019;Chateauroux;46.7980558;1.7115349 +0630001J;LycĂ©e Polyvalent Blaise Pascal;23 Rue Blaise Pascal;63600;Ambert;45.5458577;3.7456095 +0680010S;LycĂ©e Polyvalent Blaise Pascal;74 Rue Du Logelbach;68025;Colmar;48.0828085;7.345401 +0760095R;LycĂ©e Polyvalent Blaise Pascal;5 Rue Des Emmurees;76174;Rouen;49.4329729;1.08562 +0772230F;LycĂ©e Polyvalent Blaise Pascal;15 Allee Du Commandant Guesnet;77253;Brie Comte Robert;48.6991093;2.5959103 +0932221J;LycĂ©e Polyvalent Blaise Pascal;18 Rue Marc Vieville;93250;Villemomble;48.8819491;2.5110143 +0771200L;LycĂ©e Polyvalent Blanche De Castille;42 Bis Rue Du Chateau;77300;Fontainebleau;48.4065006;2.703165 +9741087N;LycĂ©e Polyvalent Boisjoly Potier;Rue Ignaz Pleyel;97839;Le Tampon;-21.249148;55.525948 +0070001N;LycĂ©e Polyvalent Boissy D'anglas;50 Avenue Jean Jaures;07104;Annonay;45.2470817;4.67011 +0710001R;LycĂ©e Polyvalent Bonaparte;Place Du Champ De Mars;71403;Autun;46.9501701;4.2987595 +9741051Z;LycĂ©e Polyvalent Bras Panon;51 Chemin Bras Panon;97412;Bras Panon;-21.0023692;55.6829973 +0783533S;LycĂ©e Polyvalent Camille Claudel;21 Rue De La Lyre;78711;Mantes La Ville;48.9641158;1.7162431 +0951710T;LycĂ©e Polyvalent Camille Claudel;Boulevard De L'oise;95490;Vaureal;49.030652;2.0219014 +0590042A;LycĂ©e Polyvalent Camille Desmoulins;47 Rue De La Republique;59360;Le Cateau Cambresis;50.1021507;3.541294 +0950649P;LycĂ©e Polyvalent Camille Pissarro;1 Rue Henri Matisse;95300;Pontoise;49.0560649;2.0891196 +0620056Z;LycĂ©e Polyvalent Carnot;148 Rue Alfred Leroy;62701;Bruay La Buissiere;50.4792475;2.5445456 +9720825P;LycĂ©e Polyvalent Centre Sud;Qua Vaudrancourt;97224;Ducos;0.0;0.0 +0951727L;LycĂ©e Polyvalent Charles Baudelaire;13 Rue Du Grand Tremblay;95470;Fosses;49.101103;2.504328 +9711032V;LycĂ©e Polyvalent Charles Coeffin;Trioncelle;97122;Baie Mahault;0.0;0.0 +0840001V;LycĂ©e Polyvalent Charles De Gaulle (place);104 Place Charles De Gaulle;84405;Apt;47.3408348;-1.5269078 +0380053B;LycĂ©e Polyvalent Charles Gabriel Pravaz;257 Rue Du Pre Saint Martin;38480;Le Pont De Beauvoisin;45.5398188;5.6687632 +0570021N;LycĂ©e Polyvalent Charles Hermite;6 Rue Du Calvaire;57260;Dieuze;48.8088865;6.7245785 +0922277A;LycĂ©e Polyvalent Charles Petiet;65 Bd Gallieni;92391;Villeneuve La Garenne;48.9267832;2.3291268 +0660021P;LycĂ©e Polyvalent Charles Renouvier;Route De Catllar;66500;Prades;42.6265495;2.4199729 +0142120D;LycĂ©e Polyvalent Charles Tellier;Route De Vire;14110;Conde Sur Noireau;48.8514625;-0.5649747 +0890032B;LycĂ©e Polyvalent Chevalier D'eon;2 Place Edmond Jacob;89700;Tonnerre;47.8571627;3.9726577 +0941918Z;LycĂ©e Polyvalent Christophe Colomb;154 Rue De Boissy;94370;Sucy En Brie;48.760231;2.5268783 +0690097F;LycĂ©e Polyvalent Claude Bernard;234 Rue Philippe Heron;69665;Villefranche Sur Saone;45.9892277;4.7122182 +0922427N;LycĂ©e Polyvalent Claude Garamont;69 Rue De L'industrie;92701;Colombes;48.9143256;2.2460678 +0271581X;LycĂ©e Polyvalent Clement Ader;Rue De Rouen;27305;Bernay;49.0936362;0.6014418 +0772342C;LycĂ©e Polyvalent Clement Ader;74-76 Rue Georges Clemenceau;77220;Tournan En Brie;48.7426674;2.7497947 +0910676R;LycĂ©e Polyvalent Clement Ader;37 Bis Rue G.anthonioz De Gaull;91200;Athis Mons;48.7107797;2.3924387 +0050003B;LycĂ©e Polyvalent Climatique D Altitude;2 Rue Marius Chancel;05105;Briancon;44.8987602;6.6403997 +0381630R;LycĂ©e Polyvalent Clinique Du Gresivaudan;10 Avenue Maquis Gresivaudan;38702;La Tronche;45.2048437;5.7474496 +0210006T;LycĂ©e Polyvalent Clos Maire;4 Rue Des Roles;21206;Beaune;47.033372;4.8358441 +0781884Z;LycĂ©e Polyvalent Condorcet;Allee Condorcet;78520;Limay;48.986312;1.751793 +0940122X;LycĂ©e Polyvalent Condorcet;1 Avenue Condorcet;94214;St Maur Des Fosses;48.7954086;2.5120495 +0831563Y;LycĂ©e Polyvalent Costebelle;150 Bd Felix Descroix;83408;Hyeres;43.0980537;6.1232362 +0762920L;LycĂ©e Polyvalent Coubertin;130 Rue Georges Clemenceau;76210;Bolbec;49.5651014;0.4607496 +0750650Z;LycĂ©e Polyvalent D'alembert;22 Sente Des Dorees;75019;Paris 19;48.886887;2.393574 +0932122B;LycĂ©e Polyvalent D'alembert;7 Rue Du Commandant L'herminier;93300;Aubervilliers;48.9173889;2.3867244 +0941474S;LycĂ©e Polyvalent Darius Milhaud;80 Rue Du Professeur Bergonie;94276;Le Kremlin Bicetre;48.8022789;2.3507331 +7200123W;LycĂ©e Polyvalent De Balagne;Av.paul Bisgambiglia;20220;L Ile Rousse;42.63651;8.937046 +9741206T;LycĂ©e Polyvalent De Bois D'olive;;97432;St Pierre;49.1838819;-0.360612 +0400007R;LycĂ©e Polyvalent De Borda;7 Avenue Paul Doumer;40107;Dax;43.7031026;-1.0566084 +9741233X;LycĂ©e Polyvalent De Bras Fusil;Zac De Bras Fusil;97470;St Benoit;0.0;0.0 +9760316P;LycĂ©e Polyvalent De Chirongui;;97620;Chirongui;0.0;0.0 +0150030B;LycĂ©e Polyvalent De Haute Auvergne;20 Rue Marcellin Boudet;15101;St Flour;45.0349214;3.0838309 +0951824S;LycĂ©e Polyvalent De L Hautil;1 Rue Gabriel Faure;95280;Jouy Le Moutier;49.010143;2.025968 +0220013V;LycĂ©e Polyvalent De La Fontaine Des Eaux;La Fontaine Des Eaux;22102;Dinan;48.4614665;-2.0384413 +0772296C;LycĂ©e Polyvalent De La Mare Carree;Rue Du LycĂ©e;77552;Moissy Cramayel;0.0;0.0 +0380049X;LycĂ©e Polyvalent De La Matheysine;3 Rue Lesdiguieres;38350;La Mure;44.9032559;5.785122 +0061987C;LycĂ©e Polyvalent De La Montagne;Quartier Du Clot;06420;Valdeblore;44.0699954;7.1679872 +0950947N;LycĂ©e Polyvalent De La Tourelle;8 Rue Fernand Leger;95200;Sarcelles;48.9928689;2.3828852 +0492224B;LycĂ©e Polyvalent De L'hyrome;71 Rue Nationale;49120;Chemille;47.2166775;-0.7288251 +9760127J;LycĂ©e Polyvalent De Mamoudzou;;97600;Mamoudzou;-12.780556;45.227778 +0641844G;LycĂ©e Polyvalent De Navarre;Rue Jai Alai;64220;St Jean Pied De Port;43.1673642;-1.2319105 +9760229V;LycĂ©e Polyvalent De Petite-terre;;97615;Pamandzi;-12.8004511;45.2829634 +9760182U;LycĂ©e Polyvalent De Sada;;97640;Sada;45.0526421;7.3985249 +9741231V;LycĂ©e Polyvalent De Sainte Anne;;97437;St Benoit;43.6491386;3.8061648 +9741380G;LycĂ©e Polyvalent De St Paul 4;363 Route De Savannah;97411;St Paul;0.0;0.0 +9741186W;LycĂ©e Polyvalent De Trois Bassins;81 Rue Georges Brassens;97426;Les Trois Bassins;-21.1040298;55.3016406 +0130050J;LycĂ©e Polyvalent Denis Diderot;23 Bd Laveran;13388;Marseille 13;43.3251745;5.4146234 +0660004W;LycĂ©e Polyvalent Deodat De Severac;Avenue Des Tilleuls;66403;Ceret;42.4852289;2.7526652 +0890008A;LycĂ©e Polyvalent Des Chaumes;16 Avenue Du Parc Des Chaumes;89206;Avallon;47.4878384;3.9149854 +0740009H;LycĂ©e Polyvalent Des Glieres;2 A Avenue De Verdun;74107;Annemasse;46.1882191;6.2434341 +0210013A;LycĂ©e Polyvalent Desire Nisard;19 Rue De Seine;21402;Chatillon Sur Seine;47.8624852;4.5786136 +0750712S;LycĂ©e Polyvalent Diderot;61 Rue David D'angers;75019;Paris 19;48.8813652;2.3961279 +0750676C;LycĂ©e Polyvalent Dorian;74 Avenue Philippe Auguste;75011;Paris 11;48.8543909;2.3924246 +0390029V;LycĂ©e Polyvalent Du Bois;Rue De Strasbourg;39330;Mouchard;46.974334;5.797075 +7200719U;LycĂ©e Polyvalent Du Fium'orbu;Zi Miggliacciaro;20243;Prunelli Di Fiumorbo;42.010314;9.325429 +0421788R;LycĂ©e Polyvalent Du Forez;10 Route De Civens;42110;Feurs;45.7508946;4.2267226 +0763002A;LycĂ©e Polyvalent Du Golf;Chemin Du Golf;76372;Dieppe;49.918011;1.059239 +0831242Z;LycĂ©e Polyvalent Du Golfe De Saint Tropez;Quartier Saint Martin;83580;Gassin;43.248582;6.569659 +0382863F;LycĂ©e Polyvalent Du Gresivaudan;1 Avenue Du Taillefer;38240;Meylan;45.2098436;5.7821035 +0790031E;LycĂ©e Polyvalent Du Haut Val De Sevre;22 Rue Du Panier Fleuri;79403;St Maixent L Ecole;46.4112715;-0.2201006 +9760270P;LycĂ©e Polyvalent Du Nord;;97630;Acoua;41.8291937;9.4039267 +0781883Y;LycĂ©e Polyvalent Dumont D Urville;2 Avenue De Franche Comte;78310;Maurepas;48.764406;1.9522346 +0762600N;LycĂ©e Polyvalent E. Delamare Deboutteville;5 Rue Andre Bertrand;76440;Forges Les Eaux;49.6182713;1.5372705 +0212045J;LycĂ©e Polyvalent E.j. Marey;5 Rue Du 16e Chasseurs;21206;Beaune;47.0286521;4.8505659 +0251671F;LycĂ©e Polyvalent Edgar Faure;2 Rue Du Dr Sauze;25503;Morteau;47.0622542;6.6074931 +0750671X;LycĂ©e Polyvalent Edgar Quinet;63 Rue Des Martyrs;75009;Paris 09;48.8814303;2.3398192 +0700905D;LycĂ©e Polyvalent Edouard Belin;18 Rue Edouard Belin;70006;Vesoul;47.6337311;6.1648428 +0280021W;LycĂ©e Polyvalent Edouard Branly;29 Avenue Kennedy;28100;Dreux;48.7305183;1.3791302 +0941018W;LycĂ©e Polyvalent Edouard Branly;33 Rue Du Petit Bois;94000;Creteil;48.7840361;2.4667703 +0380091T;LycĂ©e Polyvalent Edouard Herriot;Avenue Edouard Herriot;38506;Voiron;45.3698096;5.5884471 +0380073Y;LycĂ©e Polyvalent Elie Cartan;2 Rue Justin Vernet;38351;La Tour Du Pin;45.5642227;5.4436956 +0750677D;LycĂ©e Polyvalent Elisa Lemonnier;20 Avenue Armand Rousseau;75012;Paris 12;48.8360288;2.4091258 +0711729U;LycĂ©e Polyvalent Emiland Gauthey;20 Rue De L'ancien Collège;71321;Chalon Sur Saone;0.0;0.0 +0170020E;LycĂ©e Polyvalent Emile Combes;Rue Des Cordeliers;17800;Pons;45.578308;-0.550741 +0500002F;LycĂ©e Polyvalent Emile Littre;28 Rue De Verdun;50303;Avranches;48.679946;-1.3603845 +0670089H;LycĂ©e Polyvalent Emile Mathis;1 Rue Du Dauphine;67311;Schiltigheim;48.6337017;7.7455694 +0160022M;LycĂ©e Polyvalent Emile Roux;Avenue Du General De Gaulle;16500;Confolens;46.0082;0.6721341 +0590233H;LycĂ©e Polyvalent Emile Zola;174 Rue De La Baillerie;59391;Wattrelos;50.7073916;3.2319465 +0781819D;LycĂ©e Polyvalent Emilie De Breteuil;3 Rue Du Canal;78053;Montigny Le Bretonneux;48.7819147;2.0421257 +0772688D;LycĂ©e Polyvalent Emilie Du Chatelet;110 Bd Du Champ Du Moulin;77700;Serris;48.8486962;2.7884286 +0430953C;LycĂ©e Polyvalent Emmanuel Chabrier;Le Piny-haut;43201;Yssingeaux;45.128135;4.1201489 +0573491K;LycĂ©e Polyvalent Ernest Cuvelette;Rue De Grenoble;57800;Freyming Merlebach;49.146598;6.787573 +0300027S;LycĂ©e Polyvalent Ernest Hemingway;98 Avenue Jean Jaures;30910;Nimes;43.8260467;4.3533949 +0770940D;LycĂ©e Polyvalent Etienne Bezout;31 Avenue Etienne Dailly;77796;Nemours;48.2606523;2.7152517 +0922443F;LycĂ©e Polyvalent Etienne-jules Marey;154 Rue De Silly;92100;Boulogne Billancourt;48.8320565;2.2340158 +0930119Z;LycĂ©e Polyvalent Eugene Delacroix;4 Rue Du Docteur Schweitzer;93700;Drancy;48.9239128;2.4582228 +0922397F;LycĂ©e Polyvalent Eugene Ionesco;152 Avenue De Verdun;92130;Issy Les Moulineaux;48.8189753;2.2539959 +0951810B;LycĂ©e Polyvalent Eugene Ronceray;5 7 Rue Marcel Langlois;95875;Bezons;48.9218246;2.2168644 +0590168M;LycĂ©e Polyvalent Eugene Thomas;100 Avenue Leo Lagrange;59530;Le Quesnoy;50.2480377;3.6440963 +0932116V;LycĂ©e Polyvalent Eugenie Cotton;58 Avenue Faidherbe;93100;Montreuil;48.866896;2.4372326 +0490018D;LycĂ©e Polyvalent Europe Robert Schuman;39 Avenue De L Europe;49311;Cholet;47.0461537;-0.88536 +0932047V;LycĂ©e Polyvalent Evariste Galois;32 Avenue Montaigne;93165;Noisy Le Grand;48.8444723;2.5393145 +0951748J;LycĂ©e Polyvalent Evariste Galois;14 Bd Leon Blum;95260;Beaumont Sur Oise;49.1454738;2.2950659 +0951811C;LycĂ©e Polyvalent F. Et N. Leger;7 Allee F Et N Leger;95104;Argenteuil;48.947411;2.2489499 +0380092U;LycĂ©e Polyvalent Ferdinand Buisson;21 Boulevard Edgar Kolfer;38506;Voiron;45.3607983;5.5995988 +0760030V;LycĂ©e Polyvalent Ferdinand Buisson;6 Rue Auguste Houzeau;76504;Elbeuf;49.2867649;1.0043623 +0941972H;LycĂ©e Polyvalent Fernand Leger;15 Avenue Henri Barbusse;94200;Ivry Sur Seine;48.8092163;2.3777145 +0910621F;LycĂ©e Polyvalent Francisque Sarcey;Chemin Du Champ De Courses;91410;Dourdan;48.5346237;1.9966259 +0941952L;LycĂ©e Polyvalent Francois Arago;36 Avenue De L'europe;94190;Villeneuve St Georges;48.7311569;2.4586032 +0693566B;LycĂ©e Polyvalent Francois Mansart;Rue Jacquard;69240;Thizy;46.0249019;4.3125338 +0142132S;LycĂ©e Polyvalent Francois Rabelais;Esplanade Rabelais;14200;Herouville St Clair;49.2082446;-0.290863 +0370009J;LycĂ©e Polyvalent Francois Rabelais;28 Quai Danton;37501;Chinon;47.16364;0.2344406 +0693504J;LycĂ©e Polyvalent Francois Rabelais;Chemin Du Dodin;69571;Dardilly;45.8161875;4.7629028 +0850068M;LycĂ©e Polyvalent Francois Rabelais;45 Rue Rabelais;85205;Fontenay Le Comte;46.4679784;-0.8171308 +0752701D;LycĂ©e Polyvalent Francois Truffaut;28 Rue Debelleyme;75003;Paris 03;48.861928;2.3634247 +0911937L;LycĂ©e Polyvalent Francois Truffaut;Rue Georges Pompidou;91072;Bondoufle;48.6186709;2.3897445 +0762976X;LycĂ©e Polyvalent Francoise De Grace;16 Rue De La Vallee;76071;Le Havre;49.4930608;0.1501086 +0771027Y;LycĂ©e Polyvalent Frederic Joliot Curie;168 Rue F Joliot Curie;77196;Dammarie Les Lys;48.518903;2.6489522 +0941301D;LycĂ©e Polyvalent Frederic Mistral;7-9 Rue Frederic Mistral;94260;Fresnes;48.7609403;2.3299967 +0611182D;LycĂ©e Polyvalent Gabriel;7 Rue Saint Exupery;61204;Argentan;48.7533879;-0.0075234 +0070029U;LycĂ©e Polyvalent Gabriel Faure;Place Mallarme;07301;Tournon Sur Rhone;45.0672005;4.8344977 +0762911B;LycĂ©e Polyvalent Galilee;461 Route De Belbeuf;76520;Franqueville St Pierre;49.3983018;1.1607919 +0400002K;LycĂ©e Polyvalent Gaston Crampe;Av Droits De L Homme Et Citoyen;40801;Aire Sur L Adour;43.700062;-0.262294 +0301654K;LycĂ©e Polyvalent Genevieve De Gaulle-anthonioz;43 Rue Du Moulin;30540;Milhaud;43.7864243;4.309575 +0031044U;LycĂ©e Polyvalent Genevieve Vincent;15 Bd Du General De Gaulle;03600;Commentry;46.2911311;2.7336414 +0910622G;LycĂ©e Polyvalent Geoffroy-st-hilaire;2-6 Av Geoffroy St Hilaire;91153;Etampes;48.4351205;2.1463921 +0360019A;LycĂ©e Polyvalent George Sand;25 Avenue George Sand;36400;La Chatre;46.5824064;1.9815737 +0951788C;LycĂ©e Polyvalent George Sand;Avenue Du LycĂ©e;95331;Domont;0.0;0.0 +0763237F;LycĂ©e Polyvalent Georges Baptiste;41 Rte De Duclair;76380;Canteleu;49.4431486;1.0186583 +0950666H;LycĂ©e Polyvalent Georges Braque;21 Rue Victor Puiseux;95100;Argenteuil;48.950485;2.2505933 +0420027B;LycĂ©e Polyvalent Georges Brassens;8 Rue Grange Burlat;42800;Rive De Gier;45.5325721;4.60488 +0762601P;LycĂ©e Polyvalent Georges Brassens;27 Boulevard Gustave Eiffel;76270;Neufchatel En Bray;49.7379225;1.4389657 +0911828T;LycĂ©e Polyvalent Georges Brassens;8 R Georges Brassens;91080;Courcouronnes;48.626643;2.4205622 +0932260B;LycĂ©e Polyvalent Georges Brassens;Rue Des Bancs Publics;93420;Villepinte;48.9554743;2.5328795 +0940743X;LycĂ©e Polyvalent Georges Brassens;12 Avenue Le Foll;94290;Villeneuve Le Roi;48.7301773;2.4276725 +9740053P;LycĂ©e Polyvalent Georges Brassens;16 Avenue Georges Brassens;97493;St Denis;44.9366126;-0.4864077 +0772339Z;LycĂ©e Polyvalent Georges Clemenceau;5 Rue Georges Clemenceau;77430;Champagne Sur Seine;48.3990891;2.7972818 +6200043C;LycĂ©e Polyvalent Georges Clemenceau;Bd Jean Nicolai;20100;Sartene;41.621822;8.97472 +0700018P;LycĂ©e Polyvalent Georges Colomb;1 Rue G Colomb;70204;Lure;47.6879696;6.4987354 +0672614C;LycĂ©e Polyvalent Georges Imbert;2 Rue Vincent D'indy;67261;Sarre Union;48.9359751;7.0886788 +0341921D;LycĂ©e Polyvalent Georges Pompidou;351 Av Mal De Lattre Tassigny;34172;Castelnau Le Lez;43.6469391;3.9199801 +0771940R;LycĂ©e Polyvalent Gerard De Nerval;89 Cours Des Roches Noisiel;77441;Champs Sur Marne;48.8427362;2.6154529 +9711012Y;LycĂ©e Polyvalent Grand Bourg;Rue De La Savane;97112;Grand Bourg;0.0;0.0 +0940742W;LycĂ©e Polyvalent Guillaume Bude;2 Voie Georges Pompidou;94456;Limeil Brevannes;48.7441012;2.5110985 +0740013M;LycĂ©e Polyvalent Guillaume Fichet;219 Rue De Pressy;74136;Bonneville;46.0793702;6.411561 +0754476H;LycĂ©e Polyvalent Guillaume Tirel;237 Boulevard Raspail;75014;Paris 14;48.8387393;2.3308641 +0330028B;LycĂ©e Polyvalent Gustave Eiffel;143 Crs Marne;33074;Bordeaux;44.837789;-0.57918 +0922398G;LycĂ©e Polyvalent Gustave Eiffel;78 Avenue Du Pdt Pompidou;92500;Rueil Malmaison;48.8715605;2.1940934 +0940111K;LycĂ©e Polyvalent Gustave Eiffel;61 Avenue Du President Wilson;94235;Cachan;48.789111;2.325304 +0620256S;LycĂ©e Polyvalent Guy Mollet;57 Rue Bocquet Flochel;62022;Arras;50.2800007;2.7946232 +0380014J;LycĂ©e Polyvalent Hector Berlioz;Place De L Europe;38260;La Cote St Andre;45.2002269;6.6710747 +0772277G;LycĂ©e Polyvalent Henri Becquerel;1 Bd Henri Rousselle;77370;Nangis;48.5518365;3.0076186 +0261397C;LycĂ©e Polyvalent Henri Laurens;Quartier Des Rioux;26241;St Vallier;0.0;0.0 +0912251C;LycĂ©e Polyvalent Henri PoincarĂ©;36 Rue Leon Bourgeois;91122;Palaiseau;48.702946;2.2370181 +0932120Z;LycĂ©e Polyvalent Henri Sellier;73 Avenue Du Colonel Fabien;93190;Livry Gargan;48.9186434;2.5162221 +0710042K;LycĂ©e Polyvalent Henri Vincenot;Montee Saint Claude;71500;Louhans;46.623382;5.214177 +0550008K;LycĂ©e Polyvalent Henri Vogt;12 Rue Andre Malraux;55205;Commercy;48.7548233;5.5858673 +0711422K;LycĂ©e Polyvalent Hilaire De Chardonnet;1 Rue Henri Dunant;71321;Chalon Sur Saone;46.8024283;4.8663853 +0210018F;LycĂ©e Polyvalent Hippolyte Fontaine;20 Boulevard Voltaire;21033;Dijon;47.3181031;5.0511309 +0771996B;LycĂ©e Polyvalent Honore De Balzac;Avenue Paul Langevin;77290;Mitry Mory;48.9724391;2.6505336 +0022044L;LycĂ©e Polyvalent Hotelier;Passage Le Corbusier;02200;Soissons;49.376636;3.32342 +0622807P;LycĂ©e Polyvalent Hotelier;Avenue Du Chateau;62520;Le Touquet Paris Plage;50.516361;1.602514 +9711066G;LycĂ©e Polyvalent Hotelier Du Gosier;Saint-felix;97190;Le Gosier;0.0;0.0 +0881664F;LycĂ©e Polyvalent Hotelier J-b Simeon Chardin;32 Boulevard D'alsace;88407;Gerardmer;48.0732813;6.8764418 +9720823M;LycĂ©e Polyvalent Hotelier Nord Caraibes;Bellefontaine;97222;Bellefontaine;46.558079;6.068529 +0641823J;LycĂ©e Polyvalent Hotelier Tourisme;2 Rue F Jammes;64204;Biarritz;43.4623738;-1.5522272 +0390033Z;LycĂ©e Polyvalent Hyacinthe Friant;2 Rue Hyacinthe Friant;39801;Poligny;0.0;0.0 +9710981P;LycĂ©e Polyvalent Iles Du Nord;Marigot;97052;St Martin;18.0664178;-63.0872351 +9830003L;LycĂ©e Polyvalent J.garnier;65 Avenue James Cook;98849;Noumea;-22.2686289;166.4242812 +0350048M;LycĂ©e Polyvalent Jacques Cartier;31 Rue De La Balue;35403;St Malo;48.6280158;-2.0040075 +0390013C;LycĂ©e Polyvalent Jacques Duhamel;Rue Charles Laurent Thouverey;39107;Dole;47.09534;5.49081 +0750428H;LycĂ©e Polyvalent Jacques Monod;12 Rue Victor Cousin;75005;Paris 05;48.8476683;2.3423041 +0301778V;LycĂ©e Polyvalent Jacques Prevert;;30380;St Christol Les Ales;44.083566;4.077927 +0110019L;LycĂ©e Polyvalent Jacques Ruffie;Esplanade Francois Mitterand;11303;Limoux;43.0506812;2.2194271 +0542293Z;LycĂ©e Polyvalent Jacques-marie Boutet De Monvel;4 Rue Boutet De Monvel;54300;Luneville;48.5986914;6.4925295 +0022042J;LycĂ©e Polyvalent Jean Bouin;Rue Gaston Bachelard;02100;St Quentin;49.854359;3.30866 +0750708M;LycĂ©e Polyvalent Jean Drouant;20 Rue Mederic;75017;Paris 17;48.880936;2.3040609 +0110012D;LycĂ©e Polyvalent Jean Durand;Avenue Docteur Laennec;11493;Castelnaudary;43.3068767;1.9402732 +0230051F;LycĂ©e Polyvalent Jean Favard;27 Rte De Courtille;23003;Gueret;46.168088;1.8569052 +0840015K;LycĂ©e Polyvalent Jean Henri Fabre;Av Du Mont Ventoux;84208;Carpentras;44.0569097;5.0619963 +9740979W;LycĂ©e Polyvalent Jean Hinglo;2 Rue Des Sans Soucis;97825;Le Port;43.0154964;3.0609934 +0170135E;LycĂ©e Polyvalent Jean Hyppolite;Place Saint Exupery;17502;Jonzac;45.4414119;-0.4322947 +0680001G;LycĂ©e Polyvalent Jean Jacques Henner;20 Rue De Hirtzbach;68130;Altkirch;47.6153682;7.234954 +0120025M;LycĂ©e Polyvalent Jean Jaures;Avenue Jean Jaures;12401;St Affrique;43.9510528;2.8953583 +0342066L;LycĂ©e Polyvalent Jean Jaures;Avenue Saint Sauveur;34980;St Clement De Riviere;43.7098148;3.8323688 +0921166T;LycĂ©e Polyvalent Jean Jaures;280 Avenue Jean Jaures;92291;Chatenay Malabry;48.7677839;2.254895 +0930121B;LycĂ©e Polyvalent Jean Jaures;1 Rue Dombasle;93105;Montreuil;48.8636396;2.4491594 +0941974K;LycĂ©e Polyvalent Jean Jaures;9 Avenue Jean Jaures;94220;Charenton Le Pont;48.8247353;2.4126023 +0950641F;LycĂ©e Polyvalent Jean Jaures;25 Rue Charles Lecoq;95100;Argenteuil;48.9403527;2.225817 +0660011D;LycĂ©e Polyvalent Jean Lurcat;25 Avenue Albert Camus;66000;Perpignan;42.6918634;2.9063835 +0753268V;LycĂ©e Polyvalent Jean Lurcat;48 Avenue Des Gobelins;75013;Paris 13;48.8342909;2.3533682 +0561698S;LycĂ©e Polyvalent Jean Mace;Rue Jean-paul Sartre;56601;Lanester;47.765248;-3.344219 +0340042L;LycĂ©e Polyvalent Jean Mermoz;717 Avenue Jean Mermoz;34060;Montpellier;43.6109837;3.8901026 +0680066C;LycĂ©e Polyvalent Jean Mermoz;53 Rue Du Docteur Hurst;68300;St Louis;47.5793744;7.558521 +0133288D;LycĂ©e Polyvalent Jean Monnet;Bd Rhin Et Danube;13127;Vitrolles;43.424732;5.2717125 +0160020K;LycĂ©e Polyvalent Jean Monnet;66 Boulevard De Chatenay;16100;Cognac;45.701557;-0.3180834 +0611148S;LycĂ©e Polyvalent Jean Monnet;2 Rue Jean Monnet;61400;Mortagne Au Perche;48.5124382;0.5531263 +0781839A;LycĂ©e Polyvalent Jean Monnet;Place De L Europe;78940;La Queue Les Yvelines;48.8022064;1.7827586 +0851400K;LycĂ©e Polyvalent Jean Monnet;57 Rue De La Demoiselle;85500;Les Herbiers;46.8663273;-1.0202618 +0951722F;LycĂ©e Polyvalent Jean Monnet;Rue Jean Monnet;95131;Franconville;48.9919298;2.2151614 +0271585B;LycĂ©e Polyvalent Jean Moulin;Rue Du Marechal Foch;27700;Les Andelys;49.2492308;1.4406574 +0492123S;LycĂ©e Polyvalent Jean Moulin;1 Place Jean Moulin;49017;Angers;47.491678;-0.551624 +0932118X;LycĂ©e Polyvalent Jean Moulin;2 Avenue Charles De Gaulle;93150;Le Blanc Mesnil;48.9433914;2.4685539 +0130053M;LycĂ©e Polyvalent Jean Perrin;74 Rue Verdillon;13395;Marseille 10;43.2746696;5.4264973 +0951104J;LycĂ©e Polyvalent Jean Perrin;2 Rue Des Egalisses;95310;St Ouen L Aumone;49.0572645;2.1326835 +0910975R;LycĂ©e Polyvalent Jean Pierre Timbaud;4 Rue Henri Douard;91220;Bretigny Sur Orge;48.6109653;2.3129067 +0330109P;LycĂ©e Polyvalent Jean Renou;2 Rue Jean Renou;33192;La Reole;44.5815968;-0.0413168 +0670084C;LycĂ©e Polyvalent Jean Rostand;18 Boulevard De La Victoire;67084;Strasbourg;48.5829461;7.7626845 +0782540M;LycĂ©e Polyvalent Jean Rostand;66 Rue Fernand Bodet;78200;Mantes La Jolie;48.9979639;1.696681 +0931584S;LycĂ©e Polyvalent Jean Rostand;8 Rue Pierre Audat;93420;Villepinte;48.959629;2.545226 +0511951U;LycĂ©e Polyvalent Jean Talon;105 Avenue Daniel Simonnot;51037;Chalons En Champagne;48.9551578;4.3400748 +0450050K;LycĂ©e Polyvalent Jean Zay;2 Rue Ferdinand Buisson;45044;Orleans;47.9067083;1.9127555 +0930833A;LycĂ©e Polyvalent Jean Zay;Avenue Du Marechal Juin;93604;Aulnay Sous Bois;48.93782;2.5057694 +0550025D;LycĂ©e Polyvalent Jean-auguste Margueritte;13 Place Commandant Galland;55107;Verdun;49.1563087;5.3904397 +0271634E;LycĂ©e Polyvalent Jean-baptiste Decretot;7 Rue De La Gare;27400;Louviers;49.2162353;1.1756759 +0782557F;LycĂ©e Polyvalent Jean-baptiste Poquelin;72 Rue Leon Desoyer;78101;St Germain En Laye;48.8995389;2.0856482 +0671832C;LycĂ©e Polyvalent Jean-baptiste Schwilgue;1 Rue Du Stade;67604;Selestat;48.2504987;7.3971004 +0880036L;LycĂ©e Polyvalent Jean-baptiste Vuillaume;Avenue Graillet;88503;Mirecourt;48.298504;6.132288 +0341794R;LycĂ©e Polyvalent Jean-francois Champollion;Avenue De Figuieres;34973;Lattes;43.5876661;3.9306222 +0942125Z;LycĂ©e Polyvalent Jean-jacques Rousseau;15 Rue Lebrun;94400;Vitry Sur Seine;48.7828517;2.3819482 +0950650R;LycĂ©e Polyvalent Jean-jacques Rousseau;2 Rue J J Rousseau;95200;Sarcelles;48.9938677;2.3821641 +0280044W;LycĂ©e Polyvalent Jehan De Beauce;20 Rue Du Commandant Chesne;28000;Chartres;48.4586432;1.4735645 +0772276F;LycĂ©e Polyvalent Jehan De Chelles;47 Rue Des Cites;77649;Chelles;48.888756;2.6115193 +0420008F;LycĂ©e Polyvalent Jeremie De La Rue;Route De Saint Bonnet;42190;Charlieu;46.167128;4.1718563 +0590018Z;LycĂ©e Polyvalent Jesse De Forest;15 Avenue Du Pont Rouge;59363;Avesnes Sur Helpe;50.121543;3.9255446 +0941930M;LycĂ©e Polyvalent Johannes Gutenberg;16-18 Rue De Saussure;94000;Creteil;48.7697246;2.4709831 +0890005X;LycĂ©e Polyvalent Joseph Fourier;Rue R Poincare;89010;Auxerre;47.79215;3.552682 +0312759F;LycĂ©e Polyvalent Joseph Gallieni;79 Route D'espagne;31100;Toulouse;43.573033;1.4243835 +0590044C;LycĂ©e Polyvalent Joseph Marie Jacquard;4 Avenue Jean Moulin;59544;Caudry;50.1263865;3.4040061 +9720726G;LycĂ©e Polyvalent Joseph Pernock;Le Lorrain;97214;Le Lorrain;0.0;0.0 +0681839E;LycĂ©e Polyvalent Joseph Storck (hotelier);Rue Jules Ferry;68504;Guebwiller;47.901203;7.228875 +0340028W;LycĂ©e Polyvalent Joseph Vallot;5 Boulevard Gambetta;34702;Lodeve;43.7313632;3.3171287 +9720725F;LycĂ©e Polyvalent Joseph Zobel;Quartier Thoraille;97215;Riviere Salee;0.0;0.0 +0142131R;LycĂ©e Polyvalent Jules Dumont D'urville;73 Rue De Lebisey;14070;Caen;49.1965539;-0.3548514 +0782565P;LycĂ©e Polyvalent Jules Ferry;29 Rue Du Marechal Joffre;78000;Versailles;48.7942841;2.1208585 +0340040J;LycĂ©e Polyvalent Jules Guesde;110 Avenue De Lodeve;34060;Montpellier;43.6106065;3.8486836 +0250011B;LycĂ©e Polyvalent Jules Haag;1 Rue Labbe;25041;Besancon;47.239422;6.013522 +0951756T;LycĂ©e Polyvalent Jules Verne;1 Rue Michel Strogoff;95800;Cergy;49.0528165;2.009558 +0710018J;LycĂ©e Polyvalent Julien Wittmer;13 Rue De Champagny;71120;Charolles;46.4353344;4.2731882 +0911492C;LycĂ©e Polyvalent L Essouriau;Avenue De Dordogne;91940;Les Ulis;48.6767778;2.1657625 +0710026T;LycĂ©e Polyvalent L?on Blum;72 Rue Jean Jaures;71200;Le Creusot;46.8049544;4.4140633 +0430003V;LycĂ©e Polyvalent La Fayette;Plateau St Laurent;43103;Brioude;45.295564;3.386482 +9720771F;LycĂ©e Polyvalent La Jetee;Route De La Jetee;97240;Le Francois;46.21037;6.15433 +0382838D;LycĂ©e Polyvalent La Pleiade;Rue Du Repos;38232;Pont De Cheruy;45.7529419;5.1711019 +9741173G;LycĂ©e Polyvalent La Possession;;97419;La Possession;44.6915692;-0.8600616 +0380063M;LycĂ©e Polyvalent La Saulaie;La Saulaie;38162;St Marcellin;45.1599563;5.3196875 +0772295B;LycĂ©e Polyvalent La Tour Des Dames;Rue De Vilpres;77540;Rozay En Brie;48.6791913;2.9646581 +0940113M;LycĂ©e Polyvalent Langevin-wallon;126 Avenue Roger Salengro;94507;Champigny Sur Marne;48.8171426;2.4974266 +0772340A;LycĂ©e Polyvalent L'arche Guedon;2 Passage De L'arche Guedon;77200;Torcy;48.8499971;2.6359863 +0681768C;LycĂ©e Polyvalent Lavoisier;42 Rue Lavoisier;68200;Mulhouse;47.7536602;7.3250939 +0620008X;LycĂ©e Polyvalent Lazare Carnot;21 Boulevard Carnot;62022;Arras;50.2875817;2.77396 +0210019G;LycĂ©e Polyvalent Le Castel;22 Rue Daubenton;21033;Dijon;47.3131033;5.0362136 +0071397F;LycĂ©e Polyvalent Le Cheylard;Quartier Plaisance;07160;Le Cheylard;44.9113929;4.42675 +0762964J;LycĂ©e Polyvalent Le Corbusier;Rue De L'universite;76800;St Etienne Du Rouvray;49.3870486;1.0650878 +0721094Y;LycĂ©e Polyvalent Le Mans Sud;128 Rue Henri Champion;72058;Le Mans;47.9831673;0.2336784 +0831646N;LycĂ©e Polyvalent Le Muy;Avenue De Vaugrenier;83490;Le Muy;43.4761296;6.5373255 +0932282A;LycĂ©e Polyvalent Leo Lagrange;2 Rue Compagnon;93140;Bondy;48.9064253;2.4914471 +0941413A;LycĂ©e Polyvalent Leon Blum;5 Rue Jean Gabin;94000;Creteil;48.779746;2.4450351 +0061478Z;LycĂ©e Polyvalent Leonard De Vinci;214 Rue Jean Joannon;06633;Antibes;43.6049397;7.0729317 +0624141P;LycĂ©e Polyvalent Leonard De Vinci;Rue Pasteur Martin Luther King;62228;Calais;50.9537082;1.8876944 +0754475G;LycĂ©e Polyvalent Leonard De Vinci;20 Rue Bourseul;75015;Paris 15;48.8382608;2.3020551 +0782556E;LycĂ©e Polyvalent Leonard De Vinci;2 Bd Hector Berlioz;78100;St Germain En Laye;48.8914922;2.0638407 +0851390Z;LycĂ©e Polyvalent Leonard De Vinci;Rue Du Fromenteau;85603;Montaigu;46.971961;-1.303299 +0911946W;LycĂ©e Polyvalent Leonard De Vinci;1 Place Leonard De Vinci;91240;St Michel Sur Orge;48.6177611;2.3315865 +0921230M;LycĂ©e Polyvalent Leonard De Vinci;4 Av Georges Pompidou;92304;Levallois Perret;48.8973095;2.2778034 +0781951X;LycĂ©e Polyvalent Leopold Sedar Senghor;Place Pierre Beregovoy;78200;Magnanville;48.96488;1.683672 +9740045F;LycĂ©e Polyvalent Les Avirons;;97425;Les Avirons;-21.240637;55.334205 +0441552Y;LycĂ©e Polyvalent Les Bourdonnieres;Rue De La Perriere;44265;Nantes;47.186446;-1.517926 +0270029K;LycĂ©e Polyvalent Les Fontenelles;Chemin Les Fontenelles;27406;Louviers;49.209294;1.156647 +0332832Z;LycĂ©e Polyvalent Les Iris;13 Rue Saint Cricq;33305;Lormont;44.8700168;-0.5322274 +0040533H;LycĂ©e Polyvalent Les Iscles;116 Bd Regis Ryckebush;04100;Manosque;43.831469;5.7990491 +0211986V;LycĂ©e Polyvalent Les Marcs D'or;24 R Du Fort De La Motte Giron;21006;Dijon;47.3158772;4.9969497 +0781860Y;LycĂ©e Polyvalent Les Pierres Vives;1 Rue Des Alouettes;78420;Carrieres Sur Seine;48.913654;2.1725297 +0932267J;LycĂ©e Polyvalent Liberte;27 A 39 Rue De La Liberte;93230;Romainville;48.8829412;2.4294153 +9740054R;LycĂ©e Polyvalent Lislet Geoffroy;Cite Scolaire Du Butor;97491;St Denis;48.936181;2.357443 +0380008C;LycĂ©e Polyvalent L'oiselet;Rue Marion Prolongee;38317;Bourgoin Jallieu;45.597108;5.27212 +0701035V;LycĂ©e Polyvalent Louis Aragon;Rue Pierre Mendes France;70400;Hericourt;47.574053;6.761598 +0691644M;LycĂ©e Polyvalent Louis Armand;Avenue Du Beaujolais;69651;Villefranche Sur Saone;45.9990616;4.7063263 +0751708Z;LycĂ©e Polyvalent Louis Armand;319 321 Rue Lecourbe;75015;Paris 15;48.837404;2.2850709 +0951974E;LycĂ©e Polyvalent Louis Armand;32 Rue Stephane Proust;95600;Eaubonne;48.9970012;2.288332 +0170051N;LycĂ©e Polyvalent Louis Audouin Dubreuil;Rue Philippe Jeannet;17415;St Jean D Angely;45.951385;-0.526721 +0782549X;LycĂ©e Polyvalent Louis Bascan;5 Av Du Gal Leclerc;78513;Rambouillet;48.6410608;1.8245937 +0891199V;LycĂ©e Polyvalent Louis Davier;Avenue Moliere;89306;Joigny;47.982086;3.414577 +0573227Y;LycĂ©e Polyvalent Louis De Cormontaigne;12 Place Cormontaigne;57010;Metz;49.1243217;6.1633963 +0340030Y;LycĂ©e Polyvalent Louis Feuillade;49 Rue Romain Rolland;34402;Lunel;43.6697437;4.1223872 +0951763A;LycĂ©e Polyvalent Louis Jouvet;26 Rue De St Prix;95152;Taverny;49.0058062;2.2732907 +0740006E;LycĂ©e Polyvalent Louis Lachenal-argonay;335 Route Champ Farcon;74372;Argonay;45.938068;6.132849 +0140052F;LycĂ©e Polyvalent Louis Liard;15 Rue Saint Jean;14700;Falaise;48.8949844;-0.1949088 +0672615D;LycĂ©e Polyvalent Louis Marchal;2 Route De La Hardt;67125;Molsheim;48.5369628;7.5012312 +0922464D;LycĂ©e Polyvalent Louise Michel;11 Boulevard Du Midi;92000;Nanterre;48.8897402;2.193836 +0680068E;LycĂ©e Polyvalent Louise Weiss;Route Du Stade;68160;Ste Marie Aux Mines;48.236375;7.1681767 +0781950W;LycĂ©e Polyvalent Louise Weiss;201 Avenue Du Gal De Gaulle;78260;Acheres;48.9707469;2.0741265 +0750463W;LycĂ©e Polyvalent Lucas De Nehou;4 Rue Des Feuillantines;75005;Paris 05;48.8412988;2.3435246 +0932117W;LycĂ©e Polyvalent Lucie Aubrac;51 Rue Victor Hugo;93500;Pantin;48.8937132;2.4096485 +0701078S;LycĂ©e Polyvalent Lumiere;33 Ter Rue Grammont;70306;Luxeuil Les Bains;47.8248597;6.377976 +9840023C;LycĂ©e Polyvalent LycĂ©e Polyvalent De Taaone;Pirae;98716;Pirae;-17.5353835;-149.5478833 +9840407V;LycĂ©e Polyvalent LycĂ©e Tertiaire De Pirae;Pirae;98716;Pirae;-17.5353835;-149.5478833 +0570094T;LycĂ©e Polyvalent Mangin;34 Rue Gambetta;57401;Sarrebourg;48.7391327;7.0583227 +0342091N;LycĂ©e Polyvalent Marc Bloch;;34500;Beziers;43.344233;3.215795 +0070003R;LycĂ©e Polyvalent Marcel Gimond;Boulevard De L'europe;07205;Aubenas;44.619386;4.3808809 +0672806L;LycĂ©e Polyvalent Marcel Rudloff;Avenue Francois Mitterrand;67200;Strasbourg;48.5862403;7.6958226 +0320015T;LycĂ©e Polyvalent Marechal Lannes;Cite Scolaire 1 Pl Brossolette;32700;Lectoure;43.933604;0.623621 +0623902E;LycĂ©e Polyvalent Marguerite Yourcenar;68 Rue Antoine De Saint Exupery;62660;Beuvry;50.51858;2.6641857 +0672677W;LycĂ©e Polyvalent Marguerite Yourcenar;Rue Victor Schoelcher;67152;Erstein;48.4227904;7.6499811 +0911945V;LycĂ©e Polyvalent Marguerite Yourcenar;62 Rue Des Edouets;91423;Morangis;48.7081491;2.3259431 +0342225J;LycĂ©e Polyvalent Marianne;;34000;Montpellier;43.6047275;3.9011747 +0911962N;LycĂ©e Polyvalent Marie Laurencin;51 Rue Paul Cezanne;91542;Mennecy;48.5616978;2.4440346 +0312746S;LycĂ©e Polyvalent Marie Louise Dissard Francoise;5 Boulevard Alain Savary;31170;Tournefeuille;43.5423773;1.3405506 +0730003G;LycĂ©e Polyvalent Marlioz;Chemin Du LycĂ©e;73102;Aix Les Bains;0.0;0.0 +0754530S;LycĂ©e Polyvalent Martin Nadaud;23 Rue De La Bidassoa;75020;Paris 20;48.8662003;2.393588 +0681882B;LycĂ©e Polyvalent Martin Schongauer;25 Rue Voltaire;68000;Colmar;48.0696475;7.3511399 +0941951K;LycĂ©e Polyvalent Marx Dormoy;500 Rue Du Professeur Milliez;94507;Champigny Sur Marne;48.8154449;2.5349215 +0875023M;LycĂ©e Polyvalent Maryse Bastie;Rue Louis Amstrong;87000;Limoges;45.8162774;1.2623048 +0710010A;LycĂ©e Polyvalent Mathias;3 Place Mathias;71321;Chalon Sur Saone;46.7865371;4.8603337 +0580761M;LycĂ©e Polyvalent Maurice Genevoix;51 Route D'avril Sur Loire;58300;Decize;46.8284621;3.441725 +0831559U;LycĂ©e Polyvalent Maurice Janetti;Quartier Mirade;83470;St Maximin La Ste Baume;43.452497;5.864004 +0932030B;LycĂ©e Polyvalent Maurice Utrillo;152 Rue Jean Durand;93240;Stains;48.9524279;2.3691348 +0750502N;LycĂ©e Polyvalent Maximilien Vox-art-dessin;5 Rue Madame;75006;Paris 06;48.8520258;2.3319993 +0133406G;LycĂ©e Polyvalent Mediterranee (de La );Qua La Plaine Av Mediterranee;13600;La Ciotat;43.190758;5.611854 +9730309Y;LycĂ©e Polyvalent Melkior Garre;Route De Montabo;97305;Cayenne;4.9347066;-52.2954502 +0610006A;LycĂ©e Polyvalent Mezeray;6 Place Robert Dugue;61200;Argentan;48.7448047;-0.0151903 +0760032X;LycĂ©e Polyvalent Michel Anguier;41 Bis Rue De La Republique;76260;Eu;50.0482297;1.4220286 +0740027C;LycĂ©e Polyvalent Mont Blanc Rene Dayve;Rue Rene Dayve;74190;Passy;45.907806;6.708722 +0922249V;LycĂ©e Polyvalent Montesquieu;21 Rue Du Capitaine Facq;92350;Le Plessis Robinson;48.7776941;2.2637716 +0920136Y;LycĂ©e Polyvalent Newton-enrea;1 Place Jules Verne;92110;Clichy;48.9087541;2.3073035 +0442094M;LycĂ©e Polyvalent Nicolas Appert;24 Avenue De La Choliere;44702;Orvault;47.251063;-1.6067232 +9720692V;LycĂ©e Polyvalent Nord Atlantique;Boulevard De La Voie Lactee;97230;Ste Marie;14.7787858;-60.9900416 +9711082Z;LycĂ©e Polyvalent Nord Grande Terre;Port Louis;97117;Port Louis;47.7107928;-3.3642883 +0142133T;LycĂ©e Polyvalent P. S. De Laplace;130 Rue De La Delivrande;14075;Caen;49.1961605;-0.3600384 +0910727W;LycĂ©e Polyvalent Parc De Vilgenis;80 Rue De Versailles;91305;Massy;48.7325208;2.257078 +0360005K;LycĂ©e Polyvalent Pasteur;12 Boulevard Fr Mitterrand;36300;Le Blanc;48.9090111;2.4475893 +0040023D;LycĂ©e Polyvalent Paul Arene;13 Avenue Du Stade;04203;Sisteron;44.1830487;5.9474019 +0030026M;LycĂ©e Polyvalent Paul Constans;Rue Christophe Thivrier;03107;Montlucon;46.3359499;2.577509 +0940119U;LycĂ©e Polyvalent Paul Doumer;2 Rue Paul Doumer;94170;Le Perreux Sur Marne;48.8336998;2.5088853 +0870040W;LycĂ©e Polyvalent Paul Eluard;Bellvue De Glane;87205;St Junien;45.8951323;0.8918402 +0930125F;LycĂ©e Polyvalent Paul Eluard;15 Et 17 Avenue Jean Moulin;93206;St Denis;48.941642;2.364800 +0391092A;LycĂ©e Polyvalent Paul Emile Victor;625 Avenue De Gottmadingen;39303;Champagnole;46.7427137;5.922959 +0130143K;LycĂ©e Polyvalent Paul Langevin;131 Avenue Dr Alexander Fleming;13691;Martigues;43.404811;5.053728 +0912163G;LycĂ©e Polyvalent Paul Langevin;Rue Paul Langevin;91706;Ste Genevieve Des Bois;48.6281114;2.3335769 +0932229T;LycĂ©e Polyvalent Paul Le Rolland;136 Avenue De Castelnau;93700;Drancy;48.9245764;2.461786 +0010034G;LycĂ©e Polyvalent Paul Painleve;13 Place Des Deportes;01108;Oyonnax;46.256308;5.6597797 +0750558Z;LycĂ©e Polyvalent Paul Poiret;19 Rue Des Taillandiers;75011;Paris 11;48.8546499;2.3750458 +0932073Y;LycĂ©e Polyvalent Paul Robert;2-4 Rue Du Chateau;93260;Les Lilas;48.8834638;2.4213298 +0290022S;LycĂ©e Polyvalent Paul Serusier;Av De Waldkappel;29837;Carhaix Plouguer;48.2788842;-3.5441266 +0951937P;LycĂ©e Polyvalent Paul-emile Victor;116 Rue De Livilliers;95520;Osny;49.0723746;2.0700655 +0382895R;LycĂ©e Polyvalent Philibert Delorme;68 Boulevard Saint Hubert;38081;L Isle D Abeau;45.6224977;5.2225335 +0520019N;LycĂ©e Polyvalent Philippe Lebon;11 Rue De Sprendlingen;52301;Joinville;48.4440729;5.1395217 +0941975L;LycĂ©e Polyvalent Pierre Brossolette;5 Rue Pierre Brossolette;94270;Le Kremlin Bicetre;48.8150108;2.3595076 +0290030A;LycĂ©e Polyvalent Pierre Gueguin;Le Porzou;29182;Concarneau;47.8582636;-3.8778006 +9741263E;LycĂ©e Polyvalent Pierre Lagourgue;30 Chemin Mazeau Trois Mares;97430;Le Tampon;-21.25766;55.50818 +0020034B;LycĂ©e Polyvalent Pierre Mechain;19 Rue Leo Lagrange;02011;Laon;49.5587989;3.6319641 +0133015G;LycĂ©e Polyvalent Pierre Mendes France;Avenue Yitzhak Rabin;13741;Vitrolles;43.448548;5.246601 +0580014A;LycĂ©e Polyvalent Pierre-gilles De Gennes;Rue Du Colonel Rabier;58206;Cosne Cours Sur Loire;47.400273;2.92962 +9711033W;LycĂ©e Polyvalent Pointe Noire;Grande Plaine;97116;Pointe Noire;-4.1428413;11.8891721 +0760054W;LycĂ©e Polyvalent Porte Oceane;44 Rue Emile Zola;76090;Le Havre;49.4885624;0.1055085 +6200063Z;LycĂ©e Polyvalent Porto Vecchio;Rte De L'ospedale;20137;Porto Vecchio;41.652522;9.193939 +0210003P;LycĂ©e Polyvalent Prieur De La Cote D'or;6 Rue Vauban;21130;Auxonne;47.1947391;5.3866255 +9730328U;LycĂ©e Polyvalent PrivĂ© A.m.javouhey Cayenne;Cite Des Manguiers;97328;Cayenne;4.9332324;-52.3234507 +9720479N;LycĂ©e Polyvalent PrivĂ© Adventiste Rama;Cite Scolaire Adventiste Rama;97228;Ste Luce;14.4904216;-60.9530711 +0754030Y;LycĂ©e Polyvalent PrivĂ© Albert De Mun;2 Rue D'olivet;75007;Paris 07;48.8497255;2.3195956 +9720615L;LycĂ©e Polyvalent PrivĂ© Amep;183 Route De Redoute;97204;Fort De France;14.6282525;-61.0579894 +0430098Y;LycĂ©e Polyvalent PrivĂ© Anne Marie Martel;2 Et 9 Rue De Vienne;43002;Le Puy En Velay;45.046138;3.888936 +0110670U;LycĂ©e Polyvalent PrivĂ© Beausejour;16 Rue Michelet;11108;Narbonne;43.1855901;3.0063747 +0490840X;LycĂ©e Polyvalent PrivĂ© Bourg Chevreau Ste Anne;7 Rue Du 8 Mai 1945;49504;Segre;47.6814632;-0.8721799 +0754029X;LycĂ©e Polyvalent PrivĂ© Carcado Saisseval;121 Boulevard Raspail;75006;Paris 06;48.8453132;2.3288349 +0754045P;LycĂ©e Polyvalent PrivĂ© Catherine Laboure;29 Rue Gassendi;75014;Paris 14;48.8335807;2.3258612 +0711896A;LycĂ©e Polyvalent PrivĂ© Catholique Chalonnais;3 Rue General Giraud;71100;Chalon Sur Saone;46.7911722;4.8574959 +9741255W;LycĂ©e Polyvalent PrivĂ© Catholique St Charles;Rue Auguste Babet;97458;St Pierre;-21.33756;55.47994 +0762442S;LycĂ©e Polyvalent PrivĂ© Daniel Brottier;11 Impasse Du Chateau;76660;Smermesnil;49.8063368;1.4206513 +0300112J;LycĂ©e Polyvalent PrivĂ© De La Cci;1 Ter Avenue General Leclerc;30020;Nimes;43.832345;4.3683606 +0150047V;LycĂ©e Polyvalent PrivĂ© De La Communication St Geraud;23 Rue Du College;15013;Aurillac;44.9314468;2.4464301 +9830377T;LycĂ©e Polyvalent PrivĂ© Do Kamo (asee);15 Bis Rue Taragnat;98800;Noumea;-22.2742559;166.4556316 +0711755X;LycĂ©e Polyvalent PrivĂ© Du Sacre Coeur;22 Avenue De Charolles;71604;Paray Le Monial;46.4510984;4.124986 +0311134P;LycĂ©e Polyvalent PrivĂ© Emilie De Rodat;25 Avenue De Lombez;31027;Toulouse;43.5959637;1.4207585 +0170100S;LycĂ©e Polyvalent PrivĂ© Fenelon Notre-dame;36 Rue Massiou;17005;La Rochelle;46.1652912;-1.1504523 +0371184L;LycĂ©e Polyvalent PrivĂ© Fontiville;77 Rue De Fontiville;37250;Veigne;47.2973253;0.7387831 +0711249X;LycĂ©e Polyvalent PrivĂ© Frederic Ozanam;45 Rue De L'heritan;71031;Macon;46.3115653;4.8261909 +0951998F;LycĂ©e Polyvalent PrivĂ© G A R A C;3 Boulevard Gallieni;95102;Argenteuil;48.9438075;2.2446175 +0150760V;LycĂ©e Polyvalent PrivĂ© Gerbert;47 Avenue Des Prades;15000;Aurillac;44.9163841;2.4366131 +0761359P;LycĂ©e Polyvalent PrivĂ© Germaine Coty;86 Rue De Chateaudun;76620;Le Havre;49.5152892;0.1198095 +0530068L;LycĂ©e Polyvalent PrivĂ© Haute Follis;91 Rue Haute Follis;53000;Laval;48.0740234;-0.7854105 +0440119R;LycĂ©e Polyvalent PrivĂ© Hotelier Ste Anne;250 Boulevard Laennec;44615;St Nazaire;47.2752568;-2.2350718 +0060750H;LycĂ©e Polyvalent PrivĂ© Institution St Joseph Carnoles;191 Avenue Aristide Briand;06190;Roquebrune Cap Martin;43.7649927;7.4842023 +0350801F;LycĂ©e Polyvalent PrivĂ© Jean Paul Ii;2 Rue Antoine De Saint Exupery;35762;St Gregoire;45.5157091;2.3704006 +0440257R;LycĂ©e Polyvalent PrivĂ© Jeanne Bernard-bel Air;48 Avenue De La Baraudiere;44800;St Herblain;47.2355437;-1.5912533 +0010878Z;LycĂ©e Polyvalent PrivĂ© Jeanne D'arc;95 Rue Du LycĂ©e;01170;Cessy;0.0;0.0 +0350808N;LycĂ©e Polyvalent PrivĂ© Jeanne D'arc;13 Place De La Republique;35503;Vitre;48.124893;-1.2086706 +0460039T;LycĂ©e Polyvalent PrivĂ© Jeanne D'arc;51 Bd Colonel Teulie;46100;Figeac;44.6122989;2.0341249 +0761735Y;LycĂ©e Polyvalent PrivĂ© Jeanne D'arc;22 Rue Du General De Gaulle;76310;Ste Adresse;49.501971;0.0892924 +0850136L;LycĂ©e Polyvalent PrivĂ© Jeanne D'arc;3 Bis Bd Raymond Parpaillon;85603;Montaigu;46.9798514;-1.3120818 +0890064L;LycĂ©e Polyvalent PrivĂ© Jeanne D'arc;69 Grande Rue Aristide Briand;89200;Avallon;47.4869016;3.9071141 +0490904S;LycĂ©e Polyvalent PrivĂ© Jeanne Delanoue;11 Boulevard Jeanne D Arc;49304;Cholet;47.0640598;-0.8673109 +0480706G;LycĂ©e Polyvalent PrivĂ© Joseph Gibelin;43 Avenue De La Gare;48200;St Chely D Apcher;44.8001253;3.273392 +0754015G;LycĂ©e Polyvalent PrivĂ© Jules Richard Microtechniques;21 Rue Carducci;75019;Paris 19;48.8774956;2.3855772 +0611147R;LycĂ©e Polyvalent PrivĂ© L Esperance;Boulevard Du Nord;61800;Tinchebray;48.764231;-0.736472 +0440279P;LycĂ©e Polyvalent PrivĂ© La Baugerie;38 Bd Des Pas Enchantes;44232;St Sebastien Sur Loire;47.2100641;-1.5019567 +0761343X;LycĂ©e Polyvalent PrivĂ© La Chataigneraie;2 Rue Charles Scherer;76240;Le Mesnil Esnard;49.4132672;1.1461844 +0342175E;LycĂ©e Polyvalent PrivĂ© La Merci Littoral;603 Av De La Petite Motte;34280;La Grande Motte;43.5656148;4.0735973 +0150051Z;LycĂ©e Polyvalent PrivĂ© La Presentation Notre Dame;1 Cours Spy Des Ternes;15100;St Flour;45.0344847;3.0899936 +0351930H;LycĂ©e Polyvalent PrivĂ© La Providence;21 Rue De Rennes;35360;Montauban De Bretagne;48.1983239;-2.0463959 +0410693F;LycĂ©e Polyvalent PrivĂ© La Providence;23 Rue Des Saintes Maries;41034;Blois;47.5930538;1.3304792 +0754042L;LycĂ©e Polyvalent PrivĂ© Le Rebours;44 Boulevard Auguste Blanqui;75013;Paris 13;48.8301339;2.3505104 +0422160V;LycĂ©e Polyvalent PrivĂ© Le Renouveau;All Des Bois Chateau Colcombet;42530;St Genest Lerpt;45.452188;4.341445 +0211091X;LycĂ©e Polyvalent PrivĂ© Les Arcades;13 Rue Du Vieux College;21000;Dijon;47.3200096;5.0457552 +0761341V;LycĂ©e Polyvalent PrivĂ© Les Tourelles;53 Rue Verte;76000;Rouen;49.4502554;1.0928484 +0580071M;LycĂ©e Polyvalent PrivĂ© L'esperance;10 Cloitre St Cyr;58000;Nevers;46.9862254;3.1562197 +0830101J;LycĂ©e Polyvalent PrivĂ© Maintenon;10 Boulevard Pasteur;83409;Hyeres;43.1200536;6.1340928 +0190083M;LycĂ©e Polyvalent PrivĂ© Marguerite Bahuet;41bis Av Michelet St Antoine;19316;Brive La Gaillarde;45.14663;1.530476 +0370881G;LycĂ©e Polyvalent PrivĂ© Marmoutier;17 Quai Marmoutier;37100;Tours;47.4018774;0.7172022 +0660552S;LycĂ©e Polyvalent PrivĂ© Maso;7 Avenue Des Palmiers;66000;Perpignan;42.6997916;2.8874756 +0921484N;LycĂ©e Polyvalent PrivĂ© Montalembert;238 Boulevard Saint Denis;92400;Courbevoie;48.903176;2.2760713 +0311138U;LycĂ©e Polyvalent PrivĂ© Montalembert Ntre-dame;152 Avenue De Lespinet;31400;Toulouse;43.5761758;1.4709404 +0850130E;LycĂ©e Polyvalent PrivĂ© Nd Du Roc;Rue Charlemagne;85035;La Roche Sur Yon;46.6839572;-1.4199783 +0340991T;LycĂ©e Polyvalent PrivĂ© Nevers;18 Rue De La Garenne;34090;Montpellier;43.6204369;3.8692299 +0440274J;LycĂ©e Polyvalent PrivĂ© Notre Dame;50 Rue Jean Jaures;44401;Reze;47.1877269;-1.5471117 +0850142T;LycĂ©e Polyvalent PrivĂ© Notre Dame;Rue De Bois Fosse;85304;Challans;46.832239;-1.881754 +0851344Z;LycĂ©e Polyvalent PrivĂ© Notre Dame;29 Rue Rabelais;85205;Fontenay Le Comte;46.467908;-0.8142398 +0290206S;LycĂ©e Polyvalent PrivĂ© Notre Dame De Kerbertrand;154 Rue De Pont-aven;29391;Quimperle;47.8710014;-3.5663631 +0190062P;LycĂ©e Polyvalent PrivĂ© Notre Dame De La Providence;19 Rue General De Gaulle;19200;Ussel;45.5495205;2.3075733 +0080091F;LycĂ©e Polyvalent PrivĂ© Notre-dame;1 Place De La Basilique;08000;Charleville Mezieres;49.7607798;4.7162174 +0271070S;LycĂ©e Polyvalent PrivĂ© Notre-dame;7 Rue Du Chantier;27000;Evreux;49.0203676;1.1525311 +0650885J;LycĂ©e Polyvalent PrivĂ© Notre-dame De Garaison;2 Route Cier Garaison;65670;Monleon Magnoac;43.2066692;0.5032436 +0951994B;LycĂ©e Polyvalent PrivĂ© O R T;32 Avenue De Choiseul;95400;Villiers Le Bel;48.9940718;2.4104291 +0520685M;LycĂ©e Polyvalent PrivĂ© Oudinot;Route De Neuilly;52000;Chaumont;48.0843712;5.1460026 +0391146J;LycĂ©e Polyvalent PrivĂ© Pasteur Mont Roland;;39107;Dole;46.9583818;5.5032587 +0751712D;LycĂ©e Polyvalent PrivĂ© Petrelle;8 Rue Petrelle;75009;Paris 09;48.8806327;2.3486726 +0761356L;LycĂ©e Polyvalent PrivĂ© Providence Misericorde;42 Rue De Le Nostre;76020;Rouen;49.4441716;1.0794378 +0440246D;LycĂ©e Polyvalent PrivĂ© Sacre Coeur;3 Rue Francis Portais;44100;Nantes;47.2080132;-1.6088332 +0480022N;LycĂ©e Polyvalent PrivĂ© Sacre Coeur;1 Rue Du College;48300;Langogne;44.7256821;3.8539001 +0790078F;LycĂ©e Polyvalent PrivĂ© Saint Andre;14 Rue De Souche;79007;Niort;46.3274523;-0.4445119 +0211090W;LycĂ©e Polyvalent PrivĂ© Saint Benigne;99 Rue De Talant;21000;Dijon;47.3310729;5.0153828 +0360505D;LycĂ©e Polyvalent PrivĂ© Saint Cyr;1 Rue Lecherbonnier;36101;Issoudun;46.951712;1.995903 +0890067P;LycĂ©e Polyvalent PrivĂ© Saint Etienne;2 Rue Louise Et Leon Vernis;89100;Sens;48.204667;3.294032 +0450106W;LycĂ©e Polyvalent PrivĂ© Saint Francois De Sales;66 Rue Paul Bert;45504;Gien;47.6882519;2.6252631 +0370748M;LycĂ©e Polyvalent PrivĂ© Saint Gatien;107 Rue De La Douzillere;37300;Joue Les Tours;47.3353239;0.6524983 +0060774J;LycĂ©e Polyvalent PrivĂ© Saint Joseph;14 Rue Barla;06300;Nice;43.7018025;7.2828387 +0211089V;LycĂ©e Polyvalent PrivĂ© Saint Joseph;39 Rue Du Transvaal;21010;Dijon;47.3153813;5.0377019 +0410954P;LycĂ©e Polyvalent PrivĂ© Saint Joseph;18 Rue Lemyre De Villers;41100;Vendome;47.792551;1.065343 +0580778F;LycĂ©e Polyvalent PrivĂ© Saint Joseph;118 Bis Rue Des Montapins;58000;Nevers;46.977082;3.1350203 +0790080H;LycĂ©e Polyvalent PrivĂ© Saint Joseph;4 Rue Du Docteur Brillaud;79302;Bressuire;46.8380755;-0.4907658 +0160083D;LycĂ©e Polyvalent PrivĂ© Saint Joseph L'amandier;Allee Jean Pierre Gault;16710;St Yrieix Sur Charente;45.6931026;0.1181619 +0711494N;LycĂ©e Polyvalent PrivĂ© Saint Lazare;7 Rue St Germain;71404;Autun;46.9493625;4.2955232 +0912161E;LycĂ©e Polyvalent PrivĂ© Saint Leon;8 Quai De L Essonne;91100;Corbeil Essonnes;48.6088484;2.4825139 +0450107X;LycĂ©e Polyvalent PrivĂ© Saint Louis;Au Chateau;45203;Montargis;48.0391073;2.7103188 +0450758E;LycĂ©e Polyvalent PrivĂ© Saint Paul-bourdon Blanc;4 Rue Neuve Saint Aignan;45057;Orleans;47.8984756;1.9160429 +0610698C;LycĂ©e Polyvalent PrivĂ© Saint Thomas D'aquin;1 Place C Duperron;61105;Flers;48.7491921;-0.5655076 +0212023K;LycĂ©e Polyvalent PrivĂ© Saint Vincent De Paul;9 Rue E Humblot;21401;Chatillon Sur Seine;47.859253;4.5757386 +0831444U;LycĂ©e Polyvalent PrivĂ© Sainte Jeanne D Arc;Avenue Dreo;83170;Brignoles;43.4059478;6.0668721 +0160079Z;LycĂ©e Polyvalent PrivĂ© Sainte Marthe Chavagnes;51 Rue Du Minage;16000;Angouleme;45.6497236;0.15127 +0360693H;LycĂ©e Polyvalent PrivĂ© Sainte Solange;1 Place De La Gare;36028;Chateauroux;46.810104;1.698855 +0660077A;LycĂ©e Polyvalent PrivĂ© Sainte-louise-de-marillac;68 Avenue Victor Dalbiez;66000;Perpignan;42.6895911;2.8844606 +0311131L;LycĂ©e Polyvalent PrivĂ© Sainte-therese;16 Rue Du Bugatet;31804;St Gaudens;43.1061649;0.7217848 +0753217P;LycĂ©e Polyvalent PrivĂ© Sainte-therese;40 Rue La Fontaine;75016;Paris 16;48.8509879;2.2719157 +0341523W;LycĂ©e Polyvalent PrivĂ© Saint-joseph;11 Avenue Marx Dormoy;34202;Sete;43.4038819;3.6931944 +0480023P;LycĂ©e Polyvalent PrivĂ© Saint-joseph;2 Rue Des Penitents;48100;Marvejols;44.5542067;3.2915228 +0500126R;LycĂ©e Polyvalent PrivĂ© Saint-joseph;25 Rue Des Ecoles;50800;Villedieu Les Poeles;48.8370642;-1.2167469 +0754025T;LycĂ©e Polyvalent PrivĂ© Saint-nicolas;92 Rue De Vaugirard;75006;Paris 06;48.8468268;2.3260737 +0110045P;LycĂ©e Polyvalent PrivĂ© Saint-stanislas;77 Rue Aime Ramond;11000;Carcassonne;43.2118475;2.3495443 +0580076T;LycĂ©e Polyvalent PrivĂ© Simone Dounon;5 Rue Marcelin Berthelot;58200;Cosne Cours Sur Loire;47.4125079;2.9241087 +0720818Y;LycĂ©e Polyvalent PrivĂ© St Charles;75 Avenue Bollee;72000;Le Mans;48.0020737;0.2075092 +0350793X;LycĂ©e Polyvalent PrivĂ© St Etienne;99 Rue De La Chalotais;35512;Cesson Sevigne;48.1295633;-1.597344 +0440247E;LycĂ©e Polyvalent PrivĂ© St Felix;27 Rue Du Ballet;44001;Nantes;47.2316606;-1.560045 +0610692W;LycĂ©e Polyvalent PrivĂ© St Francois De Sales;100 Rue Labillardiere;61007;Alencon;48.430496;0.0965975 +0440253L;LycĂ©e Polyvalent PrivĂ© St Jb De La Salle;14 Rue Du Ballet;44010;Nantes;47.2317713;-1.5611167 +0180571Y;LycĂ©e Polyvalent PrivĂ© St Jean Baptiste De La Salle;52 Avenue De La Liberation;18020;Bourges;47.1051882;2.4041241 +0912117G;LycĂ©e Polyvalent PrivĂ© St Jean St Paul;1 Av De Ratisbonne;91000;Evry;48.6437303;2.4357094 +0240081H;LycĂ©e Polyvalent PrivĂ© St Joseph;Boulevard Eugene Leroy;24205;Sarlat La Caneda;44.8902364;1.2144075 +0290184T;LycĂ©e Polyvalent PrivĂ© St Joseph;Route De Pencran;29413;Landerneau;48.4479875;-4.2370715 +0441653H;LycĂ©e Polyvalent PrivĂ© St Joseph La Joliverie;141 Route De Clisson;44232;St Sebastien Sur Loire;47.1897277;-1.5177365 +0721478R;LycĂ©e Polyvalent PrivĂ© St Joseph Lorraine;Le Pizieux;72700;Pruille Le Chetif;47.984176;0.1387529 +0490888Z;LycĂ©e Polyvalent PrivĂ© St Julien La Baronnerie;Rue Helene Boucher;49481;St Sylvain D Anjou;47.487831;-0.496899 +0850137M;LycĂ©e Polyvalent PrivĂ© St Louis;104 Rue Pierre Brossolette;85007;La Roche Sur Yon;46.6861278;-1.4404186 +0511145T;LycĂ©e Polyvalent PrivĂ© St Michel;39 Rue Martin Peller;51100;Reims;49.2466602;4.0133052 +0910843X;LycĂ©e Polyvalent PrivĂ© St Pierre;70 Rue De Montgeron;91800;Brunoy;48.6945366;2.4899797 +0440256P;LycĂ©e Polyvalent PrivĂ© St Pierre La Joliverie;141 Route De Clisson;44232;St Sebastien Sur Loire;47.1897277;-1.5177365 +0300127A;LycĂ©e Polyvalent PrivĂ© St Vincent De Paul;3 Bd De Bruxelles;30020;Nimes;43.834096;4.362298 +0511147V;LycĂ©e Polyvalent PrivĂ© St Vincent De Paul;1 Rue De La Fraternite;51001;Chalons En Champagne;48.9779739;4.3614798 +0720843A;LycĂ©e Polyvalent PrivĂ© Ste Anne;5 Rue Alain De Rouge;72305;Sable Sur Sarthe;47.8412381;-0.3363364 +0560119A;LycĂ©e Polyvalent PrivĂ© Ste Anne-st Louis;2 Route De Locmaria;56400;Ste Anne D Auray;47.7047103;-2.9507241 +0720822C;LycĂ©e Polyvalent PrivĂ© Ste Catherine;202 Rue De St Aubin;72000;Le Mans;48.0222385;0.1782785 +0451335G;LycĂ©e Polyvalent PrivĂ© Ste Croix St Euverte;28 Rue De L Etelon;45043;Orleans;47.9025554;1.9171035 +0850135K;LycĂ©e Polyvalent PrivĂ© Ste Marie;12 Place Jeanne D Arc;85111;Chantonnay;46.6862118;-1.0522409 +0850133H;LycĂ©e Polyvalent PrivĂ© Ste Marie Du Port;La Meriniere;85108;Les Sables D Olonne;46.509488;-1.765298 +0350840Y;LycĂ©e Polyvalent PrivĂ© Ste Therese;27 Rue Sully Prudhomme;35000;Rennes;48.0948205;-1.6669327 +0850077X;LycĂ©e Polyvalent PrivĂ© Ste Ursule;56 Rue Georges Clemenceau;85403;Lucon;46.4577364;-1.1672514 +0440252K;LycĂ©e Polyvalent PrivĂ© Talensac;18 Rue De Talensac;44002;Nantes;47.2218408;-1.5585936 +9720782T;LycĂ©e Polyvalent PrivĂ© Techno-compta;188 Avenue Maurice Bishop;97200;Fort De France;14.6087039;-61.0560875 +0931026K;LycĂ©e Polyvalent Protectorat Saint Joseph;36 Rue Jacques Duclos;93600;Aulnay Sous Bois;48.9456045;2.4946283 +0530949U;LycĂ©e Polyvalent Raoul Vadepied;Boulevard Rossignol;53602;Evron;48.1517259;-0.40075 +0310040A;LycĂ©e Polyvalent Raymond Naves;139 Route D'albi;31018;Toulouse;43.6371858;1.4645232 +0762880T;LycĂ©e Polyvalent Raymond Queneau;Rue Du Docteur Zamenhof;76194;Yvetot;49.613437;0.769068 +0830007G;LycĂ©e Polyvalent Raynouard;Rue G. Pelissier;83170;Brignoles;43.40655;6.061187 +0922149L;LycĂ©e Polyvalent Rene Auffray;23 Rue Fernand Pelloutier;92582;Clichy;48.9033466;2.3000039 +0710048S;LycĂ©e Polyvalent Rene Cassin;49 Boulevard Des Neuf Cles;71018;Macon;46.3147768;4.8211064 +0771941S;LycĂ©e Polyvalent Rene Cassin;1 Av P Mendes Frances Noisiel;77426;Champs Sur Marne;48.8425281;2.6013372 +0932222K;LycĂ©e Polyvalent Rene Cassin;16 Allee Des Bosquets;93340;Le Raincy;48.9008371;2.5202268 +0772223Y;LycĂ©e Polyvalent Rene Descartes;4 Boulevard Copernic;77420;Champs Sur Marne;48.844743;2.5858742 +0500049G;LycĂ©e Polyvalent Robert De Mortain;30 Rue De La 30e Div Americaine;50140;Mortain;48.649222;-0.941546 +0720017C;LycĂ©e Polyvalent Robert Garnier;Avenue Du General De Gaulle;72405;La Ferte Bernard;48.184306;0.648803 +0741669M;LycĂ©e Polyvalent Roger Frison Roche;Promenade Du Fori;74401;Chamonix Mont Blanc;45.928101;6.872169 +9740002J;LycĂ©e Polyvalent Roland Garros;Rue Roland Garros;97839;Le Tampon;-21.2735691;55.5219446 +0580008U;LycĂ©e Polyvalent Romain Rolland;5 Rue Pablo Neruda;58503;Clamecy;47.4566989;3.5317777 +0950667J;LycĂ©e Polyvalent Romain Rolland;21 Av De Montmorency;95190;Goussainville;49.0299597;2.453191 +0660856X;LycĂ©e Polyvalent Rosa Luxemburg;2 Avenue Jean Moulin;66141;Canet En Roussillon;42.7039851;3.021471 +0831616F;LycĂ©e Polyvalent Rouviere;Quartier Sainte Musse;83070;Toulon;43.124228;5.928 +0771238C;LycĂ©e Polyvalent Saint Aspais;36 Rue Saint Barthelemy;77007;Melun;48.542074;2.6558301 +0930936M;LycĂ©e Polyvalent Saint Benoist De L'europe;82 Avenue Gambetta;93172;Bagnolet;48.870888;2.4176316 +0640057P;LycĂ©e Polyvalent Saint Cricq;4 B Av Des Etats Unis;64015;Pau;43.2982785;-0.3595802 +0890070T;LycĂ©e Polyvalent Saint Joseph;1 Bd De La Marne;89015;Auxerre;47.811752;3.56117 +0771246L;LycĂ©e Polyvalent Sainte Genevieve;12 Rue De La Visitation;77109;Meaux;48.9653532;2.8843027 +0772153X;LycĂ©e Polyvalent Sainte Marie;41 Rue De Chaage;77109;Meaux;48.9658178;2.8806747 +0010006B;LycĂ©e Polyvalent Saint-exupery;15 Avenue Saint-exupery;01206;Bellegarde Sur Valserine;46.1142691;5.8180076 +0312686B;LycĂ©e Polyvalent Saint-exupery;1 Place Alain Savary;31703;Blagnac;43.6816094;1.4178833 +9730371R;LycĂ©e Polyvalent Saint-laurent Ii;3 All?e Des Buissons Ardents;97393;St Laurent Du Maroni;0.0;0.0 +0281077U;LycĂ©e Polyvalent Silvia Monfort;6 Rue De L Orme De Sours;28600;Luisant;48.4192711;1.4555464 +0772310T;LycĂ©e Polyvalent Simone Signoret;Place Du 14 Juillet;77000;Vaux Le Penil;48.5310815;2.6726108 +0212015B;LycĂ©e Polyvalent Simone Weil;1 Rue Pelletier De Chambure;21000;Dijon;47.3210003;5.0500088 +0783447Y;LycĂ©e Polyvalent Simone Weil;Rue Du Val D Oise;78700;Conflans Ste Honorine;49.0066604;2.0870198 +0772332S;LycĂ©e Polyvalent Sonia Delaunay;1 Rue Du LycĂ©e;77240;Cesson;0.0;0.0 +0781952Y;LycĂ©e Polyvalent Sonia Delaunay;Avenue De St Germain;78450;Villepreux;48.8301026;1.9931895 +0542208G;LycĂ©e Polyvalent Stanislas;468 Rue De Vandoeuvre;54600;Villers Les Nancy;48.6636628;6.1530599 +0670114K;LycĂ©e Polyvalent Stanislas;7 Rue Du LycĂ©e;67163;Wissembourg;0.0;0.0 +9741052A;LycĂ©e Polyvalent Stella;;97424;St Leu;48.8626934;2.3496713 +0932121A;LycĂ©e Polyvalent Suger;6 Avenue Le Roy Des Barres;93200;St Denis;48.936181;2.357443 +0870019Y;LycĂ©e Polyvalent Suzanne Valadon;39 Rue Francois Perrin;87032;Limoges;45.8293539;1.2447165 +0680016Y;LycĂ©e Polyvalent Theodore Deck;5 Rue Des Chanoines;68504;Guebwiller;47.9061156;7.2133487 +0480688M;LycĂ©e Polyvalent Theophile Roussel;15 Rue Du Docteur Yves Dalle;48200;St Chely D Apcher;44.8039562;3.2735377 +0770942F;LycĂ©e Polyvalent Thibaut De Champagne;3 Rue Du College;77160;Provins;48.5612146;3.2921093 +0840918S;LycĂ©e Polyvalent Val De Durance;Rte Etang De La Bonde;84123;Pertuis;43.694275;5.501843 +0511884W;LycĂ©e Polyvalent Val De Murigny;2 Rue Vauban;51097;Reims;49.2237219;4.0277678 +0031082K;LycĂ©e Polyvalent Valery Larbaud;8 Boulevard Gabriel Peronnet;03306;Cusset;46.126235;3.445922 +0290012F;LycĂ©e Polyvalent Vauban;Rue De Kerichen;29801;Brest;48.4284436;-4.4688478 +0623981R;LycĂ©e Polyvalent Vauban;1 Rue De Bretagne;62120;Aire Sur La Lys;50.6333856;2.4090237 +0781984H;LycĂ©e Polyvalent Vaucanson;14 Rue Albert Thomas;78132;Les Mureaux;48.982369;1.923312 +0390027T;LycĂ©e Polyvalent Victor Berard;35 Quai Aime Lamy;39403;Morez;46.5171316;6.0256123 +0650005C;LycĂ©e Polyvalent Victor Duruy;Allees Jean Jaures;65201;Bagneres De Bigorre;43.0637029;0.153508 +0342090M;LycĂ©e Polyvalent Victor Hugo;;34400;Lunel;43.67445;4.135366 +0330126H;LycĂ©e Polyvalent Victor Louis;2 Avenue De Thouars;33405;Talence;44.8017092;-0.593576 +0070021K;LycĂ©e Polyvalent Vincent D'indy;9 Boulevard Du LycĂ©e;07006;Privas;0.0;0.0 +0781859X;LycĂ©e Polyvalent Vincent Van Gogh;Rue Jules Ferry;78410;Aubergenville;48.954462;1.855006 +9830483H;LycĂ©e Polyvalent Williama Haudra;;98820;Lifou;-22.2518502;166.4523382 +0010032E;LycĂ©e Polyvalent Xavier Bichat;Avenue Du Lac;01130;Nantua;46.1557254;5.601148 +0071351F;LycĂ©e Polyvalent Xavier Mallet;Rue Frederic Mistral;07400;Le Teil;44.5379585;4.6891329 +0250043L;LycĂ©e Polyvalent Xavier Marmier;53 Rue De Doubs;25304;Pontarlier;46.9158344;6.3514785 +0622196A;LycĂ©e PrivĂ© Baudimont Saint-charles;17 Rue Saint Maurice;62008;Arras;50.2937988;2.770153 +0910819W;LycĂ©e PrivĂ© Cours Secondaire;11 Rue De Courtaboeuf;91400;Orsay;48.6946177;2.1908145 +0451565G;LycĂ©e PrivĂ© De L Horticulture Et Du Paysage D Orleans;66 Avenue De La Mouillere;45072;Orleans;47.881944;1.9102444 +0430968U;LycĂ©e PrivĂ© Europeen St-jacques De Compostelle;4-10 Boulevard Montferrand;43000;Le Puy En Velay;45.0473977;3.8821493 +0291574D;LycĂ©e PrivĂ© Hortic Et Paysage De Kerbernez;Kerbernez;29700;Plomelin;47.943747;-4.129242 +0593225K;LycĂ©e PrivĂ© LycĂ©e PrivĂ© De Marcq;170 Rue Du College;59700;Marcq En Baroeul;50.6829913;3.1036007 +0753825A;LycĂ©e PrivĂ© Pour Handicapes Morvan;68 Rue De La Chaussee D'antin;75009;Paris 09;48.8758516;2.3320853 +0754089M;LycĂ©e PrivĂ© Pour Handicapes Votre Ecole Chez Vous;29 Rue Merlin;75011;Paris 11;48.8613416;2.3848162 +0011058V;LycĂ©e PrivĂ© Prof Rural Le Molard;10 Place De La Halle;01150;St Sorlin En Bugey;45.9040178;5.3485298 +0592921E;LycĂ©e PrivĂ© Saint Remi;10 Rue Notre Dame Des Victoires;59100;Roubaix;50.6950525;3.1805112 +0641535W;LycĂ©e Prof Agric Horticol Pr Armand David;1 Route De Mssionnaires;64240;Hasparren;43.3811075;-1.3049408 +0601768W;LycĂ©e Prof Agric PrivĂ© Du Valois;15 Rue D'ognes;60440;Nanteuil Le Haudouin;49.136273;2.813378 +0400784K;LycĂ©e Prof Agricole Pr;Au Rayon;40230;Saubrigues;43.610771;-1.314267 +0351958N;LycĂ©e Prof Agricole PrivĂ©;26 Rue Du Chatelet;35605;Redon;47.6473408;-2.0925548 +0351960R;LycĂ©e Prof Agricole PrivĂ©;15 Rue Du Bourg Au Loup;35140;St Aubin Du Cormier;48.2568795;-1.396259 +0561460H;LycĂ©e Prof Agricole PrivĂ©;20 Place Du Docteur Queinnec;56140;Malestroit;47.8099502;-2.3846483 +0561464M;LycĂ©e Prof Agricole PrivĂ©;Rue Du Chanoine Niol;56230;Questembert;47.663927;-2.454126 +0351947B;LycĂ©e Prof Agricole PrivĂ© Edmond Michelet;Parc De Montaubert;35300;Fougeres;48.3572455;-1.2009328 +0280946B;LycĂ©e Prof Agricole PrivĂ© Efagrir;2 Rue Des Fleurs;28630;Mignieres;48.3595403;1.4288729 +0280942X;LycĂ©e Prof Agricole PrivĂ© Gabriel Bridet;40 Rte Hubert Baraine;28260;Anet;48.8526049;1.435093 +0351943X;LycĂ©e Prof Agricole PrivĂ© La Noe St Yves;Route De Chateaubriant;35470;Bain De Bretagne;47.8025633;-1.7386898 +0221677D;LycĂ©e Prof Agricole PrivĂ© Le Restmeur;Pabu;22200;Pabu;48.7057532;-3.252573 +0221675B;LycĂ©e Prof Agricole PrivĂ© Notre Dame;Rue St Vincent;22290;Lanvollon;48.630202;-2.9889739 +0291573C;LycĂ©e Prof Agricole PrivĂ© Pierre Tremintin;2 Rue St Pol;29430;Plouescat;48.6583593;-4.1696711 +0561456D;LycĂ©e Prof Agricole PrivĂ© St Yves;2 Rue De La Liberation;56110;Gourin;48.1407693;-3.6033035 +0221671X;LycĂ©e Prof Agricole PrivĂ© Ste Marie;29 Rue De La Barriere;22250;Broons;48.3221871;-2.2642991 +0291575E;LycĂ©e Prof Agricole PrivĂ© Ste Marie;Rue Du 11 Novembre;29610;Plouigneau;48.5651547;-3.6991958 +0690275Z;LycĂ©e Prof Agricole Viticole;37 Route De Beaujeu;69220;St Jean D Ardieres;46.1136651;4.7273837 +0221670W;LycĂ©e Prof Horticole PrivĂ©;Coat An Doc'h;22170;Lanrodec;48.532569;-3.033864 +0941724N;LycĂ©e Prof PrivĂ© Des Metiers Batiment Et Travaux Publics;18 Rue De Belfort;94307;Vincennes;48.8501369;2.4311329 +0332495H;LycĂ©e Prof PrivĂ© Des Metiers Bel Orme;67 Rue Bel Orme;33000;Bordeaux;44.8497536;-0.5941378 +0693373S;LycĂ©e Prof PrivĂ© Des Metiers Carrel;7 Rue Pierre Robin;69362;Lyon 07;45.7493853;4.8581918 +0290200K;LycĂ©e Prof PrivĂ© Des Metiers Des Metiers Le Porsmeur;4 Rue Du Docteur Prouff;29678;Morlaix;48.5769099;-3.8335892 +0420998G;LycĂ©e Prof PrivĂ© Des Metiers Des Monts Du Lyonnais;9 Rue De Montbrison;42140;Chazelles Sur Lyon;45.6376681;4.3846408 +0693371P;LycĂ©e Prof PrivĂ© Des Metiers Don Bosco;12 Et 24 Montee Saint Laurent;69322;Lyon 05;45.7510345;4.8167844 +0931369H;LycĂ©e Prof PrivĂ© Des Metiers Francoise Cabrini;20 Rue Du Docteur Sureau;93167;Noisy Le Grand;48.8461618;2.5491228 +0312062Y;LycĂ©e Prof PrivĂ© Des Metiers Issec Pigier;36 Rue Alsace Lorraine;31000;Toulouse;43.605598;1.445689 +0690712Z;LycĂ©e Prof PrivĂ© Des Metiers Jamet Buffereau;17 Rue Des Chartreux;69283;Lyon 01;45.7720374;4.8233234 +0292140U;LycĂ©e Prof PrivĂ© Des Metiers La Croix Rouge;2 Rue Mirabeau;29229;Brest;48.4075125;-4.4894481 +0693374T;LycĂ©e Prof PrivĂ© Des Metiers La Mache;75 Boulevard Jean Xxiii;69373;Lyon 08;45.7364603;4.8695511 +0801946F;LycĂ©e Prof PrivĂ© Des Metiers La Providence;146 Bd De Saint Quentin;80094;Amiens;49.8789518;2.3007807 +0292144Y;LycĂ©e Prof PrivĂ© Des Metiers Le Likes;20 Place De La Tourbie;29196;Quimper;47.9996048;-4.1024423 +0420991Z;LycĂ©e Prof PrivĂ© Des Metiers Le Marais Sainte Therese;48 Boulevard Thiers;42000;St Etienne;45.4507808;4.3896969 +0121429N;LycĂ©e Prof PrivĂ© Des Metiers Louis Querbes;29 Rue Maurice Bompard;12000;Rodez;44.3485775;2.5792732 +0220122N;LycĂ©e Prof PrivĂ© Des Metiers Montbareil;2 Rue Marechal Joffre;22200;Guingamp;48.5632365;-3.1498718 +0070114L;LycĂ©e Prof PrivĂ© Des Metiers Notre Dame;26 Avenue Du Vanel;07000;Privas;44.7358608;4.6056151 +0693375U;LycĂ©e Prof PrivĂ© Des Metiers Notre Dame;72 Rue Des Jardiniers;69657;Villefranche Sur Saone;45.9885367;4.7206723 +0810113H;LycĂ©e Prof PrivĂ© Des Metiers Notre-dame;Avenue D'hauterive;81101;Castres;43.599188;2.244541 +0690653K;LycĂ©e Prof PrivĂ© Des Metiers Orsel;30 Rue Orsel;69600;Oullins;45.7164792;4.8123384 +0691680B;LycĂ©e Prof PrivĂ© Des Metiers Saint Charles;2831 Route De Strasbourg;69140;Rillieux La Pape;45.820291;4.8954392 +0021999M;LycĂ©e Prof PrivĂ© Des Metiers Saint Joseph;4 Place Thiers;02402;Chateau Thierry;49.0428865;3.4009902 +0690641X;LycĂ©e Prof PrivĂ© Des Metiers Saint Marc;4 Rue Sainte Helene;69287;Lyon 02;45.7554117;4.828113 +0801948H;LycĂ©e Prof PrivĂ© Des Metiers Saint Martin;68 Rue Delpech;80043;Amiens;49.8842672;2.29491 +0801950K;LycĂ©e Prof PrivĂ© Des Metiers Saint Remi;4 Rue Des Sergents;80006;Amiens;49.8942788;2.2987524 +0421006R;LycĂ©e Prof PrivĂ© Des Metiers Saint Vincent;Place De L'egalite;42400;St Chamond;45.4798433;4.5132717 +0421980Z;LycĂ©e Prof PrivĂ© Des Metiers Sainte Barbe;10 Rue Franklin;42028;St Etienne;45.4285207;4.3845892 +0420985T;LycĂ©e Prof PrivĂ© Des Metiers Sainte Claire;8 Rue Des Parottes;42450;Sury Le Comtal;45.5360878;4.187921 +0420984S;LycĂ©e Prof PrivĂ© Des Metiers Sainte Marie;39 Rue Des Freres Chappe;42007;St Etienne;45.4399091;4.395858 +0020498F;LycĂ©e Prof PrivĂ© Des Metiers Sainte Sophie;22 Rue Curie;02110;Bohain En Vermandois;49.9830328;3.4565714 +0650886K;LycĂ©e Prof PrivĂ© Des Metiers Saint-pierre;24 Avenue D'azereix;65000;Tarbes;43.2289169;0.0578127 +0290202M;LycĂ©e Prof PrivĂ© Des Metiers St Gabriel;Rue Jean Lautredou;29125;Pont L Abbe;47.8619569;-4.216529 +0020492Z;LycĂ©e Prof PrivĂ© Des Metiers St Vincent De Paul;13 Avenue De Reims;02205;Soissons;49.3754463;3.3353105 +0421742R;LycĂ©e Prof PrivĂ© Des Metiers Tezenas Du Montcel;14 Place Girodet;42000;St Etienne;45.4480193;4.3809644 +0352446U;LycĂ©e Prof PrivĂ© Hotelier Ste Therese;18 Rue Du Four;35130;La Guerche De Bretagne;47.9429138;-1.2313142 +0011057U;LycĂ©e Prof PrivĂ© Rural De L'ain;5 Rue Docteur Levrat;01130;Nantua;46.1537153;5.6035237 +0351962T;LycĂ©e Prof Rural PrivĂ©;50 Rue Nationale;35190;Tinteniac;48.3282833;-1.8332082 +0561458F;LycĂ©e Prof Rural PrivĂ© Ker Anna;3 Rue Ker Anna;56700;Kervignac;47.7614575;-3.238536 +0761744H;LycĂ©e Prof. Agricole Annexe Du Pays De Bray;4 Ave Des Canadiens;76270;Neufchatel En Bray;49.7304107;1.4327269 +0430114R;LycĂ©e Prof. Agricole PrivĂ© Isvt St Dominique Vals;La Roche Arnaud;43001;Le Puy En Velay;45.038381;3.891388 +0121293R;LycĂ©e Prof.agricole PrivĂ©;14 Rue Frayssinous;12130;St Geniez D Olt;44.466538;2.9759538 +0211504W;LycĂ©e Prof.agricole PrivĂ©;20 Rue Danton;21210;Saulieu;47.2773555;4.2315727 +0820724S;LycĂ©e Prof.agricole PrivĂ©;30 Rue De La Republique;82500;Beaumont De Lomagne;43.8823822;0.9864743 +0771740Y;LycĂ©e Prof.agricole PrivĂ© Assomption;2 Rue De Salins;77130;Forges;48.418206;2.960877 +0820725T;LycĂ©e Prof.agricole PrivĂ© Clair Foyer Caussadais;Route De Negrepelisse;82300;Caussade;44.14681;1.5243137 +0711441F;LycĂ©e Prof.agricole PrivĂ© De La Bresse;600 Avenue Fernand Point;71500;Louhans;46.6235063;5.2333128 +0441782Y;LycĂ©e Prof.agricole PrivĂ© Grand Blottereau;34 Chemin Du Ponceau;44300;Nantes;47.224905;-1.51142 +0121291N;LycĂ©e Prof.agricole PrivĂ© Horticole;Route De Villefranche;12390;Rignac;44.4130599;2.2824371 +0460504Y;LycĂ©e Prof.agricole PrivĂ© Institut Alain De Solminihac;Rue Du 8 Mai;46220;Prayssac;44.5050085;1.1855864 +0580662E;LycĂ©e Prof.agricole PrivĂ© Iperma;Domaine Laveyne;58330;St Saulge;47.104764;3.514389 +0441784A;LycĂ©e Prof.agricole PrivĂ© Jean-baptiste Eriau;Espace Rohan;44150;Ancenis;47.365464;-1.177491 +0441794L;LycĂ©e Prof.agricole PrivĂ© Kerguenec;;44350;St Molf;47.390932;-2.424955 +0341472R;LycĂ©e Prof.agricole PrivĂ© Le Cep D Or;Avenue De La Piscine;34800;Clermont L Herault;43.6314047;3.4337484 +0341473S;LycĂ©e Prof.agricole PrivĂ© Le Roc Blanc;1 Rue De L Albarede;34190;Ganges;43.9336912;3.7098028 +0491801S;LycĂ©e Prof.agricole PrivĂ© Les Buissonnets;7 Boulevard Daviers;49100;Angers;47.4791568;-0.5584661 +0721328C;LycĂ©e Prof.agricole PrivĂ© Les Horizons;15 Rue De Touraine;72220;St Gervais En Belin;47.8742353;0.2176217 +0441785B;LycĂ©e Prof.agricole PrivĂ© Les Prateaux;28 Bis Rue Bizeul;44130;Blain;47.4756295;-1.769521 +0530815Y;LycĂ©e Prof.agricole PrivĂ© Lp Rural PrivĂ©;Pannard;53500;Ernee;48.3039668;-0.9412869 +0881690J;LycĂ©e Prof.agricole PrivĂ© LycĂ©e Prof.rural;684 Rue De La Mairie;88270;Harol;48.1542309;6.247106 +0912126S;LycĂ©e Prof.agricole PrivĂ© M.f.h.essonne Verte;Avenue Du 8 Mai 1945;91154;Etampes;48.4402827;2.1552271 +0341606L;LycĂ©e Prof.agricole PrivĂ© Maurice Clavel;Rue De La Raffinerie;34110;Frontignan;43.4434006;3.7583253 +0721337M;LycĂ©e Prof.agricole PrivĂ© Nazareth;;72340;Ruille Sur Loir;47.7497789;0.620518 +0721336L;LycĂ©e Prof.agricole PrivĂ© Notre Dame;10 Rue Du Tertre;72400;La Ferte Bernard;48.1886105;0.6387134 +0781071R;LycĂ©e Prof.agricole PrivĂ© PrivĂ© Le Notre;Domaine De Pinceloup;78120;Sonchamp;48.5868348;1.8825612 +0711439D;LycĂ©e Prof.agricole PrivĂ© Reine Antier;Au Bourg;71620;St Martin En Bresse;46.340815;5.0723608 +0530813W;LycĂ©e Prof.agricole PrivĂ© Robert Schuman;62 Rue De La Division Leclerc;53200;Chateau Gontier;47.8339799;-0.7164153 +0311843K;LycĂ©e Prof.agricole PrivĂ© Rural;7 Place Des Marchands;31370;Rieumes;43.4127035;1.12037 +0311842J;LycĂ©e Prof.agricole PrivĂ© Rural L'oustal;15 Place D'orleans;31380;Montastruc La Conseillere;43.7168431;1.5901694 +0441790G;LycĂ©e Prof.agricole PrivĂ© Saint Joseph;40 Rue Michel Grimault;44141;Chateaubriant;47.7156138;-1.3765267 +0441789F;LycĂ©e Prof.agricole PrivĂ© Saint Martin;Rte De La Foret;44270;Machecoul;46.9980392;-1.8028421 +0371259T;LycĂ©e Prof.agricole PrivĂ© Sainte Jeanne D Arc;;37600;Verneuil Sur Indre;47.055817;1.042124 +0121292P;LycĂ©e Prof.agricole PrivĂ© Vaxergues Charles Palies;198 Avenue Du Dr Galtier;12400;St Affrique;43.95819;2.887126 +0891001E;LycĂ©e Prof.rural PrivĂ© Sainte Colombe;Rue Henri Cavallier;89100;St Denis;48.214857;3.273402 +0332382K;LycĂ©e Profes Agricole PrivĂ© St Clement;Domaine Labeyrie;33430;Cudos;44.3933095;-0.2146137 +0120867C;LycĂ©e Profess Agricole PrivĂ© Francois Marty;;12200;Monteils;44.26547;1.998179 +0673005C;LycĂ©e Professessionnel I.e.s.c.;14 Rue Du Zornhoff;67700;Saverne;48.7492289;7.3773102 +0730012S;LycĂ©e Professionnel;Rue Du Grand Barberaz;73190;Challes Les Eaux;45.5498015;5.9820346 +0750787Y;LycĂ©e Professionnel;61 Rue Corvisart;75013;Paris 13;48.8305676;2.3487691 +0811324Z;LycĂ©e Professionnel;Rue Du LycĂ©e;81207;Mazamet;0.0;0.0 +0020025S;LycĂ©e Professionnel;Chateau Potel;02460;La Ferte Milon;49.177915;3.124745 +0110027V;LycĂ©e Professionnel;1 Av Edouard Herriot;11500;Quillan;42.875129;2.18546 +0120036Z;LycĂ©e Professionnel;Avenue Leo Lagrange;12300;Decazeville;44.5676272;2.2523499 +0490020F;LycĂ©e Professionnel;Rue De La Tuilerie;49321;Cholet;47.0709727;-0.8903426 +0500090B;LycĂ©e Professionnel;Rue Dauphine;50600;St Hilaire Du Harcouet;48.5715264;-1.0976365 +0542306N;LycĂ©e Professionnel;4 Rue De La Tuilerie;54803;Jarny;49.1581264;5.8767019 +0590123N;LycĂ©e Professionnel;115 Rue Francisco Ferrer;59007;Lille;50.63052;3.0934521 +0590125R;LycĂ©e Professionnel;1 Rue Michel Servet;59003;Lille;50.628714;3.0361729 +0593496E;LycĂ©e Professionnel;89 Rue De La Chaussiette;59163;Conde Sur L Escaut;50.4598956;3.5860573 +0620030W;LycĂ©e Professionnel;60 Route D'albert;62452;Bapaume;50.1059853;2.8437041 +0620191W;LycĂ©e Professionnel;800 Rue Leon Blum;62251;Henin Beaumont;50.4306013;2.9485013 +0622240Y;LycĂ©e Professionnel;1 Rue Porte Becquerelle;62170;Montreuil;50.4644173;1.7663501 +0720028P;LycĂ©e Professionnel;Rue Jean Jaures;72600;Mamers;48.349964;0.363104 +0750786X;LycĂ©e Professionnel;92 96 Rue Barrault;75013;Paris 13;48.8230263;2.3467166 +0752608C;LycĂ©e Professionnel;135 Rue Belliard;75018;Paris 18;48.8962641;2.3344205 +0800060F;LycĂ©e Professionnel;Avenue Robert Solente;80301;Albert;49.9999141;2.6573268 +0850029V;LycĂ©e Professionnel;29 Boulevard Guitton;85020;La Roche Sur Yon;46.658489;-1.4330727 +0880031F;LycĂ©e Professionnel;Route De La Rochotte;88407;Gerardmer;48.064932;6.8739463 +0950709E;LycĂ©e Professionnel;100 Av Charles Vaillant;95400;Arnouville Les Gonesse;48.9854345;2.4073595 +0750770E;LycĂ©e Professionnel Abbe Gregoire;70 Bis Rue De Turbigo;75003;Paris 03;48.8661184;2.3593985 +0131709M;LycĂ©e Professionnel Adam De Craponne;218 Rue Chateau Redon;13665;Salon De Provence;43.6355195;5.0986535 +0031047X;LycĂ©e Professionnel Agricole;;03310;Durdat Larequille;46.251014;2.69973 +0071231A;LycĂ©e Professionnel Agricole;Montee Du Savel;07100;Annonay;45.2407678;4.675018 +0071234D;LycĂ©e Professionnel Agricole;19 Av Victor Descours;07270;Lamastre;44.9871441;4.5842853 +0350940G;LycĂ©e Professionnel Agricole;La Lande De La Rencontre;35140;St Aubin Du Cormier;48.2645709;-1.428232 +0382375A;LycĂ©e Professionnel Agricole;5 Rue De La Republique;38440;St Jean De Bournay;45.5023241;5.1423932 +0740276Y;LycĂ©e Professionnel Agricole;;74130;Contamine Sur Arve;46.128077;6.34251 +0150599V;LycĂ©e Professionnel Agricole;Domaine De Volzac;15100;St Flour;45.024512;3.06915 +0190624A;LycĂ©e Professionnel Agricole;;19460;Naves;45.312913;1.767888 +0370794M;LycĂ©e Professionnel Agricole;104 Avenue De La Republique;37170;Chambray Les Tours;47.3405814;0.7240973 +0381818V;LycĂ©e Professionnel Agricole;La Marteliere;38500;Voiron;45.377585;5.592192 +0381888W;LycĂ©e Professionnel Agricole;164 Allee Louis Clerget;38110;La Tour Du Pin;45.565881;5.4382031 +0410626H;LycĂ©e Professionnel Agricole;6 Rue De L Agriculture;41800;Montoire Sur Le Loir;47.7495268;0.8763012 +0430111M;LycĂ©e Professionnel Agricole;Avenue De L'hermitage;43000;Espaly St Marcel;45.055765;3.86089 +0450027K;LycĂ©e Professionnel Agricole;7 Rue Des Deportes;45340;Beaune La Rolande;48.0709911;2.433444 +0450836P;LycĂ©e Professionnel Agricole;11 Rue Des Pervenches;45270;Bellegarde;47.9881528;2.438748 +0470107L;LycĂ©e Professionnel Agricole;Fazanis;47400;Tonneins;44.37731;0.348298 +0501234V;LycĂ©e Professionnel Agricole;Route De Fougeres;50600;St Hilaire Du Harcouet;48.5683384;-1.095299 +0520741Y;LycĂ©e Professionnel Agricole;5 Ruelle Aux Loups;52500;Fayl Billot;47.7791736;5.6033372 +0530520C;LycĂ©e Professionnel Agricole;4o Route De Sable;53200;Chateau Gontier;47.8306463;-0.6922463 +0601265Z;LycĂ©e Professionnel Agricole;91 Rue Andre Regnier;60170;Ribecourt Dreslincourt;49.5135257;2.9208686 +0610746E;LycĂ©e Professionnel Agricole;Avenue Du General Leclerc;61000;Alencon;48.4172116;0.089494 +0631225P;LycĂ©e Professionnel Agricole;Le Marchedial;63210;Rochefort Montagne;45.6864149;2.800831 +0640253C;LycĂ©e Professionnel Agricole;Route De Bordeaux;64300;Orthez;43.4943844;-0.7597037 +0640254D;LycĂ©e Professionnel Agricole;Soeix;64404;Oloron Ste Marie;43.1629834;-0.5907772 +0650138X;LycĂ©e Professionnel Agricole;59 Route De Pau;65000;Tarbes;43.2381467;0.0400529 +0671779V;LycĂ©e Professionnel Agricole;33 Avenue De La Gare;67152;Erstein;48.419731;7.6515589 +0711073F;LycĂ©e Professionnel Agricole;Les Perrieres;71700;Tournus;46.5636067;4.892747 +0711120G;LycĂ©e Professionnel Agricole;Chemin D'ouze;71120;Charolles;46.4307098;4.2649363 +0790767E;LycĂ©e Professionnel Agricole;130 Route De Coulonges;79011;Niort;46.3410959;-0.4743859 +0870590U;LycĂ©e Professionnel Agricole;Route De La Souterraine;87190;Magnac Laval;46.214577;1.166048 +0870671G;LycĂ©e Professionnel Agricole;Domaine De La Faye;87500;St Yrieix La Perche;45.511306;1.249044 +0890849P;LycĂ©e Professionnel Agricole;1 Avenue Du Docteur Schweitzer;89290;Champs Sur Yonne;47.7347694;3.603143 +0771357G;LycĂ©e Professionnel Agricole Agricole;La Bretonniere;77120;Chailly En Brie;48.813781;3.152278 +0131848N;LycĂ©e Professionnel Agricole Amenagement Paysager (de L');89 Traverse Parangon;13008;Marseille 08;43.2463268;5.3798313 +0720907V;LycĂ©e Professionnel Agricole Andre Provots;Le Haut Bois;72250;Brette Les Pins;47.917679;0.332145 +0561758G;LycĂ©e Professionnel Agricole Antenne Lpa St Jean Brevelay;76 Rue Du Talhouet;56701;Hennebont;47.8168284;-3.2640287 +0030118M;LycĂ©e Professionnel Agricole Antoine Brun;44 Avenue Charles De Gaulle;03120;Lapalisse;46.2443117;3.6338821 +0021523V;LycĂ©e Professionnel Agricole Aumont;Place Foch;02380;Coucy La Ville;49.533112;3.328146 +0382376B;LycĂ©e Professionnel Agricole Bellevue;4 Rue Des Recollets;38160;St Marcellin;45.1563352;5.3213845 +0170394L;LycĂ©e Professionnel Agricole Chadignac;Le Petit Chadignac;17119;Saintes;45.721281;-0.663772 +0341059S;LycĂ©e Professionnel Agricole Charles Marie De La Condamine;4 Allee General Montagne;34120;Pezenas;43.4618994;3.4197081 +0660039J;LycĂ©e Professionnel Agricole Claude Simon;4 Rue Pasteur;66602;Rivesaltes;42.7661082;2.8699034 +0595770B;LycĂ©e Professionnel Agricole Communaute Urbaine Dunkerque;1972 Rue De Leffrinckoucke;59240;Dunkerque;51.0462169;2.4456828 +0860818X;LycĂ©e Professionnel Agricole Danielle Mathiron;Domaine Des Chevaliers;86540;Thure;46.831987;0.459635 +0400139J;LycĂ©e Professionnel Agricole De Chalosse;Route De Pomarez;40250;Mugron;43.751052;-0.748762 +0730812L;LycĂ©e Professionnel Agricole De Cognin;13 Avenue Henry Bordeaux;73160;Cognin;45.5643012;5.8933314 +0801335S;LycĂ©e Professionnel Agricole De La Baie De Somme;21 Rue Du Lieutenant Caron;80100;Abbeville;50.1108191;1.8427116 +0801328J;LycĂ©e Professionnel Agricole De La Haute Somme;10 Rue Du Quinconce;80200;Peronne;49.9358497;2.9280029 +0421210M;LycĂ©e Professionnel Agricole De Montravel-villars;Montravel;42390;Villars;45.4751884;4.3506677 +9840277D;LycĂ©e Professionnel Agricole De Opunohu;A;98728;Moorea Maiao;-17.6555246;-150.6343154 +0580054U;LycĂ©e Professionnel Agricole De Plagny;128 Rte De Lyon;58000;Sermoise Sur Loire;46.9632774;3.1571413 +9740097M;LycĂ©e Professionnel Agricole De Saint Joseph;24 Rue Raphael Babet;97480;St Joseph;-21.3766259;55.6161038 +0631223M;LycĂ©e Professionnel Agricole Des Combrailles;Avenue De La Gare;63390;St Gervais D Auvergne;46.030667;2.814032 +0040056P;LycĂ©e Professionnel Agricole Digne-carmejane;Route D Espinouse;04510;Le Chaffaut St Jurson;44.047146;6.158502 +0763004C;LycĂ©e Professionnel Agricole Du Bois;Rue Du General De Gaulle;76630;Envermeu;49.8930353;1.2701613 +0752426E;LycĂ©e Professionnel Agricole Du Breuil-ec.hort.et Arbor.;Route De La Ferme;75012;Paris 12;48.8235759;2.4572633 +0580582T;LycĂ©e Professionnel Agricole Du Morvan;Rue Mendes France;58120;Chateau Chinon Ville;47.059639;3.938102 +0761767H;LycĂ©e Professionnel Agricole Du Pays De Bray;Le Chateau;76220;Bremontier Merval;49.494288;1.652919 +0261069W;LycĂ©e Professionnel Agricole Du Tricastin-baronnies;25 Le Courreau;26130;St Paul Trois Chateaux;44.3472476;4.7671547 +0171590L;LycĂ©e Professionnel Agricole Ensmic;Avenue Francois Mitterrand;17700;Surgeres;46.1087429;-0.7680787 +0460661U;LycĂ©e Professionnel Agricole Epla De Cahors;Lacoste;46090;Le Montat;44.377312;1.441901 +0810578N;LycĂ©e Professionnel Agricole Epla De Lavaur;Domaine De Flamarens;81500;Lavaur;43.694745;1.864111 +0320557G;LycĂ©e Professionnel Agricole Epla De Mirande;Domaine De Valentees;32300;Mirande;43.516823;0.403639 +0320646D;LycĂ©e Professionnel Agricole Epla De Mirande;Route De Pau;32400;Riscle;43.658594;-0.0868189 +0820531G;LycĂ©e Professionnel Agricole Epla De Moissac;Avenue Du Sarlac;82201;Moissac;44.101327;1.09785 +0120938E;LycĂ©e Professionnel Agricole Epla De Saint-affrique;Route De Bournac;12400;St Affrique;43.9578528;2.8419831 +0311268K;LycĂ©e Professionnel Agricole Epla Saint-gaudens;16 Rue Olivier De Serres;31806;St Gaudens;43.1174843;0.730278 +0160108F;LycĂ©e Professionnel Agricole Felix Gaillard;Chez Fouquet;16300;Salles De Barbezieux;45.4515173;-0.1120687 +9720779P;LycĂ©e Professionnel Agricole Four A Chaux;Four A Chaux;97231;Le Robert;14.6647441;-60.936892 +0271108H;LycĂ©e Professionnel Agricole Gilbert Martin;Rue Pierre Corneille;27110;Le Neubourg;49.146564;0.907873 +0341001D;LycĂ©e Professionnel Agricole Honore De Balzac;Avenue De La Galine;34172;Castelnau Le Lez;43.630843;3.902609 +0861145C;LycĂ©e Professionnel Agricole Inst Maison Famil Vienne;47 Route De Montmorillon;86300;Chauvigny;46.5635216;0.646916 +0161002C;LycĂ©e Professionnel Agricole Inst Rur Education Orientation;;16370;Cherves Richemont;45.744433;-0.356736 +0860820Z;LycĂ©e Professionnel Agricole Jean Marie Bouloux;Chateau Ringuet;86501;Montmorillon;46.4163527;0.8441393 +0741556P;LycĂ©e Professionnel Agricole Jeanne Antide;55 Impasse Du Brevent;74930;Reignier;46.1310261;6.2712676 +0370878D;LycĂ©e Professionnel Agricole La Gabilliere;46 Av Emile Gounin;37403;Amboise;47.3929481;0.9793789 +0071239J;LycĂ©e Professionnel Agricole La Pelissiere Vallee Du Rhone;4 Rue Du Repos;07300;Tournon Sur Rhone;45.0652226;4.8340225 +0861122C;LycĂ©e Professionnel Agricole La Perriere;La Perriere;86200;La Roche Rigault;46.974342;0.186944 +0840606C;LycĂ©e Professionnel Agricole La Ricarde;Avnue Jean Boin;84800;L Isle Sur La Sorgue;43.9219696;5.0448297 +0560274U;LycĂ©e Professionnel Agricole Le Suillo;;56660;St Jean Brevelay;47.845229;-2.720734 +0790977H;LycĂ©e Professionnel Agricole Le Val De L Ouin;3 Au 9 Rue De La Sagesse;79700;Mauleon;46.9223173;-0.7528173 +0382369U;LycĂ©e Professionnel Agricole Le Vallon;13 Rue De L Eglise;38690;Chabons;45.4443564;5.4306894 +0595771C;LycĂ©e Professionnel Agricole Legta Du Nord;Avenue Du Chateau;59590;Raismes;50.3959279;3.4830104 +0382370V;LycĂ©e Professionnel Agricole Les Alpes;42 Rue Des Alpes;38350;La Mure;44.9054002;5.7895942 +0131715U;LycĂ©e Professionnel Agricole Les Alpilles;Rue Edouard Heriot;13210;St Remy De Provence;43.790754;4.835772 +0830184Z;LycĂ©e Professionnel Agricole Les Arcs;Route De Draguignan D555;83460;Les Arcs;43.4855012;6.4849386 +0741225E;LycĂ©e Professionnel Agricole Les Roselieres;3260 Route D Albertville;74320;Sevrier;45.8541292;6.1438701 +0741224D;LycĂ©e Professionnel Agricole Les Trois Vallees;2 Avenue De L'ermitage;74200;Thonon Les Bains;46.3679703;6.48484 +0160981E;LycĂ©e Professionnel Agricole Ltpr Claire Champagne;1 Rue Malestiers;16130;Segonzac;45.61808;-0.2231605 +0261315N;LycĂ©e Professionnel Agricole LycĂ©e De Val De Drome;Route De Valence N;26760;Monteleger;44.868636;4.9412779 +0390809T;LycĂ©e Professionnel Agricole Mancy;410 Montee Gauthier Villars;39015;Lons Le Saunier;46.6644134;5.5482578 +0550754W;LycĂ©e Professionnel Agricole Martial Brousse;Place Saint Paul;55100;Verdun;49.163828;5.3843585 +0110710M;LycĂ©e Professionnel Agricole Martin Luther King;Centre Pierre Reverdy;11782;Narbonne;43.184277;3.003078 +0110712P;LycĂ©e Professionnel Agricole Martin Luther King;36 Avenue De Narbonne;11220;St Laurent De La Cabrerisse;43.0856549;2.7015968 +0490963F;LycĂ©e Professionnel Agricole Montreuil-bellay;Rte De Meron;49260;Montreuil Bellay;47.1312384;-0.1332827 +0861123D;LycĂ©e Professionnel Agricole Odile Pasquier;5 Place De La Marne;86700;Couhe;46.3002383;0.1827319 +0382371W;LycĂ©e Professionnel Agricole Paul Claudel;16 Rue Du Marche Vieux;38460;Cremieu;45.7264346;5.253525 +0320530C;LycĂ©e Professionnel Agricole Pavie;;32550;Pavie;43.609862;0.591676 +9840356P;LycĂ©e Professionnel Agricole PrivĂ© Anne Marie Javourey;Place Notre Dame;98714;Papeete;-17.535022;-149.569594 +9760181T;LycĂ©e Professionnel Agricole PrivĂ© De Coconi;;97670;Chiconi;43.4865563;-0.7687041 +0221032C;LycĂ©e Professionnel Agricole Public;Route De Dinan;22350;Caulnes;48.2968278;-2.1519311 +9760206V;LycĂ©e Professionnel Agricole Public De Coconi;;97670;Chiconi;43.4865563;-0.7687041 +0382378D;LycĂ©e Professionnel Agricole Saint Exupery;264 Rue Des Frenes;38870;St Simeon De Bressieux;45.3301563;5.2612136 +9730364H;LycĂ©e Professionnel Agricole Savane Matiti;Savane Matiti Macouria;97355;Macouria;4.9155544;-52.3693218 +0261065S;LycĂ©e Professionnel Agricole Val De Drome;Route De Valence;26760;Monteleger;44.868636;4.9412779 +0711815M;LycĂ©e Professionnel Agricole Velet;Route De St Didier-sur-arroux;71190;Etang Sur Arroux;46.8627761;4.1780787 +0141421U;LycĂ©e Professionnel Agricole Vire;Route De Caen;14500;Vire;48.8541038;-0.8794987 +0840218F;LycĂ©e Professionnel Agricole Viticole;Mas Mongin Quartier Du Gres;84100;Orange;44.1380989;4.807511 +0640028H;LycĂ©e Professionnel Aizpurdi;1 Les Allees;64702;Hendaye;43.3570403;-1.7742956 +0570061G;LycĂ©e Professionnel Alain Fournier;Rue Emile Boilvin;57000;Metz;49.10305;6.1761419 +0595787V;LycĂ©e Professionnel Alain Savary;Rue Alain Savary;59391;Wattrelos;50.7099702;3.2012067 +0620257T;LycĂ©e Professionnel Alain Savary;14 Avenue Jean Zay;62022;Arras;50.2838793;2.7487122 +0550026E;LycĂ©e Professionnel Alain-fournier;12 Av President Kennedy;55107;Verdun;49.1447039;5.3973437 +0251557G;LycĂ©e Professionnel Albert Camus;Impasse Camus;25200;Bethoncourt;47.537051;6.8015439 +0420074C;LycĂ©e Professionnel Albert Camus;32 Bis Rue De La Loire;42704;Firminy;45.3883922;4.2830196 +0440056X;LycĂ©e Professionnel Albert Chassagne;5 Rue Alexis Maneyrol;44560;Paimboeuf;47.2866375;-2.0259524 +0030061A;LycĂ©e Professionnel Albert Londres;Avenue De La Liberation;03306;Cusset;46.1306952;3.4360709 +0572756L;LycĂ©e Professionnel Albert Schweitzer;44 Rue Schellenthal;57230;Bitche;49.0567339;7.4180043 +0420077F;LycĂ©e Professionnel Albert Thomas;20 Rue Albert Thomas;42328;Roanne;46.0440474;4.0740983 +0710080B;LycĂ©e Professionnel Alexandre Dumaine;95 Espace Alexandre Dumaine;71018;Macon;46.30415;4.833486 +0840113S;LycĂ©e Professionnel Alexandre Dumas;Rue Alphonse Jauffret;84300;Cavaillon;43.8326963;5.0287304 +0500089A;LycĂ©e Professionnel Alexis De Tocqueville;34 Avenue Henri Poincare;50100;Cherbourg Octeville;49.6304753;-1.6077873 +0931198X;LycĂ©e Professionnel Alfred Costes;146 Avenue Henri Barbusse;93000;Bobigny;48.9083628;2.42744 +0690107S;LycĂ©e Professionnel Alfred De Musset;128 Rue De La Poudrette;69100;Villeurbanne;45.75696;4.9211312 +0061561P;LycĂ©e Professionnel Alfred Hutinel;21 Rue De Cannes;06150;Cannes;43.5533656;6.970213 +0590264S;LycĂ©e Professionnel Alfred Kastler;123 Rue Paul Elie Casanova;59723;Denain;50.3293842;3.4072591 +0801704T;LycĂ©e Professionnel Alfred Manessier;33 Rue De La Resistance;80420;Flixecourt;50.0091454;2.0757544 +0540077R;LycĂ©e Professionnel Alfred Mezieres;3 Avenue Andre Malraux;54401;Longwy;49.5216573;5.7601943 +0040007L;LycĂ©e Professionnel Alphonse Beau De Rochas;10 Avenue General Leclerc;04000;Digne Les Bains;43.0535129;2.2158732 +0622174B;LycĂ©e Professionnel Alphonse Daudet;Rue Alphonse Daudet;62620;Barlin;50.456584;2.619812 +0350009V;LycĂ©e Professionnel Alphonse Pelle;30 Rue Montplaisir;35120;Dol De Bretagne;48.5435906;-1.7620966 +0130146N;LycĂ©e Professionnel Alpilles (les);Quartier Les Molieres;13140;Miramas;43.588896;5.002136 +0261100E;LycĂ©e Professionnel Amblard;43 Rue Amblard;26000;Valence;44.9278503;4.9011313 +0400049L;LycĂ©e Professionnel Ambroise Croizat;92 Av Marcel Paul;40220;Tarnos;43.5355002;-1.4901891 +0594532F;LycĂ©e Professionnel Ambroise Croizat;Rue Marcel Paul;59950;Auby;50.4150948;3.0507573 +0740062R;LycĂ©e Professionnel Amedee Gordini;31 Route De Sacconges;74602;Seynod;45.8851811;6.109274 +9740479C;LycĂ©e Professionnel Amiral Lacaze;Rue Stanislas Gimart;97493;St Denis;48.936181;2.357443 +0130072H;LycĂ©e Professionnel Ampere;56 Bd Romain Rolland;13395;Marseille 10;43.2796927;5.4237647 +0560019S;LycĂ©e Professionnel Ampere;Rue Guethennoc;56120;Josselin;47.9598749;-2.547896 +0720064D;LycĂ©e Professionnel Ampere;Boulevard Du Quebec;72206;La Fleche;47.7087173;-0.0792501 +0600049C;LycĂ©e Professionnel Amyot D Inville;Chemin De Reims;60309;Senlis;49.192707;2.5701139 +0330011H;LycĂ©e Professionnel Anatole De Monzie;12 Cours Gambetta;33430;Bazas;44.4332382;-0.213939 +9720515C;LycĂ©e Professionnel Andre Aliker;34 Boulevard Amilcar Cabral;97200;Fort De France;14.609371;-61.07256 +0381605N;LycĂ©e Professionnel Andre Argouges;61 Rue Leon Jouhaux;38029;Grenoble;45.1753906;5.7445124 +0440315D;LycĂ©e Professionnel Andre Boulloche;32 Rue Du Plessis;44606;St Nazaire;47.2873869;-2.2243657 +0640031L;LycĂ©e Professionnel Andre Campa;29 Avenue Joliot Curie;64110;Jurancon;43.2913485;-0.4011011 +0692968B;LycĂ©e Professionnel Andre Cuzin;42 Chemin De Crepieux;69300;Caluire Et Cuire;45.8010809;4.8573366 +0594653M;LycĂ©e Professionnel Andre Jurenil;Boulevard Du 8 Mai 1945;59723;Denain;50.32402;3.396566 +0622089J;LycĂ©e Professionnel Andre Malraux;314 Rue Jules Massenet;62408;Bethune;50.5200303;2.6516077 +0911037H;LycĂ©e Professionnel Andre-marie Ampere;12b A 26 Rte Du Bois Pommier;91390;Morsang Sur Orge;48.6571262;2.3453375 +0810018E;LycĂ©e Professionnel Anne Veaute;46 Boulevard Magenta;81104;Castres;43.6008543;2.2348464 +0480505N;LycĂ©e Professionnel Annexe Lp E Peytavin Mende;Quartier Des Chauvets;48300;Langogne;44.727236;3.855416 +0211356K;LycĂ©e Professionnel Antoine Antoine;5 Rue De Longvic;21303;Chenove;47.2897502;5.00852 +0592850C;LycĂ©e Professionnel Antoine De Saint Exupery;1 Rue Louis Pergaud;59432;Halluin;50.7783564;3.1134241 +0470004Z;LycĂ©e Professionnel Antoine Lomet;221 Av D Italie;47000;Agen;44.1865224;0.6245447 +0762765T;LycĂ©e Professionnel Antoine-laurent Lavoisier;Rue Des Moteaux;76620;Le Havre;49.5263165;0.1002857 +0140005E;LycĂ©e Professionnel Arcisse De Caumont;3 Rue Baron Gerard;14402;Bayeux;49.2843359;-0.7097585 +0840763Y;LycĂ©e Professionnel Argensol (quartier De L');Rue Henri Dunant;84106;Orange;44.1317994;4.8348234 +0090019X;LycĂ©e Professionnel Aristide Berges;Avenue De La Gare;09201;St Girons;43.6583119;-0.5866688 +0271268G;LycĂ©e Professionnel Aristide Briand;2 Rue Pierre Semard;27031;Evreux;49.0168441;1.1602102 +0670062D;LycĂ©e Professionnel Aristide Briand;12 Rue Du Barrage;67301;Schiltigheim;48.6038727;7.7515437 +0930831Y;LycĂ©e Professionnel Aristide Briand;120 Avenue Aristide Briand;93155;Le Blanc Mesnil;48.94482;2.4493596 +0840046U;LycĂ©e Professionnel Aristide Briand (cours);7 Cours Aristide Briand;84100;Orange;44.138288;4.8045546 +0752700C;LycĂ©e Professionnel Armand Carrel;45 Rue Armand Carrel;75019;Paris 19;48.8827398;2.3759898 +0080028M;LycĂ©e Professionnel Armand Malaise;84 Rue Du Bois Fortant;08003;Charleville Mezieres;49.7465138;4.7139474 +0240050Z;LycĂ©e Professionnel Arnaud Daniel;Rue Couleau;24600;Riberac;45.245019;0.336231 +0601822E;LycĂ©e Professionnel Arthur Rimbaud;319 Rue Aristide Briand;60170;Ribecourt Dreslincourt;49.5074328;2.9250178 +0931738J;LycĂ©e Professionnel Arthur Rimbaud;112-114 Avenue Jean Jaures;93120;La Courneuve;48.9219026;2.4080165 +0520008B;LycĂ©e Professionnel Ashton;47 Avenue D'ashton Under Lyne;52000;Chaumont;48.0919255;5.143953 +0710087J;LycĂ©e Professionnel Astier;Rue De Bourgogne;71600;Paray Le Monial;46.4628376;4.1041988 +0810047L;LycĂ©e Professionnel Aucouturier;Route De Blaye;81400;Carmaux;44.0511877;2.1480562 +0430023S;LycĂ©e Professionnel Auguste Aymard;2 Rue Saint Marcel;43000;Espaly St Marcel;45.0490932;3.8675833 +0620110H;LycĂ©e Professionnel Auguste Behal;6 Rue Paul Eluard;62300;Lens;50.4456289;2.8230467 +0260044G;LycĂ©e Professionnel Auguste Bouvet;10 Rue Bouvet;26102;Romans Sur Isere;45.0481031;5.0466555 +0061635V;LycĂ©e Professionnel Auguste Escoffier;Chemin Du Brecq;06801;Cagnes Sur Mer;43.6713865;7.1486137 +0951618T;LycĂ©e Professionnel Auguste Escoffier;77 Rue De Pierrelaye;95610;Eragny;49.0215662;2.1093175 +0770944H;LycĂ©e Professionnel Auguste Perdonnet;1 Allee Du Chateau;77407;Lagny Sur Marne;48.8541166;2.6465725 +0911343R;LycĂ©e Professionnel Auguste Perret;Avenue De La Liberte;91024;Evry;48.6405766;2.4346873 +9710746J;LycĂ©e Professionnel Augustin Arron;Rue De La Republique;97122;Baie Mahault;16.2655166;-61.5871614 +0271606Z;LycĂ©e Professionnel Augustin Boismard;Rue Emile Neuville;27800;Brionne;49.197356;0.713757 +0270018Y;LycĂ©e Professionnel Augustin Hebert;20 Rue Dugesclin;27025;Evreux;49.0193767;1.1716123 +9830460H;LycĂ©e Professionnel Augustin Ty;Touho;98831;Touho;-20.7797486;165.2385163 +0590144L;LycĂ©e Professionnel Automobile Alfred Mongy;129 Rue De La Briqueterie;59700;Marcq En Baroeul;50.6678924;3.0819707 +0594400M;LycĂ©e Professionnel Automobile Et Transports;Rue Alexis Carrel;59791;Grande Synthe;51.0042875;2.3198217 +0630073M;LycĂ©e Professionnel B.t.p;29 Route De Marsat;63530;Volvic;45.8754617;3.0454878 +0690003D;LycĂ©e Professionnel Barthelemy Thimonnier;160 Avenue Andre Lassagne;69592;L Arbresle;45.8343637;4.6130454 +0700019R;LycĂ©e Professionnel Bartholdi;31 Rue Du Dr Deubel;70202;Lure;47.6844457;6.4874168 +9720424D;LycĂ©e Professionnel Bateliere;1 Rue Mon Ideal;97233;Schoelcher;0.0;0.0 +0060038J;LycĂ©e Professionnel Batiment;17 Boulevard Pierre Sola;06300;Nice;43.7074475;7.2861065 +0750793E;LycĂ©e Professionnel Beaugrenelle;62 Rue Saint Charles;75015;Paris 15;48.8482389;2.2868428 +0593187U;LycĂ©e Professionnel Beaupre;Avenue De Beaupre;59481;Haubourdin;50.6018;2.9906864 +0371211R;LycĂ©e Professionnel Beauregard;15 Rue Andre Bauchant;37110;Chateau Renault;47.585999;0.906274 +0420075D;LycĂ©e Professionnel Beauregard;4 Avenue Paul Cezanne;42605;Montbrison;45.6137248;4.0570332 +0700024W;LycĂ©e Professionnel Beauregard;32 Rue P Rimey;70306;Luxeuil Les Bains;47.8220301;6.3674511 +0350050P;LycĂ©e Professionnel Bel Air;7 Rue Ernest Renan;35190;Tinteniac;48.3324662;-1.8241016 +0420049A;LycĂ©e Professionnel Benoit Charvet;30 Avenue Benoit Charvet;42021;St Etienne;45.4455705;4.3772934 +0470641S;LycĂ©e Professionnel Benoit D Azy;Av Charles De Gaulle;47501;Fumel;44.498344;0.958251 +0420958N;LycĂ©e Professionnel Benoit Fourneyron;24 Rue Virgile;42014;St Etienne;45.4176774;4.4197149 +0620124Y;LycĂ©e Professionnel Bernard Chochoy;Rue Marx Dormoy;62380;Lumbres;50.709505;2.129646 +0170078T;LycĂ©e Professionnel Bernard Palissy;1 Rue De Gascogne;17107;Saintes;45.7360174;-0.6152519 +0760022L;LycĂ©e Professionnel Bernard Palissy;5 Sente Aux Loups;76152;Maromme;49.481859;1.044099 +0190674E;LycĂ©e Professionnel Bernart De Ventadour;Boulevard De La Jaloustre;19200;Ussel;45.541611;2.315461 +0540085Z;LycĂ©e Professionnel Bertrand Schwartz;5 Rue Ste Anne;54340;Pompey;48.7738149;6.107286 +9710090W;LycĂ©e Professionnel Blachon Lamentin;Blachon;97129;Lamentin;0.0;0.0 +0130059U;LycĂ©e Professionnel Blaise Pascal;49 Traverse Capron;13012;Marseille 12;43.313125;5.4208382 +0170052P;LycĂ©e Professionnel Blaise Pascal;11 Rue De Dampierre;17415;St Jean D Angely;45.9498953;-0.5125652 +0490785M;LycĂ©e Professionnel Blaise Pascal;2 Rue Du LycĂ©e;49502;Segre;0.0;0.0 +0520029Z;LycĂ©e Professionnel Blaise Pascal;1 Avenue Marcel Paul;52100;St Dizier;48.6456201;4.9737363 +0570031Z;LycĂ©e Professionnel Blaise Pascal;5 Rue Paul Ney;57608;Forbach;49.1806835;6.8958864 +0890057D;LycĂ©e Professionnel Blaise Pascal;20 Ter Avenue Des Cosmonautes;89400;Migennes;47.9674694;3.5100005 +0810962F;LycĂ©e Professionnel Borde Basse;Cite Scolaire;81108;Castres;43.606214;2.241295 +0190008F;LycĂ©e Professionnel Bort-artense;259 Rue Du LycĂ©e;19110;Bort Les Orgues;0.0;0.0 +0820032P;LycĂ©e Professionnel Bourdelle;3 Boulevard Edouard Herriot;82003;Montauban;44.0128398;1.3704149 +0750794F;LycĂ©e Professionnel Brassai;8 Rue Quinault;75015;Paris 15;48.844121;2.2984015 +0350059Z;LycĂ©e Professionnel Brequigny;7 Avenue Georges Graff;35205;Rennes;48.0836213;-1.6920985 +0440074S;LycĂ©e Professionnel Brossaud-blancho;10 Bd Pierre De Coubertin;44616;St Nazaire;47.2693774;-2.2334295 +0131606A;LycĂ©e Professionnel Calade (la);430 Che De La Madrague Ville;13015;Marseille 15;43.3408984;5.3530112 +0020088K;LycĂ©e Professionnel Camille Claudel;9 Rue De Panleu;02209;Soissons;49.3786895;3.3245369 +0140019V;LycĂ©e Professionnel Camille Claudel;57 Av Mal De Lattre De Tassigny;14070;Caen;49.2034569;-0.3849055 +0590260M;LycĂ©e Professionnel Camille Claudel;1 Rue Paul Lafargue;59613;Fourmies;50.0122895;4.0404278 +0631409P;LycĂ©e Professionnel Camille Claudel;4 Rue De La Charme;63039;Clermont Ferrand;45.7981945;3.1198096 +0690125L;LycĂ©e Professionnel Camille Claudel;15 Rue De Cuire;69004;Lyon 04;45.7765856;4.8303022 +0881140L;LycĂ©e Professionnel Camille Claudel;2 Rue Du Parmont;88202;Remiremont;48.014224;6.5852025 +0940145X;LycĂ©e Professionnel Camille Claudel;4 Rue Des Carrieres;94400;Vitry Sur Seine;48.7998475;2.3944324 +0501860A;LycĂ©e Professionnel Camille Corot;242 Rue De L'exode;50015;St Lo;49.1092676;-1.0899166 +0750419Y;LycĂ©e Professionnel Camille Jenatzy;6 Rue Charles Hermite;75018;Paris 18;48.8998372;2.3697896 +0130068D;LycĂ©e Professionnel Camille Jullian;50 Boulevard De La Barasse;13396;Marseille 11;43.2859618;5.4774775 +0670043H;LycĂ©e Professionnel Camille Schneider;13 Avenue De La Gare;67120;Molsheim;48.5387452;7.4970582 +0420076E;LycĂ©e Professionnel Carnot;35 Avenue Carnot;42300;Roanne;46.0419603;4.0699904 +9710722H;LycĂ©e Professionnel Carnot;28 Rue Jean Jaures;97110;Pointe A Pitre;0.0;0.0 +0790928E;LycĂ©e Professionnel Carrosserie G Barre;Rue Jean Perrin;79010;Niort;46.315039;-0.439958 +0590266U;LycĂ©e Professionnel Cesar Baggio;Boulevard D'alsace;59000;Lille;50.6177376;3.0676499 +0220070G;LycĂ©e Professionnel Chaptal;6 Allee Chaptal;22015;St Brieuc;48.5125105;-2.7377027 +0240012H;LycĂ©e Professionnel Chardeuil Metiers Du Batiment;Batiment Chardeuil;24420;Coulaures;45.304424;0.955207 +0160047P;LycĂ©e Professionnel Charles A Coulomb;Avenue Joachim Du Bellay;16016;Angouleme;45.6303964;0.1570913 +0340061G;LycĂ©e Professionnel Charles Allies;24 Boulevard Joliot Curie;34120;Pezenas;43.4561047;3.417854 +0771880A;LycĂ©e Professionnel Charles Baudelaire;Bd Du Chevalier Bayard;77333;Meaux;48.954379;2.914558 +0911254U;LycĂ©e Professionnel Charles Baudelaire;Avenue De La Liberte;91000;Evry;48.6405766;2.4346873 +0660015H;LycĂ©e Professionnel Charles Blanc;Rue Charles Blanc;66000;Perpignan;42.694663;2.9296329 +0110823K;LycĂ©e Professionnel Charles Cros;1 Rue Michel Verges;11870;Carcassonne;43.2278833;2.3385008 +0720049M;LycĂ©e Professionnel Charles Cros;Rue St Denis;72305;Sable Sur Sarthe;47.838785;-0.342691 +0601871H;LycĂ©e Professionnel Charles De Gaulle;Rue Jacques Daguerre;60321;Compiegne;49.387482;2.7840919 +0750508V;LycĂ©e Professionnel Charles De Gaulle;17 Rue Ligner;75020;Paris 20;48.8580201;2.3973207 +0570088L;LycĂ©e Professionnel Charles Jully;59 Rue Marechal Foch;57501;St Avold;49.1057612;6.6927566 +0130151U;LycĂ©e Professionnel Charles Mongrand;10 Bd Cristofol;13110;Port De Bouc;43.405449;4.985931 +0330076D;LycĂ©e Professionnel Charles Peguy;2 Rue Du College Technique;33326;Eysines;44.8886389;-0.626501 +0680074L;LycĂ©e Professionnel Charles Pointet;5 Rue Des Tirailleurs Marocains;68802;Thann;47.810702;7.102382 +0130171R;LycĂ©e Professionnel Charles Privat;10 Rue Lucien Guintoli;13632;Arles;43.6705402;4.6268451 +0680037W;LycĂ©e Professionnel Charles Stoessel;1 Rue Du Fil;68068;Mulhouse;47.7478368;7.3198372 +0351054F;LycĂ©e Professionnel Charles Tillon;9 Allee Georges Palante;35009;Rennes;48.1069004;-1.6607643 +0590085X;LycĂ©e Professionnel Charlotte Perriand;Chateau Du Bois;59242;Genech;50.523207;3.218428 +0451442Y;LycĂ©e Professionnel Chateau Blanc;1 Rue Saint Just;45120;Chalette Sur Loing;48.0043061;2.7179901 +9720501M;LycĂ©e Professionnel Chateauboeuf;Z A C De Chateauboeuf;97255;Fort De France;14.62199;-61.0428795 +0360003H;LycĂ©e Professionnel Chateauneuf;83 Avenue Rollinat;36200;Argenton Sur Creuse;46.5824816;1.514488 +0130055P;LycĂ©e Professionnel Chatelier (le);108 Avenue Roger Salengro;13003;Marseille 03;43.3145473;5.3705429 +0750783U;LycĂ©e Professionnel Chenneviere Malezieux;31 35 Avenue Ledru Rollin;75012;Paris 12;48.8474871;2.37054 +0490059Y;LycĂ©e Professionnel Chevrollier;2 Rue Adrien Recouvreur;49001;Angers;47.4543325;-0.5571808 +0790038M;LycĂ©e Professionnel Cite Scolaire Jean Moulin;Rue Albert Buisson;79101;Thouars;46.9937436;-0.2131568 +0590268W;LycĂ©e Professionnel Cite Scolaire Pierre Forest;Boulevard Charles De Gaulle;59605;Maubeuge;50.2784762;3.9816647 +0750436S;LycĂ©e Professionnel Claude Anthime Corbon;5 Rue Corbon;75015;Paris 15;48.8363768;2.303238 +0720003M;LycĂ©e Professionnel Claude Chappe;Rue Des Colleges;72231;Arnage;47.935313;0.185062 +0921626T;LycĂ©e Professionnel Claude Chappe;54 A 80 Rue Des Alouettes;92000;Nanterre;48.8830353;2.2112838 +0430028X;LycĂ©e Professionnel Claude Favard;6 Rue Jean Catinot;43250;Ste Florine;45.4024955;3.3243487 +0420078G;LycĂ©e Professionnel Claude Lebois;8 Boulevard Alamagny;42403;St Chamond;45.4685576;4.5111391 +0761322Z;LycĂ©e Professionnel Claude Monet;267 Rue Felix Faure;76085;Le Havre;49.5009436;0.110217 +0320030J;LycĂ©e Professionnel Clement Ader;10 Rue Du Chemin Neuf;32130;Samatan;43.4952631;0.9297273 +9740679V;LycĂ©e Professionnel Cluny Catholique Ste Suzanne;4 Chemin Des Trois Freres;97441;Ste Suzanne;-20.90902;55.6055231 +0351878B;LycĂ©e Professionnel Coetlogon;53 Rue Antoine Joly;35083;Rennes;48.1229261;-1.6921595 +0020052W;LycĂ©e Professionnel Colard Noel;132 Avenue De La Republique;02100;St Quentin;49.8628358;3.3085027 +0130071G;LycĂ©e Professionnel Colbert;13 Rue Capitaine Dessemond;13284;Marseille 07;43.2882341;5.3567872 +0590269X;LycĂ©e Professionnel Colbert;2 Parvis Jean Baptiste Colbert;59208;Tourcoing;50.724993;3.16207 +0760087G;LycĂ©e Professionnel Colbert;197 Avenue Des Allies;76141;Le Petit Quevilly;49.4211557;1.0520198 +0783249H;LycĂ©e Professionnel Colbert;52 Avenue Pasteur;78170;La Celle St Cloud;48.8397868;2.1336782 +9830006P;LycĂ©e Professionnel Commercial Et Hotelier;2 Ă‹ 4 Rue G. Baudoux;98845;Noumea;0.0;0.0 +0250014E;LycĂ©e Professionnel Conde;5 Place Marulaz;25030;Besancon;47.2394964;6.0179211 +0370888P;LycĂ©e Professionnel D Arsonval;Place De La Marne;37305;Joue Les Tours;47.347121;0.6668929 +9830517V;LycĂ©e Professionnel D Ouvea (alp);Lekine;98814;Ouvea;0.0;0.0 +0921595J;LycĂ©e Professionnel Daniel Balavoine;7 Rue Marceau Delorme;92270;Bois Colombes;48.9199025;2.2723388 +0690018V;LycĂ©e Professionnel Danielle Casanova;7 Avenue Danielle Casanova;69700;Givors;45.5945754;4.7674675 +0190013L;LycĂ©e Professionnel Danton;Rue Danton;19311;Brive La Gaillarde;45.1590287;1.5216108 +0540032S;LycĂ©e Professionnel Darche;2 Rue Vauban;54405;Longwy;49.523518;5.764968 +0320026E;LycĂ©e Professionnel D'artagnan;Avenue Des Pyrenees;32110;Nogaro;43.754576;-0.029898 +0622271G;LycĂ©e Professionnel D'artois;Rue Jules Mousseron;62290;Noeux Les Mines;50.4729178;2.6598973 +0440036A;LycĂ©e Professionnel De Bougainville;2 Rue Eugene Leroux;44100;Nantes;47.1980237;-1.5930076 +9760296T;LycĂ©e Professionnel De Chirongui;Chirongui;97620;Chirongui;0.0;0.0 +0441550W;LycĂ©e Professionnel De Guerande Olivier Guichard;3 Rue Des Colleges;44350;Guerande;47.338907;-2.424615 +9830511N;LycĂ©e Professionnel De Houailou (alp);Wani Route Provinciale 3;98816;Houailou;0.0;0.0 +9760125G;LycĂ©e Professionnel De Kahani;;97600;Ouangani;0.0;0.0 +9760163Y;LycĂ©e Professionnel De Kaweni;Kaweni;97600;Mamoudzou;-12.780556;45.227778 +9830514S;LycĂ©e Professionnel De Kone (alp);Lotissement Internat;98860;Kone;0.0;0.0 +9830515T;LycĂ©e Professionnel De Koumac (alp);Rue Willy Boarat;98850;Koumac;0.0;0.0 +0861408N;LycĂ©e Professionnel De Kyoto;35 Rue Pierre De Coubertin;86034;Poitiers;46.589285;0.3654183 +0800013E;LycĂ©e Professionnel De L Acheuleen;21 Bis Rue Du 31 Aout 1944;80097;Amiens;49.8809507;2.3249847 +0800065L;LycĂ©e Professionnel De L Authie;20 Rue De Routequeue;80600;Doullens;50.149672;2.3555172 +0332781U;LycĂ©e Professionnel De L Estuaire;41 Rue Jauffre Rudel;33390;Blaye;45.126841;-0.662941 +0210032W;LycĂ©e Professionnel De La Ceramique Henry Moisand;9 Rue Du LycĂ©e;21110;Longchamp;0.0;0.0 +9830509L;LycĂ©e Professionnel De La Foa (alp);;98880;La Foa;45.980309;4.177918 +0601845E;LycĂ©e Professionnel De La Foret;Place Georges Paquier;60635;Chantilly;49.181337;2.462574 +0881411F;LycĂ©e Professionnel De La Haute Moselotte;125 Rue Du Bois Des Dames;88290;Saulxures Sur Moselotte;47.9503028;6.7725191 +9830516U;LycĂ©e Professionnel De La Roche (alp);Route Municipale 1;98828;Mare;49.0742798;-0.0712908 +0740056J;LycĂ©e Professionnel De La Vallee De L'arve P.beche;165 Avenue P. Bechet;74304;Cluses;46.0649788;6.5745449 +9830508K;LycĂ©e Professionnel De La Vallee Du Tir (alp);30 Rue Calmette;98801;Noumea;-22.2620695;166.447292 +0620163R;LycĂ©e Professionnel De L'aa;1 Avenue De Rome;62505;St Omer;50.748789;2.278218 +0510050C;LycĂ©e Professionnel De L'argonne;Avenue Pertison;51801;Ste Menehould;49.096369;4.916346 +0650014M;LycĂ©e Professionnel De L'arrouza;28 Boulevard Roger Cazenave;65107;Lourdes;43.0895992;-0.0486093 +9740737H;LycĂ©e Professionnel De L'horizon;25 Avenue Georges Brassens;97408;St Denis;44.9368252;-0.4863571 +0490013Y;LycĂ©e Professionnel De Narce;Route De Narce;49800;Brain Sur L Authion;47.445012;-0.4313428 +9830512P;LycĂ©e Professionnel De Poindimie (alp);30 Bis Route Provinciale 3;98822;Poindimie;0.0;0.0 +0920150N;LycĂ©e Professionnel De Prony;4 Rue De Bretagne;92600;Asnieres Sur Seine;48.9041761;2.2846969 +9740575G;LycĂ©e Professionnel De Saint Pierre;180 Rue Marius Et Ary Leblond;97410;St Pierre;0.0;0.0 +0230008J;LycĂ©e Professionnel Delphine Gay;Avenue Joliot Curie;23400;Bourganeuf;45.954755;1.749135 +0100016N;LycĂ©e Professionnel Denis Diderot;102 Avenue Jean Jaures;10100;Romilly Sur Seine;48.5166632;3.7160661 +0900355X;LycĂ©e Professionnel Denis Diderot;Rue D'alembert;90800;Bavilliers;47.627813;6.837506 +0410036S;LycĂ©e Professionnel Denis Papin;46 Rue De La Deniserie;41206;Romorantin Lanthenay;47.3673731;1.7432173 +0930128J;LycĂ©e Professionnel Denis Papin;34 Avenue Michelet;93120;La Courneuve;48.928846;2.3863127 +0310090E;LycĂ©e Professionnel Deodat De Severac;26 Boulevard Deodat De Severac;31076;Toulouse;43.5869625;1.4260334 +0762602R;LycĂ©e Professionnel Des 4 Cantons - Grieu;1 Avenue Des Quatre Cantons;76044;Rouen;49.4538868;1.1333653 +0610050Y;LycĂ©e Professionnel Des Andaines;3 Place Du General De Gaulle;61600;La Ferte Mace;48.5869648;-0.3589215 +0620131F;LycĂ©e Professionnel Des Deux Caps;56 Rue Pasteur;62250;Marquise;50.8170749;1.7088829 +0330033G;LycĂ©e Professionnel Des Menuts;36 Rue Des Douves;33800;Bordeaux;44.829973;-0.5631822 +0290092T;LycĂ©e Professionnel Des Metiers;Rue De Logodec;29590;Pont De Buis Les Quimerch;48.251652;-4.091863 +9730372S;LycĂ©e Professionnel Des Metiers;Quartier Balata Rn1;97351;Matoury;4.847214;-52.33112 +0291633T;LycĂ©e Professionnel Des Metiers Rene Laennec;61 Rue Du LycĂ©e;29120;Pont L Abbe;0.0;0.0 +0590102R;LycĂ©e Professionnel Des Monts De Flandre;4 Avenue Des Flandres;59522;Hazebrouck;50.6933308;2.5173338 +0594652L;LycĂ©e Professionnel Des Plaines Du Nord;74 Avenue De Petite Synthe;59792;Grande Synthe;51.0148286;2.3096488 +0622801H;LycĂ©e Professionnel Des Travaux Publics;Le Bois Des Dames;62702;Bruay La Buissiere;50.4999418;2.5616801 +0440541Z;LycĂ©e Professionnel Des Trois Rivieres;1 Rue Des Cormiers;44160;Pontchateau;47.4435735;-2.0964974 +0630061Z;LycĂ©e Professionnel Desaix;Le Mas Boutin;63700;St Eloy Les Mines;46.161333;2.8192499 +0760036B;LycĂ©e Professionnel Descartes;1575 Boulevard Nelson Mandela;76401;Fecamp;49.7442498;0.3892768 +9720429J;LycĂ©e Professionnel Dillon;Cite Dillon;97200;Fort De France;14.6035016;-61.0460438 +0594375K;LycĂ©e Professionnel Dinah Derycke;365 Rue Jules Guesde;59658;Villeneuve D Ascq;50.6395081;3.1380213 +0810995S;LycĂ©e Professionnel Docteur Clement De Pemille;17 Bis Avenue De L'europe;81302;Graulhet;43.7650158;1.9948594 +0570095U;LycĂ©e Professionnel Dominique Labroise;22 Rue De La Mesange;57403;Sarrebourg;48.736734;7.0686686 +0600048B;LycĂ©e Professionnel Donation De Rothschild;Chateau De Laversine;60740;St Maximin;49.235208;2.451886 +0090024C;LycĂ©e Professionnel Dr Philippe Tissie;17 Rue Du Capus;09700;Saverdun;43.2330329;1.5797801 +0690092A;LycĂ©e Professionnel Du 1er Film;14 Rue Du Premier Film;69008;Lyon 08;45.745231;4.8688581 +0640050G;LycĂ©e Professionnel Du 4 Septembre 1870;22 Av Du Quatre Septembre;64400;Oloron Ste Marie;43.1977225;-0.6090365 +0160792Z;LycĂ©e Professionnel Du Batiment;360 Route De Bordeaux Sillac;16022;Angouleme;45.648377;0.1562369 +0860048K;LycĂ©e Professionnel Du Batiment - Auguste Perret;46 Rue De La Bugellerie;86036;Poitiers;46.5973386;0.3314259 +0280009H;LycĂ©e Professionnel Du Batiment Ph De L Orme;7 Rue Jules Ferry;28114;Luce;48.4374249;1.4602902 +0560039N;LycĂ©e Professionnel Du Blavet;43 Rue Charles Gounod;56306;Pontivy;48.0568748;-2.9592048 +0310006N;LycĂ©e Professionnel Du Bois;Route De Luchon;31110;Montauban De Luchon;42.80681;0.60472 +0011010T;LycĂ©e Professionnel Du Bugey;113 Rue Du 5eme Rtm;01306;Belley;45.7606158;5.68952 +0740059M;LycĂ©e Professionnel Du Chablais;Chemin De Morcy;74207;Thonon Les Bains;46.359481;6.458842 +0620188T;LycĂ©e Professionnel Du Detroit;200 Rue Guillaume Apollinaire;62107;Calais;50.939417;1.8738335 +0451037H;LycĂ©e Professionnel Du Giennois;20 Rue Du 32 Eme R I;45500;Gien;47.698059;2.624758 +0951572T;LycĂ©e Professionnel Du Grand Cerf;1 Et 3 Rue Karl Marx;95870;Bezons;48.923505;2.2077554 +0590270Y;LycĂ©e Professionnel Du Hainaut;1 Avenue Villars;59322;Valenciennes;50.3582813;3.5107769 +0420065T;LycĂ©e Professionnel Du Haut Forez;Le Bourg;42600;Verrieres En Forez;45.570879;3.996448 +0801739F;LycĂ©e Professionnel Du Marquenterre;2 Rue Du Marais;80120;Rue;50.2688787;1.6760326 +0311324W;LycĂ©e Professionnel Du Mirail;Avenue Jean Baylet;31081;Toulouse;43.5846573;1.3942102 +0730032N;LycĂ©e Professionnel Du Nivolet;Route De Barby;73491;La Ravoire;45.5713919;5.9668199 +0170070J;LycĂ©e Professionnel Du Pays D'aunis;Rue Du Stade;17700;Surgeres;46.108536;-0.747223 +0680041A;LycĂ©e Professionnel Du Rebberg;1 Rue De Verdun;68100;Mulhouse;47.7403099;7.3460648 +0951282C;LycĂ©e Professionnel Du Vexin;Route De Marines;95750;Chars;49.1580627;1.9424217 +0800061G;LycĂ©e Professionnel Du Vimeu;3 Rue Denis Papin;80534;Friville Escarbotin;50.0832324;1.5461642 +9710690Y;LycĂ©e Professionnel Ducharmoy;Cite Huyghes Despointes;97120;St Claude;46.387405;5.8677659 +0290108K;LycĂ©e Professionnel Dupuy De Lome;34 Rue Dupuy De Lome;29287;Brest;48.3909239;-4.509126 +0671696E;LycĂ©e Professionnel Economique;Place Du Dr Fr. Kretz;67604;Selestat;48.259386;7.454241 +0671737Z;LycĂ©e Professionnel Economique;3 Quai Charles Frey;67000;Strasbourg;48.5784816;7.7465186 +0520795G;LycĂ©e Professionnel Edme Bouchardon;16 Rue Youri Gagarine;52903;Chaumont;48.104773;5.1478457 +0500032N;LycĂ©e Professionnel Edmond Doucet;Rue Paul Doumer;50120;Equeurdreville Hainneville;49.646515;-1.6551279 +0590265T;LycĂ©e Professionnel Edmond Labbe;817 Rue Charles Bourseul;59508;Douai;50.3764002;3.0685187 +0690129R;LycĂ©e Professionnel Edmond Labbe;9 Chemin Des Chassagnes;69600;Oullins;45.7224869;4.8100176 +0760007V;LycĂ©e Professionnel Edmond Labbe;52 Avenue Aristide Briand;76360;Barentin;49.5422723;0.956811 +0750800M;LycĂ©e Professionnel Edmond Rostand;15 Rue De L'evangile;75018;Paris 18;48.8927119;2.3626153 +0620193Y;LycĂ©e Professionnel Edouard Branly;2 Rue De La Porte Gayole;62321;Boulogne Sur Mer;50.7198852;1.6152414 +0801336T;LycĂ©e Professionnel Edouard Branly;70 Boulevard De Saint Quentin;80098;Amiens;49.8791607;2.2965051 +0850028U;LycĂ©e Professionnel Edouard Branly;5 Boulevard Edouard Branly;85006;La Roche Sur Yon;46.6746179;-1.4417319 +0860006P;LycĂ©e Professionnel Edouard Branly;2 Rue Edouard Branly;86106;Chatellerault;46.8021551;0.5431246 +0800062H;LycĂ©e Professionnel Edouard Gand;70 Bd De St Quentin;80098;Amiens;49.8791607;2.2965051 +0593661J;LycĂ©e Professionnel Edouard Lalo;11 Rue De Thionville;59009;Lille;50.6434451;3.065631 +0870041X;LycĂ©e Professionnel Edouard Vaillant;38 Route Du Derot;87200;St Junien;45.8992579;0.911986 +9730308X;LycĂ©e Professionnel Elie Castor;Rue Edjide Duchesne;97388;Kourou;5.1518573;-52.644595 +0760088H;LycĂ©e Professionnel Elisa Lemonnier;59 Boulevard Charles De Gaulle;76141;Le Petit Quevilly;49.4193664;1.0601232 +0310033T;LycĂ©e Professionnel Elisabeth Et Norbert Casteret;27 Avenue De L'isle;31806;St Gaudens;43.1126792;0.7258415 +0520032C;LycĂ©e Professionnel Emile Baudot;Route De Saint Dizier;52130;Wassy;48.499508;4.948753 +0331882S;LycĂ©e Professionnel Emile Combes;23 Rue Emile Combes;33321;Begles;44.8064674;-0.5473837 +0371258S;LycĂ©e Professionnel Emile Delataille;Place Des Prebendes;37602;Loches;47.1263983;0.98353 +0880064S;LycĂ©e Professionnel Emile Galle;5 Rue Auguste Dedecker;88151;Thaon Les Vosges;48.2491595;6.4083611 +0540015Y;LycĂ©e Professionnel Emile Levassor;2 Rue Levassor;54110;Dombasle Sur Meurthe;48.6079509;6.359411 +0480019K;LycĂ©e Professionnel Emile Peytavin;Avenue Du 11 Novembre;48001;Mende;44.5233613;3.4833187 +0132569X;LycĂ©e Professionnel Emile Zola;Av De L Arc De Meyran;13181;Aix En Provence;43.5118833;5.4584873 +0550003E;LycĂ©e Professionnel Emile Zola;5 Rue D'anjou;55012;Bar Le Duc;48.7816739;5.1710159 +0440141P;LycĂ©e Professionnel Emilien Maillard;230 Rue Du Pressoir Rouge;44154;Ancenis;47.3752742;-1.1822546 +0760024N;LycĂ©e Professionnel Emulation Dieppoise;2 Rue De Stalingrad;76201;Dieppe;49.917085;1.082993 +0850033Z;LycĂ©e Professionnel Eric Tabarly;48 Rue Du Moulin;85108;Les Sables D Olonne;46.4964057;-1.8007527 +0752846L;LycĂ©e Professionnel Erik Satie;2-4 Rue Durouchoux;75014;Paris 14;48.8331604;2.3263037 +0590193P;LycĂ©e Professionnel Ernest Couteaux;37 Avenue Du College;59734;St Amand Les Eaux;50.4477034;3.4431281 +0130058T;LycĂ©e Professionnel Estaque (l');310 Rue Rabelais;13016;Marseille 16;43.3620918;5.3247729 +0750798K;LycĂ©e Professionnel Esthetique;9 Rue Fortuny;75017;Paris 17;48.8820156;2.3062483 +0750808W;LycĂ©e Professionnel Etienne Dolet;7 9 Rue D'eupatoria;75020;Paris 20;48.8687503;2.3868082 +0440294F;LycĂ©e Professionnel Etienne Lenoir;9 Rue De L Europe;44146;Chateaubriant;47.729359;-1.3637892 +0420079H;LycĂ©e Professionnel Etienne Mimard;32 Rue Etienne Mimard;42021;St Etienne;45.438102;4.3960568 +0510008G;LycĂ©e Professionnel Etienne Oehmichen;8 Avenue Du Mont Hery;51037;Chalons En Champagne;48.9761202;4.3588161 +0130025G;LycĂ©e Professionnel Etoile (de L');14 Rue Jules Ferry;13541;Gardanne;43.4529944;5.469251 +0550891V;LycĂ©e Professionnel Eugene Freyssinet;6 Av President Kennedy;55107;Verdun;49.1456674;5.3935978 +0210056X;LycĂ©e Professionnel Eugene Guillaume;12 Av Mal De Lattre De Tassigny;21506;Montbard;47.6226812;4.3398528 +0311092U;LycĂ©e Professionnel Eugene Montel;2 Boulevard Marcel Dassault;31770;Colomiers;43.6101188;1.3508466 +0680039Y;LycĂ©e Professionnel F.d. Roosevelt;17 Bd Presid Roosevelt;68060;Mulhouse;47.7486201;7.3305514 +0220083W;LycĂ©e Professionnel Felix Le Dantec;Rue Des Cordiers;22303;Lannion;48.7310544;-3.446948 +0950657Y;LycĂ©e Professionnel Ferdinand Buisson;245 Rue Ferdinand Buisson;95120;Ermont;48.9779421;2.2726886 +0390914G;LycĂ©e Professionnel Ferdinand Fillod;1 Rue Lamartine;39160;St Amour;46.4378786;5.3440389 +0120038B;LycĂ©e Professionnel Ferdinand Foch;1 Rue Vieussens;12000;Rodez;44.350556;2.5620421 +0840700E;LycĂ©e Professionnel Ferdinand Revoul;Route De Nyons;84601;Valreas;44.3870032;5.0112201 +0620018H;LycĂ©e Professionnel Fernand Degrugillier;1 Boulevard Emile Basly;62260;Auchel;50.5069023;2.4663519 +0693094N;LycĂ©e Professionnel Fernand Forest;Rue De L'egalite;69800;St Priest;45.691598;4.9463832 +0250001R;LycĂ©e Professionnel Fernand Leger;6 Rue Rene Girardot;25404;Audincourt;47.4837032;6.8498439 +0340006X;LycĂ©e Professionnel Fernand Leger;63 Rte De Clermont L Herault;34600;Bedarieux;43.6120939;3.1676971 +0590263R;LycĂ©e Professionnel Fernand Leger;Route De Steendam;59411;Coudekerque Branche;51.0187231;2.3991535 +0610022T;LycĂ©e Professionnel Fernand Leger;Allee Eugene Cabrol;61104;Flers;48.743349;-0.555477 +0762836V;LycĂ©e Professionnel Fernand Leger;Chemin Des Coquereaux;76530;Grand Couronne;49.362074;1.0172754 +0130157A;LycĂ©e Professionnel Ferrages (quartier Les);Quartier Les Ferrages;13250;St Chamas;43.550669;5.034508 +6200004K;LycĂ©e Professionnel Finosello;Av Mal Lyautey;20189;Ajaccio;41.9379483;8.7447941 +0611287T;LycĂ©e Professionnel Flers;;61100;Flers;48.750059;-0.575794 +0330060L;LycĂ©e Professionnel Flora Tristan;Domaine De La Chausse;33360;Camblanes Et Meynac;44.7653476;-0.4941232 +0610019P;LycĂ©e Professionnel Flora Tristan;7 9 Av Meunier De La Raillere;61600;La Ferte Mace;48.589377;-0.356027 +0620121V;LycĂ©e Professionnel Flora Tristan;9 Ave Du General De Gaulle;62192;Lillers;50.5309405;2.5081599 +0920170K;LycĂ©e Professionnel Florian;9 Bis Rue De La Marne;92330;Sceaux;48.7815135;2.3034754 +0130056R;LycĂ©e Professionnel Floride (la);54 Bd Gay Lussac Zin 903;13014;Marseille 14;0.0;0.0 +0060023T;LycĂ©e Professionnel Francis De Croisset;34 Chemin De La Cavalerie;06130;Grasse;43.6526492;6.9315142 +0640053K;LycĂ©e Professionnel Francis Jammes;Route De Mont De Marsan;64301;Orthez;43.48756;-0.7739279 +0511748Y;LycĂ©e Professionnel Francois 1er;Faubourg De Vitry Le Brule;51308;Vitry Le Francois;48.729036;4.59224 +0620144V;LycĂ©e Professionnel Francois Albert;Rue Jules Mousseron;62290;Noeux Les Mines;50.4729178;2.6598973 +0440033X;LycĂ©e Professionnel Francois Arago;23 Rue Du Recteur Schmitt;44322;Nantes;47.2520908;-1.5519956 +0090020Y;LycĂ©e Professionnel Francois Camel;28 Rue Jules Ferry;09201;St Girons;42.9738873;1.1537953 +0693095P;LycĂ©e Professionnel Francois Cevert;104 Chemin De La Sauvegarde;69132;Ecully;45.7836623;4.7878684 +0623377J;LycĂ©e Professionnel Francois Hennebique;Rue Colbert;62804;Lievin;50.4257392;2.7540206 +0260041D;LycĂ©e Professionnel Francois Jean Armorin;35 Avenue H.grand;26402;Crest;44.731422;5.0164927 +0594302F;LycĂ©e Professionnel Francois Mansart;270 Avenue Henri Barbusse;59582;Marly;50.3445555;3.5571247 +0580552K;LycĂ©e Professionnel Francois Mitterrand;Rue Pierre Mendes France;58120;Chateau Chinon Ville;47.059639;3.938102 +0595894L;LycĂ©e Professionnel Francois Rabelais;817 Rue Charles Bourseul;59507;Douai;50.3764002;3.0685187 +0021479X;LycĂ©e Professionnel Francoise Dolto;4 Avenue Des Lilas;02120;Guise;49.9027382;3.639282 +0382274R;LycĂ©e Professionnel Francoise Dolto;4 Rue Piardiere;38120;Fontanil Cornillon;45.2512325;5.666418 +0710079A;LycĂ©e Professionnel Francoise Dolto;1 Rue Capitaine Priet;71307;Montceau Les Mines;46.681852;4.3871729 +0930138V;LycĂ©e Professionnel Frederic Bartholdi;12 Rue De La Liberte;93200;St Denis;48.9438959;2.3653605 +0690106R;LycĂ©e Professionnel Frederic Fays;46 Rue Frederic Fays;69615;Villeurbanne;45.7625759;4.9047165 +0130062X;LycĂ©e Professionnel Frederic Mistral;46 Boulevard Sainte Anne;13417;Marseille 08;43.2580155;5.392781 +0220187J;LycĂ©e Professionnel Fulgence Bienvenue;Rue Eon De L'etoile;22606;Loudeac;48.1851796;-2.7600028 +0540084Y;LycĂ©e Professionnel Fulgence Bienvenue;10 Rue Prosper Merimee;54580;Auboue;49.2107677;5.9836034 +0720034W;LycĂ©e Professionnel Funay-helene Boucher;22 Rue Rodolphe Diesel;72003;Le Mans;47.984752;0.2253982 +0310056T;LycĂ©e Professionnel Gabriel Peri;30 Rue Gabriel Peri;31013;Toulouse;43.6061733;1.454834 +0940132H;LycĂ©e Professionnel Gabriel Peri;41 Avenue Boileau;94500;Champigny Sur Marne;48.8067115;2.5458851 +0011118K;LycĂ©e Professionnel Gabriel Voisin;21 Avenue De Jasseron;01000;Bourg En Bresse;46.2113123;5.2450739 +0100945Y;LycĂ©e Professionnel Gabriel Voisin;Chemin Des Champs De La Loge;10012;Troyes;48.31499;4.069673 +0380100C;LycĂ©e Professionnel Galilee;124 Avenue General Leclerc;38209;Vienne;45.5052095;4.8559693 +0750785W;LycĂ©e Professionnel Galilee;28 Rue De Patay;75013;Paris 13;48.8252444;2.3751304 +0380010E;LycĂ©e Professionnel Gambetta;14 Avenue Gambetta;38309;Bourgoin Jallieu;45.5880165;5.273173 +0130006L;LycĂ©e Professionnel Gambetta (cours);100 Bis Cours Gambetta;13100;Aix En Provence;43.5203551;5.4599761 +0100906F;LycĂ©e Professionnel Gaston Bachelard;5 Bis Rue Gaston Bachelard;10200;Bar Sur Aube;48.2298972;4.7045231 +0750553U;LycĂ©e Professionnel Gaston Bachelard;2 Rue Tagore;75013;Paris 13;48.8206816;2.3602263 +0300057Z;LycĂ©e Professionnel Gaston Darboux;7 Rue Jules Raimu;30908;Nimes;43.8184647;4.3295053 +0530778H;LycĂ©e Professionnel Gaston Lesnard;84 Boulevard Volney;53015;Laval;48.0565772;-0.7961695 +0020077Y;LycĂ©e Professionnel Gay Lussac;Boulevard Gambetta;02302;Chauny;49.613832;3.2214 +0870013S;LycĂ©e Professionnel George Sand;9 Rue De L'hozanne;87210;Le Dorat;46.2145054;1.0820723 +0720065E;LycĂ©e Professionnel George Washington;8 Place Washington;72002;Le Mans;47.9956706;0.2030578 +0572175E;LycĂ©e Professionnel Georges Bastide;Square Georges Bastide;57150;Creutzwald;49.204987;6.6962469 +0880056H;LycĂ©e Professionnel Georges Baumont;Rue De L'orme;88102;St Die Des Vosges;48.29796;6.946644 +0300951W;LycĂ©e Professionnel Georges Brassens;Avenue Vigan Braquet;30205;Bagnols Sur Ceze;44.1607643;4.6116424 +0590250B;LycĂ©e Professionnel Georges Bustin;13 Rue Du 8 Mai 1945;59690;Vieux Conde;50.4642251;3.569136 +0190045W;LycĂ©e Professionnel Georges Cabanis;6 Boulevard Henry De Jouvenel;19311;Brive La Gaillarde;45.1600815;1.5232644 +0270052K;LycĂ©e Professionnel Georges Dumezil;Route D'ivry;27207;Vernon;49.0818358;1.4769566 +0590198V;LycĂ©e Professionnel Georges Guynemer;99 Rue De La Republique;59430;St Pol Sur Mer;51.0287687;2.3564086 +0740054G;LycĂ©e Professionnel Germain Sommeiller;2 Boulevard Taine;74008;Annecy;45.906769;6.1291068 +0280925D;LycĂ©e Professionnel Gilbert Courtois;2 Rue Salvador Allende;28105;Dreux;48.7366183;1.3444523 +0630053R;LycĂ©e Professionnel Gilbert Romme;75 Rue Du Creux;63201;Riom;45.8859718;3.131179 +0170025K;LycĂ©e Professionnel Gilles Jamain;2a Boulevard Pouzet;17312;Rochefort;45.9309537;-0.9657396 +0510017S;LycĂ©e Professionnel Godart Roger;8 Rue Godart Roger;51331;Epernay;49.0398693;3.9654465 +0831014B;LycĂ©e Professionnel Golf-hotel;Allee Georges Dussauge;83400;Hyeres;43.1306904;6.1572977 +0940140S;LycĂ©e Professionnel Gourdou-leseurre;50-56 Boulevard De Champigny;94210;St Maur Des Fosses;48.8024866;2.5069878 +0141640G;LycĂ©e Professionnel Guibray;8 Rue Des Champs St Georges;14700;Falaise;48.8900926;-0.1882493 +0030905T;LycĂ©e Professionnel Gustave Eiffel;Rue Jules Bertin;03800;Gannat;46.088356;3.197488 +0110034C;LycĂ©e Professionnel Gustave Eiffel;2 Rue Jean Moulin;11108;Narbonne;43.1899207;3.01902 +0130013U;LycĂ©e Professionnel Gustave Eiffel;Avenue Manoukian;13682;Aubagne;43.2867325;5.5484578 +0370053G;LycĂ©e Professionnel Gustave Eiffel;1 Rue Marechal Augereau;37073;Tours;47.4209434;0.67437 +0590262P;LycĂ©e Professionnel Gustave Eiffel;96 Rue Jules Lebleu;59427;Armentieres;50.6880881;2.8733271 +0690008J;LycĂ©e Professionnel Gustave Eiffel;6 A 12 Av Ferdinand Gaillard;69530;Brignais;45.6766545;4.7549711 +0752961L;LycĂ©e Professionnel Gustave Eiffel;1 Rue Du General Camou;75007;Paris 07;48.858841;2.2994949 +0770945J;LycĂ©e Professionnel Gustave Eiffel;4 Av. D'ormes;77130;Varennes Sur Seine;48.3741592;2.9264466 +0910632T;LycĂ©e Professionnel Gustave Eiffel;9 Avenue De La Republique;91300;Massy;48.7351223;2.2952471 +0951673C;LycĂ©e Professionnel Gustave Eiffel;9 Allee Jean De Florette;95120;Ermont;48.9837039;2.2450203 +0750775K;LycĂ©e Professionnel Gustave Ferrie;7 Rue Des Ecluses St Martin;75010;Paris 10;48.8765153;2.3673877 +0760142S;LycĂ©e Professionnel Gustave Flaubert;1 Rue Albert Dupuis;76044;Rouen;49.4508628;1.1226207 +0590255G;LycĂ©e Professionnel Guy Debeyre;R Ctre Torpilleur Le Triomphant;59383;Dunkerque;51.03456;2.3752019 +0310051M;LycĂ©e Professionnel Guynemer;43 Rue Leo Lagrange;31400;Toulouse;43.5874782;1.4525238 +0640049F;LycĂ©e Professionnel Guynemer;1 Av Du 19 Mars 1962;64404;Oloron Ste Marie;43.1835704;-0.6322436 +0670067J;LycĂ©e Professionnel Haute-bruche;18 Rue Des Grives;67131;Schirmeck;48.4859231;7.2352351 +0750802P;LycĂ©e Professionnel Hector Guimard;19 Rue Curial;75019;Paris 19;48.8918906;2.3723661 +0441823T;LycĂ©e Professionnel Heinlex;1 Rue Albert Einstein;44600;St Nazaire;47.2646046;-2.2632765 +0540059W;LycĂ©e Professionnel Helene Bardot;12 Place Saint Antoine;54701;Pont A Mousson;48.9037504;6.0558391 +0690093B;LycĂ©e Professionnel Helene Boucher;18 Rue Rosenberg;69631;Venissieux;45.6981022;4.8907074 +0371099U;LycĂ©e Professionnel Henri Becquerel;1 Rue Jules Ladoumegue;37000;Tours;47.377011;0.731656 +0332344U;LycĂ©e Professionnel Henri Brulle;Route De St Emilion;33503;Libourne;44.9122971;-0.2202256 +0620190V;LycĂ©e Professionnel Henri Darras;Chemin Des Manufactures;62803;Lievin;50.412424;2.766446 +0490784L;LycĂ©e Professionnel Henri Dunant;15 Rue De Haarlem;49017;Angers;47.4842499;-0.5241609 +0700011G;LycĂ©e Professionnel Henri Fertet;Place Du General Boichut;70104;Gray;47.444006;5.59541 +0780584L;LycĂ©e Professionnel Henri Matisse;55 Rue De Montfort;78196;Trappes;48.7814528;1.9945968 +0623328F;LycĂ©e Professionnel Henri Senez;Bd F Darchicourt;62252;Henin Beaumont;50.405948;2.962536 +0541290J;LycĂ©e Professionnel Henri Wallon;1 Rue Henry Wallon;54190;Villerupt;49.4639405;5.9266187 +0640058R;LycĂ©e Professionnel Honore Baradat;Avenue Du Loup;64050;Pau;43.3132924;-0.3555257 +0660520G;LycĂ©e Professionnel Hotel Resto Leon Blum;15 Avenue Paul Alduy;66103;Perpignan;42.6741796;2.9046494 +0070016E;LycĂ©e Professionnel Hotelier;Route De Tauriers;07110;Largentiere;44.55113;4.279915 +0910629P;LycĂ©e Professionnel Hotelier Chateau Des Coudraies;2 Bd Charles De Gaulle;91450;Etiolles;48.6349849;2.466748 +0650874X;LycĂ©e Professionnel Hotelier Et Econom Lautreamont;Avenue D Azereix;65016;Tarbes;43.227193;0.055477 +0740014N;LycĂ©e Professionnel Hotelier Francois Bise;86 Rue D'asniere;74136;Bonneville;46.0780249;6.409053 +0640042Y;LycĂ©e Professionnel Hotelier Haute Vue;Avenue Des Cimes;64160;Morlaas;43.3406345;-0.2758834 +9740738J;LycĂ©e Professionnel Hotelier La Renaissance;63 Rue Auguste Vinson;97867;St Paul;-21.0284314;55.2652076 +0573080N;LycĂ©e Professionnel Hurlevent;Rue Du Petit Bois;57460;Behren Les Forbach;49.172573;6.926953 +0590257J;LycĂ©e Professionnel Ile De Flandre;246 Bis Quai De La Derivation;59427;Armentieres;50.6911473;2.8904884 +0595480L;LycĂ©e Professionnel Ile Jeanty;Rue Auguste Waeteraere;59140;Dunkerque;51.030326;2.361877 +0330089T;LycĂ©e Professionnel Indust.et Hotel.j.monnet;40 Avenue Henri Brulle;33506;Libourne;44.9053141;-0.2418679 +0950658Z;LycĂ©e Professionnel Industriel Chateau D Epluches;45 Avenue Du Chateau;95310;St Ouen L Aumone;49.0549212;2.1309912 +0340088L;LycĂ©e Professionnel Irene Et Frederic Joliot Curie;19 Boulevard Joliot Curie;34200;Sete;43.3972261;3.6659039 +0090543S;LycĂ©e Professionnel Irenee Cros;2 Avenue De L'ariege;09103;Pamiers;43.1098821;1.6140065 +0880023X;LycĂ©e Professionnel Isabelle Viviani;75 Rue De Remiremont;88020;Epinal;48.1629574;6.4496322 +9740921H;LycĂ©e Professionnel Isnelle Amelin;Rue Marcel Goulette Duparc;97438;Ste Marie;0.0;0.0 +0801252B;LycĂ©e Professionnel J C Athanase Peltier;8 Avenue Jean Moulin;80400;Ham;49.7510579;3.077901 +0070002P;LycĂ©e Professionnel J Et E Montgolfier;17 Rue Du Capitaine De Canson;07100;Annonay;45.2442892;4.6718918 +0420073B;LycĂ©e Professionnel Jacob Holtzer;5 Rue Michelet;42704;Firminy;45.3882874;4.2914303 +0760006U;LycĂ©e Professionnel Jacquard;2 Rue Antoine Bourdelle;76360;Barentin;49.5417967;0.9497241 +0880057J;LycĂ©e Professionnel Jacques Augustin;20 Rue Du Nouvel Hopital;88100;St Die Des Vosges;48.2885775;6.9603839 +0692718E;LycĂ©e Professionnel Jacques Brel;7 Avenue D'oschatz;69200;Venissieux;45.6952773;4.8802134 +0180042Y;LycĂ©e Professionnel Jacques Coeur;108 Rue Jean Baffier;18026;Bourges;47.0738989;2.409201 +0470029B;LycĂ©e Professionnel Jacques De Romas;Route De Mezin;47600;Nerac;44.1274306;0.311799 +0060002V;LycĂ©e Professionnel Jacques Dolle;120 Chemin De Saint Claude;06600;Antibes;43.5929211;7.1137122 +0620162P;LycĂ©e Professionnel Jacques Durand;9 Rue Monsigny;62505;St Omer;50.7501372;2.2593951 +0620011A;LycĂ©e Professionnel Jacques Le Caron;61 Avenue De L'hippodrome;62000;Arras;50.2926345;2.7440119 +0380023U;LycĂ©e Professionnel Jacques Prevert;9 Rue La Republique;38601;Fontaine;45.1951041;5.6674681 +0390015E;LycĂ©e Professionnel Jacques Prevert;31 Place Barberousse;39107;Dole;47.0886756;5.488823 +0782603F;LycĂ©e Professionnel Jacques Prevert;88 Avenue Des Etats Unis;78000;Versailles;48.81003;2.1492612 +0590249A;LycĂ©e Professionnel Jacques-yves Cousteau;27 Rue Pasteur;59290;Wasquehal;50.6723546;3.1272292 +0620037D;LycĂ©e Professionnel Jan Lavezzari;15 Avenue Du Phare;62602;Berck;50.3988519;1.5605537 +0380037J;LycĂ©e Professionnel Jean Jaures;5 Rue Ancien Champ De Mars;38000;Grenoble;45.1887412;5.7201699 +0160862A;LycĂ©e Professionnel Jean Albert Gregoire;Rue Jean Albert Gregoire;16800;Soyaux;45.629823;0.217669 +0130064Z;LycĂ©e Professionnel Jean Baptiste Brochier;9 Bd Mireille Lauze;13010;Marseille 10;43.282372;5.4039153 +0870051H;LycĂ©e Professionnel Jean Baptiste Darnet;28 Avenue De Perigueux;87500;St Yrieix La Perche;45.5048579;1.2020927 +0820039X;LycĂ©e Professionnel Jean Baylet;20 Avenue Du Marechal Leclerc;82403;Valence;44.1144715;0.8939859 +0490058X;LycĂ©e Professionnel Jean Bertin;25 Rue Marceau;49401;Saumur;47.250839;-0.0746699 +0160036C;LycĂ©e Professionnel Jean Caillaud;Cite Scolaire De Puyguillen;16600;Ruelle Sur Touvre;45.6767619;0.219218 +0240007C;LycĂ©e Professionnel Jean Capelle;Av Marechal Leclerc;24108;Bergerac;44.8433876;0.477406 +0290072W;LycĂ©e Professionnel Jean Chaptal;35 Chemin Des Justices;29191;Quimper;47.9918404;-4.1193383 +0371123V;LycĂ©e Professionnel Jean Chaptal;Rue Du Clos Des Gardes;37400;Amboise;47.3993791;0.9837666 +0400047J;LycĂ©e Professionnel Jean D Arcet;1 Place Ste Quitterie;40801;Aire Sur L Adour;43.6966232;-0.2690223 +0450750W;LycĂ©e Professionnel Jean De La Taille;Allee De Burglengenfeld;45300;Pithiviers;48.1679602;2.2424772 +0820700R;LycĂ©e Professionnel Jean De Prades;Route De Toulouse;82102;Castelsarrasin;44.0334192;1.1211267 +0090003E;LycĂ©e Professionnel Jean Durroux;1 Et 3 Avenue Jean Durroux;09000;Ferrieres Sur Ariege;42.942515;1.61601 +0280700J;LycĂ©e Professionnel Jean Felix Paulsen;1 Boulevard Du 8 Mai 1945;28205;Chateaudun;48.0626165;1.3363333 +0670129B;LycĂ©e Professionnel Jean Geiler;14 Rue Des Bateliers;67064;Strasbourg;48.5802264;7.755564 +0350761M;LycĂ©e Professionnel Jean Guehenno;3 Promenade Du Gue Maheu;35305;Fougeres;48.3418263;-1.2027846 +0560053D;LycĂ©e Professionnel Jean Guehenno;79 Avenue De La Marne;56017;Vannes;47.6597846;-2.7844548 +0610049X;LycĂ©e Professionnel Jean Guehenno;16 Rue Pierre Huet;61105;Flers;48.744858;-0.552585 +0541283B;LycĂ©e Professionnel Jean Hanzelet;Place De Trey;54701;Pont A Mousson;48.9095605;6.0576284 +0440310Y;LycĂ©e Professionnel Jean Jacques Audubon;10 Boulevard Paul Langevin;44220;Coueron;47.2137802;-1.7226389 +0230003D;LycĂ©e Professionnel Jean Jaures;38 Rue Jean Jaures;23200;Aubusson;45.7620497;3.541304 +0350031U;LycĂ©e Professionnel Jean Jaures;24 Rue Victor Rault;35205;Rennes;48.0924623;-1.6893605 +0141599M;LycĂ©e Professionnel Jean Jooris;1 Rue Salvador Allende;14162;Dives Sur Mer;49.2802055;-0.0990374 +0132211H;LycĂ©e Professionnel Jean Lurcat;Chemin De St Macaire;13693;Martigues;43.4217515;5.0344096 +0451067R;LycĂ©e Professionnel Jean Lurcat;4 Rue Du Perron;45401;Fleury Les Aubrais;47.9295823;1.9245113 +0690045Z;LycĂ©e Professionnel Jean Lurcat;4 Rue Ludovic Arrachart;69008;Lyon 08;45.7323666;4.8628141 +0572075W;LycĂ©e Professionnel Jean Mace;58 Avenue Jean Mermoz;57290;Fameck;49.3016789;6.1145402 +0941232D;LycĂ©e Professionnel Jean Mace;103 Rue Mirabeau;94600;Choisy Le Roi;48.7637484;2.4216827 +0541605B;LycĂ©e Professionnel Jean Marc Reiser;1 Rue Du Stade;54810;Longlaville;49.5258418;5.7963167 +9730094P;LycĂ©e Professionnel Jean Marie Michotte;Boulevard De La Republique;97305;Cayenne;4.9367721;-52.323572 +0142178S;LycĂ©e Professionnel Jean Mermoz;1 Rue Georges Fauvel;14504;Vire;48.8384706;-0.8776385 +0150036H;LycĂ©e Professionnel Jean Mermoz;10 Rue Du Docteur Chibret;15005;Aurillac;44.9354809;2.4517539 +0180010N;LycĂ©e Professionnel Jean Mermoz;Allee Des Colleges;18028;Bourges;47.0995745;2.4231125 +0340012D;LycĂ©e Professionnel Jean Mermoz;25 Rue Ferdinand De Lesseps;34500;Beziers;43.3492469;3.2328792 +0950949R;LycĂ©e Professionnel Jean Mermoz;21 Rue Emile Combres;95560;Montsoult;49.0673876;2.3237953 +0020022N;LycĂ©e Professionnel Jean Monnet;22 Rue D Aboville;02800;La Fere;49.6600295;3.3666683 +0030060Z;LycĂ©e Professionnel Jean Monnet;39 Place Jules Ferry;03401;Yzeure;46.5660374;3.3552285 +0220075M;LycĂ©e Professionnel Jean Monnet;9 Rue Des Ursulines;22800;Quintin;48.4057441;-2.9105637 +0470782V;LycĂ©e Professionnel Jean Monnet;Rue Marcel Pagnol;47510;Foulayronnes;44.231803;0.6430793 +0541816F;LycĂ©e Professionnel Jean Monnet;8 Rue St Don;54110;Dombasle Sur Meurthe;48.6255302;6.3542519 +0590124P;LycĂ©e Professionnel Jean Monnet;111 Avenue De Dunkerque;59042;Lille;50.6334462;3.0310112 +0910631S;LycĂ©e Professionnel Jean Monnet;51 Ave Du Gal De Gaulle;91260;Juvisy Sur Orge;48.6952319;2.3801097 +0920164D;LycĂ©e Professionnel Jean Monnet;128 Avenue Jean Jaures;92120;Montrouge;48.813054;2.3056284 +0540086A;LycĂ©e Professionnel Jean Morette;1 Rue Du College;54970;Landres;49.3163495;5.8025968 +0080066D;LycĂ©e Professionnel Jean Moulin;996 Av De La Cite Scolaire;08500;Revin;49.9269895;4.6510517 +0130150T;LycĂ©e Professionnel Jean Moulin;1 Boulevard Marcel Cachin;13110;Port De Bouc;43.4076675;4.9949774 +0180026F;LycĂ©e Professionnel Jean Moulin;45 Rue Jean Moulin;18206;St Amand Montrond;46.7345343;2.5142771 +0340013E;LycĂ©e Professionnel Jean Moulin;Avenue Martyrs De La Resistance;34521;Beziers;43.3429502;3.233546 +0590188J;LycĂ©e Professionnel Jean Moulin;49 Bd Du General De Gaulle;59051;Roubaix;50.68501;3.17217 +0623327E;LycĂ©e Professionnel Jean Moulin;3 Rue Jean Moulin;62117;Brebieres;50.3312733;3.0211252 +0782602E;LycĂ©e Professionnel Jean Moulin;18 Rue Du Docteur Audigier;78150;Le Chesnay;48.8263325;2.1329428 +0931739K;LycĂ©e Professionnel Jean Moulin;9 Rue Jean Moulin;93110;Rosny Sous Bois;48.8656577;2.4930275 +0940143V;LycĂ©e Professionnel Jean Moulin;8 Rue Du Docteur Lebel;94307;Vincennes;48.8465644;2.4312472 +7200093N;LycĂ©e Professionnel Jean Nicoli;Cours Pierangeli;20296;Bastia;42.6985128;9.4509783 +0782593V;LycĂ©e Professionnel Jean Perrin;1 Rue Lucien Sampaix;78210;St Cyr L Ecole;48.8028083;2.0597426 +0910715H;LycĂ©e Professionnel Jean Perrin;26 Rue Leontine Sohier;91163;Longjumeau;48.6909037;2.3033805 +9740910W;LycĂ©e Professionnel Jean Perrin;Rue Du LycĂ©e;97440;St Andre;0.0;0.0 +0540081V;LycĂ©e Professionnel Jean Prouve;53 Rue De Bonsecours;54052;Nancy;48.6757518;6.1959881 +0721301Y;LycĂ©e Professionnel Jean Rondeau;Route De Rahay;72120;St Calais;47.932495;0.7654461 +0160049S;LycĂ©e Professionnel Jean Rostand;Rue Louise Leriget;16017;Angouleme;45.6532678;0.1797211 +0580050P;LycĂ©e Professionnel Jean Rostand;9 Boulevard Saint Exupery;58019;Nevers;47.0040888;3.1563606 +0760082B;LycĂ©e Professionnel Jean Rostand;Hameau De Neufmesnil;76550;Offranville;49.857248;1.019358 +0572012C;LycĂ©e Professionnel Jean Victor Poncelet;7 Rue Paul Valery;57070;Metz;49.1090537;6.2192344 +0120014A;LycĂ©e Professionnel Jean Vigo;Le Puits De Cales;12100;Millau;44.090247;3.059129 +0590256H;LycĂ©e Professionnel Jean-baptiste Carpeaux;Rue Pelabon;59154;Crespin;50.4079774;3.6689185 +0300056Y;LycĂ©e Professionnel Jean-baptiste Dumas;Place De La Belgique;30104;Ales;44.128435;4.0760861 +0622275L;LycĂ©e Professionnel Jean-charles Cazin;42 Rue Cazin;62321;Boulogne Sur Mer;50.7206515;1.609213 +0790015M;LycĂ©e Professionnel Jean-francois Cail;1 Rue Maurice Gadioux;79110;Chef Boutonne;46.1042153;-0.0694183 +0670127Z;LycĂ©e Professionnel Jean-frederic Oberlin;4 Rue De L'academie;67000;Strasbourg;48.5816645;7.7601602 +0820006L;LycĂ©e Professionnel Jean-louis Etienne;4 Rue Lavoisier;82303;Caussade;44.1612238;1.5403785 +0450043C;LycĂ©e Professionnel Jeannette Verdier;35 Avenue Gaillardin;45207;Montargis;47.9940559;2.7288555 +0640040W;LycĂ©e Professionnel Jean-pierre Champo;7 Rue Des Evades De France;64130;Mauleon Licharre;43.2241138;-0.8777013 +0931024H;LycĂ©e Professionnel Jean-pierre Timbaud;103 Avenue De La Republique;93300;Aubervilliers;48.9044948;2.3912217 +0330119A;LycĂ©e Professionnel Jehan Duperier;Chemin Tiran;33166;St Medard En Jalles;44.8960834;-0.705443 +0310091F;LycĂ©e Professionnel Jolimont;44 Chemin Cassaing;31079;Toulouse;43.6168618;1.4763001 +0620150B;LycĂ©e Professionnel Joliot Curie;9 Rue Leo Lagrange;62590;Oignies;50.4673489;2.9974803 +0150022T;LycĂ©e Professionnel Joseph Constant;3 Rue De La Coste;15300;Murat;45.1109178;2.860881 +0573283J;LycĂ©e Professionnel Joseph Cressot;6 Rue Mozart;57310;Guenange;49.2998824;6.2004744 +0371100V;LycĂ©e Professionnel Joseph Cugnot;Les Hucherolles;37500;Chinon;47.170435;0.2522948 +0310050L;LycĂ©e Professionnel Joseph Gallieni;39 Rue Adonis;31086;Toulouse;43.6313119;1.4271439 +0220072J;LycĂ©e Professionnel Joseph Savina;5 Place De La Republique;22220;Treguier;48.7859048;-3.2335656 +0680027K;LycĂ©e Professionnel Joseph Vogt;5 Rue Paul Burgi;68290;Masevaux;47.7681754;6.9983689 +0010017N;LycĂ©e Professionnel Joseph-marie Carriat;1 Rue De Crouy;01011;Bourg En Bresse;46.1981597;5.2225406 +0690281F;LycĂ©e Professionnel Joseph-marie Jacquard;20 Rue Louis Auguste Blanqui;69921;Oullins;45.7118138;4.8102856 +0250063H;LycĂ©e Professionnel Jouffroy D'abbans;16 Rue De L'helvetie;25112;Baume Les Dames;47.3504244;6.3520577 +0570237Y;LycĂ©e Professionnel Jouffroy D'abbans;39 Chemin D'avril;57250;Moyeuvre Grande;49.264711;6.0130248 +0260042E;LycĂ©e Professionnel Jules Algoud;Rue Barthelemy De Laffemas;26901;Valence;44.917059;4.915496 +6200003J;LycĂ©e Professionnel Jules Antonini;Av Noel Franchini;20186;Ajaccio;41.919229;8.738635 +0340045P;LycĂ©e Professionnel Jules Ferry;270 Avenue De La Colline;34077;Montpellier;43.607123;3.8419213 +0620142T;LycĂ©e Professionnel Jules Ferry;61 Rue Bocquet Flochel;62022;Arras;50.2791979;2.7961293 +0900019G;LycĂ©e Professionnel Jules Ferry;18 Rue De Verdun;90101;Delle;47.5088411;6.99076 +0110008Z;LycĂ©e Professionnel Jules Fil;Boulevard Joliot Curie;11021;Carcassonne;43.2148527;2.3675002 +0760062E;LycĂ©e Professionnel Jules Lecesne;99 Rue Jules Lecesne;76090;Le Havre;49.4931496;0.1201446 +0290102D;LycĂ©e Professionnel Jules Lesven;34 Rue Jules Lesven;29801;Brest;48.4056622;-4.4787824 +0760132F;LycĂ©e Professionnel Jules Siegfried;1 Rue Dume D'aplemont;76054;Le Havre;49.4974627;0.1241609 +0600063T;LycĂ©e Professionnel Jules Uhry;10 Rue Aristide Briand;60107;Creil;49.2580804;2.4755361 +0020013D;LycĂ©e Professionnel Jules Verne;;02401;Chateau Thierry;49.064633;3.479051 +0140020W;LycĂ©e Professionnel Jules Verne;12 Rue Lucien Bossoutrot;14126;Mondeville;49.176963;-0.319769 +0220019B;LycĂ©e Professionnel Jules Verne;Route De Corlay;22205;Guingamp;48.5538053;-3.1497201 +0601897L;LycĂ©e Professionnel Jules Verne;Rue Caradane;60210;Grandvilliers;49.665342;1.939705 +0620187S;LycĂ©e Professionnel Jules Verne;54 Avenue Du Mont Levin;62630;Etaples;50.5205723;1.6454476 +0690130S;LycĂ©e Professionnel Jules Verne;75 Route De Saint Clement;69173;Tarare;45.8973407;4.4437914 +0020078Z;LycĂ©e Professionnel Julie Daubie;3 Place Robert Aumont;02011;Laon;49.5611106;3.6109299 +0570145Y;LycĂ©e Professionnel Julie Daubie;Rue Joffre;57120;Rombas;49.2534261;6.0999961 +0560042S;LycĂ©e Professionnel Julien Crozet;4 Rue Des Recollets;56290;Port Louis;47.706917;-3.3562259 +0710077Y;LycĂ©e Professionnel Julien De Balleure;141 Avenue Boucicaut;71321;Chalon Sur Saone;46.7888739;4.8359262 +9740082W;LycĂ©e Professionnel Julien De Rontaunay;Cite Scolaire Du Butor;97492;St Denis;48.936181;2.357443 +0500037U;LycĂ©e Professionnel Julliot De La Morandiere;Rue De La Crete;50406;Granville;48.8264841;-1.5782999 +0570109J;LycĂ©e Professionnel La Briquerie;15 Route De La Briquerie;57100;Thionville;49.363583;6.1609848 +0731043M;LycĂ©e Professionnel La Cardiniere;191 Chemin De La Cardiniere;73017;Chambery;45.5625736;5.9310737 +0350709F;LycĂ©e Professionnel La Champagne;2 Rue Du Sergent Harris;35506;Vitre;48.1210809;-1.229255 +0440032W;LycĂ©e Professionnel La Chauviniere;2 Rue De La Fantaisie;44322;Nantes;47.2535027;-1.5719339 +0220064A;LycĂ©e Professionnel La Closerie;10 Rue Pierre Loti;22410;St Quay Portrieux;48.6576705;-2.8477013 +0831354W;LycĂ©e Professionnel La Coudouliere;Chemin De La Coudouliere;83140;Six Fours Les Plages;43.0975054;5.8170999 +0511190S;LycĂ©e Professionnel La Fontaine Du Ve;Avenue De La Fontaine Du Ve;51122;Sezanne;48.726156;3.7170255 +0572755K;LycĂ©e Professionnel La Malgrange;Chaussee D'amerique;57129;Thionville;49.3717995;6.16505 +0330069W;LycĂ©e Professionnel La Morlette;62 Rue Camille Pelletan;33151;Cenon;44.8564425;-0.5190718 +0620220C;LycĂ©e Professionnel La Peupleraie;Rue Jules Mattez;62430;Sallaumines;50.4131724;2.8628092 +0500027H;LycĂ©e Professionnel La Roquelle;5 Rue Des Courtilles;50207;Coutances;49.0412575;-1.4410444 +0490005P;LycĂ©e Professionnel La Roseraie;200 Rue Du Docteur Guichard;49015;Angers;47.4491825;-0.5502084 +0691676X;LycĂ©e Professionnel La Sauvagere;5 Rue Communieu;69256;Lyon 09;45.7963868;4.8256612 +0940137N;LycĂ©e Professionnel La Source;54 Avenue De La Source;94130;Nogent Sur Marne;48.8298866;2.4656826 +0540060X;LycĂ©e Professionnel La Tournelle;2 Rue De Lorraine;54550;Pont St Vincent;48.6020214;6.1028929 +0920158X;LycĂ©e Professionnel La Tournelle;87 Boulevard National;92250;La Garenne Colombes;48.9076332;2.2381311 +9720430K;LycĂ©e Professionnel La Trinite;Cite Scolaire Frantz Fanon;97220;La Trinite;0.0;0.0 +0190701J;LycĂ©e Professionnel Lavoisier;Rue Lavoisier;19311;Brive La Gaillarde;45.1527958;1.5133529 +0590189K;LycĂ©e Professionnel Lavoisier;31 Rue Lavoisier;59059;Roubaix;50.676634;3.1298011 +0601470X;LycĂ©e Professionnel Lavoisier;8 Rue Jules Ferry;60110;Meru;49.2297048;2.125951 +0771995A;LycĂ©e Professionnel Le Champ De Claye;71 Rue Pasteur;77414;Claye Souilly;48.9380503;2.6749314 +0080048J;LycĂ©e Professionnel Le Chateau;1 Place Du Chateau;08208;Sedan;49.6930187;4.9075711 +0880001Y;LycĂ©e Professionnel Le Chesnois;44 Rue Du Chesnois;88240;Bains Les Bains;48.0003643;6.2481647 +0020061F;LycĂ©e Professionnel Le Corbusier;Passage Le Corbusier;02200;Soissons;49.376636;3.32342 +0590216P;LycĂ©e Professionnel Le Corbusier;74 Rue De Lille;59208;Tourcoing;50.7224263;3.1544077 +0950656X;LycĂ©e Professionnel Le Corbusier;30 Rue Jean Jaures;95240;Cormeilles En Parisis;48.9784415;2.2015132 +0390020K;LycĂ©e Professionnel Le Corbusier - Batiment;255 Rue Charles Ragmey;39015;Lons Le Saunier;46.6790667;5.5648843 +0860039A;LycĂ©e Professionnel Le Dolmen;71 Rue Du Dolmen;86036;Poitiers;46.5781164;0.3674768 +0730006K;LycĂ©e Professionnel Le Grand Arc;265 Chemin De La Charrette;73200;Albertville;45.661974;6.3661905 +0881339C;LycĂ©e Professionnel Le Haut De Bellieu;Route Du Stand;88306;Neufchateau;48.360448;5.706625 +0520940P;LycĂ©e Professionnel Le Haut Du Val;9 Rue St Exupery;52008;Chaumont;48.0985269;5.1329186 +0760114L;LycĂ©e Professionnel Le Hurle-vent;1 Avenue Jean Moulin;76470;Le Treport;50.0566018;1.3684502 +0580042F;LycĂ©e Professionnel Le Mont-chatelet;Boulevard Saint Saturnin;58210;Varzy;47.359394;3.383169 +0740010J;LycĂ©e Professionnel Le Saleve;59 Route D'etrembieres;74107;Annemasse;46.1860606;6.2311348 +0810016C;LycĂ©e Professionnel Le Sidobre;80 Avenue Rene Cassin;81103;Castres;43.5958949;2.2370177 +0861113T;LycĂ©e Professionnel Le Verger;14 Rue Jean Pidoux;86100;Chatellerault;46.8190339;0.5624545 +0130063Y;LycĂ©e Professionnel Leau (bd);63 Boulevard Leau;13008;Marseille 08;43.2534617;5.3862316 +0440116M;LycĂ©e Professionnel Leloup Bouhier;11 Boulevard De Launay;44100;Nantes;47.2102412;-1.5728065 +0460012N;LycĂ©e Professionnel Leo Ferre;75 Avenue Cavaignac;46300;Gourdon;44.7341503;1.3771929 +0620059C;LycĂ©e Professionnel Leo Lagrange;Rue Victor Schoelcher;62160;Bully Les Mines;50.4289606;2.764828 +0830016S;LycĂ©e Professionnel Leon Blum;1111 Boulevard Leon Blum;83011;Draguignan;43.5271939;6.4854513 +0060022S;LycĂ©e Professionnel Leon Chiris;51 Chemin Des Capucins;06130;Grasse;43.6507373;6.9307514 +9740552G;LycĂ©e Professionnel Leon De Lepervanche;Av Raymond Mondon;97828;Le Port;-20.9343006;55.3032171 +0070009X;LycĂ©e Professionnel Leon Pavin;Route De La Gare;07210;Chomerac;44.7068354;4.6613722 +0130172S;LycĂ©e Professionnel Leonard De Vinci;8 Rue Du Rempart;13007;Marseille 07;43.2888013;5.3649989 +0240984P;LycĂ©e Professionnel Leonard De Vinci;Chemin De Saltgourde;24000;Perigueux;45.1949847;0.6770043 +0260050N;LycĂ©e Professionnel Leonard De Vinci;Avenue Henri Becquerel;26702;Pierrelatte;44.380434;4.698 +0440035Z;LycĂ©e Professionnel Leonard De Vinci;31 Rue De La Bottiere;44319;Nantes;47.2381866;-1.5215084 +0530079Y;LycĂ©e Professionnel Leonard De Vinci;129 Boulevard De L Europe;53103;Mayenne;48.3010639;-0.623681 +0595678B;LycĂ©e Professionnel Leonard De Vinci;Rue De La Renaissance;59125;Trith St Leger;50.330411;3.482308 +0791029P;LycĂ©e Professionnel Leonard De Vinci;37 Boulevard Lescure;79300;Bressuire;46.8321172;-0.4870723 +0920680P;LycĂ©e Professionnel Leonard De Vinci;5 Avenue Henri Barbusse;92220;Bagneux;48.7979653;2.3079713 +0170030R;LycĂ©e Professionnel Leonce Vieljeux;118 Rue Des Gonthieres;17001;La Rochelle;46.1774956;-1.1513475 +0741164N;LycĂ©e Professionnel Les Carillons;3 Avenue De Prelevet;74962;Cran Gevrier;45.8949329;6.1014022 +0360011S;LycĂ©e Professionnel Les Charmilles;;36019;Chateauroux;46.859935;1.7200194 +0331460H;LycĂ©e Professionnel Les Chartrons;130 Rue Du Jardin Public;33026;Bordeaux;44.8558775;-0.573314 +0921592F;LycĂ©e Professionnel Les Cotes De Villebon;3 Rue Henri Etlin;92360;Meudon;48.794339;2.2211379 +0520041M;LycĂ©e Professionnel Les Franchises;65 Rue Eugene Gallion;52200;Langres;47.8670425;5.3428612 +0911493D;LycĂ©e Professionnel Les Freres Moreau;Rte De Brunoy;91480;Quincy Sous Senart;48.6769958;2.5295462 +0630256L;LycĂ©e Professionnel Les Gravouses;4 Rue De Barante;63000;Clermont Ferrand;45.7867625;3.0677884 +0790090U;LycĂ©e Professionnel Les Grippeaux;1 Rue Edouard Herriot;79204;Parthenay;46.638001;-0.2595192 +0592833J;LycĂ©e Professionnel Les Hauts De Flandre;Rue Jude Blanckaert;59474;Seclin;50.5438352;3.0261402 +0250067M;LycĂ©e Professionnel Les Huisselets;8 Avenue De Lattre De Tassigny;25206;Montbeliard;47.5117132;6.7966991 +0600004D;LycĂ©e Professionnel Les Jacobins;2 Rue Vincent De Beauvais;60000;Beauvais;49.4306564;2.086988 +0100039N;LycĂ©e Professionnel Les Lombards;12 Avenue Des Lombards;10025;Troyes;48.2747672;4.0725464 +0060042N;LycĂ©e Professionnel Les Palmiers;15 Avenue Banco;06300;Nice;43.6985849;7.2919792 +0500028J;LycĂ©e Professionnel Les Sapins;7 Rue Des Courtilles;50207;Coutances;49.0412575;-1.4410444 +0440537V;LycĂ©e Professionnel Les Savarieres;5 Avenue De Glinde;44230;St Sebastien Sur Loire;47.211077;-1.4905961 +0860010U;LycĂ©e Professionnel Les Terres Rouges;Rue Jean Moulin;86400;Civray;46.134811;0.284259 +0550004F;LycĂ©e Professionnel Ligier Richier;3 R Du Stade;55013;Bar Le Duc;48.7757163;5.1677519 +0772225A;LycĂ©e Professionnel Lino Ventura;Avenue Marcel Pagnol;77834;Ozoir La Ferriere;48.7607611;2.6924768 +0381602K;LycĂ©e Professionnel L'odyssee;5 Boulevard Des Colleges;38232;Pont De Cheruy;45.7571727;5.1765032 +0270027H;LycĂ©e Professionnel Louis Aragon;23 Rue Vieille D'eragny;27140;Gisors;49.279774;1.776579 +0440352U;LycĂ©e Professionnel Louis Armand;Bd Jean De Grandmaison;44270;Machecoul;46.9910112;-1.8193584 +0592712C;LycĂ©e Professionnel Louis Armand;Rue Mermoz;59571;Jeumont;50.2945453;4.1134706 +0731249L;LycĂ©e Professionnel Louis Armand;321 Rue Du Grand Champ;73020;Chambery;45.5947932;5.9203139 +0860052P;LycĂ©e Professionnel Louis Armand;63 Rue De La Bugellerie;86022;Poitiers;46.5974421;0.3312595 +0910756C;LycĂ©e Professionnel Louis Armand;9 Rue Pierre De Coubertin;91330;Yerres;48.7130134;2.4793774 +0130033R;LycĂ©e Professionnel Louis Bleriot;8 Bd De La Liberation;13721;Marignane;43.4192168;5.251698 +0592611T;LycĂ©e Professionnel Louis Bleriot;Rue Gauthier;59407;Cambrai;50.162795;3.254045 +0780273Y;LycĂ©e Professionnel Louis Bleriot;Rue Leo Lagrange;78197;Trappes;48.777248;1.981663 +0911401D;LycĂ©e Professionnel Louis Bleriot;2 Avenue Des Meuniers;91154;Etampes;48.4369719;2.1466572 +0921505L;LycĂ©e Professionnel Louis Dardenne;25 Rue Louis Dardenne;92170;Vanves;48.8200923;2.287475 +0400004M;LycĂ©e Professionnel Louis Darmante;23 Rue Jean Baptiste Gabarra;40130;Capbreton;43.6409194;-1.4311885 +0640012R;LycĂ©e Professionnel Louis De Foix;4 Av Jean Rostand;64103;Bayonne;43.4849353;-1.4754015 +0160119T;LycĂ©e Professionnel Louis Delage;245 Avenue Victor Hugo;16100;Cognac;45.6873156;-0.310592 +9710052E;LycĂ©e Professionnel Louis Delgres;Rue Amedee Fengarol;97160;Le Moule;0.0;0.0 +0250002S;LycĂ©e Professionnel Louis Garnier;29 Rue Des Cantons;25404;Audincourt;47.4827916;6.8496192 +0230027E;LycĂ©e Professionnel Louis Gaston Roussillat;La Valette;23320;St Vaury;46.220712;1.76194 +0881370L;LycĂ©e Professionnel Louis Geisler;3 Rue De La Belle Orge;88110;Raon L Etape;48.4037543;6.8345632 +0920163C;LycĂ©e Professionnel Louis Girard;85 Rue Louis Girard;92240;Malakoff;48.8121182;2.2822567 +0350032V;LycĂ©e Professionnel Louis Guilloux;76 Av Des Buttes De Coesmes;35703;Rennes;48.1249592;-1.6332938 +0590187H;LycĂ©e Professionnel Louis Loucheur;8 Boulevard De Lyon;59059;Roubaix;50.6776818;3.1752082 +0040011R;LycĂ©e Professionnel Louis Martin Bret;Allee Du Parc;04100;Manosque;43.827348;5.7848689 +0251002D;LycĂ©e Professionnel Louis Pergaud;91 93 Boulevard Blum;25022;Besancon;47.263734;6.0453694 +0590037V;LycĂ©e Professionnel Louise De Bettignies;Boulevard Paul Bezin;59407;Cambrai;50.1724242;3.2399915 +0690046A;LycĂ©e Professionnel Louise Labe;65 Boulevard Yves Farge;69007;Lyon 07;45.7396294;4.8310855 +0161003D;LycĂ©e Professionnel Louise Michel;Rue Villebois Mareuil;16700;Ruffec;46.0266253;0.1927592 +0380099B;LycĂ©e Professionnel Louise Michel;30 Rue Louise Michel;38037;Grenoble;45.1687882;5.7081825 +0440063E;LycĂ©e Professionnel Louis-jacques Goussier;20 Rue Du Chateau De Reze;44400;Reze;47.1806714;-1.5600159 +0783214V;LycĂ©e Professionnel Lucien Rene Duchesne;49 Av Maurice De Hirsch;78170;La Celle St Cloud;48.8413881;2.1185136 +0491646Y;LycĂ©e Professionnel Ludovic Menard;24 Place Des Tilleuls;49800;Trelaze;47.4464427;-0.504172 +0700038L;LycĂ©e Professionnel Luxembourg;16 Pl Du 11e Chasseurs;70014;Vesoul;47.6206224;6.1507974 +9840267T;LycĂ©e Professionnel LycĂ©e Professionnel De Faaa;Faaa;98704;Faaa;0.0;0.0 +0595856V;LycĂ©e Professionnel LycĂ©e Professionnel De L'yser;18 Bis Route D'esquelbecq;59726;Wormhout;50.8568096;2.4376592 +9840341Y;LycĂ©e Professionnel LycĂ©e Professionnel De Mahina;Mahina;98709;Mahina;-41.2713267;174.9088245 +9840166H;LycĂ©e Professionnel LycĂ©e Professionnel D'uturoa;Uturoa;98735;Uturoa;-16.7331706;-151.4403326 +9840160B;LycĂ©e Professionnel LycĂ©e Professionnel St Joseph;Papeete;98718;Punaauia;-17.5753232;-149.6059799 +9840019Y;LycĂ©e Professionnel LycĂ©e Protestant Tuteao A Vaih;Uturoa;98735;Uturoa;-16.7331706;-151.4403326 +0600016S;LycĂ©e Professionnel M.grenet (industriel);13 Avenue De Huy;60321;Compiegne;49.4025055;2.8188505 +0600017T;LycĂ©e Professionnel M.grenet (mixte);13 Avenue De Huy;60321;Compiegne;49.4025055;2.8188505 +0690047B;LycĂ©e Professionnel Magenta;64 66 Rue Magenta;69100;Villeurbanne;45.7660861;4.8714756 +0060043P;LycĂ©e Professionnel Magnan;34 Rue Auguste Renoir;06000;Nice;43.693971;7.2481912 +0720013Y;LycĂ©e Professionnel Mal Leclerc Hauteclocque;Rue Du Grand Douai;72500;Chateau Du Loir;47.6925943;0.4146083 +0860022G;LycĂ©e Professionnel Marc Godrie;19 Rue Des Meures;86206;Loudun;47.0112905;0.0893771 +0692418D;LycĂ©e Professionnel Marc Seguin;20 Boulevard Marcel Sembat;69694;Venissieux;45.7159355;4.8819736 +0331668J;LycĂ©e Professionnel Marcel Dassault;3 Rue Chateaubriand;33695;Merignac;44.8051719;-0.6258497 +0750788Z;LycĂ©e Professionnel Marcel Deprez;39 Rue De La Roquette;75011;Paris 11;48.8552481;2.3736701 +0880007E;LycĂ©e Professionnel Marcel Goulette;3 Rue Etienne Simard;88132;Charmes;48.3785984;6.2980364 +0610004Y;LycĂ©e Professionnel Marcel Mezen;25 Rue Marcel Mezen;61041;Alencon;48.4296677;0.1044785 +0870730W;LycĂ©e Professionnel Marcel Pagnol;103 Rue De Feytiat;87039;Limoges;45.8201238;1.2850476 +0930129K;LycĂ©e Professionnel Marcel Pagnol;1 Rue Jules Guesde;93140;Bondy;48.9041305;2.4741275 +0760146W;LycĂ©e Professionnel Marcel Sembat;128 Rue Leon Salva;76300;Sotteville Les Rouen;49.4152133;1.0855593 +0010021T;LycĂ©e Professionnel Marcelle Parde;47 Avenue Alsace Lorraine;01011;Bourg En Bresse;46.2019083;5.2208402 +0611157B;LycĂ©e Professionnel Marechal Leclerc;30 Rue Jean Henri Fabre;61014;Alencon;48.4113332;0.0934634 +0840041N;LycĂ©e Professionnel Maria Casares;1 Rue Des Bavardages;84082;Avignon;43.9356519;4.8292506 +0753350J;LycĂ©e Professionnel Maria Deraismes;19 Rue Maria Deraismes;75017;Paris 17;48.8947945;2.3274455 +0300041G;LycĂ©e Professionnel Marie Curie;Route De Florac;30270;St Jean Du Gard;44.1140867;3.8718827 +0600062S;LycĂ©e Professionnel Marie Curie;Boulevard Pierre De Coubertin;60180;Nogent Sur Oise;49.266627;2.464235 +0630023H;LycĂ©e Professionnel Marie Curie;19 Boulevard Ambroise Brugiere;63039;Clermont Ferrand;45.7894827;3.1182095 +0690109U;LycĂ©e Professionnel Marie Curie;64 Boulevard Eugene Reguillon;69100;Villeurbanne;45.7614463;4.8962541 +0100027A;LycĂ©e Professionnel Marie De Champagne;2 Avenue Marie De Champagne;10026;Troyes;48.3040593;4.0617662 +0750776L;LycĂ©e Professionnel Marie Laurencin;114 Quai De Jemmapes;75010;Paris 10;48.8746642;2.3635341 +0540061Y;LycĂ©e Professionnel Marie Marvingt;8 Rue Jean Moulin;54510;Tomblaine;48.6960232;6.2152112 +0171432P;LycĂ©e Professionnel Maritime;Avenue Du Marechal Juin;17022;La Rochelle;46.1575289;-1.180007 +0561486L;LycĂ©e Professionnel Maritime;38 Avenue Louis Bougo;56410;Etel;47.6612096;-3.2015002 +9720787Y;LycĂ©e Professionnel Maritime;Quartier Beausejour;97220;La Trinite;0.0;0.0 +0641770B;LycĂ©e Professionnel Maritime;Av Eugene Corre Qu De L Untxin;64500;Ciboure;43.387537;-1.675432 +0762735K;LycĂ©e Professionnel Maritime Anita Conti;84 Quai Guy De Maupassant;76402;Fecamp;49.763033;0.3711007 +7200118R;LycĂ©e Professionnel Maritime Batterie Les Turquines;Batterie Les Turquines;20200;Bastia;42.697283;9.450881 +0561861U;LycĂ©e Professionnel Maritime Centre Francois Toullec;10 Rue Francois Toullec;56100;Lorient;47.7323195;-3.3792775 +0623903F;LycĂ©e Professionnel Maritime De Boulogne Le Portel;6 Rue Georges Honore;62480;Le Portel;50.7190753;1.5860061 +0442092K;LycĂ©e Professionnel Maritime Des Pays De La Loire;Rue Du Port Boyer;44315;Nantes;47.2410135;-1.5374604 +0291107W;LycĂ©e Professionnel Maritime Du Guilvinec;Zone D'activites De Kervarc'h;29730;Treffiagat;47.807191;-4.261599 +9741098A;LycĂ©e Professionnel Maritime E.a.m Le Port;1 Rue De La Poste;97821;Le Port;-20.9341273;55.291486 +0501599S;LycĂ©e Professionnel Maritime Et Aquacole;Rue Matignon;50103;Cherbourg Octeville;49.63774;-1.618745 +0341699M;LycĂ©e Professionnel Maritime Paul Bousquet;Rue Des Cormorans;34207;Sete;43.4192489;3.6729485 +0221624W;LycĂ©e Professionnel Maritime Pierre Loti;1 Rue Pierre Loti;22501;Paimpol;48.8017255;-3.0192327 +0351914R;LycĂ©e Professionnel Maritime Public;116 Bd Des Tallards;35403;St Malo;48.64063;-2.0087017 +0070031W;LycĂ©e Professionnel Marius Bouvier;Avenue Lamastre;07301;Tournon Sur Rhone;45.068112;4.799918 +0570077Z;LycĂ©e Professionnel Maryse Bastie;Rue Du Tivoli;57700;Hayange;49.3347331;6.0763366 +0350062C;LycĂ©e Professionnel Maupertuis;Rue Pierre De Coubertin;35407;St Malo;48.6400752;-2.0058975 +0590133Z;LycĂ©e Professionnel Maurice Duhamel;1079 Rue Guy Mocquet;59373;Loos;50.6025679;3.0118909 +0132319A;LycĂ©e Professionnel Maurice Genevoix;Av Du General De Gaulle;13700;Marignane;43.4128456;5.2048951 +0030059Y;LycĂ©e Professionnel Maurice Guyot;Rue Ernest Montuses;03105;Montlucon;46.3228476;2.6201222 +0623864N;LycĂ©e Professionnel Maximilien De Robespierre;89 Rue Leon Blum;62307;Lens;50.4452423;2.7877082 +0711322B;LycĂ©e Professionnel Metiers De L Automobile;1 Rue De La Manutention;71321;Chalon Sur Saone;46.7792864;4.8435194 +0750784V;LycĂ©e Professionnel Metiers De L'ameublement;9 Rue Pierre Bourdan;75012;Paris 12;48.8468966;2.3914855 +0570144X;LycĂ©e Professionnel Metiers Du Batiment Et T.p;154 Chemin De Blory;57950;Montigny Les Metz;49.0873538;6.1678431 +0440034Y;LycĂ©e Professionnel Michelet;41 Boulevard Michelet;44322;Nantes;47.2343621;-1.556604 +0941298A;LycĂ©e Professionnel Michelet;1 Rue Michelet;94120;Fontenay Sous Bois;48.8518993;2.4732442 +0270051J;LycĂ©e Professionnel Modeste Leroy;32 Rue Pierre Brossolette;27016;Evreux;49.0216265;1.1618627 +0640080P;LycĂ©e Professionnel Moliere;11 Rue Moliere;64301;Orthez;43.4800842;-0.7688565 +0730050H;LycĂ©e Professionnel Monge;1 Avenue Marius Berroir;73000;Chambery;45.5660699;5.9341975 +0801194N;LycĂ©e Professionnel Montaigne;3 Rue Montaigne;80084;Amiens;49.9115347;2.3217034 +0390021L;LycĂ©e Professionnel Montciel;1 Avenue De Montciel;39016;Lons Le Saunier;46.6724435;5.5427757 +0260116K;LycĂ©e Professionnel Montesquieu;2 Rue De Montesquieu;26000;Valence;44.9384823;4.9256564 +0250064J;LycĂ©e Professionnel Montjoux;25 Av Du Cdt Marceau;25010;Besancon;47.2490074;6.016123 +0910755B;LycĂ©e Professionnel Nadar;42bis Rue Charles Mory;91210;Draveil;48.6831045;2.4326334 +0610027Y;LycĂ©e Professionnel Napoleon;15 Rue Des Sports;61306;L Aigle;48.7582148;0.6363548 +0330032F;LycĂ©e Professionnel Nicolas Bremontier;152 Cours De L Yser;33077;Bordeaux;44.823338;-0.5706489 +0623463C;LycĂ©e Professionnel Normandie-niemen;Avenue Normandie-niemen;62228;Calais;50.9523426;1.9037152 +0750796H;LycĂ©e Professionnel Octave Feuillet;9 Rue Octave Feuillet;75016;Paris 16;48.8613442;2.2723331 +0330102G;LycĂ©e Professionnel Odilon Redon;R Maquis Vignes Oudides;33250;Pauillac;45.1907714;-0.7495194 +0441656L;LycĂ©e Professionnel Pablo Neruda;Rue Pablo Neruda;44340;Bouguenais;47.16973;-1.623866 +0240028A;LycĂ©e Professionnel Pablo Picasso;64 Av Georges Pompidou;24004;Perigueux;45.1924021;0.7267429 +0620255R;LycĂ©e Professionnel Pablo Picasso;2 Boulevard Anatole France;62210;Avion;50.4005382;2.8354531 +0693200D;LycĂ©e Professionnel Pablo Picasso;12 Che De La Cote A Cailloux;69700;Givors;45.6012034;4.7609173 +0830059N;LycĂ©e Professionnel Parc St Jean;Place Du 4 Septembre;83059;Toulon;43.1230487;5.9530768 +0320040V;LycĂ©e Professionnel Pardailhan;Chemin De Baron;32008;Auch;43.6546824;0.5749589 +9740472V;LycĂ©e Professionnel Patu De Rosemont;72 Rue Joseph Hubert;97470;St Benoit;0.0;0.0 +0910628N;LycĂ©e Professionnel Paul Belmondo;23 Av De La Division Leclerc;91290;Arpajon;48.5966104;2.2461236 +0640013S;LycĂ©e Professionnel Paul Bert;73 Rue Bourgneuf;64115;Bayonne;43.4908724;-1.471528 +0941355M;LycĂ©e Professionnel Paul Bert;1 Rue Du Gue Aux Aurochs;94700;Maisons Alfort;48.8155654;2.4242767 +0330114V;LycĂ©e Professionnel Paul Broca;7 Avenue De Verdun;33220;Ste Foy La Grande;44.8382054;0.2152357 +0141276L;LycĂ©e Professionnel Paul Cornu;9 Rue Paul Cornu;14107;Lisieux;49.15711;0.2224269 +0570072U;LycĂ©e Professionnel Paul Dassenoy;Route De Conthil;57340;Morhange;48.9118442;6.6439595 +0490801E;LycĂ©e Professionnel Paul Emile Victor;101 Rue Des Roses;49243;Avrille;47.5010511;-0.5887594 +0450786K;LycĂ©e Professionnel Paul Gauguin;10 Av De La Rech Scientifique;45071;Orleans;47.8395647;1.9428858 +0790043T;LycĂ©e Professionnel Paul Guerin;19 Rue Des Fiefs;79004;Niort;46.3145227;-0.443402 +0050008G;LycĂ©e Professionnel Paul Heraud;25 Chemin De Bonne;05000;Gap;44.5671853;6.0801577 +0730900G;LycĂ©e Professionnel Paul Heroult;307 Avenue Du Mont Cenis;73302;St Jean De Maurienne;45.2725564;6.3502605 +9710418C;LycĂ©e Professionnel Paul Lacave;Avenue Germain St-ruff;97130;Capesterre Belle Eau;16.05;-61.57 +0300011Z;LycĂ©e Professionnel Paul Langevin;21 Rue De La Redoute;30301;Beaucaire;43.8118169;4.6414128 +0593495D;LycĂ©e Professionnel Paul Langevin;Rue Paul Langevin;59119;Waziers;50.387032;3.102779 +0600061R;LycĂ©e Professionnel Paul Langevin;3 Avenue Montaigne;60009;Beauvais;49.414897;2.1063143 +0921677Y;LycĂ©e Professionnel Paul Langevin;9 Rue Paul Langevin;92000;Nanterre;48.890582;2.184257 +9740934X;LycĂ©e Professionnel Paul Langevin;6 Allee Des Hibiscus;97480;St Joseph;-21.3813367;55.6096619 +0540037X;LycĂ©e Professionnel Paul Lapie;6 Avenue Du Docteur Kahn;54301;Luneville;48.5978471;6.5103348 +0540082W;LycĂ©e Professionnel Paul Louis Cyffle;1 Rue Cyffle;54000;Nancy;48.6867685;6.1812641 +0921625S;LycĂ©e Professionnel Paul Painleve;5 Rue De La Montagne;92400;Courbevoie;48.8955744;2.2588753 +0060028Y;LycĂ©e Professionnel Paul Valery;1 Avenue Saint Jacques;06500;Menton;43.7834263;7.511873 +0080838T;LycĂ©e Professionnel Paul Verlaine;Rue Du Docteur Gobinet;08305;Rethel;49.5167514;4.3704596 +0130012T;LycĂ©e Professionnel Perdiguier;Che Des Moines Qua Du Trebon;13200;Arles;43.6017739;4.6243751 +9720091S;LycĂ©e Professionnel Petit Manoir;Quartier Petit Manoir;97286;Le Lamentin;0.0;0.0 +9830306R;LycĂ©e Professionnel Petro Attiti;1 Rue Teillard De Chardin;98849;Noumea;-22.2758;166.458 +0332345V;LycĂ©e Professionnel Philadelphe De Gerde;3, Allee Philadelphe De Gerde;33600;Pessac;44.7870737;-0.6520039 +0332346W;LycĂ©e Professionnel Philippe Cousteau;Rue Arnaudin;33240;St Andre De Cubzac;44.9912798;-0.4501201 +0580020G;LycĂ©e Professionnel Pierre Beregovoy;56 Rue Gambetta;58600;Fourchambault;47.019317;3.0831499 +0420021V;LycĂ©e Professionnel Pierre Coton;Le Bourg;42510;Neronde;45.838012;4.238368 +0620189U;LycĂ©e Professionnel Pierre De Coubertin;320 Boulevard Du 8 Mai;62225;Calais;50.958627;1.837843 +0770932V;LycĂ©e Professionnel Pierre De Coubertin;Chaussee De Paris;77100;Meaux;48.9559126;2.869815 +0421691K;LycĂ©e Professionnel Pierre Desgranges;32 Rue Des Bullieux;42166;Andrezieux Boutheon;45.5374319;4.2959439 +0170031S;LycĂ©e Professionnel Pierre Doriole;221 Avenue De Perigny;17012;La Rochelle;46.1616962;-1.1243029 +0060027X;LycĂ©e Professionnel Pierre Et Marie Curie;353 Av Du Doyen Jean Lepine;06500;Menton;43.7776606;7.4846068 +0500066A;LycĂ©e Professionnel Pierre Et Marie Curie;377 Rue De L Exode;50010;St Lo;49.107496;-1.0893717 +0530040F;LycĂ©e Professionnel Pierre Et Marie Curie;1 Rue Branly;53204;Chateau Gontier;47.8341556;-0.6994932 +0570051W;LycĂ©e Professionnel Pierre Et Marie Curie;21 Rue Des Vosges;57804;Freyming Merlebach;49.153717;6.8188013 +0590015W;LycĂ©e Professionnel Pierre Et Marie Curie;1 Rue Du Foyer;59620;Aulnoye Aymeries;50.2026966;3.835739 +0640079N;LycĂ©e Professionnel Pierre Et Marie Curie;Avenue Pierre Angot;64150;Mourenx;43.3703832;-0.6251249 +0760013B;LycĂ©e Professionnel Pierre Et Marie Curie;33 Rue Du Calvaire;76210;Bolbec;49.5698937;0.4817532 +0890053Z;LycĂ©e Professionnel Pierre Et Marie Curie;1 Place Walesa;89094;Sens;48.20065;3.28268 +0132276D;LycĂ©e Professionnel Pierre Latecoere;Avenue Des Bolles;13800;Istres;43.5039779;4.994962 +0752388N;LycĂ©e Professionnel Pierre Lescot;35 Rue Des Bourdonnais;75001;Paris 01;48.8602998;2.3453363 +0050027C;LycĂ©e Professionnel Pierre Mendes France;Place Des Aires;05400;Veynes;44.5309937;5.7990979 +0650035K;LycĂ©e Professionnel Pierre Mendes France;Rue Du College;65501;Vic En Bigorre;43.388411;0.056435 +0801514L;LycĂ©e Professionnel Pierre Mendes France;Route De Saint Denis;80201;Peronne;49.9361685;2.9435387 +0880013L;LycĂ©e Professionnel Pierre Mendes France;100 Rue Du Shah De Perse;88141;Contrexeville;48.1816011;5.8950657 +0911578W;LycĂ©e Professionnel Pierre Mendes France;Av De L Aunette;91133;Ris Orangis;48.6393295;2.4105804 +0620167V;LycĂ©e Professionnel Pierre Mendes-france;Rue Antoine De Saint Exupery;62166;St Pol Sur Ternoise;51.024809;2.343421 +0620192X;LycĂ©e Professionnel Pierre Mendes-france;Rue De Saint Omer;62701;Bruay La Buissiere;50.4874136;2.5382001 +0951090U;LycĂ©e Professionnel Pierre Mendes-france;1 Rue De Goussainville;95400;Villiers Le Bel;49.0047221;2.4173569 +0390024P;LycĂ©e Professionnel Pierre Vernotte;6 Route De St Laurent;39261;Moirans En Montagne;46.4348496;5.7248544 +0250013D;LycĂ©e Professionnel Pierre-adrien Paris;8 Rue Mercator;25000;Besancon;47.2604676;5.9960551 +0160048R;LycĂ©e Professionnel Pierre-andre Chabanne;28 Rue Bir Hakeim;16260;Chasseneuil Sur Bonnieure;45.8235254;0.453489 +0590005K;LycĂ©e Professionnel Pierre-joseph Fontaine;2 Av Leo Lagrange;59416;Anzin;50.3696507;3.4916736 +0592610S;LycĂ©e Professionnel Pierre-joseph Laurent;128 Rue Edmond Laudeau;59580;Aniche;50.3265557;3.2625615 +0101122R;LycĂ©e Professionnel Pigier;40 Place Jean Jaures;10000;Troyes;48.2947188;4.0701812 +9720844K;LycĂ©e Professionnel Place D Armes;Place D Armes;97232;Le Lamentin;14.6166147;-60.9887419 +0590098L;LycĂ©e Professionnel Placide Courtoy;Rue Pierre Curie;59330;Hautmont;50.255989;3.905332 +0130054N;LycĂ©e Professionnel Poinso-chapuis;49 Traverse Parangon;13272;Marseille 08;43.2427647;5.3806897 +9720005Y;LycĂ©e Professionnel Pointe Des Negres;Rue Marie Therese Gertrude;97261;Fort De France;14.6040646;-61.084565 +0700882D;LycĂ©e Professionnel Pontarcher;Place Jacques Brel;70014;Vesoul;47.6354117;6.1686975 +0240039M;LycĂ©e Professionnel Porte D Aquitaine;15 Rue A Bonneau;24800;Thiviers;45.4243846;0.9267852 +0271319M;LycĂ©e Professionnel Porte De Normandie;830 Chemin Des Poissonniers;27130;Verneuil Sur Avre;48.7405634;0.9156189 +0740031G;LycĂ©e Professionnel Porte Des Alpes;26 Rue De La Curdy;74151;Rumilly;45.8644993;5.9406325 +0380101D;LycĂ©e Professionnel Portes De L'oisans;Avenue Aristide Briand;38220;Vizille;45.067413;5.769686 +0240048X;LycĂ©e Professionnel Pre De Cordy;Av. De La Dordogne;24200;Sarlat La Caneda;44.8754955;1.2126435 +0390056Z;LycĂ©e Professionnel Pre Saint Sauveur;Cite Scolaire Pre St Sauveur;39201;St Claude;46.387405;5.8677659 +0430103D;LycĂ©e Professionnel PrivĂ©;1 Place Neron;43120;Monistrol Sur Loire;45.2946516;4.1708988 +9711139L;LycĂ©e Professionnel PrivĂ© 3ec;1108 Imm.capt Grammont;97142;Les Abymes;16.27395;-61.502615 +0700947Z;LycĂ©e Professionnel PrivĂ© Agricole Sainte Marie;13 Rue Victor Hugo;70106;Gray;47.4436381;5.5914217 +0311200L;LycĂ©e Professionnel PrivĂ© Airbus - Toulouse;57 Chemin Du Sang De Serp;31060;Toulouse;43.6158658;1.4282595 +0382093U;LycĂ©e Professionnel PrivĂ© Alpes Sud-isere;Rond Point Du Villaret-susville;38350;Susville;44.9166667;5.7811111 +0030112F;LycĂ©e Professionnel PrivĂ© Anna Rodier;39 Cours Jean Jaures;03008;Moulins;46.5669568;3.3349455 +0631050Z;LycĂ©e Professionnel PrivĂ© Anna Rodier;54 Boulevard Jean Jaures;63000;Clermont Ferrand;45.7679299;3.0798386 +0570283Y;LycĂ©e Professionnel PrivĂ© Anne De Mejanes;3 Rue Goussaud;57000;Metz;49.1235079;6.173823 +0420987V;LycĂ©e Professionnel PrivĂ© Arago;28 Rue Arago;42300;Roanne;46.0292922;4.0690676 +0920979P;LycĂ©e Professionnel PrivĂ© Atelier App.pte Mecan.;19 Rue Victor Hugo;92130;Issy Les Moulineaux;48.8264404;2.2758007 +0690652J;LycĂ©e Professionnel PrivĂ© Ateliers Appren De L'industrie;148 Avenue Franklin Roosevelt;69120;Vaulx En Velin;45.7546702;4.9351651 +9710879D;LycĂ©e Professionnel PrivĂ© Bel Air;Moudong Nord;97122;Baie Mahault;0.0;0.0 +0312059V;LycĂ©e Professionnel PrivĂ© Billieres;18 Rue Du 14 Juillet;31100;Toulouse;43.5910132;1.414594 +0450764L;LycĂ©e Professionnel PrivĂ© Blanche De Castille;;45303;Pithiviers;48.156984;2.192561 +9710083N;LycĂ©e Professionnel PrivĂ© Boc-calmet;Rue Paul Lacave- Assainissement;97110;Pointe A Pitre;16.241111;-61.533056 +0381806G;LycĂ©e Professionnel PrivĂ© Bordier;26 Rue Prosper Merimee;38100;Grenoble;45.1737355;5.7260681 +0292139T;LycĂ©e Professionnel PrivĂ© Brest - Rive Droite - Javouhey;4 Rue Des Remparts;29238;Brest;48.3832192;-4.5023223 +0131484T;LycĂ©e Professionnel PrivĂ© Brise Lames;Route De La Vierge;13500;Martigues;43.4170127;5.0437853 +0131463V;LycĂ©e Professionnel PrivĂ© Cabucelle (la);16 Boulevard Denis Papin;13015;Marseille 15;43.3342857;5.3626747 +0312063Z;LycĂ©e Professionnel PrivĂ© Castelnouvel;Castelnouvel;31490;Leguevin;43.594447;1.254584 +9741308D;LycĂ©e Professionnel PrivĂ© Catholique De La Montagne;Chemin Piton Tresor Pk7;97417;St Denis;48.936181;2.357443 +0131474G;LycĂ©e Professionnel PrivĂ© Caucadis;Boulevard Alfred Casile;13127;Vitrolles;43.4478245;5.2426452 +0941407U;LycĂ©e Professionnel PrivĂ© Ce 3p;5 Rue Rene Robin;94200;Ivry Sur Seine;48.8137588;2.3781028 +0740152N;LycĂ©e Professionnel PrivĂ© Cecam;Chemin De Beauregard;74490;St Jeoire;46.1429375;6.4595274 +0131415T;LycĂ©e Professionnel PrivĂ© Celony;4 Bis Av Delattre De Tassigny;13090;Aix En Provence;43.5303056;5.4383758 +0241129X;LycĂ©e Professionnel PrivĂ© Cent.tech.coif.esthetique;49 Rue Candillac;24100;Bergerac;44.8507187;0.4855329 +0300028T;LycĂ©e Professionnel PrivĂ© Centre Cevenol;3 Quai Boissier De Sauvages;30100;Ales;44.12714;4.0767327 +0240092V;LycĂ©e Professionnel PrivĂ© Chambre Com Et Industrie;Zone Industrielle Av H Deluc;24750;Boulazac;45.180548;0.768949 +0541998D;LycĂ©e Professionnel PrivĂ© Charles De Foucauld;1 Rue Jeannot;54000;Nancy;48.6894923;6.1881706 +0672602P;LycĂ©e Professionnel PrivĂ© Charles De Foucauld;Allee D'athenes;67306;Schiltigheim;48.6075759;7.7099709 +0133276R;LycĂ©e Professionnel PrivĂ© Charles Peguy;102 Rue Sylvabelle;13006;Marseille 06;43.2882408;5.3748305 +0131445A;LycĂ©e Professionnel PrivĂ© Charlotte Grawitz;20 Chemin Chateau Gombert;13013;Marseille 13;43.3350871;5.4206869 +0381758E;LycĂ©e Professionnel PrivĂ© Charmilles;15 Rue Montesquieu;38100;Grenoble;45.1762181;5.7280364 +0932424E;LycĂ©e Professionnel PrivĂ© Chne Or;17 Rue Du Clos Benard;93300;Aubervilliers;48.9114654;2.3861326 +0541370W;LycĂ©e Professionnel PrivĂ© Claude Daunot;10 Boulevard Clemenceau;54052;Nancy;48.6779501;6.1805671 +0133305X;LycĂ©e Professionnel PrivĂ© Clovis Hugues;1 Rue Fernand Dol;13100;Aix En Provence;43.5260677;5.452697 +0754165V;LycĂ©e Professionnel PrivĂ© Coiffure;15-17 Rue Des Fillettes;75018;Paris 18;48.8946669;2.3633961 +0771190A;LycĂ©e Professionnel PrivĂ© Coiffure Et Esthetique;6 Rue Jean Jaures;77670;St Mammes;48.3866661;2.8150085 +0440267B;LycĂ©e Professionnel PrivĂ© Coiffure P.masson;12 Rue D Alger;44100;Nantes;47.210504;-1.5676183 +0601164P;LycĂ©e Professionnel PrivĂ© Croiset;7 Avenue De Joinville;60500;Chantilly;49.1761667;2.4601513 +0470821M;LycĂ©e Professionnel PrivĂ© Ctr.etu.fem.rur.l Ermitage;304 Avenue Amouroux;47000;Agen;44.2118115;0.6187838 +0442071M;LycĂ©e Professionnel PrivĂ© Daniel Brottier;Chemin Du Couvent;44340;Bouguenais;47.187338;-1.5866796 +0762285W;LycĂ©e Professionnel PrivĂ© Daniel Brottier;11 Impasse Du Chateau;76660;Smermesnil;49.8063368;1.4206513 +0691724Z;LycĂ©e Professionnel PrivĂ© De Coiffure;22 Rue D'algerie;69001;Lyon 01;45.767637;4.8327698 +0691723Y;LycĂ©e Professionnel PrivĂ© De Coiffure De Lyon;28 Rue Valentin Couturier;69004;Lyon 04;45.7763552;4.8274152 +9711080X;LycĂ©e Professionnel PrivĂ© De Coiffure Et D'esthetique;Lot 76 Centre Commercial;97139;Les Abymes;16.27395;-61.502615 +0311190A;LycĂ©e Professionnel PrivĂ© De Coiffure Skhole D'art;55 Avenue Louis Breguet;31400;Toulouse;43.5772349;1.4794166 +0820885S;LycĂ©e Professionnel PrivĂ© De Coiffure Skhole D'art;3 Rue Bataille De Dunkerque;82000;Montauban;44.0068309;1.3461514 +0280687V;LycĂ©e Professionnel PrivĂ© De Couasnon;37 Rue De Moronval;28100;Dreux;48.7329442;1.3867752 +0441791H;LycĂ©e Professionnel PrivĂ© De L Erdre;13 Rue Du Gal Leclerc;44390;Nort Sur Erdre;47.4389938;-1.4992748 +0593020M;LycĂ©e Professionnel PrivĂ© De La Coiffure;68 Rue Saint Etienne;59800;Lille;50.6361624;3.0595327 +0761353H;LycĂ©e Professionnel PrivĂ© De La Coiffure A. Pourriere;24 Rue D'herbouville;76000;Rouen;49.4489961;1.0861707 +0331547C;LycĂ©e Professionnel PrivĂ© De La Cote D Argent;46 Bis Avenue De La Liberation;33380;Biganos;44.6407598;-0.9743709 +0573270V;LycĂ©e Professionnel PrivĂ© De La Salle;2 Rue St Maximin;57070;Metz;49.1048183;6.1935928 +0420982P;LycĂ©e Professionnel PrivĂ© Des Collines;4 Rue Ferdinand Buisson;42800;Rive De Gier;45.5261265;4.6165582 +0561606S;LycĂ©e Professionnel PrivĂ© Des Metiers N.dame De La Paix;Beg Er Lann;56275;Ploemeur;47.7506666;-3.3973939 +0061462G;LycĂ©e Professionnel PrivĂ© Don Bosco;40 Place Don Bosco;06046;Nice;43.7086006;7.2813297 +0131466Y;LycĂ©e Professionnel PrivĂ© Don Bosco;78 Rue Stanislas Torrents;13006;Marseille 06;43.2855609;5.3795529 +0530073S;LycĂ©e Professionnel PrivĂ© Don Bosco;18 Boulevard Anatole France;53102;Mayenne;48.295993;-0.6158636 +0622211S;LycĂ©e Professionnel PrivĂ© Du Calaisis;71 73 Rue Chantilly;62100;Calais;50.9435057;1.8553408 +0070113K;LycĂ©e Professionnel PrivĂ© Du Sacre Coeur;7 Avenue De La Gare;07301;Tournon Sur Rhone;45.0637796;4.834587 +0720823D;LycĂ©e Professionnel PrivĂ© Du Sacre Coeur De Pontlieu;30 Rue Des Sapins;72100;Le Mans;47.9798872;0.2105683 +0022109G;LycĂ©e Professionnel PrivĂ© Du Val De Serre;10 Rue De La Halle;02270;Pouilly Sur Serre;49.6813163;3.587762 +0741285V;LycĂ©e Professionnel PrivĂ© E.c.a.;2 Rue Des Carillons;74942;Annecy Le Vieux;45.914268;6.1419587 +0760143T;LycĂ©e Professionnel PrivĂ© Ecole Industrielle;50 Rue Meridienne;76100;Rouen;49.4256007;1.0837566 +0131434N;LycĂ©e Professionnel PrivĂ© Ecole Libre De Metiers;24 Rue Des Bons Enfants;13006;Marseille 06;43.2908998;5.388285 +0132193N;LycĂ©e Professionnel PrivĂ© Edmond Rostand;114 Rue Edmond Rostand;13006;Marseille 06;43.2821817;5.3828274 +0440255N;LycĂ©e Professionnel PrivĂ© Encia;6 Rue Crebillon;44003;Nantes;47.214125;-1.5596911 +0011258M;LycĂ©e Professionnel PrivĂ© Ensemble Scolaire Lamartine;41 Rue Georges Girerd;01302;Belley;45.7612095;5.6848602 +0941915W;LycĂ©e Professionnel PrivĂ© Espace Beaute Thalgo;16 Rue D'alsace Lorraine;94100;St Maur Des Fosses;48.8054972;2.4744603 +0754184R;LycĂ©e Professionnel PrivĂ© Eugene Napoleon;254 Rue Du Fg Saint Antoine;75012;Paris 12;48.8489028;2.3916976 +9710827X;LycĂ©e Professionnel PrivĂ© Faeec;Lot 76 Centre Commercial;97139;Les Abymes;16.27395;-61.502615 +0940823J;LycĂ©e Professionnel PrivĂ© Foyer Des Ptt;36 Avenue Du President Wilson;94234;Cachan;48.7922449;2.3291571 +9830272D;LycĂ©e Professionnel PrivĂ© Francois D Assise (ddec);Lot 25 Pie Era;98870;Bourail;44.5379358;1.6760691 +0381732B;LycĂ©e Professionnel PrivĂ© Francois Verguin;Rte De Sablons;38550;Le Peage De Roussillon;45.3619259;4.7957204 +0280689X;LycĂ©e Professionnel PrivĂ© Francoise D Aubigne;23 Rue Du Marechal Maunoury;28130;Maintenon;48.587838;1.5762147 +9830273E;LycĂ©e Professionnel PrivĂ© Gabriel Rivat (ddec);Village;98824;Pouebo;0.0;0.0 +0920985W;LycĂ©e Professionnel PrivĂ© Georges Guerin;5 Rue Deves;92200;Neuilly Sur Seine;48.8831271;2.2707066 +0610724F;LycĂ©e Professionnel PrivĂ© Giel Don Bosco;Les Cours;61210;Giel Courteilles;48.7569657;-0.1951044 +0592973L;LycĂ©e Professionnel PrivĂ© Gilbert Cesbron;34 Rue Richard Lenoir;59060;Roubaix;50.6978383;3.171796 +0631736V;LycĂ©e Professionnel PrivĂ© Godefroy De Bouillon;14 Rue Godefroy De Bouillon;63037;Clermont Ferrand;45.781122;3.0929055 +0700077D;LycĂ©e Professionnel PrivĂ© Gpe Scolaire De La Compassion;Route De Saint Sulpice;70110;Villersexel;47.551021;6.432211 +0941722L;LycĂ©e Professionnel PrivĂ© Gregor Mendel;205 Rue De Fontenay;94300;Vincennes;48.8488616;2.4272778 +0280691Z;LycĂ©e Professionnel PrivĂ© Guery;5 Rue Des Marais;28000;Chartres;48.4414704;1.4978421 +0592976P;LycĂ©e Professionnel PrivĂ© Helene Boucher;Rue Roger Salengro;59490;Somain;50.3734891;3.2807582 +0133282X;LycĂ©e Professionnel PrivĂ© Henri Leroy;1 Rue Des Ecoles;13230;Port St Louis Du Rhone;43.3867633;4.8031175 +0881623L;LycĂ©e Professionnel PrivĂ© Horticulture Et Paysage;Roville Aux Chenes;88700;Roville Aux Chenes;48.358452;6.630084 +9710878C;LycĂ©e Professionnel PrivĂ© I S F C A;9 Centre Commercial Le Galion;97139;Les Abymes;16.27395;-61.502615 +0442227G;LycĂ©e Professionnel PrivĂ© Ifom;17 Bd Des Martyrs Nantais;44200;Nantes;47.2030171;-1.5454647 +0641664L;LycĂ©e Professionnel PrivĂ© Immac.concept.beau Frene;Bd Edouard Herriot;64051;Pau;43.3050247;-0.3782249 +0520692V;LycĂ©e Professionnel PrivĂ© Immaculee Conception;1 Bis Rue Du Mal De Lattre;52115;St Dizier;48.6385221;4.9486945 +0530904V;LycĂ©e Professionnel PrivĂ© Immaculee Conception;15 Rue Crossardiere;53000;Laval;48.0734452;-0.7680567 +0593027V;LycĂ©e Professionnel PrivĂ© Industries Lilloises;82 Rue Des Meuniers;59000;Lille;50.6221365;3.0581427 +0500132X;LycĂ©e Professionnel PrivĂ© Ingenieur Cachin;4 Rue Ingenieur Cachin;50100;Cherbourg Octeville;49.6396131;-1.617479 +0691875N;LycĂ©e Professionnel PrivĂ© Inst Techn Carrieres Carole;21 Quai Tilsitt;69002;Lyon 02;45.7558718;4.8263561 +0593094T;LycĂ©e Professionnel PrivĂ© Institut Familial;16 Ter Rue Lamartine;59426;Armentieres;50.6866796;2.8758479 +0141866C;LycĂ©e Professionnel PrivĂ© Institut Lemonnier;60 Rue D Herouville;14013;Caen;49.1935816;-0.3487181 +0501825M;LycĂ©e Professionnel PrivĂ© Institut Saint Lo;18 Rue De L'oratoire;50180;Agneaux;49.1184709;-1.1060995 +0680154Y;LycĂ©e Professionnel PrivĂ© Institut Sonnenberg;1 Rue Du Moulin;68130;Carspach;47.6651522;7.2235055 +0680161F;LycĂ©e Professionnel PrivĂ© Institution Don Bosco;60 Rue D'ensisheim;68272;Wittenheim;47.8136036;7.3394336 +0681656F;LycĂ©e Professionnel PrivĂ© Institution Saint Jean;3 Route De Bale;68025;Colmar;48.0119328;7.3843166 +0672305S;LycĂ©e Professionnel PrivĂ© Institution Sainte Clotilde;19 Rue De Verdun;67083;Strasbourg;48.5874674;7.7724234 +0672299K;LycĂ©e Professionnel PrivĂ© Institution Ste Philomene;19a Boulevard Hanauer;67504;Haguenau;48.8113152;7.7888563 +0570225K;LycĂ©e Professionnel PrivĂ© Interentreprise;Rue Robert Schuman;57220;Boulay Moselle;49.185664;6.491576 +0572500H;LycĂ©e Professionnel PrivĂ© Interentreprise;Rue Jacques Touba;57430;Sarralbe;48.991472;7.024928 +0690654L;LycĂ©e Professionnel PrivĂ© Interfora;6 Rue Jean Mace;69190;St Fons;45.7059602;4.8532599 +0492082X;LycĂ©e Professionnel PrivĂ© Itec;2 Rue Pilastre;49100;Angers;47.479365;-0.5445983 +0381899H;LycĂ©e Professionnel PrivĂ© Itec Boisfleury;76 Grande Rue;38701;La Tronche;45.2047507;5.7377606 +0930934K;LycĂ©e Professionnel PrivĂ© Itmc;Cent Aff Paris Nord Imm Ampere;93153;Le Blanc Mesnil;48.941345;2.46436 +0132790M;LycĂ©e Professionnel PrivĂ© Jacques Raynaud;59 Traverse Susini;13013;Marseille 13;43.3355765;5.4182619 +0690660T;LycĂ©e Professionnel PrivĂ© Japy;1 Place Louis Pradel;69203;Lyon 01;45.7680387;4.8375832 +0311883D;LycĂ©e Professionnel PrivĂ© Jasmin Coiffure;4 Rue Des Teinturiers;31300;Toulouse;43.5969809;1.4357278 +0382893N;LycĂ©e Professionnel PrivĂ© Jean Marie Vianney;22 Avenue Hector Berlioz;38260;La Cote St Andre;45.386288;5.26266 +9830270B;LycĂ©e Professionnel PrivĂ© Jean Xxiii (ddec);Route Du Mont Mou;98890;Paita;0.0;0.0 +0421002L;LycĂ©e Professionnel PrivĂ© Jean-baptiste D'allard;7 Rue Du Bief;42601;Montbrison;45.602668;4.0626362 +0693370N;LycĂ©e Professionnel PrivĂ© Jean-baptiste De La Salle;1 Rue Neyret;69283;Lyon 01;45.7715543;4.8280213 +0541377D;LycĂ©e Professionnel PrivĂ© Jean-baptiste Vatelot;6 Rue De La Republique;54203;Toul;48.6729066;5.8919085 +0133274N;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;5 Rue Saint Roch;13632;Arles;43.6743556;4.6432771 +0331569B;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;3 Place De La Fraternite;33230;St Medard De Guizieres;45.0154483;-0.0603156 +0610721C;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;16 Rue De La Vicomte;61203;Argentan;48.7455115;-0.0208491 +0740156T;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;18 Bis Avenue Jules Ferry;74200;Thonon Les Bains;46.3724399;6.4832781 +0880133S;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;1 Avenue Chanzy;88600;Bruyeres;48.2086223;6.7188568 +0950800D;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;20 Rue De La Liberte;95100;Argenteuil;48.9433576;2.2505002 +0950812S;LycĂ©e Professionnel PrivĂ© Jeanne D Arc;2 Bis Bd Toussaint Lucas;95130;Franconville;48.9875913;2.2306862 +0080093H;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;27 Rue Bournizet;08400;Vouziers;49.3973761;4.6979472 +0120105Z;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;3 Place Du Mandarous;12104;Millau;44.0996885;3.0784006 +0121310J;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;23 Rue Lamartine;12402;St Affrique;43.956919;2.890886 +0220123P;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;4 Rue De La Bienfaisance;22300;Lannion;48.7287135;-3.4562299 +0381733C;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;2 Rue Jacques Prevert;38550;Le Peage De Roussillon;45.3712936;4.8011019 +0391001B;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;10 Rue Du Sauget;39303;Champagnole;46.7456228;5.9058307 +0511153B;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;94 Avenue De Laon;51100;Reims;49.2662257;4.0257119 +0730773U;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;3 Place De L'eglise;73203;Albertville;45.6780169;6.3908431 +0810108C;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;23 Rue De La Vanne;81200;Mazamet;43.4847739;2.3737673 +0881498A;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;6 Rue Du Canton;88202;Remiremont;48.0192476;6.5872824 +0881651S;LycĂ©e Professionnel PrivĂ© Jeanne D'arc;1 Avenue De Herringen;88300;Neufchateau;48.3590156;5.6926631 +0271074W;LycĂ©e Professionnel PrivĂ© Jeanne D'arc - Saint-anselme;11/13 R Leprevost De Beaumont;27300;Bernay;49.0942864;0.6045856 +0693372R;LycĂ©e Professionnel PrivĂ© Jeanne De Lestonnac;132 Rue Vendome;69006;Lyon 06;45.7648477;4.8456527 +0930929E;LycĂ©e Professionnel PrivĂ© Jeanne La Lorraine;3 Boulevard Du Nord;93340;Le Raincy;48.9017008;2.513264 +0100078F;LycĂ©e Professionnel PrivĂ© Jeanne Mance;Rue Du Paradis;10000;Troyes;48.2903865;4.0658966 +9830299H;LycĂ©e Professionnel PrivĂ© Joahana Vakie (ddec);Ecole De Nindiah;98816;Houailou;0.0;0.0 +0720825F;LycĂ©e Professionnel PrivĂ© Joseph Roussel;50 Avenue Bollee;72000;Le Mans;48.0020204;0.2071975 +0450759F;LycĂ©e Professionnel PrivĂ© L Abbaye;2 Rue De L Abbaye;45190;Beaugency;47.7765588;1.6341757 +0133275P;LycĂ©e Professionnel PrivĂ© La Cadenelle;134 Bd Des Liberateurs;13012;Marseille 12;43.3020176;5.4572235 +0830128N;LycĂ©e Professionnel PrivĂ© La Colette;Residence Ste Catherine Tour A;83000;Toulon;43.1200697;5.9383291 +0690697H;LycĂ©e Professionnel PrivĂ© La Favorite Sainte Therese;107 Rue Cdt Charcot;69110;Ste Foy Les Lyon;45.7464605;4.7883369 +0740148J;LycĂ©e Professionnel PrivĂ© La Fontaine;116 Rue De La Fontaine;74210;Faverges;45.7445312;6.2942685 +0060715V;LycĂ©e Professionnel PrivĂ© La Providence;236 Route De Grenoble;06200;Nice;43.6819305;7.1990341 +0260082Y;LycĂ©e Professionnel PrivĂ© La Providence;14 18 Rue Henri Chalamet;26000;Valence;44.9294183;4.8938111 +0311209W;LycĂ©e Professionnel PrivĂ© La Providence;30 Rue Jean Moulin;31250;Revel;43.4582667;2.0065414 +0490886X;LycĂ©e Professionnel PrivĂ© La Providence;33 Avenue Gustave Ferrie;49306;Cholet;47.0623002;-0.8582673 +0570248K;LycĂ©e Professionnel PrivĂ© La Providence;4 Rue De Sarrelouis;57320;Bouzonville;49.2902615;6.5356148 +0570279U;LycĂ©e Professionnel PrivĂ© La Providence;1 Rue Des Freres Mesguin;57260;Dieuze;48.8116359;6.7201268 +0331561T;LycĂ©e Professionnel PrivĂ© La Ruche;1 Rue Poquelin Moliere;33000;Bordeaux;44.8401086;-0.576113 +0693332X;LycĂ©e Professionnel PrivĂ© La Vidaude;Chemin De La Vidaude;69563;St Genis Laval;45.6925289;4.7800249 +0420992A;LycĂ©e Professionnel PrivĂ© Lachaux;Rue Edouard Michot;42501;Le Chambon Feugerolles;45.3979878;4.3243091 +0020493A;LycĂ©e Professionnel PrivĂ© Lacordaire;Route De Laon;02800;Charmes;49.655364;3.385869 +0501789Y;LycĂ©e Professionnel PrivĂ© Le Bon Sauveur;Rue E De Surville;50001;St Lo;49.112089;-1.094787 +0381741L;LycĂ©e Professionnel PrivĂ© Le Breda;L'epinette;38580;Allevard;45.404864;6.084012 +0640179X;LycĂ©e Professionnel PrivĂ© Le Guichot;42 Rue D Espagne;64100;Bayonne;43.489412;-1.4771746 +0730772T;LycĂ©e Professionnel PrivĂ© Le Margeriaz;28 Rue De Buisson Rond;73000;Barberaz;45.5665442;5.9414436 +0701016Z;LycĂ©e Professionnel PrivĂ© Le Marteroy;7 Avenue Aristide Briand;70000;Vesoul;47.6288058;6.1668986 +0440282T;LycĂ©e Professionnel PrivĂ© Le Masle;9 Rue Jean Pierre Dufrexou;44612;St Nazaire;47.2722287;-2.2058751 +0340923U;LycĂ©e Professionnel PrivĂ© Le Parterre;15 Rue Courbezou;34600;Bedarieux;43.6146339;3.1582161 +0490903R;LycĂ©e Professionnel PrivĂ© Le Pinier Neuf;;49601;Beaupreau;48.384469;0.1593171 +0131485U;LycĂ©e Professionnel PrivĂ© Le Rocher;Montee De La Transhumance;13300;Salon De Provence;43.6449432;5.0949437 +0340992U;LycĂ©e Professionnel PrivĂ© Le Sacre Coeur;46 Boulevard D Angleterre;34500;Beziers;43.3470472;3.2120912 +0592964B;LycĂ©e Professionnel PrivĂ© Leonard De Vinci;10 Rue Notre Dame Des Victoires;59100;Roubaix;50.6950525;3.1805112 +0100063P;LycĂ©e Professionnel PrivĂ© Leonie Aviat;3 Rue Etienne Pedron;10000;Troyes;48.3043057;4.0791779 +0490910Y;LycĂ©e Professionnel PrivĂ© Les Ardilliers;1 Quai Du Jagueneau;49421;Saumur;47.2516631;-0.0584264 +0741287X;LycĂ©e Professionnel PrivĂ© Les Bressis;85 Route Des Creuses;74600;Seynod;45.8932394;6.1001202 +0782100J;LycĂ©e Professionnel PrivĂ© Les Chataigniers;11 Bis Avenue Jean Jaures;78000;Versailles;48.8174674;2.1442539 +0631408N;LycĂ©e Professionnel PrivĂ© Les Cordeliers;Place Des Cordeliers;63100;Clermont Ferrand;45.7942923;3.1131005 +0740143D;LycĂ©e Professionnel PrivĂ© Les Cordeliers;11 Rue M. Berthelot;74301;Cluses;46.0593129;6.5787129 +0061394H;LycĂ©e Professionnel PrivĂ© Les Fauvettes;44 Avenue J De Noailles;06400;Cannes;43.5541346;6.9996242 +0381809K;LycĂ©e Professionnel PrivĂ© Les Gorges;22 Rue Des Orphelines;38500;Voiron;45.3692772;5.5910909 +0090045A;LycĂ©e Professionnel PrivĂ© Les Jacobins;Rue Du Rempart Du Touroncq;09100;Pamiers;43.114753;1.607953 +9710775R;LycĂ©e Professionnel PrivĂ© Les Perseverants;28 Rue Lardenoy;97100;Basse Terre;15.9932345;-61.7242003 +0381805F;LycĂ©e Professionnel PrivĂ© Les Portes De Chartreuse;387 Avenue De Stalingrad;38343;Voreppe;45.2947821;5.6336357 +0311971Z;LycĂ©e Professionnel PrivĂ© Les Potiers;4 Rue Du Sachet;31400;Toulouse;43.5931107;1.4478264 +0382169B;LycĂ©e Professionnel PrivĂ© Les Prairies;31 Rue Mainssieux;38500;Voiron;45.3630158;5.5928307 +0141868E;LycĂ©e Professionnel PrivĂ© Les Rosiers;17 Chemin De Rocques;14100;Lisieux;49.154762;0.2306014 +9711064E;LycĂ©e Professionnel PrivĂ© L'institut Des Ameriques;Forum De Grand-camp;97142;Les Abymes;16.27395;-61.502615 +0595723A;LycĂ©e Professionnel PrivĂ© Louise De Marillac;2 Rue D'antin;59003;Lille;50.6268165;3.0474897 +0593066M;LycĂ©e Professionnel PrivĂ© Lp Des Forges;13 Rue Du Couvent;59220;Denain;50.32239;3.382042 +0595918M;LycĂ©e Professionnel PrivĂ© Lp E.p.i.d.;20 Rue De Lille;59140;Dunkerque;51.0277456;2.3724543 +0070100W;LycĂ©e Professionnel PrivĂ© Marc Seguin-saint Charles;Route De Californie;07100;Annonay;45.2440877;4.6758762 +9830271C;LycĂ©e Professionnel PrivĂ© Marcellin Champagnat (ddec);Rt Terr 1;98890;Paita;0.0;0.0 +0593015G;LycĂ©e Professionnel PrivĂ© Maria Goretti;1/e Rue De Verlinghem;59832;Lambersart;50.653684;3.0247746 +0221867K;LycĂ©e Professionnel PrivĂ© Marie Balavenne;47 Boulevard Laennec;22005;St Brieuc;48.5111408;-2.7744799 +0830116A;LycĂ©e Professionnel PrivĂ© Marie France;220 Avenue Marcel Castie;83000;Toulon;43.1234854;5.941595 +0131449E;LycĂ©e Professionnel PrivĂ© Marie Gasquet;38 R Electriciens St Barnabe;13012;Marseille 12;43.3041972;5.4172944 +0541364P;LycĂ©e Professionnel PrivĂ© Marie Immaculee;33 Av Du General Leclerc;54000;Nancy;48.6821438;6.1840703 +0133277S;LycĂ©e Professionnel PrivĂ© Modele Electronique;233 Bd St Marcel;13011;Marseille 11;43.2865274;5.4686969 +0740155S;LycĂ©e Professionnel PrivĂ© Mont Blanc;148 Route De Doran;74700;Sallanches;45.9375916;6.6254618 +0820061W;LycĂ©e Professionnel PrivĂ© Montauriol;1 Boulevard Montauriol;82000;Montauban;44.0100506;1.3594123 +0640184C;LycĂ©e Professionnel PrivĂ© Montpensier;34 Rue Montpensier;64000;Pau;43.3010083;-0.3729012 +0261139X;LycĂ©e Professionnel PrivĂ© Montplaisir;75 Rue Montplaisir;26000;Valence;44.9312156;4.9083899 +0940851P;LycĂ©e Professionnel PrivĂ© Morin;22 Avenue De Verdun;94000;Creteil;48.7948841;2.462591 +0312064A;LycĂ©e Professionnel PrivĂ© Myriam;9 Et 20 Rue Mage;31000;Toulouse;43.5977887;1.4472602 +0440262W;LycĂ©e Professionnel PrivĂ© Nazareth;40 Rue Michel Grimault;44141;Chateaubriant;47.7156138;-1.3765267 +0290207T;LycĂ©e Professionnel PrivĂ© Nd Du Kreisker;2 Rue Cadiou;29250;St Pol De Leon;48.6825255;-3.9870981 +0781582W;LycĂ©e Professionnel PrivĂ© N-dame Du Grandchamp;97 Rue Royale;78000;Versailles;48.7933549;2.1239896 +0280684S;LycĂ©e Professionnel PrivĂ© Notre Dame;Chateau Des Vaux;28240;St Maurice St Germain;48.494383;1.089488 +0350790U;LycĂ©e Professionnel PrivĂ© Notre Dame;6 Rue De Vannes;35601;Redon;47.6473747;-2.0900067 +0350807M;LycĂ©e Professionnel PrivĂ© Notre Dame;Rue De Dinan;35290;St Meen Le Grand;48.1926274;-2.188228 +0501941N;LycĂ©e Professionnel PrivĂ© Notre Dame;43 Rue Sebline;50500;Carentan;49.3051863;-1.2471225 +0541357G;LycĂ©e Professionnel PrivĂ© Notre Dame;1 Rue Du Bois Prieur;54350;Mont St Martin;49.5335424;5.764854 +0691465T;LycĂ©e Professionnel PrivĂ© Notre Dame;1 Rue Honore Petetin;69700;Givors;45.5910944;4.7716109 +0783537W;LycĂ©e Professionnel PrivĂ© Notre Dame;15 Rue De Strasbourg;78200;Mantes La Jolie;48.991379;1.711402 +0881530K;LycĂ©e Professionnel PrivĂ© Notre Dame - Saint Joseph;23 Rue Thiers;88012;Epinal;48.1701687;6.4513835 +0141191U;LycĂ©e Professionnel PrivĂ© Notre Dame De Fidelite;8 Rue Du Petit Clos Saint Marc;14074;Caen;49.2111144;-0.3733099 +0501787W;LycĂ©e Professionnel PrivĂ© Notre Dame De La Providence;9 Rue Du Chanoine Beranger;50303;Avranches;48.686819;-1.3690481 +0572951Y;LycĂ©e Professionnel PrivĂ© Notre Dame De La Providence;22 Place Notre Dame;57126;Thionville;49.3576246;6.1602375 +0880120C;LycĂ©e Professionnel PrivĂ© Notre Dame De La Providence;14 Rue Pasteur;88109;St Die Des Vosges;48.2825237;6.9504488 +0601946P;LycĂ©e Professionnel PrivĂ© Notre Dame De La Tilloye;1 Av De La Liberation;60204;Compiegne;49.4062037;2.8305249 +0141193W;LycĂ©e Professionnel PrivĂ© Notre Dame De Nazareth;Avenue De La Basilique;14440;Douvres La Delivrande;49.2970159;-0.3732011 +0260078U;LycĂ©e Professionnel PrivĂ© Notre Dame Des Champs;Rue Eugene Blain;26106;Romans Sur Isere;45.046612;5.042497 +0550700M;LycĂ©e Professionnel PrivĂ© Notre Dame Des Vertus;30 Rue Du General De Gaulle;55500;Ligny En Barrois;48.6873861;5.3236757 +0470730N;LycĂ©e Professionnel PrivĂ© Notre Dame La Compassion;1ter Rue De Langeot;47200;Marmande;44.4978288;0.1723321 +0480041J;LycĂ©e Professionnel PrivĂ© Notre-dame;Quartier Fontanilles;48000;Mende;44.517611;3.501873 +0761347B;LycĂ©e Professionnel PrivĂ© Notre-dame;5 Rue Hervieux;76500;Elbeuf;49.2889278;1.0017247 +0593030Y;LycĂ©e Professionnel PrivĂ© Notre-dame Du Sacre Coeur;158 Avenue Saint Marcel;59120;Loos;50.6097613;3.0255216 +0754191Y;LycĂ©e Professionnel PrivĂ© Notre-dame -st Vincent De Paul;49 Rue Bobillot;75013;Paris 13;48.828049;2.3529993 +0950804H;LycĂ©e Professionnel PrivĂ© Notre-famille;2 Rue Des Patis;95520;Osny;49.0501663;2.086339 +0693406C;LycĂ©e Professionnel PrivĂ© O R T;133 Rue Marius Berliet;69008;Lyon 08;45.7380862;4.8677943 +0141186N;LycĂ©e Professionnel PrivĂ© Oasis;18 Rue De L Oratoire;14000;Caen;49.18111;-0.3599751 +0320652K;LycĂ©e Professionnel PrivĂ© Oratoire Sainte-marie;50 Bis Rue Victor Hugo;32002;Auch;43.6505336;0.5776322 +0131424C;LycĂ©e Professionnel PrivĂ© Ort Leon Bramson;9 Rue Des Forges;13010;Marseille 10;43.2791281;5.4104058 +0312191N;LycĂ©e Professionnel PrivĂ© Ort Maurice Grynfogel;14 Rue Etienne Collongues;31770;Colomiers;43.601951;1.333155 +0430101B;LycĂ©e Professionnel PrivĂ© Paradis;Rue Pont De La Chartreuse;43700;Brives Charensac;45.0529219;3.9138113 +0921932A;LycĂ©e Professionnel PrivĂ© Passy Buzenval;50 Avenue Otis Mygatt;92508;Rueil Malmaison;48.8591268;2.1832515 +0300109F;LycĂ©e Professionnel PrivĂ© Pasteur;3 Rue Pasteur;30110;La Grand Combe;44.2107935;4.0283226 +9720073X;LycĂ©e Professionnel PrivĂ© Patronage Saint-louis;Route Du Lamentin Km 4;97200;Fort De France;14.609371;-61.07256 +9710066V;LycĂ©e Professionnel PrivĂ© Pensionnat De Versailles;8 Rue Victor Hugues;97100;Basse Terre;16.1985639;-61.5901551 +9830294C;LycĂ©e Professionnel PrivĂ© Pere Gueneau (ddec);24 Rte Terr 1 Village;98870;Bourail;0.0;0.0 +0131437S;LycĂ©e Professionnel PrivĂ© Phocea R Attoyan (coiffure);1 Rue D Arcole;13006;Marseille 06;43.2894588;5.3774154 +0541363N;LycĂ©e Professionnel PrivĂ© Pierre De Coubertin;5 R Du President Robert Schuman;54000;Nancy;48.6825501;6.1799118 +0050039R;LycĂ©e Professionnel PrivĂ© Pierre Et Louis Poutrain;;05260;St Jean St Nicolas;44.6683333;6.2294444 +0771204R;LycĂ©e Professionnel PrivĂ© Pigier Melun;30 Boulevard Victor Hugo;77000;Melun;48.5394984;2.6580575 +0070107D;LycĂ©e Professionnel PrivĂ© Presentation De Marie;21 Avenue Notre Dame;07700;Bourg St Andeol;44.3730123;4.6450101 +0631048X;LycĂ©e Professionnel PrivĂ© Rene Rambaud - Coiffure;27 Avenue Du Marechal Leclerc;63000;Clermont Ferrand;45.7843743;3.0868642 +0931573E;LycĂ©e Professionnel PrivĂ© Robert Schuman;5 Avenue Du General De Gaulle;93440;Dugny;48.9494067;2.4150708 +0940820F;LycĂ©e Professionnel PrivĂ© Robert Schuman;5-6 Rue De L'eglise;94340;Joinville Le Pont;48.8192804;2.4664616 +0381738H;LycĂ©e Professionnel PrivĂ© Robin St Vincent De Paul;;38204;Vienne;46.3491859;-1.0850299 +0783330W;LycĂ©e Professionnel PrivĂ© Roulleau;42 Rue De Tessancourt;78250;Meulan;49.0086789;1.9050102 +0801947G;LycĂ©e Professionnel PrivĂ© Sacre Coeur;1 Rue De L Oratoire;80026;Amiens;49.8933658;2.3044821 +0801951L;LycĂ©e Professionnel PrivĂ© Sacre Coeur;36 Boulevard Des Anglais;80200;Peronne;49.9264177;2.9342576 +0040502Z;LycĂ©e Professionnel PrivĂ© Sacre Coeur (du);2 Avenue Des Thermes;04000;Digne Les Bains;44.0917238;6.2407469 +0940853S;LycĂ©e Professionnel PrivĂ© Sacre-coeur;3 Boulevard De Stalingrad;94320;Thiais;48.7672928;2.4035625 +0631737W;LycĂ©e Professionnel PrivĂ© Saint Alyre;20 Rue Sainte George;63037;Clermont Ferrand;45.7834986;3.0818986 +0070112J;LycĂ©e Professionnel PrivĂ© Saint Andre;18 Rue Emile Combes;07400;Le Teil;44.5388909;4.6878848 +0572101Z;LycĂ©e Professionnel PrivĂ© Saint Andre;1 Rue De L'eglise;57840;Ottange;49.4410443;6.0178869 +0251112Y;LycĂ©e Professionnel PrivĂ© Saint Benigne;1 Rue Du Chanoine Prenel;25300;Pontarlier;46.9020101;6.3561888 +0021906L;LycĂ©e Professionnel PrivĂ© Saint Charles;1 Rue Du Brouage;02300;Chauny;49.6147218;3.2121038 +0860752A;LycĂ©e Professionnel PrivĂ© Saint Gabriel Notre Dame;27 Rue Du Vieux Palais;86108;Chatellerault;46.8156207;0.542157 +0891223W;LycĂ©e Professionnel PrivĂ© Saint Jacques;6 Rue Du Faubourg St Jacques;89300;Joigny;47.9834057;3.3881557 +0950805J;LycĂ©e Professionnel PrivĂ© Saint Jean;Rond Point De La Tour Du Mail;95117;Sannois;48.970782;2.256869 +0010099C;LycĂ©e Professionnel PrivĂ© Saint Joseph;3 Rue Du LycĂ©e;01000;Bourg En Bresse;0.0;0.0 +0010814E;LycĂ©e Professionnel PrivĂ© Saint Joseph;101 Rue Henri Grobon;01705;Miribel;45.825157;4.9512247 +0022002R;LycĂ©e Professionnel PrivĂ© Saint Joseph;2 Chaussee De Fontaine;02140;Fontaine Les Vervins;49.8365787;3.906547 +0180572Z;LycĂ©e Professionnel PrivĂ© Saint Joseph;11 Rue Gourdon;18100;Vierzon;47.2244551;2.0669876 +0251592V;LycĂ©e Professionnel PrivĂ© Saint Joseph;28 Av Fontaine Argent;25000;Besancon;47.2462201;6.0340665 +0382600V;LycĂ©e Professionnel PrivĂ© Saint Joseph;1 Rue Lakanal;38506;Voiron;45.3678543;5.5870773 +0622192W;LycĂ©e Professionnel PrivĂ© Saint Joseph;30 Rue Des Berceaux;62630;Etaples;50.5129606;1.6437015 +0690651H;LycĂ©e Professionnel PrivĂ© Saint Joseph;327 Rue Garibaldi;69363;Lyon 07;45.7475187;4.8515373 +0741355W;LycĂ©e Professionnel PrivĂ© Saint Joseph;St Martin Sur Arve;74703;Sallanches;45.912929;6.689997 +0831187P;LycĂ©e Professionnel PrivĂ© Saint Joseph;2229 Route De Faveyrolles;83190;Ollioules;43.139436;5.8845344 +0900425Y;LycĂ©e Professionnel PrivĂ© Saint Joseph;14 Rue De Badonvillers;90000;Belfort;47.6461321;6.8486574 +0680159D;LycĂ©e Professionnel PrivĂ© Saint Joseph De Cluny;53 Avenue Roger Salengro;68100;Mulhouse;47.7509394;7.347119 +0570264C;LycĂ©e Professionnel PrivĂ© Saint Joseph-la Providence;10 Rue De L Abbe Heydel;57801;Freyming Merlebach;49.1466767;6.8148536 +0572953A;LycĂ©e Professionnel PrivĂ© Saint Joseph-la Providence;2 Avenue Passaga;57600;Forbach;49.1853591;6.9006303 +0261138W;LycĂ©e Professionnel PrivĂ© Saint Louis;Clos Soubeyran;26402;Crest;0.0;0.0 +0593060F;LycĂ©e Professionnel PrivĂ© Saint Louis;145 Avenue Marc Sangnier;59427;Armentieres;50.7001104;2.8845967 +0371328T;LycĂ©e Professionnel PrivĂ© Saint Martin;47 Rue Nericault Destouches;37000;Tours;47.3920294;0.6835155 +0541344T;LycĂ©e Professionnel PrivĂ© Saint Michel;Chartreuse De Bosserville;54510;Art Sur Meurthe;48.664136;6.243642 +0754175F;LycĂ©e Professionnel PrivĂ© Saint Paul;81 Ter Rue Jean Pierre Timbaud;75011;Paris 11;48.867957;2.3777608 +0421978X;LycĂ©e Professionnel PrivĂ© Saint Paul Forez;13 Rue Du College;42603;Montbrison;45.6084481;4.0659129 +0801945E;LycĂ©e Professionnel PrivĂ© Saint Pierre;24 Place Clemenceau;80103;Abbeville;50.1087199;1.8347016 +0701001H;LycĂ©e Professionnel PrivĂ© Saint Pierre Fourier;10 Q Rue Des Casernes;70103;Gray;47.4448206;5.5926946 +0801949J;LycĂ©e Professionnel PrivĂ© Saint Riquier;50 Chaussee Jules Ferry;80094;Amiens;49.8829134;2.3258793 +0030105Y;LycĂ©e Professionnel PrivĂ© Saint Vincent;150 Boulevard De Courtais;03100;Montlucon;46.3419497;2.6072664 +0740144E;LycĂ©e Professionnel PrivĂ© Saint Vincent;55 Route De Bossey;74160;Collonges Sous Saleve;46.1413463;6.1456029 +0060776L;LycĂ©e Professionnel PrivĂ© Saint Vincent De Paul;17 Rue Fodere;06300;Nice;43.6991063;7.2864757 +0370757X;LycĂ©e Professionnel PrivĂ© Saint Vincent De Paul;30 Rue Delperier;37058;Tours;47.3925342;0.6769264 +0570241C;LycĂ©e Professionnel PrivĂ© Saint Vincent De Paul;4 Rue Marie Douchet;57440;Algrange;49.3580552;6.0494087 +0930946Y;LycĂ©e Professionnel PrivĂ© Saint Vincent De Paul;25 Rue Albert Walter;93200;St Denis;48.9380004;2.3591377 +0810100U;LycĂ©e Professionnel PrivĂ© Saint-dominique;17 Lices Georges Pompidou;81000;Albi;43.9298245;2.1477169 +0420980M;LycĂ©e Professionnel PrivĂ© Sainte Anne;24 Rue Du Pilat;42400;St Chamond;45.4720855;4.514178 +0421979Y;LycĂ©e Professionnel PrivĂ© Sainte Anne;4 Rue Saint Alban;42300;Roanne;46.0363817;4.0633804 +0550983V;LycĂ©e Professionnel PrivĂ© Sainte Anne;14 Rue Mautrote;55104;Verdun;49.1604635;5.3812032 +0730774V;LycĂ©e Professionnel PrivĂ© Sainte Anne;39 Montee Saint Jean;73292;La Motte Servolex;45.5973189;5.8780976 +0700076C;LycĂ©e Professionnel PrivĂ© Sainte Anne Saint Joseph;1 Rue De La Tannerie;70204;Lure;47.6877792;6.4943028 +0593079B;LycĂ©e Professionnel PrivĂ© Sainte Bernadette;225 Rue Des Anciens D'afn;59572;Jeumont;50.296322;4.100791 +0570268G;LycĂ©e Professionnel PrivĂ© Sainte Chretienne;1 Passage Du Pensionnat;57506;St Avold;49.1049716;6.7083946 +0131675A;LycĂ©e Professionnel PrivĂ© Sainte Elisabeth;Lot N?35;13240;Septemes Les Vallons;43.3973237;5.3694594 +0251035P;LycĂ©e Professionnel PrivĂ© Sainte Famille;33 Rue Du General Brulard;25000;Besancon;47.2275972;6.0026427 +0741286W;LycĂ©e Professionnel PrivĂ© Sainte Famille;Avenue Des Voirons;74805;La Roche Sur Foron;46.0773767;6.2971227 +0730776X;LycĂ©e Professionnel PrivĂ© Sainte Genevieve;2 Boulevard Du Theatre;73000;Chambery;45.5659213;5.9237499 +0551032Y;LycĂ©e Professionnel PrivĂ© Sainte Jeanne D'arc;23 Rue Poincare;55200;Commercy;48.7606796;5.5902284 +0570294K;LycĂ©e Professionnel PrivĂ© Sainte Marie;21 Avenue Du General De Gaulle;57400;Sarrebourg;48.7310531;7.05137 +0593063J;LycĂ©e Professionnel PrivĂ© Sainte Marie;2 Rue Emile Hie;59270;Bailleul;50.7375223;2.73195 +0622195Z;LycĂ©e Professionnel PrivĂ© Sainte Marie;31 Rue Du Paradis;62310;Fruges;50.5156254;2.1365448 +0390094R;LycĂ©e Professionnel PrivĂ© Sainte Marie - Fenelon;84 Rue Saint Desire;39000;Lons Le Saunier;46.6706079;5.5489595 +0420996E;LycĂ©e Professionnel PrivĂ© Sainte Marie La Grand'grange;Rue Mondragon;42400;St Chamond;45.483205;4.5102909 +0830114Y;LycĂ©e Professionnel PrivĂ© Sainte Marthe;8 Rue Des 4 Freres Bernard;83390;Cuers;43.237875;6.0721999 +0671679L;LycĂ©e Professionnel PrivĂ© Sainte Therese;4 Rue Des Allies;67970;Oermingen;49.0008662;7.128229 +0740151M;LycĂ©e Professionnel PrivĂ© Sainte Therese;8 Avenue De L'aumone;74150;Rumilly;45.8644371;5.950467 +0141867D;LycĂ©e Professionnel PrivĂ© Sainte Ursule;30 R De La Misericorde;14019;Caen;49.1819847;-0.3555065 +0271073V;LycĂ©e Professionnel PrivĂ© Sainte-agnes;126 Rue D'albufera;27200;Vernon;49.090865;1.4801589 +0811070Y;LycĂ©e Professionnel PrivĂ© Sainte-cecile;1 Avenue Du Breuil;81000;Albi;43.9432297;2.1483149 +0300106C;LycĂ©e Professionnel PrivĂ© Sainte-marie;Impasse Des Recollets;30200;Bagnols Sur Ceze;44.1612433;4.6238415 +0311970Y;LycĂ©e Professionnel PrivĂ© Sainte-marie De Nevers;10 Rue Du Perigord;31070;Toulouse;43.6072684;1.4432532 +0311219G;LycĂ©e Professionnel PrivĂ© Sainte-marie Saint-sernin;19 Boulevard Armand Duportal;31000;Toulouse;43.6068673;1.4345863 +0761344Y;LycĂ©e Professionnel PrivĂ© Sainte-therese;34 Rue Ferdinand Cartier;76150;Maromme;49.4855079;1.0441959 +0460050E;LycĂ©e Professionnel PrivĂ© Saint-etienne;49 Rue Des Soubirous;46000;Cahors;44.4504769;1.4412933 +0110053Y;LycĂ©e Professionnel PrivĂ© Saint-francois;2 Rue Des Amidonniers;11000;Carcassonne;43.2096831;2.3495761 +0761348C;LycĂ©e Professionnel PrivĂ© Saint-hildevert;43 Bis Rue De Ferrieres;76220;Gournay En Bray;49.4821029;1.7281726 +0754239A;LycĂ©e Professionnel PrivĂ© Saint-jean De Montmartre;31 Rue Caulaincourt;75018;Paris 18;48.8881336;2.3336903 +0121260E;LycĂ©e Professionnel PrivĂ© Saint-joseph;Avenue Etienne Soulie;12200;Villefranche De Rouergue;44.357108;2.0390698 +0121309H;LycĂ©e Professionnel PrivĂ© Saint-joseph;1 Rue Sarrus;12000;Rodez;44.3451427;2.5715885 +0141192V;LycĂ©e Professionnel PrivĂ© Saint-joseph;145 Avenue De La Republique;14800;Deauville;49.3547504;0.0669966 +0312065B;LycĂ©e Professionnel PrivĂ© Saint-joseph;85 Rue De Limayrac;31079;Toulouse;43.593452;1.4734636 +0810105Z;LycĂ©e Professionnel PrivĂ© Saint-joseph;38 Avenue De Lavaur;81100;Castres;43.6075533;2.2350212 +0271081D;LycĂ©e Professionnel PrivĂ© Saint-ouen;30 Rue Sadi Carnot;27500;Pont Audemer;49.3571648;0.5145026 +0141184L;LycĂ©e Professionnel PrivĂ© Saint-pierre;50 Rue Canchy;14000;Caen;49.1741178;-0.3513773 +0820551D;LycĂ©e Professionnel PrivĂ© Saint-roch;Malepeyre;82390;Durfort Lacapelette;44.19504;1.1837629 +0761349D;LycĂ©e Professionnel PrivĂ© Saint-vincent De Paul;26 Boulevard Amiral Mouchez;76600;Le Havre;49.4849886;0.1351204 +0730779A;LycĂ©e Professionnel PrivĂ© Savoisienne;85 Chemin Des Nuettes;73420;Drumettaz Clarafond;45.6649369;5.911833 +0601947R;LycĂ©e Professionnel PrivĂ© Sevigne;20 Rue De La Sous Prefecture;60200;Compiegne;49.415931;2.8299869 +0771211Y;LycĂ©e Professionnel PrivĂ© Sncf;35 Rue Du Chateau D'arcy;77390;Chaumes En Brie;48.666504;2.880979 +0690698J;LycĂ©e Professionnel PrivĂ© Societe Enseig. Prof. Du Rhone;46 Rue Professeur Rochaix;69003;Lyon 03;45.7487694;4.8778351 +0131432L;LycĂ©e Professionnel PrivĂ© St Andre (les Routiers);368 Boulevard Barnier;13016;Marseille 16;43.3606468;5.3425878 +0331585U;LycĂ©e Professionnel PrivĂ© St Augustin;19 Rue Paul Courteault;33000;Bordeaux;44.8346037;-0.607579 +0440302P;LycĂ©e Professionnel PrivĂ© St Donatien-immaculee;6 Rue Guillet De La Brosse;44013;Nantes;47.2296841;-1.5440911 +0133280V;LycĂ©e Professionnel PrivĂ© St Eloi;9 Avenue Jules Isaac;13626;Aix En Provence;43.5348838;5.4513323 +0781856U;LycĂ©e Professionnel PrivĂ© St Erembert;5 Rue Salomon Reinach;78100;St Germain En Laye;48.8958802;2.0996683 +0292158N;LycĂ©e Professionnel PrivĂ© St Esprit;3 Rue Emile Souvestre;29403;Landivisiau;48.5121112;-4.0797023 +0911964R;LycĂ©e Professionnel PrivĂ© St Eugene;10 Rue De La Pie Voleuse;91120;Palaiseau;48.7131857;2.2405189 +0781855T;LycĂ©e Professionnel PrivĂ© St Francois D Assise;45 Avenue Du Manet;78180;Montigny Le Bretonneux;48.7664455;2.0213888 +0920963X;LycĂ©e Professionnel PrivĂ© St Francois D Assise;39 Rue Boris Vilde;92260;Fontenay Aux Roses;48.7884925;2.2801094 +0332550T;LycĂ©e Professionnel PrivĂ© St Genes;160 Rue St Genes;33081;Bordeaux;44.8249282;-0.5813679 +0560199M;LycĂ©e Professionnel PrivĂ© St Georges;16 Rue Marechal Foch;56000;Vannes;47.6524299;-2.7669182 +0131433M;LycĂ©e Professionnel PrivĂ© St Henri;37 Chemin De Bernex;13016;Marseille 16;43.366048;5.3394241 +0870919B;LycĂ©e Professionnel PrivĂ© St Jean;26 Rue Eugene Varlin;87016;Limoges;45.8263601;1.2749309 +0840941S;LycĂ©e Professionnel PrivĂ© St Jean Baptiste De La Salle;9 R Notre Dame Sept Douleurs;84008;Avignon;43.9489266;4.815644 +0101015Z;LycĂ©e Professionnel PrivĂ© St Joseph;21 Rue Du Cloitre St Etienne;10042;Troyes;48.2970565;4.0816607 +0221865H;LycĂ©e Professionnel PrivĂ© St Joseph;Rue Mouexigne;22404;Lamballe;48.4631626;-2.5132448 +0332535B;LycĂ©e Professionnel PrivĂ© St Joseph;12 Avenue Du 8 Mai;33291;Blanquefort;44.9087878;-0.6456849 +0350788S;LycĂ©e Professionnel PrivĂ© St Joseph;42 Route De St James;35300;Fougeres;48.3659336;-1.2085963 +0400883T;LycĂ©e Professionnel PrivĂ© St Joseph;Le Bourg;40310;Gabarret;43.984986;0.011111 +0400916D;LycĂ©e Professionnel PrivĂ© St Joseph;Le Berceau;40990;St Vincent De Paul;43.7469013;-1.0103303 +0511151Z;LycĂ©e Professionnel PrivĂ© St Joseph;41 Rue Du General Fery;51037;Chalons En Champagne;48.9614846;4.3716507 +0541988T;LycĂ©e Professionnel PrivĂ© St Joseph;413 Avenue De Boufflers;54524;Laxou;48.6934935;6.1381862 +0640181Z;LycĂ©e Professionnel PrivĂ© St Joseph;1 Rte Des Missionnaires;64240;Hasparren;43.3811075;-1.3049408 +9710593T;LycĂ©e Professionnel PrivĂ© St Joseph De Cluny;La Jaille;97122;Baie Mahault;16.2547641;-61.56198 +9720074Y;LycĂ©e Professionnel PrivĂ© St Joseph De Cluny;22 Route De Cluny;97200;Fort De France;14.627187;-61.0738508 +9830269A;LycĂ©e Professionnel PrivĂ© St Joseph De Cluny (ddec);45 Rue Alma Ctre Ville;98845;Noumea;-22.2697334;166.4407828 +0601896K;LycĂ©e Professionnel PrivĂ© St Joseph Du Moncel;8 Place De L Eglise;60722;Pont Ste Maxence;49.3158812;2.6215379 +0131468A;LycĂ©e Professionnel PrivĂ© St Louis;538 Chemin De La Madrague-ville;13015;Marseille 15;43.347103;5.3554272 +0291808H;LycĂ©e Professionnel PrivĂ© St Marc;12 Rue De Kerfeunteun;29910;Tregunc;47.8537038;-3.8548436 +0721298V;LycĂ©e Professionnel PrivĂ© St Martin;Chateau De Maison Rouge;72500;La Bruere Sur Loir;48.537019;-0.503046 +0331548D;LycĂ©e Professionnel PrivĂ© St Michel;20 Av Gl De Gaulle;33290;Blanquefort;44.910084;-0.635244 +0560197K;LycĂ©e Professionnel PrivĂ© St Michel;Fondation D'auteuil;56320;Priziac;48.1024272;-3.4316011 +0851516L;LycĂ©e Professionnel PrivĂ© St Michel;32 Rue Du Calvaire;85290;St Laurent Sur Sevre;46.9593823;-0.8910565 +0131441W;LycĂ©e Professionnel PrivĂ© St Michel (ann. St Ch. Camas);75 Rue Saint Savournin;13005;Marseille 05;43.2967646;5.3871316 +0920981S;LycĂ©e Professionnel PrivĂ© St Philippe;1 Rue Du Pere Brottier;92190;Meudon;48.8066002;2.2467973 +0490897J;LycĂ©e Professionnel PrivĂ© St Serge;13 Rue Des Fours A Chaux;49103;Angers;47.4840708;-0.5409198 +0440261V;LycĂ©e Professionnel PrivĂ© St Thomas D'aquin;83 Rue Clemenceau;44153;Ancenis;47.3668587;-1.1786221 +0781581V;LycĂ©e Professionnel PrivĂ© St Thomas De Villeneuve;15 Rue Des Louviers;78100;St Germain En Laye;48.8983954;2.0921371 +0290192B;LycĂ©e Professionnel PrivĂ© St Tremeur;1 Rue Cazuguel;29833;Carhaix Plouguer;48.275584;-3.574188 +0132835L;LycĂ©e Professionnel PrivĂ© St Vincent De Paul;30 Rue Stanislas Torrents;13006;Marseille 06;43.287572;5.3782713 +0240089S;LycĂ©e Professionnel PrivĂ© St Vincent De Paul;23 Av G Pompidou;24000;Perigueux;45.1889317;0.7230677 +0332497K;LycĂ©e Professionnel PrivĂ© St Vincent De Paul;47 A 51 Rue Des Sablieres;33074;Bordeaux;44.825364;-0.5753274 +0601162M;LycĂ©e Professionnel PrivĂ© St Vincent De Paul;8 Bd Du General De Gaulle;60000;Beauvais;49.4291558;2.0897125 +0640189H;LycĂ©e Professionnel PrivĂ© St Vincent De Paul;25 Rue Rene Cassin;64000;Pau;43.301636;-0.3672163 +0783325R;LycĂ©e Professionnel PrivĂ© St Vincent De Paul;44 Avenue De Saint Cloud;78000;Versailles;48.8049728;2.1359398 +0641663K;LycĂ©e Professionnel PrivĂ© Ste Anne;Notre Dame Du Refuge;64604;Anglet;43.496758;-1.52561 +0641408H;LycĂ©e Professionnel PrivĂ© Ste Bernadette;1 Avenue De Gassion;64190;Audaux;43.3580963;-0.7937267 +0371326R;LycĂ©e Professionnel PrivĂ© Ste Clotilde St Joseph;9 Rue Henri Dunan;37400;Amboise;47.4085229;0.9738971 +0292157M;LycĂ©e Professionnel PrivĂ© Ste Elisabeth;9 Rue Lamennais;29171;Douarnenez;48.0905065;-4.3275018 +0641665M;LycĂ©e Professionnel PrivĂ© Ste Elisabeth;45 Avenue Du Pic Du Midi;64800;Igon;43.1518007;-0.2248725 +0221866J;LycĂ©e Professionnel PrivĂ© Ste Elisabeth-kersa;Chateau De Kersa;22620;Ploubazlanec;48.794307;-3.046996 +0332496J;LycĂ©e Professionnel PrivĂ© Ste Famille -saintonge;12 Rue De Saintonge;33023;Bordeaux;44.8321381;-0.5798944 +0131459R;LycĂ©e Professionnel PrivĂ© Ste Marie;2 Rue De Jouques;13100;Aix En Provence;43.5318267;5.4462333 +0511149X;LycĂ©e Professionnel PrivĂ© Ste Marie;26 Rue Du Docteur Verron;51200;Epernay;49.0449891;3.9564484 +0492003L;LycĂ©e Professionnel PrivĂ© Ste Marie-groupe Maine;3 Rue Du Margat;49105;Angers;47.4855856;-0.5607137 +0240094X;LycĂ©e Professionnel PrivĂ© Ste Marthe - St Front;74 Avenue Pasteur;24100;Bergerac;44.8601889;0.4951121 +0440307V;LycĂ©e Professionnel PrivĂ© Ste Therese;80 Rue D Anjou;44600;St Nazaire;47.2796821;-2.2092813 +0641705F;LycĂ©e Professionnel PrivĂ© St-joseph;650 Rue Hiribehere;64480;Ustaritz;43.4045104;-1.462666 +0580075S;LycĂ©e Professionnel PrivĂ© Therese Desreumaux;4 Rte De La Machine;58300;St Leger Des Vignes;46.838903;3.456643 +0501788X;LycĂ©e Professionnel PrivĂ© Thomas Helye;37 Rue Emile Zola;50100;Cherbourg Octeville;49.6370758;-1.6291084 +0340920R;LycĂ©e Professionnel PrivĂ© Turgot;94 Rue Pierre Flourens;34098;Montpellier;43.6377935;3.8324336 +0141197A;LycĂ©e Professionnel PrivĂ© Victorine Magne;39 Avenue Du 6 Juin;14103;Lisieux;49.1409396;0.2117813 +0131677C;LycĂ©e Professionnel PrivĂ© Vigie (la);Pl La Bastide La Maurelette;13015;Marseille 15;0.0;0.0 +0650067V;LycĂ©e Professionnel PrivĂ© Vincent De Paul;16 Rue St Vincent De Paul;65000;Tarbes;43.2303093;0.079062 +0840082H;LycĂ©e Professionnel PrivĂ© Vincent De Paul;1 Rue Chiron;84000;Avignon;43.9511699;4.8046831 +0620221D;LycĂ©e Professionnel Professeur Clerc;34 Rue Andre Pantigny;62230;Outreau;50.703243;1.5894109 +0220186H;LycĂ©e Professionnel Public;47 Rue Rene Le Magorec;22110;Rostrenen;48.2332958;-3.3089866 +0640066Z;LycĂ©e Professionnel Ramiro Arrue;Quart Chantaco Rte D Ascain;64500;St Jean De Luz;43.3691803;-1.6437374 +0870119G;LycĂ©e Professionnel Raoul Dautry;14 Rue Du Puy Imbert;87036;Limoges;45.8387379;1.2785581 +0900236T;LycĂ©e Professionnel Raoul Follereau;3 Rue Louis Marchal;90016;Belfort;47.6383708;6.8462164 +0860029P;LycĂ©e Professionnel Raoul Mortier;72 Avenue De L Europe;86501;Montmorillon;46.4257827;0.8591632 +0150008C;LycĂ©e Professionnel Raymond Cortat;55 Avenue Jean Chanal;15005;Aurillac;44.947086;2.4625249 +9720468B;LycĂ©e Professionnel Raymond Neris;Cite Scolaire Marin;97290;Le Marin;14.4904216;-60.9530711 +0121157T;LycĂ©e Professionnel Raymond Savignac;Rue Agnes Savignac;12203;Villefranche De Rouergue;44.3563661;2.0381734 +0860823C;LycĂ©e Professionnel Reaumur;4 Rue Salvador Allende;86036;Poitiers;46.5817037;0.370543 +0650029D;LycĂ©e Professionnel Reffye;76 Avenue Joffre;65016;Tarbes;43.2394768;0.0631445 +0540067E;LycĂ©e Professionnel Regional Du Toulois;440 Avenue Du General Bigeard;54201;Toul;48.6667052;5.8891311 +0280043V;LycĂ©e Professionnel Remi Belleau;33 Rue Bretonnerie;28400;Nogent Le Rotrou;48.3189144;0.8109948 +0130057S;LycĂ©e Professionnel Rene Caillie;173 Boulevard De St-loup;13011;Marseille 11;43.2817581;5.4283584 +0190034J;LycĂ©e Professionnel Rene Cassin;Bd Du Marquisat;19012;Tulle;45.2632089;1.7723838 +0570124A;LycĂ©e Professionnel Rene Cassin;2 Rue Rene Cassin;57052;Metz;49.1358349;6.1674007 +0594404S;LycĂ©e Professionnel Rene Cassin;Rue Des Ecoles;59182;Montigny En Ostrevent;50.3695619;3.1875453 +0750588G;LycĂ©e Professionnel Rene Cassin;185 Avenue De Versailles;75016;Paris 16;48.840068;2.2637559 +0900294F;LycĂ©e Professionnel Rene Cassin;3 Rue Louis Marchal;90016;Belfort;47.6383708;6.8462164 +0840939P;LycĂ©e Professionnel Rene Char;2 Rue Pierre A Renoir;84033;Avignon;43.9460637;4.8531827 +0850146X;LycĂ©e Professionnel Rene Couzinet;Boulevard Jean Yole;85302;Challans;46.840774;-1.87671 +0730048F;LycĂ©e Professionnel Rene Perrin;41 Rue Rene Perrin;73400;Ugine;45.7514631;6.4161324 +0310054R;LycĂ©e Professionnel Renee Bonnet;1 Allee Du Lieutenant Lafay;31432;Toulouse;43.5550127;1.4681713 +9710709U;LycĂ©e Professionnel Richeval;Quartier Richeval;97111;Morne A L Eau;0.0;0.0 +0271097W;LycĂ©e Professionnel Risle-seine;Rue Des Deportes;27500;Pont Audemer;49.353527;0.520603 +0530013B;LycĂ©e Professionnel Robert Buron;68 Rue Bellessort;53013;Laval;48.0694098;-0.7598676 +0601363F;LycĂ©e Professionnel Robert Desnos;2 Rue Jules Michelet;60803;Crepy En Valois;49.2284134;2.8941975 +0760144U;LycĂ©e Professionnel Robert Schuman;51 Avenue Du 8 Mai;76610;Le Havre;49.5161566;0.1664377 +0840042P;LycĂ©e Professionnel Robert Schuman;138 Av De Tarascon;84084;Avignon;43.9250989;4.8091279 +0601787S;LycĂ©e Professionnel Roberval;10 Rue Des Grez;60600;Breuil Le Vert;49.3641021;2.4171306 +9740004L;LycĂ©e Professionnel Roches Maigres;25 Rue Leconte De Lisle;97899;St Louis;0.0;0.0 +0570016H;LycĂ©e Professionnel Romain Rolland;30 Rue Des Peupliers;57150;Creutzwald;49.1995571;6.6988264 +0590078P;LycĂ©e Professionnel Romain Rolland;11 Rue Du Pont De Pierre;59750;Feignies;50.2915826;3.9206938 +0801628K;LycĂ©e Professionnel Romain Rolland;Rue Romain Rolland;80080;Amiens;49.9159787;2.2980057 +0171238D;LycĂ©e Professionnel Rompsay;Avenue De Perigny;17025;La Rochelle;46.15927;-1.132608 +0290078C;LycĂ©e Professionnel Roz Glas;1 Place Jean Zay;29391;Quimperle;47.8846911;-3.5532464 +0400783J;LycĂ©e Professionnel Rural Ste Elisabeth Et;137 Route De La Vieille Cote;40180;St Pandelon;43.6721063;-1.0435113 +0891159B;LycĂ©e Professionnel Saint Germain;2 Place St Germain;89000;Auxerre;47.8002699;3.5720023 +9720516D;LycĂ©e Professionnel Saint James;Route De Fonds St Denis;97250;St Pierre;14.7419691;-61.133656 +9750003E;LycĂ©e Professionnel Saint Pierre;Rue Marcel Bonin;97500;St Pierre;0.0;0.0 +0400057V;LycĂ©e Professionnel Saint-exupery;Avenue Du LycĂ©e;40161;Parentis En Born;0.0;0.0 +0620043K;LycĂ©e Professionnel Salvador Allende;Rue De L'universite;62408;Bethune;50.5213285;2.6532171 +0501677B;LycĂ©e Professionnel Sauxmarais;444 Rue De La Chasse Aux Loups;50110;Tourlaville;49.6380184;-1.591671 +7200011Z;LycĂ©e Professionnel Scamaroni;Rue Du 4eme D M N;20600;Bastia;42.697283;9.450881 +0692518M;LycĂ©e Professionnel Sermenaz;Rue De Bonn;69140;Rillieux La Pape;45.819655;4.897126 +0050009H;LycĂ©e Professionnel Sevigne;6 Rue Jean Mace;05003;Gap;44.5614745;6.0739966 +0590217R;LycĂ©e Professionnel Sevigne;151 Rue De La Malcense;59208;Tourcoing;50.730799;3.1650448 +0570100Z;LycĂ©e Professionnel Simon Lazard;25 Rue J J Kieffer;57215;Sarreguemines;49.1209348;7.074776 +0790702J;LycĂ©e Professionnel Simone Signoret;36 Rue De Malabry;79301;Bressuire;46.8407623;-0.4847921 +0930135S;LycĂ©e Professionnel Simone Weil;121 Avenue Jean Lolive;93500;Pantin;48.8927394;2.4107582 +0650028C;LycĂ©e Professionnel Sixte Vignon;12 Rue Du Taillade;65801;Aureilhan;43.2462704;0.0848529 +0410832G;LycĂ©e Professionnel Sonia Delaunay;21 Bis Rue D Auvergne;41016;Blois;47.5948627;1.3360367 +0590111A;LycĂ©e Professionnel Sonia Delaunay;121 Rue De La Mitterie;59461;Lille;50.6491583;2.9967355 +0630070J;LycĂ©e Professionnel Sonia Delaunay;Cite Pontel 7 Ter Av J.jaures;63304;Thiers;45.857907;3.545177 +0572028V;LycĂ©e Professionnel Sophie Germain;9 Impasse Colbert;57129;Thionville;49.366287;6.1478214 +0841078R;LycĂ©e Professionnel Sorgues (de);Chemin De La Lucette;84700;Sorgues;43.9976096;4.8794552 +0520923W;LycĂ©e Professionnel St Exupery;82 Rue Anatole France;52105;St Dizier;48.6430006;4.9626225 +0330082K;LycĂ©e Professionnel Sud Gironde;1 Av Des Resistants;33212;Langon;44.54777;-0.252276 +0752109K;LycĂ©e Professionnel Suzanne Valadon;7 Rue Ferdinand Flocon;75018;Paris 18;48.8916988;2.345863 +0711384U;LycĂ©e Professionnel Theodore Monod;Rue De La Loge;71450;Blanzy;46.680842;4.3819194 +0921676X;LycĂ©e Professionnel Theodore Monod;26 Avenue Leon Jouhaux;92160;Antony;48.735355;2.3064839 +0930133P;LycĂ©e Professionnel Theodore Monod;187 Rue De Brement;93130;Noisy Le Sec;48.8872329;2.4666533 +0752845K;LycĂ©e Professionnel Theophile Gautier;49 Rue De Charenton;75012;Paris 12;48.8511953;2.3727407 +0710014E;LycĂ©e Professionnel Thomas Dumorey Metiers Batimen;3 Rue Jean Rostand;71321;Chalon Sur Saone;46.7776154;4.8396918 +0382031B;LycĂ©e Professionnel Thomas Edison;Rue Normandie Niemen;38130;Echirolles;45.1467389;5.719765 +0790964U;LycĂ©e Professionnel Thomas Jean Main;39 R Coteau St Hubert;79011;Niort;46.3419255;-0.4775602 +0330031E;LycĂ©e Professionnel Toulouse Lautrec;115 Rue Joseph Abria;33000;Bordeaux;44.8339958;-0.5933109 +0810003N;LycĂ©e Professionnel Toulouse-lautrec;15 Rue Charles Portal;81027;Albi;43.925348;2.1395873 +0251349F;LycĂ©e Professionnel Toussaint Louverture;81 Route De Besancon;25304;Pontarlier;46.9141729;6.3458015 +0330142A;LycĂ©e Professionnel Tregey Rive De Garonne;24 Rue De Tregey;33015;Bordeaux;44.8391974;-0.5478589 +0251079M;LycĂ©e Professionnel Tristan Bernard;13 Rue Goya;25000;Besancon;47.2176939;5.9813344 +0290105G;LycĂ©e Professionnel Tristan Corbiere;16 Rue De Kerveguen;29671;Morlaix;48.5705251;-3.8221176 +0590186G;LycĂ©e Professionnel Turgot;2 Rue De La Paix;59053;Roubaix;50.6962493;3.1783973 +0951281B;LycĂ©e Professionnel Turgot;3 Place Au Pain;95162;Montmorency;48.9853003;2.3212499 +0750778N;LycĂ©e Professionnel Turquetil;18 Passage Turquetil;75011;Paris 11;48.8521798;2.3928741 +0030924N;LycĂ©e Professionnel Val D'allier;15 Rue Du Beaupuy;03150;Varennes Sur Allier;46.3144612;3.4066414 +0940134K;LycĂ©e Professionnel Val De Bievre;15 17 Rue D'arcueil;94257;Gentilly;48.8129955;2.3447508 +0630078T;LycĂ©e Professionnel Val De Dore;68 Avenue Leo Lagrange;63300;Thiers;45.8468155;3.5278217 +0570234V;LycĂ©e Professionnel Val De Fensch;204 Rue Victor Rimmel;57240;Knutange;49.3429114;6.0222329 +0590152V;LycĂ©e Professionnel Val De Lys;Rue Jacqueminemars;59940;Estaires;50.6466217;2.7048506 +0760145V;LycĂ©e Professionnel Val De Seine;Avenue Georges Braque;76124;Le Grand Quevilly;49.402714;1.04095 +0570324T;LycĂ©e Professionnel Valentin Metzinger;Rue De Montreal;57500;St Avold;49.1002999;6.7197012 +0590126S;LycĂ©e Professionnel Valentine Labbe;41 Rue Paul Doumer;59110;La Madeleine;50.6473736;3.0721697 +0921229L;LycĂ©e Professionnel Valmy;130 Boulevard De Valmy;92700;Colombes;48.9306689;2.2551693 +0920166F;LycĂ©e Professionnel Vassily Kandinsky;96 Boulevard Bineau;92200;Neuilly Sur Seine;48.888011;2.2757687 +0890819G;LycĂ©e Professionnel Vauban;22 Rue Faidherbe;89010;Auxerre;47.8000287;3.565054 +0380098A;LycĂ©e Professionnel Vaucanson;27 Rue Anatole France;38030;Grenoble;45.1722442;5.7083809 +0130170P;LycĂ©e Professionnel Vauvenargues;60 Boulevard Carnot;13100;Aix En Provence;43.5284055;5.4549853 +0180823X;LycĂ©e Professionnel Vauvert;Rue Marguerite Audoux;18000;Bourges;47.0776989;2.3787816 +0592832H;LycĂ©e Professionnel Vertes Feuilles;95 Rue Georges Maertens;59871;St Andre Lez Lille;50.6327986;3.0613862 +0260037Z;LycĂ©e Professionnel Victor Hugo;442 Avenue Victor Hugo;26021;Valence;44.9076529;4.8831446 +0840044S;LycĂ©e Professionnel Victor Hugo;139 Av Victor Hugo;84208;Carpentras;44.0515235;5.0474084 +0370771M;LycĂ©e Professionnel Victor Laloux;2 Rue Marcel Proust;37200;Tours;47.358625;0.7029341 +0140018U;LycĂ©e Professionnel Victor Lepine;40 Rue Victor Lepine;14000;Caen;49.1711498;-0.3420176 +9740020D;LycĂ©e Professionnel Victor Schoelcher;14 Rue Saint Philippe;97899;St Louis;45.7504748;4.8660377 +0250066L;LycĂ©e Professionnel Viette;1 B Rue P Donzelot;25206;Montbeliard;47.4951765;6.8092234 +0130065A;LycĂ©e Professionnel Viste (la);Traverse Bonnet;13015;Marseille 15;43.35932;5.3571954 +0921500F;LycĂ©e Professionnel Voilin;26 Rue Lucien Voilin;92800;Puteaux;48.8828072;2.2370604 +0930846P;LycĂ©e Professionnel Voillaume;136 Rue De Mitry;93604;Aulnay Sous Bois;48.9457777;2.5099926 +0301210C;LycĂ©e Professionnel Voltaire;399 Rue Bellini;30903;Nimes;43.8266006;4.3381288 +0620258U;LycĂ©e Professionnel Voltaire;Place Bertin Ledoux;62410;Wingles;50.494578;2.852404 +9740015Y;LycĂ©e Professionnel Vue Belle;;97422;St Paul;49.164628;5.38442 +0670023L;LycĂ©e Professionnel Xavier Nessel;123 Route De Strasbourg;67504;Haguenau;48.8001884;7.7656679 +0510037N;LycĂ©e Professionnel Yser;Avenue De L'yser;51053;Reims;49.2484301;4.0585216 +0692682R;LycĂ©e Rural PrivĂ© Jean Monnet;304 Boulevard De La Bardiere;69590;St Symphorien Sur Coise;45.6295165;4.4610934 +0441788E;LycĂ©e Techn Et Professionnel Le Bois Tillac;Allee Du Bois Tillac;44640;Le Pellerin;47.2018844;-1.7712182 +0390970T;LycĂ©e Technique Rural PrivĂ©;1 Allee De La Savine;39400;Morbier;46.5571966;6.0150673 +0291208F;LycĂ©e Technol Agricole PrivĂ©;Le Nivot;29590;Loperec;48.312256;-4.011024 +0351954J;LycĂ©e Technol Agricole PrivĂ©;L'abbaye;35160;Montfort Sur Meu;48.130223;-1.943634 +0560228U;LycĂ©e Technol Agricole PrivĂ© Anne De Bretagne;5 Place Anne De Bretagne;56500;Locmine;47.8833082;-2.8324355 +0221593M;LycĂ©e Technol Agricole PrivĂ© Centre De Formation D'armor;Le Chef Du Bois;22450;Pommerit Jaudy;48.742229;-3.244975 +0561408B;LycĂ©e Technol Agricole PrivĂ© Des Pays De Vilaine;Route De Peillac;56220;St Jacut Les Pins;47.7154571;-2.1948712 +0221033D;LycĂ©e Technol Agricole PrivĂ© Espa;La Ville Davy;22120;Quessoy;48.425678;-2.667812 +0311270M;LycĂ©e Technol Agricole PrivĂ© La Cadene;Rue Buissonniere;31670;Labege;43.535875;1.521728 +0350918H;LycĂ©e Technol Agricole PrivĂ© Les Vergers;4 Rue Des Murets;35120;Dol De Bretagne;48.5521611;-1.7541892 +0292000S;LycĂ©e Technol Agricole PrivĂ© St Antoine;11 Rue St J-b De La Salle;29870;Lannilis;48.5722565;-4.5194361 +0221673Z;LycĂ©e Technol Agricole PrivĂ© St Ilan;52 Rue De St Ilan;22360;Langueux;48.5048429;-2.7062202 +0291751W;LycĂ©e Technol Agricole PrivĂ© St Joseph;12 Allee De Kerustum;29101;Quimper;47.9804929;-4.0927405 +0351953H;LycĂ©e Technol Agricole PrivĂ© St Nicolas-la Providence;28 Rue De Rennes;35360;Montauban De Bretagne;48.1977763;-2.0426486 +0211503V;LycĂ©e Technol.agricole PrivĂ© Anne-marie Javouhey;Rue Anne-marie Javouhey;21250;Chamblanc;47.0220768;5.1517811 +0601194X;LycĂ©e Technol.agricole PrivĂ© Charles Quentin;1 Rue Sabatier;60350;Pierrefonds;49.3433941;2.9760698 +0530816Z;LycĂ©e Technol.agricole PrivĂ© D Orion;2 Rue De La Liberation;53600;Evron;48.1565742;-0.4088119 +0441032H;LycĂ©e Technol.agricole PrivĂ© Ecole D'agriculture;29 Rue De Rennes;44590;Derval;47.6713835;-1.6741115 +0491804V;LycĂ©e Technol.agricole PrivĂ© Edmond Michelet;Le Pre Neuf;49330;Etriche;47.6505269;-0.444111 +0440981C;LycĂ©e Technol.agricole PrivĂ© Gabriel Deshayes;4, Route De Redon;44530;St Gildas Des Bois;47.5189743;-2.0396361 +0320633P;LycĂ©e Technol.agricole PrivĂ© Institut Saint-christophe;Aux Stournes;32140;Masseube;43.429726;0.579788 +0441795M;LycĂ©e Technol.agricole PrivĂ© La Motte;17 Rue Abbe Perrin;44320;St Pere En Retz;47.2054129;-2.0427713 +0491809A;LycĂ©e Technol.agricole PrivĂ© Les 3 Provinces;11 Boulevard Jeanne D Arc;49304;Cholet;47.0640598;-0.8673109 +0840797K;LycĂ©e Technol.agricole PrivĂ© Les Chenes;524 Av Du Pont Des Fontaines;84200;Carpentras;44.0610607;5.0551521 +0850609A;LycĂ©e Technol.agricole PrivĂ© Les Etablieres;Rte De Nantes;85015;La Roche Sur Yon;46.7055761;-1.4296139 +0261067U;LycĂ©e Technol.agricole PrivĂ© Les Mandailles;;26330;Chateauneuf De Galaure;45.234516;4.960742 +0491027A;LycĂ©e Technol.agricole PrivĂ© Pouille;;49136;Les Ponts De Ce;47.4268572;-0.5516965 +0491802T;LycĂ©e Technol.agricole PrivĂ© Robert D Arbrissel;Place Urbain Ii;49120;Chemille;47.210608;-0.7294054 +0530818B;LycĂ©e Technol.agricole PrivĂ© Rochefeuille;Route De Caen;53100;Mayenne;48.543782;-0.207489 +0801613U;LycĂ©e Technol.agricole PrivĂ© Sainte Colette;Rue De L Enclos;80800;Corbie;49.9096772;2.510355 +0711440E;LycĂ©e Technol.agricole PrivĂ© Ste Marguerite Marie;Jannots;71220;Verosvres;46.401991;4.453972 +0810833R;LycĂ©e Technol.agricole PrivĂ© Touscayrats;Touscayrats;81110;Verdalle;43.5060659;2.180924 +0421674S;LycĂ©e Technol.prive Agricole Le Puits De L'aune;Rue Louis Blanc;42110;Feurs;45.7383928;4.2364878 +0130161E;LycĂ©e Technologique Adam De Craponne;218 Rue Chateau Redon;13651;Salon De Provence;43.6355195;5.0986535 +0131656E;LycĂ©e Technologique Agricole Aix-valabre;Chemin Moulin Fort;13548;Gardanne;43.464305;5.453012 +0040512K;LycĂ©e Technologique Agricole Digne-carmejane;Route D Espinouse;04510;Le Chaffaut St Jurson;44.047146;6.158502 +0840110N;LycĂ©e Technologique Agricole Francois Petrarque;Site Agroparc;84911;Avignon;43.949317;4.805528 +0251481Z;LycĂ©e Technologique Agricole Jeanne D'arc;22 Rue Jeanne D'arc;25300;Pontarlier;46.9039198;6.3575183 +0251479X;LycĂ©e Technologique Agricole Les Fontenelles;15 Rue Du Couvent;25210;Les Fontenelles;47.1959275;6.7471417 +0840607D;LycĂ©e Technologique Agricole Louis Giraud;Chemin De L Hermitage;84208;Carpentras;44.0836262;5.059517 +0750710P;LycĂ©e Technologique Auguste Renoir;24 Rue Ganneron;75018;Paris 18;48.8870203;2.3283565 +0750697A;LycĂ©e Technologique Batiment-saint-lambert;15 Rue Saint Lambert;75015;Paris 15;48.8377738;2.2915594 +0420937R;LycĂ©e Technologique Benoit Fourneyron;24 Rue Virgile;42013;St Etienne;45.4176774;4.4197149 +0520028Y;LycĂ©e Technologique Blaise Pascal;1 Avenue Marcel Paul;52100;St Dizier;48.6456201;4.9737363 +0500087Y;LycĂ©e Technologique Claude Lehec;Rue Dauphine;50600;St Hilaire Du Harcouet;48.5715264;-1.0976365 +0190012K;LycĂ©e Technologique Danton;Rue Danton;19311;Brive La Gaillarde;45.1590287;1.5216108 +9760220K;LycĂ©e Technologique De Dzoumogne;;97650;Bandraboua;0.0;0.0 +0590223X;LycĂ©e Technologique Du Hainaut;1 Avenue Villars;59322;Valenciennes;50.3582813;3.5107769 +0750672Y;LycĂ©e Technologique Duperre Esaa;11 Rue Dupetit Thouars;75003;Paris 03;48.8652834;2.3620241 +0750685M;LycĂ©e Technologique Ec Nat Chimie Physiq Biologie;11 Rue Pirandello;75013;Paris 13;48.8365925;2.3562968 +0750612H;LycĂ©e Technologique Ec Nat Sup Des Arts Appliques;63 65 Rue Olivier De Serres;75015;Paris 15;48.833647;2.2948847 +0594391C;LycĂ©e Technologique Ec Sup Arts Appliques Textile;539 Avenue Des Nations Unies;59056;Roubaix;50.6936638;3.1752265 +0750707L;LycĂ©e Technologique École Nationale De Commerce;70 Boulevard Bessieres;75017;Paris 17;48.8971407;2.3208753 +0801327H;LycĂ©e Technologique Edouard Branly;70 Boulevard De Saint Quentin;80098;Amiens;49.8791607;2.2965051 +0480009Z;LycĂ©e Technologique Emile Peytavin;Avenue Du 11 Novembre;48001;Mende;44.5233613;3.4833187 +0750686N;LycĂ©e Technologique Estienne Esaig;18 Boulevard Auguste Blanqui;75013;Paris 13;48.8308013;2.3524558 +0211033J;LycĂ©e Technologique Gustave Eiffel;15 Avenue Champollion;21074;Dijon;47.3338611;5.0676398 +0590011S;LycĂ©e Technologique Gustave Eiffel;96 Rue Jules Lebleu;59427;Armentieres;50.6880881;2.8733271 +0290070U;LycĂ©e Technologique Jean Chaptal;35 Chemin Des Justices;29191;Quimper;47.9918404;-4.1193383 +0750674A;LycĂ©e Technologique Jules Siegfried;12 Rue D'abbeville;75010;Paris 10;48.8788604;2.3501271 +0760056Y;LycĂ©e Technologique Jules Siegfried;1 Rue Dume D'aplemont;76054;Le Havre;49.4974627;0.1241609 +0141687H;LycĂ©e Technologique Jules Verne;12 Rue Lucien Bossoutrot;14126;Mondeville;49.176963;-0.319769 +0332836D;LycĂ©e Technologique Latresne Constructions Aeronautiques;Chateau De Latresne;33360;Latresne;44.779974;-0.491589 +0380034F;LycĂ©e Technologique Louise Michel;30 Rue Louise Michel;38037;Grenoble;45.1687882;5.7081825 +0171428K;LycĂ©e Technologique LycĂ©e De La Mer Et Du Littoral;Rue William Bertrand;17560;Bourcefranc Le Chapus;45.846735;-1.15707 +9840268U;LycĂ©e Technologique LycĂ©e Hotelier De Tahiti;Punaauia;98718;Punaauia;-17.5943083;-149.6116857 +0130051K;LycĂ©e Technologique Marie Curie;16 Boulevard Jeanne D Arc;13392;Marseille 05;43.2937083;5.4004604 +0710012C;LycĂ©e Technologique Nicephore Niepce;141 Avenue Boucicaut;71321;Chalon Sur Saone;46.7888739;4.8359262 +0330029C;LycĂ©e Technologique Nicolas Bremontier;152 Cours De L Yser;33077;Bordeaux;44.823338;-0.5706489 +0130011S;LycĂ©e Technologique Pasquet;54 Bd Marcellin Berthelot;13200;Arles;43.6724263;4.6297406 +7200583W;LycĂ©e Technologique Paul Vincensini;Rue Du 4eme D M M;20600;Bastia;42.697283;9.450881 +0031041R;LycĂ©e Technologique PrivĂ© Anna Rodier;39 Cours Jean Jaures;03008;Moulins;46.5669568;3.3349455 +0573683U;LycĂ©e Technologique PrivĂ© Anne De Mejanes;3 Rue Goussaud;57000;Metz;49.1235079;6.173823 +0170124T;LycĂ©e Technologique PrivĂ© Aris Formation Pigier;16 Rue Leonce Vieljeux;17000;La Rochelle;46.1578836;-1.1553283 +9710993C;LycĂ©e Technologique PrivĂ© Bel Air;Moudong Nord;97122;Baie Mahault;0.0;0.0 +0331591A;LycĂ©e Technologique PrivĂ© Bel Orme;67 Rue De Bel Orme;33000;Bordeaux;44.8497536;-0.5941378 +0133286B;LycĂ©e Technologique PrivĂ© Belsunce;13 Rue Fauchier;13002;Marseille 02;43.3044451;5.3717817 +0690646C;LycĂ©e Technologique PrivĂ© Carrel;7 Rue Pierre Robin;69362;Lyon 07;45.7493853;4.8581918 +0133424B;LycĂ©e Technologique PrivĂ© Caucadis;Bd Alfred Casile;13127;Vitrolles;43.4478245;5.2426452 +0133425C;LycĂ©e Technologique PrivĂ© Celony;4 Bis Av De Lattre De Tassigny;13090;Aix En Provence;43.5303056;5.4383758 +0131402D;LycĂ©e Technologique PrivĂ© Charles Peguy;102 Rue Sylvabelle;13006;Marseille 06;43.2882408;5.3748305 +0131681G;LycĂ©e Technologique PrivĂ© Chimie Et Biologie La Forbine;4 Boulevard De La Forbine;13011;Marseille 11;43.2868081;5.4665119 +0542448T;LycĂ©e Technologique PrivĂ© Claude Daunot;10 Boulevard Clemenceau;54052;Nancy;48.6779501;6.1805671 +0592969G;LycĂ©e Technologique PrivĂ© Dampierre;85 Avenue De Denain;59326;Valenciennes;50.3543272;3.4992095 +0521045D;LycĂ©e Technologique PrivĂ© De L'assomption;50 Rue Du Mal De Lattre;52108;St Dizier;48.6381589;4.9511085 +0593071T;LycĂ©e Technologique PrivĂ© Deforest De Lewarde;151 Rue Jean De Gouy;59504;Douai;50.3715086;3.0819657 +0133396W;LycĂ©e Technologique PrivĂ© Don Bosco;78 Rue Stanislas Torrents;13006;Marseille 06;43.2855609;5.3795529 +0691659D;LycĂ©e Technologique PrivĂ© Don Bosco;12 Et 24 Montee Saint Laurent;69322;Lyon 05;45.7510345;4.8167844 +0070098U;LycĂ©e Technologique PrivĂ© Du Sacre Coeur;7 Avenue De La Gare;07301;Tournon Sur Rhone;45.0637796;4.834587 +0753537M;LycĂ©e Technologique PrivĂ© Ec Sup Tech Biologie Appliquee;56 Rue Planchat;75020;Paris 20;48.8560952;2.3964535 +0750330B;LycĂ©e Technologique PrivĂ© Ec Technique Sup Laboratoire;93 95 R Du Dessous Des Berges;75013;Paris 13;48.8281956;2.3731118 +0593078A;LycĂ©e Technologique PrivĂ© Fondation Depoorter;7-9 Rue Depoorter;59524;Hazebrouck;50.7201268;2.5342537 +0132280H;LycĂ©e Technologique PrivĂ© Henri Leroy;1 Rue Des Ecoles;13230;Port St Louis Du Rhone;43.3867633;4.8031175 +0690631L;LycĂ©e Technologique PrivĂ© Icof;8 Avenue Debrousse;69005;Lyon 05;45.7505097;4.8141393 +0870097H;LycĂ©e Technologique PrivĂ© Inst D Econ Sociale Familiale;5 Rue De La Cite;87000;Limoges;45.8290178;1.2642372 +0141175B;LycĂ©e Technologique PrivĂ© Institut Lemonnier;60 Rue D Herouville;14013;Caen;49.1935816;-0.3487181 +0751883P;LycĂ©e Technologique PrivĂ© Institut PrivĂ©;119 Boulevard Diderot;75012;Paris 12;48.8479674;2.3912553 +0681885E;LycĂ©e Technologique PrivĂ© Institut Sonnenberg;1 Rue Du Moulin;68130;Carspach;47.6651522;7.2235055 +0380018N;LycĂ©e Technologique PrivĂ© Iser - Bordier;26 Rue Prosper Merimee;38100;Grenoble;45.1737355;5.7260681 +0381900J;LycĂ©e Technologique PrivĂ© Itec Boisfleury Europe;76 Grande Rue;38701;La Tronche;45.2047507;5.7377606 +0741307U;LycĂ©e Technologique PrivĂ© Jeanne D Arc;18 Bis Avenue Jules Ferry;74200;Thonon Les Bains;46.3724399;6.4832781 +0592966D;LycĂ©e Technologique PrivĂ© Jehanne D'arc;106 Rue De Lille;59202;Tourcoing;50.7228019;3.1512774 +0690634P;LycĂ©e Technologique PrivĂ© La Mache;75 Boulevard Jean Xxiii;69373;Lyon 08;45.7364603;4.8695511 +0754037F;LycĂ©e Technologique PrivĂ© La Plaine Monceau;9 Boulevard De Courcelles;75008;Paris 08;48.881052;2.3158423 +0061852F;LycĂ©e Technologique PrivĂ© La Providence;236 Route De Grenoble;06200;Nice;43.6819305;7.1990341 +0592967E;LycĂ©e Technologique PrivĂ© La Sagesse;40 Rue De Mons;59306;Valenciennes;50.3598706;3.5288319 +0593061G;LycĂ©e Technologique PrivĂ© La Sagesse;7 Rue Du Temple;59405;Cambrai;50.1755939;3.2277333 +0500127S;LycĂ©e Technologique PrivĂ© Le Bon Sauveur;Rue E De Surville;50001;St Lo;49.112089;-1.094787 +0133554T;LycĂ©e Technologique PrivĂ© Le Rocher;42 Boulevard Ledru Rollin;13300;Salon De Provence;43.6441863;5.0948018 +0741101V;LycĂ©e Technologique PrivĂ© Les Bressis;85 Route Des Creuses;74600;Seynod;45.8932394;6.1001202 +0060735S;LycĂ©e Technologique PrivĂ© Les Fauvettes;44 Avenue Jean De Noailles;06400;Cannes;43.5541346;6.9996242 +0382943T;LycĂ©e Technologique PrivĂ© Les Portes De Chartreuse;387 Avenue De Stalingrad;38343;Voreppe;45.2947821;5.6336357 +0350787R;LycĂ©e Technologique PrivĂ© Les Rimains;Esplanade De La Gare;35409;St Malo;48.5706771;-1.9687989 +0141418R;LycĂ©e Technologique PrivĂ© Les Rosiers;17 Chemin De Rocques;14100;Lisieux;49.154762;0.2306014 +0754016H;LycĂ©e Technologique PrivĂ© L'initiative;24 Rue Bouret;75019;Paris 19;48.8814352;2.3745457 +0121428M;LycĂ©e Technologique PrivĂ© Louis Querbes;29 Rue Maurice Bompard;12000;Rodez;44.3485775;2.5792732 +0641534V;LycĂ©e Technologique PrivĂ© Ltp Nay Baudreix;Route De Lys;64800;Baudreix;43.20577;-0.2589659 +0071359P;LycĂ©e Technologique PrivĂ© Marc Seguin-saint Charles;Route De Californie;07100;Annonay;45.2440877;4.6758762 +0220116G;LycĂ©e Technologique PrivĂ© Marie Balavenne;47 Boulevard Laennec;22005;St Brieuc;48.5111408;-2.7744799 +0831207L;LycĂ©e Technologique PrivĂ© Marie France;220 Avenue Marcel Castie;83000;Toulon;43.1234854;5.941595 +0133474F;LycĂ©e Technologique PrivĂ© Marie Gasquet;38 R Electriciens St Barnabe;13012;Marseille 12;43.3041972;5.4172944 +0131398Z;LycĂ©e Technologique PrivĂ© Maximilien De Sully;52 Rue Auguste Blanqui;13006;Marseille 06;43.2922507;5.3874655 +0131436R;LycĂ©e Technologique PrivĂ© Modele Electronique;233 Bd St Marcel;13396;Marseille 11;43.2865274;5.4686969 +0754048T;LycĂ©e Technologique PrivĂ© Morin;182 Rue De Vaugirard;75015;Paris 15;48.8421148;2.3096576 +0311201M;LycĂ©e Technologique PrivĂ© Myriam;9 Et 20 Rue Mage;31000;Toulouse;43.5977887;1.4472602 +0440259T;LycĂ©e Technologique PrivĂ© Nd D'esperance;15 Rue Du Bois Savary;44615;St Nazaire;47.2747567;-2.2080565 +0570210U;LycĂ©e Technologique PrivĂ© Notre Dame;2 Rue De Metz;57245;Peltre;49.0684672;6.2497106 +0690553B;LycĂ©e Technologique PrivĂ© Notre Dame;72 Rue Des Jardiniers;69657;Villefranche Sur Saone;45.9885367;4.7206723 +0142084P;LycĂ©e Technologique PrivĂ© Notre Dame De Fidelite;8 Rue Du Petit Clos Saint Marc;14074;Caen;49.2111144;-0.3733099 +0142108R;LycĂ©e Technologique PrivĂ© Notre Dame De Nazareth;Avenue De La Basilique;14440;Douvres La Delivrande;49.2970159;-0.3732011 +0593006X;LycĂ©e Technologique PrivĂ© Notre-dame D'annay;15 Place Du Concert;59000;Lille;50.6422985;3.0607077 +0690642Y;LycĂ©e Technologique PrivĂ© O R T;133 Rue Marius Berliet;69008;Lyon 08;45.7380862;4.8677943 +0931796X;LycĂ©e Technologique PrivĂ© Ort;39-45 Rue Raspail;93107;Montreuil;48.8528323;2.424645 +0133334D;LycĂ©e Technologique PrivĂ© Ort Leon Bramson;9 Rue Des Forges;13010;Marseille 10;43.2791281;5.4104058 +9711022J;LycĂ©e Technologique PrivĂ© Pensionnat De Versailles;8 Rue Victor Hugues;97100;Basse Terre;16.1985639;-61.5901551 +0542161F;LycĂ©e Technologique PrivĂ© Pierre De Coubertin;5 R Du President Robert Schuman;54000;Nancy;48.6825501;6.1799118 +0650065T;LycĂ©e Technologique PrivĂ© Pradeau-la Sede;14 Rue Mesclin;65912;Tarbes;43.2366268;0.0682376 +0070099V;LycĂ©e Technologique PrivĂ© Saint Andre;18 Rue Emile Combes;07400;Le Teil;44.5388909;4.6878848 +0573519R;LycĂ©e Technologique PrivĂ© Saint Andre;1 Rue De L'eglise;57840;Ottange;49.4410443;6.0178869 +0622199D;LycĂ©e Technologique PrivĂ© Saint Denis;8 Place Saint Jean;62504;St Omer;50.750051;2.2606387 +0593077Z;LycĂ©e Technologique PrivĂ© Saint Joseph;10 Rue De La Paix;59529;Hazebrouck;50.718103;2.5331551 +0622198C;LycĂ©e Technologique PrivĂ© Saint Joseph;26 Route De Calais;62280;St Martin Boulogne;50.7367968;1.6223612 +0741414K;LycĂ©e Technologique PrivĂ© Saint Joseph;St Martin Sur Arve;74703;Sallanches;45.912929;6.689997 +0573073F;LycĂ©e Technologique PrivĂ© Saint Joseph-la Providence;10 Rue De L'abbe Heydel;57801;Freyming Merlebach;49.146198;6.811347 +0260072M;LycĂ©e Technologique PrivĂ© Saint Louis;Clos Soubeyran;26402;Crest;0.0;0.0 +0592963A;LycĂ©e Technologique PrivĂ© Saint Martin;54 Rue De Lille;59062;Roubaix;50.6867645;3.1712288 +0542295B;LycĂ©e Technologique PrivĂ© Saint Michel;Charteuse De Bosserville;54510;Art Sur Meurthe;48.664136;6.243642 +0593104D;LycĂ©e Technologique PrivĂ© Saint Pierre;10 Rue Du General Goutierre;59612;Fourmies;50.0092398;4.0503645 +0573366Z;LycĂ©e Technologique PrivĂ© Saint Vincent De Paul;4 Rue Marie Douchet;57440;Algrange;49.3580552;6.0494087 +0573172N;LycĂ©e Technologique PrivĂ© Sainte ChrĂ©tienne;1 Passage Du Pensionnat;57506;St Avold;49.1049716;6.7083946 +0141178E;LycĂ©e Technologique PrivĂ© Sainte Ursule;1 Place Wurzburg;14067;Caen;49.2015078;-0.3799957 +0810095N;LycĂ©e Technologique PrivĂ© Sainte-CĂ©cile;1 Avenue Du Breuil;81000;Albi;43.9432297;2.1483149 +0811175M;LycĂ©e Technologique PrivĂ© Saint-Joseph;38 Avenue De Lavaur;81100;Castres;43.6075533;2.2350212 +0650921Y;LycĂ©e Technologique PrivĂ© Saint-Pierre;24 Avenue D'azereix;65000;Tarbes;43.2289169;0.0578127 +0755121J;LycĂ©e Technologique PrivĂ© Sinai;9 Rue Jacques Ibert;75017;Paris 17;48.8890075;2.2909761 +0371439N;LycĂ©e Technologique PrivĂ© St Francois Inst N-d La Riche;30 Rue Delperier;37058;Tours;47.3925342;0.6769264 +0132922F;LycĂ©e Technologique PrivĂ© St Jean;76 Av Georges Borel;13300;Salon De Provence;43.6406902;5.0821442 +0840940R;LycĂ©e Technologique PrivĂ© St Jean Baptiste De La Salle;9 Rue Notre Dame Sept Douleurs;84008;Avignon;43.9489266;4.815644 +0593005W;LycĂ©e Technologique PrivĂ© St Jean-baptiste De La Salle;2 Rue Jean Le Vasseur;59000;Lille;50.6138111;3.0423599 +0100059K;LycĂ©e Technologique PrivĂ© St Joseph;21 Rue Cloitre St Etienne;10042;Troyes;48.2970565;4.0816607 +0783323N;LycĂ©e Technologique PrivĂ© St Thomas De Villeneuve;15 Rue Des Louviers;78100;St Germain En Laye;48.8983954;2.0921371 +0601950U;LycĂ©e Technologique PrivĂ© St Vincent De Paul;8 Bd Du General De Gaulle;60000;Beauvais;49.4291558;2.0897125 +0641441U;LycĂ©e Technologique PrivĂ© Ste Anne;Notre Dame Du Refuge;64604;Anglet;43.496758;-1.52561 +0911981J;LycĂ©e Technologique PrivĂ© Ste Jeanne D Arc;36 Av Du 8 Mai 1945;91120;Palaiseau;48.7096979;2.2428188 +0133395V;LycĂ©e Technologique PrivĂ© Ste Marie;2 Rue De Jouques;13100;Aix En Provence;43.5318267;5.4462333 +0490887Y;LycĂ©e Technologique PrivĂ© Ste Marie;43 Rue St Bonaventure;49307;Cholet;47.0594586;-0.8778263 +0593034C;LycĂ©e Technologique PrivĂ© Theophile Legrand;16 Rue Bertrand;59720;Louvroil;50.2659008;3.9615428 +0492019D;LycĂ©e Technologique PrivĂ© Urbain Mongazon;1 Rue Du Colombier;49036;Angers;47.4592971;-0.5294038 +0593058D;LycĂ©e Technologique PrivĂ© Vauban;1 Place Vauban;59140;Dunkerque;51.0294231;2.3781519 +0130049H;LycĂ©e Technologique Rempart (rue Du);1 Rue Du Rempart;13007;Marseille 07;43.2889391;5.3652071 +0670086E;LycĂ©e Technologique Rene Cassin;4 Rue Schoch;67046;Strasbourg;48.5829505;7.7702809 +0332468D;LycĂ©e Technologique St Louis;Rue Capitaine Kleber Dupuy;33028;Bordeaux;44.837789;-0.57918 +0511219Y;LycĂ©e Viticole De La Champagne;Route D'oger;51190;Avize;48.9669337;4.0084888 +0241111C;LycĂ©ee Prof Agricole;Domaine De La Brie;24240;Monbazillac;44.793529;0.492161 +0301433V;Maison Famil Rurale Horticole Le Grand Mas;Route De Nimes;30700;Uzes;43.9965584;4.4132526 +0520934H;Maison Familiale Rurale Des Recollets;Avenue Du 21eme R.i.;52202;Langres;47.849961;5.3290179 +0753494R;Museum D'histoire Naturelle;57 Rue Cuvier;75231;Paris 05;48.8441222;2.3556153 +0753496T;Observatoire De Paris;61 Avenue De L'observatoire;75014;Paris 14;48.8369989;2.3365396 +0190058K;Ogec Notre Dame Jeanne D'arc LycĂ©e Bossuet;11 Rue Bossuet;19107;Brive La Gaillarde;45.1582613;1.52277 +0750030A;Palais De La Decouverte;Avenue Franklin D Roosevelt;75008;Paris 08;48.8705294;2.3101262 +0450762J;Sarl Inst. Formation Prof.;31 Av Louis Maurice Chautemps;45200;Montargis;48.0069208;2.7464731 +0382974B;Ste Grenobloise De Formation Sup2i;9 Rue General Ferrie;38100;Grenoble;45.1796694;5.723777 +9710585J;UniversitĂ© Antilles Guyane;Bd Legitimus Bp 771;97159;Pointe A Pitre;16.241111;-61.533056 +0131842G;UniversitĂ© Aix-marseille 1;3, Place Victor-hugo;13331;Marseille 03;43.3042684;5.3782868 +0131843H;UniversitĂ© Aix-marseille 2;58 Bd Charles Livon;13284;Marseille 07;43.2920635;5.3590116 +0132364Z;UniversitĂ© Aix-marseille 3;3, Avenue Robert Schuman;13628;Aix En Provence;43.5192855;5.4406011 +0331764N;UniversitĂ© Bordeaux 1;351, Cours De La Liberation;33405;Talence;44.808849;-0.5914683 +0331765P;UniversitĂ© Bordeaux 2;146, Rue Leo Saignat;33076;Bordeaux;44.8264547;-0.6017476 +0331766R;UniversitĂ© Bordeaux 3;Esplanade Montaigne Dom Univ;33405;Talence;44.802614;-0.588054 +0332929E;Universite Bordeaux 4;Avenue Leon Duguit;33608;Pessac;44.7988471;-0.6154882 +0631262E;UniversitĂ© Clermont-ferrand 1;49 Boulevard Gergovia Bp 32;63001;Clermont Ferrand;45.777222;3.087025 +0631525R;UniversitĂ© Clermont-ferrand 2;34 Avenue Carnot Bp 185;63006;Clermont Ferrand;45.777222;3.087025 +0801344B;UniversitĂ© D'amiens;Chemin Du Thil;80025;Amiens;49.8762421;2.2668361 +0490970N;UniversitĂ© D'angers;Rue Des Arenes Bp 3532;49035;Angers;47.478419;-0.563166 +0623957P;UniversitĂ© D'arras;9 Rue Du Temple Bp 665;62030;Arras;50.291002;2.777535 +0840685N;UniversitĂ© D'avignon;35 Rue Joseph Vernet Bp 218;84010;Avignon;43.949317;4.805528 +0251215K;UniversitĂ© De Besancon;3 Rue Goudimel;25030;Besancon;47.2402992;6.0225572 +0290346U;UniversitĂ© De Brest;Rue Des Archives Bp 137;29269;Brest;48.390394;-4.486076 +0561718N;Universite De Bretagne Sud;12 Avenue Saint Symphorien;56000;Vannes;47.6623582;-2.7552643 +0141408E;UniversitĂ© De Caen;Esplanade De La Paix;14032;Caen;49.1881893;-0.3638111 +0951793H;UniversitĂ© De Cergy-pontoise;33 Bd Du Port Bp 220;95011;Cergy;49.035617;2.060325 +0730858L;UniversitĂ© De Chambery;27 Rue Marcoz Bp 1104;73011;Chambery;45.564601;5.917781 +0601223D;UniversitĂ© De Compiègne;Rue Roger Couttolenc Bp 649;60206;Compiegne;49.417816;2.826145 +7200664J;UniversitĂ© De Corse;7 Av J Nicoli Bp 52;20250;Corte;42.309409;9.149022 +0211237F;UniversitĂ© De Dijon;Campus Univ Montmuzard Bp 138;21004;Dijon;47.322047;5.04148 +0595964M;UniversitĂ© De Dunkerque;9 Quai De La Citadelle Bp 1022;59375;Dunkerque;51.03456;2.3752019 +9830445S;Universite De La Nouvelle-caledonie;145 Av James Cook;98847;Noumea;-22.2686289;166.4242812 +9840349G;Universite De La Polynesie Francaise;Punaauia;98714;Papeete;-17.5753232;-149.6059799 +9740478B;UniversitĂ© De La RĂ©union;15 Avenue Rene Cassin;97489;St Denis;47.827397;1.9235839 +0171463Y;UniversitĂ© De La Rochelle;23 Avenue Albert Einstein;17071;La Rochelle;46.148256;-1.154995 +0870669E;UniversitĂ© De Limoges;Hotel Burgy 13 Rue De Geneve;87065;Limoges;45.8340567;1.2442706 +0772502B;Universite De Marne La Vallee;5 Bld Descartes A Champs/marne;77454;Champs Sur Marne;48.8400977;2.5862493 +0932056E;UniversitĂ© De Marne La VallĂ©e;2 Rue De La Butte Verte;93160;Noisy Le Grand;48.8423725;2.5796219 +0572081C;UniversitĂ© De Metz;Ile Du Saulcy Bp 794;57012;Metz;49.119666;6.176905 +0681166Y;UniversitĂ© De Mulhouse;2, Rue Des Freres Lumiere;68093;Mulhouse;47.73208;7.315734 +0440984F;UniversitĂ© De Nantes;Quai Tourville Bp 1026;44035;Nantes;47.218371;-1.553621 +0060931E;UniversitĂ© De Nice;28, Avenue Valrose;06034;Nice;43.7158479;7.2657113 +0640251A;UniversitĂ© De Pau Et Pays De L'adour;Av De L Universite Bp 576;64012;Pau;43.2951;-0.370797 +0660437S;UniversitĂ© De Perpignan;52 Avenue De Villeneuve;66860;Perpignan;42.6809965;2.9025928 +0860856N;UniversitĂ© De Poitiers;15 Rue De L Hotel Dieu;86034;Poitiers;46.5857078;0.3458139 +0511296G;UniversitĂ© De Reims;Villa Douce 9 Bd De La Paix;51097;Reims;49.2543882;4.0406026 +0761904G;UniversitĂ© De Rouen;1, Rue Thomas Becket;76821;Mont St Aignan;49.4603349;1.0673087 +0421095M;UniversitĂ© De Saint-etienne;34 Rue Francis Baulier;42023;St Etienne;45.4294967;4.391713 +0830766G;UniversitĂ© De Toulon;Avenue De L Universite Bp 132;83957;La Garde;43.128777;6.017199 +0370800U;UniversitĂ© De Tours;3, Rue Des Tanneurs;37041;Tours;47.3960763;0.6815944 +0101060Y;UniversitĂ© De Troyes;13 Bd Henri Barbusse Bp 2060;10010;Troyes;48.2965919;4.074735 +0593279U;UniversitĂ© De Valenciennes;Le Mont Houy Bp 311;59304;Valenciennes;50.357113;3.518332 +0781944P;UniversitĂ© De Versailles;23, Rue Du Refuge;78035;Versailles;48.8040427;2.1440967 +0450855K;UniversitĂ© D'orlĂ©ans;Chateau De La Source;45067;Orleans;47.8507118;1.9337794 +0762762P;UniversitĂ© Du Havre;25 Rue Philippe Lebon Bp 1123;76063;Le Havre;49.49437;0.107929 +0720916E;UniversitĂ© Du Mans;Av Messiaen Bp 535;72017;Le Mans;48.00611;0.199556 +0911975C;UniversitĂ© Evry;23 Boulevard Des Coquibus;91025;Evry;48.6226142;2.4426967 +9840303G;UniversitĂ© Francaise Du Pacifique;B P 4635 Papeete;98800;Noumea;-22.2758;166.458 +0381838S;UniversitĂ© Grenoble 1;621 Av Centrale Bp53x;38041;Grenoble;45.188529;5.724524 +0381839T;UniversitĂ© Grenoble 2;Domaine Universitaire Bp 47x;38402;St Martin D Heres;45.165133;5.767761 +0381840U;UniversitĂ© Grenoble 3;Dom Univ Ave Centrale Bp 25;38040;Grenoble;45.188529;5.724524 +0593559Y;UniversitĂ© Lille 1;Dom Universitaire Scientifique;59655;Villeneuve D Ascq;50.623334;3.145 +0593560Z;UniversitĂ© Lille 2;42, Rue Paul Duez;59800;Lille;50.6312819;3.0756487 +0593561A;UniversitĂ© Lille Iii;Dom Univ Lit Et Jur Bp 149;59653;Villeneuve D Ascq;50.623334;3.145 +0691774D;UniversitĂ© Lyon 1;43 Bd Du 11 Nov 1918 Bp 761;69622;Villeurbanne;45.771944;4.8901709 +0691775E;UniversitĂ© Lyon 2;86, Rue Pasteur;69365;Lyon 07;45.7495518;4.8374252 +0692437Z;UniversitĂ© Lyon 3;1 Rue Universite Bp 0638;69239;Lyon 02;0.0;0.0 +0341087X;UniversitĂ© Montpellier 1;5,bd Henri Iv;34006;Montpellier;43.6149374;3.8736413 +0341088Y;UniversitĂ© Montpellier 2;Place Eugene Bataillon;34095;Montpellier;43.631505;3.860602 +0341089Z;UniversitĂ© Montpellier 3;B P 5043;34032;Montpellier;43.6340946;3.8704922 +0541507V;UniversitĂ© Nancy 1;24 30 R Lionnois Bp 3069;54013;Nancy;48.692054;6.184417 +0541508W;UniversitĂ© Nancy 2;25, Rue Baron Louis Bp 454;54001;Nancy;48.692054;6.184417 +0751717J;UniversitĂ© Paris 1;12 Place Du Pantheon;75231;Paris 05;48.8468209;2.3448981 +0921204J;UniversitĂ© Paris 10;200 Avenue De La Republique;92001;Nanterre;48.9047283;2.2088302 +0911101C;UniversitĂ© Paris 11;15, Rue Georges Clemenceau;91405;Orsay;48.6981864;2.181768 +0941111X;UniversitĂ© Paris 12;61, Avenue Du General De Gaulle;94010;Creteil;48.788165;2.445979 +0931238R;UniversitĂ© Paris 13;Avenue Jean-baptiste Clement;93430;Villetaneuse;48.955902;2.3388238 +0751718K;UniversitĂ© Paris 2;12, Place Du Pantheon;75231;Paris 05;48.8468209;2.3448981 +0751719L;UniversitĂ© Paris 3;17, Place De La Sorbonne;75230;Paris 05;48.8485744;2.3421878 +0751720M;UniversitĂ© Paris 4;1, Rue Victor Cousin;75230;Paris 05;48.8478098;2.3424414 +0751721N;UniversitĂ© Paris 5;12 Rue De L Ecole De Medecine;75270;Paris 06;48.8512077;2.3407847 +0751722P;UniversitĂ© Paris 6;4, Place Jussieu;75252;Paris 05;48.8465589;2.3544484 +0751723R;UniversitĂ© Paris 7;2,place Jussieu;75251;Paris 05;48.8460464;2.355228 +0931827F;UniversitĂ© Paris 8;2 Rue De La Liberte;93526;St Denis;48.9452421;2.3639438 +0750736T;UniversitĂ© Paris 9;Place Du Marechal De Tassigny;75775;Paris 16;48.8712128;2.2744029 +0350936C;UniversitĂ© Rennes 1;2 Rue Du Thabor Bp 1134;35014;Rennes;48.113475;-1.675708 +0350937D;UniversitĂ© Rennes 2;6, Avenue Gaston Berger;35043;Rennes;48.1187652;-1.7048071 +0671712X;UniversitĂ© Strasbourg 1;4 Rue Blaise Pascal Bp1032 F;67070;Strasbourg;48.583148;7.747882 +0671713Y;UniversitĂ© Strasbourg 2;22, Rue Descartes;67084;Strasbourg;48.579007;7.7651262 +0671778U;UniversitĂ© Strasbourg 3;1 Place D Athenes Bp 66;67045;Strasbourg;48.583148;7.747882 +0470940S;Universite Thematique Agen;Agropole Route De Condom;47931;Agen;44.203142;0.616363 +0311382J;UniversitĂ© Toulouse 1;Place Anatole France;31042;Toulouse;43.6060249;1.4384679 +0311383K;UniversitĂ© Toulouse 2;5, Allee Antonio Machado;31058;Toulouse;43.5776992;1.4040191 +0311384L;UniversitĂ© Toulouse 3;118, Route De Narbonne;31062;Toulouse;43.5631577;1.4628392 diff --git a/config/etc/README b/config/etc/README new file mode 100644 index 00000000..26aea491 --- /dev/null +++ b/config/etc/README @@ -0,0 +1,9 @@ + +* Scripts utiles pour l'installation de ScoDoc + +- scodoc : script de demarrage a placer dans /etc/rc.* + +- scodoc-site-ssl: configuration de site (https) pour Apache, a placer dans + /etc/apache2/sites-available/ + puis editer pour remplacer le nom du site et indiquer vos certificats crypto + puis activer par a2ensite scodoc-site-ssl diff --git a/config/etc/firehol.conf b/config/etc/firehol.conf new file mode 100644 index 00000000..2f5d964a --- /dev/null +++ b/config/etc/firehol.conf @@ -0,0 +1,19 @@ +# +# Exemple de configuration pare-feu firehol minimal pour un serveur ScoDoc +# +# Suppose que l'interface reseau Internet est XXX_INTERFACE_XXX +# +# E. Viennet, juin 2008, jul 2017 +# + +version 5 + +FIREHOL_LOG_MODE="ULOG" + +interface XXX_INTERFACE_XXX internet + protection strong + client all accept # plutot gentil, a adapter a vos besoins (attention a ntp et aux mises a jour) + server ssh accept + server https accept + # pas de log des paquets venant d'Internet... + server any nolog drop diff --git a/config/etc/scodoc b/config/etc/scodoc new file mode 100755 index 00000000..cf7a10ff --- /dev/null +++ b/config/etc/scodoc @@ -0,0 +1,75 @@ +#!/bin/sh +# +# zope This shell script takes care of starting and stopping +# zope under apache (proxy) +# Emmanuel Viennet @ LIPN, June 2002, Sept 2011 (LSB-compliance for Debian 6) +# +# chkconfig: - 90 10 +# description: zope is a web server + + +### BEGIN INIT INFO +# Provides: ScoDoc +# Required-Start: $local_fs $remote_fs $network $syslog +# Required-Stop: $local_fs $remote_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start/stop ScoDoc server +### END INIT INFO + +# Source function library. +#. /etc/init.d/functions + + +ZOPE_DIR=/opt/scodoc/instance + +# [ -x $ZOPE_DIR/bin/python ] || exit 0 + +RETVAL=0 +prog="Zope" + +start() { + # Start Zope persistent process + # Start daemons. + echo -n $"Starting $prog: " + echo $ZOPE_DIR/bin/zopectl start + $ZOPE_DIR/bin/zopectl start 2> /dev/null + RETVAL=$? + echo +# [ $RETVAL -eq 0 ] && touch /var/lock/subsys/zope + return $RETVAL +} + +stop() { + # Stop daemons. + echo -n 'Stopping zope daemon: ' + $ZOPE_DIR/bin/zopectl stop + RETVAL=$? + echo +# [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/zope + return $RETVAL +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; +# status) +# status $ZOPE_DIR/bin/python +# RETVAL=$? +# ;; + restart|reload) + stop + start + RETVAL=$? + ;; + *) + echo $"Usage: $0 {start|stop|restart}" + exit 1 +esac + +exit $RETVAL diff --git a/config/etc/scodoc-site-ssl-apache2.4.orig b/config/etc/scodoc-site-ssl-apache2.4.orig new file mode 100644 index 00000000..ba1a612c --- /dev/null +++ b/config/etc/scodoc-site-ssl-apache2.4.orig @@ -0,0 +1,56 @@ + +# Fichier config Apache basique pour ScoDoc +# E. Viennet, juin 2008, sept 2009, mar 2017 (Apache 2.4) + +# Apache fonctionne ici en proxy https devant zope (lui meme sur 8080). + +# A EDITER !!! +# 1- REMPLACER YOUR.FULL.HOST.NAME par votre nom de site (www.monscodoc.com) +# 2- indiquer vos certificats ssl (.key, .pem) +# 3- eventuellement, le certificat de votre authorite de certif. (CA). +# 4- eventuellement, revoir les fichiers de logs (penser au logrotate !) + + +NameVirtualHost *:443 + + ServerAdmin webmaster + ServerName YOUR.FULL.HOST.NAME + SSLEngine on + + SSLCertificateFile /etc/apache2/scodoc-ssl/apache.pem + # SSLCertificateKeyFile /etc/apache2/ssl/iutv.univ-paris13.fr.key + + # Votre authorite de certification: + # SSLCACertificateFile /etc/apache2/scodoc-ssl/ct_root.pem + # SSLCACertificateFile /etc/apache2/scodoc-ssl/sureserverEDU.pem + + DocumentRoot /var/www/ + + ErrorLog /var/log/apache2/ssl-error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog /var/log/apache2/ssl-access.log combined + + RewriteEngine on + # "RewriteLogLevel" n'est plus disponible dans apache2.4, il faut utiliser l'option "rewrite" dans l'instruction "LogLevel" + #LogLevel warn rewrite:trace3 + + # ScoDoc static content, served directly: + # RewriteCond %{HTTP:Authorization} ^(.*) + RewriteRule ^/ScoDoc/static/(.*) /opt/scodoc/Products/ScoDoc/static/$1 [L] + + # Le reste est pour Zope: + RewriteRule ^/(.*) http://localhost:8080/VirtualHostBase/https/YOUR.FULL.HOST.NAME:443/$1 [L,P] + + # Log avec utilisateur authentifie par ScoDoc + LogFormat "%h %l %{X-ScoDoc-User}o %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" scodoc + CustomLog "/var/log/apache2/scodoc_access.log" scodoc + + + + +Require all granted + diff --git a/config/etc/scodoc-site-ssl.orig b/config/etc/scodoc-site-ssl.orig new file mode 100644 index 00000000..0f628f23 --- /dev/null +++ b/config/etc/scodoc-site-ssl.orig @@ -0,0 +1,52 @@ + +# Fichier config Apache basique pour ScoDoc +# E. Viennet, juin 2008, sept 2009 + +# Apache fonctionne ici en proxy https devant zope (lui meme sur 8080). + +# A EDITER !!! +# 1- REMPLACER YOUR.FULL.HOST.NAME par votre nom de site (www.monscodoc.com) +# 2- indiquer vos certificats ssl (.key, .pem) +# 3- eventuellement, le certificat de votre authorite de certif. (CA). +# 4- eventuellement, revoir les fichiers de logs (penser au logrotate !) + + +NameVirtualHost *:443 + + ServerAdmin webmaster + ServerName YOUR.FULL.HOST.NAME + SSLEngine on + + SSLCertificateFile /etc/apache2/scodoc-ssl/apache.pem + # SSLCertificateKeyFile /etc/apache2/ssl/iutv.univ-paris13.fr.key + + # Votre authorite de certification: + # SSLCACertificateFile /etc/apache2/scodoc-ssl/ct_root.pem + # SSLCACertificateFile /etc/apache2/scodoc-ssl/sureserverEDU.pem + + DocumentRoot /var/www/ + + ErrorLog /var/log/apache2/ssl-error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog /var/log/apache2/ssl-access.log combined + + RewriteEngine on + RewriteLog /var/log/rewrite.ssl + RewriteLogLevel 0 + + # ScoDoc static content, served directly: + RewriteCond %{HTTP:Authorization} ^(.*) + RewriteRule ^/ScoDoc/static/(.*) /opt/scodoc/instance/Products/ScoDoc/static/$1 [L] + + # Le reste est pour Zope: + RewriteRule ^/(.*) http://localhost:8080/VirtualHostBase/https/YOUR.FULL.HOST.NAME:443/$1 [L,P] + + # Log avec utilisateur authentifie par ScoDoc + LogFormat "%h %l %{X-ScoDoc-User}o %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" scodoc + CustomLog "/var/log/apache2/scodoc_access.log" scodoc + + diff --git a/config/etc/scodoc-site.orig b/config/etc/scodoc-site.orig new file mode 100644 index 00000000..6829d876 --- /dev/null +++ b/config/etc/scodoc-site.orig @@ -0,0 +1,22 @@ +# +# Config Apache minimale http: redirige tout vers https +# (voir scodoc-site-ssl) +# +NameVirtualHost * + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/ + + AllowOverride None + + + RewriteEngine on + + ReWriteCond %{SERVER_PORT} !^443$ + RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [NC,R,L] + + ErrorLog /var/log/apache2/error.log + LogLevel warn + CustomLog /var/log/apache2/access.log combined + ServerSignature On + diff --git a/config/etc/scodoc-updater.service b/config/etc/scodoc-updater.service new file mode 100644 index 00000000..dd0ca9b9 --- /dev/null +++ b/config/etc/scodoc-updater.service @@ -0,0 +1,18 @@ +# +# Systemd service to upgrade ScoDoc +# +# Put this file in /etc/systemd/system, along with scodoc-updater.timer +# and then run +# systemctl enable scodoc-updater.timer +# systemctl start scodoc-updater.timer +# +# E. Viennet jun 2019 +# + +[Unit] +Description=Update ScoDoc Software + +[Service] +Type=oneshot +ExecStart=/opt/scodoc/Products/ScoDoc/config/upgrade.sh + diff --git a/config/etc/scodoc-updater.timer b/config/etc/scodoc-updater.timer new file mode 100644 index 00000000..3a7cddff --- /dev/null +++ b/config/etc/scodoc-updater.timer @@ -0,0 +1,18 @@ +# +# Systemd service to upgrade ScoDoc +# +# Put this file in /etc/systemd/system, along with scodoc-updater.service +# +# E. Viennet jun 2019 +# + +[Unit] +Description=Upgrade ScoDoc and Linux each week on Saturday night (sunday, 2:30 am) + +[Timer] +OnCalendar=Sun *-*-* 02:30:00 +# Add random delay, to avoid overloading ScoDoc upgrade server +RandomizedDelaySec=30min + +[Install] +WantedBy=timers.target diff --git a/config/initialize_database.sh b/config/initialize_database.sh new file mode 100755 index 00000000..d57849a0 --- /dev/null +++ b/config/initialize_database.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Initialize database (create tables) for a ScoDoc instance +# This script must be executed as www-data user +# +# $db_name and $DEPT passed as environment variables + +source config.sh +source utils.sh + +if [ $(id -nu) != $POSTGRES_USER ] +then + echo "$0: script must be runned as user $POSTGRES_USER" + exit 1 +fi + +echo 'Initializing tables in database ' $db_name +$PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name -f $SCODOC_DIR/misc/createtables.sql + + +# Set DeptName in preferences: +echo "insert into sco_prefs (name, value) values ('DeptName', '"${DEPT}\'\) | $PSQL -U $POSTGRES_USER -p $POSTGRES_PORT $db_name \ No newline at end of file diff --git a/config/install_cal_modules.sh b/config/install_cal_modules.sh new file mode 100755 index 00000000..1e5f4898 --- /dev/null +++ b/config/install_cal_modules.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Install module(s) for calendars +# (if already installed, do nothing) + + +# Test if installed +# (NB: don't launch python, to be faster and also to avoid import bug zope vs pytz) + +if [ -e /opt/zope213/lib/python2.7/site-packages/icalendar ] || [ /usr/lib/python2.7/dist-packages/icalendar ] +then + exit 0 # already installed +else + echo "Installing icalendar" + /opt/zope213/bin/pip install icalendar +fi + + + diff --git a/config/install_debian10.sh b/config/install_debian10.sh new file mode 100755 index 00000000..7e9579ee --- /dev/null +++ b/config/install_debian10.sh @@ -0,0 +1,259 @@ +#!/bin/bash + +# +# ScoDoc: install third-party software necessary for our installation +# starting for a minimal Debian (Stretch, 9.0) install. +# +# E. Viennet, Jun 2008, Apr 2009, Sept 2011, Sept 2013, Nov 2013, Mar 2017, Jul 2017, Jun 2019, Oct 2019 +# + +source config.sh +source utils.sh + +check_uid_root $0 + +PYTHON=/opt/zope213/bin/python + +# ------------ Safety checks +if [ ${debian_version} != "10" ] +then + echo "Version du systeme Linux Debian incompatible" + exit 1 +fi + +if [ $(arch) != "x86_64" ] +then + echo "Version du systeme Linux Debian incompatible (pas X86 64 bits)" + exit 1 +fi + +# ------------ Permissions & directories +# source dir should be writable by scodoc to write bytecode files +chgrp www-data $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* +chmod g+w $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* + +chgrp -R www-data "${SCODOC_DIR}"/static/photos +chmod -R g+w "${SCODOC_DIR}"/static/photos + +if [ ! -e "${SCODOC_VERSION_DIR}" ]; then + mkdir "${SCODOC_VERSION_DIR}" + chown www-data.www-data "${SCODOC_VERSION_DIR}" +fi + +# ------------ LOCALES +echo +echo '---- Configuration des locales...' +echo + +if [ ! -e /etc/locale.gen ] +then + touch /etc/locale.gen +fi + + +for locname in en_US.UTF-8 en_US.ISO-8859-15 en_US.ISO-8859-1 +do + outname=$(echo ${locname//-/} | tr '[A-Z]' '[a-z]') + if [ $(locale -a | egrep -i ^${outname}$ | wc -l) -lt 1 ] + then + echo adding $locname + echo "$locname ${locname##*.}" >> /etc/locale.gen + fi +done + +/usr/sbin/locale-gen --keep-existing + + +if [ "$LANG" != "en_US.UTF-8" ] +then + # ceci est necessaire a cause de postgresql 8.3 qui + # cree son cluster lors de l'install avec la locale par defaut ! + echo "Attention: changement de la locale par defaut" + mv /etc/default/locale /etc/default/locale.orig + echo "LANG=\"en_US.UTF-8\"" > /etc/default/locale + export LANG=en_US.UTF-8 +fi +echo 'Done.' + +# ------------ FIX pour passage Debian 7 -> Debian >= 8 +chsh -s /bin/sh www-data + +# ------------ AJOUT DES PAQUETS NECESSAIRES +apt-get update +apt-get -y install subversion curl cracklib-runtime +apt-get -y install apache2 ssl-cert +apt-get -y install postgresql +apt-get -y install graphviz + +# ------------ INSTALL DES EXTENSIONS PYTHON (2.7) + +apt-get -y install python-docutils +apt-get -y install python-jaxml +apt-get -y install python-psycopg2 +apt-get -y install python-pyrss2gen +apt-get -y install python-pil python-reportlab +apt-get -y install python-cracklib # was python-crack +apt-get -y install python-icalendar +apt-get -y install python-requests + +apt-get -y install python-egenix-mxtools python-egenix-mxdatetime + + +# ------------ +SVNVERSION=$(cd ..; svnversion) +SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&svn=$SVNVERSION) +echo $SVERSION > "${SCODOC_VERSION_DIR}/scodoc.sn" + + +# ------------ POSTFIX +echo +echo "ScoDoc a besoin de pouvoir envoyer des messages par mail." +echo -n "Voulez vous configurer la messagerie (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + apt-get -y install postfix +fi + +# ------------ CONFIG FIREWALL (non teste en Debian 10) +echo +echo "Le firewall aide a proteger votre serveur d'intrusions indesirables." +echo -n "Voulez vous configurer un firewall minimal (ufw) ? (y/n) [n] " +read ans +if [ "$(norm_ans "$ans")" = 'Y' ] +then + echo 'Installation du firewall IP ufw (voir documentation Debian)' + echo ' on autorise les connexions ssh et https' + apt-get -y install ufw + ufw default deny incoming + ufw default allow outgoing + ufw allow ssh + ufw allow https + yes | ufw enable +fi + +# Nota: after this point, the network _may_ be unreachable +# (if firewall config is wrong) + +# ------------ CONFIG APACHE +a2enmod ssl +a2enmod proxy +a2enmod proxy_http +a2enmod rewrite + +echo +echo "La configuration du serveur web va modifier votre installation Apache pour supporter ScoDoc." +echo -n "Voulez vous configurer le serveur web Apache maintenant (tres conseille) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "Configuration d'Apache" + server_name="" + while [ -z $server_name ] + do + echo "Le nom de votre serveur doit normalement etre connu dans le DNS." + echo -n "Nom complet de votre serveur (exemple: notes.univ.fr): " + read server_name + done + # --- CERTIFICATS AUTO-SIGNES + echo + echo "Il est possible d'utiliser des certificats cryptographiques" + echo "auto-signes, qui ne seront pas reconnus comme de confiance" + echo "par les navigateurs, mais offrent une certaine securite." + echo -n 'Voulez vous generer des certificats ssl auto-signes ? (y/n) [y] ' + read ans + if [ "$(norm_ans "$ans")" != 'N' ] + then + # attention: utilise dans scodoc-site-ssl.orig + ssl_dir=/etc/apache2/scodoc-ssl + if [ ! -e $ssl_dir ] + then + mkdir $ssl_dir + fi + /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem + cert_status=$? + else + cert_status=-1 + fi + # --- + echo 'generation de /etc/apache2/sites-available/scodoc-site-ssl' + cat $SCODOC_DIR/config/etc/scodoc-site-ssl-apache2.4.orig | sed -e "s:YOUR\.FULL\.HOST\.NAME:$server_name:g" > /etc/apache2/sites-available/scodoc-site-ssl.conf + echo 'activation du site...' + a2ensite scodoc-site-ssl + + echo 'Remplacement du site Apache par defaut (sic ! old saved as .bak)' + fn=/etc/apache2/sites-available/000-default.conf + if [ -e $fn ] + then + mv $fn $fn.bak + fi + cp $SCODOC_DIR/config/etc/scodoc-site.orig $fn + + if [ -z "$(grep Listen /etc/apache2/ports.conf | grep 443)" ] + then + echo 'adding port 443' + echo 'Listen 443' >> /etc/apache2/ports.conf + fi + + echo 'configuring Apache proxy' + mv /etc/apache2/mods-available/proxy.conf /etc/apache2/mods-available/proxy.conf.bak + cat > /etc/apache2/mods-available/proxy.conf < +# Proxy config for ScoDoc default installation +ProxyRequests Off + + Order deny,allow + Allow from all + + +EOF + +fi + +systemctl restart apache2 + +# ------------ CONFIG SERVICE SCODOC +echo +echo "Installer le service scodoc permet de lancer automatiquement le serveur au demarrage." +echo -n "Voulez vous installer le service scodoc ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo 'Installation du demarrage automatique de ScoDoc' + cp $SCODOC_DIR/config/etc/scodoc /etc/init.d/ + update-rc.d scodoc defaults +fi + + +# ------------ CONFIG MISE A JOUR HEBDOMADAIRE +echo +echo -n "Mises a jour hebdomadaires (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + cp $SCODOC_DIR/config/etc/scodoc-updater.service /etc/systemd/system + cp $SCODOC_DIR/config/etc/scodoc-updater.timer /etc/systemd/system + systemctl enable scodoc-updater.timer + systemctl start scodoc-updater.timer +fi + +# ------------ THE END +echo +echo "Installation terminee." +echo +echo "Vous pouvez maintenant creer la base d'utilisateurs avec ./create_user_db.sh" +echo "puis creer un departement avec ./create_dept.sh" +echo "Ou bien restaurer vos donnees a partir d'une ancienne installation a l'aide du script restore_scodoc_data.sh" +echo "(voir https://trac.lipn.univ-paris13.fr/projects/scodoc/wiki/MigrationDonneesScoDoc)" +echo + + +if [ "${cert_status}" != 0 ] +then + echo "Attention: le serveur Web Apache n'a pas de certificat." + echo "Il est probable qu'il ne fonctionne pas." + echo "Installez vos certificats ou generez provisoirement des certificats autosignes" + echo "avec la commande: /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem" + echo +fi + diff --git a/config/install_debian7.sh b/config/install_debian7.sh new file mode 100755 index 00000000..aed314e3 --- /dev/null +++ b/config/install_debian7.sh @@ -0,0 +1,256 @@ +#!/bin/bash + +# +# ScoDoc: install third-party software necessary for our installation +# starting for a minimal Debian (Wheezy, 7.0) install. +# +# E. Viennet, Jun 2008, Apr 2009, Sept 2011, Sept 2013, Nov 2013 +# + +source config.sh +source utils.sh + +check_uid_root $0 + +PYTHON=/opt/zope213/bin/python + +# ------------ Safety checks +if [ ${debian_version} != "7" ] +then + echo "Version du systeme Linux Debian incompatible" + exit 1 +fi + +if [ $(arch) != "x86_64" ] +then + echo "Version du systeme Linux Debian incompatible (pas X86 64 bits)" + exit 1 +fi + +# ------------ Permissions +# source dir should be writable by scodoc to write bytecode files +chgrp www-data $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* +chmod g+w $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* + +chgrp -R www-data "${SCODOC_DIR}"/static/photos +chmod -R g+w "${SCODOC_DIR}"/static/photos + +# ------------ LOCALES +echo +echo '---- Configuration des locales...' +echo + +if [ ! -e /etc/locale.gen ] +then +touch /etc/locale.gen +fi + + +for locname in en_US.UTF-8 en_US.ISO-8859-15 en_US.ISO-8859-1 +do + outname=$(echo ${locname//-/} | tr '[A-Z]' '[a-z]') + if [ $(locale -a | egrep -i ^${outname}$ | wc -l) -lt 1 ] + then + echo adding $locname + echo "$locname ${locname##*.}" >> /etc/locale.gen + fi +done + +/usr/sbin/locale-gen --keep-existing + + +if [ "$LANG" != "en_US.UTF-8" ] +then + # ceci est necessaire a cause de postgresql 8.3 qui + # cree son cluster lors de l'install avec la locale par defaut ! + echo "Attention: changement de la locale par defaut" + mv /etc/default/locale /etc/default/locale.orig + echo "LANG=\"en_US.UTF-8\"" > /etc/default/locale + export LANG=en_US.UTF-8 +fi +echo 'Done.' + + +# ------------ AJOUT DES PAQUETS NECESSAIRES +apt-get update +apt-get -y dist-upgrade +apt-get -y install subversion curl cracklib-runtime firehol +apt-get -y install apache2 ssl-cert +apt-get -y install postgresql-9.1 postgresql-client-9.1 +apt-get -y install graphviz + +# ------------ INSTALL DES EXTENSIONS PYTHON (2.7) + +apt-get -y install python-jaxml +apt-get -y install python-psycopg2 +apt-get -y install python-pyrss2gen +apt-get -y install python-imaging python-reportlab +apt-get -y install python-cracklib # was python-crack +apt-get -y install python-pyparsing +apt-get -y install python-beautifulsoup + +apt-get -y install python-egenix-mxtools python-egenix-mxdatetime + +# Installe la version standard de pydot (XXX A TESTER) +# apt-get -y install python-pydot +# python-pydot is currently bugged in Debian 5: install our 0.9.10 +# Le probleme: pydot v > 1 a change l'API : resultat de get_node est une liste. Resolu par sco_utils.pydot_get_node +# pydot 1.0.25 bug avec python 2.4 (get_node_list() renvoie toujours []) +# 1.0.3 idem (voir misc/testpydot.py) +echo '\nInstallation de pydot\n' +apt-get -y remove python-pydot +(cd /tmp; tar xfz $SCODOC_DIR/config/softs/pydot-0.9.10.tar.gz) +(cd /tmp/pydot-0.9.10; $PYTHON setup.py install) + + +# UNUSED BY ScoDoc 7: +# SOFTS="$SCODOC_DIR/config/softs" + +# ------------ Upgrade from svn +(cd "${SCODOC_DIR}"; svn update) + +# ------------ +SVNVERSION=$(cd ..; svnversion) +SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&svn=$SVNVERSION) +echo $SVERSION > "${SCODOC_VERSION_DIR}/scodoc.sn" + + +# ------------ PYEXCELERATOR +echo +echo 'Installation de pyExcelerator' +echo + +(cd /tmp; tar xfz $SCODOC_DIR/config/softs/pyExcelerator-0.6.3a.patched.tgz) +(cd /tmp/pyExcelerator-0.6.3a.patched; $PYTHON setup.py install) + +echo 'Done.' + +# ------------ POSTFIX +echo +echo "ScoDoc a besoin de pouvoir envoyer des messages par mail." +echo -n "Voulez vous configurer la messagerie (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + apt-get -y install postfix +fi + +# ------------ CONFIG FIREWALL +echo +echo "Le firewall aide a proteger votre serveur d'intrusions indesirables." +echo -n "Voulez vous installer un firewall minimal ? (y/n) [n] " +read ans +if [ "$(norm_ans "$ans")" = 'Y' ] +then + echo 'Installation du firewall IP (voir /etc/firehol/firehol.conf)' + echo "Attention: suppose que l'interface reseau vers Internet est eth0" + echo " si ce n'est pas le cas, editer /etc/firehol/firehol.conf" + echo " et relancer: /etc/init.d/firehol restart" + echo + cp $SCODOC_DIR/config/etc/firehol.conf /etc/firehol/ + mv /etc/default/firehol /etc/default/firehol.orig + cat /etc/default/firehol.orig | sed 's/START_FIREHOL=NO/START_FIREHOL=YES/' > /tmp/firehol && mv /tmp/firehol /etc/default/firehol + # mise a jour RESERVED_IPS (le script fournit en Deb 7 get-iana est buggy) + echo yes | $SCODOC_DIR/config/softs/get-iana.sh +fi + +# Nota: after this point, the network may be unreachable +# (if firewall config is wrong) + +# ------------ CONFIG APACHE +a2enmod ssl +a2enmod proxy +a2enmod proxy_http +a2enmod rewrite + +echo +echo "La configuration du serveur web va modifier votre installation Apache pour supporter ScoDoc." +echo -n "Voulez vous configurer le serveur web Apache maintenant ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "Configuration d'Apache" + server_name="" + while [ -z $server_name ] + do + echo "Le nom de votre serveur doit normalement etre connu dans le DNS." + echo -n "Nom complet de votre serveur (exemple: notes.univ.fr): " + read server_name + done + # --- CERTIFICATS AUTO-SIGNES + echo + echo "Il est possible d'utiliser des certificats cryptographiques" + echo "auto-signes, qui ne seront pas reconnus comme de confiance" + echo "par les navigateurs, mais offrent une certaine securite." + echo -n 'Voulez vous generer des certificats ssl auto-signes ? (y/n) [y] ' + read ans + if [ "$(norm_ans "$ans")" != 'N' ] + then + # attention: utilise dans scodoc-site-ssl.orig + ssl_dir=/etc/apache2/scodoc-ssl + if [ ! -e $ssl_dir ] + then + mkdir $ssl_dir + fi + /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem + fi + # --- + echo 'generation de /etc/apache2/sites-available/scodoc-site-ssl' + cat $SCODOC_DIR/config/etc/scodoc-site-ssl.orig | sed -e "s:YOUR\.FULL\.HOST\.NAME:$server_name:g" > /etc/apache2/sites-available/scodoc-site-ssl + echo 'activation du site...' + a2ensite scodoc-site-ssl + + echo 'Remplacement du site Apache par defaut (sic ! old saved as .bak)' + fn=/etc/apache2/sites-available/default + if [ -e $fn ] + then + mv $fn $fn.bak + fi + cp $SCODOC_DIR/config/etc/scodoc-site.orig $fn + + if [ -z "$(grep Listen /etc/apache2/ports.conf | grep 443)" ] + then + echo 'adding port 443' + echo 'Listen 443' >> /etc/apache2/ports.conf + fi + + echo 'configuring Apache proxy' + mv /etc/apache2/mods-available/proxy.conf /etc/apache2/mods-available/proxy.conf.bak + cat > /etc/apache2/mods-available/proxy.conf < +# Proxy config for ScoDoc default installation +ProxyRequests Off + + Order deny,allow + Allow from all + + +EOF + + /etc/init.d/apache2 restart +fi + + +# ------------ CONFIG SERVICE SCODOC +echo +echo "Installer le service scodoc permet de lancer automatiquement le serveur au demarrage." +echo -n "Voulez vous installer le service scodoc ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo 'Installation du demarrage automatique de ScoDoc' + cp $SCODOC_DIR/config/etc/scodoc /etc/init.d/ + insserv scodoc +fi + +# ------------ THE END +echo +echo "Installation terminee." +echo +echo "Vous pouvez maintenant creer la base d'utilisateurs avec ./create_user_db.sh" +echo "puis creer un departement avec ./create_dept.sh" +echo "Ou bien restaurer vos donnees a partir d'une ancienne installation a l'aide du script restore_scodoc_data.sh" +echo "(voir https://trac.lipn.univ-paris13.fr/projects/scodoc/wiki/MigrationVersScoDocSept)" +echo + + diff --git a/config/install_debian8.sh b/config/install_debian8.sh new file mode 100755 index 00000000..2741ceed --- /dev/null +++ b/config/install_debian8.sh @@ -0,0 +1,275 @@ +#!/bin/bash + +# +# ScoDoc: install third-party software necessary for our installation +# starting for a minimal Debian (Jessie, 8.0) install. +# +# E. Viennet, Jun 2008, Apr 2009, Sept 2011, Sept 2013, Nov 2013, Mar 2017 +# + +source config.sh +source utils.sh + +check_uid_root $0 + +PYTHON=/opt/zope213/bin/python + +# ------------ Safety checks +if [ ${debian_version} != "8" ] +then + echo "Version du systeme Linux Debian incompatible" + exit 1 +fi + +if [ $(arch) != "x86_64" ] +then + echo "Version du systeme Linux Debian incompatible (pas X86 64 bits)" + exit 1 +fi + +# ------------ Permissions +# source dir should be writable by scodoc to write bytecode files +chgrp www-data $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* +chmod g+w $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* + +chgrp -R www-data "${SCODOC_DIR}"/static/photos +chmod -R g+w "${SCODOC_DIR}"/static/photos + +# ------------ LOCALES +echo +echo '---- Configuration des locales...' +echo + +if [ ! -e /etc/locale.gen ] +then +touch /etc/locale.gen +fi + + +for locname in en_US.UTF-8 en_US.ISO-8859-15 en_US.ISO-8859-1 +do + outname=$(echo ${locname//-/} | tr '[A-Z]' '[a-z]') + if [ $(locale -a | egrep -i ^${outname}$ | wc -l) -lt 1 ] + then + echo adding $locname + echo "$locname ${locname##*.}" >> /etc/locale.gen + fi +done + +/usr/sbin/locale-gen --keep-existing + + +if [ "$LANG" != "en_US.UTF-8" ] +then + # ceci est necessaire a cause de postgresql 8.3 qui + # cree son cluster lors de l'install avec la locale par defaut ! + echo "Attention: changement de la locale par defaut" + mv /etc/default/locale /etc/default/locale.orig + echo "LANG=\"en_US.UTF-8\"" > /etc/default/locale + export LANG=en_US.UTF-8 +fi +echo 'Done.' + +# ------------ FIX pour passage Debien 7 -> Debian 8 +chsh -s /bin/sh www-data + +# ------------ AJOUT DES PAQUETS NECESSAIRES +apt-get update +apt-get -y install subversion curl cracklib-runtime firehol +apt-get -y install apache2 ssl-cert +apt-get -y install postgresql-9.4 postgresql-client-9.4 +apt-get -y install graphviz + +# ------------ INSTALL DES EXTENSIONS PYTHON (2.7) + +apt-get -y install python-jaxml +apt-get -y install python-psycopg2 +apt-get -y install python-pyrss2gen +apt-get -y install python-imaging python-reportlab +apt-get -y install python-cracklib # was python-crack +#apt-get -y install python-pyparsing +apt-get -y install python-beautifulsoup + +apt-get -y install python-egenix-mxtools python-egenix-mxdatetime + +# pydot est un probleme: todo: rececrire l'affichae du graphe sans pydot +# ou contribuer une versions ans bug de pydot +# ou encore simplement eviter d'utiliser graph_from_edges ? +# +# python-pydot is currently bugged in Debian 5, 6, 7, 8: install our 0.9.10 +# Le probleme: pydot v > 1 a change l'API : resultat de get_node est une liste. Resolu par sco_utils.pydot_get_node +# pydot 1.0.25 bug avec python 2.4 (get_node_list() renvoie toujours []) +# 1.0.3 idem (voir misc/testpydot.py) +# 1.2.4 / python2.7: bug graph_from_edges toujours lĂ  +# voir https://bugs.launchpad.net/ubuntu/+source/pydot/+bug/490015 +echo '\nInstallation de pydot\n' + +apt-get -y remove python-pydot +# Version ancienne de pyparsing requise par pydot +apt-get -y remove python-pyparsing +/opt/zope213/bin/pip install pyparsing==1.5.7 +(cd /tmp; tar xfz $SCODOC_DIR/config/softs/pydot-0.9.10.tar.gz) +(cd /tmp/pydot-0.9.10; $PYTHON setup.py install) + + +# UNUSED BY ScoDoc 7: +# SOFTS="$SCODOC_DIR/config/softs" + +# ------------ +SVNVERSION=$(cd ..; svnversion) +SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&svn=$SVNVERSION) +echo $SVERSION > "${SCODOC_VERSION_DIR}/scodoc.sn" + + +# ------------ PYEXCELERATOR +# inutile en Debian 8 car dans l'image ScoDoc +#echo +#echo 'Installation de pyExcelerator' +#echo +#(cd /tmp; tar xfz $SCODOC_DIR/config/softs/pyExcelerator-0.6.3a.patched.tgz) +#(cd /tmp/pyExcelerator-0.6.3a.patched; $PYTHON setup.py install) +#echo 'Done.' + +# ------------ POSTFIX +echo +echo "ScoDoc a besoin de pouvoir envoyer des messages par mail." +echo -n "Voulez vous configurer la messagerie (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + apt-get -y install postfix +fi + +# ------------ CONFIG FIREWALL +echo +echo "Le firewall aide a proteger votre serveur d'intrusions indesirables." +echo -n "Voulez vous installer un firewall minimal ? (y/n) [n] " +read ans +if [ "$(norm_ans "$ans")" = 'Y' ] +then + network_interface="eth0" + echo 'Installation du firewall IP (voir /etc/firehol/firehol.conf)' + echo "Attention: suppose que l'interface reseau vers Internet est eth0" + echo " si ce n'est pas le cas, editer /etc/firehol/firehol.conf" + echo " et relancer: /etc/init.d/firehol restart" + echo + cat $SCODOC_DIR/config/etc/firehol.conf | sed "s/XXX_INTERFACE_XXX/${network_interface}/" > /etc/firehol/firehol.conf + mv /etc/default/firehol /etc/default/firehol.orig + cat /etc/default/firehol.orig | sed 's/START_FIREHOL=NO/START_FIREHOL=YES/' > /tmp/firehol && mv /tmp/firehol /etc/default/firehol + # mise a jour RESERVED_IPS (le script fournit en Deb 7 get-iana est buggy) + echo yes | $SCODOC_DIR/config/softs/get-iana.sh +fi + +# Nota: after this point, the network may be unreachable +# (if firewall config is wrong) + +# ------------ CONFIG APACHE +a2enmod ssl +a2enmod proxy +a2enmod proxy_http +a2enmod rewrite + +echo +echo "La configuration du serveur web va modifier votre installation Apache pour supporter ScoDoc." +echo -n "Voulez vous configurer le serveur web Apache maintenant (tres conseille) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "Configuration d'Apache" + server_name="" + while [ -z $server_name ] + do + echo "Le nom de votre serveur doit normalement etre connu dans le DNS." + echo -n "Nom complet de votre serveur (exemple: notes.univ.fr): " + read server_name + done + # --- CERTIFICATS AUTO-SIGNES + echo + echo "Il est possible d'utiliser des certificats cryptographiques" + echo "auto-signes, qui ne seront pas reconnus comme de confiance" + echo "par les navigateurs, mais offrent une certaine securite." + echo -n 'Voulez vous generer des certificats ssl auto-signes ? (y/n) [y] ' + read ans + if [ "$(norm_ans "$ans")" != 'N' ] + then + # attention: utilise dans scodoc-site-ssl.orig + ssl_dir=/etc/apache2/scodoc-ssl + if [ ! -e $ssl_dir ] + then + mkdir $ssl_dir + fi + /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem + cert_status=$? + else + cert_status=-1 + fi + # --- + echo 'generation de /etc/apache2/sites-available/scodoc-site-ssl' + cat $SCODOC_DIR/config/etc/scodoc-site-ssl-apache2.4.orig | sed -e "s:YOUR\.FULL\.HOST\.NAME:$server_name:g" > /etc/apache2/sites-available/scodoc-site-ssl.conf + echo 'activation du site...' + a2ensite scodoc-site-ssl + + echo 'Remplacement du site Apache par defaut (sic ! old saved as .bak)' + fn=/etc/apache2/sites-available/000-default.conf + if [ -e $fn ] + then + mv $fn $fn.bak + fi + cp $SCODOC_DIR/config/etc/scodoc-site.orig $fn + + if [ -z "$(grep Listen /etc/apache2/ports.conf | grep 443)" ] + then + echo 'adding port 443' + echo 'Listen 443' >> /etc/apache2/ports.conf + fi + + echo 'configuring Apache proxy' + mv /etc/apache2/mods-available/proxy.conf /etc/apache2/mods-available/proxy.conf.bak + cat > /etc/apache2/mods-available/proxy.conf < +# Proxy config for ScoDoc default installation +ProxyRequests Off + + Order deny,allow + Allow from all + + +EOF + +fi + +service apache2 restart + + +# ------------ CONFIG SERVICE SCODOC +echo +echo "Installer le service scodoc permet de lancer automatiquement le serveur au demarrage." +echo -n "Voulez vous installer le service scodoc ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo 'Installation du demarrage automatique de ScoDoc' + cp $SCODOC_DIR/config/etc/scodoc /etc/init.d/ + insserv scodoc +fi + +# ------------ THE END +echo +echo "Installation terminee." +echo +echo "Vous pouvez maintenant creer la base d'utilisateurs avec ./create_user_db.sh" +echo "puis creer un departement avec ./create_dept.sh" +echo "Ou bien restaurer vos donnees a partir d'une ancienne installation a l'aide du script restore_scodoc_data.sh" +echo "(voir https://trac.lipn.univ-paris13.fr/projects/scodoc/wiki/MigrationDonneesScoDoc)" +echo + +if [ "${cert_status}" != 0 ] +then + echo "Attention: le serveur Web Apache n'a pas de certificat." + echo "Il est probable qu'il ne fonctionne pas." + echo "Installez vos certificats ou generez provisoirement des certificats autosignes" + echo "avec la commande: /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem" + echo +fi + + diff --git a/config/install_debian9.sh b/config/install_debian9.sh new file mode 100755 index 00000000..0e54bd14 --- /dev/null +++ b/config/install_debian9.sh @@ -0,0 +1,302 @@ +#!/bin/bash + +# +# ScoDoc: install third-party software necessary for our installation +# starting for a minimal Debian (Stretch, 9.0) install. +# +# E. Viennet, Jun 2008, Apr 2009, Sept 2011, Sept 2013, Nov 2013, Mar 2017, Jul 2017, Jun 2019 +# + +source config.sh +source utils.sh + +check_uid_root $0 + +PYTHON=/opt/zope213/bin/python + +# ------------ Safety checks +if [ ${debian_version} != "9" ] +then + echo "Version du systeme Linux Debian incompatible" + exit 1 +fi + +if [ $(arch) != "x86_64" ] +then + echo "Version du systeme Linux Debian incompatible (pas X86 64 bits)" + exit 1 +fi + +# ------------ Permissions & directories +# source dir should be writable by scodoc to write bytecode files +chgrp www-data $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* +chmod g+w $SCODOC_DIR $SCODOC_DIR/ZopeProducts/* + +chgrp -R www-data "${SCODOC_DIR}"/static/photos +chmod -R g+w "${SCODOC_DIR}"/static/photos + +if [ ! -e "${SCODOC_VERSION_DIR}" ]; then + mkdir "${SCODOC_VERSION_DIR}" + chown www-data.www-data "${SCODOC_VERSION_DIR}" +fi + +# ------------ LOCALES +echo +echo '---- Configuration des locales...' +echo + +if [ ! -e /etc/locale.gen ] +then +touch /etc/locale.gen +fi + + +for locname in en_US.UTF-8 en_US.ISO-8859-15 en_US.ISO-8859-1 +do + outname=$(echo ${locname//-/} | tr '[A-Z]' '[a-z]') + if [ $(locale -a | egrep -i ^${outname}$ | wc -l) -lt 1 ] + then + echo adding $locname + echo "$locname ${locname##*.}" >> /etc/locale.gen + fi +done + +/usr/sbin/locale-gen --keep-existing + + +if [ "$LANG" != "en_US.UTF-8" ] +then + # ceci est necessaire a cause de postgresql 8.3 qui + # cree son cluster lors de l'install avec la locale par defaut ! + echo "Attention: changement de la locale par defaut" + mv /etc/default/locale /etc/default/locale.orig + echo "LANG=\"en_US.UTF-8\"" > /etc/default/locale + export LANG=en_US.UTF-8 +fi +echo 'Done.' + +# ------------ FIX pour passage Debian 7 -> Debian 8 +chsh -s /bin/sh www-data + +# ------------ AJOUT DES PAQUETS NECESSAIRES +apt-get update +apt-get -y install subversion curl cracklib-runtime +apt-get -y install apache2 ssl-cert +apt-get -y install firehol +apt-get -y install insserv +apt-get -y install postgresql-9.6 postgresql-client-9.6 +apt-get -y install graphviz + +# ------------ INSTALL DES EXTENSIONS PYTHON (2.7) + +apt-get -y install python-docutils +apt-get -y install python-jaxml +apt-get -y install python-psycopg2 +apt-get -y install python-pyrss2gen +apt-get -y install python-imaging python-reportlab +apt-get -y install python-cracklib # was python-crack +#apt-get -y install python-pyparsing +apt-get -y install python-beautifulsoup + +apt-get -y install python-egenix-mxtools python-egenix-mxdatetime + +# pydot est un probleme: todo: rececrire l'affichage du graphe sans pydot +# ou contribuer une versions sans bug de pydot +# ou encore simplement eviter d'utiliser graph_from_edges ? +# +# python-pydot is currently bugged in Debian 5, 6, 7, 8: install our 0.9.10 +# Le probleme: pydot v > 1 a change l'API : resultat de get_node est une liste. Resolu par sco_utils.pydot_get_node +# pydot 1.0.25 bug avec python 2.4 (get_node_list() renvoie toujours []) +# 1.0.3 idem (voir misc/testpydot.py) +# 1.2.4 / python2.7: bug graph_from_edges toujours lĂ  +# voir https://bugs.launchpad.net/ubuntu/+source/pydot/+bug/490015 +echo '\nInstallation de pydot\n' + +apt-get -y remove python-pydot +# Version ancienne de pyparsing requise par pydot +apt-get -y remove python-pyparsing +/opt/zope213/bin/pip install pyparsing==1.5.7 +(cd /tmp; tar xfz $SCODOC_DIR/config/softs/pydot-0.9.10.tar.gz) +(cd /tmp/pydot-0.9.10; $PYTHON setup.py install) + +# Fix dateutil: +/opt/zope213/bin/pip install python-dateutil --upgrade + +# UNUSED BY ScoDoc 7: +# SOFTS="$SCODOC_DIR/config/softs" + +# ------------ +SVNVERSION=$(cd ..; svnversion) +SVERSION=$(curl --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&svn=$SVNVERSION) +echo $SVERSION > "${SCODOC_VERSION_DIR}/scodoc.sn" + + +# ------------ PYEXCELERATOR +# inutile en Debian 8 car dans l'image ScoDoc +#echo +#echo 'Installation de pyExcelerator' +#echo +#(cd /tmp; tar xfz $SCODOC_DIR/config/softs/pyExcelerator-0.6.3a.patched.tgz) +#(cd /tmp/pyExcelerator-0.6.3a.patched; $PYTHON setup.py install) +#echo 'Done.' + +# ------------ POSTFIX +echo +echo "ScoDoc a besoin de pouvoir envoyer des messages par mail." +echo -n "Voulez vous configurer la messagerie (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + apt-get -y install postfix +fi + +# ------------ CONFIG FIREWALL +echo +echo "Le firewall aide a proteger votre serveur d'intrusions indesirables." +echo -n "Voulez vous installer un firewall minimal (firehol) ? (y/n) [n] " +read ans +if [ "$(norm_ans "$ans")" = 'Y' ] +then + echo 'Installation du firewall IP (voir /etc/firehol/firehol.conf)' + echo "Debian 9 n'utilise plus eth0 mais des noms fixes, comme enp0s3" + echo "Vos interfaces reseau actuelles sont:" + ip a + echo -n "Nom de votre interface reseau vers Internet ? " + read network_interface + echo "Attention: configure l'interface reseau vers Internet ${network_interface}" + echo " si ce n'est pas le cas, editer /etc/firehol/firehol.conf" + echo " et relancer: /etc/init.d/firehol restart" + echo + cat $SCODOC_DIR/config/etc/firehol.conf | sed "s/XXX_INTERFACE_XXX/${network_interface}/" > /etc/firehol/firehol.conf + mv /etc/default/firehol /etc/default/firehol.orig + cat /etc/default/firehol.orig | sed 's/START_FIREHOL=NO/START_FIREHOL=YES/' > /tmp/firehol && mv /tmp/firehol /etc/default/firehol + # mise a jour RESERVED_IPS (le script fournit en Deb 7 get-iana est buggy) + echo yes | $SCODOC_DIR/config/softs/get-iana.sh +fi + +# Nota: after this point, the network may be unreachable +# (if firewall config is wrong) + +# ------------ CONFIG APACHE +a2enmod ssl +a2enmod proxy +a2enmod proxy_http +a2enmod rewrite + +echo +echo "La configuration du serveur web va modifier votre installation Apache pour supporter ScoDoc." +echo -n "Voulez vous configurer le serveur web Apache maintenant (tres conseille) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo "Configuration d'Apache" + server_name="" + while [ -z $server_name ] + do + echo "Le nom de votre serveur doit normalement etre connu dans le DNS." + echo -n "Nom complet de votre serveur (exemple: notes.univ.fr): " + read server_name + done + # --- CERTIFICATS AUTO-SIGNES + echo + echo "Il est possible d'utiliser des certificats cryptographiques" + echo "auto-signes, qui ne seront pas reconnus comme de confiance" + echo "par les navigateurs, mais offrent une certaine securite." + echo -n 'Voulez vous generer des certificats ssl auto-signes ? (y/n) [y] ' + read ans + if [ "$(norm_ans "$ans")" != 'N' ] + then + # attention: utilise dans scodoc-site-ssl.orig + ssl_dir=/etc/apache2/scodoc-ssl + if [ ! -e $ssl_dir ] + then + mkdir $ssl_dir + fi + /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem + cert_status=$? + else + cert_status=-1 + fi + # --- + echo 'generation de /etc/apache2/sites-available/scodoc-site-ssl' + cat $SCODOC_DIR/config/etc/scodoc-site-ssl-apache2.4.orig | sed -e "s:YOUR\.FULL\.HOST\.NAME:$server_name:g" > /etc/apache2/sites-available/scodoc-site-ssl.conf + echo 'activation du site...' + a2ensite scodoc-site-ssl + + echo 'Remplacement du site Apache par defaut (sic ! old saved as .bak)' + fn=/etc/apache2/sites-available/000-default.conf + if [ -e $fn ] + then + mv $fn $fn.bak + fi + cp $SCODOC_DIR/config/etc/scodoc-site.orig $fn + + if [ -z "$(grep Listen /etc/apache2/ports.conf | grep 443)" ] + then + echo 'adding port 443' + echo 'Listen 443' >> /etc/apache2/ports.conf + fi + + echo 'configuring Apache proxy' + mv /etc/apache2/mods-available/proxy.conf /etc/apache2/mods-available/proxy.conf.bak + cat > /etc/apache2/mods-available/proxy.conf < +# Proxy config for ScoDoc default installation +ProxyRequests Off + + Order deny,allow + Allow from all + + +EOF + +fi + +service apache2 restart + + +# ------------ CONFIG SERVICE SCODOC +echo +echo "Installer le service scodoc permet de lancer automatiquement le serveur au demarrage." +echo -n "Voulez vous installer le service scodoc ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + echo 'Installation du demarrage automatique de ScoDoc' + cp $SCODOC_DIR/config/etc/scodoc /etc/init.d/ + insserv scodoc +fi + + +# ------------ CONFIG MISE A JOUR HEBDOMADAIRE +echo +echo -n "Mises a jour hebdomadaires (tres recommande) ? (y/n) [y] " +read ans +if [ "$(norm_ans "$ans")" != 'N' ] +then + cp $SCODOC_DIR/config/etc/scodoc-updater.service /etc/systemd/system + cp $SCODOC_DIR/config/etc/scodoc-updater.timer /etc/systemd/system + systemctl enable scodoc-updater.timer + systemctl start scodoc-updater.timer +fi + +# ------------ THE END +echo +echo "Installation terminee." +echo +echo "Vous pouvez maintenant creer la base d'utilisateurs avec ./create_user_db.sh" +echo "puis creer un departement avec ./create_dept.sh" +echo "Ou bien restaurer vos donnees a partir d'une ancienne installation a l'aide du script restore_scodoc_data.sh" +echo "(voir https://trac.lipn.univ-paris13.fr/projects/scodoc/wiki/MigrationDonneesScoDoc)" +echo + + +if [ "${cert_status}" != 0 ] +then + echo "Attention: le serveur Web Apache n'a pas de certificat." + echo "Il est probable qu'il ne fonctionne pas." + echo "Installez vos certificats ou generez provisoirement des certificats autosignes" + echo "avec la commande: /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf $ssl_dir/apache.pem" + echo +fi + diff --git a/config/postupgrade-db.py b/config/postupgrade-db.py new file mode 100755 index 00000000..b6d930d7 --- /dev/null +++ b/config/postupgrade-db.py @@ -0,0 +1,650 @@ +#!/opt/zope213/bin/python +# -*- coding: utf-8 -*- + +""" +ScoDoc post-upgrade script: databases housekeeping + +This script is runned by upgrade.sh after each SVN update. + +Runned as "www-data" with Zope shutted down and postgresql up. + + +Useful to update database schema (eg add new tables or columns to +existing scodoc instances). + +E. Viennet, june 2008, sept 2013 +""" + +from scodocutils import * + +for dept in get_depts(): + log('\nChecking database for dept %s' % dept) + cnx_string = None + try: + cnx_string = get_dept_cnx_str(dept) + cnx = psycopg2.connect( cnx_string ) + except: + log('\n*** Error: departement %s not upgraded ! ***\n' % dept) + log('connexion string was "%s"' % cnx_string) + traceback.print_exc() + continue + cnx.set_session(autocommit=False) + cursor = cnx.cursor() + # Apply upgrades: + + # SVN 564 -> 565 + # add resp_can_edit to notes_formsemestre: + check_field(cnx, 'notes_formsemestre', 'resp_can_edit', + ['alter table notes_formsemestre add column resp_can_edit int default 0', + 'update notes_formsemestre set resp_can_edit=0']) + + # SVN 580 -> 581 + # add resp_can_change_ens to notes_formsemestre: + check_field(cnx, 'notes_formsemestre', 'resp_can_change_ens', + ['alter table notes_formsemestre add column resp_can_change_ens int default 1', + 'update notes_formsemestre set resp_can_change_ens=1']) + + # Fix bug ayant empeche la creation de la table + check_table( cnx, 'admissions', [ + """CREATE TABLE admissions ( + adm_id text DEFAULT notes_newid_etud('ADM'::text) NOT NULL, + etudid text NOT NULL, + annee integer, + bac text, + specialite text, + annee_bac integer, + math real, + physique real, + anglais real, + francais real, + rang integer, -- dans les voeux du candidat (inconnu avec APB) + qualite real, + rapporteur text, + decision text, + score real, + commentaire text, + nomlycee text, + villelycee text, + codepostallycee text, + codelycee text, + debouche text, -- situation APRES etre passe par chez nous (texte libre) + type_admission text, -- 'APB', 'APC-PC', 'CEF', 'Direct', '?' (autre) + boursier_prec integer default NULL, -- etait boursier dans le cycle precedent (lycee) ? + classement integer default NULL, -- classement par le jury d'admission (1 Ă  N), global (pas celui d'APB si il y a des groupes) + apb_groupe text, -- code du groupe APB + apb_classement_gr integer default NULL -- classement (1..Ngr) par le jury dans le groupe APB +) WITH OIDS; +"""] ) + + # SVN 651 + # Nouvelles donnees d'admission + check_field(cnx, 'admissions', 'codelycee', + ['alter table admissions add column codelycee text', + ]) + check_field(cnx, 'admissions', 'codepostallycee', + ['alter table admissions add column codepostallycee text', + ]) + + # New preferences system + check_field(cnx, 'sco_prefs', 'formsemestre_id', + ["alter table sco_prefs add column pref_id text DEFAULT notes_newid('PREF'::text) UNIQUE NOT NULL", + "update sco_prefs set pref_id=oid", + "alter table sco_prefs add column formsemestre_id text default NULL", + "alter table sco_prefs drop CONSTRAINT sco_prefs_pkey", + "alter table sco_prefs add unique( name, formsemestre_id)", + # copie anciennes prefs: + "insert into sco_prefs (name, value, formsemestre_id) select 'left_margin', left_margin, formsemestre_id from notes_formsemestre_pagebulletin", + "insert into sco_prefs (name, value, formsemestre_id) select 'top_margin', top_margin, formsemestre_id from notes_formsemestre_pagebulletin", + "insert into sco_prefs (name, value, formsemestre_id) select 'right_margin', right_margin, formsemestre_id from notes_formsemestre_pagebulletin", + "insert into sco_prefs (name, value, formsemestre_id) select 'bottom_margin', bottom_margin, formsemestre_id from notes_formsemestre_pagebulletin", + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_title', title, formsemestre_id from notes_formsemestre_pagebulletin", + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_intro_mail', intro_mail, formsemestre_id from notes_formsemestre_pagebulletin", + "drop table notes_formsemestre_pagebulletin", + # anciens champs de formsemestre: + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_abs', gestion_absence, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column gestion_absence", + + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_decision', bul_show_decision, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column bul_show_decision", + + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_uevalid', bul_show_uevalid, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column bul_show_uevalid", + + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_codemodules', bul_show_codemodules, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column bul_show_codemodules", + + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_rangs', bul_show_rangs, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column bul_show_rangs", + + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_ue_rangs', bul_show_ue_rangs, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column bul_show_ue_rangs", + + "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_mod_rangs', bul_show_mod_rangs, formsemestre_id from notes_formsemestre", + "alter table notes_formsemestre drop column bul_show_mod_rangs", + ]) + # fixed previous bug (misspelled pref) + cursor.execute("update sco_prefs set name = 'bul_show_codemodules' where name = 'bul_showcodemodules'") + + # billets d'absences + if not sequence_exists(cnx, 'notes_idgen_billets'): + log('creating sequence notes_idgen_billets') + cursor.execute('CREATE SEQUENCE notes_idgen_billets;') + + if not function_exists(cnx, 'notes_newid_billet'): + log('creating function notes_newid_billet') + cursor.execute("""CREATE FUNCTION notes_newid_billet( text ) returns text as ' + select $1 || to_char( nextval(''notes_idgen_billets''), ''FM999999999'' ) + as result; + ' language SQL;""") + + check_table( cnx, 'billet_absence', [ + """CREATE TABLE billet_absence ( + billet_id text DEFAULT notes_newid_billet('B'::text) NOT NULL, + etudid text NOT NULL, + abs_begin timestamp with time zone, + abs_end timestamp with time zone, + description text, -- "raison" de l'absence + etat integer default 0 -- 0 new, 1 processed +) WITH OIDS; +"""] ) + + # description absence + check_field(cnx, 'absences', 'description', + ['alter table absences add column description text' + ]) + check_field(cnx, 'absences', 'entry_date', + ['alter table absences add column entry_date timestamp with time zone DEFAULT now()' + ]) + check_field(cnx, 'billet_absence', 'entry_date', + ['alter table billet_absence add column entry_date timestamp with time zone DEFAULT now()' + ]) + # Nouvelles preferences pour bulletins PDF: migre bul_show_chiefDept + cursor.execute("update sco_prefs set name = 'bul_show_sig_right' where name = 'bul_show_chiefDept'") + # cursor.execute("insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_sig_left', value, formsemestre_id from sco_prefs where name = 'bul_show_sig_right'") + # date et lieu naissance (pour IFAG Sofia) + check_field(cnx, 'identite', 'date_naissance', + ['alter table identite add column date_naissance date', + "update identite set date_naissance=to_date(to_char( annee_naissance, 'FM9999') || '-01-01', 'YYYY-MM-DD')", + 'alter table identite drop column annee_naissance' + ]) + check_field(cnx, 'identite', 'lieu_naissance', + ['alter table identite add column lieu_naissance text' + ]) + # justification billets: + check_field(cnx, 'billet_absence', 'justified', + [ 'alter table billet_absence add column justified integer default 0', + 'update billet_absence set justified=0' + ]) + + # ----------------------- New groups + # 1- Create new tables + check_table( cnx, 'partition', [ + """CREATE TABLE partition( + partition_id text default notes_newid2('P') PRIMARY KEY, + formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id), + partition_name text, -- "TD", "TP", ... + compute_ranks integer default 1, -- calcul rang etudiants dans les groupes + numero SERIAL, -- ordre de presentation + UNIQUE(formsemestre_id,partition_name) +) WITH OIDS; +"""] ) + check_table( cnx, 'group_descr', [ + """CREATE TABLE group_descr ( + group_id text default notes_newid2('G') PRIMARY KEY, + partition_id text REFERENCES partition(partition_id), + group_name text, -- "A", "C2", ... + UNIQUE(partition_id, group_name) +) WITH OIDS; +"""] ) + check_table( cnx, 'group_membership', [ + """CREATE TABLE group_membership( + group_membership_id text default notes_newid2('GM') PRIMARY KEY, + etudid text REFERENCES identite(etudid), + group_id text REFERENCES group_descr(group_id), + UNIQUE(etudid, group_id) +) WITH OIDS; +"""] ) + + # 2- For each sem, create 1 to 4 partitions: all, TD (if any), TP, TA + # Here we have to deal with plain SQL, nasty... + if field_exists(cnx, 'notes_formsemestre_inscription', 'groupetd'): + # Some very old stduents didn't have addresses: it's now mandatory + cursor.execute("insert into adresse (etudid) select etudid from identite i except select etudid from adresse") + # + cursor.execute("SELECT formsemestre_id from notes_formsemestre") + formsemestre_ids = [ x[0] for x in cursor.fetchall() ] + for formsemestre_id in formsemestre_ids: + # create "all" partition (with empty name) + cursor.execute("INSERT into partition (formsemestre_id, compute_ranks) VALUES (%(formsemestre_id)s, 1)", {'formsemestre_id' : formsemestre_id } ) + cursor.execute("select partition_id from partition where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + partition_id = cursor.fetchone()[0] + # create group "all" (without name) + cursor.execute("INSERT into group_descr (partition_id) VALUES (%(pid)s)", { 'pid' : partition_id } ) + cursor.execute("SELECT group_id from group_descr where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + group_id = cursor.fetchone()[0] + # inscrit etudiants: + cursor.execute("INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", { 'group_id' : group_id, 'formsemestre_id' : formsemestre_id } ) + + # create TD, TP, TA + cursor.execute("SELECT distinct(groupetd) from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", { 'formsemestre_id' : formsemestre_id } ) + groupetds = [ x[0] for x in cursor.fetchall() if x[0] ] + if len(groupetds) > 1 or (len(groupetds)==1 and groupetds[0] != 'A'): + # TD : create partition + cursor.execute("SELECT * from notes_formsemestre where formsemestre_id=%(formsemestre_id)s", { 'formsemestre_id' : formsemestre_id } ) + nomgroupetd = cursor.dictfetchone()['nomgroupetd'] + if not nomgroupetd: # pas de nom ??? on invente un nom stupide et unique + nomgroupetd = 'TD_'+str(time.time()).replace('.','')[-3:] + cursor.execute("INSERT into partition (formsemestre_id, partition_name) VALUES (%(formsemestre_id)s,%(nomgroupetd)s)", { 'formsemestre_id' : formsemestre_id, 'nomgroupetd' : nomgroupetd } ) + cursor.execute("select partition_id from partition where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + partition_id = cursor.fetchone()[0] + # create groups + for groupetd in groupetds: + cursor.execute("INSERT into group_descr (partition_id, group_name) VALUES (%(pid)s, %(group_name)s)", { 'pid' : partition_id, 'group_name' : groupetd } ) + cursor.execute("SELECT group_id from group_descr where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + group_id = cursor.fetchone()[0] + # inscrit les etudiants + cursor.execute("INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s and groupetd=%(groupetd)s", { 'group_id' : group_id, 'formsemestre_id' : formsemestre_id, 'groupetd' : groupetd } ) + # TA + cursor.execute("SELECT distinct(groupeanglais) from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", { 'formsemestre_id' : formsemestre_id } ) + groupetds = [ x[0] for x in cursor.fetchall() if x[0] ] + if len(groupetds) > 0: + # TA : create partition + cursor.execute("SELECT * from notes_formsemestre where formsemestre_id=%(formsemestre_id)s", { 'formsemestre_id' : formsemestre_id } ) + nomgroupetd = cursor.dictfetchone()['nomgroupeta'] + if not nomgroupetd: # pas de nom ??? on invente un nom stupide et unique + nomgroupetd = 'TA_'+str(time.time()).replace('.','')[-3:] + cursor.execute("INSERT into partition (formsemestre_id, partition_name) VALUES (%(formsemestre_id)s,%(nomgroupeta)s)", { 'formsemestre_id' : formsemestre_id, 'nomgroupeta' : nomgroupetd } ) + cursor.execute("select partition_id from partition where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + partition_id = cursor.fetchone()[0] + # create groups + for groupetd in groupetds: + cursor.execute("INSERT into group_descr (partition_id, group_name) VALUES (%(pid)s, %(group_name)s)", { 'pid' : partition_id, 'group_name' : groupetd } ) + cursor.execute("SELECT group_id from group_descr where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + group_id = cursor.fetchone()[0] + # inscrit les etudiants + cursor.execute("INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s and groupeanglais=%(groupetd)s", { 'group_id' : group_id, 'formsemestre_id' : formsemestre_id, 'groupetd' : groupetd } ) + + # TP + cursor.execute("SELECT distinct(groupetp) from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", { 'formsemestre_id' : formsemestre_id } ) + groupetds = [ x[0] for x in cursor.fetchall() if x[0] ] + if len(groupetds) > 0: + # TP : create partition + cursor.execute("SELECT * from notes_formsemestre where formsemestre_id=%(formsemestre_id)s", { 'formsemestre_id' : formsemestre_id } ) + nomgroupetd = cursor.dictfetchone()['nomgroupetp'] + if not nomgroupetd: # pas de nom ??? on invente un nom stupide et unique + nomgroupetd = 'TP_'+str(time.time()).replace('.','')[-3:] + cursor.execute("INSERT into partition (formsemestre_id, partition_name) VALUES (%(formsemestre_id)s,%(nomgroupeta)s)", { 'formsemestre_id' : formsemestre_id, 'nomgroupeta' : nomgroupetd } ) + cursor.execute("select partition_id from partition where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + partition_id = cursor.fetchone()[0] + # create groups + for groupetd in groupetds: + cursor.execute("INSERT into group_descr (partition_id, group_name) VALUES (%(pid)s, %(group_name)s)", { 'pid' : partition_id, 'group_name' : groupetd } ) + cursor.execute("SELECT group_id from group_descr where oid=%(oid)s", { 'oid' : cursor.lastoid() }) + group_id = cursor.fetchone()[0] + # inscrit les etudiants + cursor.execute("INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s and groupetp=%(groupetd)s", { 'group_id' : group_id, 'formsemestre_id' : formsemestre_id, 'groupetd' : groupetd } ) + + # 3- Suppress obsolete fields + cursor.execute( """alter table notes_formsemestre drop column nomgroupetd""" ) + cursor.execute( """alter table notes_formsemestre drop column nomgroupetp""" ) + cursor.execute( """alter table notes_formsemestre drop column nomgroupeta""" ) + + cursor.execute( """alter table notes_formsemestre_inscription drop column groupetd""" ) + cursor.execute( """alter table notes_formsemestre_inscription drop column groupetp""" ) + cursor.execute( """alter table notes_formsemestre_inscription drop column groupeanglais""" ) + # ----------------------- /New groups + + # Add moy_ue to validations: + check_field(cnx, 'scolar_formsemestre_validation', 'moy_ue', + ['alter table scolar_formsemestre_validation add column moy_ue real', + ]) + # Add photo_filename + check_field(cnx, 'identite', 'photo_filename', + ['alter table identite add column photo_filename text', + ]) + # Add module's ECTS + check_field(cnx, 'notes_modules', 'ects', + ['alter table notes_modules add column ects real', + ]) + # Add "statut" to identite (default to NULL) + check_field(cnx, 'identite', 'statut', + ['alter table identite add column statut text', + ]) + # Add user-defined expressions + check_field(cnx, 'notes_moduleimpl', 'computation_expr', + ['alter table notes_moduleimpl add column computation_expr text']) + # Add semestre_id to scolar_formsemestre_validation + check_field(cnx, 'scolar_formsemestre_validation', 'semestre_id', + ['alter table scolar_formsemestre_validation add column semestre_id int']) + + # Add + check_table( cnx, 'absences_notifications', [ """ + CREATE TABLE absences_notifications ( + etudid text NOT NULL, + notification_date timestamp with time zone DEFAULT now(), + email text NOT NULL, + nbabs integer, + nbabsjust integer + ) WITH OIDS; + """] ) + # rename old preference "send_mail_absence_to_chef" + cursor.execute("update sco_prefs set name = 'abs_notify_chief' where name = 'send_mail_absence_to_chef'") + + check_table( cnx, 'notes_formsemestre_ue_computation_expr', [ """ + CREATE TABLE notes_formsemestre_ue_computation_expr ( + notes_formsemestre_ue_computation_expr_id text default notes_newid('UEXPR') PRIMARY KEY, + formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id), + ue_id text REFERENCES notes_ue(ue_id), + computation_expr text, -- formule de calcul moyenne + UNIQUE(formsemestre_id, ue_id) + ) WITH OIDS; + """] ) + + + # add moduleimpl_id to absences: + check_field(cnx, 'absences', 'moduleimpl_id', + ['alter table absences add column moduleimpl_id text']) + + # add type_parcours + check_field(cnx, 'notes_formations', 'type_parcours', + ['alter table notes_formations add column type_parcours int DEFAULT 0', + 'update notes_formations set type_parcours=0 where type_parcours is NULL' + ]) + + # add etape_apo2 + check_field(cnx, 'notes_formsemestre', 'etape_apo2', + ['alter table notes_formsemestre add column etape_apo2 text']) + # add etape_apo3 + check_field(cnx, 'notes_formsemestre', 'etape_apo3', + ['alter table notes_formsemestre add column etape_apo3 text']) + # add etape_apo4 + check_field(cnx, 'notes_formsemestre', 'etape_apo4', + ['alter table notes_formsemestre add column etape_apo4 text']) + # add publish_incomplete + check_field(cnx, 'notes_evaluation', 'publish_incomplete', + ['alter table notes_evaluation add column publish_incomplete int DEFAULT 0', + 'update notes_evaluation set publish_incomplete=0 where publish_incomplete is NULL' + ]) + + # add ens_can_create_eval to notes_formsemestre: + check_field(cnx, 'notes_formsemestre', 'ens_can_edit_eval', + ['alter table notes_formsemestre add column ens_can_edit_eval int default 0', + 'update notes_formsemestre set ens_can_edit_eval=0']) + + # add evaluation_type + check_field(cnx, 'notes_evaluation', 'evaluation_type', + ['alter table notes_evaluation add column evaluation_type int DEFAULT 0', + 'update notes_evaluation set evaluation_type=0 where evaluation_type is NULL' + ]) + + # add partition rank on bulletins + check_field(cnx, 'partition', 'bul_show_rank', + ['alter table partition add column bul_show_rank int DEFAULT 0', + 'update partition set bul_show_rank=0 where bul_show_rank is NULL']) + # add formsemestre to abs notifications + check_field(cnx, 'absences_notifications', 'formsemestre_id', + ['alter table absences_notifications add column formsemestre_id text DEFAULT NULL', + ]) + # Add "debouche" to admission + check_field(cnx, 'admissions', 'debouche', + ['alter table admissions add column debouche text DEFAULT NULL', + # et en profite pour corrige le From par defaut des mails: + "update sco_prefs set value='noreply@univ-paris13.fr' where name='email_from_addr' and value='noreply'" + ]) + # Increase semestre indices + for i in range(5,9): + cursor.execute("SELECT * from notes_semestres where semestre_id=%(i)s", { 'i' : i } ) + r = cursor.fetchall() + if not r: + log("adding semestre_id %s" % i) + cursor.execute("INSERT INTO notes_semestres (semestre_id) VALUES (%(i)s)", { 'i' : i } ) + # ECTS associes aux UE: + check_field(cnx, 'notes_ue', 'ects', + ['alter table notes_ue add column ects float DEFAULT NULL', + ]) + # Numeros des evaluations: + check_field(cnx, 'notes_evaluation', 'numero', + ['alter table notes_evaluation add column numero int DEFAULT 0', + ]) + # add nom_usuel to identite + check_field(cnx, 'identite', 'nom_usuel', + ['alter table identite add column nom_usuel text DEFAULT NULL', + ]) + # add type_admission + check_field(cnx, 'admissions', 'type_admission', + ['alter table admissions add column type_admission text DEFAULT NULL', + ]) + check_field(cnx, 'admissions', 'boursier_prec', + ['alter table admissions add column boursier_prec integer default NULL', + ]) + # add modalites formation + check_table( cnx, 'notes_form_modalites', [ + """CREATE TABLE notes_form_modalites ( + form_modalite_id text default notes_newid('Md') PRIMARY KEY, + modalite text, -- la clef dans notes_formsemestre + titre text, -- le nom complet de la modalite pour les documents scodoc + numero SERIAL -- integer, ordre de presentation + );""", + """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('', 'Autres formations');""", + """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('FI', 'Formation Initiale');""", + """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('FC', 'Formation Continue');""", + """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('FAP', 'Apprentissage');""", + """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('DEC', 'Formation DĂ©calĂ©es');""", + """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('LIC', 'Licence');""" + ] ) + # Add code_specialite + check_field( cnx, 'notes_formations', 'code_specialite', + [ 'alter table notes_formations add column code_specialite text default NULL', + ]) + # Fix modules without codes + cursor.execute("UPDATE notes_modules SET code = 'M_' || coalesce(upper(substring(titre from 1 for 2)), '') || '_' || coalesce(semestre_id,'0') where code is NULL;"); + + # Add ue.is_external + check_field( cnx, 'notes_ue', 'is_external', + [ 'alter table notes_ue add column is_external integer default 0', + ]) + check_field( cnx, 'scolar_formsemestre_validation', 'is_external', + [ 'alter table scolar_formsemestre_validation add column is_external integer default 0', + ]) + # Add codes apogee + check_field( cnx, 'notes_ue', 'code_apogee', + [ 'alter table notes_ue add column code_apogee text UNIQUE', + ]) + check_field( cnx, 'notes_modules', 'code_apogee', + [ 'alter table notes_modules add column code_apogee text UNIQUE', + ]) + check_field( cnx, 'notes_formsemestre', 'elt_sem_apo', + [ 'alter table notes_formsemestre add column elt_sem_apo text', + ]) + check_field( cnx, 'notes_formsemestre', 'elt_annee_apo', + [ 'alter table notes_formsemestre add column elt_annee_apo text', + ]) + # Classement admission + check_field(cnx, 'admissions', 'classement', + ['alter table admissions add column classement integer default NULL', + ]) + # Supprime contraintes erronĂ©es sur codes Apogee: + if list_constraint( cnx, constraint_name='notes_ue_code_apogee_key' ): + log('dropping buggy constraint on notes_ue_code_apogee') + cursor.execute("alter table notes_ue drop CONSTRAINT notes_ue_code_apogee_key;") + if list_constraint( cnx, constraint_name='notes_modules_code_apogee_key' ): + log('dropping buggy constraint on notes_modules_code_apogee') + cursor.execute("alter table notes_modules drop CONSTRAINT notes_modules_code_apogee_key;") + + # SemSet: + check_table( cnx, 'notes_semset', [ + """CREATE TABLE notes_semset ( + semset_id text default notes_newid('NSS') PRIMARY KEY, + title text, + annee_scolaire int default NULL, -- 2016 + sem_id int default NULL -- 0, 1, 2 + ) WITH OIDS;""", ] ) + check_field(cnx, 'notes_semset', 'annee_scolaire', + ['alter table notes_semset add column annee_scolaire integer default NULL', + ]) + check_table( cnx, 'notes_semset_formsemestre', [ + """CREATE TABLE notes_semset_formsemestre ( + formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, + semset_id text REFERENCES notes_semset (semset_id) ON DELETE CASCADE, + PRIMARY KEY (formsemestre_id, semset_id) + ) WITH OIDS;""", ] ) + + # ModuleTags + check_table( cnx, 'notes_tags', [ + """CREATE TABLE notes_tags ( + tag_id text default notes_newid('TAG') PRIMARY KEY, + title text UNIQUE NOT NULL + ) WITH OIDS;""", ] ) + check_table( cnx, 'notes_modules_tags', [ + """CREATE TABLE notes_modules_tags ( + tag_id text REFERENCES notes_tags(tag_id) ON DELETE CASCADE, + module_id text REFERENCES notes_modules(module_id) ON DELETE CASCADE, + PRIMARY KEY (tag_id, module_id) + ) WITH OIDS;""", ] ) + + # add show_in_lists on partition + check_field(cnx, 'partition', 'show_in_lists', + ['alter table partition add column show_in_lists integer DEFAULT 1', + 'update partition set show_in_lists=1 where show_in_lists is NULL']) + # table codes etapes apogee semestre + check_table( cnx, 'notes_formsemestre_etapes', [ + """CREATE TABLE notes_formsemestre_etapes ( + formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, + etape_apo text NOT NULL +) WITH OIDS;""", + """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo FROM notes_formsemestre WHERE etape_apo is not NULL;""", + """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo2 FROM notes_formsemestre WHERE etape_apo2 is not NULL;""", + """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo3 FROM notes_formsemestre WHERE etape_apo3 is not NULL;""", + """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo4 FROM notes_formsemestre WHERE etape_apo4 is not NULL;""", + """ALTER table notes_formsemestre DROP column etape_apo;""", + """ALTER table notes_formsemestre DROP column etape_apo2;""", + """ALTER table notes_formsemestre DROP column etape_apo3;""", + """ALTER table notes_formsemestre DROP column etape_apo4;""", + ] ) + # Admission APB: groupe et classement dans groupe + check_field(cnx, 'admissions', 'apb_groupe', + ['alter table admissions add column apb_groupe text default NULL', + ]) + check_field(cnx, 'admissions', 'apb_classement_gr', + ['alter table admissions add column apb_classement_gr integer default NULL', + ]) + # Adresse mail perso + check_field(cnx, 'adresse', 'emailperso', + ['alter table adresse add column emailperso text', + ]) + # Ajout de modalites supplementaires + cursor.execute("SELECT modalite from notes_form_modalites where modalite= 'CP'") + if not len(cursor.fetchall()): + log('adding modalite "CP"') + cursor.execute("INSERT INTO notes_form_modalites (modalite, titre) VALUES ('CP', 'Contrats de Professionnalisation');") + # Un index oubliĂ© sur notes_notes: + if 'notes_notes_evaluation_id_idx' not in list_table_index(cnx, 'notes_notes'): + log('creating index on notes_notes') + cursor.execute("CREATE INDEX notes_notes_evaluation_id_idx ON notes_notes (evaluation_id)") + + # boursier (ajout API nov 2017) + check_field(cnx, 'identite', 'boursier', + ['alter table identite add column boursier text' + ]) + # Suivi des anciens etudiants (debouche) + # cree table suivi et recopie ancien champs debouche de la table admission + check_table( cnx, 'itemsuivi', [ + """CREATE TABLE itemsuivi ( + itemsuivi_id text DEFAULT notes_newid('SUI'::text) PRIMARY KEY, + etudid text NOT NULL, + item_date date DEFAULT now(), + situation text + ) WITH OIDS;""", + + """INSERT INTO itemsuivi (etudid, situation) + SELECT etudid, debouche FROM admissions WHERE debouche is not null; + """ + ] ) + check_table( cnx, 'itemsuivi_tags', [ + """CREATE TABLE itemsuivi_tags ( + tag_id text default notes_newid('TG') PRIMARY KEY, + title text UNIQUE NOT NULL + ) WITH OIDS;""", + ] ) + check_table( cnx, 'itemsuivi_tags_assoc', [ + """CREATE TABLE itemsuivi_tags_assoc ( + tag_id text REFERENCES itemsuivi_tags(tag_id) ON DELETE CASCADE, + itemsuivi_id text REFERENCES itemsuivi(itemsuivi_id) ON DELETE CASCADE, + PRIMARY KEY (tag_id, itemsuivi_id) + ) WITH OIDS;""", + ] ) + + # Types de modules (pour malus) + check_field(cnx, 'notes_modules', 'module_type', + ['alter table notes_modules add column module_type int', + ]) + + # Responsables de semestres + check_table( cnx, 'notes_formsemestre_responsables', [ + """CREATE TABLE notes_formsemestre_responsables ( + formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, + responsable_id text NOT NULL, + UNIQUE(formsemestre_id, responsable_id) + ) WITH OIDS;""", + """INSERT into notes_formsemestre_responsables (formsemestre_id, responsable_id) SELECT formsemestre_id, responsable_id FROM notes_formsemestre WHERE responsable_id is not NULL;""", + """ALTER table notes_formsemestre DROP column responsable_id;""", + ]) + # Fonction pour anonymisation: + if not function_exists(cnx, 'random_text_md5'): + log('creating function random_text_md5') + # inspirĂ©e par https://www.simononsoftware.com/random-string-in-postgresql/ + cursor.execute("""CREATE FUNCTION random_text_md5( integer ) returns text + LANGUAGE SQL + AS $$ + select upper( substring( (SELECT string_agg(md5(random()::TEXT), '') + FROM generate_series( + 1, + CEIL($1 / 32.)::integer) + ), 1, $1) ); + $$;""") + # departement naissance (ajout fev 2020) + check_field( + cnx, 'identite', 'dept_naissance', + ['alter table identite add column dept_naissance text']) + # Modalite semestres exterieurs + cursor.execute("SELECT modalite from notes_form_modalites where modalite= 'EXT'") + if not len(cursor.fetchall()): + log('adding modalite "EXT"') + cursor.execute("INSERT INTO notes_form_modalites (modalite, titre) VALUES ('EXT', 'ExtĂ©rieur');") + # Coefficients d'UE + check_field(cnx, 'notes_ue', 'coefficient', + ["alter table notes_ue add column coefficient float", + # Initialise les coefficients Ă©gaux aux ECTS: + "update notes_ue set coefficient=ects", + # Force pref locale sur semestres existants: + """INSERT INTO sco_prefs (name, value, formsemestre_id) + SELECT DISTINCT 'use_ue_coefs', '0', formsemestre_id FROM notes_formsemestre + ON CONFLICT DO NOTHING + """ + ]) + # Add here actions to performs after upgrades: + cnx.commit() + cnx.close() + + +# Base utilisateurs: +log('\nChecking users database') +cnx = psycopg2.connect( get_users_cnx_str() ) +cursor = cnx.cursor() +check_field(cnx, 'sco_users', 'passwd_temp', + ['alter table sco_users add column passwd_temp int default 0', + 'update sco_users set passwd_temp=0' ]) +check_field(cnx, 'sco_users', 'status', + ["alter table sco_users add column status text default NULL"]) +check_field(cnx, 'sco_users', 'date_expiration', + ["alter table sco_users add column date_expiration date", + "update sco_users set status=NULL where status=''" # fix a bug in previous update... + ]) +check_field(cnx, 'sco_users', 'login_edt', + ["alter table sco_users add column login_edt text default NULL", + ]) +cnx.commit() +cnx.close() + +# The end. +sys.exit(0) diff --git a/config/postupgrade.py b/config/postupgrade.py new file mode 100755 index 00000000..1137357a --- /dev/null +++ b/config/postupgrade.py @@ -0,0 +1,60 @@ +#!/opt/zope213/bin/python + +""" +ScoDoc post-upgrade script. + +This script is launched by upgrade.sh after each SVN update. + +Run as "root" with Zope shutted down and postgresql up, +_before_ upgrading the database. + +E. Viennet, June 2008 +Mar 2017: suppress upgrade of very old Apache configs +Aug 2020: move photos to .../var/scodoc/ +""" +import os +import sys +import glob +import shutil +from scodocutils import log, SCODOC_DIR, SCODOC_VAR_DIR, SCODOC_LOGOS_DIR + +if os.getuid() != 0: + log('postupgrade.py: must be run as root') + sys.exit(1) + +# --- +# Migrate photos (2020-08-16, svn 1908) +old_photo_dir = os.path.join(SCODOC_DIR, "static", "photos") +photo_dirs = glob.glob( old_photo_dir + "/F*") +if photo_dirs: + log("Moving photos to new directory...") + shutil.move(old_photo_dir, SCODOC_VAR_DIR) + +# Migrate depts (2020-08-17, svn 1909) + +old_depts_dir = os.path.join(SCODOC_DIR, "config", "depts") +cfg_files = glob.glob( old_depts_dir + "/*.cfg") +depts_dir = os.path.join(SCODOC_VAR_DIR, "config/depts/") +for cfg in cfg_files: + log("Moving %s to new directory..." % cfg) + shutil.move(cfg, depts_dir) + +# Move logos +if not os.path.exists(SCODOC_LOGOS_DIR): + old_logos = os.path.join(SCODOC_DIR,"logos") + if os.path.exists(old_logos): + log("Moving logos to new directory...") + dest = os.path.normpath(os.path.join(SCODOC_LOGOS_DIR, "..")) + shutil.move(old_logos, dest) + else: + log("Warning: logos directory is missing (%s)" % SCODOC_LOGOS_DIR) + +# Move dept-specific logos +for d in glob.glob( SCODOC_DIR + "/logos_*" ): + log("Moving %s to %s" % (d, SCODOC_LOGOS_DIR)) + shutil.move(d, SCODOC_LOGOS_DIR) + +# Continue here... + +# --- +sys.exit(0) diff --git a/config/psql_restore_databases.sh b/config/psql_restore_databases.sh new file mode 100755 index 00000000..9eb260a5 --- /dev/null +++ b/config/psql_restore_databases.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# aux script called by restore_scodoc_data.sh as "postgres" user +# DO NOT CALL DIRECTLY + +PG_DUMPFILE=$1 + +# Check locale of installation. If invalid, reinitialize all system + +is_latin1=$(psql -l | grep postgres | grep iso88591 | wc -l) +if [ $is_latin1 -gt 1 ] +then + echo "Recreating postgres cluster using UTF-8" + + pg_dropcluster --stop 9.1 main + + pg_createcluster --locale en_US.UTF-8 --start 9.1 main +fi + + +# Drop all current ScoDoc databases, if any: +for f in $(psql -l --no-align --field-separator . | grep SCO | cut -f 1 -d.); do + echo dropping $f + dropdb $f +done +echo "Restoring postgres data..." +psql -f "$PG_DUMPFILE" postgres + diff --git a/config/restore_scodoc_data.sh b/config/restore_scodoc_data.sh new file mode 100755 index 00000000..aed3a1ea --- /dev/null +++ b/config/restore_scodoc_data.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# +# ScoDoc: restore data (saved by save_scodoc_data) into current install +# +# Utile pour migrer ScoDoc d'un serveur a un autre +# A executer en tant que root sur le nouveau serveur +# +# E. Viennet, Sept 2011, Nov 2013, Mar 2017, Aug 2020 +# + + +INSTANCE_DIR=/opt/scodoc/ +SCODOC_DIR="${INSTANCE_DIR}/Products/ScoDoc" +SCODOC_VAR_DIR="${INSTANCE_DIR}/var/scodoc" + +source utils.sh +check_uid_root $0 + +# Safety check +echo "Ce script va remplacer les donnees de votre installation ScoDoc par celles" +echo "enregistrees dans le fichier fourni." +echo "Ce fichier doit avoir ete cree par le script save_scodoc_data.sh, sur une autre machine." +echo +echo "Attention: TOUTES LES DONNEES DE CE SERVEUR SERONT REMPLACEES !" +echo "Notamment, tous les utilisateurs et departements existants seront effaces !" +echo +echo "TOUTES LES BASES POSTGRESQL SERONT EFFACEES !!!" +echo +echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]" +read ans +if [ ! "$(norm_ans "$ans")" = 'Y' ] +then + echo "Annulation" + exit 1 +fi + +# Usage +if [ ! $# -eq 1 ] +then + echo "Usage: $0 directory_or_archive" + exit 1 +fi + +SRC=$1 + +if [ "${SRC:0:1}" != "/" ] +then + echo "Usage: $0 directory_or_archive" + echo "Erreur: utiliser un chemin absolu (commencant par /)" + exit 1 +fi + +# Source directory +if [ "${SRC##*.}" = 'tgz' ] +then + echo "Opening tgz archive..." + tmp=$(mktemp -d) + chmod a+rx "$tmp" + cd "$tmp" + tar xfz "$SRC" + SRC=$(ls -1d "$tmp"/*) + IS_TMP=1 + # If source is a tgz, can use mv + COPY="mv" +else + IS_TMP=0 + # If source is a directory, does not modify its content + COPY="cp -rp" +fi + +echo "Source is $SRC" +echo "Stopping ScoDoc..." +/etc/init.d/scodoc stop + +# Erase all postgres databases and load data +chmod a+rx "$SRC" +chmod a+r "$SRC"/scodoc.dump.txt +PG_DUMPFILE="$SRC/scodoc.dump.txt" + +su -c "$SCODOC_DIR/config/psql_restore_databases.sh $PG_DUMPFILE" postgres + +# +echo Copying data files... + +rm -rf "$INSTANCE_DIR/var" +$COPY "$SRC/var" "$INSTANCE_DIR" + +if [ ! -e "${SCODOC_VAR_DIR}/config/" ] +then + mkdir "${SCODOC_VAR_DIR}/config/" + chown www-data.www-data "${SCODOC_VAR_DIR}/config/" + chmod 775 "${SCODOC_VAR_DIR}/config/" +fi + +rm -rf "$SCODOC_DIR/config/depts" +if [ -e "$SRC/depts" ] +then + # legacy depts => move them to var + $COPY "$SRC/depts" "${SCODOC_VAR_DIR}/config/" +fi + +rm -rf "$SCODOC_DIR/static/photos" +if [ -e "$SRC/photos" ] +then + # legacy photos (in /static/photos) => move them to var + $COPY "$SRC/photos" "${SCODOC_VAR_DIR}/" +fi + +rm -rf "$SCODOC_DIR/logos" +$COPY "$SRC/logos" "$SCODOC_DIR/" + +mv "$SCODOC_DIR/config/scodoc_config.py" "$SCODOC_DIR/config/scodoc_config.py.$(date +%Y%m%d-%H%M%S)" +$COPY "$SRC/scodoc_config.py" "$SCODOC_DIR/config/" +# Verifie le codage de ce fichier: +if [ -z "$(file $SCODOC_DIR/config/scodoc_config.py | grep -i UTF-8)" ] +then + mv "$SCODOC_DIR/config/scodoc_config.py" "$SCODOC_DIR/config/scodoc_config.py.orig" + iconv -f iso8859-15 -t utf-8 "$SCODOC_DIR/config/scodoc_config.py.orig" > "$SCODOC_DIR/config/scodoc_config.py" +fi + +rm -rf "$INSTANCE_DIR/log" +$COPY "$SRC/log" "$INSTANCE_DIR/" + +# Fix file ownership and access rights +chown -R www-data.root "$INSTANCE_DIR/log" +chown -R www-data.root "$INSTANCE_DIR/var" +chmod 775 "$INSTANCE_DIR./log" "$INSTANCE_DIR./var" +chown -R www-data.root "$SCODOC_DIR" +chmod -R 775 "$SCODOC_DIR" + +# Remove tmp directory +if [ $IS_TMP = "1" ] +then + rm -rf $tmp +fi + +# Mise a jour BD ScoDoc +cd $SCODOC_DIR/config +./upgrade.sh + +# +echo +echo "Ok. Run \"/etc/init.d/scodoc start\" to start ScoDoc." + diff --git a/config/save_scodoc_data.sh b/config/save_scodoc_data.sh new file mode 100755 index 00000000..09cc0644 --- /dev/null +++ b/config/save_scodoc_data.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# +# ScoDoc: save all user data (database, configs, images, archives...) in separate directory +# +# Utile pour migrer ScoDoc d'un serveur a un autre +# Executer en tant que root sur le serveur d'origine +# +# E. Viennet, Sept 2011, Aug 2020 +# + +# Destination directory +if [ ! $# -eq 1 ] +then + echo "Usage: $0 destination_directory" + exit 1 +fi +DEST=$1 +# remove trailing slashs if needed: +shopt -s extglob +DEST="${DEST%%+(/)}" + +if [ ! -e "$DEST" ] +then + echo Creating directory "$DEST" + mkdir "$DEST" +else + echo "Error: Directory " "$DEST" " exists" + echo "remove it or specify another destination !" + exit 2 +fi + +INSTANCE_DIR=/opt/scodoc +SCODOC_DIR="$INSTANCE_DIR/Products/ScoDoc" + +source utils.sh +check_uid_root $0 + +echo "Stopping ScoDoc..." +/etc/init.d/scodoc stop + +# Dump all postgres databases +echo "Dumping SQL database..." +chown postgres "$DEST" +su -c "pg_dumpall > \"$DEST\"/scodoc.dump.txt" postgres +if [ ! $? -eq 0 ] +then + echo "Error dumping postgresql database\nPlease check that SQL server is running\nAborting." + exit 1 +fi +chown root "$DEST" + +# Zope DB and ScoDoc archives: +echo "Copying var/ ..." +cp -rp "$INSTANCE_DIR/var" "$DEST" + +# Depts db config (now in .../var) +shopt -s nullglob +if [ ! -z "$(echo ${SCODOC_DIR}/config/depts/*.cfg)" ] +then + echo "Copying legacy depts configs..." + cp -rp "$SCODOC_DIR/config/depts" "$DEST" +fi + + + +# Photos des etudiants (now in .../var) +if [ -e "$SCODOC_DIR/static/photos" ] +then + echo "Copying legacy photos..." + cp -rp "$SCODOC_DIR/static/photos" "$DEST" +fi + +echo "Copying logos..." +cp -rp "$SCODOC_DIR/logos" "$DEST" + +echo "Copying configuration file..." +cp -p "$SCODOC_DIR/config/scodoc_config.py" "$DEST" + +echo "Copying server logs..." +cp -rp "$INSTANCE_DIR/log" "$DEST" + + +# --- Archive all files in a tarball to ease transfer +echo +echo "Archiving backup files in a $DEST.tgz..." +base=$(basename "$DEST") +(cd "$DEST"/..; tar cfz "$DEST".tgz "$base") + +echo "Done (you can copy " "$DEST"".tgz to destination machine)." diff --git a/config/scodoc_config.py b/config/scodoc_config.py new file mode 100644 index 00000000..32d3fa21 --- /dev/null +++ b/config/scodoc_config.py @@ -0,0 +1,131 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +# +# Configuration globale de ScoDoc (version juin 2009) +# + +# La plupart des rĂ©glages sont stoquĂ©s en base de donnĂ©e et accessibles via le web +# (pages de paramètres ou prĂ©fĂ©rences). +# Les valeurs indiquĂ©es ici sont les valeurs initiales que prendront +# les paramètres lors de la crĂ©ation d'un nouveau dĂ©partement, +# elles ne sont plus utilisĂ©es ensuite. + +# Nota: il y a aussi des rĂ©glages dans sco_utils.py, mais ils nĂ©cessitent +# souvent de comprendre le code qui les utilise pour ne pas faire d'erreur: attention. + + +class CFG : + pass + +CONFIG = CFG() + +CONFIG.always_require_ine = 0 # set to 1 if you want to require INE + +# The base URL, use only if you are behind a proxy +# eg "https://scodoc.example.net/ScoDoc" +CONFIG.ABSOLUTE_URL = "" + +# +# ------------- Documents PDF ------------- +# +CONFIG.SCOLAR_FONT = 'Helvetica' +CONFIG.SCOLAR_FONT_SIZE = 10 +CONFIG.SCOLAR_FONT_SIZE_FOOT = 6 + +# Pour pieds de pages Procès verbaux: +# (markup leger reportlab supportĂ©, par ex. blah blah) +CONFIG.INSTITUTION_NAME="Institut Universitaire de Technologie - UniversitĂ© Paris 13" +CONFIG.INSTITUTION_ADDRESS="Web www.iutv.univ-paris13.fr - 99 avenue Jean-Baptiste ClĂ©ment - F 93430 Villetaneuse" + +CONFIG.INSTITUTION_CITY="Villetaneuse" + + +# Taille du l'image logo: largeur/hauteur (ne pas oublier le . !!!) +CONFIG.LOGO_FOOTER_ASPECT = 326/96. # W/H XXX provisoire: utilisera PIL pour connaitre la taille de l'image +CONFIG.LOGO_FOOTER_HEIGHT = 10 # taille dans le document en millimetres + +CONFIG.LOGO_HEADER_ASPECT = 549 / 346. # XXX logo IUTV +CONFIG.LOGO_HEADER_HEIGHT = 28 # taille verticale dans le document en millimetres + +# Pied de page PDF : un format Python, %(xxx)s est remplacĂ© par la variable xxx. +# Les variables dĂ©finies sont: +# day : Day of the month as a decimal number [01,31] +# month : Month as a decimal number [01,12]. +# year : Year without century as a decimal number [00,99]. +# Year : Year with century as a decimal number. +# hour : Hour (24-hour clock) as a decimal number [00,23]. +# minute: Minute as a decimal number [00,59]. +# +# server_url: URL du serveur ScoDoc +# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir VERSION.py) + +CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = "EditĂ© par %(scodoc_name)s le %(day)s/%(month)s/%(year)s Ă  %(hour)sh%(minute)s sur %(server_url)s" + + + +# +# ------------- Calcul bonus modules optionnels (sport, culture...) ------------- +# +from bonus_sport import * + +CONFIG.compute_bonus = bonus_iutv +# Mettre "bonus_demo" pour logguer des informations utiles au developpement... + +# +# ------------- Capitalisation des UEs ------------- +# Deux Ă©coles: +# - règle "DUT": capitalisation des UE obtenues avec moyenne UE >= 10 ET de toutes les UE +# des semestres validĂ©s (ADM, ADC, AJ). (conforme Ă  l'arrĂŞtĂ© d'aoĂ»t 2005) +# +# - règle "LMD": capitalisation uniquement des UE avec moy. > 10 + +CONFIG.CAPITALIZE_ALL_UES = True # si vrai, capitalise toutes les UE des semestres validĂ©s (règle "LMD"). + + +# +# ----------------------------------------------------- +# +# -------------- Personnalisation des pages +# +# ----------------------------------------------------- +# Nom (chemin complet) d'un fichier .html Ă  inclure juste après le +# le des pages ScoDoc +CONFIG.CUSTOM_HTML_HEADER = '' + +# Fichier html a inclure en fin des pages (juste avant le ) +CONFIG.CUSTOM_HTML_FOOTER = '' + +# Fichier .html Ă  inclure dans la pages connexion/dĂ©connexion (accueil) +# si on veut que ce soit diffĂ©rent (par dĂ©faut la mĂŞme chose) +CONFIG.CUSTOM_HTML_HEADER_CNX = CONFIG.CUSTOM_HTML_HEADER +CONFIG.CUSTOM_HTML_FOOTER_CNX = CONFIG.CUSTOM_HTML_FOOTER + + +# ----------------------------------------------------- +# +# -------------- Noms de LycĂ©es +# +# ----------------------------------------------------- + +# Fichier de correspondance codelycee -> noms +# (chemin relatif au repertoire d'install des sources) +CONFIG.ETABL_FILENAME = 'config/etablissements.csv' + + +# ---------------------------------------------------- +CONFIG.ALLOW_NULL_PRENOM = False # True for UCAC (Ă©tudiants camerounais sans prĂ©noms) + +CONFIG.ETUD_MAX_FILE_SIZE = 10*1024*1024 # taille max des fichiers archive etudiants (en octets) + +CONFIG.PUBLISH_PORTAL_PHOTO_URL = False # si pas de photo et portail, publie l'url (Ă©tait vrai jusqu'en oct 2016) + +CONFIG.MIN_PASSWORD_LENGTH = 0 # si > 0: longueur minimale requise des nouveaux mots de passe (le test cracklib.FascistCheck s'appliquera dans tous les cas) + +# ---------------------------------------------------- +# Ce dictionnaire est fusionnĂ© Ă  celui de sco_codes_parcours +# pour dĂ©finir les codes jury et explications associĂ©es +CONFIG.CODES_EXPL = { + # AJ : 'AjournĂ© (Ă©chec)', + + } diff --git a/config/scodocutils.py b/config/scodocutils.py new file mode 100644 index 00000000..98a5be95 --- /dev/null +++ b/config/scodocutils.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- + +""" + Some utilities used by upgrade scripts +""" + + +import sys, os, psycopg2, glob, subprocess, traceback, time + +sys.path.append("..") + + +def log(msg): + sys.stdout.flush() + sys.stderr.write(msg + "\n") + sys.stderr.flush() + + +SCODOC_DIR = os.environ.get("SCODOC_DIR", "") +if not SCODOC_DIR: + log("Error: environment variable SCODOC_DIR is not defined") + sys.exit(1) +SCODOC_VAR_DIR = os.environ.get("SCODOC_VAR_DIR", "") +if not SCODOC_VAR_DIR: + log("Error: environment variable SCODOC_VAR_DIR is not defined") + sys.exit(1) +SCODOC_LOGOS_DIR = os.environ.get("SCODOC_LOGOS_DIR", "") + +def get_dept_cnx_str(dept): + "db cnx string for dept" + f = os.path.join(SCODOC_VAR_DIR, "config", "depts", dept + ".cfg") + try: + return open(f).readline().strip() + except: + log("Error: can't read connexion string for dept %s" % dept) + log("(tried to open %s)" % f) + raise + + +def get_users_cnx_str(): + "db cnx string for users database (used only during upgrades to modify db schema)" + # uses default in sco_utils + # For customized installs, define the value here (used only during upgrades) + import sco_utils + + return sco_utils.SCO_DEFAULT_SQL_USERS_CNX + + +def get_depts(): + "list of defined depts" + files = glob.glob(SCODOC_VAR_DIR + "/config/depts/*.cfg") + return [os.path.splitext(os.path.split(f)[1])[0] for f in files] + + +def field_exists(cnx, table, field): + "true if field exists in sql table" + cursor = cnx.cursor() + cursor.execute( + "SELECT column_name FROM information_schema.columns WHERE table_name = '%s'" + % table + ) + r = cursor.fetchall() + fields = [f[0] for f in r] + return field in fields + + +def list_constraint(cnx, constraint_name=""): + "liste la contrainte (utile surtout pour savoir si elle existe)" + cursor = cnx.cursor() + cursor.execute( + "SELECT * FROM information_schema.table_constraints WHERE constraint_name = %(constraint_name)s", + {"constraint_name": constraint_name}, + ) + return cursor.fetchall() + + +def list_table_index(cnx, table): + "liste les index associĂ©s Ă  cette table" + cursor = cnx.cursor() + cursor.execute( + """SELECT t.relname as table_name, i.relname as index_name, a.attname as column_name + FROM + pg_class t, pg_class i, pg_index ix, pg_attribute a + WHERE + t.oid = ix.indrelid and i.oid = ix.indexrelid and a.attrelid = t.oid + and a.attnum = ANY(ix.indkey) and t.relkind = 'r' + and t.relname = %(table)s; + """, + {"table": table}, + ) + r = cursor.fetchall() + return [x[1] for x in r] # ne garde que le nom de l'index + + +def _run_sql(sql, cnx): + cursor = cnx.cursor() + error = False + try: + for cmd in sql: + log("executing SQL: %s" % cmd) + cursor.execute(cmd) + cnx.commit() + except: + cnx.rollback() + log("check_field: failure. Aborting transaction.") + error = True + traceback.print_exc() + return error + + +def check_field(cnx, table, field, sql_create_commands): + "if field does not exists in table, run sql commands" + if not field_exists(cnx, table, field): + log("missing field %s in table %s: trying to create it" % (field, table)) + error = _run_sql(sql_create_commands, cnx) + if not field_exists(cnx, table, field): + log("check_field: new field still missing !") + raise Exception("database configuration problem") + elif error: + log("\n\nAN UNEXPECTED ERROR OCCURRED WHILE UPGRADING DATABASE !\n\n") + else: + log("field %s added successfully." % field) + + +def table_exists(cnx, table): + "true if SQL table exists" + cursor = cnx.cursor() + cursor.execute( + "SELECT table_name FROM information_schema.tables where table_name='%s'" % table + ) + r = cursor.fetchall() + return len(r) > 0 + + +def check_table(cnx, table, sql_create_commands): + "if table does not exists in table, run sql commands" + if not table_exists(cnx, table): + log("missing table %s: trying to create it" % (table)) + error = _run_sql(sql_create_commands, cnx) + if not table_exists(cnx, table): + log("check_table: new table still missing !") + raise Exception("database configuration problem") + elif error: + log("\n\nAN UNEXPECTED ERROR OCCURRED WHILE UPGRADING DATABASE !\n\n") + else: + log("table %s added successfully." % table) + + +def sequence_exists(cnx, seq_name): + "true if SQL sequence exists" + cursor = cnx.cursor() + cursor.execute( + """SELECT relname FROM pg_class + WHERE relkind = 'S' and relname = '%s' + AND relnamespace IN ( + SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%%' AND nspname != 'information_schema' + ); + """ + % seq_name + ) + r = cursor.fetchall() + return len(r) > 0 + + +def function_exists(cnx, func_name): + "true if SQL function exists" + cursor = cnx.cursor() + cursor.execute( + """SELECT routine_name FROM information_schema.routines + WHERE specific_schema NOT IN ('pg_catalog', 'information_schema') + AND type_udt_name != 'trigger' + AND routine_name = '%s';""" + % func_name + ) + r = cursor.fetchall() + return len(r) > 0 diff --git a/config/set_passwd.sh b/config/set_passwd.sh new file mode 100755 index 00000000..2d024c3a --- /dev/null +++ b/config/set_passwd.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# +# ScoDoc: reglage du mot de passe admin Zope +# (in Zope terminology, an emergency user) +# +# Doit ętre lancé par l'utilisateur unix root dans le repertoire .../config +# ^^^^^^^^^^^^^^^^^^^^^ +# E. Viennet, Juin 2008, Jul 2019 +# + +source config.sh +source utils.sh + + +if [ "$UID" != "0" ] +then + echo "Erreur: le script $0 doit etre lance par root" + exit 1 +fi + +echo "Creation d'un utilisateur d'urgence pour ScoDoc" +echo "(utile en cas de perte de votre mot de passe admin)" + +if [ ${debian_version} != "10" ] +then + mdir=/opt/zope213/lib/python2.7/site-packages/Zope2-2.13.21-py2.7.egg/Zope2/utilities +else + mdir=/opt/zope213/lib/python2.7/site-packages/Zope2/utilities +fi + +python $mdir/zpasswd.py $SCODOC_DIR/../../access + +echo +echo "redemarrer scodoc pour prendre en compte le mot de passe" +echo diff --git a/config/softs/get-iana.sh b/config/softs/get-iana.sh new file mode 100755 index 00000000..f1a9f912 --- /dev/null +++ b/config/softs/get-iana.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +# $Id: get-iana.sh,v 1.15 2013/01/06 23:49:08 ktsaou Exp $ +# +# $Log: get-iana.sh,v $ +# Revision 1.15 2013/01/06 23:49:08 ktsaou +# Removed depedency to get-iana.sh +# It is not usefull any more. +# +# Revision 1.14 2010/06/07 15:44:09 ktsaou +# Made get-iana.sh support the latest IANA format. +# +# Revision 1.13 2010/04/08 22:03:08 ktsaou +# Removed --proxy=off for wget. +# +# Revision 1.12 2008/03/17 22:08:43 ktsaou +# Updated for latest IANA reservations format. +# +# Revision 1.11 2007/06/13 14:40:04 ktsaou +# *** empty log message *** +# +# Revision 1.10 2007/05/05 23:38:31 ktsaou +# Added support for external definitions of: +# +# RESERVED_IPS +# PRIVATE_IPS +# MULTICAST_IPS +# UNROUTABLE_IPS +# +# in files under the same name in /etc/firehol/. +# Only RESERVED_IPS is mandatory (firehol will complain if it is not there, +# but it will still work without it), and is also the only file that firehol +# checks how old is it. If it is 90+ days old, firehol will complain again. +# +# Changed the supplied get-iana.sh script to generate the RESERVED_IPS file. +# FireHOL also instructs the user to use this script if the file is missing +# or is too old. +# +# Revision 1.9 2007/04/29 19:34:11 ktsaou +# *** empty log message *** +# +# Revision 1.8 2005/06/02 15:48:52 ktsaou +# Allowed 127.0.0.1 to be in RESERVED_IPS +# +# Revision 1.7 2005/05/08 23:27:23 ktsaou +# Updated RESERVED_IPS to current IANA reservations. +# +# Revision 1.6 2004/01/10 18:44:39 ktsaou +# Further optimized and reduced PRIVATE_IPS using: +# http://www.vergenet.net/linux/aggregate/ +# +# The supplied get-iana.sh uses 'aggregate-flim' if it finds it in the path. +# (aggregate-flim is the name of this program when installed on Gentoo) +# +# Revision 1.5 2003/08/23 23:26:50 ktsaou +# Bug #793889: +# Change #!/bin/sh to #!/bin/bash to allow FireHOL run on systems that +# bash is not linked to /bin/sh. +# +# Revision 1.4 2002/10/27 12:44:42 ktsaou +# CVS test +# + +# +# Program that downloads the IPv4 address space allocation by IANA +# and creates a list with all reserved address spaces. +# + +# IPV4_ADDRESS_SPACE_URL="http://www.iana.org/assignments/ipv4-address-space" +IPV4_ADDRESS_SPACE_URL="http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt" + +# The program will match all rows in the file which start with a number, have a slash, +# followed by another number, for which the following pattern will also match on the +# same rows +IANA_RESERVED="(RESERVED|UNALLOCATED)" + +# which rows that are matched by the above, to ignore +# (i.e. not include them in RESERVED_IPS)? +#IANA_IGNORE="(Multicast|Private use|Loopback|Local Identification)" +IANA_IGNORE="Multicast" + +tempfile="/tmp/iana.$$.$RANDOM" + +AGGREGATE="`which aggregate-flim 2>/dev/null`" +if [ -z "${AGGREGATE}" ] +then + AGGREGATE="`which aggregate 2>/dev/null`" +fi + +if [ -z "${AGGREGATE}" ] +then + echo >&2 + echo >&2 + echo >&2 "WARNING" + echo >&2 "Please install 'aggregate-flim' to shrink the list of IPs." + echo >&2 + echo >&2 +fi + +echo >&2 +echo >&2 "Fetching IANA IPv4 Address Space, from:" +echo >&2 "${IPV4_ADDRESS_SPACE_URL}" +echo >&2 + +wget -O - "${IPV4_ADDRESS_SPACE_URL}" |\ + egrep "^ *[0-9]+/[0-9]+.*${IANA_RESERVED}" |\ + egrep -vi "${IANA_IGNORE}" |\ + sed "s/^ \+//g" |\ + cut -d ' ' -f 1 |\ +( + while IFS="/" read range net + do + # echo >&2 "$range/$net" + + if [ ! $net -eq 8 ] + then + echo >&2 "Cannot handle network masks of $net bits ($range/$net)" + continue + fi + + first=`echo $range | cut -d '-' -f 1` + first=`expr $first + 0` + last=`echo $range | cut -d '-' -f 2` + last=`expr $last + 0` + + x=$first + while [ ! $x -gt $last ] + do + # test $x -ne 127 && echo "$x.0.0.0/$net" + echo "$x.0.0.0/$net" + x=$[x + 1] + done + done +) | \ +( + if [ ! -z "${AGGREGATE}" -a -x "${AGGREGATE}" ] + then + "${AGGREGATE}" + else + cat + fi +) >"${tempfile}" + +echo >&2 +echo >&2 +echo >&2 "FOUND THE FOLLOWING RESERVED IP RANGES:" +printf "RESERVED_IPS=\"" +i=0 +for x in `cat ${tempfile}` +do + i=$[i + 1] + printf "${x} " +done +printf "\"\n" + +if [ $i -eq 0 ] +then + echo >&2 + echo >&2 + echo >&2 "Failed to find reserved IPs." + echo >&2 "Possibly the file format has been changed, or I cannot fetch the URL." + echo >&2 + + rm -f ${tempfile} + exit 1 +fi +echo >&2 +echo >&2 +echo >&2 "Differences between the fetched list and the list installed in" +echo >&2 "/etc/firehol/RESERVED_IPS:" + +echo >&2 "# diff /etc/firehol/RESERVED_IPS ${tempfile}" +diff /etc/firehol/RESERVED_IPS ${tempfile} + +if [ $? -eq 0 ] +then + touch /etc/firehol/RESERVED_IPS + echo >&2 + echo >&2 "No differences found." + echo >&2 + + rm -f ${tempfile} + exit 0 +fi + +echo >&2 +echo >&2 +echo >&2 "Would you like to save this list to /etc/firehol/RESERVED_IPS" +echo >&2 "so that FireHOL will automatically use it from now on?" +echo >&2 +while [ 1 = 1 ] +do + printf >&2 "yes or no > " + read x + + case "${x}" in + yes) cp -f /etc/firehol/RESERVED_IPS /etc/firehol/RESERVED_IPS.old 2>/dev/null + cat "${tempfile}" >/etc/firehol/RESERVED_IPS || exit 1 + echo >&2 "New RESERVED_IPS written to '/etc/firehol/RESERVED_IPS'." + break + ;; + + no) + echo >&2 "Saved nothing." + break + ;; + + *) echo >&2 "Cannot understand '${x}'." + ;; + esac +done + +rm -f ${tempfile} + diff --git a/config/softs/pyExcelerator-0.6.3a.patched.tgz b/config/softs/pyExcelerator-0.6.3a.patched.tgz new file mode 100644 index 0000000000000000000000000000000000000000..32c6bf31928eb96494f1867db5e881ead16320e5 GIT binary patch literal 222066 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

      ha9hl~`L2+@zS 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 A1^@s6 literal 0 HcmV?d00001 diff --git a/config/softs/pydot-0.9.10.tar.gz b/config/softs/pydot-0.9.10.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..b923b58c6190c4a5ce1d69d5398d67cfef2e33fc GIT binary patch literal 14462 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 "1.7" ]] +then + (cd "$SCODOC_DIR"; find . -name .svn -type d -exec dirname {} \; | xargs svn upgrade) +fi + +echo "Stopping ScoDoc..." +/etc/init.d/scodoc stop + +echo +echo "Using SVN to update $SCODOC_DIR..." +(cd "$SCODOC_DIR"; svn update) + +SVNVERSION=$(cd ..; svnversion) + +if [ ! -e "${SCODOC_VERSION_DIR}" ]; then + mkdir -p "${SCODOC_VERSION_DIR}" + chown www-data.www-data "${SCODOC_VERSION_DIR}" +fi +if [ ! -e "${SCODOC_VERSION_DIR}"/scodoc.sn ]; then + if [ -e "$SCODOC_DIR"/config/scodoc.sn ]; then + # migrate from old scodoc installs + mv "$SCODOC_DIR"/config/scodoc.sn "${SCODOC_VERSION_DIR}" + fi +fi + +SN=$(cat "$SCODOC_DIR"/config/scodoc.sn 2> /dev/null || cat "${SCODOC_VERSION_DIR}"/scodoc.sn) +if [ -e "$SCODOC_DIR"/config/scodoc.sn ] || [ -e "${SCODOC_VERSION_DIR}"/scodoc.sn ] +then + if [[ ! "${SN}" =~ ^[0-9].* ]] + then + SN='' # fix for invalid previous replies + fi + mode=upgrade +else + mode=install +fi + +CMD="curl --fail --connect-timeout 5 --silent http://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=$mode\&svn=${SVNVERSION}\&sn=${SN}" +#echo $CMD +SVERSION="$(${CMD})" + +if [ $? == 0 ]; then + #echo "answer=${SVERSION}" + echo "${SVERSION}" > "${SCODOC_VERSION_DIR}"/scodoc.sn +else + echo 'Warning: cannot connect to scodoc release server' +fi + + +# Check that no Zope "access" file has been forgotten in the way: +if [ -e $SCODOC_DIR/../../access ] +then + mv $SCODOC_DIR/../../access $SCODOC_DIR/../../access.bak +fi + +# Fix some permissions which may have been altered in the way: +chsh -s /bin/sh $POSTGRES_USER # www-data, nologin in Debian 9 +chown root.www-data "$SCODOC_DIR" # important to create .pyc +chmod 775 "${SCODOC_DIR}" +chmod a+r "$SCODOC_DIR"/*.py + +chown -R root.www-data "$SCODOC_DIR"/config +chmod 775 "$SCODOC_DIR"/config +chmod a+rx "$SCODOC_DIR"/config/postupgrade-db.py +chmod a+r "$SCODOC_DIR"/config/scodocutils.py + +chown -R root.www-data "$SCODOC_DIR"/misc +chmod -R a+r "$SCODOC_DIR"/misc +# depts dir: +if [ ! -e "${SCODOC_VAR_DIR}/config/depts" ] +then + mkdir "${SCODOC_VAR_DIR}/config/depts" +fi +# ScoDoc must be able to write to var directory: +chown -R www-data.www-data "${SCODOC_VAR_DIR}" +chmod -R u+w "${SCODOC_VAR_DIR}" + +# Important to create .pyc: +chgrp -R www-data "${SCODOC_DIR}"/ZopeProducts +chmod -R g+w "${SCODOC_DIR}"/ZopeProducts + +# Se recharge car ce fichier peut avoir change durant le svn up ! +if [ -z "$SCODOC_UPGRADE_RUNNING" ] +then + export SCODOC_UPGRADE_RUNNING=1 + ./upgrade.sh + exit 0 +fi + +# Add some python modules: +"$SCODOC_DIR"/config/install_cal_modules.sh +/opt/zope213/bin/python -c "import requests" >& /dev/null +if [ $? -ne 0 ] +then + /opt/zope213/bin/pip install requests +fi + +# Ensure www-data can duplicate databases (for dumps) +su -c $'psql -c \'alter role "www-data" with CREATEDB;\'' "$POSTGRES_SUPERUSER" +#' + +# post-upgrade scripts +echo "Executing post-upgrade script..." +"$SCODOC_DIR"/config/postupgrade.py + +echo "Executing post-upgrade database script..." +su -c "$SCODOC_DIR/config/postupgrade-db.py" $POSTGRES_USER + +# +echo +echo "Starting ScoDoc..." +/etc/init.d/scodoc start + + diff --git a/config/utils.sh b/config/utils.sh new file mode 100644 index 00000000..8637b736 --- /dev/null +++ b/config/utils.sh @@ -0,0 +1,57 @@ + +# Misc utilities for ScoDoc install shell scripts + +to_lower() { + echo $1 | tr "[:upper:]" "[:lower:]" +} + +to_upper() { + echo $1 | tr "[:lower:]" "[:upper:]" +} + +norm_ans() { + x=$(to_upper $1 | tr O Y) + echo ${x:0:1} +} + +check_uid_root() { + if [ "$UID" != "0" ] + then + echo "Erreur: le script $1 doit etre lance par root" + exit 1 + fi +} + +terminate() { + echo + echo "Erreur: $1" + echo + exit 1 +} + +# --- Ensure postgres user www-data exists +init_postgres_user() { # run as root + if [ -z $(echo "select usename from pg_user;" | su -c "$PSQL -d template1 -p $POSTGRES_PORT" $POSTGRES_SUPERUSER | grep $POSTGRES_USER) ] + then + # add database user + echo "Creating postgresql user $POSTGRES_USER" + su -c "createuser -p $POSTGRES_PORT --no-superuser --no-createdb --no-adduser --no-createrole ${POSTGRES_USER}" $POSTGRES_SUPERUSER + fi +} + + +# XXX inutilise +gen_passwd() { + PASSWORD_LENGTH="8" + ALLOWABLE_ASCII="~@#$%^&*()_+=-?><0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + SEED=$(head -c4 /dev/urandom | od -t u4 | awk '{ print $2 }') + RANDOM=$SEED + n=1 + password="" + while [ "$n" -le "$PASSWORD_LENGTH" ] + do + password="$password${ALLOWABLE_ASCII:$(($RANDOM%${#ALLOWABLE_ASCII})):1}" + n=$((n+1)) + done + echo $password +} diff --git a/csv2rules.py b/csv2rules.py new file mode 100755 index 00000000..c99c9343 --- /dev/null +++ b/csv2rules.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +"""Converti fichier CSV decrivant regles parcours DUT +en "regles" python. + +CSV: utf-8/Euro, tab, no text delimiter +(le faire avec OpenOffice ou NeoOffice...) + +""" +import sys, pdb + +sourcefile = sys.argv[1] # fichier CSV + +ALL = "ALL" + +HEAD = """# -*- coding: utf-8 -*- +# +# Generated by csv2rules.py *** DO NOT EDIT *** +# +# Command: %s %s +# +from sco_codes_parcours import * + +rules_source_file='%s' + +""" % ( + sys.argv[0], + sys.argv[1], + sourcefile, +) + +from sco_codes_parcours import * + + +def _fmt(s): + if not s: + return None + if strlower(s) in ("ok", "oui", "o", "y", "yes"): + return True + if strlower(s) in ("no", "non"): + return False + if s == "*": + return ALL + return s + + +colidx_code_sem = 7 # index colonne 'code_sem' dans feuille CSV +iue = colidx_code_sem + 1 +icodeprev = 1 # idex col "Prev" + + +def genrules(csv): + r = [] + linenum = 1 + try: + for line in csv: + line = line.strip() + if line: + if line[0] == "#": + r.append(line) + else: + fs = [_fmt(s) for s in line.split("\t")] + if not "," in fs[icodeprev]: + fs[icodeprev] = fs[icodeprev] + "," # liste codes prev + if fs[iue] and not "," in fs[iue]: + fs[iue] = fs[iue] + "," # liste codes UE + if fs[iue]: + fs[iue] = "(" + fs[iue] + ")" + else: + fs[iue] = "()" + fs[-1] = "'" + fs[-1].replace("'", "\\'") + "'" + r.append( + "( '%s', ((%s), %s, %s, %s, %s, %s)," + % tuple(fs[:colidx_code_sem]) + ) + r.append( + " (%s, %s, %s, %s, %s, %s) )," % tuple(fs[colidx_code_sem:]) + ) + linenum += 1 + except: + sys.stderr.write("error line %d on\n%s\n" % (linenum, csv[linenum])) + raise + return ( + HEAD + + "DUTRules = [ DUTRule(rid, p, c) for (rid, p,c) in (\n" + + "\n".join(r) + + "\n)]" + ) + + +print(genrules(open(sourcefile).readlines())) diff --git a/debug.py b/debug.py new file mode 100644 index 00000000..b8c0d763 --- /dev/null +++ b/debug.py @@ -0,0 +1,116 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +"""Configuration pour debugguer en mode console + +Lancer ScoDoc ainsi: (comme root) + + /opt/scodoc/bin/zopectl debug + +Puis + +from debug import * +context = go(app) +# ou +context = go_dept(app, 'CJ') + +authuser = app.acl_users.getUserById('admin') +# ou app.ScoDoc.acl_users.getUserById('toto') pour un utilisateur scodoc +authuser = authuser.__of__(app.acl_users) + +Exemple: +sems = context.Notes.formsemestre_list() +formsemestre_id = sems[0]['formsemestre_id'] + +# Affiche tous les semestres: +for sem in sems: + print sem['formsemestre_id'], sem['titre_num'] + +# Recupere la table de notes: +nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_id) + + +""" +from notesdb import * +from notes_log import log +from sco_utils import * + +from gen_tables import GenTable +import sco_archives +import sco_groups +import sco_evaluations +import sco_formsemestre +import sco_formsemestre_edit +import sco_compute_moy +import sco_parcours_dut +import sco_codes_parcours +import sco_bulletins +import sco_excel +import sco_formsemestre_status +import sco_bulletins_xml + +# Prend le premier departement comme context + + +def go(app, n=0): + context = app.ScoDoc.objectValues("Folder")[n].Scolarite + print("context in dept ", context.DeptId()) + return context + + +def go_dept(app, dept): + objs = app.ScoDoc.objectValues("Folder") + for o in objs: + context = o.Scolarite + if context.DeptId() == dept: + print("context in dept ", context.DeptId()) + return context + raise ValueError("dep %s not found" % dept) + + +class FakeUser: + def __init__(self, name): + self.name = name + + def __str__(self): + return self.name + + def has_permission(self, op, context): + return True + + +class DummyResponse: + """Emulation vide de Reponse http Zope""" + + def __init__(self): + self.header = {} + self.redirected = "" + + def setHeader(self, name, value): + self.header[name] = value + + def redirect(self, url): + self.redirected = url + + +class DummyRequest: + """Emulation vide de Request Zope""" + + def __init__(self): + self.RESPONSE = DummyResponse() + self.AUTHENTICATED_USER = FakeUser("admin") + self.form = {} + self.URL = "http://scodoc/" + self.URL1 = self.URL + self.URL0 = self.URL + self.BASE0 = "localhost" + self.REMOTE_ADDR = "127.0.0.1" + self.HTTP_REFERER = "" + self.REQUEST_METHOD = "get" + self.QUERY_STRING = "" + + +REQUEST = DummyRequest() + +# handful shorcut +pp = pprint.pprint diff --git a/dtml/docLogin.dtml b/dtml/docLogin.dtml new file mode 100644 index 00000000..fd46ec9f --- /dev/null +++ b/dtml/docLogin.dtml @@ -0,0 +1,49 @@ + +

      + + + + + + +

      + +

      + + + + + Added by Emmanuel for ScoDoc + + + + + + + + + + + + + + + +
      + Nom + + +
      + Mot de passe + + +
      +
      +
      +Ok "> +
      + +
      + +
      + diff --git a/dtml/docLogout.dtml b/dtml/docLogout.dtml new file mode 100644 index 00000000..5210fc74 --- /dev/null +++ b/dtml/docLogout.dtml @@ -0,0 +1,16 @@ + +

      +

      +

      Vous êtes déconnecté de ScoDoc. +

      + +

      ">revenir Ă  l'accueil

      + +
      +

      (Attention: si vous êtes administrateur, vous ne pouvez vous déconnecter complètement qu'en relançant votre navigateur) +

      +
      + + + + diff --git a/dtml/entreprises/entreprise_contact_create.dtml b/dtml/entreprises/entreprise_contact_create.dtml new file mode 100644 index 00000000..e0c3ab05 --- /dev/null +++ b/dtml/entreprises/entreprise_contact_create.dtml @@ -0,0 +1,62 @@ + + + + + + + + + + + +

      Nouveau "contact" avec l'entreprise

      + + + + + + + + +

      +
      + + + + + + + +
      + + \ No newline at end of file diff --git a/dtml/entreprises/entreprise_contact_delete.dtml b/dtml/entreprises/entreprise_contact_delete.dtml new file mode 100644 index 00000000..df73659b --- /dev/null +++ b/dtml/entreprises/entreprise_contact_delete.dtml @@ -0,0 +1,27 @@ + + + + +

      Suppression du contact

      + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dtml/entreprises/entreprise_contact_edit.dtml b/dtml/entreprises/entreprise_contact_edit.dtml new file mode 100644 index 00000000..5fe437f1 --- /dev/null +++ b/dtml/entreprises/entreprise_contact_edit.dtml @@ -0,0 +1,62 @@ + + + + + + + +

      + + +

      Contact avec entreprise

      + + + + + + + +

      +
      + + + +

      Supprimer ce contact

      +
      + + + + + + +
      + + + \ No newline at end of file diff --git a/dtml/entreprises/entreprise_contact_list.dtml b/dtml/entreprises/entreprise_contact_list.dtml new file mode 100644 index 00000000..cffeb351 --- /dev/null +++ b/dtml/entreprises/entreprise_contact_list.dtml @@ -0,0 +1,54 @@ + + + + + + +

      Listes des contacts avec l'entreprise

      + + +

      Listes des contacts

      + +
      + + + + + + + + + + + + + + + + + + + + + + + +
      DateObjetEntrepriseEtudiantDescription
      "> + + + + + "> + + +
      Aucun contact !
      + + + +

      +">nouveau "contact" +

      +
      +
      + + \ No newline at end of file diff --git a/dtml/entreprises/entreprise_correspondant_create.dtml b/dtml/entreprises/entreprise_correspondant_create.dtml new file mode 100644 index 00000000..2ffdbba4 --- /dev/null +++ b/dtml/entreprises/entreprise_correspondant_create.dtml @@ -0,0 +1,43 @@ + + + + + +

      dans l'entreprise

      + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dtml/entreprises/entreprise_correspondant_delete.dtml b/dtml/entreprises/entreprise_correspondant_delete.dtml new file mode 100644 index 00000000..ac50550e --- /dev/null +++ b/dtml/entreprises/entreprise_correspondant_delete.dtml @@ -0,0 +1,27 @@ + + + + +

      Suppression du correspondant

      + + + + + + + + + + + + + + + + diff --git a/dtml/entreprises/entreprise_correspondant_edit.dtml b/dtml/entreprises/entreprise_correspondant_edit.dtml new file mode 100644 index 00000000..2b254568 --- /dev/null +++ b/dtml/entreprises/entreprise_correspondant_edit.dtml @@ -0,0 +1,41 @@ + + + + +

      + + + + + + + + + + + + + + + diff --git a/dtml/entreprises/entreprise_correspondant_list.dtml b/dtml/entreprises/entreprise_correspondant_list.dtml new file mode 100644 index 00000000..085d4375 --- /dev/null +++ b/dtml/entreprises/entreprise_correspondant_list.dtml @@ -0,0 +1,52 @@ + + + + +

      Listes des correspondants dans l'entreprise

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NomEntrepriseFonctionTéléphoneMailNote
      "> + + + + + + / + / + + "> +
      ">
      +
      ">modifier ">supprimer
      Aucun correspondant dans cette entreprise !
      + +

      + + + diff --git a/dtml/entreprises/entreprise_delete.dtml b/dtml/entreprises/entreprise_delete.dtml new file mode 100644 index 00000000..7f7db5f8 --- /dev/null +++ b/dtml/entreprises/entreprise_delete.dtml @@ -0,0 +1,60 @@ + + + + + +

      Suppression de l'entreprise

      + +

      Attention: supression définitive de l'entreprise, de ses correspondants et contacts. +

      + + + + +

      Correspondants dans l'entreprise (seront supprimés):

      +
        + + +
      • + + () +
      • +
        +
        +
      +
      + + + +

      Contacts avec l'entreprise (seront supprimés):

      +
        + + +
      • + () +
      • +
        +
        +
      +
      + + + + + + + + + + + + + + + diff --git a/dtml/entreprises/index_html.dtml b/dtml/entreprises/index_html.dtml new file mode 100644 index 00000000..1e2fbc25 --- /dev/null +++ b/dtml/entreprises/index_html.dtml @@ -0,0 +1,139 @@ + + + + + + + + + + +

      + + +

      +Attention: version test préliminaire. Signaler les problèmes à Emmanuel +

      +
      + + + + + + + + + + + + + + +

      valeur invalide pour 'sort_type' !

      +
      + + + + + + + + + + +
      + + + + + + + + +
      nomvilleĂ©tudiant     Tri par:
      +
      + + + + + +
      + + + +
      EntreprisesRĂ©sultats - sur +
      + + + + + + + + + + + + + + + + + + + + + + +
      &start=">
       
      + + +

      Aucune entreprise !

      + + + +

      +

      + "> + + + + + + page prĂ©cĂ©dente     + + + + + + page suivante + + + +   RĂ©sultats par page : + +
      + +

      + diff --git a/dtml/manage_addZNotesForm.dtml b/dtml/manage_addZNotesForm.dtml new file mode 100644 index 00000000..e58b49e0 --- /dev/null +++ b/dtml/manage_addZNotesForm.dtml @@ -0,0 +1,49 @@ + + + + +
      +

      +Notes Objects are very usefull thus not documented yet... +

      +
      + +

      + + + + + + + + + + + + +
      + +
      + + + diff --git a/dtml/manage_addZScolarForm.dtml b/dtml/manage_addZScolarForm.dtml new file mode 100644 index 00000000..580a83e1 --- /dev/null +++ b/dtml/manage_addZScolarForm.dtml @@ -0,0 +1,62 @@ + + + + +
      +

      +ZScolar: gestion scolarite d'un departement +

      +
      + +

      + +
      +
      + Id +
      +
      + +
      +
      + Title +
      +
      + +
      + + + + + + + + + + + + + + + + + +
      + +
      + + + diff --git a/dtml/manage_editZNotesForm.dtml b/dtml/manage_editZNotesForm.dtml new file mode 100644 index 00000000..456b631f --- /dev/null +++ b/dtml/manage_editZNotesForm.dtml @@ -0,0 +1,21 @@ + + + + + + +

      + id:
      + title:
      +
      + +
      + + + + + diff --git a/dtml/manage_editZScolarForm.dtml b/dtml/manage_editZScolarForm.dtml new file mode 100644 index 00000000..aab8859e --- /dev/null +++ b/dtml/manage_editZScolarForm.dtml @@ -0,0 +1,21 @@ + + + + + + +

      + id:
      + title:
      +
      + +
      + + + + + diff --git a/dutrules.py b/dutrules.py new file mode 100644 index 00000000..e0c37115 --- /dev/null +++ b/dutrules.py @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- +# +# Generated by csv2rules.py *** DO NOT EDIT *** +# +# Command: ./csv2rules.py misc/parcoursDUT.csv +# +from sco_codes_parcours import * + +rules_source_file = "misc/parcoursDUT.csv" + +DUTRules = [ + DUTRule(rid, p, c) + for (rid, p, c) in ( + # Id Prev. Assiduité Moy Gen Barres UE Comp prev/cur Suivant Code SEM Codes UE Code prev. (si modifié) Devenir Action Explication + # Semestre prec. validé: + ( + "10", + ((None, ADM, ADC, ADJ), True, True, True, ALL, ALL), + (ADM, (ADM,), None, NEXT, None, "Passage normal"), + ), + ( + "20", + ((None, ADM, ADC, ADJ), True, False, True, ALL, True), + (ATT, (ADM,), None, NEXT, None, "Pas moy: attente suivant pour compenser"), + ), + ( + "30", + ((None, ADM, ADC, ADJ), True, ALL, False, ALL, ALL), + (ATB, (ADM, AJ), None, NEXT, None, "Pas barre UE"), + ), + ( + "40", + ((None, ADM, ADC, ADJ), False, ALL, ALL, ALL, True), + ( + ATJ, + (AJ,), + None, + NEXT, + None, + "Pb assiduité, passe sans valider pour l'instant", + ), + ), + ( + "50", + ((ADM, ADJ, ADC), True, False, ALL, True, ALL), + (ADC, (ADM, CMP), None, NEXT, None, "Compense avec semestre précédent"), + ), + # Semestre prec. ATJ (pb assiduité): + ( + "60", + ((ATJ,), False, ALL, ALL, ALL, ALL), + (NAR, (AJ,), AJ, REO, None, "Pb assiduité persistant: réorientation"), + ), + ( + "70", + ((ATJ,), False, ALL, ALL, ALL, ALL), + ( + AJ, + (AJ,), + AJ, + REDOANNEE, + None, + "Pb assiduité persistant: redoublement année", + ), + ), + ( + "80", + ((ALL,), False, ALL, ALL, ALL, ALL), + (AJ, (), ADM, REO, None, "Pb assiduité, étudiant en échec."), + ), + # Semestre prec. ATT (pb moy gen): + ( + "90", + ((ATT,), True, True, True, True, ALL), + (ADM, (ADM,), ADC, NEXT, None, "Passage, et compense précédent"), + ), + ( + "100", + ((ATT,), True, True, True, ALL, ALL), + (ADM, (ADJ,), ADJ, NEXT, None, "Passage, le jury valide le précédent"), + ), + ( + "110", + ((ATT,), False, True, True, ALL, True), + ( + ATJ, + (AJ,), + ADJ, + NEXT, + None, + "Passage, le jury valide le précédent, pb assiduité", + ), + ), + ( + "120", + ((ATT,), True, False, ALL, ALL, ALL), + (AJ, (AJ,), AJ, REDOANNEE, None, "Redoublement année"), + ), + ( + "130", + ((ATT,), ALL, True, True, False, ALL), + ( + AJ, + (AJ,), + AJ, + REDOANNEE, + None, + "Pas de compensation ni validation du précédent", + ), + ), + ( + "140", + ((ATT,), True, False, True, ALL, ALL), + ( + ATT, + (), + ADJ, + NEXT, + None, + "Pas moy, le jury valide le précédent, semestre en attente pour compenser", + ), + ), + # Semestre prec. ATB (pb barre UE): + ( + "200", + ((ATB,), ALL, ALL, ALL, ALL, ALL), + ( + AJ, + (ADM, AJ), + AJ, + REDOANNEE, + None, + "Le précédent ne peut pas être validé, redoublement année", + ), + ), + ( + "210", + ((ATB,), ALL, ALL, ALL, ALL, ALL), + ( + NAR, + (ADM, AJ), + NAR, + REO, + None, + "Le précédent ne peut pas être validé, réorientation", + ), + ), + ( + "220", + ((ATB,), True, True, True, ALL, ALL), + (ADM, (ADM,), ADJ, NEXT, None, "Le jury valide le précédent"), + ), + ( + "230", + ((ATB,), True, False, True, ALL, True), + ( + ATT, + (ADM, AJ), + ADJ, + NEXT, + None, + "Le jury valide le précédent, pas moyenne gen., attente suivant", + ), + ), + ( + "240", + ((ATB,), True, ALL, False, ALL, True), + ( + ATB, + (ADM, AJ), + ADJ, + NEXT, + None, + "Le jury valide le précédent, pb barre UE, attente", + ), + ), + ( + "250", + ((ATB,), False, ALL, ALL, ALL, True), + ( + ATJ, + (AJ,), + ADJ, + NEXT, + None, + "Le jury valide le précédent, mais probleme assiduité.", + ), + ), + ( + "260", + ((ATB, ATT), ALL, True, True, ALL, ALL), + ( + ADJ, + (), + AJ, + REDOANNEE, + None, + "Le jury valide ce semestre, et fait recommencer le précédent.", + ), + ), + # Semestre prec. AJ (ajourné): + ( + "300", + ((AJ,), True, False, ALL, ALL, ALL), + (AJ, (), AJ, REDOANNEE, None, "Echec de 2 semestres, redouble année"), + ), + ( + "310", + ((AJ,), True, True, False, ALL, ALL), + (AJ, (), AJ, REDOANNEE, None, "Echec de 2 semestres, redouble année"), + ), + ( + "320", + ((AJ,), False, ALL, ALL, ALL, ALL), + (NAR, (), None, REO, None, "Echec, pas assidu: réorientation"), + ), + ( + "330", + ((AJ,), True, True, True, ALL, ALL), + ( + ATT, + (), + None, + REDOANNEE, + None, + "Ne valide pas car mais manque le précédent: redouble (modif 2017)", + ), + ), + # Décisions du jury: + ( + "400", + ((ALL,), True, False, ALL, ALL, ALL), + (ADJ, (ADM, CMP), None, NEXT, None, "Le jury décide de valider"), + ), + ( + "410", + ((ATT, ATB), True, False, ALL, ALL, ALL), + ( + ADJ, + (ADM, CMP), + ADJ, + NEXT, + None, + "Le jury décide de valider ce semestre et le précédent", + ), + ), + ( + "420", + ((ALL,), True, True, False, ALL, ALL), + (ADJ, (ADM, CMP), None, NEXT, None, "Le jury décide de valider"), + ), + ( + "430", + ((ATT, ATB), True, True, False, ALL, ALL), + ( + ADJ, + (ADM, CMP), + ADJ, + NEXT, + None, + "Le jury décide de valider ce semestre et le précédent", + ), + ), + ( + "450", + ((ATT, ATB), False, False, True, ALL, True), + ( + ATT, + (ADM, AJ), + ADJ, + NEXT, + None, + "Pb moy: attente, mais le jury valide le précédent", + ), + ), + # Semestres “décales” (REDOSEM) + ( + "500", + ((None, ADM, ADC, ADJ, ATT, ATB), True, False, ALL, False, ALL), + (AJ, (), None, REDOSEM, None, "Pas moy: redouble ce semestre"), + ), + ( + "510", + ((None, ADM, ADC, ADJ, ATT, ATB), True, True, False, False, ALL), + (AJ, (), None, REDOSEM, None, "Pas barre UE: redouble ce semestre"), + ), + ( + "520", + ((None, ADM, ADC, ADJ, ATB, ATT), False, ALL, ALL, ALL, ALL), + (AJ, (), None, REDOSEM, None, "Pb assiduité: redouble ce semestre"), + ), + # Nouvelles regles avec plusieurs devenirs en semestres decales: + ( + "550", + ((ATT, ATB), ALL, False, ALL, False, ALL), + ( + AJ, + (), + None, + RA_OR_RS, + None, + "Deux semestres ratés, choix de recommencer le premier ou le second", + ), + ), + ( + "560", + ((ATT, ATB), ALL, True, False, False, ALL), + ( + AJ, + (), + None, + RA_OR_RS, + None, + "Deux semestres ratés, choix de recommencer le premier ou le second", + ), + ), + ( + "570", + ((None, ADM, ADJ, ADC), ALL, False, True, False, ALL), + ( + ATT, + (), + None, + RS_OR_NEXT, + None, + "Semestre raté, choix de redoubler le semestre ou de continuer pour éventuellement compenser.", + ), + ), + ( + "580", + ((None, ADM, ADJ, ADC), ALL, ALL, False, False, ALL), + ( + ATB, + (), + None, + RS_OR_NEXT, + None, + "Semestre raté, choix de redoubler ou de s'en remettre au jury du semestre suivant.", + ), + ), + # Exclusion (art. 22): si precedent non valide et pas les barres dans le courant, on peut ne pas autoriser a redoubler: + # (le cas ATB est couvert plus haut) + ( + "600", + ((AJ, ATT, NAR), True, False, ALL, ALL, ALL), + (NAR, (), NAR, REO, None, "Non autorisé à redoubler"), + ), + ( + "610", + ((AJ, ATT, NAR), True, True, False, ALL, ALL), + (NAR, (), NAR, REO, None, "Non autorisé à redoubler"), + ), + ) +] diff --git a/gen_tables.py b/gen_tables.py new file mode 100644 index 00000000..0e14585f --- /dev/null +++ b/gen_tables.py @@ -0,0 +1,706 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +"""Géneration de tables aux formats XHTML, PDF, Excel, XML et JSON. + +Les données sont fournies comme une liste de dictionnaires, chaque élément de +cette liste décrivant une ligne du tableau. + +Chaque colonne est identifiée par une clé du dictionnaire. + +Voir exemple en fin de ce fichier. + +Les clés commençant par '_' sont réservées. Certaines altèrent le traitement, notamment +pour spécifier les styles de mise en forme. +Par exemple, la clé '_css_row_class' spécifie le style CSS de la ligne. + +""" + +import random +from collections import OrderedDict + +# XML generation package (apt-get install jaxml) +import jaxml + +import sco_excel +from sco_pdf import * + + +def mark_paras(L, tags): + """Put each (string) element of L between + """ + for tag in tags: + b = "<" + tag + ">" + c = "" + L = [b + (x or "") + c for x in L] + return L + + +class DEFAULT_TABLE_PREFERENCES: + values = { + "SCOLAR_FONT": "Helvetica", # used for PDF, overriden by preferences argument + "SCOLAR_FONT_SIZE": 10, + "SCOLAR_FONT_SIZE_FOOT": 6, + } + + def __getitem__(self, k): + return self.values[k] + + +class GenTable: + """Simple 2D tables with export to HTML, PDF, Excel, CSV. + Can be sub-classed to generate fancy formats. + """ + + default_css_class = "gt_table stripe cell-border compact hover order-column" + + def __init__( + self, + rows=[{}], # liste de dict { column_id : value } + columns_ids=[], # id des colonnes a afficher, dans l'ordre + titles={}, # titres (1ere ligne) + bottom_titles={}, # titres derniere ligne (optionnel) + caption=None, + page_title="", # titre fenetre html + pdf_link=True, + xls_link=True, + xml_link=False, + table_id=None, # for html and xml + html_class=None, # class de l'element
      +
      + Id +
      +
      + +
      +
      + Title +
      +
      + +
      +
      + DB connexion string +
      +
      + +
      (en plus des classes par defaut, + html_class_ignore_default=False, # sauf si html_class_ignore_default est vrai) + html_sortable=False, + html_highlight_n=2, # une ligne sur 2 de classe "gt_hl" + html_col_width=None, # force largeur colonne + html_generate_cells=True, # generate empty ]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"
      cells even if not in rows (useless?) + html_title="", # avant le tableau en html + html_caption=None, # override caption if specified + html_header=None, + html_next_section="", # html fragment to put after the table + html_with_td_classes=False, # put class=column_id in each + html_before_table="", # html snippet to put before the in the page + html_empty_element="", # replace table when empty + base_url=None, + origin=None, # string added to excel and xml versions + filename="table", # filename, without extension + xls_sheet_name="feuille", + xls_before_table=[], # liste de cellules a placer avant la table + pdf_title="", # au dessus du tableau en pdf + pdf_table_style=None, + pdf_col_widths=None, + xml_outer_tag="table", + xml_row_tag="row", + text_fields_separator="\t", + preferences=None, + ): + self.rows = rows # [ { col_id : value } ] + self.columns_ids = columns_ids # ordered list of col_id + self.titles = titles # { col_id : title } + self.bottom_titles = bottom_titles + self.origin = origin + self.base_url = base_url + self.filename = filename + self.caption = caption + self.html_header = html_header + self.html_before_table = html_before_table + self.html_empty_element = html_empty_element + self.page_title = page_title + self.pdf_link = pdf_link + self.xls_link = xls_link + self.xml_link = xml_link + # HTML parameters: + if not table_id: # random id + self.table_id = "gt_" + str(random.randint(0, 1000000)) + else: + self.table_id = table_id + self.html_generate_cells = html_generate_cells + self.html_title = html_title + self.html_caption = html_caption + self.html_next_section = html_next_section + self.html_with_td_classes = html_with_td_classes + if html_class is None: + html_class = self.default_css_class + if html_class_ignore_default: + self.html_class = html_class + else: + self.html_class = self.default_css_class + " " + html_class + + self.sortable = html_sortable + self.html_highlight_n = html_highlight_n + self.html_col_width = html_col_width + # XLS parameters + self.xls_sheet_name = xls_sheet_name + self.xls_before_table = xls_before_table + # PDF parameters + self.pdf_table_style = pdf_table_style + self.pdf_col_widths = pdf_col_widths + self.pdf_title = pdf_title + # XML parameters + self.xml_outer_tag = xml_outer_tag + self.xml_row_tag = xml_row_tag + # TEXT parameters + self.text_fields_separator = text_fields_separator + # + if preferences: + self.preferences = preferences + else: + self.preferences = DEFAULT_TABLE_PREFERENCES() + + def get_nb_cols(self): + return len(self.columns_ids) + + def get_nb_rows(self): + return len(self.rows) + + def is_empty(self): + return len(self.rows) == 0 + + def get_data_list( + self, + with_titles=False, + with_lines_titles=True, + with_bottom_titles=True, + omit_hidden_lines=False, + pdf_mode=False, # apply special pdf reportlab processing + pdf_style_list=[], # modified: list of platypus table style commands + ): + "table data as a list of lists (rows)" + T = [] + line_num = 0 # line number in input data + out_line_num = 0 # line number in output list + if with_titles and self.titles: + l = [] + if with_lines_titles: + if self.titles.has_key("row_title"): + l = [self.titles["row_title"]] + + T.append(l + [self.titles.get(cid, "") for cid in self.columns_ids]) + + for row in self.rows: + line_num += 1 + l = [] + if with_lines_titles: + if row.has_key("row_title"): + l = [row["row_title"]] + + if not (omit_hidden_lines and row.get("_hidden", False)): + colspan_count = 0 + col_num = len(l) + for cid in self.columns_ids: + colspan_count -= 1 + # if colspan_count > 0: + # continue # skip cells after a span + content = row.get(cid, "") or "" # nota: None converted to '' + colspan = row.get("_%s_colspan" % cid, 0) + if colspan > 1: + pdf_style_list.append( + ( + "SPAN", + (col_num, out_line_num), + (col_num + colspan - 1, out_line_num), + ) + ) + colspan_count = colspan + l.append(content) + col_num += 1 + if pdf_mode: + mk = row.get("_pdf_row_markup", []) # a list of tags + if mk: + l = mark_paras(l, mk) + T.append(l) + # + for cmd in row.get("_pdf_style", []): # relocate line numbers + pdf_style_list.append( + ( + cmd[0], + (cmd[1][0], cmd[1][1] + out_line_num), + (cmd[2][0], cmd[2][1] + out_line_num), + ) + + cmd[3:] + ) + + out_line_num += 1 + if with_bottom_titles and self.bottom_titles: + line_num += 1 + l = [] + if with_lines_titles: + if self.bottom_titles.has_key("row_title"): + l = [self.bottom_titles["row_title"]] + + T.append(l + [self.bottom_titles.get(cid, "") for cid in self.columns_ids]) + return T + + def get_titles_list(self): + "list of titles" + l = [] + return l + [self.titles.get(cid, "") for cid in self.columns_ids] + + def gen(self, format="html", columns_ids=None): + """Build representation of the table in the specified format. + See make_page() for more sophisticated output. + """ + if format == "html": + return self.html() + elif format == "xls": + return self.excel() + elif format == "text" or format == "csv": + return self.text() + elif format == "pdf": + return self.pdf() + elif format == "xml": + return self.xml() + elif format == "json": + return self.json() + raise ValueError("GenTable: invalid format: %s" % format) + + def _gen_html_row(self, row, line_num=0, elem="td", css_classes=""): + "row is a dict, returns a string ..." + if not row: + return "" # empty row + + if self.html_col_width: # XXXX Obsolete ? + std = ' style="width:%s;"' % self.html_col_width + else: + std = "" + + cla = css_classes + " " + row.get("_css_row_class", "") + if line_num % self.html_highlight_n: + cls = ' class="gt_hl %s"' % cla + else: + if cla: + cls = ' class="%s"' % cla + else: + cls = "" + H = ["" % (cls, row.get("_tr_attrs", ""))] + # titre ligne + if row.has_key("row_title"): + content = str(row["row_title"]) + help = row.get("row_title_help", "") + if help: + content = '%s' % ( + help, + content, + ) + H.append('") + r = [] + colspan_count = 0 + for cid in self.columns_ids: + if not cid in row and not self.html_generate_cells: + continue # skip cell + colspan_count -= 1 + if colspan_count > 0: + continue # skip cells after a span + content = row.get("_" + str(cid) + "_html", row.get(cid, "")) + if content is None: + content = "" + else: + content = str(content) + help = row.get("_%s_help" % cid, "") + if help: + target = row.get("_%s_target" % cid, "#") + else: + target = row.get("_%s_target" % cid, "") + cell_id = row.get("_%s_id" % cid, None) + if cell_id: + idstr = ' id="%s"' % cell_id + else: + idstr = "" + cell_link_class = row.get("_%s_link_class" % cid, "discretelink") + if help or target: + content = '%s' % ( + cell_link_class, + target, + help, + idstr, + content, + ) + klass = row.get("_%s_class" % cid, "") + if self.html_with_td_classes: + c = cid + else: + c = "" + if c or klass: + klass = ' class="%s"' % (" ".join((klass, c))) + else: + klass = "" + colspan = row.get("_%s_colspan" % cid, 0) + if colspan > 1: + colspan_txt = ' colspan="%d" ' % colspan + colspan_count = colspan + else: + colspan_txt = "" + r.append( + "<%s%s %s%s%s>%s" + % ( + elem, + std, + row.get("_%s_td_attrs" % cid, ""), + klass, + colspan_txt, + content, + elem, + ) + ) + + H.append("".join(r) + "") + return "".join(H) + + def html(self): + "Simple HTML representation of the table" + if self.is_empty() and self.html_empty_element: + return self.html_empty_element + "\n" + self.html_next_section + hid = ' id="%s"' % self.table_id + tablclasses = [] + if self.html_class: + tablclasses.append(self.html_class) + if self.sortable: + tablclasses.append("sortable") + if tablclasses: + cls = ' class="%s"' % " ".join(tablclasses) + else: + cls = "" + + if self.html_col_width: + std = ' style="width:%s;"' % self.html_col_width + else: + std = "" + H = [self.html_before_table, "" % (hid, cls)] + + line_num = 0 + # thead + H.append("") + if self.titles: + H.append( + self._gen_html_row( + self.titles, line_num, elem="th", css_classes="gt_firstrow" + ) + ) + # autres lignes Ă  placer dans la tĂŞte: + for row in self.rows: + if row.get("_table_part") == "head": + line_num += 1 + H.append(self._gen_html_row(row, line_num)) # uses td elements + H.append("") + + H.append("") + for row in self.rows: + if row.get("_table_part", "body") == "body": + line_num += 1 + H.append(self._gen_html_row(row, line_num)) + H.append("") + + H.append("") + for row in self.rows: + if row.get("_table_part") == "foot": + line_num += 1 + H.append(self._gen_html_row(row, line_num)) + if self.bottom_titles: + H.append( + self._gen_html_row( + self.bottom_titles, + line_num + 1, + elem="th", + css_classes="gt_lastrow sortbottom", + ) + ) + H.append("") + + H.append("
      ' + content + "
      ") + + caption = self.html_caption or self.caption + if caption or self.base_url: + H.append('

      ') + if caption: + H.append(caption) + if self.base_url: + if self.xls_link: + H.append( + ' %s' + % (self.base_url, ICON_XLS) + ) + if self.xls_link and self.pdf_link: + H.append("  ") + if self.pdf_link: + H.append( + ' %s' + % (self.base_url, ICON_PDF) + ) + H.append("

      ") + + H.append(self.html_next_section) + return "\n".join(H) + + def excel(self, wb=None): + "Simple Excel representation of the table" + L = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name) + style_bold = sco_excel.Excel_MakeStyle(bold=True) + + L.cells += self.xls_before_table + L.set_style(style_bold, li=len(L.cells)) + L.append(self.get_titles_list()) + L.cells += [[x for x in line] for line in self.get_data_list()] + if self.caption: + L.append([]) # empty line + L.append([self.caption]) + if self.origin: + L.append([]) # empty line + L.append([self.origin]) + + return L.gen_workbook(wb=wb) + + def text(self): + "raw text representation of the table" + return "\n".join( + [ + self.text_fields_separator.join([x for x in line]) + for line in self.get_data_list() + ] + ) + + def pdf(self): + "PDF representation: returns a ReportLab's platypus Table instance" + r = [] + try: + PDFLOCK.acquire() + r = self._pdf() + finally: + PDFLOCK.release() + return r + + def _pdf(self): + """PDF representation: returns a list of ReportLab's platypus objects + (notably a Table instance) + """ + if not self.pdf_table_style: + LINEWIDTH = 0.5 + self.pdf_table_style = [ + ("FONTNAME", (0, 0), (-1, 0), self.preferences["SCOLAR_FONT"]), + ("LINEBELOW", (0, 0), (-1, 0), LINEWIDTH, Color(0, 0, 0)), + ("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), + ("VALIGN", (0, 0), (-1, -1), "TOP"), + ] + nb_cols = len(self.columns_ids) + if self.rows and self.rows[0].has_key("row_title"): + nb_cols += 1 + if not self.pdf_col_widths: + self.pdf_col_widths = (None,) * nb_cols + # + CellStyle = styles.ParagraphStyle({}) + CellStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"] + CellStyle.fontName = self.preferences["SCOLAR_FONT"] + CellStyle.leading = 1.0 * self.preferences["SCOLAR_FONT_SIZE"] # vertical space + LINEWIDTH = 0.5 + # + titles = ["%s" % x for x in self.get_titles_list()] + pdf_style_list = [] + Pt = [ + [Paragraph(SU(str(x)), CellStyle) for x in line] + for line in ( + self.get_data_list( + pdf_mode=True, + pdf_style_list=pdf_style_list, + with_titles=True, + omit_hidden_lines=True, + ) + ) + ] + pdf_style_list += self.pdf_table_style + # log('len(Pt)=%s' % len(Pt)) + # log( 'line lens=%s' % [ len(x) for x in Pt ] ) + # log( 'style=\n%s' % pdf_style_list) + col_min = min([x[1][0] for x in pdf_style_list]) + col_max = max([x[2][0] for x in pdf_style_list]) + lin_min = min([x[1][1] for x in pdf_style_list]) + lin_max = max([x[2][1] for x in pdf_style_list]) + # log('col_min=%s col_max=%s lin_min=%s lin_max=%s' % (col_min, col_max, lin_min, lin_max)) + T = Table(Pt, repeatRows=1, colWidths=self.pdf_col_widths, style=pdf_style_list) + + objects = [] + StyleSheet = styles.getSampleStyleSheet() + if self.pdf_title: + objects.append(Paragraph(SU(self.pdf_title), StyleSheet["Heading3"])) + if self.caption: + objects.append(Paragraph(SU(self.caption), StyleSheet["Normal"])) + objects.append(Spacer(0, 0.4 * cm)) + objects.append(T) + + return objects + + def xml(self): + """XML representation of the table. + The schema is very simple: + + + + +
      + The tag names and can be changed using + xml_outer_tag and xml_row_tag + """ + doc = jaxml.XML_document(encoding=SCO_ENCODING) + getattr(doc, self.xml_outer_tag)( + id=self.table_id, origin=self.origin or "", caption=self.caption or "" + ) + doc._push() + for row in self.rows: + doc._push() + row_title = row.get("row_title", "") + if row_title: + getattr(doc, self.xml_row_tag)(title=row_title) + else: + getattr(doc, self.xml_row_tag)() + for cid in self.columns_ids: + doc._push() + v = row.get(cid, "") + if v is None: + v = "" + getattr(doc, cid)(value=str(v)) + doc._pop() + doc._pop() + doc._pop() + return repr(doc) + + def json(self): + """JSON representation of the table. + """ + d = [] + for row in self.rows: + r = {} + for cid in self.columns_ids: + v = row.get(cid, None) + if v != None: + v = str(v) + r[cid] = v + d.append(r) + return json.dumps(d, encoding=SCO_ENCODING) + + def make_page( + self, + context, + title="", + format="html", + page_title="", + filename=None, + REQUEST=None, + javascripts=[], + with_html_headers=True, + publish=True, + init_qtip=False, + ): + """ + Build page at given format + This is a simple page with only a title and the table. + If not publish, does not set response header + """ + if not filename: + filename = self.filename + page_title = page_title or self.page_title + html_title = self.html_title or title + if format == "html": + H = [] + if with_html_headers: + H.append( + self.html_header + or context.sco_header( + REQUEST, + page_title=page_title, + javascripts=javascripts, + init_qtip=init_qtip, + ) + ) + if html_title: + H.append(html_title) + H.append(self.html()) + if with_html_headers: + H.append(context.sco_footer(REQUEST)) + return "\n".join(H) + elif format == "pdf": + objects = self.pdf() + doc = pdf_basic_page(objects, title=title, preferences=self.preferences) + if publish: + return sendPDFFile(REQUEST, doc, filename + ".pdf") + else: + return doc + elif format == "xls": + xls = self.excel() + if publish: + return sco_excel.sendExcelFile(REQUEST, xls, filename + ".xls") + else: + return xls + elif format == "text": + return self.text() + elif format == "csv": + return sendCSVFile(REQUEST, self.text(), filename + ".csv") + elif format == "xml": + xml = self.xml() + if REQUEST and publish: + REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) + return xml + elif format == "json": + js = self.json() + if REQUEST and publish: + REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE) + return js + else: + log("make_page: format=%s" % format) + raise ValueError("_make_page: invalid format") + + +# ----- +class SeqGenTable: + """Sequence de GenTable: permet de générer un classeur excel avec un tab par table. + L'ordre des tabs est conservé (1er tab == 1ere table ajoutée) + """ + + def __init__(self): + self.genTables = OrderedDict() + + def add_genTable(self, name, gentable): + self.genTables[name] = gentable + + def get_genTable(self, name): + return self.genTables.get(name) + + def excel(self): + """Export des genTables dans un unique fichier excel avec plusieurs feuilles tagguées""" + book = sco_excel.Workbook() # Le fichier xls en devenir + for (name, gt) in self.genTables.items(): + gt.excel(wb=book) # Ecrit dans un fichier excel + return book.savetostr() + + +# ----- Exemple d'utilisation minimal. +if __name__ == "__main__": + T = GenTable( + rows=[{"nom": "Toto", "age": 26}, {"nom": "Titi", "age": 21}], + columns_ids=("nom", "age"), + ) + print("--- HTML:") + print(T.gen(format="html")) + print("\n--- XML:") + print(T.gen(format="xml")) + print("\n--- JSON:") + print(T.gen(format="json")) diff --git a/html_sco_header.py b/html_sco_header.py new file mode 100644 index 00000000..c6f4dc05 --- /dev/null +++ b/html_sco_header.py @@ -0,0 +1,262 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +from sco_utils import * +from sco_formsemestre_status import formsemestre_page_title + +""" +HTML Header/Footer for ScoDoc pages +""" + + +# Some constants: + +# Multiselect menus are used on a few pages and not loaded by default +BOOTSTRAP_MULTISELECT_JS = [ + "libjs/bootstrap-3.1.1-dist/js/bootstrap.min.js", + "libjs/bootstrap-multiselect/bootstrap-multiselect.js", + "libjs/purl.js", +] + +BOOTSTRAP_MULTISELECT_CSS = [ + "libjs/bootstrap-3.1.1-dist/css/bootstrap.min.css", + "libjs/bootstrap-3.1.1-dist/css/bootstrap-theme.min.css", + "libjs/bootstrap-multiselect/bootstrap-multiselect.css", +] + +# Header: +def sco_header( + context, + REQUEST=None, + # optional args + container=None, # objet qui a lancé la demande + page_title="", # page title + no_side_bar=False, # hide sidebar + cssstyles=[], # additionals CSS sheets + javascripts=[], # additionals JS filenames to load + scripts=[], # script to put in page header + bodyOnLoad="", # JS + init_jquery=True, # load and init jQuery + init_jquery_ui=True, # include all stuff for jquery-ui and initialize scripts + init_qtip=False, # include qTip + init_google_maps=False, # Google maps + init_datatables=True, + titrebandeau="", # titre dans bandeau superieur + head_message="", # message action (petit cadre jaune en haut) + user_check=True, # verifie passwords temporaires +): + "Main HTML page header for ScoDoc" + + # If running for first time, initialize roles and permissions + try: + ri = context.roles_initialized + except: + ri = None # old instances does not have this attribute + if ri == "0": + context._setup_initial_roles_and_permissions() + + # context est une instance de ZScolar. container est une instance qui "acquiert" ZScolar + if container: + context = container # je pense que cela suffit pour ce qu'on veut. + + # Add a HTTP header (can be used by Apache to log requests) + if REQUEST.AUTHENTICATED_USER: + REQUEST.RESPONSE.setHeader("X-ScoDoc-User", str(REQUEST.AUTHENTICATED_USER)) + + # Get more parameters from REQUEST + if not head_message and REQUEST.form.has_key("head_message"): + head_message = REQUEST.form["head_message"] + + params = { + "page_title": page_title or context.title_or_id(), + "no_side_bar": no_side_bar, + "ScoURL": context.ScoURL(), + "encoding": SCO_ENCODING, + "titrebandeau_mkup": "", + "authuser": str(REQUEST.AUTHENTICATED_USER), + } + if bodyOnLoad: + params["bodyOnLoad_mkup"] = """onload="%s" """ % bodyOnLoad + else: + params["bodyOnLoad_mkup"] = "" + if no_side_bar: + params["margin_left"] = "1em" + else: + params["margin_left"] = "140px" + + if init_jquery_ui or init_qtip or init_datatables: + init_jquery = True + + H = [ + """ + + + +%(page_title)s + + + + + +""" + % params + ] + # jQuery UI + if init_jquery_ui: + # can modify loaded theme here + H.append( + '\n' + ) + if init_google_maps: + # It may be necessary to add an API key: + H.append( + '' + ) + + # Feuilles de style additionnelles: + for cssstyle in cssstyles: + H.append( + """\n""" + % cssstyle + ) + + H.append( + """ + + + + + + +""" + % params + ) + + # jQuery + if init_jquery: + H.append( + """ + """ + ) + H.append( + '' + ) + # qTip + if init_qtip: + H.append( + '' + ) + H.append( + '' + ) + + if init_jquery_ui: + H.append( + '' + ) + # H.append('') + H.append( + '' + ) + if init_google_maps: + H.append( + '' + ) + if init_datatables: + H.append( + '' + ) + H.append( + '' + ) + # JS additionels + for js in javascripts: + H.append( + """\n""" + % js + ) + + H.append( + """ +""" + % params + ) + # Scripts de la page: + if scripts: + H.append("""""") + + H.append("") + + # Body et bandeau haut: + H.append("""""" % params) + H.append(CUSTOM_HTML_HEADER) + # + if not no_side_bar: + H.append(context.sidebar(REQUEST)) + H.append("""
      """) + # + # Barre menu semestre: + H.append(formsemestre_page_title(context, REQUEST)) + + # Avertissement si mot de passe Ă  changer + if user_check: + authuser = REQUEST.AUTHENTICATED_USER + passwd_temp = context.Users.user_info(user_name=str(authuser))["passwd_temp"] + if passwd_temp: + H.append( + """
      + Attention !
      + Vous avez reçu un mot de passe temporaire.
      + Vous devez le changer: cliquez ici +
      """ + % (context.ScoURL(), str(authuser)) + ) + # + if head_message: + H.append('
      ' + cgi.escape(head_message) + "
      ") + # + # div pour affichage messages temporaires + H.append('
      ') + # + return "".join(H) + + +def sco_footer(context, REQUEST=None): + """Main HTMl pages footer + """ + return """
      """ + CUSTOM_HTML_FOOTER + """""" diff --git a/html_sidebar.py b/html_sidebar.py new file mode 100644 index 00000000..cf379ce6 --- /dev/null +++ b/html_sidebar.py @@ -0,0 +1,185 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +from sco_utils import * +from ZAbsences import getAbsSemEtud + +""" +Génération de la "sidebar" (marge gauche des pages HTML) +""" + + +def sidebar_common(context, REQUEST=None): + "partie commune a toutes les sidebar" + authuser = REQUEST.AUTHENTICATED_USER + params = {"ScoURL": context.ScoURL(), "authuser": str(authuser)} + H = [ + 'ScoDoc', + '' + % params, + context.sidebar_dept(REQUEST), + """

      Scolarité

      + Semestres
      + Programmes
      + Absences
      + """ + % params, + ] + + if authuser.has_permission(ScoUsersAdmin, context) or authuser.has_permission( + ScoUsersView, context + ): + H.append( + """Utilisateurs
      """ + % params + ) + + if 0: # XXX experimental + H.append( + """Services
      """ + % params + ) + + if authuser.has_permission(ScoChangePreferences, context): + H.append( + """Paramétrage
      """ + % params + ) + + return "".join(H) + + +def sidebar(context, REQUEST=None): + "Main HTML page sidebar" + # rewritten from legacy DTML code + params = {"ScoURL": context.ScoURL(), "SCO_USER_MANUAL": SCO_USER_MANUAL} + + H = [' +""" + % params + ) # ' + # + return "".join(H) + + +def sidebar_dept(context, REQUEST=None): + """Partie supérieure de la marge de gauche + """ + infos = { + "BASE0": REQUEST.BASE0, + "DeptIntranetTitle": context.get_preference("DeptIntranetTitle"), + "DeptIntranetURL": context.get_preference("DeptIntranetURL"), + "DeptName": context.get_preference("DeptName"), + "ScoURL": context.ScoURL(), + } + + H = [ + """

      DĂ©pt. %(DeptName)s

      + Accueil
      """ + % infos + ] + if infos["DeptIntranetURL"]: + H.append( + '%(DeptIntranetTitle)s
      ' + % infos + ) + H.append( + """
      Entreprises
      """ + % infos + ) + return "\n".join(H) diff --git a/htmlutils.py b/htmlutils.py new file mode 100644 index 00000000..aab3bbb7 --- /dev/null +++ b/htmlutils.py @@ -0,0 +1,72 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2020 Emmanuel Viennet. All rights reserved. +# +# 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 +# +############################################################################## + +"""Various HTML generation functions +""" + + +def horizontal_bargraph(value, mark): + """html drawing an horizontal bar and a mark + used to vizualize the relative level of a student + """ + tmpl = """ + + + + + """ + return tmpl % {"value": int(value), "mark": int(mark)} + + +import listhistogram + + +def histogram_notes(notes): + "HTML code drawing histogram" + if not notes: + return "" + bins, H = listhistogram.ListHistogram(notes, 21, minmax=(0, 20)) + D = ['
        • '] + left = 5 + colwidth = 16 # must match #q-graph li.bar width in stylesheet + if max(H) <= 0: + return "" + hfactor = 95.0 / max(H) # garde une marge de 5% pour l'esthetique + for i in range(len(H)): + if H[i] >= 0: + x = left + i * (4 + colwidth) + heightpercent = H[i] * hfactor + if H[i] > 0: + nn = "

          %d

          " % H[i] + else: + nn = "" + D.append( + '
        • %s

          %d

        • ' + % (x, heightpercent, nn, i) + ) + D.append("
      ") + return "\n".join(D) diff --git a/imageresize.py b/imageresize.py new file mode 100644 index 00000000..252a6f6d --- /dev/null +++ b/imageresize.py @@ -0,0 +1,29 @@ +"""Simple image resize using PIL""" + +from PIL import Image as PILImage +from cStringIO import StringIO + + +def ImageScale(img_file, maxx, maxy): + im = PILImage.open(img_file) + im.thumbnail((maxx, maxy), PILImage.ANTIALIAS) + out_file_str = StringIO() + im.save(out_file_str, im.format) + out_file_str.seek(0) + tmp = out_file_str.read() + out_file_str.close() + return tmp + + +def ImageScaleH(img_file, W=None, H=90): + im = PILImage.open(img_file) + if W is None: + # keep aspect + W = (im.size[0] * H) / im.size[1] + im.thumbnail((W, H), PILImage.ANTIALIAS) + out_file_str = StringIO() + im.save(out_file_str, im.format) + out_file_str.seek(0) + tmp = out_file_str.read() + out_file_str.close() + return tmp diff --git a/intervals.py b/intervals.py new file mode 100644 index 00000000..f7ac47d0 --- /dev/null +++ b/intervals.py @@ -0,0 +1,245 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + + +# Code from http://code.activestate.com/recipes/457411/ + +from bisect import bisect_left, bisect_right +from itertools import izip + + +class intervalmap(object): + """ + This class maps a set of intervals to a set of values. + + >>> i = intervalmap() + >>> i[0:5] = '0-5' + >>> i[8:12] = '8-12' + >>> print i[2] + 0-5 + >>> print i[10] + 8-12 + >>> print repr(i[-1]) + None + >>> print repr(i[17]) + None + >>> i[4:9] = '4-9' + >>> print [(j,i[j]) for j in range(6)] + [(0, '0-5'), (1, '0-5'), (2, '0-5'), (3, '0-5'), (4, '4-9'), (5, '4-9')] + >>> print list(i.items()) + [((0, 4), '0-5'), ((4, 9), '4-9'), ((9, 12), '8-12')] + >>> i[:0] = 'less than 0' + >>> i[-5] + 'less than 0' + >>> i[0] + '0-5' + >>> print list(i.items()) + [((None, 0), 'less than 0'), ((0, 4), '0-5'), ((4, 9), '4-9'), ((9, 12), '8-12')] + >>> i[21:] = 'more than twenty' + >>> i[42] + 'more than twenty' + >>> i[10.5:15.5] = '10.5-15.5' + >>> i[11.5] + '10.5-15.5' + >>> i[0.5] + '0-5' + >>> print list(i.items()) + [((None, 0),... ((9, 10.5), '8-12'), ((10.5, 15.5), '10.5-15.5'), ((21, None),... + >>> i = intervalmap() + >>> i[0:2] = 1 + >>> i[2:8] = 2 + >>> i[4:] = 3 + >>> i[5:6] = 4 + >>> i + {[0, 2] => 1, [2, 4] => 2, [4, 5] => 3, [5, 6] => 4, [6, None] => 3} + """ + + def __init__(self): + """ + Initializes an empty intervalmap. + """ + self._bounds = [] + self._items = [] + self._upperitem = None + + def __setitem__(self, _slice, _value): + """ + Sets an interval mapping. + """ + assert isinstance(_slice, slice), "The key must be a slice object" + + if _slice.start is None: + start_point = -1 + else: + start_point = bisect_left(self._bounds, _slice.start) + + if _slice.stop is None: + end_point = -1 + else: + end_point = bisect_left(self._bounds, _slice.stop) + + if start_point >= 0: + if ( + start_point < len(self._bounds) + and self._bounds[start_point] < _slice.start + ): + start_point += 1 + + if end_point >= 0: + self._bounds[start_point:end_point] = [_slice.start, _slice.stop] + if start_point < len(self._items): + self._items[start_point:end_point] = [ + self._items[start_point], + _value, + ] + else: + self._items[start_point:end_point] = [self._upperitem, _value] + else: + self._bounds[start_point:] = [_slice.start] + if start_point < len(self._items): + self._items[start_point:] = [self._items[start_point], _value] + else: + self._items[start_point:] = [self._upperitem] + self._upperitem = _value + else: + if end_point >= 0: + self._bounds[:end_point] = [_slice.stop] + self._items[:end_point] = [_value] + else: + self._bounds[:] = [] + self._items[:] = [] + self._upperitem = _value + + def __getitem__(self, _point): + """ + Gets a value from the mapping. + """ + assert not isinstance(_point, slice), "The key cannot be a slice object" + + index = bisect_right(self._bounds, _point) + if index < len(self._bounds): + return self._items[index] + else: + return self._upperitem + + def items(self): + """ + Returns an iterator with each item being + ((low_bound,high_bound), value). The items are returned + in order. + """ + previous_bound = None + for b, v in izip(self._bounds, self._items): + if v is not None: + yield (previous_bound, b), v + previous_bound = b + if self._upperitem is not None: + yield (previous_bound, None), self._upperitem + + def values(self): + """ + Returns an iterator with each item being a stored value. The items + are returned in order. + """ + for v in self._items: + if v is not None: + yield v + if self._upperitem is not None: + yield self._upperitem + + def __repr__(self): + s = [] + for b, v in self.items(): + if v is not None: + s.append("[%r, %r] => %r" % (b[0], b[1], v)) + return "{" + ", ".join(s) + "}" + + +if __name__ == "__main__": + # Test 1 + i = intervalmap() + i[9:] = "!" + assert repr(i) == "{[9, None] => '!'}" + i[:5] = "Hello" + i[6:7] = "World" + assert repr(i) == "{[None, 5] => 'Hello', [6, 7] => 'World', [9, None] => '!'}" + i[8:10] = "(Test)" + assert ( + repr(i) + == "{[None, 5] => 'Hello', [6, 7] => 'World', [8, 10] => '(Test)', [10, None] => '!'}" + ) + i[:3] = "My," + assert ( + repr(i) + == "{[None, 3] => 'My,', [3, 5] => 'Hello', [6, 7] => 'World', [8, 10] => '(Test)', [10, None] => '!'}" + ) + i[5.5:6] = "Cruel" + assert ( + repr(i) + == "{[None, 3] => 'My,', [3, 5] => 'Hello', [5.5, 6] => 'Cruel', [6, 7] => 'World', [8, 10] => '(Test)', [10, None] => '!'}" + ) + i[6:6.5] = "And Harsh" + assert ( + repr(i) + == "{[None, 3] => 'My,', [3, 5] => 'Hello', [5.5, 6] => 'Cruel', [6, 6.5] => 'And Harsh', [6.5, 7] => 'World', [8, 10] => '(Test)', [10, None] => '!'}" + ) + i[5.9:6.6] = None + assert ( + repr(i) + == "{[None, 3] => 'My,', [3, 5] => 'Hello', [5.5, 5.9000000000000004] => 'Cruel', [6.5999999999999996, 7] => 'World', [8, 10] => '(Test)', [10, None] => '!'}" + ) + assert " ".join(i.values()) == "My, Hello Cruel World (Test) !" + print("Test 1 OK") + + # Test 2 + i = intervalmap() + i[:0] = "A" + i[2:5] = "B" + i[8:10] = "C" + i[12:] = "D" + assert ( + repr(i) + == "{[None, 0] => 'A', [2, 5] => 'B', [8, 10] => 'C', [12, None] => 'D'}" + ) + i[:] = "K" + assert repr(i) == "{[None, None] => 'K'}" + assert i[5] == "K" + i[0:10] = "L" + i[6:8] = "M" + i[20:] = "J" + assert i[-1] == "K" + assert i[5] == "L" + assert i[7] == "M" + assert i[9] == "L" + assert i[15] == "K" + assert i[42] == "J" + print("Test 2 OK") + + # Test 3 + try: + from datetime import datetime + except: + print("Test 3 skipped") + else: + i = intervalmap() + i[: datetime(2005, 10, 24)] = "A" + i[datetime(2005, 11, 11) : datetime(2005, 11, 17)] = "B" + i[datetime(2005, 11, 30) :] = "C" + assert i[datetime(2005, 9, 25)] == "A" + assert i[datetime(2005, 10, 23)] == "A" + assert i[datetime(2005, 10, 26)] == None + assert i[datetime(2005, 11, 9)] == None + assert i[datetime(2005, 11, 16)] == "B" + assert i[datetime(2005, 11, 23)] == None + assert i[datetime(2005, 11, 29)] == None + assert i[datetime(2005, 11, 30)] == "C" + assert i[datetime(2005, 12, 3)] == "C" + print("Test 3 OK") + + try: + import doctest + except: + print("Skipping the doctests") + else: + print("And now, the doctests") + doctest.testmod(optionflags=doctest.ELLIPSIS) diff --git a/listhistogram.py b/listhistogram.py new file mode 100644 index 00000000..2e19a6b4 --- /dev/null +++ b/listhistogram.py @@ -0,0 +1,31 @@ +import math + + +def ListHistogram(L, nbins, minmax=None, normalize=None): + """Compute histogram of a list. + Does not use Numeric or numarray. + + H[i] is the number of elements from L + such that bins[i] <= L[i] < bins[i+1] + """ + n = len(L) + if minmax is None: + xmin = min(L) + xmax = max(L) + else: + xmin, xmax = minmax + # clip data + for i in range(n): + if L[i] < xmin: + L[i] = xmin + if L[i] > xmax: + L[i] = xmax + bin_width = (xmax - xmin) / float(nbins - 1) + H = [0] * nbins + for i in range(n): + idx = int(math.floor((L[i] - xmin) / bin_width)) + H[idx] += 1 + bins = [] + for i in range(nbins): + bins.append(xmin + bin_width * i) + return bins, H diff --git a/logos/logo_footer.jpg b/logos/logo_footer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1afe1fe65139f29f2b49bd8f41e8db86f80f84e5 GIT binary patch literal 10000 zcmd6M1yG&KvhKftg}YmDcbDKAB)B^)EXcy$LxA8GoFuqQa19m`G(d2d;O>OrkL;YY z_u229ckipZ^{QS^*G$hhJu^M>PxbusF!QhiU@FMU%K{)60I+xrz{5U(CGBo*=M8`W zH~;`tAE85lPQt;yKtw=*M?ykIMnXdUJAq(fVZm@6d{1jBCLA>|` zSdTLU0}G1)hl&aY|I&v6!NLJpl-OWS93BZZs>IweTr=k!F3gwJlIjqbnorbj@Eqc* z30E+0|x(P69^NFQWb`RL!1*kNX_{rl^M>X&5zxI z;b0%l|D^|d?1@vt>{t!c`DMZ=M^1GYg}CbX6VsrFIRN!>bTC-3SdUuKqU*nh@)#pX zY?|fUo(BC1{1VVlj5psYDVY8Z1W}g}TO7*I{0#(XWX!E2aM}I>p;h_3<`KF)WFfE1 zpP@YStF$(k&D3#NaYjF{FH{?BnHHYCpqv*V&8lZ$!n*G-7=Ele+*1lwt!GS!;yK;2 zHfVl2LwS;t?uKqHx8d%Cq%fn#Q67$W>_nDd5@aPBtlO@j_J zIH4QrHoXq5s@KZ`0JNy*zv}#=WkK#*iFW~(_M%mJ$&%?c-I-`7{r0@S3;&3jcke{G zK9s(fti-=zwEX7vqh)f)*ZsB+01|O){zVU_oq$l+9stB1cL(C*{sDnuPW;sQ2iH@r ztliH8GV4nQj^d$9{^SMAiD$*j1j+v7h0HXk4rN{AH79QFY{vS5>r+$@K$QN;m%6$4 z9&1H7yUfqXR@AT1+zwNXWi92^@$|Yj^u8q!y>nEs6eDYl8T!7^U0k2}nq@c{bP?|6SH|bZ2X_0t)vGC5iF{k~uZyiX^78tT+am@Mt9R(c%;^%P zy{iOn#`n{T1hw(^rQ+C&I#Ms5+Rwawoyw^3)cZ5l?XVK(#5{$Q6xYffOI1~XCpWaA z`5DfgyZV`0#nUX9p6ATyo~~8{cw{|zlXgizl2RfI>Ox&M9ssm8f|9kAz#p+XeIHut z>~9^mqOVFjqf0Bv9H4#=K<|}MF%yX;Z>@7hwbm%@Vq>4h=ddq#V*6xFHq1zcp>jJ% z8@Gjf5tFm7WKMoDm8mjQ>pU#_je{R@Z%HcOGuAn6(yC-ymSd`UmVkOe#_xZ5r2`}j?(4$*raE4InDurj*fv7UL7P!$0l7ZYQjmG zb896{tAUlJOEF}FiO0H^+gqoE<*RR%&wt*14WpXtYAs2SxBmP97`g2ZkfO{7Wbj(L zPYMYX5L`j=UcD$MkujZPh9A}Ci|ne?M$4`GcM$OyBJHDTMc-VfjkxnOy4D0)s3wnF z#NHe)meUBFsfa9qVjd^^Lb)hES*ItW<)@B2o_cy&VZMw#BgblWzrd}cM+S8q`HONi zxx_(*vP%7izjtN*dM)eJ&M*I@Qe^WyY^s+$*Zp~Sn|eM~eSNEvdz6~Zk-Vk9TGFkr zL(4$Kw0a0Dv}6aPkLT&04Zo2FR6f3o)y`S6;c7dSuOh;RtGj8&R<>K6k2s-fA|@=~ zo|oFJgal3Qe+W8cG$;3!en@Kj_ZI*7#$f*V#vI8I9mmhTjl=H5o~3TbdgX1O-eFE| ztS%T!UWJgjrOrf1=b_8d5L@Qo;9k*6Xdp6-|?+YOu6x^^u!sSGlx_n`Jmn0&2l*P+vmOfG)ON7%+$_ zPlfH@6p+AiyBM8R$haCow*EP6GM(MGltsRT2oCbKF>^SPTRz9T|Hwn(`B;RW1{QtX z=~r{k=pJsu|B#@bfY-SR#W<|P*(n0dHpn!~PuxCf=4vci%|!=Kl@G|L*?etFkG7;t z>oF&aw&MJi#C5mrbR&>~HyZ;EyusI*H%;GcMzT#g_Nh8eIpC%J%j__dzc1_-+y2i2 zA#FOE*1rafKh_%d*IGXSW`nn>zt$zYNF84^o~qMrUzNL#m@<%C8HaOUA^7+LW&iyK>I-Pw4gd-Er&ARn-3=hr_xf7=bX*jK7&8glz~ysQ^~+4lSGVthuf73UFfSy zlwM$P*5PFr+yEnq}d-sswn ze(3-96iOfThTZgQLn^oJ$C+Fv_V<|nl6BE&J__fz%he+wwNp%#MkcNy?v>VRv2fNmoN+C%Rm;y5 ztZV9G9LHlRoQ)5*XewfKnX9{kTZ2sHe{UEo{qW4AII<{gIN1)e!zVT)53grfuAuDD z>lwyBA5&F)Mm}lUqi4?&eu8Ga6rY3XJC)?DYOH@^EK173NJf;CXoc*chXNK?+t|=g zF3f1enqk;ccHgMtVY%KdWoVCliAH95GRbcTZ*JR~M}R`b#t@QX)MT7BrtT*@_-!pN4#1jLk{_O#MjYGGAj~EZiEGNgaXRR0PVSlS zMOjIyp#J#)^tt4C={d^Q!T0maO2l3)I2Bqd_2^e4U9<$8pHlj5RN%cUPL!Ecp*GR& z;A=nKjDs(NGm~P%t^l(6X$Ar;E(_3GBF!qzc3_m!L+Cq&r z)29K;I3Q6wni+b3GD+r_LUIlL$?B$oEkbOpwpBxQ6Jg?#pg!2*4_()@w!PW4Fqzz; z+vnY)dy<6Scol)heE0xdzX@>CwCR*@znO5(ijQ~z3O|7!fF`Z%#=BNuO1BBs32t%; zVU0!7lFhEap}%ay&!u!V zZF(`?$BP8BUOk2}+=0!~>d2BpWQ?ueFZLgep?9U@b=zvjd>;T`;4op74c27uWuK(Al;HJDIxq&O zF}T{BbisvxAoZO4(wM1*tpa7F8A{nNK@1N+3(1#@s2QlIRWKN+_4=a0UeR+W?4x@& z4#f#k;`JZ5ZNnOB=&nv*-I8=t6!O$q;14udL2qIL^5EY!Fmuw zEq9V9>D%SVwIMZ|v2d|ApLVR=k~k=3v|4)54DCs@NRJa?vCpF!(i~?SBuM;d;6jR| zSNk)8_8B*O`%uGq+~@dpo!&|BL{*yh1=-O4aq^56IyL?UdOVzT++;hsd$Ci};=q%k z2Y`3kTz^6ZL=P&zozGEcf1iw@oPpKDQsDd)D3&XRlZu&6W>TB$=Pb-pu9P_sRd@jQ zE}6B*NDXo~lE&?Zb_LJv)W#=S65i7|uCr0JX(=T9@LpEV?%>T2AaM*Hu+iYc@!T=n ztaxg@I2MXjhk}i`{$idj5!>O5bA_0s^LYZVTG;q^OS7lS28p}LWM)Q?<_m5KK?63E z*?Hw&kCE=TQ(9^>;x_dJ3nlvY(elCDg2!F{>MBn)<7$Vm-O7hm@!jFI;`v#VNn03} zZf_We#Cb6=(AAD;dStKV+OI&4_Wor0im7w>zI>3k;bC7j$awqqM!hfZZgiTe&ndNXrZkKK2Rx_*dBq??ngK#@m z!u_U6P6+DKA@Zx~SRQqO(S?>wG%SGi*dUU%m^_Y<(7V3XkuP0Nr>{hAf8`_=af5(d z%UMmZQl}t_a9G%~ZLPtAa%_H7rh$j%tEok!Rr>rk>&G4a@o@-TGi%e-ropnVH=gVw zRm*z#8UIVBaiYjZ#f5$Dg8~J_o{JT8GD&K8Wx79@0 zZcZaB3m;k!go%kol?#90xbd;0>0k5Y^ffiZ8p6khP32wvDQOr-TDp-+t)=vjvi~%P zKbsy;FwMQJHaKXJ&N|`zs0K*HJX#KEq$UHpqWzYUUz~*?HL?D+Q9}#9w^>AD zD(k2R0!rkcz%|zl8PAZ2#f<8VBd^Bd=XqhDgfLs?>n!nW@mU#+1__9>t*k_rQhIOe z>&WY)N)%`fytgQjwGbF!n}AWfWK`}FybX-O;JY>F4NN^paeH(&)Q{1uqWkz--Tf%hXg|?ZFJTKl z(rzJY`NDdBl?K+f38mfS65Cs%nIH`5xI|hrp@VZ@hA3v6Fy7`A;OJer;tu+0+y_mHoD4l-R^=uVjN|E(crJ^;Z~qBXm|&I< z+cV(yZOP_VhYMM(OXb{TnoNA*1coJ{{L?XyzW@%#sxF}TP}P3QwKeM+IZf*UFDdNve9|;&0xL9Ni%oa2XwC^ z4N}_W9mP7mI&Jv&OBcDicR+rWCsl5=IWafOqvm&J#D&E!5X%Ui^yf3sAOo(P;=~P8as2$ zyX1UCvY}Z6#l(vyoeS`HaWj^52spGAlXd(j+XyLI9!%(^2<@tHGAuNEOuP7|Vf#!) zZ_G6ea~3H5LPSNfd}QH8#$@>uzF}HZ$*6W51IwY%Q9aN8-aW*C6>0a3yi$V5bWz-| z%-bJF)d_=T?&4jDavpOzI^VIFmpG|MXBTOH-uXNygY02Gds*_^WJlKwcGv3xhTO=1 za3QK+GaWoD^=HmP3Xp`1cE(nhgp0)q6+_=y>`Qjz+l>+oma@o$V4f1ENDN8Fm7S&{M)s)NT zL~C0t2YkXDJ+8G@r0;^}?1Zdd2*0!p%N0q*`ZkF^_c~t~9eqk0k;}sukzlvLXTMSi zr$gv!_lF1kWRu4O@Zp#V9ha{tLz+K#yYQJrY2+De=c$|se&)B;^I-OpY-7YNqqX5Z zB!h+l-Uwv-X76fVXG!{upM>3S)c4t9$-QUF@S1DKTBYR}@+SiOTJ|dcBi|jrlOUAf zNx8+9hk;eRVJapeMXXcW}7+cy!1qHH1rHK$;OWP zOlxNkB*c4!@T4ReBjo`|i`_D;Jr?6c%e;>$7|pWrT00B(_xyf5dLAPFRcAwqoQvx9 zi#KY!xyZ)OM8?*h_+Rs68vLxmoLaaV5sB+$aWn#9%fH3wJye&COD9rNV=>xGi>ej#FQS>#<)=i<5VdIAk9wJC z%+9_wBv{0=`F1?RRoUiGQ5v_m2x$mhF~v|Q$^dq#PSDd-qW7Klek%`e;oXkdNkvg# zeE0!X0y^a?Ln7(&&zj3KNIYcDDetnYgW*uWPTNTHXRT09sUOnrAxKLxdh$vFHYX`{A_lT{BLNzJ+R_ty0$NHL+nQE7j%2JbJys-l@++L z;9v9$kf%wMr%6GPra5e^Q>RH3|HtD12m$Ls1HmMH-I(*7rlC(5(`=~!Ui z&u3HJM1FT(6$IZ&6mRJ0+=jx)z107nkE$bZQDVfnErFe?^fYU|tW~MC@7Wu}xGT81 zV!Y_YE4SoN(q{6;pN$KjiY;vJy-owaG^naX(nU7afXy)5`RO_XXPL;Ftr}lW@R}4Q zeokamf`=ul@`o_e*|@2s=@M1t`_nM^S_L=O94ENYNMQyn92}gji@xXkmD6besZVea zS6L1+QWQh*n;9gZrvs8%-NaWZh_+NLX!!Awd#b~t6d&Q?NlBT0)G^m^t0K<@F#t?2 z*%^=Jstc`=y&YJq26Wjt;un)~@5o*KE&#V83YopYY)aLA6n!&V_1FznNnol@LK7nU zx{n6M&54KKBk~jlaSE(1QIlljguxk65Q5i%NH@nDd&kZtal#pmB9y)0Ll{EIVUwy!6?`%{*bOo9;f|MN9bF;) z7Abz&szOY{;$Nl_U7}-n{0LvQXTxz(=aIY$RQtw+$7wW%ncV`)u3s3gUSi2bS3{8; z%apO1MpW^shJQiC!45r{A{L^8W{0f-FEhXqMrSX55m;plzxAp@gL#kzsUf5nL}jRv zMoCj)8C}S8c_gbvj-mP#N(CvQL6)05_bKSB3zGr?@i@%I(WoWsu$*ilC$OvS)TW$! zbVE;fw%k5;xJL@D+@OkLeI^w_vlpFSq*MJN8oO{VjLBOB1;fv*0myI^z#h<^ zf?PQ@B43CZlVRsbK}`6$L1Rok65e)lk7K)P+@ec{(Un(t5|dnt?**25goN^xVDQ*0 z_&4RA$7BP`M9h+zctsFSm16-ssBYXV)yN%r<5QqTN^;23QMSGMSuL!77B zsuda1U%{M57jWD;UU!Hj!>!nlvq4lU>Zq_HpIef(n8YWmlhU1Ug9AWwVb)P2qSi zH1P0^xx(?b(dC+_R<7R7HPc`x{gNU z>#!|+Mu7lFt6l(-<$b4yKKe3)J}iE+?BjT?-J~89WY&==Y6el!Ae_-X{PJFR!3H6Z zMgl)C8*a7TvdV>bz0+Mv0(Z!`?U;ns8Q?1HmaRfAPIeNhJJP%^$?p&(4dI1wDK_a{ zev1r_Hq})uaAK#Dse5ynLlnmR9)*r}!r{>EXcxK0jW3VxdGeFhj8&(Z4TZV#o#lzY z^Yq0vhJ`>(s&uP+-KJKTEoF0z;g<(MWAqyZXY+hBjHZ|8i4{(Su4;~B=cuUpAjy{O ziy*EQT@Qv*z2LVzZItv#x-5t`b0@8ChP;)@Zc^HL*41`5s|&$dsze&?<|#0kK?ayI zoa+0}&3Ei@M$=zaEtZ(4V%ki_$D?9TAn1HlpI670;VP5zAlrI^C5f1`q}}5zA3WE7 z52#iM_|)fcdhmhNv}5EAD=0TbEaC0}Of)J%tOtPlKRZ<|d-AIIhiWzpLA$e?98ANF zwIkpD@V`3#bgnS(mouf7$(xunfw-ESMn8$rJCEev$5#h(KcAl);n`E6_2d<3x(ZaDRX2gNFFy9FZSB|=f{d0Y z_+qB+l*r;yT+uj!w?tw-sOwVJu=6{JGzHh(xb?7l&p$UdGvlie_)2El z`3Ip*wE}TvjNK7ajkRov%w+}7kfeNqS2^L-c>6Q}qse1Y=VAA?8LnZ)R~xNniC*oK z>e>73PK=MDQ6IWQ(A~Uy4V2pAU|q;1Cxffi63;pOi}Y4)3A4|hD-1J~tsYIzd!4SR z7HAI?lfB4t4iOz`-^xS!f^6}fe8Re%>uMaoGJDZD)Asoq&pOOOvj}BlJ=xU*(4h!& z-xNCQK!xE^QM*LEar_cxE{@@KLs=bXYVC9J0Q757HcHe?SH{?UZ3)R+aMxKrXgJ9t z>Dt72Cz!FM(gpKvylkHIQ;7Xh+|;AZ!fx2QLr-ESK^3&-Qd=X~aaBK9Zzq_BraUI@ zwz%SaMwY-zS}FpUm6C@ivw>dr#mFM`dTXC{jYd4_g&ntw_hxku-=6w!B0602-ig)} z=x_ZH3%Gw`9S18(%(%>4KJ|*YamMlBJPR>)SpNCDpdeG>SF`hDaM`SrM`{4b-P2C- zh=pg;t;44&@1?s=uOp*9I*(3W;j1dn_p~X*TH4I@3N=p>M?NA4IU%gNlhQBxAz;+p z)CEw3UzWv4%&7{!ISTARtsv*$1TTHPI=Xyk9X_T`>^&#u)BYnO1Pb%TYOS{Dg)NWu zSHB&kX!?ac?f~}M=BFb#9IVDdj%SFip=fJSr#B~jeMy_6{$*FEZClJsF8@umR!}|L zzyFyS?M3gZOWSv>@I~+?MEViK)JEt0nH23CZ{nTC|Hg2zQ=+QNZ3*2R(CP|`GlJAh zctfMzlNFb3Y!1Vg!!f1ZAZj&(t(5_@>HGdg9bZej7lPNG?+F^8?qm2+$RHQ}-IT|h zQ^lq%wbl4Upew=*mZVdnTv4fsePv0u7>X@bR(W wFU{ra>w@LrRZgOJ+)_Mmpqa}nh8^H$mzAOz&IXk{z5j-E!?b@>;KS_y0-tt?k^lez literal 0 HcmV?d00001 diff --git a/logos/logo_header.jpg b/logos/logo_header.jpg new file mode 100644 index 0000000000000000000000000000000000000000..53117a9fbe8511034a298a250c3cc0b38afd7b0a GIT binary patch literal 30519 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

      IBw(i=WM6L-;Hhil=kvOj7e|^o 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 z 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≫O?wblNrD+99O0 zI*uEcZr=PO@by~G*K;^cAvOaWf!sO{ycSq;j&HB!d|jtw2jK(3y^eCLEsU?L@z2Q>fy002ovPDHLkV1g@_m{b4& literal 0 HcmV?d00001 diff --git a/misc/PublicationBulletins/Bulletins-Orleans/index.php b/misc/PublicationBulletins/Bulletins-Orleans/index.php new file mode 100644 index 00000000..5e3259b1 --- /dev/null +++ b/misc/PublicationBulletins/Bulletins-Orleans/index.php @@ -0,0 +1,310 @@ + Menu Semestre => Modifier le semestre => Publication" soit cochĂ©e. + +// Code contribuĂ© par Yann Leboulanger (UniversitĂ© Paris 10), Juin 2013 +// et modifiĂ© par Pascal Legrand (Nov 2017) +// +// Exemple publication des bulletins de notes vers les Ă©tudiants +// L'Ă©tudiant est authenfiĂ© via le CAS +// Le bulletin est rĂ©cupĂ©rĂ© en format XML en interrogeant ScoDoc +// +// Il faut crĂ©er un utilisateur ScoDoc n'ayant que des droits de lecture. +// +// A adapter à  vos besoins locaux. + +include_once 'CAS.php'; +// *********************************************** CONFIGURATION *************************************************** +phpCAS::client(CAS_VERSION_2_0,'URL_CAS',443,''); +phpCAS::setNoCasServerValidation(); +phpCAS::forceAuthentication(); + +$nip = phpCAS::getUser(); + +// Login information of a scodoc user that can access notes +$sco_user = 'USER'; +$sco_pw = 'PASS'; +$sco_url = 'https://SERVEUR/ScoDoc/'; + +// URL oĂą sont stockĂ©es les photos, si celle-ci diffère de "$sco_url". +// Cette valeur est concatĂ©nĂ©e avec la valeur de "etudiant['photo_url']". (/ScoDoc/static/photos/.....) +$photo_url = 'https://SERVEUR/ScoDoc/'; +// *********************************************** CONFIGURATION *************************************************** + +// ************************************************* FONCTIONS ***************************************************** +// DĂ©finition de la fonction d'encodage des headers +function http_build_headers( $headers ) { + $headers_brut = ''; + foreach( $headers as $nom => $valeur ) { + $headers_brut .= $nom . ': ' . $valeur . "\r\n"; + } + return $headers_brut; +} + +// RĂ©cupĂ©ration du dĂ©partement +function get_dept($nip) { + global $sco_url; + $dept = file_get_contents( $sco_url . 'get_etud_dept?code_nip=' . $nip); + return ($dept); +} + +function get_EtudInfos_page($nip, $dept) { +// RĂ©cupĂ©ration des informations concernant l'Ă©tudiant. +// NĂ©cessite une authentification avec sco_user et sco_pw - Il est possible de choisir le format XML ou JSON. +// etud_info +// Paramètres: etudid ou code_nip ou code_ine +// RĂ©sultat: informations sur cet Ă©tudiant et les semestres dans lesquels il est (ou a Ă©tĂ©) inscrit. +// Exemple: etud_info?format=json&etudid=12345 + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array('format' => 'xml', 'code_nip' => $nip, '__ac_name' => $sco_user, '__ac_password' => $sco_pw); + // CrĂ©ation du contenu brut de la requĂŞte + $contenu = http_build_query($donnees); + // DĂ©finition des headers + $headers = http_build_headers(array('Content-Type' => 'application/x-www-form-urlencoded', 'Content-Length' => strlen( $contenu))); + // DĂ©finition du contexte + $options = array('http' => array('method' => 'POST', 'content' => $contenu, 'header' => $headers)); + // CrĂ©ation du contexte + $contexte = stream_context_create($options); + // Envoi du formulaire POST + $retour = file_get_contents($sco_url . $dept . '/Scolarite/Notes/etud_info', false, $contexte); + return ($retour); +} + +function get_all_semestres($xml_data) +// Tous les semestres suivis par l'Ă©tudiant +{ + $data = array(); + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + $sem = (array) $s['formsemestre_id']; + $data[] = $sem[0]; + } + return $data; +} + +function get_current_semestre($xml_data) +// Semestre courrant suivi par l'Ă©tudiant +{ + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + if ($s['current'] == 1) + $sem = (array) $s['formsemestre_id']; + return ($sem[0]); + } +} + +function get_semestre_info($sem, $dept) { +// Renvoi les informations dĂ©taillĂ©es d'un semestre +// Ne nĂ©cessite pas d'authentification avec sco_user et sco_pw - Il est possible de choisir le format XML ou JSON. +// formsemestre_list +// Paramètres (tous optionnels): formsesmestre_id, formation_id, etape_apo, etape_apo2 +// Coquille dans la doc : formsesmestre_id +// RĂ©sultat: liste des semestres correspondant. +// Exemple: formsemestre_list?format=xml&etape_apo=V1RT + global $sco_pw; + global $sco_user; + global $sco_url; + $donnees = array('format' => 'xml', 'formsemestre_id' => $sem, '__ac_name' => $sco_user, '__ac_password' => $sco_pw); + // CrĂ©ation du contenu brut de la requĂŞte + $contenu = http_build_query( $donnees ); + // DĂ©finition des headers + $headers = http_build_headers( array('Content-Type' => 'application/x-www-form-urlencoded', 'Content-Length' => strlen( $contenu) ) ); + // DĂ©finition du contexte + $options = array( 'http' => array('method' => 'POST', 'content' => $contenu, 'header' => $headers ) ); + // CrĂ©ation du contexte + $contexte = stream_context_create($options); + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/formsemestre_list', false, $contexte ); +/* + echo '

      '; +*/ + return ($retour); +} + +function print_semestres_list($sems, $dept, $sem) { +// Affiche le nom (titre_num) de tous les semestres suivis par l'Ă©tudiant dans un formulaire + echo '
      ' . "\n"; + echo '
      ' . "\n"; + echo ' Liste des semestres' . "\n"; + echo '

      ' . "\n"; + echo ' ' . "\n"; + echo '
      ' . "\n"; + echo '
      ' . "\n"; + echo '

      ' . "\n"; + echo '

      ' . "\n"; + echo ' ' . "\n"; +} + +function get_bulletinetud_page($nip, $sem, $dept) { +// formsemestre_bulletinetud +// Paramètres: formsemestre_id, etudid, format (xml ou json), version (short, selectedevalsou long) +// Résultat: bulletin de notes +// Exemple: ici au format JSON, pour une version courte (version=short) + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array('format' => 'xml', 'code_nip' => $nip, 'formsemestre_id' => $sem, 'version' => 'long', '__ac_name' => $sco_user, '__ac_password' => $sco_pw ); + // Création du contenu brut de la requête + $contenu = http_build_query( $donnees ); + // Définition des headers + $headers = http_build_headers( array('Content-Type' => 'application/x-www-form-urlencoded', 'Content-Length' => strlen( $contenu) ) ); + // Définition du contexte + $options = array( 'http' => array('method' => 'POST', 'content' => $contenu, 'header' => $headers ) ); + // Création du contexte + $contexte = stream_context_create($options); + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/formsemestre_bulletinetud', false, $contexte ); + return ($retour); +} + +function print_semestre($xml_data, $sem, $dept, $show_moy) { + global $photo_url; + $xml = simplexml_load_string($xml_data); + echo '

      ' . $xml->etudiant['sexe'] . ' ' . $xml->etudiant['prenom'] . ' ' . $xml->etudiant['nom'] . '

      ' . "\n" . '
      ' . "\n"; + $retour = get_semestre_info($sem, $dept); + $xml2 = simplexml_load_string($retour); + $publie= $xml2->formsemestre['bul_hide_xml']; + if (isset($xml->absences)) { + (isset($xml->absences['nbabs'])) ? $nbabs = $xml->absences['nbabs']: $nbabs = 0; + (isset($xml->absences['nbabsjust'])) ? $nbabsjust = $xml->absences['nbabsjust']: $nbabsjust = 0; + echo ' Vous avez à  ce jour ' . $nbabs . ' demi-journée(s) d\'absences, dont ' . $nbabsjust . ' justifiée(s)
      ' . "\n"; + } + else { + echo ' Les absences ne sont pas saisies.
      ' . "\n"; + } + echo '

      ' . htmlentities($xml2->formsemestre['titre_num']) . '

      ' . "\n"; + if ($publie == 1) { + echo ' Publication des notes non activée sur ScoDoc pour ce semestre
      ' . "\n"; + } + else { + echo '
      ' . "\n"; + echo '
      ' . "\n"; + echo '
      " + titrebandeau + "
      ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + if ($show_moy) { + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + } + foreach ($xml->ue as $ue) { + $coef = 0; + foreach ($ue->module as $mod) { + $coef += (float) $mod['coefficient']; + } + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + if ($show_moy) { + echo ' ' . "\n"; + } + else { + echo ' ' . "\n"; + } + echo ' ' . "\n"; + foreach ($ue->module as $mod) { + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + if ($show_moy) { + echo ' ' . "\n"; + } + else { + echo ' ' . "\n"; + } + echo ' ' . "\n"; + echo ' ' . "\n"; + if (!$show_moy) { + foreach ($mod->evaluation as $eval) { + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + echo ' ' . "\n"; + } + } + } + } + echo '
      UEModuleEvaluationNote/20Coef
      Moyenne générale:' . $xml->note['value'] . '
      ' . $ue['acronyme'] . '
      ' . htmlentities($ue['titre']) . '
      ' . $ue->note['value'] . '' . $coef . '
      ' . htmlentities($mod['titre']) . '' . $mod->note['value'] . '' . $mod['coefficient'] . '
      ' . htmlentities($eval['description']) . '' . $eval->note['value'] . '
      ' . "\n"; + echo ' ' . "\n"; + echo '
      ' . "\n"; + if ($show_moy) { + echo $xml->situation . "\n"; + } + } +} +// ************************************************* FONCTIONS ***************************************************** + +// ************************************************** HTML ***************************************************** +echo' + + + Bulletins de notes + + + + + +'; + +$dept = get_dept($nip); +if ($dept) { + $etud_info = get_EtudInfos_page($nip, $dept); + $sems = get_all_semestres($etud_info); + $sem_current = get_current_semestre($etud_info); +// (Condition) ? : + (isset($_POST['sem'])) ? $sem = $_POST['sem']:$sem = $sem_current; + print_semestres_list($sems, $dept, $sem); + (!isset($_POST['notes_moy'])) ? $_POST['notes_moy']='notes':''; + echo '
      Affichage des ' . ucfirst($_POST['notes_moy']) . '' . "\n";; + $bulletin_page = get_bulletinetud_page($nip, $sem, $dept); + ($_POST['notes_moy'] == 'notes') ? print_semestre($bulletin_page, $sem, $dept, False):print_semestre($bulletin_page, $sem, $dept, True); +// ($sem == $sem_current) ? print_semestre($bulletin_page, $sem, $dept, False):print_semestre($bulletin_page, $sem, $dept, True); +} +else { + echo ' Numéro étudiant inconnu : ' . $nip . ' - Contactez votre Chef de département
      ' . "\n"; +} +$erreur=0; // Tout est OK +/* +echo '

      '; +echo 'get_etud_info : '; +echo '
      ' . htmlentities($etud_info) . '
      '; +echo 'sems : '; +echo '
      ' . print_r($sems) . '
      '; +echo 'sem_current : '; +echo '
      ' . htmlentities($sem_current) . '
      '; +echo 'get_bulletinetud_page : '; +echo '
      ' . htmlentities($bulletin_page) . '
      '; +echo '
      '; +*/ +echo '' . "\n"; +echo '' . "\n"; +// ************************************************** HTML ***************************************************** +?> diff --git a/misc/PublicationBulletins/Bulletins-Orleans/style.css b/misc/PublicationBulletins/Bulletins-Orleans/style.css new file mode 100644 index 00000000..d9de68f2 --- /dev/null +++ b/misc/PublicationBulletins/Bulletins-Orleans/style.css @@ -0,0 +1,419 @@ +html:after { + /* common custom values */ + content: "IUT de Chartres Specimen"; /* your site name */ + font-size: 720%; /* font size */ + color: rgba(0, 0, 0, .05); + /* alpha, could be even rgba(0,0,0,.02) */ + + /* rest of the logic */ + z-index: 9999; + cursor: default; + display: block; + position: fixed; + top: 33%; + right: 0; + bottom: 0; + left: 0; + font-family: Verdana, Geneva, sans-serif; + font-weight: bold; + font-style: italic; + text-align: center; + line-height: 100%; + + /* not sure about who implemented what .. + ... so bring it all */ + -webkit-pointer-events: none; + -moz-pointer-events: none; + -ms-pointer-events: none; + -o-pointer-events: none; + pointer-events: none; + + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +body { + font-weight:light; + font-family:arial; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color: #4a6e91; + text-align: left; +} + +table { + border-collapse: collapse; + border: #4a6e91 1px solid; + width: 100%; + font-family : arial, verdana, sans-serif ; + font-size: 12px; +} + +td.titre { + border-top: #4a6e91 1px solid; + border-right: #4a6e91 1px solid; + border-left: #4a6e91 1px solid; + border-bottom: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.titre_vide { + background-color: #dddddd; + border-top: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.ue { + background-color: #dddddd; + border-top: #4a6e91 1px solid; + + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.ue_vide { + background-color: #dddddd; + border-right: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.module { + background-color: #eeeeee; + border-top: #4a6e91 1px solid; + border-right: #4a6e91 0px solid; + border-left: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.module_vide { + background-color: #eeeeee; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.evaluation { + background-color: #ffffff; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.evaluation_vide { + background-color: #eeeeee; + border-top: #4a6e91 1px solid; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.note { + background-color: #ffffff; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.note_vide { + background-color: #eeeeee; + border-top: #4a6e91 1px solid; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.coef { + background-color: #eeeeee; + border-top: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.coef_ue { + background-color: #dddddd; + border-top: #4a6e91 1px solid; + border-bottom: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.coef_vide { + background-color: #ffffff; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.moyennes { + background-color: #eeeeee; + border-top: #4a6e91 1px solid; + border-right: #4a6e91 1px solid; + font-weight:light; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +td.moyennes_bold { + background-color: #dddddd; + border-top: #4a6e91 1px solid; + font-weight:bold; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color:#4a6e91; + text-align: left; + text-decoration:none; + padding:2px; +} + +img { + vertical-align : middle; +} + +form { + display: table; + width: auto; /*matching the parents div width*/ + color: #333333; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; +} + +fieldset { + width: 100%; + background: #F6F6F6; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + border: 0; + background: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#FFFFFF)); + background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%); + box-shadow: 3px 3px 10px #ccc; + -moz-box-shadow: 3px 3px 10px #ccc; + -webkit-box-shadow: 3px 3px 10px #ccc; +} + +legend { + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:bold; + background-color: #4F709F; + color: white; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-shadow: 2px 2px 4px #888; + -moz-box-shadow: 2px 2px 4px #888; + -webkit-box-shadow: 2px 2px 4px #888; + text-shadow: 1px 1px 1px #333; +} + +label { + color: #4a6e91; + font-family: Verdana, Geneva, sans-serif; + font-weight:bold; + font-size: 12px; + text-align: right; + height: 20px; + line-height: 20px; +} + +input, textarea, select { + border: 1px solid #d9d9d9; + font-family: Verdana, Geneva, sans-serif; + font-size: 12px; + color: #4a6e91; +} + +.submit { + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:bold; + width: 100px; + border: 0; + background: #009900; + color: white; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-shadow: 2px 2px 4px #888; + -moz-box-shadow: 2px 2px 4px #888; + -webkit-box-shadow: 2px 2px 4px #888; + margin-bottom: 4px; + text-shadow: 1px 1px 1px #333; +} + +.bulletin { + width: 85%; + padding: 20px; + background: #F6F6F6; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + border: 0; + background: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#FFFFFF)); + background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%); + box-shadow: 3px 3px 10px #ccc; + -moz-box-shadow: 3px 3px 10px #ccc; + -webkit-box-shadow: 3px 3px 10px #ccc; +} + +.absences { + width: 75%; + padding: 20px; + background: #F6F6F6; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + border: 0; + background: -webkit-gradient(linear, left top, left bottom, from(#EFEFEF), to(#FFFFFF)); + background: -moz-linear-gradient(center top, #EFEFEF, #FFFFFF 100%); + box-shadow: 3px 3px 10px #ccc; + -moz-box-shadow: 3px 3px 10px #ccc; + -webkit-box-shadow: 3px 3px 10px #ccc; +} + +.info { + display: table; + width: auto; /*matching the parents div width*/ + padding: 10px; + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:bold; + border: 0; + background: #4F709F; + color: white; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-shadow: 2px 2px 4px #888; + -moz-box-shadow: 2px 2px 4px #888; + -webkit-box-shadow: 2px 2px 4px #888; + margin-bottom: 4px; + text-shadow: 1px 1px 1px #333; +} + +.alert { + display: table; + width: auto; /*matching the parents div width*/ + padding: 10px; + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:bold; + border: 0; + background: #4F709F; + color: white; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-shadow: 2px 2px 4px #888; + -moz-box-shadow: 2px 2px 4px #888; + -webkit-box-shadow: 2px 2px 4px #888; + margin-bottom: 4px; + text-shadow: 1px 1px 1px #333; +} + +.code { + border: 1px dotted black; + display: table; + width: auto; /*matching the parents div width*/ + padding: 10px; + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:light; + font-style: italic; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + margin-bottom: 4px; +} + +.nbabs { + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:bold; + color: #FF4500; +} + +.nbabsjust { + font-family: Verdana, Geneva, sans-serif; + font-size: 14px; + font-weight:bold; + color: #32CD32; +} + + diff --git a/misc/PublicationBulletins/ExemplePHP/index-abs.php b/misc/PublicationBulletins/ExemplePHP/index-abs.php new file mode 100644 index 00000000..06a28503 --- /dev/null +++ b/misc/PublicationBulletins/ExemplePHP/index-abs.php @@ -0,0 +1,761 @@ + $valeur ) { + $headers_brut .= $nom . ': ' . $valeur . "\r\n"; + } + + return $headers_brut; +} + + +function get_EtudAbs_page($nip, $dept,$beg_date) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $end_date=date("Y-m-d"); + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw, + 'beg_date' => $beg_date, + 'end_date' => $end_date); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Absences/XMLgetAbsEtud', false, $contexte ); + + return ($retour); +} + + +function get_BilletAbs_list($nip, $dept) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw, +); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Absences/XMLgetBilletsEtud', false, $contexte ); + + return ($retour); +} + + +function Get_EtudAbs_billet($nip, $dept,$begin,$end,$description) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $end_date=date("Y-m-d"); +$justified="0"; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw, + 'description' =>$description, + 'justified' =>$justified, + 'begin' => $begin, + 'end' => $end); + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Absences/AddBilletAbsence', false, $contexte ); + + return ($retour); +} + + +function get_EtudInfos_page($nip, $dept) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + + $donnees = array( + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/XMLgetEtudInfos', false, $contexte ); + + return ($retour); +} + +function get_bulletinetud_page($nip, $sem, $dept) { + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + 'formsemestre_id' => $sem, + 'version' => 'selectedevals', + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/formsemestre_bulletinetud', false, $contexte ); + + return ($retour); +} + +function get_semestre_info($sem, $dept) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'formsemestre_id' => $sem, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/XMLgetFormsemestres', false, $contexte ); + + return ($retour); +} + +function get_all_semestres($xml_data) +{ + $data = array(); + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + $sem = (array) $s['formsemestre_id']; + $data[] = $sem[0]; + } + return $data; +} + +function get_current_semestre($xml_data) +{ + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + if ($s['current'] == 1) + $sem = (array) $s['formsemestre_id']; + return ($sem[0]); + } +} + +function print_semestres_list($sems, $dept, $sem) +{ + echo 'Semestre : + +'; +} + +function print_semestre($xml_data, $sem, $dept, $show_moy=False) +{ + global $etudid; + global $nip; + global $sco_user; + global $sco_pw; + global $sco_url; + $xml = simplexml_load_string($xml_data); + $etudid= $xml->etudiant['etudid']; + + if (!$show_moy) { + echo '

      Les informations contenues dans ce tableau sont + provisoires. L'état n'a pas valeur de bulletin de notes.';} + + echo '
      Il vous appartient de contacter vos enseignants + ou votre département en cas de désaccord.

      '; + + echo '

      ' . $xml->etudiant['sexe'] . ' ' . $xml->etudiant['prenom'] . ' ' . $xml->etudiant['nom'] . '

      '; + //echo '
      '; + $retour = get_semestre_info($sem, $dept); + $xml2 = simplexml_load_string($retour); + $debut=date("Y-m-d",strtotime($xml2->formsemestre['dateord'])); + + echo ''.convertir_utf8($xml2->formsemestre['titre_num']).'
      '; + if (!$show_moy) { echo "vous avez ŕ ce jour ".convertir_utf8($xml->absences['nbabs'])." demi-journées d'absences dont ".convertir_utf8($xml->absences['nbabsjust']).' justifiées';} + echo ' +
      +
      +'; + echo ' + + + + + + + + + +'; + if ($show_moy) { + echo ''; + } + foreach ($xml->ue as $ue) { + $coef = 0; + foreach ($ue->module as $mod) { + $coef += $mod['coefficient']; + } + echo ' + + + + +'; + + if ($show_moy) { + echo ' +'; + } + else { + echo ' + +'; + } + +echo ' +'; + foreach ($ue->module as $mod) { + echo ' + + + + +'; + + if ($show_moy) { + echo ' +'; + } + else { + echo ' +'; + } + + echo ' +'; + + if (!$show_moy) { + foreach ($mod->evaluation as $eval) { + echo ' + + + + + + +'; + } + } + } + } + echo '
      UECode ModuleModuleEvaluationNote/20(Min/Max)Coef
      Moyenne générale:' . $xml->note['value'] . '('.$xml->note['min'].'/'.$xml->note['max'].')
      -' . $ue['acronyme'] . '' . $ue->note['value'] . '('.$ue->note['min'].'/'.$ue->note['max'].')' . $coef . '
      ' . $mod['code'] . '' . convertir_utf8($mod['titre']) . '' . $mod->note['value'] . '('.$mod->note['min'].'/'.$mod->note['max'].')' . $mod['coefficient'] . '
      ' . convertir_utf8($eval['description']) . '' . $eval->note['value'] . '('.$eval->note['min'].'/'.$eval->note['max'].')(' . $eval['coefficient'] . ')
      +
      +'; +$code=$xml->decision['code']; + +$date_fin=$xml->decision['date_fin']; +echo $date_fin; + + if ($show_moy) { + echo "Situation sous réserve de validation par le jury :
      ".convertir_utf8($xml->situation); + } + else{if($code!=""){echo "Situation sous réserve de validation par le jury :
      ". convertir_utf8($xml->situation);}} + + + if (!$show_moy) { +echo ' +
      +

      Cliquez ici pour afficher/masquer la liste des absences du semestre:

      '; + + $retourabs = get_EtudAbs_page($nip, $dept,$debut); + $xmlabs = simplexml_load_string($retourabs); + + + + echo ' + '; + +echo ' +
      '; + +echo ' +

      Déclaration des motifs d'absences:

      '; + + echo ' + + + + + + + + + +
      Date et heure de début: + + + +
      Date et heure de fin: + + + +
      + + Motif: + +
      + + +
      +Veuillez indiquer les matičres et enseignants concernés (pour les absences de courte durée).
      Apportez par ailleurs le numéro du billet affiché dans le tableau ci aprčs ainsi que vos justificatifs éventuels au secrétariat du département.
      + + + +
      + +
      '; + + + +if (isset($_POST["begin"]) and isset($_POST["end"]) and isset($_POST["begtime"]) and isset($_POST["endtime"]) and isset($_POST["description"]) and $_POST["end"]>=$_POST["begin"]){ +$date1 = new DateTime($_POST["begin"]); +$date1->setTime(intval(substr($_POST["begtime"],0,2)), intval(substr($_POST["begtime"],-2))); + +$date2 = new DateTime($_POST["end"]); +$date2->setTime(intval(substr($_POST["endtime"],0,2)), intval(substr($_POST["endtime"],-2))); +Get_EtudAbs_billet($nip, $dept,$date1->format('Y-m-d H:i:s') , $date2->format('Y-m-d H:i:s') , $_POST["description"]);} + +echo ' +

      Billets d'absences déposés:

      '; + $retourbillets = get_BilletAbs_list($nip, $dept); + $xmlbillets = simplexml_load_string($retourbillets); + + echo ' + + + + + + +'; + +foreach ($xmlbillets->row as $billet) { + echo " +"; +} + +echo '
      Billet Du Au MotifSituation
      ". $billet->billet_id['value'] . ''. convertir_utf8($billet->abs_begin_str['value']). ' ' . convertir_utf8($billet->abs_end_str['value']) . ' ' . convertir_utf8($billet->description['value']) .' ' . convertir_utf8($billet->etat_str['value']) ."
      +
      '; + + +}} + + + + +function get_dept($nip) +{ + global $sco_url; + $dept = file_get_contents( $sco_url . 'get_etud_dept?code_nip=' . $nip); + return ($dept); +} + + +// function pour la recuperation des infos ldap +function search_results($info) { + foreach ($info as $inf) { + if (is_array($inf)) { + foreach ($inf as $key => $in) { + if ((count($inf[$key]) - 1) > 0) { + if (is_array($in)) { + unset($inf[$key]["count"]); + } + $results[$key] = $inf[$key]; + } + } + } + } + $results["dn"] = explode(',', $info[0]["dn"]); + return $results; +} + + +// Programme principal + + +echo ' + +Bulletin de notes + + + + + + + + + + '; + +echo " + + + + +"; + + +$user = $_SERVER['PHP_AUTH_USER']; +//echo 'USER: '.$user."\n"."
      "; + +//$user = "ei121713"; +//echo "On triche USER = ".$user."\n"."
      "; + +$ds = ldap_connect("ldap://ldap"); +if ($ds) { + $r = ldap_bind($ds); + $sr = ldap_search($ds, "ou=people,dc=univ-lehavre,dc=fr", "(&(objectClass=ulhEtudiant)(uid=$user))"); + $info = ldap_get_entries($ds, $sr); + + //echo $info["count"]." IS Search Result(s) for \"".$user."\"\n"; + $results = search_results($info); + // si pas de reponse de l a nnuaire, ce n est pas un etudiant + if ($info["count"] == 0 ) { + echo ' + + getEtud + + +

      Service de consultation des notes

      +
      + Il faut être etudiant de l'IUT pour accéder à ses notes. +
      + + '; + } else { + foreach ($results as $key => $result) { + if ($key == 'supannetuid' ) { + //echo " * ".$key." : \n"; + if (is_array($result)){ + foreach($result as $res){ + //echo " ".$res."\n"; + } + } + //echo "
      "; + $nip=$res; + } + } + } + ldap_close($ds); +} +// Login information of a scodoc user that can access notes +$sco_user = 'lecturenotes'; +$sco_pw = 'XXXXXXX'; +$sco_url = 'https://scodoc.XXXXX.fr/ScoDoc/'; + +$user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0'; + +echo '
      '; +if ($nip) { +$dept = get_dept($nip); +if ($dept) { + $retour = get_EtudInfos_page($nip, $dept); + $sems = get_all_semestres($retour); + $sem_current = get_current_semestre($retour); + if (isset($_POST["sem"])) { + $sem = $_POST["sem"]; + } + else { + $sem = $sem_current; + } + print_semestres_list($sems, $dept, $sem); + $retour = get_bulletinetud_page($nip, $sem, $dept); + if ($sem == $sem_current) { + print_semestre($retour, $sem, $dept, False); + } + else { + print_semestre($retour, $sem, $dept, True); + } + $erreur=0; // Tout est OK +} +else { + echo "Numéro étudiant inconnu : " . $nip . ". Contactez votre Chef de département."; +} +} + +echo '
      '; + + +echo ' + +'; + + +?> diff --git a/misc/PublicationBulletins/ExemplePHP/index.php b/misc/PublicationBulletins/ExemplePHP/index.php new file mode 100644 index 00000000..2ecefa6c --- /dev/null +++ b/misc/PublicationBulletins/ExemplePHP/index.php @@ -0,0 +1,320 @@ + $valeur ) { + $headers_brut .= $nom . ': ' . $valeur . "\r\n"; + } + + return $headers_brut; +} + +function get_EtudInfos_page($nip, $dept) +{ + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requête + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/XMLgetEtudInfos', false, $contexte ); + + return ($retour); +} + +function get_bulletinetud_page($nip, $sem, $dept) { + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + 'formsemestre_id' => $sem, + 'version' => 'selectedevals', + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requête + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/formsemestre_bulletinetud', false, $contexte ); + + return ($retour); +} + +function get_semestre_info($sem, $dept) +{ + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'formsemestre_id' => $sem, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requête + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/XMLgetFormsemestres', false, $contexte ); + + return ($retour); +} + +function get_all_semestres($xml_data) +{ + $data = array(); + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + $sem = (array) $s['formsemestre_id']; + $data[] = $sem[0]; + } + return $data; +} + +function get_current_semestre($xml_data) +{ + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + if ($s['current'] == 1) + $sem = (array) $s['formsemestre_id']; + return ($sem[0]); + } +} + +function print_semestres_list($sems, $dept, $sem) +{ + echo 'Semestre : + +'; +} + +function print_semestre($xml_data, $sem, $dept, $show_moy=False) +{ + $xml = simplexml_load_string($xml_data); + echo '

      ' . $xml->etudiant['sexe'] . ' ' . $xml->etudiant['prenom'] . ' ' . $xml->etudiant['nom'] . '

      '; + echo '
      +'; + $retour = get_semestre_info($sem, $dept); + $xml2 = simplexml_load_string($retour); + echo $xml2->formsemestre['titre_num']; + echo ' +
      +
      +'; + echo ' + + + + + + + +'; + if ($show_moy) { + echo ''; + } + foreach ($xml->ue as $ue) { + $coef = 0; + foreach ($ue->module as $mod) { + $coef += $mod['coefficient']; + } + echo ' + + + +'; + + if ($show_moy) { + echo ' +'; + } + else { + echo ' +'; + } + +echo ' +'; + foreach ($ue->module as $mod) { + echo ' + + + +'; + + if ($show_moy) { + echo ' +'; + } + else { + echo ' +'; + } + + echo ' +'; + + if (!$show_moy) { + foreach ($mod->evaluation as $eval) { + echo ' + + + + + +'; + } + } + } + } + echo '
      UEModuleEvaluationNote/20Coef
      Moyenne générale:' . $xml->note['value'] . '
      -' . $ue['acronyme'] . '' . $ue->note['value'] . '' . $coef . '
      ' . $mod['code'] . '' . $mod->note['value'] . '' . $mod['coefficient'] . '
      ' . $eval['description'] . '' . $eval->note['value'] . '
      +
      +'; + if ($show_moy) { + echo $xml->situation; + } +} + +function get_dept($nip) +{ + global $sco_url; + $dept = file_get_contents( $sco_url . 'get_etud_dept?code_nip=' . $nip); + return ($dept); +} + + + +echo ' + +Bulletin de notes + + + + + + +'; + +echo '
      '; + +$dept = get_dept($nip); +if ($dept) { + $retour = get_EtudInfos_page($nip, $dept); + $sems = get_all_semestres($retour); + $sem_current = get_current_semestre($retour); + if (isset($_POST["sem"])) { + $sem = $_POST["sem"]; + } + else { + $sem = $sem_current; + } + print_semestres_list($sems, $dept, $sem); + $retour = get_bulletinetud_page($nip, $sem, $dept); + if ($sem == $sem_current) { + print_semestre($retour, $sem, $dept, False); + } + else { + print_semestre($retour, $sem, $dept, True); + } + $erreur=0; // Tout est OK +} +else { + echo "Numéro étudiant inconnu : " . $nip . ". Contactez votre Chef de département."; +} + +echo '
      '; + +echo ' +'; +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/Acces aux notes etudiants.pdf b/misc/PublicationBulletins/Portail-LeHavre/Acces aux notes etudiants.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7503b0eef9c1a54a7f149efa210e5b26896a4298 GIT binary patch literal 122618 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 literal 0 HcmV?d00001 diff --git a/misc/PublicationBulletins/Portail-LeHavre/Billets_etudiants.pdf b/misc/PublicationBulletins/Portail-LeHavre/Billets_etudiants.pdf new file mode 100644 index 0000000000000000000000000000000000000000..72d824e870138ce389a2165df541a675efa8a4a0 GIT binary patch literal 203646 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 literal 0 HcmV?d00001 diff --git a/misc/PublicationBulletins/Portail-LeHavre/PDF_Billet.php b/misc/PublicationBulletins/Portail-LeHavre/PDF_Billet.php new file mode 100755 index 00000000..8bf7a7bf --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/PDF_Billet.php @@ -0,0 +1,151 @@ +AddPage(); + + +$PDF->SetFont('Arial','B',16); + +$fill = 1; +$PDF->SetFillColor(224,228,200); +$PDF->SetTextColor(0,0,0); + +$PDF->Text(20,15,"$semestre"); +$PDF->SetXY(20,20); +$PDF->MultiCell(180,5,"(du $debutsemestre au $finsemestre)",0,'C',0); + + +$PDF->SetXY(20,30); +$PDF->SetFont('Arial','',10); +$PDF->MultiCell(180,5,"Formulaire ŕ compléter par l'étudiant. \n A faire signer par les enseignants et ŕ déposer au secrétariat sans attendre avec les justificatifs s'il y a lieu.",0,'C',0); +$PDF->SetFont('Arial','B',12); + +$PDF->Text(15,40,"$trait"); +$PDF->Text(20,50,"$etudiant"); +$PDF->Text(100,55,"Groupe (TD/TP): $groupe"); +$PDF->Text(20,55,"$numbillet",0,0,'L'); +$PDF->Text(15,60,"$trait"); +$PDF->SetFont('Arial','',11); +$PDF->Text(20,70,"$dates"); + +$PDF->Text(20,75,"Justificatif apporté: Oui Non"); +$PDF->SetFont('Arial','B',11); +$PDF->Text(20,80,"Motif: "); +$PDF->SetFont('Arial','',10); +$PDF->SetXY(20,82); +$PDF->MultiCell(180,5,"$motif",1,'L',0); +$PDF->SetXY(20,122); + +// Définition des propriétés du tableau. +$larccel=38; +$larccel2=55; +//$R=151;$G=190;$B=13; +$R=224;$G=228;$B=216; + +$proprietesTableau = array( + 'TB_ALIGN' => 'L', + 'L_MARGIN' => 1, + 'BRD_COLOR' => array(0,0,0), + 'BRD_SIZE' => '0.5', + ); + +// Definition des proprietes du header du tableau. +$proprieteHeader = array( + 'T_COLOR' => array(0,0,0), + 'T_SIZE' => 10, + 'T_FONT' => 'Arial', + 'T_ALIGN' => 'C', + 'V_ALIGN' => 'T', + 'T_TYPE' => 'B', + 'LN_SIZE' => 7, + 'BG_COLOR_COL0' => array($R, $G, $B), + 'BG_COLOR' => array($R, $G, $B), + 'BRD_COLOR' => array(0,0,0), + 'BRD_SIZE' => 0.2, + 'BRD_TYPE' => '1', + 'BRD_TYPE_NEW_PAGE' => '', + ); + +// Contenu du header du tableau. +$contenuHeader = array( + $larccel, $larccel, $larccel2, $larccel2, + "Matiere","Enseignant","Emargement Enseignant","Observations" + ); + + // Contenu du tableau. +$contenuTableau = array( + "", "", "","", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "","", + "", "", "", "", + 0, 0, 0, "", + ); +// Definition des propriétés du reste du contenu du tableau. +$proprieteContenu = array( + 'T_COLOR' => array(0,0,0), + 'T_SIZE' => 10, + 'T_FONT' => 'Arial', + 'T_ALIGN_COL0' => 'L', + 'T_ALIGN' => 'R', + 'V_ALIGN' => 'M', + 'T_TYPE' => '', + 'LN_SIZE' => 6, + 'BG_COLOR_COL0' => array($R, $G, $B), + 'BG_COLOR' => array(255,255,255), + 'BRD_COLOR' => array(0,0,0), + 'BRD_SIZE' => 0.2, + 'BRD_TYPE' => '1', + 'BRD_TYPE_NEW_PAGE' => '', + ); + + +$PDF->drawTableau($PDF, $proprietesTableau, $proprieteHeader, $contenuHeader, $proprieteContenu, $contenuTableau); +$PDF->Text(15,180,"Indiquez ci-dessous les Devoirs surveillés, contrôles TP, interrogations écrites concernés:"); +$PDF->SetXY(20,182); +$PDF->drawTableau($PDF, $proprietesTableau, $proprieteHeader, $contenuHeader, $proprieteContenu, $contenuTableau); +$PDF->SetXY(20,235); +$PDF->SetFont('Arial','I',10); +$PDF->MultiCell(180,3,'Je déclare avoir fait, ou faire expressément, le nécessaire pour rattraper tous les cours cités ci-dessus, tant au niveau des documents distribués, du déroulement des séances de travail et d’éventuelles évaluations. +La recevabilité de l’absence sera appréciée par l’équipe de direction.',0,'L',0); +$PDF->SetFont('Arial','B',11); +$PDF->SetFillColor(224,228,216); +$PDF->SetXY(20,260); +$PDF->MultiCell(180,5,"Partie réservée ŕ l'administration: +Absence justifiée : Oui Non Autorisé ŕ rattraper les contrôles: Oui Non",1,'C',1); +$PDF->Text(15,250,"Signature du Directeur des études:"); +$PDF->Text(125,250,"Signature de l'étudiant:"); +$PDF->Text(80,290,"Imprimé le ".date("d/n/Y ŕ H:i")); + + +$PDF->Ln(); +$PDF->Output(); +$PDF->Output($nom.".pdf", "D"); + + +?> +

       

      diff --git a/misc/PublicationBulletins/Portail-LeHavre/README.txt b/misc/PublicationBulletins/Portail-LeHavre/README.txt new file mode 100644 index 00000000..7a757dcd --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/README.txt @@ -0,0 +1,6 @@ +Portail-LeHavre + +Auteurs: Code contribué par Yann Leboulanger (Université Paris 10), Juin 2013 +Modifié par D. SOUDIERE avec le concours de Catherine Hatinguais + + diff --git a/misc/PublicationBulletins/Portail-LeHavre/css/radar_bulletin.css b/misc/PublicationBulletins/Portail-LeHavre/css/radar_bulletin.css new file mode 100644 index 00000000..a70e2517 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/css/radar_bulletin.css @@ -0,0 +1,90 @@ +/* # -*- mode: css -*- + ScoDoc, (c) Emmanuel Viennet 1998 - 2012 + */ + +div#radar_bulletin { + width: 500px; + margin-left: auto ; + margin-right: auto ; +} + +.radar { + font-family: sans-serif; + font-size: 12pt; +} + +.radar rect { + stroke: white; + fill: steelblue; +} + +path { + fill:none; + stroke:gray; +} + +path.radarnoteslines { + stroke: darkblue; + stroke-width: 1.5px; +} + +path.radarmoylines { + stroke: rgb(20,90,50);; + stroke-width: 1.25px; + stroke-dasharray: 8, 4; +} + +line.radarrad { + stroke: rgb(150, 150, 150); +} + +circle.radar_disk_tick { + stroke-width: 1; + stroke : rgb(150,150,150); + fill : none; +} +circle.radar_disk_red { + stroke-width: 0px; + fill: #FFC0CB; + fill-opacity: 0.5; +} + +path#radar_disk_tic_8 { + fill: #FF493C; + fill-opacity: 0.5; +} + +path#radar_disk_tic_10 { + fill: #FFC0CB; + fill-opacity: 0.5; + stroke-width: 1.5px; + stroke: #F44646; +} + +path#radar_disk_tic_20 { + fill: grey; + fill-opacity: 0.1; +} + +.radar_center_mark { + stroke: red; + stroke-width : 2; + fill: red; +} + +rect.radartip { + fill: #F5FB84; + font-size: 10pt; +} + +text.textaxis { + fill: red; + font-size: 8pt; + text-anchor: end; +} + +text.note_label { + fill: darkblue; + font-size: 10pt; + font-weight: bold; +} diff --git a/misc/PublicationBulletins/Portail-LeHavre/css/scodoc.css b/misc/PublicationBulletins/Portail-LeHavre/css/scodoc.css new file mode 100644 index 00000000..bc50a343 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/css/scodoc.css @@ -0,0 +1,1900 @@ +/* # -*- mode: css -*- + ScoDoc, (c) Emmanuel Viennet 1998 - 2012 + */ + +html,body { + margin:0; + padding:0; + width: 100%; + background-color: rgb(238,238,255); +} + +a:link { color: #008080 } +a:visited { color : #000080 } + + +@media print { +.noprint { + display:none; +} +} + +h1, h2, h3 { + font-family : arial, verdana, sans-serif ; +} + +/* ----- bandeau haut ------ */ +span.bandeaugtr { + width: 100%; + margin: 0; + border-width: 0; + padding-left: 160px; +/* background-color: rgb(17,51,85); */ +} +@media print { +span.bandeaugtr { + display:none; +} +} +tr.bandeaugtr { +/* background-color: rgb(17,51,85); */ + color: rgb(255,215,0); + /* font-style: italic; */ + font-weight: bold; + border-width: 0; + margin: 0; +} + +#authuser { + margin-top: 16px; +} + +#authuserlink { + color: rgb(255,0,0); + text-decoration: none; +} +#authuserlink:hover { + text-decoration: underline; +} +#deconnectlink { + font-size: 75%; + font-style: normal; + color: rgb(255,0,0); + text-decoration: underline; +} + +/* ----- page content ------ */ + +div.about-logo { + text-align: center; + padding-top: 10px; +} + + +div.head_message { + margin-top: 2px; + margin-bottom: 8px; + padding: 5px; + margin-left: auto; + margin-right: auto; + background-color: #ffff73; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; + font-family : arial, verdana, sans-serif ; + font-weight: bold; + width: 40%; + text-align: center; +} + +div.passwd_warn { + font-weight: bold; + font-size: 200%; + background-color: #feb199; + width: 80%; + height: 200px; + text-align: center; + padding: 20px; + margin-left: auto; + margin-right: auto; + margin-top: 10px; +} + + +P.footer { + font-size: 80%; + color: rgb(60,60,60); + margin-top: 10px; + border-top: 1px solid rgb(60,60,60); +} + +/* ---- (left) SIDEBAR ----- */ + +div.sidebar { + position: absolute; + top: 5px; left: 5px; + width: 130px; + border: black 1px; + /* debug background-color: rgb(245,245,245); */ +} +@media print { +div.sidebar { + display:none; +} +} +a.sidebar:link { + color:#358; + text-decoration:none; +} +a.sidebar:visited { + color: #358; + text-decoration: none; +} +a.sidebar:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +a.scodoc_title { + color: rgb(102,102,102); + font-family: arial,verdana,sans-serif; + font-size: large; + font-weight: bold; + text-transform: uppercase; + text-decoration: none; +} +h2.insidebar { + color: rgb(102,102,102); + font-weight: bold; + font-size: large; + margin-bottom: 0; +} + +h3.insidebar { + color: rgb(102,102,102); + font-weight: bold; + font-size: medium; + margin-bottom: 0; + margin-top: 0; +} + +ul.insidebar { + padding-left: 1em; + list-style: circle; +} + + +.etud-insidebar { + font-size: small; + background-color: rgb(220,220,220); + width: 100%; + -moz-border-radius: 6px; + -khtml-border-radius: 6px; + border-radius: 6px; +} + +.etud-insidebar h2 { + color: rgb(153,51,51); + font-size: medium; +} + + +.etud-insidebar ul { + padding-left: 1.5em; + margin-left: 0; +} + +div.logo-insidebar { + padding-left: 1em; +} + +div.etud_info_div { + border: 4px solid gray; + height: 90px; + background-color: #f7f7ff; +} + +div.eid_left { + padding: 4px; + border: 0px; + vertical-align: top; + margin-right: 100px; +} + +span.eid_right { + padding: 0px; + border: 0px; + position: absolute; + right: 5px; + top: 5px; +} + +table.listesems th { + text-align: left; + padding-top: 0.5em; + padding-left: 0.5em; +} + +table.listesems td.semicon { + padding-left: 1.5em; +} + +table.listesems tr.firstsem td { + padding-top: 0.8em; +} + +h2.listesems { + padding-bottom: 0px; + margin-bottom: 0px; +} + +/* ----- Liste des news ----- */ + +div.news { + margin-top: 1em; + margin-bottom: 0px; + margin-right: 16px; + margin-left: 16px; + padding: 0.5em; + background-color: #F5F6CE; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; +} + +span.newstitle { + font-family : arial, verdana, sans-serif ; + font-weight: bold; +} + +ul.newslist { + padding-left: 1em; + padding-bottom: 0em; + list-style: circle; +} + +span.newsdate { + padding-right: 2em; +} + +span.newstext { + font-style: italic; +} + +/* --- infos sur premiere page Sco --- */ +div.scoinfos { + margin-top: 0.5em; + margin-bottom: 0px; + padding: 2px; + padding-bottom: 0px; + background-color: #F4F4B2; +} + +/* ----- fiches etudiants ------ */ + +div.ficheEtud { + background-color: #f5edc8; /* rgb(255,240,128); */ + border: 1px solid gray; + width: 910px; + padding: 10px; + margin-top: 10px; +} + +div.menus_etud { + position: absolute; + margin-left: 1px; + margin-top: 1px; +} +div.ficheEtud h2 { + padding-top: 10px; +} + +div.code_nip { + padding-top: 10px; + font-family: "Andale Mono", "Courier"; +} + +div.fichesituation { + background-color: rgb( 231, 234, 218 ); /* E7EADA */ + margin: 0.5em 0 0.5em 0; + padding: 0.5em; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; +} + +div.ficheadmission { + background-color: rgb( 231, 234, 218 ); /* E7EADA */ + margin: 0.5em 0 0.5em 0; + padding: 0.5em; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; +} + +div.etudarchive ul { + padding:0; + margin:0; + margin-left: 1em; + list-style-type:none; +} + +div.etudarchive ul li { + background-image: url(/ScoDoc/static/icons/bullet_arrow.png); + background-repeat: no-repeat; + background-position: 0 .4em; + padding-left: .6em; +} +div.etudarchive ul li.addetudarchive { + background-image: url(/ScoDoc/static/icons/bullet_plus.png); + padding-left: 1.2em +} +span.etudarchive_descr { + margin-right: .4em; +} +span.deletudarchive { + margin-left: 0.5em; +} +div.fichedebouche { + background-color: rgb( 0, 100, 0 ); + color: gold; + margin: 0.5em 0 0.5em 0; + padding: 0.5em; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; +} + +span.debouche_tit { + font-weight: bold; + padding-right: 1em; +} + +div.ficheinscriptions { + background-color: #eae3e2; /* was EADDDA */ + margin: 0.5em 0 0.5em 0; + padding: 0.5em; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; +} + +.ficheinscriptions a.sem { + text-decoration: none; + font-weight: bold; + color: blue; +} + +.ficheinscriptions a.sem:hover { + color: red; +} + + +td.photocell { + padding-left: 32px; +} + +div.fichetitre { + font-weight: bold; +} + +td.fichetitre2 { + font-weight: bold; + vertical-align: top; +} + +td.fichetitre2 .formula { + font-weight: normal; + color: rgb(0,64,0); + border: 1px solid red; + padding-left: 1em; + padding-right: 1em; + padding-top: 3px; + padding-bottom: 3px; + margin-right: 1em; +} + +span.formula { + font-size: 80%; + font-family: Courier, monospace; + font-weight: normal; +} + +td.fichetitre2 .fl { + font-weight: normal; +} + +.ficheannotations { + background-color: #ddffdd; + margin: 0.5em 0 0.5em 0; + padding: 0.5em; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; +} + +td.annodel { +} + +/* Page accueil Sco */ +span.infostitresem { + font-weight: normal; +} +span.linktitresem { + font-weight: normal; +} +span.linktitresem a:link {color: red;} +span.linktitresem a:visited {color: red;} + +.listegroupelink a:link { color: blue; } +.listegroupelink a:visited { color: blue; } +.listegroupelink a:hover { color: red; } + +a.stdlink, a.stdlink:visited { + color: blue; + text-decoration: underline; +} +a.stdlink:hover { + color: red; + text-decoration: underline; +} + +/* markup non semantique pour les cas simples */ + +.fontred { color: red; } +.fontorange { color: rgb(215, 90, 0); } +.fontitalic { font-style: italic; } +.redboldtext { + font-weight: bold; + color: red; +} + +.greenboldtext { + font-weight: bold; + color: green; +} + +a.redlink { + color: red; +} +a.redlink:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +a.discretelink { + color: black; + text-decoration: none; +} + +a.discretelink:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +.rightcell { + text-align: right; +} + +.rightjust { + padding-left: 2em; +} + +.centercell { + text-align: center; +} + +.help { + font-style: italic; +} + +p.indent { + padding-left: 2em; +} + +.blacktt { + font-family: Courier, monospace; + font-weight: normal; + color: black; +} + +p.msg { + color: red; + font-weight: bold; + border: 1px solid blue; + background-color: rgb(140,230,250); + padding: 10px; +} + +table.tablegrid { + border-color: black; + border-width: 0 0 1px 1px; + border-style: solid; + border-collapse: collapse; +} +table.tablegrid td, table.tablegrid th { + border-color: black; + border-width: 1px 1px 0 0; + border-style: solid; + margin: 0; + padding-left: 4px; + padding-right: 4px; +} + +/* ----- Notes ------ */ +a.smallbutton { + border-width: 0; + margin: 0; + margin-left: 2px; + text-decoration: none; +} +span.evallink { + font-size: 80%; + font-weight: normal; +} +.boldredmsg { + color: red; + font-weight: bold; +} + +tr.etuddem td { + color: rgb(100,100,100); + font-style: italic; +} + +td.etudabs, td.etudabs a.discretelink, tr.etudabs td.moyenne a.discretelink { + color: rgb(180,0,0); +} +tr.moyenne td { + font-weight: bold; +} + +table.notes_evaluation th.eval_complete a.sortheader { + color: green; +} +table.notes_evaluation th.eval_incomplete a.sortheader { + color: red; +} +table.notes_evaluation th.eval_attente a.sortheader { + color: rgb(215, 90, 0);; +} +table.notes_evaluation tr td a.discretelink:hover { + text-decoration: none; +} +table.notes_evaluation tr td.tdlink a.discretelink:hover { + color: red; + text-decoration: underline; +} +table.notes_evaluation tr td.tdlink a.discretelink, table.notes_evaluation tr td.tdlink a.discretelink:visited { + color: blue; + text-decoration: underline; +} + +table.notes_evaluation tr td { + padding-left: 0.5em; + padding-right: 0.5em; +} + +div.notes_evaluation_stats { + margin-top: -15px; +} + +.eval_description p { + margin-bottom: 5px; + margin-top: 5px; +} +span.eval_info { + font-style: italic; +} +span.eval_complete { + color: green; +} +span.eval_incomplete { + color: red; +} +span.eval_attente { + color: rgb(215, 90, 0); +} + +table.tablenote { + border-collapse: collapse; + border: 2px solid blue; +/* width: 100%;*/ + margin-bottom: 20px; + margin-right: 20px; +} +table.tablenote th { + padding-left: 1em; +} +.tablenote a { + text-decoration: none; + color: black; +} +.tablenote a:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +table.tablenote_anonyme { + border-collapse: collapse; + border: 2px solid blue; +} + +tr.tablenote { + border: solid blue 1px; +} +td.colnote { + text-align : right; + padding-right: 0.5em; + border: solid blue 1px; +} +td.colnotemoy { + text-align : right; + padding-right: 0.5em; + font-weight: bold; +} +td.colcomment, span.colcomment { + text-align: left; + padding-left: 2em; + font-style: italic; + color: rgb(80,100,80); +} + +h2.formsemestre, .gtrcontent h2 { + margin-top: 2px; + font-size: 130%; +} + +.formsemestre_page_title table.semtitle, .formsemestre_page_title table.semtitle td { + padding: 0px; + margin-top: 0px; + margin-bottom: 0px; + border-width: 0; + border-collapse: collapse; +} +.formsemestre_page_title table.semtitle { + /* width: 100%; */ +} + +.formsemestre_page_title { + width: 100%; + padding-top:5px; + padding-bottom: 10px; +} + +.formsemestre_page_title table.semtitle td.infos table { + padding-top: 10px; +} +.formsemestre_menubar { + border-top: 3px solid #67A7E3; + background-color: #D6E9F8; + margin-top: 8px; +} + +.formsemestre_menubar .barrenav ul li a.menu { + font-size: 12px; +} + +.formsemestre_page_title .infos span { + padding-right: 25px; +} + +.formsemestre_page_title span.semtitle { + font-size: 110%; +} +.formsemestre_page_title span.resp { + color: red; + font-weight: bold; +} +.formsemestre_page_title span.nbinscrits { + text-align: right; + font-weight: bold; + padding-right: 1px; +} +.formsemestre_page_title span.lock { + +} + +div.formsemestre_status { + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; + padding: 2px 6px 2px 16px; + margin-right: 10px; +} + +table.formsemestre_status { + border-collapse: collapse; +} +tr.formsemestre_status { background-color: rgb(90%,90%,90%); } +tr.formsemestre_status_green { background-color: #EFF7F2; } +tr.formsemestre_status_ue { background-color: rgb(90%,90%,90%); } +table.formsemestre_status td { + border-top: 1px solid rgb(80%,80%,80%); + border-bottom: 1px solid rgb(80%,80%,80%); + border-left: 0px; +} +table.formsemestre_status td.evals, table.formsemestre_status th.evals, table.formsemestre_status td.resp, table.formsemestre_status th.resp { + padding-left: 1em; +} + +table.formsemestre_status th { + font-weight: bold; + text-align: left; +} +th.formsemestre_status_inscrits { + font-weight: bold; + text-align: center; +} +td.formsemestre_status_code { + width: 2em; +} +a.formsemestre_status_link { + text-decoration:none; + color: black; +} +a.formsemestre_status_link:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +td.formsemestre_status_inscrits { + text-align: center; +} +td.formsemestre_status_cell { + white-space: nowrap; +} + +span.status_ue_acro { font-weight: bold; } +span.status_ue_title { font-style: italic; padding-left: 1cm;} + +table.formsemestre_inscr td { + padding-right: 1.25em; +} + +ul.ue_inscr_list li span.tit { + font-weight: bold; +} +ul.ue_inscr_list li.tit { + padding-top: 1ex; +} +ul.ue_inscr_list li.etud { + padding-top: 0.7ex; +} + +/* Liste des groupes sur tableau bord semestre */ +.formsemestre_status h3 { + border: 0px solid black; + margin-bottom: 5px; +} + +#grouplists h4 { + font-style: italic; + margin-bottom: 0px; + margin-top: 5px; +} + +#grouplists table { + //border: 1px solid black; + border-spacing: 1px; +} + +#grouplists td { +} + + +/* Modules */ +div.moduleimpl_tableaubord { + padding: 7px; + border: 2px solid gray; +} + +table.moduleimpl_evaluations { + width: 100%; + border-spacing: 0px; +} + +th.moduleimpl_evaluations { + font-weight: normal; + text-align: left; + color: red; +} + +th.moduleimpl_evaluations a, th.moduleimpl_evaluations a:visited { + font-weight: normal; + color: red; + text-decoration: none; +} +th.moduleimpl_evaluations a:hover { + text-decoration: underline; +} + +tr.moduleimpl_evaluation_row { +} + +td.moduleimpl_evaluation_row { +} + +tr.mievr { + background-color:#eeeeee; +} + +tr.mievr_rattr { + background-color:#dddddd; +} +span.mievr_rattr { + font-weight: bold; + color: blue; + margin-left: 2em; + border: 1px solid red; + padding: 1px 3px 1px 3px; +} + +tr.mievr td.mievr_tit { + font-weight: bold; + background-color: #cccccc; +} +tr.mievr td { + text-align: left; + background-color:white; +} +tr.mievr th { + background-color:white; +} +tr.mievr td.mievr { + width: 90px; +} +tr.mievr td.mievr_menu { + width: 110px; +} +tr.mievr td.mievr_dur { + width: 60px; +} +tr.mievr td.mievr_coef { + width: 60px; +} +tr.mievr td.mievr_nbnotes { + width: 90px; +} +tr td.mievr_grtit { + vertical-align: top; + text-align: right; + font-weight: bold; +} +span.mievr_lastmodif { + padding-left: 2em; + font-weight: normal; + font-style: italic; +} +a.mievr_evalnodate { + color: rgb(215, 90, 0); + font-style: italic; + text-decoration: none; +} +a.mievr_evalnodate:hover { + color: rgb(153,51,51); + text-decoration: underline; +} +span.evalindex { + font-weight: normal; + padding-left: 4em; +} + +/* Formulaire edition des partitions */ +form#editpart table { + border: 1px solid gray; + border-collapse: collapse; +} +form#editpart tr.eptit th { + font-size: 110%; + border-bottom: 1px solid gray; +} +form#editpart td { + border-bottom: 1px dashed gray; +} +form#editpart table td { + padding-left: 1em; +} +form#editpart table td.epnav { + padding-left: 0; +} + +/* Liste des formations */ +ul.notes_formation_list { + list-style-type: none; + font-size: 110%; +} +li.notes_formation_list { + padding-top: 10px; +} + +/* Presentation formation (ue_list) */ +div.formation_descr { + background-color: rgb(250,250,240); + padding-left: 0.7em; + margin-right: 1em; +} +div.formation_descr span.fd_t { + font-weight: bold; + margin-right: 1em; +} + +div.ue_list_tit { + font-weight: bold; + margin-top: 5px; +} + +ul.notes_ue_list { + background-color: rgb(240,240,240); + font-weight: bold; + margin-top: 4px; + margin-right: 1em; +} + +span.ue_code { + font-family: Courier, monospace; + font-weight: normal; + color: black; + font-size: 80%; +} + +span.ue_type { + color: green; + margin-left: 1.5em; + margin-right: 1.5em; +} + +ul.notes_matiere_list { + background-color: rgb(220,220,220); + font-weight: normal; + font-style: italic; +} + +ul.notes_module_list { + background-color: rgb(210,210,210); + font-weight: normal; + font-style: normal; +} + +.notes_ue_list a.discretelink, .notes_ue_list a.stdlink { + color: #001084; + text-decoration: underline; +} +.notes_ue_list span.locked { + font-weight: normal; +} + +.notes_ue_list a.smallbutton img { + position: relative; + top: 2px; +} + +div#ue_list_code { + background-color: rgb(220,220,220); + font-size: small; + padding-left: 4px; + padding-bottom: 1px; + margin-top: 10px; + margin: 3ex; +} + +ul.notes_module_list { + list-style-type: none; +} + +div#ue_list_etud_validations { + background-color: rgb(220,250,220); + padding-left: 4px; + padding-bottom: 1px; + margin: 3ex; +} +div#ue_list_etud_validations span { + font-weight: bold; +} + +span.ue_share { + font-weight: bold; +} + +div.ue_warning { + border: 1px solid red; + background-color: rgb(250,220,220); + margin: 3ex; + padding-left: 1ex; + padding-right: 1ex; +} + +div.ue_warning span:before { + content: url(/ScoDoc/static/icons/warning_img.png); + vertical-align: -80%; +} + +div.ue_warning span { + font-weight: bold; +} + +/* tableau recap notes */ +table.notes_recapcomplet { + border: 2px solid blue; + border-spacing: 0px 0px; + border-collapse: collapse; + white-space: nowrap; +} +tr.recap_row_even { + background-color: rgb(210,210,210); +} +@media print { +tr.recap_row_even { /* bordures noires pour impression */ + border-top: 1px solid black; + border-bottom: 1px solid black; +} +} +tr.recap_row_moy { + border-top: 1px solid blue; + font-weight: bold; +} +tr.recap_row_min, tr.recap_row_max { + font-weight: normal; + font-style: italic; +} +tr.recap_row_max { + border-bottom: 1px solid blue; +} +td.recap_tit { + font-weight: bold; + text-align: left; + padding-right: 1.2em; +} +td.recap_tit_ue { + font-weight: bold; + text-align: left; + padding-right: 1.2em; + padding-left: 2px; + border-left: 1px solid blue; +} +td.recap_col { + padding-right: 1.2em; + text-align: left; +} +td.recap_col_moy { + padding-right: 1.5em; + text-align: left; + font-weight: bold; + color: rgb(80,0,0); +} +td.recap_col_moy_inf { + padding-right: 1.5em; + text-align: left; + font-weight: bold; + color: rgb(255,0,0); +} +td.recap_col_ue { + padding-right: 1.2em; + padding-left: 4px; + text-align: left; + font-weight: bold; + border-left: 1px solid blue; +} +td.recap_col_ue_inf { + padding-right: 1.2em; + padding-left: 4px; + text-align: left; + font-weight: bold; + color: rgb(255,0,0); + border-left: 1px solid blue; +} +td.recap_col_ue_val { + padding-right: 1.2em; + padding-left: 4px; + text-align: left; + font-weight: bold; + color: rgb(0,140,0); + border-left: 1px solid blue; +} +/* noms des etudiants sur recap complet */ +table.notes_recapcomplet a:link, table.notes_recapcomplet a:visited { + text-decoration: none; + color: black; +} + +table.notes_recapcomplet a:hover { + color: red; + text-decoration: underline; +} + +/* bulletin */ +table.notes_bulletin { + border-collapse: collapse; + border: 2px solid rgb(100,100,240); + width: 100%; + margin-right: 100px; + background-color: rgb(240,250,255); + font-family : arial, verdana, sans-serif ; + font-size: 13px; +} + +tr.notes_bulletin_row_gen { + border-top: 1px solid black; + font-weight: bold; +} +tr.notes_bulletin_row_rang { + font-weight: bold; +} + +tr.notes_bulletin_row_ue { + /* background-color: rgb(170,187,204); voir sco_utils.UE_COLORS */ + font-weight: bold; + border-top: 1px solid black; +} + +tr.bul_row_ue_cur { + background-color: rgb(180,180,180); + color: rgb(50,50,50); +} + +tr.bul_row_ue_cap { + background-color: rgb(150,170,200); + color: rgb(50,50,50); +} + +tr.notes_bulletin_row_mat { + border-top: 2px solid rgb(140,140,140); + color: blue; +} + +tr.notes_bulletin_row_mod { + border-top: 1px solid rgb(140,140,140); +} + +tr.notes_bulletin_row_mod td.titre, tr.notes_bulletin_row_mat td.titre { + padding-left: 1em; +} + +tr.notes_bulletin_row_eval { + font-style: italic; + color: rgb(60,60,80); +} +tr.notes_bulletin_row_eval_incomplete .discretelink { + color: rgb(200,0,0); +} + +tr.b_eval_first td { + border-top: 1px dashed rgb(170,170,170); +} +tr.b_eval_first td.titre { + border-top: 0px; +} +tr.notes_bulletin_row_eval td.module { + padding-left: 5px; + border-left: 1px dashed rgb(170,170,170); +} + +table.notes_bulletin td.note { + padding-left: 1em; +} +table.notes_bulletin td.min, table.notes_bulletin td.max { + font-size: 80%; +} + +table.notes_bulletin tr.notes_bulletin_row_ue_cur td.note, table.notes_bulletin tr.notes_bulletin_row_ue_cur td.min, table.notes_bulletin tr.notes_bulletin_row_ue_cur td.max { + color: rgb(80,80,80); + font-style: italic; +} + +.note_bold { + font-weight: bold; +} +td.bull_coef_eval, td.bull_nom_eval { + font-style: italic; + color: rgb(60,60,80); +} +tr.notes_bulletin_row_eval td.note { + font-style: italic; + color: rgb(40,40,40); + font-size: 90%; +} + +tr.notes_bulletin_row_eval td.note .note_nd { + font-weight: bold; + color: red; +} + +/* --- Bulletins UCAC */ +tr.bul_ucac_row_tit, tr.bul_ucac_row_ue, tr.bul_ucac_row_total, tr.bul_ucac_row_decision, tr.bul_ucac_row_mention { + font-weight: bold; + border: 1px solid black; +} +tr.bul_ucac_row_tit { + background-color: rgb(170,187,204); +} +tr.bul_ucac_row_total, tr.bul_ucac_row_decision, tr.bul_ucac_row_mention { + background-color: rgb(220,220,220); +} + +/* ---- /ucac */ + +span.bul_minmax { + font-weight: normal; + font-size: 66%; +} +span.bul_minmax:before { + content: " "; +} + +a.bull_link { + text-decoration:none; + color: rgb(20,30,30); +} +a.bull_link:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +table.bull_head { + width: 100%; +} +td.bull_photo { + text-align: right; +} + +div.bulletin_menubar { + padding-left: 25px; +} + +.bull_liensemestre { + font-weight: bold; +} +.bull_liensemestre a { + color: rgb(255,0,0); + text-decoration: none; +} +.bull_liensemestre a:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +div.bull_appreciations { +} + +.bull_appreciations p { + margin: 0; + font-style: italic; +} +.bull_appreciations_link { + margin-left: 1em; +} +span.bull_appreciations_date { + margin-right: 1em; + font-style: normal; + font-size: 75%; +} + +div.eval_description { + color: rgb(20,20,20); + /* border: 1px solid rgb(30,100,0); */ + padding: 3px; +} + + +/* Saisie des notes */ +div.saisienote_etape1 { + border: 2px solid blue; + padding: 5px; + background-color: rgb( 231, 234, 218 ); /* E7EADA */ +} +div.saisienote_etape2 { + border: 2px solid green; + margin-top: 1em; + padding: 5px; + background-color: rgb(234,221,218); /* EADDDA */ +} +span.titredivsaisienote { + font-weight: bold; + font-size: 115%; +} + +/* ----- Absences ------ */ +td.matin { + background-color: rgb(203,242,255); +} + +td.absent { + background-color: rgb(255,156,156); +} + +td.present { + background-color: rgb(230,230,230); +} + +span.capstr { + color: red; +} + +/* ----- Formulator ------- */ +ul.tf-msg { + color: rgb(6,80,18); + border: 1px solid red; +} + +li.tf-msg { + list-style-image: url(/ScoDoc/static/icons/warning16_img.png); + padding-top: 5px; + padding-bottom: 5px; +} + +p.warning { + font-weight: bold; + color: red; +} +/* ---- ? ne fonctionne pas : p.warning:before { /ScoDoc/static/icons/warning16_img"); } */ + +form.sco_pref table.tf { + border-spacing: 5px 15px; +} + +td.tf-ro-fieldlabel { + /* font-weight: bold; */ + vertical-align:top; + margin-top: 20px; +} +td.tf-ro-fieldlabel:after { + content: ' :'; +} +td.tf-ro-field { + vertical-align:top; +} +div.tf-ro-textarea { + border: 1px solid grey; + padding-left: 8px; +} +select.tf-selglobal { + margin-left: 10px; +} + +td.tf-fieldlabel { + /* font-weight: bold; */ + vertical-align:top; +} + +.tf-comment { + font-size: 80%; + font-style: italic; +} + +.tf-explanation { + font-style: italic; +} + +.radio_green { + background-color: green; +} + +.radio_red { + background-color: red; +} + +td.fvs_val { + border-left: 1px solid rgb(80,80,80); + text-align: center; + padding-left: 1em; + padding-right: 1em; +} + +td.fvs_val_inf { + border-left: 1px solid rgb(80,80,80); + text-align: center; + padding-left: 1em; + padding-right: 1em; + color: red; +} + +td.fvs_chk { +} + +td.fvs_tit { + font-weight: bold; + text-align: left; + border-left: 1px solid rgb(80,80,80); + text-align: center; + padding-left: 1em; + padding-right: 1em; +} +td.fvs_tit_chk { + font-weight: bold; +} + +/* ----- Entreprises ------- */ + +table.entreprise_list, table.corr_list, table.contact_list { + width : 100%; + border-width: 0px; + /* border-style: solid; */ + border-spacing: 0px 0px; + padding: 0px; +} +tr.entreprise_list_even, tr.corr_list_even, tr.contact_list_even { + background-color: rgb(85%,85%,95%); +} +tr.entreprise_list_odd, tr.corr_list_odd, tr.contact_list_odd { + background-color: rgb(90%,90%, 90%); +} + +td.entreprise_descr, td.corr_descr, td.contact_descr { + padding-left: 2em; +} +td.entreprise_descr_link { + padding-left: 2em; + white-space: nowrap; +} +td.entreprise_descr_name { white-space: nowrap; } + +a.entreprise_delete { color: black; text-decoration: underline; } +a.entreprise_delete:visited { color: black; } + +a.entreprise_edit { + text-decoration: underline; + color : rgb(0,0,204); + font-weight: normal; +} +a.entreprise_edit:visited { color : rgb(0,0,204); } +a.entreprise_edit:hover { + color: rgb(153,51,51); + text-decoration: underline; +} + +p.entreprise_create { padding-top: 2em; } +a.entreprise_create { color : black; font-weight: bold; } +a.entreprise_create:visited { color : black; } + +table.entreprise_list_title { + width: 100%; + border-top: 1px solid rgb(51,102,204); + border-spacing: 0px 0px; + padding: 0px; +} +tr.entreprise_list_title { + background-color: rgb(229,236,249); + font-weight: bold; +} +td.entreprise_list_title { + padding-left: 1em; +} +td.entreprise_list_title_res { + font-weight: normal; + text-align : right; +} + +h2.entreprise { + color: rgb(6,102,18); + border: 1px solid blue; +} + +h2.entreprise_contact:before { + content: url(/ScoDoc/static/icons/contact_img.png); + vertical-align: -80%; + padding-right: 1em; +} +h2.entreprise_correspondant:before { + content: url(/ScoDoc/static/icons/correspondant_img.png); + vertical-align: -80%; + padding-right: 1em; +} + +h2.entreprise_new:before { + content: url(/ScoDoc/static/icons/entreprise_img.png); + vertical-align: -80%; + padding-right: 2em; +} + +p.entreprise_warning, p.gtr_warning, p.gtr_interdit, p.gtr_devel { + color: red; + font-style: italic; + margin-top: -1em; +} +P.entreprise_warning:before { + content: url(/ScoDoc/static/icons/warning_img.png); + vertical-align: -80%; +} +P.gtr_interdit:before { + content: url(/ScoDoc/static/icons/interdit_img.png); + vertical-align: -80%; +} +P.gtr_devel:before { + content: url(/ScoDoc/static/icons/devel_img.png); + vertical-align: -80%; +} +div.entreprise-insidebar { + border: 1px solid blue; +} + +/* ---- Sortable tables --- */ +/* Sortable tables */ +table.sortable a.sortheader { + background-color:#E6E6E6; + color: black; + font-weight: bold; + text-decoration: none; + display: block; +} +table.sortable span.sortarrow { + color: black; + text-decoration: none; +} + +/* Horizontal bar graph */ +.graph { + width: 100px; + height: 12px; + /* background-color: rgb(200, 200, 250); */ + padding-bottom: 0px; + margin-bottom: 0; + margin-right: 0px; + margin-top: 3px; + margin-left: 10px; + border:1px solid black; + position: absolute; +} + +.bar { + background-color: rgb(100, 150, 255); + margin: 0; + padding: 0; + position: absolute; + left: 0; + top: 2px; + height: 8px; + z-index: 2; +} + +.mark { + background-color: rgb(0, 150, 0); + margin: 0; + padding: 0; + position: absolute; + top: 0; + width: 2px; + height: 100%; + z-index: 2; +} + +td.cell_graph { + width: 170px; +} + +/* ------------------ Formulaire validation semestre ---------- */ +table.recap_parcours { + color: black; + border-collapse: collapse; +} + +table.recap_parcours td { + padding-left: 8px; + padding-right: 8px; +} + +.recap_parcours tr.sem_courant { + background-color: rgb(245, 243, 116); +} + +.recap_parcours tr.sem_precedent { + background-color: rgb(90%,95%,90%); +} + +.recap_parcours tr.sem_autre { + background-color: rgb(90%,90%,90%); +} + +.rcp_l1 td { + padding-top: 5px; + border-top: 3px solid rgb(50%,50%,50%); + border-right: 0px; + border-left: 0px; + color: blue; + vertical-align: top; +} +td.rcp_dec { + color: rgb(0%,0%,50%);; +} +td.rcp_nonass { + color: red; +} + +.recap_hide_details tr.rcp_l2 { display: none; } +table.recap_hide_details td.ue_acro span { display: none; } + +.sco_hide { display: none; } + +table.recap_hide_details tr.sem_courant, table.recap_hide_details tr.sem_precedent { + display: table-row; +} +table.recap_hide_details tr.sem_courant td.ue_acro span, table.recap_hide_details tr.sem_precedent td.ue_acro span { + display: inline; +} + +.recap_parcours tr.sem_courant td.rcp_type_sem { + font-weight: bold; +} +.recap_parcours tr.sem_autre td.rcp_type_sem { + color: rgb(100%,70%,70%); +} + +.rcp_l2 td { + padding-bottom: 5px; +} + +td.rcp_moy { +} +table.recap_parcours td.datedebut { + color: rgb(0,0,128); +} +table.recap_parcours td.datefin { + color: rgb(0,0,128); +} +table.recap_parcours td.rcp_type_sem { + padding-left: 4px; + padding-right: 4px; + color: red; +} +td.ue_adm { + color: green; + font-weight: bold; +} + +td.ue_cmp { + color: green; +} + +td.ue { +} + +h3.sfv { + margin-top: 0px; +} + +form.sfv_decisions { + border:1px solid blue; + padding: 6px; + margin-right: 2px; +} +form.sfv_decisions_manuelles { + margin-top: 10px; +} +th.sfv_subtitle { + text-align: left; + font-style: italic; +} + + +tr.sfv_ass { + background-color: rgb(90%,90%,80%); +} + +tr.sfv_pbass { + background-color: rgb(90%,70%,80%); +} + +div.pas_sembox { + margin-top: 10px; + border: 2px solid #a0522d; + padding: 5px; + margin-right: 10px; + font-family: arial,verdana,sans-serif; +} + +div.pas_empty_sems { +} + +.inscrit { +/* font-weight: bold; */ +} + +.inscrailleurs { + font-weight: bold; + color: red !important; +} + +span.paspaye, span.paspaye a { + color: #9400d3 !important; +} + +.pas_sembox_title a { + font-weight: bold; + font-size: 100%; + color: #1C721C; +} + +.pas_sembox_subtitle { + font-weight: normal; + font-size: 100%; + color: blue; + border-bottom: 1px solid rgb(50%,50%,50%); + margin-bottom: 8px; +} + +.pas_recap { + font-weight: bold; + font-size: 110%; + margin-top: 10px; +} + +div.pas_help { + float: left; +} + +/* ---- check absences / evaluations ---- */ +div.module_check_absences h2 { + font-size: 100%; + color: blue; + margin-bottom:0px; +} + +div.module_check_absences h2.eval_check_absences { + font-size: 80%; + color: black; + margin-left: 20px; + margin-top:0px; + margin-bottom:5px; +} + +div.module_check_absences h3 { + font-size: 80%; + color: rgb(133,0,0); + margin-left: 40px; + margin-top:0px; + margin-bottom:0px; +} + +div.module_check_absences ul { + margin-left: 60px; + font-size: 80%; + margin-top:0px; + margin-bottom:0px; +} + +/* ----------------------------------------------- */ +/* Help bubbles (aka tooltips) */ +/* ----------------------------------------------- */ +.tooltip{ + width: 200px; + color:#000; + font:lighter 11px/1.3 Arial,sans-serif; + text-decoration:none; + text-align:center; +} + +.tooltip span.top{ + padding: 30px 8px 0; + background: url(/ScoDoc/static/icons/bt_gif.png) no-repeat top; +} + +.tooltip b.bottom{ + padding:3px 8px 15px; + color: #548912; + background: url(/ScoDoc/static/icons/bt_gif.png) no-repeat bottom; +} +/* ----------------------------------------------- */ + +/* ----------------------------- */ +/* TABLES generees par gen_table */ +/* ----------------------------- */ +/* style pour les tables generiques ne specifiant pas de classes */ +table.gt_table { + border-collapse: collapse; + background-color: rgb(90%,90%,90%); + /* border: 1px solid black; */ + padding: 10px; + margin-right: 1em; +} + +table.gt_table tr { + background-color: rgb(90%,90%,90%); +} + +table.gt_table tr.gt_hl { + background-color: rgb(90%,95%,90%); +} +table.gt_table td, table.gt_table th { + border: 1px solid rgb(80%,80%,80%); +} +table.gt_table th { + padding-left: 5px; + padding-right: 5px; +} +table.gt_table td { + text-align: right; + padding-left: 2px; + padding-right: 4px; +} + +table.table_cohorte tr.sortbottom { + background-color: rgb(85%,90%,100%); +} + +table.table_leftalign tr td { + text-align: left; +} +table.table_coldate tr td:first-child { /* largeur col. date/time */ + width: 12em; + color: rgb(0%,0%,50%); +} + + +table.table_listegroupe tr td { + padding-left: 0.5em; + padding-right: 0.5em; +} + +table.list_users td.roles { + width: 22em; +} + +table.list_users td.date_modif_passwd { + white-space: nowrap; +} + +/* ----------------------------- */ + +div.form_rename_partition { + margin-top: 2em; + margin-bottom: 2em; +} + + +td.calday { + text-align: right; + vertical-align: top; +} + +div.cal_evaluations table.monthcalendar td.calcol { +} + +div.cal_evaluations table.monthcalendar td.calcell { + padding-left: 0.6em; + width: 6em; +} + + +div.cal_evaluations table.monthcalendar td a { + color: rgb(128,0,0); +} + +#lyc_map_canvas { + width: 900px; + height: 600px; +} + +/* Mise en garde -------------------------------*/ +.attention{ + background-color:#CCCC99; + border: 2px solid #CCCC99; + position:relative; + z-index:1; + overflow:hidden; + list-style:none; + padding:0; + margin:0 0 0.25em; + display:table; + margin:10px 10px 5px 10px; + padding:5px 5px 5px 25px; + border-radius: 10px; +} +.attention:before { + content:"i"; + left:6px; + width:16px; + height:16px; + margin-top:-8px; + font-size:14px; + font-weight:bold; + font-style:italic; + line-height:15px; + text-align:center; + color:#fff; + background-color:#2385BA; + /* css3 */ + -webkit-border-radius:16px; + -moz-border-radius:16px; + border-radius:16px; + position:absolute; + top:13px; +} \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/deconnexion.php b/misc/PublicationBulletins/Portail-LeHavre/deconnexion.php new file mode 100755 index 00000000..eb906aab --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/deconnexion.php @@ -0,0 +1,27 @@ + +Merci de votre visite.

      Pensez ŕ fermer votre navigateur en partant et ne jamais enregistrer votre mot de passe.

      " +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/courier.php b/misc/PublicationBulletins/Portail-LeHavre/font/courier.php new file mode 100755 index 00000000..913f9a45 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/courier.php @@ -0,0 +1,7 @@ + diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/helvetica.php b/misc/PublicationBulletins/Portail-LeHavre/font/helvetica.php new file mode 100755 index 00000000..ca94cdf2 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/helvetica.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, + 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, + chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, + chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/helveticab.php b/misc/PublicationBulletins/Portail-LeHavre/font/helveticab.php new file mode 100755 index 00000000..276cfa8c --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/helveticab.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, + 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, + 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, + chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, + chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/helveticabi.php b/misc/PublicationBulletins/Portail-LeHavre/font/helveticabi.php new file mode 100755 index 00000000..8d217743 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/helveticabi.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, + 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, + 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, + chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, + chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/helveticai.php b/misc/PublicationBulletins/Portail-LeHavre/font/helveticai.php new file mode 100755 index 00000000..88bf4371 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/helveticai.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, + 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, + chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, + chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/index.html b/misc/PublicationBulletins/Portail-LeHavre/font/index.html new file mode 100755 index 00000000..3b408bc9 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/index.html @@ -0,0 +1 @@ +


      Colloque GEII Angoulęme 2011
      \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1250.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1250.map new file mode 100755 index 00000000..ec110af0 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1250.map @@ -0,0 +1,251 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+015A Sacute +!8D U+0164 Tcaron +!8E U+017D Zcaron +!8F U+0179 Zacute +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+015B sacute +!9D U+0165 tcaron +!9E U+017E zcaron +!9F U+017A zacute +!A0 U+00A0 space +!A1 U+02C7 caron +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+0104 Aogonek +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+015E Scedilla +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+0105 aogonek +!BA U+015F scedilla +!BB U+00BB guillemotright +!BC U+013D Lcaron +!BD U+02DD hungarumlaut +!BE U+013E lcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1251.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1251.map new file mode 100755 index 00000000..de6a198d --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1251.map @@ -0,0 +1,255 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0402 afii10051 +!81 U+0403 afii10052 +!82 U+201A quotesinglbase +!83 U+0453 afii10100 +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+20AC Euro +!89 U+2030 perthousand +!8A U+0409 afii10058 +!8B U+2039 guilsinglleft +!8C U+040A afii10059 +!8D U+040C afii10061 +!8E U+040B afii10060 +!8F U+040F afii10145 +!90 U+0452 afii10099 +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0459 afii10106 +!9B U+203A guilsinglright +!9C U+045A afii10107 +!9D U+045C afii10109 +!9E U+045B afii10108 +!9F U+045F afii10193 +!A0 U+00A0 space +!A1 U+040E afii10062 +!A2 U+045E afii10110 +!A3 U+0408 afii10057 +!A4 U+00A4 currency +!A5 U+0490 afii10050 +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+0401 afii10023 +!A9 U+00A9 copyright +!AA U+0404 afii10053 +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+0407 afii10056 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+0406 afii10055 +!B3 U+0456 afii10103 +!B4 U+0491 afii10098 +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+0451 afii10071 +!B9 U+2116 afii61352 +!BA U+0454 afii10101 +!BB U+00BB guillemotright +!BC U+0458 afii10105 +!BD U+0405 afii10054 +!BE U+0455 afii10102 +!BF U+0457 afii10104 +!C0 U+0410 afii10017 +!C1 U+0411 afii10018 +!C2 U+0412 afii10019 +!C3 U+0413 afii10020 +!C4 U+0414 afii10021 +!C5 U+0415 afii10022 +!C6 U+0416 afii10024 +!C7 U+0417 afii10025 +!C8 U+0418 afii10026 +!C9 U+0419 afii10027 +!CA U+041A afii10028 +!CB U+041B afii10029 +!CC U+041C afii10030 +!CD U+041D afii10031 +!CE U+041E afii10032 +!CF U+041F afii10033 +!D0 U+0420 afii10034 +!D1 U+0421 afii10035 +!D2 U+0422 afii10036 +!D3 U+0423 afii10037 +!D4 U+0424 afii10038 +!D5 U+0425 afii10039 +!D6 U+0426 afii10040 +!D7 U+0427 afii10041 +!D8 U+0428 afii10042 +!D9 U+0429 afii10043 +!DA U+042A afii10044 +!DB U+042B afii10045 +!DC U+042C afii10046 +!DD U+042D afii10047 +!DE U+042E afii10048 +!DF U+042F afii10049 +!E0 U+0430 afii10065 +!E1 U+0431 afii10066 +!E2 U+0432 afii10067 +!E3 U+0433 afii10068 +!E4 U+0434 afii10069 +!E5 U+0435 afii10070 +!E6 U+0436 afii10072 +!E7 U+0437 afii10073 +!E8 U+0438 afii10074 +!E9 U+0439 afii10075 +!EA U+043A afii10076 +!EB U+043B afii10077 +!EC U+043C afii10078 +!ED U+043D afii10079 +!EE U+043E afii10080 +!EF U+043F afii10081 +!F0 U+0440 afii10082 +!F1 U+0441 afii10083 +!F2 U+0442 afii10084 +!F3 U+0443 afii10085 +!F4 U+0444 afii10086 +!F5 U+0445 afii10087 +!F6 U+0446 afii10088 +!F7 U+0447 afii10089 +!F8 U+0448 afii10090 +!F9 U+0449 afii10091 +!FA U+044A afii10092 +!FB U+044B afii10093 +!FC U+044C afii10094 +!FD U+044D afii10095 +!FE U+044E afii10096 +!FF U+044F afii10097 diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1252.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1252.map new file mode 100755 index 00000000..dd490e59 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1252.map @@ -0,0 +1,251 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!8E U+017D Zcaron +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9E U+017E zcaron +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1253.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1253.map new file mode 100755 index 00000000..4bd826fb --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1253.map @@ -0,0 +1,239 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9B U+203A guilsinglright +!A0 U+00A0 space +!A1 U+0385 dieresistonos +!A2 U+0386 Alphatonos +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+2015 afii00208 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+0384 tonos +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+0388 Epsilontonos +!B9 U+0389 Etatonos +!BA U+038A Iotatonos +!BB U+00BB guillemotright +!BC U+038C Omicrontonos +!BD U+00BD onehalf +!BE U+038E Upsilontonos +!BF U+038F Omegatonos +!C0 U+0390 iotadieresistonos +!C1 U+0391 Alpha +!C2 U+0392 Beta +!C3 U+0393 Gamma +!C4 U+0394 Delta +!C5 U+0395 Epsilon +!C6 U+0396 Zeta +!C7 U+0397 Eta +!C8 U+0398 Theta +!C9 U+0399 Iota +!CA U+039A Kappa +!CB U+039B Lambda +!CC U+039C Mu +!CD U+039D Nu +!CE U+039E Xi +!CF U+039F Omicron +!D0 U+03A0 Pi +!D1 U+03A1 Rho +!D3 U+03A3 Sigma +!D4 U+03A4 Tau +!D5 U+03A5 Upsilon +!D6 U+03A6 Phi +!D7 U+03A7 Chi +!D8 U+03A8 Psi +!D9 U+03A9 Omega +!DA U+03AA Iotadieresis +!DB U+03AB Upsilondieresis +!DC U+03AC alphatonos +!DD U+03AD epsilontonos +!DE U+03AE etatonos +!DF U+03AF iotatonos +!E0 U+03B0 upsilondieresistonos +!E1 U+03B1 alpha +!E2 U+03B2 beta +!E3 U+03B3 gamma +!E4 U+03B4 delta +!E5 U+03B5 epsilon +!E6 U+03B6 zeta +!E7 U+03B7 eta +!E8 U+03B8 theta +!E9 U+03B9 iota +!EA U+03BA kappa +!EB U+03BB lambda +!EC U+03BC mu +!ED U+03BD nu +!EE U+03BE xi +!EF U+03BF omicron +!F0 U+03C0 pi +!F1 U+03C1 rho +!F2 U+03C2 sigma1 +!F3 U+03C3 sigma +!F4 U+03C4 tau +!F5 U+03C5 upsilon +!F6 U+03C6 phi +!F7 U+03C7 chi +!F8 U+03C8 psi +!F9 U+03C9 omega +!FA U+03CA iotadieresis +!FB U+03CB upsilondieresis +!FC U+03CC omicrontonos +!FD U+03CD upsilontonos +!FE U+03CE omegatonos diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1254.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1254.map new file mode 100755 index 00000000..829473b2 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1254.map @@ -0,0 +1,249 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+011E Gbreve +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0130 Idotaccent +!DE U+015E Scedilla +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+011F gbreve +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0131 dotlessi +!FE U+015F scedilla +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1255.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1255.map new file mode 100755 index 00000000..079e10c6 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1255.map @@ -0,0 +1,233 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9B U+203A guilsinglright +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+20AA afii57636 +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00D7 multiply +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD sfthyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 middot +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00F7 divide +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+05B0 afii57799 +!C1 U+05B1 afii57801 +!C2 U+05B2 afii57800 +!C3 U+05B3 afii57802 +!C4 U+05B4 afii57793 +!C5 U+05B5 afii57794 +!C6 U+05B6 afii57795 +!C7 U+05B7 afii57798 +!C8 U+05B8 afii57797 +!C9 U+05B9 afii57806 +!CB U+05BB afii57796 +!CC U+05BC afii57807 +!CD U+05BD afii57839 +!CE U+05BE afii57645 +!CF U+05BF afii57841 +!D0 U+05C0 afii57842 +!D1 U+05C1 afii57804 +!D2 U+05C2 afii57803 +!D3 U+05C3 afii57658 +!D4 U+05F0 afii57716 +!D5 U+05F1 afii57717 +!D6 U+05F2 afii57718 +!D7 U+05F3 gereshhebrew +!D8 U+05F4 gershayimhebrew +!E0 U+05D0 afii57664 +!E1 U+05D1 afii57665 +!E2 U+05D2 afii57666 +!E3 U+05D3 afii57667 +!E4 U+05D4 afii57668 +!E5 U+05D5 afii57669 +!E6 U+05D6 afii57670 +!E7 U+05D7 afii57671 +!E8 U+05D8 afii57672 +!E9 U+05D9 afii57673 +!EA U+05DA afii57674 +!EB U+05DB afii57675 +!EC U+05DC afii57676 +!ED U+05DD afii57677 +!EE U+05DE afii57678 +!EF U+05DF afii57679 +!F0 U+05E0 afii57680 +!F1 U+05E1 afii57681 +!F2 U+05E2 afii57682 +!F3 U+05E3 afii57683 +!F4 U+05E4 afii57684 +!F5 U+05E5 afii57685 +!F6 U+05E6 afii57686 +!F7 U+05E7 afii57687 +!F8 U+05E8 afii57688 +!F9 U+05E9 afii57689 +!FA U+05EA afii57690 +!FD U+200E afii299 +!FE U+200F afii300 diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1257.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1257.map new file mode 100755 index 00000000..2f2ecfa2 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1257.map @@ -0,0 +1,244 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!8D U+00A8 dieresis +!8E U+02C7 caron +!8F U+00B8 cedilla +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9B U+203A guilsinglright +!9D U+00AF macron +!9E U+02DB ogonek +!A0 U+00A0 space +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00D8 Oslash +!A9 U+00A9 copyright +!AA U+0156 Rcommaaccent +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00C6 AE +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00F8 oslash +!B9 U+00B9 onesuperior +!BA U+0157 rcommaaccent +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00E6 ae +!C0 U+0104 Aogonek +!C1 U+012E Iogonek +!C2 U+0100 Amacron +!C3 U+0106 Cacute +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+0118 Eogonek +!C7 U+0112 Emacron +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0179 Zacute +!CB U+0116 Edotaccent +!CC U+0122 Gcommaaccent +!CD U+0136 Kcommaaccent +!CE U+012A Imacron +!CF U+013B Lcommaaccent +!D0 U+0160 Scaron +!D1 U+0143 Nacute +!D2 U+0145 Ncommaaccent +!D3 U+00D3 Oacute +!D4 U+014C Omacron +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0172 Uogonek +!D9 U+0141 Lslash +!DA U+015A Sacute +!DB U+016A Umacron +!DC U+00DC Udieresis +!DD U+017B Zdotaccent +!DE U+017D Zcaron +!DF U+00DF germandbls +!E0 U+0105 aogonek +!E1 U+012F iogonek +!E2 U+0101 amacron +!E3 U+0107 cacute +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+0119 eogonek +!E7 U+0113 emacron +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+017A zacute +!EB U+0117 edotaccent +!EC U+0123 gcommaaccent +!ED U+0137 kcommaaccent +!EE U+012B imacron +!EF U+013C lcommaaccent +!F0 U+0161 scaron +!F1 U+0144 nacute +!F2 U+0146 ncommaaccent +!F3 U+00F3 oacute +!F4 U+014D omacron +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0173 uogonek +!F9 U+0142 lslash +!FA U+015B sacute +!FB U+016B umacron +!FC U+00FC udieresis +!FD U+017C zdotaccent +!FE U+017E zcaron +!FF U+02D9 dotaccent diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1258.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1258.map new file mode 100755 index 00000000..fed915f7 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp1258.map @@ -0,0 +1,247 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!8C U+0152 OE +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9B U+203A guilsinglright +!9C U+0153 oe +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+0300 gravecomb +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+0110 Dcroat +!D1 U+00D1 Ntilde +!D2 U+0309 hookabovecomb +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+01A0 Ohorn +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+01AF Uhorn +!DE U+0303 tildecomb +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+0301 acutecomb +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+0111 dcroat +!F1 U+00F1 ntilde +!F2 U+0323 dotbelowcomb +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+01A1 ohorn +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+01B0 uhorn +!FE U+20AB dong +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp874.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp874.map new file mode 100755 index 00000000..1006e6b1 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/cp874.map @@ -0,0 +1,225 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!85 U+2026 ellipsis +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!A0 U+00A0 space +!A1 U+0E01 kokaithai +!A2 U+0E02 khokhaithai +!A3 U+0E03 khokhuatthai +!A4 U+0E04 khokhwaithai +!A5 U+0E05 khokhonthai +!A6 U+0E06 khorakhangthai +!A7 U+0E07 ngonguthai +!A8 U+0E08 chochanthai +!A9 U+0E09 chochingthai +!AA U+0E0A chochangthai +!AB U+0E0B sosothai +!AC U+0E0C chochoethai +!AD U+0E0D yoyingthai +!AE U+0E0E dochadathai +!AF U+0E0F topatakthai +!B0 U+0E10 thothanthai +!B1 U+0E11 thonangmonthothai +!B2 U+0E12 thophuthaothai +!B3 U+0E13 nonenthai +!B4 U+0E14 dodekthai +!B5 U+0E15 totaothai +!B6 U+0E16 thothungthai +!B7 U+0E17 thothahanthai +!B8 U+0E18 thothongthai +!B9 U+0E19 nonuthai +!BA U+0E1A bobaimaithai +!BB U+0E1B poplathai +!BC U+0E1C phophungthai +!BD U+0E1D fofathai +!BE U+0E1E phophanthai +!BF U+0E1F fofanthai +!C0 U+0E20 phosamphaothai +!C1 U+0E21 momathai +!C2 U+0E22 yoyakthai +!C3 U+0E23 roruathai +!C4 U+0E24 ruthai +!C5 U+0E25 lolingthai +!C6 U+0E26 luthai +!C7 U+0E27 wowaenthai +!C8 U+0E28 sosalathai +!C9 U+0E29 sorusithai +!CA U+0E2A sosuathai +!CB U+0E2B hohipthai +!CC U+0E2C lochulathai +!CD U+0E2D oangthai +!CE U+0E2E honokhukthai +!CF U+0E2F paiyannoithai +!D0 U+0E30 saraathai +!D1 U+0E31 maihanakatthai +!D2 U+0E32 saraaathai +!D3 U+0E33 saraamthai +!D4 U+0E34 saraithai +!D5 U+0E35 saraiithai +!D6 U+0E36 sarauethai +!D7 U+0E37 saraueethai +!D8 U+0E38 sarauthai +!D9 U+0E39 sarauuthai +!DA U+0E3A phinthuthai +!DF U+0E3F bahtthai +!E0 U+0E40 saraethai +!E1 U+0E41 saraaethai +!E2 U+0E42 saraothai +!E3 U+0E43 saraaimaimuanthai +!E4 U+0E44 saraaimaimalaithai +!E5 U+0E45 lakkhangyaothai +!E6 U+0E46 maiyamokthai +!E7 U+0E47 maitaikhuthai +!E8 U+0E48 maiekthai +!E9 U+0E49 maithothai +!EA U+0E4A maitrithai +!EB U+0E4B maichattawathai +!EC U+0E4C thanthakhatthai +!ED U+0E4D nikhahitthai +!EE U+0E4E yamakkanthai +!EF U+0E4F fongmanthai +!F0 U+0E50 zerothai +!F1 U+0E51 onethai +!F2 U+0E52 twothai +!F3 U+0E53 threethai +!F4 U+0E54 fourthai +!F5 U+0E55 fivethai +!F6 U+0E56 sixthai +!F7 U+0E57 seventhai +!F8 U+0E58 eightthai +!F9 U+0E59 ninethai +!FA U+0E5A angkhankhuthai +!FB U+0E5B khomutthai diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/index.html b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/index.html new file mode 100755 index 00000000..3b408bc9 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/index.html @@ -0,0 +1 @@ +


      Colloque GEII Angoulęme 2011
      \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-1.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-1.map new file mode 100755 index 00000000..61740a38 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-1.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-11.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-11.map new file mode 100755 index 00000000..91688120 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-11.map @@ -0,0 +1,248 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0E01 kokaithai +!A2 U+0E02 khokhaithai +!A3 U+0E03 khokhuatthai +!A4 U+0E04 khokhwaithai +!A5 U+0E05 khokhonthai +!A6 U+0E06 khorakhangthai +!A7 U+0E07 ngonguthai +!A8 U+0E08 chochanthai +!A9 U+0E09 chochingthai +!AA U+0E0A chochangthai +!AB U+0E0B sosothai +!AC U+0E0C chochoethai +!AD U+0E0D yoyingthai +!AE U+0E0E dochadathai +!AF U+0E0F topatakthai +!B0 U+0E10 thothanthai +!B1 U+0E11 thonangmonthothai +!B2 U+0E12 thophuthaothai +!B3 U+0E13 nonenthai +!B4 U+0E14 dodekthai +!B5 U+0E15 totaothai +!B6 U+0E16 thothungthai +!B7 U+0E17 thothahanthai +!B8 U+0E18 thothongthai +!B9 U+0E19 nonuthai +!BA U+0E1A bobaimaithai +!BB U+0E1B poplathai +!BC U+0E1C phophungthai +!BD U+0E1D fofathai +!BE U+0E1E phophanthai +!BF U+0E1F fofanthai +!C0 U+0E20 phosamphaothai +!C1 U+0E21 momathai +!C2 U+0E22 yoyakthai +!C3 U+0E23 roruathai +!C4 U+0E24 ruthai +!C5 U+0E25 lolingthai +!C6 U+0E26 luthai +!C7 U+0E27 wowaenthai +!C8 U+0E28 sosalathai +!C9 U+0E29 sorusithai +!CA U+0E2A sosuathai +!CB U+0E2B hohipthai +!CC U+0E2C lochulathai +!CD U+0E2D oangthai +!CE U+0E2E honokhukthai +!CF U+0E2F paiyannoithai +!D0 U+0E30 saraathai +!D1 U+0E31 maihanakatthai +!D2 U+0E32 saraaathai +!D3 U+0E33 saraamthai +!D4 U+0E34 saraithai +!D5 U+0E35 saraiithai +!D6 U+0E36 sarauethai +!D7 U+0E37 saraueethai +!D8 U+0E38 sarauthai +!D9 U+0E39 sarauuthai +!DA U+0E3A phinthuthai +!DF U+0E3F bahtthai +!E0 U+0E40 saraethai +!E1 U+0E41 saraaethai +!E2 U+0E42 saraothai +!E3 U+0E43 saraaimaimuanthai +!E4 U+0E44 saraaimaimalaithai +!E5 U+0E45 lakkhangyaothai +!E6 U+0E46 maiyamokthai +!E7 U+0E47 maitaikhuthai +!E8 U+0E48 maiekthai +!E9 U+0E49 maithothai +!EA U+0E4A maitrithai +!EB U+0E4B maichattawathai +!EC U+0E4C thanthakhatthai +!ED U+0E4D nikhahitthai +!EE U+0E4E yamakkanthai +!EF U+0E4F fongmanthai +!F0 U+0E50 zerothai +!F1 U+0E51 onethai +!F2 U+0E52 twothai +!F3 U+0E53 threethai +!F4 U+0E54 fourthai +!F5 U+0E55 fivethai +!F6 U+0E56 sixthai +!F7 U+0E57 seventhai +!F8 U+0E58 eightthai +!F9 U+0E59 ninethai +!FA U+0E5A angkhankhuthai +!FB U+0E5B khomutthai diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-15.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-15.map new file mode 100755 index 00000000..6c2b5712 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-15.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+20AC Euro +!A5 U+00A5 yen +!A6 U+0160 Scaron +!A7 U+00A7 section +!A8 U+0161 scaron +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+017D Zcaron +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+017E zcaron +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+0152 OE +!BD U+0153 oe +!BE U+0178 Ydieresis +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-16.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-16.map new file mode 100755 index 00000000..202c8fe5 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-16.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+0105 aogonek +!A3 U+0141 Lslash +!A4 U+20AC Euro +!A5 U+201E quotedblbase +!A6 U+0160 Scaron +!A7 U+00A7 section +!A8 U+0161 scaron +!A9 U+00A9 copyright +!AA U+0218 Scommaaccent +!AB U+00AB guillemotleft +!AC U+0179 Zacute +!AD U+00AD hyphen +!AE U+017A zacute +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+010C Ccaron +!B3 U+0142 lslash +!B4 U+017D Zcaron +!B5 U+201D quotedblright +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+017E zcaron +!B9 U+010D ccaron +!BA U+0219 scommaaccent +!BB U+00BB guillemotright +!BC U+0152 OE +!BD U+0153 oe +!BE U+0178 Ydieresis +!BF U+017C zdotaccent +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0106 Cacute +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+015A Sacute +!D8 U+0170 Uhungarumlaut +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0118 Eogonek +!DE U+021A Tcommaaccent +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+0107 cacute +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+015B sacute +!F8 U+0171 uhungarumlaut +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0119 eogonek +!FE U+021B tcommaaccent +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-2.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-2.map new file mode 100755 index 00000000..65ae09f9 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-2.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+013D Lcaron +!A6 U+015A Sacute +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+0160 Scaron +!AA U+015E Scedilla +!AB U+0164 Tcaron +!AC U+0179 Zacute +!AD U+00AD hyphen +!AE U+017D Zcaron +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+0105 aogonek +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+013E lcaron +!B6 U+015B sacute +!B7 U+02C7 caron +!B8 U+00B8 cedilla +!B9 U+0161 scaron +!BA U+015F scedilla +!BB U+0165 tcaron +!BC U+017A zacute +!BD U+02DD hungarumlaut +!BE U+017E zcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-4.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-4.map new file mode 100755 index 00000000..a7d87bf3 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-4.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+0138 kgreenlandic +!A3 U+0156 Rcommaaccent +!A4 U+00A4 currency +!A5 U+0128 Itilde +!A6 U+013B Lcommaaccent +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+0160 Scaron +!AA U+0112 Emacron +!AB U+0122 Gcommaaccent +!AC U+0166 Tbar +!AD U+00AD hyphen +!AE U+017D Zcaron +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+0105 aogonek +!B2 U+02DB ogonek +!B3 U+0157 rcommaaccent +!B4 U+00B4 acute +!B5 U+0129 itilde +!B6 U+013C lcommaaccent +!B7 U+02C7 caron +!B8 U+00B8 cedilla +!B9 U+0161 scaron +!BA U+0113 emacron +!BB U+0123 gcommaaccent +!BC U+0167 tbar +!BD U+014A Eng +!BE U+017E zcaron +!BF U+014B eng +!C0 U+0100 Amacron +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+012E Iogonek +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+0116 Edotaccent +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+012A Imacron +!D0 U+0110 Dcroat +!D1 U+0145 Ncommaaccent +!D2 U+014C Omacron +!D3 U+0136 Kcommaaccent +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+0172 Uogonek +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0168 Utilde +!DE U+016A Umacron +!DF U+00DF germandbls +!E0 U+0101 amacron +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+012F iogonek +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+0117 edotaccent +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+012B imacron +!F0 U+0111 dcroat +!F1 U+0146 ncommaaccent +!F2 U+014D omacron +!F3 U+0137 kcommaaccent +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+0173 uogonek +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0169 utilde +!FE U+016B umacron +!FF U+02D9 dotaccent diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-5.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-5.map new file mode 100755 index 00000000..f9cd4edc --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-5.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0401 afii10023 +!A2 U+0402 afii10051 +!A3 U+0403 afii10052 +!A4 U+0404 afii10053 +!A5 U+0405 afii10054 +!A6 U+0406 afii10055 +!A7 U+0407 afii10056 +!A8 U+0408 afii10057 +!A9 U+0409 afii10058 +!AA U+040A afii10059 +!AB U+040B afii10060 +!AC U+040C afii10061 +!AD U+00AD hyphen +!AE U+040E afii10062 +!AF U+040F afii10145 +!B0 U+0410 afii10017 +!B1 U+0411 afii10018 +!B2 U+0412 afii10019 +!B3 U+0413 afii10020 +!B4 U+0414 afii10021 +!B5 U+0415 afii10022 +!B6 U+0416 afii10024 +!B7 U+0417 afii10025 +!B8 U+0418 afii10026 +!B9 U+0419 afii10027 +!BA U+041A afii10028 +!BB U+041B afii10029 +!BC U+041C afii10030 +!BD U+041D afii10031 +!BE U+041E afii10032 +!BF U+041F afii10033 +!C0 U+0420 afii10034 +!C1 U+0421 afii10035 +!C2 U+0422 afii10036 +!C3 U+0423 afii10037 +!C4 U+0424 afii10038 +!C5 U+0425 afii10039 +!C6 U+0426 afii10040 +!C7 U+0427 afii10041 +!C8 U+0428 afii10042 +!C9 U+0429 afii10043 +!CA U+042A afii10044 +!CB U+042B afii10045 +!CC U+042C afii10046 +!CD U+042D afii10047 +!CE U+042E afii10048 +!CF U+042F afii10049 +!D0 U+0430 afii10065 +!D1 U+0431 afii10066 +!D2 U+0432 afii10067 +!D3 U+0433 afii10068 +!D4 U+0434 afii10069 +!D5 U+0435 afii10070 +!D6 U+0436 afii10072 +!D7 U+0437 afii10073 +!D8 U+0438 afii10074 +!D9 U+0439 afii10075 +!DA U+043A afii10076 +!DB U+043B afii10077 +!DC U+043C afii10078 +!DD U+043D afii10079 +!DE U+043E afii10080 +!DF U+043F afii10081 +!E0 U+0440 afii10082 +!E1 U+0441 afii10083 +!E2 U+0442 afii10084 +!E3 U+0443 afii10085 +!E4 U+0444 afii10086 +!E5 U+0445 afii10087 +!E6 U+0446 afii10088 +!E7 U+0447 afii10089 +!E8 U+0448 afii10090 +!E9 U+0449 afii10091 +!EA U+044A afii10092 +!EB U+044B afii10093 +!EC U+044C afii10094 +!ED U+044D afii10095 +!EE U+044E afii10096 +!EF U+044F afii10097 +!F0 U+2116 afii61352 +!F1 U+0451 afii10071 +!F2 U+0452 afii10099 +!F3 U+0453 afii10100 +!F4 U+0454 afii10101 +!F5 U+0455 afii10102 +!F6 U+0456 afii10103 +!F7 U+0457 afii10104 +!F8 U+0458 afii10105 +!F9 U+0459 afii10106 +!FA U+045A afii10107 +!FB U+045B afii10108 +!FC U+045C afii10109 +!FD U+00A7 section +!FE U+045E afii10110 +!FF U+045F afii10193 diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-7.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-7.map new file mode 100755 index 00000000..e163796b --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-7.map @@ -0,0 +1,250 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+2018 quoteleft +!A2 U+2019 quoteright +!A3 U+00A3 sterling +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AF U+2015 afii00208 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+0384 tonos +!B5 U+0385 dieresistonos +!B6 U+0386 Alphatonos +!B7 U+00B7 periodcentered +!B8 U+0388 Epsilontonos +!B9 U+0389 Etatonos +!BA U+038A Iotatonos +!BB U+00BB guillemotright +!BC U+038C Omicrontonos +!BD U+00BD onehalf +!BE U+038E Upsilontonos +!BF U+038F Omegatonos +!C0 U+0390 iotadieresistonos +!C1 U+0391 Alpha +!C2 U+0392 Beta +!C3 U+0393 Gamma +!C4 U+0394 Delta +!C5 U+0395 Epsilon +!C6 U+0396 Zeta +!C7 U+0397 Eta +!C8 U+0398 Theta +!C9 U+0399 Iota +!CA U+039A Kappa +!CB U+039B Lambda +!CC U+039C Mu +!CD U+039D Nu +!CE U+039E Xi +!CF U+039F Omicron +!D0 U+03A0 Pi +!D1 U+03A1 Rho +!D3 U+03A3 Sigma +!D4 U+03A4 Tau +!D5 U+03A5 Upsilon +!D6 U+03A6 Phi +!D7 U+03A7 Chi +!D8 U+03A8 Psi +!D9 U+03A9 Omega +!DA U+03AA Iotadieresis +!DB U+03AB Upsilondieresis +!DC U+03AC alphatonos +!DD U+03AD epsilontonos +!DE U+03AE etatonos +!DF U+03AF iotatonos +!E0 U+03B0 upsilondieresistonos +!E1 U+03B1 alpha +!E2 U+03B2 beta +!E3 U+03B3 gamma +!E4 U+03B4 delta +!E5 U+03B5 epsilon +!E6 U+03B6 zeta +!E7 U+03B7 eta +!E8 U+03B8 theta +!E9 U+03B9 iota +!EA U+03BA kappa +!EB U+03BB lambda +!EC U+03BC mu +!ED U+03BD nu +!EE U+03BE xi +!EF U+03BF omicron +!F0 U+03C0 pi +!F1 U+03C1 rho +!F2 U+03C2 sigma1 +!F3 U+03C3 sigma +!F4 U+03C4 tau +!F5 U+03C5 upsilon +!F6 U+03C6 phi +!F7 U+03C7 chi +!F8 U+03C8 psi +!F9 U+03C9 omega +!FA U+03CA iotadieresis +!FB U+03CB upsilondieresis +!FC U+03CC omicrontonos +!FD U+03CD upsilontonos +!FE U+03CE omegatonos diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-9.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-9.map new file mode 100755 index 00000000..48c123ae --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/iso-8859-9.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+011E Gbreve +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0130 Idotaccent +!DE U+015E Scedilla +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+011F gbreve +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0131 dotlessi +!FE U+015F scedilla +!FF U+00FF ydieresis diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-r.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-r.map new file mode 100755 index 00000000..6ad5d05d --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-r.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+2500 SF100000 +!81 U+2502 SF110000 +!82 U+250C SF010000 +!83 U+2510 SF030000 +!84 U+2514 SF020000 +!85 U+2518 SF040000 +!86 U+251C SF080000 +!87 U+2524 SF090000 +!88 U+252C SF060000 +!89 U+2534 SF070000 +!8A U+253C SF050000 +!8B U+2580 upblock +!8C U+2584 dnblock +!8D U+2588 block +!8E U+258C lfblock +!8F U+2590 rtblock +!90 U+2591 ltshade +!91 U+2592 shade +!92 U+2593 dkshade +!93 U+2320 integraltp +!94 U+25A0 filledbox +!95 U+2219 periodcentered +!96 U+221A radical +!97 U+2248 approxequal +!98 U+2264 lessequal +!99 U+2265 greaterequal +!9A U+00A0 space +!9B U+2321 integralbt +!9C U+00B0 degree +!9D U+00B2 twosuperior +!9E U+00B7 periodcentered +!9F U+00F7 divide +!A0 U+2550 SF430000 +!A1 U+2551 SF240000 +!A2 U+2552 SF510000 +!A3 U+0451 afii10071 +!A4 U+2553 SF520000 +!A5 U+2554 SF390000 +!A6 U+2555 SF220000 +!A7 U+2556 SF210000 +!A8 U+2557 SF250000 +!A9 U+2558 SF500000 +!AA U+2559 SF490000 +!AB U+255A SF380000 +!AC U+255B SF280000 +!AD U+255C SF270000 +!AE U+255D SF260000 +!AF U+255E SF360000 +!B0 U+255F SF370000 +!B1 U+2560 SF420000 +!B2 U+2561 SF190000 +!B3 U+0401 afii10023 +!B4 U+2562 SF200000 +!B5 U+2563 SF230000 +!B6 U+2564 SF470000 +!B7 U+2565 SF480000 +!B8 U+2566 SF410000 +!B9 U+2567 SF450000 +!BA U+2568 SF460000 +!BB U+2569 SF400000 +!BC U+256A SF540000 +!BD U+256B SF530000 +!BE U+256C SF440000 +!BF U+00A9 copyright +!C0 U+044E afii10096 +!C1 U+0430 afii10065 +!C2 U+0431 afii10066 +!C3 U+0446 afii10088 +!C4 U+0434 afii10069 +!C5 U+0435 afii10070 +!C6 U+0444 afii10086 +!C7 U+0433 afii10068 +!C8 U+0445 afii10087 +!C9 U+0438 afii10074 +!CA U+0439 afii10075 +!CB U+043A afii10076 +!CC U+043B afii10077 +!CD U+043C afii10078 +!CE U+043D afii10079 +!CF U+043E afii10080 +!D0 U+043F afii10081 +!D1 U+044F afii10097 +!D2 U+0440 afii10082 +!D3 U+0441 afii10083 +!D4 U+0442 afii10084 +!D5 U+0443 afii10085 +!D6 U+0436 afii10072 +!D7 U+0432 afii10067 +!D8 U+044C afii10094 +!D9 U+044B afii10093 +!DA U+0437 afii10073 +!DB U+0448 afii10090 +!DC U+044D afii10095 +!DD U+0449 afii10091 +!DE U+0447 afii10089 +!DF U+044A afii10092 +!E0 U+042E afii10048 +!E1 U+0410 afii10017 +!E2 U+0411 afii10018 +!E3 U+0426 afii10040 +!E4 U+0414 afii10021 +!E5 U+0415 afii10022 +!E6 U+0424 afii10038 +!E7 U+0413 afii10020 +!E8 U+0425 afii10039 +!E9 U+0418 afii10026 +!EA U+0419 afii10027 +!EB U+041A afii10028 +!EC U+041B afii10029 +!ED U+041C afii10030 +!EE U+041D afii10031 +!EF U+041E afii10032 +!F0 U+041F afii10033 +!F1 U+042F afii10049 +!F2 U+0420 afii10034 +!F3 U+0421 afii10035 +!F4 U+0422 afii10036 +!F5 U+0423 afii10037 +!F6 U+0416 afii10024 +!F7 U+0412 afii10019 +!F8 U+042C afii10046 +!F9 U+042B afii10045 +!FA U+0417 afii10025 +!FB U+0428 afii10042 +!FC U+042D afii10047 +!FD U+0429 afii10043 +!FE U+0427 afii10041 +!FF U+042A afii10044 diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-u.map b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-u.map new file mode 100755 index 00000000..40a7e4fd --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/koi8-u.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+2500 SF100000 +!81 U+2502 SF110000 +!82 U+250C SF010000 +!83 U+2510 SF030000 +!84 U+2514 SF020000 +!85 U+2518 SF040000 +!86 U+251C SF080000 +!87 U+2524 SF090000 +!88 U+252C SF060000 +!89 U+2534 SF070000 +!8A U+253C SF050000 +!8B U+2580 upblock +!8C U+2584 dnblock +!8D U+2588 block +!8E U+258C lfblock +!8F U+2590 rtblock +!90 U+2591 ltshade +!91 U+2592 shade +!92 U+2593 dkshade +!93 U+2320 integraltp +!94 U+25A0 filledbox +!95 U+2022 bullet +!96 U+221A radical +!97 U+2248 approxequal +!98 U+2264 lessequal +!99 U+2265 greaterequal +!9A U+00A0 space +!9B U+2321 integralbt +!9C U+00B0 degree +!9D U+00B2 twosuperior +!9E U+00B7 periodcentered +!9F U+00F7 divide +!A0 U+2550 SF430000 +!A1 U+2551 SF240000 +!A2 U+2552 SF510000 +!A3 U+0451 afii10071 +!A4 U+0454 afii10101 +!A5 U+2554 SF390000 +!A6 U+0456 afii10103 +!A7 U+0457 afii10104 +!A8 U+2557 SF250000 +!A9 U+2558 SF500000 +!AA U+2559 SF490000 +!AB U+255A SF380000 +!AC U+255B SF280000 +!AD U+0491 afii10098 +!AE U+255D SF260000 +!AF U+255E SF360000 +!B0 U+255F SF370000 +!B1 U+2560 SF420000 +!B2 U+2561 SF190000 +!B3 U+0401 afii10023 +!B4 U+0404 afii10053 +!B5 U+2563 SF230000 +!B6 U+0406 afii10055 +!B7 U+0407 afii10056 +!B8 U+2566 SF410000 +!B9 U+2567 SF450000 +!BA U+2568 SF460000 +!BB U+2569 SF400000 +!BC U+256A SF540000 +!BD U+0490 afii10050 +!BE U+256C SF440000 +!BF U+00A9 copyright +!C0 U+044E afii10096 +!C1 U+0430 afii10065 +!C2 U+0431 afii10066 +!C3 U+0446 afii10088 +!C4 U+0434 afii10069 +!C5 U+0435 afii10070 +!C6 U+0444 afii10086 +!C7 U+0433 afii10068 +!C8 U+0445 afii10087 +!C9 U+0438 afii10074 +!CA U+0439 afii10075 +!CB U+043A afii10076 +!CC U+043B afii10077 +!CD U+043C afii10078 +!CE U+043D afii10079 +!CF U+043E afii10080 +!D0 U+043F afii10081 +!D1 U+044F afii10097 +!D2 U+0440 afii10082 +!D3 U+0441 afii10083 +!D4 U+0442 afii10084 +!D5 U+0443 afii10085 +!D6 U+0436 afii10072 +!D7 U+0432 afii10067 +!D8 U+044C afii10094 +!D9 U+044B afii10093 +!DA U+0437 afii10073 +!DB U+0448 afii10090 +!DC U+044D afii10095 +!DD U+0449 afii10091 +!DE U+0447 afii10089 +!DF U+044A afii10092 +!E0 U+042E afii10048 +!E1 U+0410 afii10017 +!E2 U+0411 afii10018 +!E3 U+0426 afii10040 +!E4 U+0414 afii10021 +!E5 U+0415 afii10022 +!E6 U+0424 afii10038 +!E7 U+0413 afii10020 +!E8 U+0425 afii10039 +!E9 U+0418 afii10026 +!EA U+0419 afii10027 +!EB U+041A afii10028 +!EC U+041B afii10029 +!ED U+041C afii10030 +!EE U+041D afii10031 +!EF U+041E afii10032 +!F0 U+041F afii10033 +!F1 U+042F afii10049 +!F2 U+0420 afii10034 +!F3 U+0421 afii10035 +!F4 U+0422 afii10036 +!F5 U+0423 afii10037 +!F6 U+0416 afii10024 +!F7 U+0412 afii10019 +!F8 U+042C afii10046 +!F9 U+042B afii10045 +!FA U+0417 afii10025 +!FB U+0428 afii10042 +!FC U+042D afii10047 +!FD U+0429 afii10043 +!FE U+0427 afii10041 +!FF U+042A afii10044 diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/makefont/makefont.php b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/makefont.php new file mode 100755 index 00000000..0baa933d --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/makefont/makefont.php @@ -0,0 +1,419 @@ +Error:
      encoding not found: '.$enc); + $cc2gn=array(); + foreach($a as $l) + { + if($l[0]=='!') + { + $e=preg_split('/[ \\t]+/',rtrim($l)); + $cc=hexdec(substr($e[0],1)); + $gn=$e[2]; + $cc2gn[$cc]=$gn; + } + } + for($i=0;$i<=255;$i++) + { + if(!isset($cc2gn[$i])) + $cc2gn[$i]='.notdef'; + } + return $cc2gn; +} + +function ReadAFM($file, &$map) +{ + //Read a font metric file + $a=file($file); + if(empty($a)) + die('File not found'); + $widths=array(); + $fm=array(); + $fix=array('Edot'=>'Edotaccent','edot'=>'edotaccent','Idot'=>'Idotaccent','Zdot'=>'Zdotaccent','zdot'=>'zdotaccent', + 'Odblacute'=>'Ohungarumlaut','odblacute'=>'ohungarumlaut','Udblacute'=>'Uhungarumlaut','udblacute'=>'uhungarumlaut', + 'Gcedilla'=>'Gcommaaccent','gcedilla'=>'gcommaaccent','Kcedilla'=>'Kcommaaccent','kcedilla'=>'kcommaaccent', + 'Lcedilla'=>'Lcommaaccent','lcedilla'=>'lcommaaccent','Ncedilla'=>'Ncommaaccent','ncedilla'=>'ncommaaccent', + 'Rcedilla'=>'Rcommaaccent','rcedilla'=>'rcommaaccent','Scedilla'=>'Scommaaccent','scedilla'=>'scommaaccent', + 'Tcedilla'=>'Tcommaaccent','tcedilla'=>'tcommaaccent','Dslash'=>'Dcroat','dslash'=>'dcroat','Dmacron'=>'Dcroat','dmacron'=>'dcroat', + 'combininggraveaccent'=>'gravecomb','combininghookabove'=>'hookabovecomb','combiningtildeaccent'=>'tildecomb', + 'combiningacuteaccent'=>'acutecomb','combiningdotbelow'=>'dotbelowcomb','dongsign'=>'dong'); + foreach($a as $l) + { + $e=explode(' ',rtrim($l)); + if(count($e)<2) + continue; + $code=$e[0]; + $param=$e[1]; + if($code=='C') + { + //Character metrics + $cc=(int)$e[1]; + $w=$e[4]; + $gn=$e[7]; + if(substr($gn,-4)=='20AC') + $gn='Euro'; + if(isset($fix[$gn])) + { + //Fix incorrect glyph name + foreach($map as $c=>$n) + { + if($n==$fix[$gn]) + $map[$c]=$gn; + } + } + if(empty($map)) + { + //Symbolic font: use built-in encoding + $widths[$cc]=$w; + } + else + { + $widths[$gn]=$w; + if($gn=='X') + $fm['CapXHeight']=$e[13]; + } + if($gn=='.notdef') + $fm['MissingWidth']=$w; + } + elseif($code=='FontName') + $fm['FontName']=$param; + elseif($code=='Weight') + $fm['Weight']=$param; + elseif($code=='ItalicAngle') + $fm['ItalicAngle']=(double)$param; + elseif($code=='Ascender') + $fm['Ascender']=(int)$param; + elseif($code=='Descender') + $fm['Descender']=(int)$param; + elseif($code=='UnderlineThickness') + $fm['UnderlineThickness']=(int)$param; + elseif($code=='UnderlinePosition') + $fm['UnderlinePosition']=(int)$param; + elseif($code=='IsFixedPitch') + $fm['IsFixedPitch']=($param=='true'); + elseif($code=='FontBBox') + $fm['FontBBox']=array($e[1],$e[2],$e[3],$e[4]); + elseif($code=='CapHeight') + $fm['CapHeight']=(int)$param; + elseif($code=='StdVW') + $fm['StdVW']=(int)$param; + } + if(!isset($fm['FontName'])) + die('FontName not found'); + if(!empty($map)) + { + if(!isset($widths['.notdef'])) + $widths['.notdef']=600; + if(!isset($widths['Delta']) && isset($widths['increment'])) + $widths['Delta']=$widths['increment']; + //Order widths according to map + for($i=0;$i<=255;$i++) + { + if(!isset($widths[$map[$i]])) + { + echo 'Warning: character '.$map[$i].' is missing
      '; + $widths[$i]=$widths['.notdef']; + } + else + $widths[$i]=$widths[$map[$i]]; + } + } + $fm['Widths']=$widths; + return $fm; +} + +function MakeFontDescriptor($fm, $symbolic) +{ + //Ascent + $asc=(isset($fm['Ascender']) ? $fm['Ascender'] : 1000); + $fd="array('Ascent'=>".$asc; + //Descent + $desc=(isset($fm['Descender']) ? $fm['Descender'] : -200); + $fd.=",'Descent'=>".$desc; + //CapHeight + if(isset($fm['CapHeight'])) + $ch=$fm['CapHeight']; + elseif(isset($fm['CapXHeight'])) + $ch=$fm['CapXHeight']; + else + $ch=$asc; + $fd.=",'CapHeight'=>".$ch; + //Flags + $flags=0; + if(isset($fm['IsFixedPitch']) && $fm['IsFixedPitch']) + $flags+=1<<0; + if($symbolic) + $flags+=1<<2; + if(!$symbolic) + $flags+=1<<5; + if(isset($fm['ItalicAngle']) && $fm['ItalicAngle']!=0) + $flags+=1<<6; + $fd.=",'Flags'=>".$flags; + //FontBBox + if(isset($fm['FontBBox'])) + $fbb=$fm['FontBBox']; + else + $fbb=array(0,$desc-100,1000,$asc+100); + $fd.=",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'"; + //ItalicAngle + $ia=(isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0); + $fd.=",'ItalicAngle'=>".$ia; + //StemV + if(isset($fm['StdVW'])) + $stemv=$fm['StdVW']; + elseif(isset($fm['Weight']) && preg_match('/bold|black/i',$fm['Weight'])) + $stemv=120; + else + $stemv=70; + $fd.=",'StemV'=>".$stemv; + //MissingWidth + if(isset($fm['MissingWidth'])) + $fd.=",'MissingWidth'=>".$fm['MissingWidth']; + $fd.=')'; + return $fd; +} + +function MakeWidthArray($fm) +{ + //Make character width array + $s="array(\n\t"; + $cw=$fm['Widths']; + for($i=0;$i<=255;$i++) + { + if(chr($i)=="'") + $s.="'\\''"; + elseif(chr($i)=="\\") + $s.="'\\\\'"; + elseif($i>=32 && $i<=126) + $s.="'".chr($i)."'"; + else + $s.="chr($i)"; + $s.='=>'.$fm['Widths'][$i]; + if($i<255) + $s.=','; + if(($i+1)%22==0) + $s.="\n\t"; + } + $s.=')'; + return $s; +} + +function MakeFontEncoding($map) +{ + //Build differences from reference encoding + $ref=ReadMap('cp1252'); + $s=''; + $last=0; + for($i=32;$i<=255;$i++) + { + if($map[$i]!=$ref[$i]) + { + if($i!=$last+1) + $s.=$i.' '; + $last=$i; + $s.='/'.$map[$i].' '; + } + } + return rtrim($s); +} + +function SaveToFile($file, $s, $mode) +{ + $f=fopen($file,'w'.$mode); + if(!$f) + die('Can\'t write to file '.$file); + fwrite($f,$s,strlen($s)); + fclose($f); +} + +function ReadShort($f) +{ + $a=unpack('n1n',fread($f,2)); + return $a['n']; +} + +function ReadLong($f) +{ + $a=unpack('N1N',fread($f,4)); + return $a['N']; +} + +function CheckTTF($file) +{ + //Check if font license allows embedding + $f=fopen($file,'rb'); + if(!$f) + die('Error: Can\'t open '.$file); + //Extract number of tables + fseek($f,4,SEEK_CUR); + $nb=ReadShort($f); + fseek($f,6,SEEK_CUR); + //Seek OS/2 table + $found=false; + for($i=0;$i<$nb;$i++) + { + if(fread($f,4)=='OS/2') + { + $found=true; + break; + } + fseek($f,12,SEEK_CUR); + } + if(!$found) + { + fclose($f); + return; + } + fseek($f,4,SEEK_CUR); + $offset=ReadLong($f); + fseek($f,$offset,SEEK_SET); + //Extract fsType flags + fseek($f,8,SEEK_CUR); + $fsType=ReadShort($f); + $rl=($fsType & 0x02)!=0; + $pp=($fsType & 0x04)!=0; + $e=($fsType & 0x08)!=0; + fclose($f); + if($rl && !$pp && !$e) + echo 'Warning: font license does not allow embedding'; +} + +/******************************************************************************* +* fontfile : chemin du fichier TTF (ou chaîne vide si pas d'incorporation) * +* afmfile : chemin du fichier AFM * +* enc : encodage (ou chaîne vide si la police est symbolique) * +* patch : patch optionnel pour l'encodage * +* type : type de la police si fontfile est vide * +*******************************************************************************/ +function MakeFont($fontfile, $afmfile, $enc='cp1252', $patch=array(), $type='TrueType') +{ + //Generate a font definition file + if(get_magic_quotes_runtime()) + @set_magic_quotes_runtime(0); + ini_set('auto_detect_line_endings','1'); + if($enc) + { + $map=ReadMap($enc); + foreach($patch as $cc=>$gn) + $map[$cc]=$gn; + } + else + $map=array(); + if(!file_exists($afmfile)) + die('Error: AFM file not found: '.$afmfile); + $fm=ReadAFM($afmfile,$map); + if($enc) + $diff=MakeFontEncoding($map); + else + $diff=''; + $fd=MakeFontDescriptor($fm,empty($map)); + //Find font type + if($fontfile) + { + $ext=strtolower(substr($fontfile,-3)); + if($ext=='ttf') + $type='TrueType'; + elseif($ext=='pfb') + $type='Type1'; + else + die('Error: unrecognized font file extension: '.$ext); + } + else + { + if($type!='TrueType' && $type!='Type1') + die('Error: incorrect font type: '.$type); + } + //Start generation + $s='Error: font file not found: '.$fontfile); + if($type=='TrueType') + CheckTTF($fontfile); + $f=fopen($fontfile,'rb'); + if(!$f) + die('Error: Can\'t open '.$fontfile); + $file=fread($f,filesize($fontfile)); + fclose($f); + if($type=='Type1') + { + //Find first two sections and discard third one + $header=(ord($file[0])==128); + if($header) + { + //Strip first binary header + $file=substr($file,6); + } + $pos=strpos($file,'eexec'); + if(!$pos) + die('Error: font file does not seem to be valid Type1'); + $size1=$pos+6; + if($header && ord($file[$size1])==128) + { + //Strip second binary header + $file=substr($file,0,$size1).substr($file,$size1+6); + } + $pos=strpos($file,'00000000'); + if(!$pos) + die('Error: font file does not seem to be valid Type1'); + $size2=$pos-$size1; + $file=substr($file,0,$size1+$size2); + } + if(function_exists('gzcompress')) + { + $cmp=$basename.'.z'; + SaveToFile($cmp,gzcompress($file),'b'); + $s.='$file=\''.$cmp."';\n"; + echo 'Font file compressed ('.$cmp.')
      '; + } + else + { + $s.='$file=\''.basename($fontfile)."';\n"; + echo 'Notice: font file could not be compressed (zlib extension not available)
      '; + } + if($type=='Type1') + { + $s.='$size1='.$size1.";\n"; + $s.='$size2='.$size2.";\n"; + } + else + $s.='$originalsize='.filesize($fontfile).";\n"; + } + else + { + //Not embedded font + $s.='$file='."'';\n"; + } + $s.="?>\n"; + SaveToFile($basename.'.php',$s,'t'); + echo 'Font definition file generated ('.$basename.'.php'.')
      '; +} +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/symbol.php b/misc/PublicationBulletins/Portail-LeHavre/font/symbol.php new file mode 100755 index 00000000..43b50e45 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/symbol.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>713,'#'=>500,'$'=>549,'%'=>833,'&'=>778,'\''=>439,'('=>333,')'=>333,'*'=>500,'+'=>549, + ','=>250,'-'=>549,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>549,'='=>549,'>'=>549,'?'=>444,'@'=>549,'A'=>722, + 'B'=>667,'C'=>722,'D'=>612,'E'=>611,'F'=>763,'G'=>603,'H'=>722,'I'=>333,'J'=>631,'K'=>722,'L'=>686,'M'=>889,'N'=>722,'O'=>722,'P'=>768,'Q'=>741,'R'=>556,'S'=>592,'T'=>611,'U'=>690,'V'=>439,'W'=>768, + 'X'=>645,'Y'=>795,'Z'=>611,'['=>333,'\\'=>863,']'=>333,'^'=>658,'_'=>500,'`'=>500,'a'=>631,'b'=>549,'c'=>549,'d'=>494,'e'=>439,'f'=>521,'g'=>411,'h'=>603,'i'=>329,'j'=>603,'k'=>549,'l'=>549,'m'=>576, + 'n'=>521,'o'=>549,'p'=>549,'q'=>521,'r'=>549,'s'=>603,'t'=>439,'u'=>576,'v'=>713,'w'=>686,'x'=>493,'y'=>686,'z'=>494,'{'=>480,'|'=>200,'}'=>480,'~'=>549,chr(127)=>0,chr(128)=>0,chr(129)=>0,chr(130)=>0,chr(131)=>0, + chr(132)=>0,chr(133)=>0,chr(134)=>0,chr(135)=>0,chr(136)=>0,chr(137)=>0,chr(138)=>0,chr(139)=>0,chr(140)=>0,chr(141)=>0,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, + chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>750,chr(161)=>620,chr(162)=>247,chr(163)=>549,chr(164)=>167,chr(165)=>713,chr(166)=>500,chr(167)=>753,chr(168)=>753,chr(169)=>753,chr(170)=>753,chr(171)=>1042,chr(172)=>987,chr(173)=>603,chr(174)=>987,chr(175)=>603, + chr(176)=>400,chr(177)=>549,chr(178)=>411,chr(179)=>549,chr(180)=>549,chr(181)=>713,chr(182)=>494,chr(183)=>460,chr(184)=>549,chr(185)=>549,chr(186)=>549,chr(187)=>549,chr(188)=>1000,chr(189)=>603,chr(190)=>1000,chr(191)=>658,chr(192)=>823,chr(193)=>686,chr(194)=>795,chr(195)=>987,chr(196)=>768,chr(197)=>768, + chr(198)=>823,chr(199)=>768,chr(200)=>768,chr(201)=>713,chr(202)=>713,chr(203)=>713,chr(204)=>713,chr(205)=>713,chr(206)=>713,chr(207)=>713,chr(208)=>768,chr(209)=>713,chr(210)=>790,chr(211)=>790,chr(212)=>890,chr(213)=>823,chr(214)=>549,chr(215)=>250,chr(216)=>713,chr(217)=>603,chr(218)=>603,chr(219)=>1042, + chr(220)=>987,chr(221)=>603,chr(222)=>987,chr(223)=>603,chr(224)=>494,chr(225)=>329,chr(226)=>790,chr(227)=>790,chr(228)=>786,chr(229)=>713,chr(230)=>384,chr(231)=>384,chr(232)=>384,chr(233)=>384,chr(234)=>384,chr(235)=>384,chr(236)=>494,chr(237)=>494,chr(238)=>494,chr(239)=>494,chr(240)=>0,chr(241)=>329, + chr(242)=>274,chr(243)=>686,chr(244)=>686,chr(245)=>686,chr(246)=>384,chr(247)=>384,chr(248)=>384,chr(249)=>384,chr(250)=>384,chr(251)=>384,chr(252)=>494,chr(253)=>494,chr(254)=>494,chr(255)=>0); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/times.php b/misc/PublicationBulletins/Portail-LeHavre/font/times.php new file mode 100755 index 00000000..837c706e --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/times.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>408,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>180,'('=>333,')'=>333,'*'=>500,'+'=>564, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>564,'='=>564,'>'=>564,'?'=>444,'@'=>921,'A'=>722, + 'B'=>667,'C'=>667,'D'=>722,'E'=>611,'F'=>556,'G'=>722,'H'=>722,'I'=>333,'J'=>389,'K'=>722,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>556,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>722,'W'=>944, + 'X'=>722,'Y'=>722,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>469,'_'=>500,'`'=>333,'a'=>444,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, + 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>333,'s'=>389,'t'=>278,'u'=>500,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>480,'|'=>200,'}'=>480,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>444,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>889,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>444,chr(148)=>444,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>980, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>200,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>564,chr(173)=>333,chr(174)=>760,chr(175)=>333, + chr(176)=>400,chr(177)=>564,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>453,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>444,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>564,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>722,chr(222)=>556,chr(223)=>500,chr(224)=>444,chr(225)=>444,chr(226)=>444,chr(227)=>444,chr(228)=>444,chr(229)=>444,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>564,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>500,chr(254)=>500,chr(255)=>500); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/timesb.php b/misc/PublicationBulletins/Portail-LeHavre/font/timesb.php new file mode 100755 index 00000000..09cff86a --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/timesb.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>555,'#'=>500,'$'=>500,'%'=>1000,'&'=>833,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>930,'A'=>722, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>778,'I'=>389,'J'=>500,'K'=>778,'L'=>667,'M'=>944,'N'=>722,'O'=>778,'P'=>611,'Q'=>778,'R'=>722,'S'=>556,'T'=>667,'U'=>722,'V'=>722,'W'=>1000, + 'X'=>722,'Y'=>722,'Z'=>667,'['=>333,'\\'=>278,']'=>333,'^'=>581,'_'=>500,'`'=>333,'a'=>500,'b'=>556,'c'=>444,'d'=>556,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>333,'k'=>556,'l'=>278,'m'=>833, + 'n'=>556,'o'=>500,'p'=>556,'q'=>556,'r'=>444,'s'=>389,'t'=>333,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>394,'|'=>220,'}'=>394,'~'=>520,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>667,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>300,chr(171)=>500,chr(172)=>570,chr(173)=>333,chr(174)=>747,chr(175)=>333, + chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>556,chr(182)=>540,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>330,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>570,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>722,chr(222)=>611,chr(223)=>556,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/timesbi.php b/misc/PublicationBulletins/Portail-LeHavre/font/timesbi.php new file mode 100755 index 00000000..b4e38d76 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/timesbi.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>389,'"'=>555,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>832,'A'=>667, + 'B'=>667,'C'=>667,'D'=>722,'E'=>667,'F'=>667,'G'=>722,'H'=>778,'I'=>389,'J'=>500,'K'=>667,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>611,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>667,'W'=>889, + 'X'=>667,'Y'=>611,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>570,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, + 'n'=>556,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>556,'v'=>444,'w'=>667,'x'=>500,'y'=>444,'z'=>389,'{'=>348,'|'=>220,'}'=>348,'~'=>570,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>389,chr(159)=>611,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>266,chr(171)=>500,chr(172)=>606,chr(173)=>333,chr(174)=>747,chr(175)=>333, + chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>576,chr(182)=>500,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>300,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>944,chr(199)=>667,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>570,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>611,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>444,chr(254)=>500,chr(255)=>444); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/timesi.php b/misc/PublicationBulletins/Portail-LeHavre/font/timesi.php new file mode 100755 index 00000000..0ba2b777 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/timesi.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>420,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>214,'('=>333,')'=>333,'*'=>500,'+'=>675, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>675,'='=>675,'>'=>675,'?'=>500,'@'=>920,'A'=>611, + 'B'=>611,'C'=>667,'D'=>722,'E'=>611,'F'=>611,'G'=>722,'H'=>722,'I'=>333,'J'=>444,'K'=>667,'L'=>556,'M'=>833,'N'=>667,'O'=>722,'P'=>611,'Q'=>722,'R'=>611,'S'=>500,'T'=>556,'U'=>722,'V'=>611,'W'=>833, + 'X'=>611,'Y'=>556,'Z'=>556,'['=>389,'\\'=>278,']'=>389,'^'=>422,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>278,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>444,'l'=>278,'m'=>722, + 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>500,'v'=>444,'w'=>667,'x'=>444,'y'=>444,'z'=>389,'{'=>400,'|'=>275,'}'=>400,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>556,chr(133)=>889,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>500,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>556,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>556,chr(148)=>556,chr(149)=>350,chr(150)=>500,chr(151)=>889,chr(152)=>333,chr(153)=>980, + chr(154)=>389,chr(155)=>333,chr(156)=>667,chr(157)=>350,chr(158)=>389,chr(159)=>556,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>275,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>675,chr(173)=>333,chr(174)=>760,chr(175)=>333, + chr(176)=>400,chr(177)=>675,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>523,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>611,chr(193)=>611,chr(194)=>611,chr(195)=>611,chr(196)=>611,chr(197)=>611, + chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>667,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>675,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>556,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>675,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>444,chr(254)=>500,chr(255)=>444); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/font/zapfdingbats.php b/misc/PublicationBulletins/Portail-LeHavre/font/zapfdingbats.php new file mode 100755 index 00000000..1f926a8c --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/font/zapfdingbats.php @@ -0,0 +1,15 @@ +0,chr(1)=>0,chr(2)=>0,chr(3)=>0,chr(4)=>0,chr(5)=>0,chr(6)=>0,chr(7)=>0,chr(8)=>0,chr(9)=>0,chr(10)=>0,chr(11)=>0,chr(12)=>0,chr(13)=>0,chr(14)=>0,chr(15)=>0,chr(16)=>0,chr(17)=>0,chr(18)=>0,chr(19)=>0,chr(20)=>0,chr(21)=>0, + chr(22)=>0,chr(23)=>0,chr(24)=>0,chr(25)=>0,chr(26)=>0,chr(27)=>0,chr(28)=>0,chr(29)=>0,chr(30)=>0,chr(31)=>0,' '=>278,'!'=>974,'"'=>961,'#'=>974,'$'=>980,'%'=>719,'&'=>789,'\''=>790,'('=>791,')'=>690,'*'=>960,'+'=>939, + ','=>549,'-'=>855,'.'=>911,'/'=>933,'0'=>911,'1'=>945,'2'=>974,'3'=>755,'4'=>846,'5'=>762,'6'=>761,'7'=>571,'8'=>677,'9'=>763,':'=>760,';'=>759,'<'=>754,'='=>494,'>'=>552,'?'=>537,'@'=>577,'A'=>692, + 'B'=>786,'C'=>788,'D'=>788,'E'=>790,'F'=>793,'G'=>794,'H'=>816,'I'=>823,'J'=>789,'K'=>841,'L'=>823,'M'=>833,'N'=>816,'O'=>831,'P'=>923,'Q'=>744,'R'=>723,'S'=>749,'T'=>790,'U'=>792,'V'=>695,'W'=>776, + 'X'=>768,'Y'=>792,'Z'=>759,'['=>707,'\\'=>708,']'=>682,'^'=>701,'_'=>826,'`'=>815,'a'=>789,'b'=>789,'c'=>707,'d'=>687,'e'=>696,'f'=>689,'g'=>786,'h'=>787,'i'=>713,'j'=>791,'k'=>785,'l'=>791,'m'=>873, + 'n'=>761,'o'=>762,'p'=>762,'q'=>759,'r'=>759,'s'=>892,'t'=>892,'u'=>788,'v'=>784,'w'=>438,'x'=>138,'y'=>277,'z'=>415,'{'=>392,'|'=>392,'}'=>668,'~'=>668,chr(127)=>0,chr(128)=>390,chr(129)=>390,chr(130)=>317,chr(131)=>317, + chr(132)=>276,chr(133)=>276,chr(134)=>509,chr(135)=>509,chr(136)=>410,chr(137)=>410,chr(138)=>234,chr(139)=>234,chr(140)=>334,chr(141)=>334,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, + chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>0,chr(161)=>732,chr(162)=>544,chr(163)=>544,chr(164)=>910,chr(165)=>667,chr(166)=>760,chr(167)=>760,chr(168)=>776,chr(169)=>595,chr(170)=>694,chr(171)=>626,chr(172)=>788,chr(173)=>788,chr(174)=>788,chr(175)=>788, + chr(176)=>788,chr(177)=>788,chr(178)=>788,chr(179)=>788,chr(180)=>788,chr(181)=>788,chr(182)=>788,chr(183)=>788,chr(184)=>788,chr(185)=>788,chr(186)=>788,chr(187)=>788,chr(188)=>788,chr(189)=>788,chr(190)=>788,chr(191)=>788,chr(192)=>788,chr(193)=>788,chr(194)=>788,chr(195)=>788,chr(196)=>788,chr(197)=>788, + chr(198)=>788,chr(199)=>788,chr(200)=>788,chr(201)=>788,chr(202)=>788,chr(203)=>788,chr(204)=>788,chr(205)=>788,chr(206)=>788,chr(207)=>788,chr(208)=>788,chr(209)=>788,chr(210)=>788,chr(211)=>788,chr(212)=>894,chr(213)=>838,chr(214)=>1016,chr(215)=>458,chr(216)=>748,chr(217)=>924,chr(218)=>748,chr(219)=>918, + chr(220)=>927,chr(221)=>928,chr(222)=>928,chr(223)=>834,chr(224)=>873,chr(225)=>828,chr(226)=>924,chr(227)=>924,chr(228)=>917,chr(229)=>930,chr(230)=>931,chr(231)=>463,chr(232)=>883,chr(233)=>836,chr(234)=>836,chr(235)=>867,chr(236)=>867,chr(237)=>696,chr(238)=>696,chr(239)=>874,chr(240)=>0,chr(241)=>874, + chr(242)=>760,chr(243)=>946,chr(244)=>771,chr(245)=>865,chr(246)=>771,chr(247)=>888,chr(248)=>967,chr(249)=>888,chr(250)=>831,chr(251)=>873,chr(252)=>927,chr(253)=>970,chr(254)=>918,chr(255)=>0); +?> diff --git a/misc/PublicationBulletins/Portail-LeHavre/fpdf.php b/misc/PublicationBulletins/Portail-LeHavre/fpdf.php new file mode 100755 index 00000000..cf642dd0 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/fpdf.php @@ -0,0 +1,2105 @@ + * +* License: LGPL * +*******************************************************************************/ + +define('FPDF_VERSION','1.03'); + +class FPDF +{ + +var $unifontSubset; +var $extraFontSubsets = 0; +var $t1asm; + +var $page; //current page number +var $n; //current object number +var $offsets; //array of object offsets +var $buffer; //buffer holding in-memory PDF +var $pages; //array containing pages +var $state; //current document state +var $compress; //compression flag +var $k; //scale factor (number of points in user unit) +var $DefOrientation; //default orientation +var $CurOrientation; //current orientation +var $PageFormats; //available page formats +var $DefPageFormat; //default page format +var $CurPageFormat; //current page format +var $PageSizes; //array storing non-default page sizes +var $wPt,$hPt; //dimensions of current page in points +var $w,$h; //dimensions of current page in user unit +var $lMargin; //left margin +var $tMargin; //top margin +var $rMargin; //right margin +var $bMargin; //page break margin +var $cMargin; //cell margin +var $x,$y; //current position in user unit +var $lasth; //height of last printed cell +var $LineWidth; //line width in user unit +var $CoreFonts; //array of standard font names +var $fonts; //array of used fonts +var $FontFiles; //array of font files +var $diffs; //array of encoding differences +var $FontFamily; //current font family +var $FontStyle; //current font style +var $underline; //underlining flag +var $CurrentFont; //current font info +var $FontSizePt; //current font size in points +var $FontSize; //current font size in user unit +var $DrawColor; //commands for drawing color +var $FillColor; //commands for filling color +var $TextColor; //commands for text color +var $ColorFlag; //indicates whether fill and text colors are different +var $ws; //word spacing +var $images; //array of used images +var $PageLinks; //array of links in pages +var $links; //array of internal links +var $AutoPageBreak; //automatic page breaking +var $PageBreakTrigger; //threshold used to trigger page breaks +var $InHeader; //flag set when processing header +var $InFooter; //flag set when processing footer +var $ZoomMode; //zoom display mode +var $LayoutMode; //layout display mode +var $title; //title +var $subject; //subject +var $author; //author +var $keywords; //keywords +var $creator; //creator +var $AliasNbPages; //alias for total number of pages +var $PDFVersion; //PDF version number + +/******************************************************************************* +* * +* Public methods * +* * +*******************************************************************************/ +function FPDF($orientation='P', $unit='mm', $format='A4') +{ + //Some checks + $this->_dochecks(); + //Initialization of properties + $this->page=0; + $this->n=2; + $this->buffer=''; + $this->pages=array(); + $this->PageSizes=array(); + $this->state=0; + $this->fonts=array(); + $this->FontFiles=array(); + $this->diffs=array(); + $this->images=array(); + $this->links=array(); + $this->InHeader=false; + $this->InFooter=false; + $this->lasth=0; + $this->FontFamily=''; + $this->FontStyle=''; + $this->FontSizePt=12; + $this->underline=false; + $this->DrawColor='0 G'; + $this->FillColor='0 g'; + $this->TextColor='0 g'; + $this->ColorFlag=false; + $this->ws=0; + //Standard fonts + $this->CoreFonts=array('courier'=>'Courier', 'courierB'=>'Courier-Bold', 'courierI'=>'Courier-Oblique', 'courierBI'=>'Courier-BoldOblique', + 'helvetica'=>'Helvetica', 'helveticaB'=>'Helvetica-Bold', 'helveticaI'=>'Helvetica-Oblique', 'helveticaBI'=>'Helvetica-BoldOblique', + 'times'=>'Times-Roman', 'timesB'=>'Times-Bold', 'timesI'=>'Times-Italic', 'timesBI'=>'Times-BoldItalic', + 'symbol'=>'Symbol', 'zapfdingbats'=>'ZapfDingbats'); + //Scale factor + if($unit=='pt') + $this->k=1; + elseif($unit=='mm') + $this->k=72/25.4; + elseif($unit=='cm') + $this->k=72/2.54; + elseif($unit=='in') + $this->k=72; + else + $this->Error('Incorrect unit: '.$unit); + + //Page format + $this->PageFormats=array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28), + 'letter'=>array(612,792), 'legal'=>array(612,1008)); + if(is_string($format)) + $format=$this->_getpageformat($format); + $this->DefPageFormat=$format; + $this->CurPageFormat=$format; + //Page orientation + $orientation=strtolower($orientation); + if($orientation=='p' || $orientation=='portrait') + { + $this->DefOrientation='P'; + $this->w=$this->DefPageFormat[0]; + $this->h=$this->DefPageFormat[1]; + } + elseif($orientation=='l' || $orientation=='landscape') + { + $this->DefOrientation='L'; + $this->w=$this->DefPageFormat[1]; + $this->h=$this->DefPageFormat[0]; + } + else + $this->Error('Incorrect orientation: '.$orientation); + $this->CurOrientation=$this->DefOrientation; + $this->wPt=$this->w*$this->k; + $this->hPt=$this->h*$this->k; + //Page margins (1 cm) + $margin=28.35/$this->k; + $this->SetMargins($margin,$margin); + //Interior cell margin (1 mm) + $this->cMargin=$margin/10; + //Line width (0.2 mm) + $this->LineWidth=.567/$this->k; + //Automatic page break + $this->SetAutoPageBreak(true,2*$margin); + //Full width display mode + $this->SetDisplayMode('fullwidth'); + //Enable compression + $this->SetCompression(true); + //Set default PDF version number + $this->PDFVersion='1.3'; +} + +function SetMargins($left, $top, $right=null) +{ + //Set left, top and right margins + $this->lMargin=$left; + $this->tMargin=$top; + if($right===null) + $right=$left; + $this->rMargin=$right; +} + +function SetLeftMargin($margin) +{ + //Set left margin + $this->lMargin=$margin; + if($this->page>0 && $this->x<$margin) + $this->x=$margin; +} + +function SetTopMargin($margin) +{ + //Set top margin + $this->tMargin=$margin; +} + +function SetRightMargin($margin) +{ + //Set right margin + $this->rMargin=$margin; +} + +function SetAutoPageBreak($auto, $margin=0) +{ + //Set auto page break mode and triggering margin + $this->AutoPageBreak=$auto; + $this->bMargin=$margin; + $this->PageBreakTrigger=$this->h-$margin; +} + +function SetDisplayMode($zoom, $layout='continuous') +{ + //Set display mode in viewer + if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) + $this->ZoomMode=$zoom; + else + $this->Error('Incorrect zoom display mode: '.$zoom); + if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') + $this->LayoutMode=$layout; + else + $this->Error('Incorrect layout display mode: '.$layout); +} + +function SetCompression($compress) +{ + //Set page compression + if(function_exists('gzcompress')) + $this->compress=$compress; + else + $this->compress=false; +} + +function SetTitle($title, $isUTF8=false) +{ + //Title of document + if($isUTF8) + $title=$this->_UTF8toUTF16($title); + $this->title=$title; +} + +function SetSubject($subject, $isUTF8=false) +{ + //Subject of document + if($isUTF8) + $subject=$this->_UTF8toUTF16($subject); + $this->subject=$subject; +} + +function SetAuthor($author, $isUTF8=false) +{ + //Author of document + if($isUTF8) + $author=$this->_UTF8toUTF16($author); + $this->author=$author; +} + +function SetKeywords($keywords, $isUTF8=false) +{ + //Keywords of document + if($isUTF8) + $keywords=$this->_UTF8toUTF16($keywords); + $this->keywords=$keywords; +} + +function SetCreator($creator, $isUTF8=false) +{ + //Creator of document + if($isUTF8) + $creator=$this->_UTF8toUTF16($creator); + $this->creator=$creator; +} + +function AliasNbPages($alias='{nb}') +{ + //Define an alias for total number of pages + $this->AliasNbPages=$alias; +} + +function Error($msg) +{ + //Fatal error + die('FPDF error: '.$msg); +} + +function Open() +{ + //Begin document + $this->state=1; +} + +function Close() +{ + //Terminate document + if($this->state==3) + return; + if($this->page==0) + $this->AddPage(); + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + //Close document + $this->_enddoc(); +} + +function AddPage($orientation='', $format='') +{ + //Start a new page + if($this->state==0) + $this->Open(); + $family=$this->FontFamily; + $style=$this->FontStyle.($this->underline ? 'U' : ''); + $size=$this->FontSizePt; + $lw=$this->LineWidth; + $dc=$this->DrawColor; + $fc=$this->FillColor; + $tc=$this->TextColor; + $cf=$this->ColorFlag; + if($this->page>0) + { + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + } + //Start new page + $this->_beginpage($orientation,$format); + //Set line cap style to square + $this->_out('2 J'); + //Set line width + $this->LineWidth=$lw; + $this->_out(sprintf('%.2F w',$lw*$this->k)); + //Set font + if($family) + $this->SetFont($family,$style,$size); + //Set colors + $this->DrawColor=$dc; + if($dc!='0 G') + $this->_out($dc); + $this->FillColor=$fc; + if($fc!='0 g') + $this->_out($fc); + $this->TextColor=$tc; + $this->ColorFlag=$cf; + //Page header + $this->InHeader=true; + $this->Header(); + $this->InHeader=false; + //Restore line width + if($this->LineWidth!=$lw) + { + $this->LineWidth=$lw; + $this->_out(sprintf('%.2F w',$lw*$this->k)); + } + //Restore font + if($family) + $this->SetFont($family,$style,$size); + //Restore colors + if($this->DrawColor!=$dc) + { + $this->DrawColor=$dc; + $this->_out($dc); + } + if($this->FillColor!=$fc) + { + $this->FillColor=$fc; + $this->_out($fc); + } + $this->TextColor=$tc; + $this->ColorFlag=$cf; +} + +function Header() +{ + //To be implemented in your own inherited class +} + +function Footer() +{ + //To be implemented in your own inherited class +} + +function PageNo() +{ + //Get current page number + return $this->page; +} + +function SetDrawColor($r, $g=null, $b=null) +{ + //Set color for all stroking operations + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->DrawColor=sprintf('%.3F G',$r/255); + else + $this->DrawColor=sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255); + if($this->page>0) + $this->_out($this->DrawColor); +} + +function SetFillColor($r, $g=null, $b=null) +{ + //Set color for all filling operations + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->FillColor=sprintf('%.3F g',$r/255); + else + $this->FillColor=sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); + $this->ColorFlag=($this->FillColor!=$this->TextColor); + if($this->page>0) + $this->_out($this->FillColor); +} + +function SetTextColor($r, $g=null, $b=null) +{ + //Set color for text + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->TextColor=sprintf('%.3F g',$r/255); + else + $this->TextColor=sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); + $this->ColorFlag=($this->FillColor!=$this->TextColor); +} + +function GetStringWidth($s) +{ + //Get width of a string in the current font + $s=(string)$s; + $cw=&$this->CurrentFont['cw']; + $w=0; + if ($this->unifontSubset) { + $unicode = $this->UTF8StringToArray($s); + foreach($unicode as $char) { + if (isset($cw[$char])) { $w+=$cw[$char]; } + else if($char>0 && $char<128 && isset($cw[chr($char)])) { $w+=$cw[chr($char)]; } + else if(isset($this->CurrentFont['desc']['MissingWidth'])) { $w += $this->CurrentFont['desc']['MissingWidth']; } + else if(isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; } + else { $w += 500; } + } + } + else { + $l=strlen($s); + for($i=0;$i<$l;$i++) + $w+=$cw[$s[$i]]; + } + return $w*$this->FontSize/1000; +} + +function SetLineWidth($width) +{ + //Set line width + $this->LineWidth=$width; + if($this->page>0) + $this->_out(sprintf('%.2F w',$width*$this->k)); +} + +function Line($x1, $y1, $x2, $y2) +{ + //Draw a line + $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k)); +} + +function Rect($x, $y, $w, $h, $style='') +{ + //Draw a rectangle + if($style=='F') + $op='f'; + elseif($style=='FD' || $style=='DF') + $op='B'; + else + $op='S'; + $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); +} + + +function AddFont($family, $style='', $file='', $uni=false) +{ + //Add a TrueType or Type1 font + $family=strtolower($family); + if($family=='arial') + $family='helvetica'; + $style=strtoupper($style); + if($style=='IB') + $style='BI'; + if($file=='') { + if ($uni) { + $file=str_replace(' ','',$family).strtolower($style).'.ttf'; + } + else { + $file=str_replace(' ','',$family).strtolower($style).'.php'; + } + } + $fontkey=$family.$style; + if(isset($this->fonts[$fontkey])) + return; + + if ($uni) { + if (defined("_SYSTEM_TTFONTS") && file_exists(_SYSTEM_TTFONTS.$file )) { $ttfilename = _SYSTEM_TTFONTS.$file ; } + else { $ttfilename = $this->_getfontpath().'unifont/'.$file ; } + $filename = $file; + $filename =str_replace(' ','',$filename ); + $filename =str_replace('-','',$filename ); + $unifilename = $this->_getfontpath().'unifont/'.strtolower(substr($filename ,0,(strpos($filename ,'.')))); + $diff = ''; + $enc = ''; + if (file_exists($unifilename.'.mtx.php')) { + include($unifilename.'.mtx.php'); + } + if (!isset($type) || $type != "TrueTypesubset") { + include_once($this->_getfontpath().'unifont/ttfonts.php'); + $ttf = new TTFontFile(); + $ttf->getMetrics($ttfilename); + $cw = $ttf->charWidths; + $type = "TrueTypesubset"; + $name = preg_replace('/ /','',$ttf->fullName); + $desc= array('Ascent'=>round($ttf->ascent), + 'Descent'=>round($ttf->descent), + 'CapHeight'=>round($ttf->capHeight), + 'Flags'=>$ttf->flags, + 'FontBBox'=>'['.round($ttf->bbox[0])." ".round($ttf->bbox[1])." ".round($ttf->bbox[2])." ".round($ttf->bbox[3]).']', + 'ItalicAngle'=>$ttf->italicAngle, + 'StemV'=>round($ttf->stemV), + 'MissingWidth'=>round($ttf->defaultWidth)); + $up = round($ttf->underlinePosition); + $ut = round($ttf->underlineThickness); + //Generate metrics .php file + $s='\n"; + if (is_writable($this->_getfontpath().'unifont')) { + $fh = fopen($unifilename.'.mtx.php',"w"); + fwrite($fh,$s,strlen($s)); + fclose($fh); + } + unset($ttf); + } + if(!isset($name)) { + $this->Error('Problem with the font definition file'); + } + $i = count($this->fonts)+$this->extraFontSubsets+1; + if(!empty($this->AliasNbPages)) + $sbarr = range(0,57); + else + $sbarr = range(0,32); + $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$ttfilename, 'subsets'=>array(0=>$sbarr), 'subsetfontids'=>array($i), 'used'=>false); + unset($cw); + } + else { + include($this->_getfontpath().$file); + if(!isset($name)) + $this->Error('Could not include font definition file'); + $i=count($this->fonts)+$this->extraFontSubsets+1; + $this->fonts[$fontkey]=array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file); + } + + if($diff) + { + //Search existing encodings + $d=0; + $nb=count($this->diffs); + for($i=1;$i<=$nb;$i++) + { + if($this->diffs[$i]==$diff) + { + $d=$i; + break; + } + } + if($d==0) + { + $d=$nb+1; + $this->diffs[$d]=$diff; + } + $this->fonts[$fontkey]['diff']=$d; + } + if($file) + { + if($type=='TrueType') + $this->FontFiles[$file]=array('length1'=>$originalsize); + else if ($uni && $type == "TrueTypesubset") + $this->FontFiles[$file]=array('type'=>"TrueTypesubset"); + else + $this->FontFiles[$file]=array('length1'=>$size1, 'length2'=>$size2); + } +} + +function SetFont($family, $style='', $size=0) +{ + //Select a font; size given in points + global $fpdf_charwidths; + + $family=strtolower($family); + if($family=='') + $family=$this->FontFamily; + if($family=='arial') + $family='helvetica'; + elseif($family=='symbol' || $family=='zapfdingbats') + $style=''; + $style=strtoupper($style); + if(strpos($style,'U')!==false) + { + $this->underline=true; + $style=str_replace('U','',$style); + } + else + $this->underline=false; + if($style=='IB') + $style='BI'; + if($size==0) + $size=$this->FontSizePt; + //Test if font is already selected + if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size) + return; + //Test if used for the first time + $fontkey=$family.$style; + if(!isset($this->fonts[$fontkey])) + { + //Check if one of the standard fonts + if(isset($this->CoreFonts[$fontkey])) + { + if(!isset($fpdf_charwidths[$fontkey])) + { + //Load metric file + $file=$family; + if($family=='times' || $family=='helvetica') + $file.=strtolower($style); + include($this->_getfontpath().$file.'.php'); + if(!isset($fpdf_charwidths[$fontkey])) + $this->Error('Could not include font metric file'); + } + $i=count($this->fonts)+1+$this->extraFontSubsets; + $name=$this->CoreFonts[$fontkey]; + $cw=$fpdf_charwidths[$fontkey]; + $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$name, 'up'=>-100, 'ut'=>50, 'cw'=>$cw); + } + else + $this->Error('Undefined font: '.$family.' '.$style); + } + //Select it + $this->FontFamily=$family; + $this->FontStyle=$style; + $this->FontSizePt=$size; + $this->FontSize=$size/$this->k; + $this->CurrentFont=&$this->fonts[$fontkey]; + if ($this->fonts[$fontkey]['type']=='TrueTypesubset') { $this->unifontSubset = true; } + else { $this->unifontSubset = false; } + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function SetFontSize($size) +{ + //Set font size in points + if($this->FontSizePt==$size) + return; + $this->FontSizePt=$size; + $this->FontSize=$size/$this->k; + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function AddLink() +{ + //Create a new internal link + $n=count($this->links)+1; + $this->links[$n]=array(0, 0); + return $n; +} + +function SetLink($link, $y=0, $page=-1) +{ + //Set destination of internal link + if($y==-1) + $y=$this->y; + if($page==-1) + $page=$this->page; + $this->links[$link]=array($page, $y); +} + +function Link($x, $y, $w, $h, $link) +{ + //Put a link on the page + $this->PageLinks[$this->page][]=array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link); +} + +function Text($x, $y, $txt) +{ + //Output a string + if ($this->unifontSubset) + $txt2 = $this->UTF8toSubset($txt); + else + $txt2='('.$this->_escape($txt).')'; + $s=sprintf('BT %.2F %.2F Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$txt2); + if($this->underline && $txt!='') + $s.=' '.$this->_dounderline($x,$y,$txt); + if($this->ColorFlag) + $s='q '.$this->TextColor.' '.$s.' Q'; + $this->_out($s); +} + +function AcceptPageBreak() +{ + //Accept automatic page break or not + return $this->AutoPageBreak; +} + +function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') +{ + //Output a cell + $k=$this->k; + if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) + { + //Automatic page break + $x=$this->x; + $ws=$this->ws; + if($ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation,$this->CurPageFormat); + $this->x=$x; + if($ws>0) + { + $this->ws=$ws; + $this->_out(sprintf('%.3F Tw',$ws*$k)); + } + } + if($w==0) + $w=$this->w-$this->rMargin-$this->x; + $s=''; + if($fill || $border==1) + { + if($fill) + $op=($border==1) ? 'B' : 'f'; + else + $op='S'; + $s=sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); + } + if(is_string($border)) + { + $x=$this->x; + $y=$this->y; + if(strpos($border,'L')!==false) + $s.=sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); + if(strpos($border,'T')!==false) + $s.=sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); + if(strpos($border,'R')!==false) + $s.=sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + if(strpos($border,'B')!==false) + $s.=sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + } + if($txt!=='') + { + if($align=='R') + $dx=$w-$this->cMargin-$this->GetStringWidth($txt); + elseif($align=='C') + $dx=($w-$this->GetStringWidth($txt))/2; + else + $dx=$this->cMargin; + if($this->ColorFlag) + $s.='q '.$this->TextColor.' '; + if ($this->unifontSubset) + $txt2 = $this->UTF8toSubset($txt); + else + $txt2='('.str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt))).')'; + $s.=sprintf('BT %.2F %.2F Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2); + if($this->underline) + $s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt); + if($this->ColorFlag) + $s.=' Q'; + if($link) + $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link); + } + if($s) + $this->_out($s); + $this->lasth=$h; + if($ln>0) + { + //Go to next line + $this->y+=$h; + if($ln==1) + $this->x=$this->lMargin; + } + else + $this->x+=$w; +} + +function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) +{ + //Output text with automatic or explicit line breaks + $cw=&$this->CurrentFont['cw']; + if($w==0) + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin); + $s=str_replace("\r",'',$txt); + if ($this->unifontSubset) { + $nb=mb_strlen($s, 'utf-8'); + while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--; + } + else { + $nb=strlen($s); + if($nb>0 && $s[$nb-1]=="\n") + $nb--; + } + + $b=0; + if($border) + { + if($border==1) + { + $border='LTRB'; + $b='LRT'; + $b2='LR'; + } + else + { + $b2=''; + if(strpos($border,'L')!==false) + $b2.='L'; + if(strpos($border,'R')!==false) + $b2.='R'; + $b=(strpos($border,'T')!==false) ? $b2.'T' : $b2; + } + } + $sep=-1; + $i=0; + $j=0; + $l=0; + $ns=0; + $nl=1; + while($i<$nb) + { + //Get next character + if ($this->unifontSubset) { + $c = mb_substr($s,$i,1,'UTF-8'); + } + else { + $c=$s[$i]; + } + if($c=="\n") { + //Explicit line break + if($this->ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + $i++; + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + if($border && $nl==2) + $b=$b2; + continue; + } + if($c==' ') + { + $sep=$i; + $ls=$l; + $ns++; + } + + if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } + else { $l += $cw[$c]*$this->FontSize/1000; } + + if($l>$wmax) + { + //Automatic line break + if($sep==-1) + { + if($i==$j) + $i++; + if($this->ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + } + else + { + if($align=='J') + { + $this->ws=($ns>1) ? ($wmax-$ls)/($ns-1) : 0; + $this->_out(sprintf('%.3F Tw',$this->ws*$this->k)); + } + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill); + } + $i=$sep+1; + } + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + if($border && $nl==2) + $b=$b2; + } + else + $i++; + } + //Last chunk + if($this->ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + if($border && strpos($border,'B')!==false) + $b.='B'; + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + $this->x=$this->lMargin; +} + +function Write($h, $txt, $link='') +{ + //Output text in flowing mode + $cw=&$this->CurrentFont['cw']; + $w=$this->w-$this->rMargin-$this->x; + + $wmax=($w-2*$this->cMargin); + $s=str_replace("\r",'',$txt); + if ($this->unifontSubset) { + $nb=mb_strlen($s, 'UTF-8'); + if($nb==1 && $s==" ") { + $this->x += $this->GetStringWidth($s); + return; + } + } + else { + $nb=strlen($s); + } + + $sep=-1; + $i=0; + $j=0; + $l=0; + $nl=1; + while($i<$nb) + { + //Get next character + //Get next character + if ($this->unifontSubset) { + $c = mb_substr($s,$i,1,'UTF-8'); + } + else { + $c=$s[$i]; + } + if($c=="\n") { + //Explicit line break + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); + } + $i++; + $sep=-1; + $j=$i; + $l=0; + if($nl==1) + { + $this->x=$this->lMargin; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin); + } + $nl++; + continue; + } + if($c==' ') + $sep=$i; + + if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } + else { $l += $cw[$c]*$this->FontSize/1000; } + + if($l>$wmax) + { + //Automatic line break + if($sep==-1) + { + if($this->x>$this->lMargin) + { + //Move to next line + $this->x=$this->lMargin; + $this->y+=$h; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin); + $i++; + $nl++; + continue; + } + if($i==$j) + $i++; + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); + } + } + else + { + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),0,2,'',0,$link); + } + else { + $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link); + } + $i=$sep+1; + } + $sep=-1; + $j=$i; + $l=0; + if($nl==1) + { + $this->x=$this->lMargin; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin); + } + $nl++; + } + else + $i++; + } + //Last chunk + if($i!=$j) { + if ($this->unifontSubset) { + $this->Cell($l,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,0,'',0,$link); + } + else { + $this->Cell($l,$h,substr($s,$j),0,0,'',0,$link); + } + } +} + +function Ln($h=null) +{ + //Line feed; default value is last cell height + $this->x=$this->lMargin; + if($h===null) + $this->y+=$this->lasth; + else + $this->y+=$h; +} + +function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') +{ + //Put an image on the page + if(!isset($this->images[$file])) + { + //First use of this image, get info + if($type=='') + { + $pos=strrpos($file,'.'); + if(!$pos) + $this->Error('Image file has no extension and no type was specified: '.$file); + $type=substr($file,$pos+1); + } + $type=strtolower($type); + if($type=='jpeg') + $type='jpg'; + $mtd='_parse'.$type; + if(!method_exists($this,$mtd)) + $this->Error('Unsupported image type: '.$type); + $info=$this->$mtd($file); + $info['i']=count($this->images)+1; + $this->images[$file]=$info; + } + else + $info=$this->images[$file]; + //Automatic width and height calculation if needed + if($w==0 && $h==0) + { + //Put image at 72 dpi + $w=$info['w']/$this->k; + $h=$info['h']/$this->k; + } + elseif($w==0) + $w=$h*$info['w']/$info['h']; + elseif($h==0) + $h=$w*$info['h']/$info['w']; + //Flowing mode + if($y===null) + { + if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) + { + //Automatic page break + $x2=$this->x; + $this->AddPage($this->CurOrientation,$this->CurPageFormat); + $this->x=$x2; + } + $y=$this->y; + $this->y+=$h; + } + if($x===null) + $x=$this->x; + $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i'])); + if($link) + $this->Link($x,$y,$w,$h,$link); +} + +function GetX() +{ + //Get x position + return $this->x; +} + +function SetX($x) +{ + //Set x position + if($x>=0) + $this->x=$x; + else + $this->x=$this->w+$x; +} + +function GetY() +{ + //Get y position + return $this->y; +} + +function SetY($y) +{ + //Set y position and reset x + $this->x=$this->lMargin; + if($y>=0) + $this->y=$y; + else + $this->y=$this->h+$y; +} + +function SetXY($x, $y) +{ + //Set x and y positions + $this->SetY($y); + $this->SetX($x); +} + +function Output($name='', $dest='') +{ + //Output PDF to some destination + if($this->state<3) + $this->Close(); + $dest=strtoupper($dest); + if($dest=='') + { + if($name=='') + { + $name='doc.pdf'; + $dest='I'; + } + else + $dest='F'; + } + switch($dest) + { + case 'I': + //Send to standard output + if(ob_get_length()) + $this->Error('Some data has already been output, can\'t send PDF file'); + if(php_sapi_name()!='cli') + { + //We send to a browser + header('Content-Type: application/pdf'); + if(headers_sent()) + $this->Error('Some data has already been output, can\'t send PDF file'); + header('Content-Length: '.strlen($this->buffer)); + header('Content-Disposition: inline; filename="'.$name.'"'); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + ini_set('zlib.output_compression','0'); + } + echo $this->buffer; + break; + case 'D': + //Download file + if(ob_get_length()) + $this->Error('Some data has already been output, can\'t send PDF file'); + header('Content-Type: application/x-download'); + if(headers_sent()) + $this->Error('Some data has already been output, can\'t send PDF file'); + header('Content-Length: '.strlen($this->buffer)); + header('Content-Disposition: attachment; filename="'.$name.'"'); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + ini_set('zlib.output_compression','0'); + echo $this->buffer; + break; + case 'F': + //Save to local file + $f=fopen($name,'wb'); + if(!$f) + $this->Error('Unable to create output file: '.$name); + fwrite($f,$this->buffer,strlen($this->buffer)); + fclose($f); + break; + case 'S': + //Return as a string + return $this->buffer; + default: + $this->Error('Incorrect output destination: '.$dest); + } + return ''; +} + +/******************************************************************************* +* * +* Protected methods * +* * +*******************************************************************************/ +function _dochecks() +{ + //Check availability of %F + if(sprintf('%.1F',1.0)!='1.0') + $this->Error('This version of PHP is not supported'); + //Check availability of mbstring + if(!function_exists('mb_strlen')) + $this->Error('mbstring extension is not available'); + //Check mbstring overloading + if(ini_get('mbstring.func_overload') & 2) + $this->Error('mbstring overloading must be disabled'); + //Disable runtime magic quotes + if(get_magic_quotes_runtime()) + @set_magic_quotes_runtime(0); +} + +function _getpageformat($format) +{ + $format=strtolower($format); + if(!isset($this->PageFormats[$format])) + $this->Error('Unknown page format: '.$format); + $a=$this->PageFormats[$format]; + return array($a[0]/$this->k, $a[1]/$this->k); +} + +function _getfontpath() +{ + if(!defined('FPDF_FONTPATH') && is_dir(dirname(__FILE__).'/font')) + define('FPDF_FONTPATH',dirname(__FILE__).'/font/'); + return defined('FPDF_FONTPATH') ? FPDF_FONTPATH : ''; +} + +function _beginpage($orientation, $format) +{ + $this->page++; + $this->pages[$this->page]=''; + $this->state=2; + $this->x=$this->lMargin; + $this->y=$this->tMargin; + $this->FontFamily=''; + //Check page size + if($orientation=='') + $orientation=$this->DefOrientation; + else + $orientation=strtoupper($orientation[0]); + if($format=='') + $format=$this->DefPageFormat; + else + { + if(is_string($format)) + $format=$this->_getpageformat($format); + } + if($orientation!=$this->CurOrientation || $format[0]!=$this->CurPageFormat[0] || $format[1]!=$this->CurPageFormat[1]) + { + //New size + if($orientation=='P') + { + $this->w=$format[0]; + $this->h=$format[1]; + } + else + { + $this->w=$format[1]; + $this->h=$format[0]; + } + $this->wPt=$this->w*$this->k; + $this->hPt=$this->h*$this->k; + $this->PageBreakTrigger=$this->h-$this->bMargin; + $this->CurOrientation=$orientation; + $this->CurPageFormat=$format; + } + if($orientation!=$this->DefOrientation || $format[0]!=$this->DefPageFormat[0] || $format[1]!=$this->DefPageFormat[1]) + $this->PageSizes[$this->page]=array($this->wPt, $this->hPt); +} + +function _endpage() +{ + $this->state=1; +} + +function _escape($s) +{ + //Escape special characters in strings + $s=str_replace('\\','\\\\',$s); + $s=str_replace('(','\\(',$s); + $s=str_replace(')','\\)',$s); + $s=str_replace("\r",'\\r',$s); + return $s; +} + +function _textstring($s) +{ + //Format a text string + return '('.$this->_escape($s).')'; +} + +function _UTF8toUTF16($s) +{ + //Convert UTF-8 to UTF-16BE with BOM + $res="\xFE\xFF"; + $nb=strlen($s); + $i=0; + while($i<$nb) + { + $c1=ord($s[$i++]); + if($c1>=224) + { + //3-byte character + $c2=ord($s[$i++]); + $c3=ord($s[$i++]); + $res.=chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2)); + $res.=chr((($c2 & 0x03)<<6) + ($c3 & 0x3F)); + } + elseif($c1>=192) + { + //2-byte character + $c2=ord($s[$i++]); + $res.=chr(($c1 & 0x1C)>>2); + $res.=chr((($c1 & 0x03)<<6) + ($c2 & 0x3F)); + } + else + { + //Single-byte character + $res.="\0".chr($c1); + } + } + return $res; +} + +function _dounderline($x, $y, $txt) +{ + //Underline text + $up=$this->CurrentFont['up']; + $ut=$this->CurrentFont['ut']; + $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); + return sprintf('%.2F %.2F %.2F %.2F re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt); +} + +function _parsejpg($file) +{ + //Extract info from a JPEG file + $a=GetImageSize($file); + if(!$a) + $this->Error('Missing or incorrect image file: '.$file); + if($a[2]!=2) + $this->Error('Not a JPEG file: '.$file); + if(!isset($a['channels']) || $a['channels']==3) + $colspace='DeviceRGB'; + elseif($a['channels']==4) + $colspace='DeviceCMYK'; + else + $colspace='DeviceGray'; + $bpc=isset($a['bits']) ? $a['bits'] : 8; + //Read whole file + $f=fopen($file,'rb'); + $data=''; + while(!feof($f)) + $data.=fread($f,8192); + fclose($f); + return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data); +} + +function _parsepng($file) +{ + //Extract info from a PNG file + $f=fopen($file,'rb'); + if(!$f) + $this->Error('Can\'t open image file: '.$file); + //Check signature + if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) + $this->Error('Not a PNG file: '.$file); + //Read header chunk + $this->_readstream($f,4); + if($this->_readstream($f,4)!='IHDR') + $this->Error('Incorrect PNG file: '.$file); + $w=$this->_readint($f); + $h=$this->_readint($f); + $bpc=ord($this->_readstream($f,1)); + if($bpc>8) + $this->Error('16-bit depth not supported: '.$file); + $ct=ord($this->_readstream($f,1)); + if($ct==0) + $colspace='DeviceGray'; + elseif($ct==2) + $colspace='DeviceRGB'; + elseif($ct==3) + $colspace='Indexed'; + else + $this->Error('Alpha channel not supported: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Unknown compression method: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Unknown filter method: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Interlacing not supported: '.$file); + $this->_readstream($f,4); + $parms='/DecodeParms <>'; + //Scan chunks looking for palette, transparency and image data + $pal=''; + $trns=''; + $data=''; + do + { + $n=$this->_readint($f); + $type=$this->_readstream($f,4); + if($type=='PLTE') + { + //Read palette + $pal=$this->_readstream($f,$n); + $this->_readstream($f,4); + } + elseif($type=='tRNS') + { + //Read transparency info + $t=$this->_readstream($f,$n); + if($ct==0) + $trns=array(ord(substr($t,1,1))); + elseif($ct==2) + $trns=array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); + else + { + $pos=strpos($t,chr(0)); + if($pos!==false) + $trns=array($pos); + } + $this->_readstream($f,4); + } + elseif($type=='IDAT') + { + //Read image data block + $data.=$this->_readstream($f,$n); + $this->_readstream($f,4); + } + elseif($type=='IEND') + break; + else + $this->_readstream($f,$n+4); + } + while($n); + if($colspace=='Indexed' && empty($pal)) + $this->Error('Missing palette in '.$file); + fclose($f); + return array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'parms'=>$parms, 'pal'=>$pal, 'trns'=>$trns, 'data'=>$data); +} + +function _readstream($f, $n) +{ + //Read n bytes from stream + $res=''; + while($n>0 && !feof($f)) + { + $s=fread($f,$n); + if($s===false) + $this->Error('Error while reading stream'); + $n-=strlen($s); + $res.=$s; + } + if($n>0) + $this->Error('Unexpected end of stream'); + return $res; +} + +function _readint($f) +{ + //Read a 4-byte integer from stream + $a=unpack('Ni',$this->_readstream($f,4)); + return $a['i']; +} + +function _parsegif($file) +{ + //Extract info from a GIF file (via PNG conversion) + if(!function_exists('imagepng')) + $this->Error('GD extension is required for GIF support'); + if(!function_exists('imagecreatefromgif')) + $this->Error('GD has no GIF read support'); + $im=imagecreatefromgif($file); + if(!$im) + $this->Error('Missing or incorrect image file: '.$file); + imageinterlace($im,0); + $tmp=tempnam('.','gif'); + if(!$tmp) + $this->Error('Unable to create a temporary file'); + if(!imagepng($im,$tmp)) + $this->Error('Error while saving to temporary file'); + imagedestroy($im); + $info=$this->_parsepng($tmp); + unlink($tmp); + return $info; +} + +function _newobj() +{ + //Begin a new object + $this->n++; + $this->offsets[$this->n]=strlen($this->buffer); + $this->_out($this->n.' 0 obj'); +} + +function _putstream($s) +{ + $this->_out('stream'); + $this->_out($s); + $this->_out('endstream'); +} + +function _out($s) +{ + //Add a line to the document + if($this->state==2) + $this->pages[$this->page].=$s."\n"; + else + $this->buffer.=$s."\n"; +} + +function _putpages() +{ + $nb=$this->page; + if(!empty($this->AliasNbPages)) + { + //Replace number of pages in fonts using subsets + $r = ''; + $nstr = "$nb"; + for($i=0;$ipages[$n]=str_replace('`'.$this->AliasNbPages.'`',$r,$this->pages[$n]); + // Now repeat for no pages in non-subset fonts + $r = $nb; + //Replace number of pages + for($n=1;$n<=$nb;$n++) + $this->pages[$n]=str_replace($this->AliasNbPages,$r,$this->pages[$n]); + } + if($this->DefOrientation=='P') + { + $wPt=$this->DefPageFormat[0]*$this->k; + $hPt=$this->DefPageFormat[1]*$this->k; + } + else + { + $wPt=$this->DefPageFormat[1]*$this->k; + $hPt=$this->DefPageFormat[0]*$this->k; + } + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + for($n=1;$n<=$nb;$n++) + { + //Page + $this->_newobj(); + $this->_out('<_out('/Parent 1 0 R'); + if(isset($this->PageSizes[$n])) + $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageSizes[$n][0],$this->PageSizes[$n][1])); + $this->_out('/Resources 2 0 R'); + if(isset($this->PageLinks[$n])) + { + //Links + $annots='/Annots ['; + foreach($this->PageLinks[$n] as $pl) + { + $rect=sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); + $annots.='<_textstring($pl[4]).'>>>>'; + else + { + $l=$this->links[$pl[4]]; + $h=isset($this->PageSizes[$l[0]]) ? $this->PageSizes[$l[0]][1] : $hPt; + $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',1+2*$l[0],$h-$l[1]*$this->k); + } + } + $this->_out($annots.']'); + } + $this->_out('/Contents '.($this->n+1).' 0 R>>'); + $this->_out('endobj'); + //Page content + $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; + $this->_newobj(); + $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); + $this->_putstream($p); + $this->_out('endobj'); + } + //Pages root + $this->offsets[1]=strlen($this->buffer); + $this->_out('1 0 obj'); + $this->_out('<_out($kids.']'); + $this->_out('/Count '.$nb); + $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$wPt,$hPt)); + $this->_out('>>'); + $this->_out('endobj'); +} + +function _putfonts() +{ + $nf=$this->n; + foreach($this->diffs as $diff) + { + //Encodings + $this->_newobj(); + $this->_out('<>'); + $this->_out('endobj'); + } + foreach($this->FontFiles as $file=>$info) + { + if (!isset($info['type']) || $info['type']!='TrueTypesubset') { + //Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n']=$this->n; + $font=''; + $f=fopen($this->_getfontpath().$file,'rb',1); + if(!$f) + $this->Error('Font file not found'); + while(!feof($f)) + $font.=fread($f,8192); + fclose($f); + $compressed=(substr($file,-2)=='.z'); + if(!$compressed && isset($info['length2'])) + { + $header=(ord($font[0])==128); + if($header) + { + //Strip first binary header + $font=substr($font,6); + } + if($header && ord($font[$info['length1']])==128) + { + //Strip second binary header + $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); + } + } + $this->_out('<_out('/Filter /FlateDecode'); + $this->_out('/Length1 '.$info['length1']); + if(isset($info['length2'])) + $this->_out('/Length2 '.$info['length2'].' /Length3 0'); + $this->_out('>>'); + $this->_putstream($font); + $this->_out('endobj'); + } + } + foreach($this->fonts as $k=>$font) + { + //Font objects + //$this->fonts[$k]['n']=$this->n+1; + $type=$font['type']; + $name=$font['name']; + if($type=='core') + { + //Standard font + $this->fonts[$k]['n']=$this->n+1; + $this->_newobj(); + $this->_out('<_out('/BaseFont /'.$name); + $this->_out('/Subtype /Type1'); + if($name!='Symbol' && $name!='ZapfDingbats') + $this->_out('/Encoding /WinAnsiEncoding'); + $this->_out('>>'); + $this->_out('endobj'); + } + elseif($type=='Type1' || $type=='TrueType') + { + //Additional Type1 or TrueType font + $this->fonts[$k]['n']=$this->n+1; + $this->_newobj(); + $this->_out('<_out('/BaseFont /'.$name); + $this->_out('/Subtype /'.$type); + $this->_out('/FirstChar 32 /LastChar 255'); + $this->_out('/Widths '.($this->n+1).' 0 R'); + $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); + if($font['enc']) + { + if(isset($font['diff'])) + $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); + else + $this->_out('/Encoding /WinAnsiEncoding'); + } + $this->_out('>>'); + $this->_out('endobj'); + //Widths + $this->_newobj(); + $cw=&$font['cw']; + $s='['; + for($i=32;$i<=255;$i++) + $s.=$cw[chr($i)].' '; + $this->_out($s.']'); + $this->_out('endobj'); + //Descriptor + $this->_newobj(); + $s='<$v) + $s.=' /'.$k.' '.$v; + $file=$font['file']; + if($file) + $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; + $this->_out($s.'>>'); + $this->_out('endobj'); + } + // TrueType embedded SUBSETS + else if ($type=='TrueTypesubset') { + $ssfaid="A"; + include_once($this->_getfontpath().'unifont/ttfonts.php'); + $ttf = new TTFontFile(); + $ttf->getMetrics($font['file'], 1); + for($sfid=0;$sfidfonts[$k]['n'][$sfid]=$this->n+1; // NB an array for subset + $subsetname = 'MPDFA'.$ssfaid.'+'.$font['name']; + $ssfaid++; + $subset = $font['subsets'][$sfid]; + unset($subset[0]); + $ttfontstream = $ttf->makeSubset($subset); + $ttfontsize = strlen($ttfontstream); + $fontstream = gzcompress($ttfontstream); + $widthstring = ''; + $toUnistring = ''; + foreach($font['subsets'][$sfid] AS $cp=>$u) { + if (isset($font['cw'][$u])) { + $widthstring .= $font['cw'][$u].' '; + } + else { + $widthstring .= $ttf->defaultWidth.' '; + } + $toUnistring .= sprintf("<%02s> <%04s>\n", strtoupper(dechex($cp)), strtoupper(dechex($u))); + } + + //Additional Type1 or TrueType font + $this->_newobj(); + $this->_out('<_out('/BaseFont /'.$subsetname); + $this->_out('/Subtype /TrueType'); + $this->_out('/FirstChar 0 /LastChar '.(count($font['subsets'][$sfid]))); + $this->_out('/Widths '.($this->n+1).' 0 R'); + $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); + $this->_out('/ToUnicode '.($this->n + 3).' 0 R'); + $this->_out('>>'); + $this->_out('endobj'); + + //Widths + $this->_newobj(); + $this->_out('['.$widthstring.']'); + $this->_out('endobj'); + + //Descriptor + $this->_newobj(); + $s='<$v) { + if ($kd == 'Flags') { $v = $v | 4; $v = $v & ~32; } + $s.=' /'.$kd.' '.$v."\n"; + } + + $s.='/FontFile2 '.($this->n + 2).' 0 R'; + $this->_out($s.'>>'); + $this->_out('endobj'); + + // ToUnicode + $toUni = "/CIDInit /ProcSet findresource begin\n"; + $toUni .= "12 dict begin\n"; + $toUni .= "begincmap\n"; + $toUni .= "/CIDSystemInfo\n"; + $toUni .= "<_newobj(); + $this->_out('<>'); + $this->_putstream($toUni); + $this->_out('endobj'); + + //Font file + $this->_newobj(); + $this->_out('<_out('/Filter /FlateDecode'); + $this->_out('/Length1 '.$ttfontsize); + $this->_out('>>'); + $this->_putstream($fontstream); + $this->_out('endobj'); + } + unset($ttf); + } + else + { + //Allow for additional types + $this->fonts[$k]['n']=$this->n+1; + $mtd='_put'.strtolower($type); + if(!method_exists($this,$mtd)) + $this->Error('Unsupported font type: '.$type); + $this->$mtd($font); + } + } +} + +function _putimages() +{ + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + reset($this->images); + while(list($file,$info)=each($this->images)) + { + $this->_newobj(); + $this->images[$file]['n']=$this->n; + $this->_out('<_out('/Subtype /Image'); + $this->_out('/Width '.$info['w']); + $this->_out('/Height '.$info['h']); + if($info['cs']=='Indexed') + $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + else + { + $this->_out('/ColorSpace /'.$info['cs']); + if($info['cs']=='DeviceCMYK') + $this->_out('/Decode [1 0 1 0 1 0 1 0]'); + } + $this->_out('/BitsPerComponent '.$info['bpc']); + if(isset($info['f'])) + $this->_out('/Filter /'.$info['f']); + if(isset($info['parms'])) + $this->_out($info['parms']); + if(isset($info['trns']) && is_array($info['trns'])) + { + $trns=''; + for($i=0;$i_out('/Mask ['.$trns.']'); + } + $this->_out('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + unset($this->images[$file]['data']); + $this->_out('endobj'); + //Palette + if($info['cs']=='Indexed') + { + $this->_newobj(); + $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; + $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); + $this->_putstream($pal); + $this->_out('endobj'); + } + } +} + +function _putxobjectdict() +{ + foreach($this->images as $image) + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); +} + +function _putresourcedict() +{ + $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_out('/Font <<'); + foreach($this->fonts as $font) { + if ($font['type']=='TrueTypesubset') { + foreach($font['n'] AS $k => $fid) { + $this->_out('/F'.$font['subsetfontids'][$k].' '.$font['n'][$k].' 0 R'); + } + } + else { + $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); + } + } + $this->_out('>>'); + $this->_out('/XObject <<'); + $this->_putxobjectdict(); + $this->_out('>>'); +} + +function _putresources() +{ + $this->_putfonts(); + $this->_putimages(); + //Resource dictionary + $this->offsets[2]=strlen($this->buffer); + $this->_out('2 0 obj'); + $this->_out('<<'); + $this->_putresourcedict(); + $this->_out('>>'); + $this->_out('endobj'); +} + +function _putinfo() +{ + $this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION)); + if(!empty($this->title)) + $this->_out('/Title '.$this->_textstring($this->title)); + if(!empty($this->subject)) + $this->_out('/Subject '.$this->_textstring($this->subject)); + if(!empty($this->author)) + $this->_out('/Author '.$this->_textstring($this->author)); + if(!empty($this->keywords)) + $this->_out('/Keywords '.$this->_textstring($this->keywords)); + if(!empty($this->creator)) + $this->_out('/Creator '.$this->_textstring($this->creator)); + $this->_out('/CreationDate '.$this->_textstring('D:'.@date('YmdHis'))); +} + +function _putcatalog() +{ + $this->_out('/Type /Catalog'); + $this->_out('/Pages 1 0 R'); + if($this->ZoomMode=='fullpage') + $this->_out('/OpenAction [3 0 R /Fit]'); + elseif($this->ZoomMode=='fullwidth') + $this->_out('/OpenAction [3 0 R /FitH null]'); + elseif($this->ZoomMode=='real') + $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); + elseif(!is_string($this->ZoomMode)) + $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']'); + if($this->LayoutMode=='single') + $this->_out('/PageLayout /SinglePage'); + elseif($this->LayoutMode=='continuous') + $this->_out('/PageLayout /OneColumn'); + elseif($this->LayoutMode=='two') + $this->_out('/PageLayout /TwoColumnLeft'); +} + +function _putheader() +{ + $this->_out('%PDF-'.$this->PDFVersion); +} + +function _puttrailer() +{ + $this->_out('/Size '.($this->n+1)); + $this->_out('/Root '.$this->n.' 0 R'); + $this->_out('/Info '.($this->n-1).' 0 R'); +} + +function _enddoc() +{ + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + //Info + $this->_newobj(); + $this->_out('<<'); + $this->_putinfo(); + $this->_out('>>'); + $this->_out('endobj'); + //Catalog + $this->_newobj(); + $this->_out('<<'); + $this->_putcatalog(); + $this->_out('>>'); + $this->_out('endobj'); + //Cross-ref + $o=strlen($this->buffer); + $this->_out('xref'); + $this->_out('0 '.($this->n+1)); + $this->_out('0000000000 65535 f '); + for($i=1;$i<=$this->n;$i++) + $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); + //Trailer + $this->_out('trailer'); + $this->_out('<<'); + $this->_puttrailer(); + $this->_out('>>'); + $this->_out('startxref'); + $this->_out($o); + $this->_out('%%EOF'); + $this->state=3; +} + +// ********* NEW FUNCTIONS ********* + +// Convert utf-8 string to for Font Subsets +function UTF8toSubset($str) { + $ret = '<'; + if ($this->AliasNbPages) + $str = preg_replace('/'.preg_quote($this->AliasNbPages,'/').'/', chr(7), $str ); + $unicode = $this->UTF8StringToArray($str); + $orig_fid = $this->CurrentFont['subsetfontids'][0]; + $last_fid = $this->CurrentFont['subsetfontids'][0]; + foreach($unicode as $c) { + if ($c == 7) { + if ($orig_fid != $last_fid) { + $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <'; + $last_fid = $orig_fid; + } + $ret .= '`'.$this->AliasNbPages.'`'; + continue; + } + for ($i=0; $i<99; $i++) { + // return c as decimal char + $init = array_search($c, $this->CurrentFont['subsets'][$i]); + if ($init!==false) { + if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) { + $ret .= '> Tj /F'.$this->CurrentFont['subsetfontids'][$i].' '.$this->FontSizePt.' Tf <'; + $last_fid = $this->CurrentFont['subsetfontids'][$i]; + } + $ret .= sprintf("%02s", strtoupper(dechex($init))); + break; + } + else if (count($this->CurrentFont['subsets'][$i]) < 255) { + $n = count($this->CurrentFont['subsets'][$i]); + $this->CurrentFont['subsets'][$i][$n] = $c; + if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) { + $ret .= '> Tj /F'.$this->CurrentFont['subsetfontids'][$i].' '.$this->FontSizePt.' Tf <'; + $last_fid = $this->CurrentFont['subsetfontids'][$i]; + } + $ret .= sprintf("%02s", strtoupper(dechex($n))); + break; + } + else if (!isset($this->CurrentFont['subsets'][($i+1)])) { + $this->CurrentFont['subsets'][($i+1)] = array(0=>0); + $new_fid = count($this->fonts)+$this->extraFontSubsets+1; + $this->CurrentFont['subsetfontids'][($i+1)] = $new_fid; + $this->extraFontSubsets++; + } + } + } + $ret .= '>'; + if ($last_fid != $orig_fid) { + $ret .= ' Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <> '; + } + return $ret; +} + +// Converts UTF-8 strings to codepoints array +function UTF8StringToArray($str) { + $out = array(); + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $h = ord($str[$i]); + if ( $h <= 0x7F ) + $out[] = $h; + elseif ( $h >= 0xC2 ) { + if ( ($h <= 0xDF) && ($i < $len -1) ) + $out[] = ($h & 0x1F) << 6 | (ord($str{++$i}) & 0x3F); + elseif ( ($h <= 0xEF) && ($i < $len -2) ) + $out[] = ($h & 0x0F) << 12 | (ord($str{++$i}) & 0x3F) << 6 + | (ord($str{++$i}) & 0x3F); + elseif ( ($h <= 0xF4) && ($i < $len -3) ) + $out[] = ($h & 0x0F) << 18 | (ord($str{++$i}) & 0x3F) << 12 + | (ord($str{++$i}) & 0x3F) << 6 + | (ord($str{++$i}) & 0x3F); + } + } + return $out; +} + + +//End of class +} + +//Handle special IE contype request +if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype') +{ + header('Content-Type: application/pdf'); + exit; +} + +?> \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/imgs/Voir_abs.png b/misc/PublicationBulletins/Portail-LeHavre/imgs/Voir_abs.png new file mode 100755 index 0000000000000000000000000000000000000000..36de4af7e2745aa3fa8c39667f5eaa504f4edda2 GIT binary patch literal 29035 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 z z0e44oqDF7u!0rJ8B~#C@mke9s1L$$q#+Qb+t;vRgiUZ@@Lt;sDj}Z6k3-npd2zN8J zM0E7@&D9nb1x$`}<}rpeqFD5T#S8gw*^u0~Pr*kmf^2lL zF-DBq{T%;BG){H=oK0Fvx%KAG(LsR5QB zy;aqHRGj%GB6yH$t0;_I5sVR7ed3DYr2+7Dq-FE5@vX~NFNTGh&hy<+BBc9;1O)0E zv4wc^UpdK<{mi`wytdmis%545O$~)fFu!22#DE$+FJ1??1iN}dy6_zm{GKr&H(pnd zztEJ*c+T+{njK=crq(mf7IR1pu@YRTMGZmvGs~X|c1`{WBp+t3OWxYgrA#)dibY4W z{kCnlPD&#dL`8VG(*gf@`Z*|6Z}sybaFR*xOePy!J_u?Mhns@QMItrLZi4vESkz>^ zf{fBWdy?)Nm=Cg~U3!H8`4!nEw&+JjMDo`72|8k{T9Nb{k0m?n2jYxR6lRm~Oq2=r znnlrwCU`cLLx=|2Q3oDr|93ViQRS{9gLTWE1zsi97a&0iI(m^^*ll5`?Fl1~9;F7W zb*^L&Ph>^{uV>lN**a*rEzg;_QCkh9)tYe*cS=knHf>Q|$$a9Iullf8Q9{pb$DRA3 zyz^N@)S}h7zdGbmSKYNSA?T4W7b5IFxj+wv5(<|TEOikVhc#A0Os`X){++QJJwCq7 z@s!kZM!miG(*R#PNI~pzbY;X+HVw10GGYZ)3aNUs%XJxs5(wA-WV=xZB|#GZ5h7l} zRfj3FU6JT8{Gq@ckd~|ERYj7EAdk9k+3gd)g=%@Z>6@JMRz+x?3=(jD5O~)vu*y)m z(-dA|#&(>Zo+#C}b~2(ty*rpzw1K^ThxFi5?OXAU;hD<>e*ebYR~^LNJ)VNB2*%aa zR*mZb{Ip`!-ZnusHF z`2UVVYaa+M_6jr}>AbV%F>FMlaLjHwB_tNEnjhWI%g{AefNxg%{uF&dl<7CXzd{p> z?)(`DZP-V+qdc%X3-O&Yswtc2N6uYW!tV~Amy$DH*`v}!&F*KHjzH#DQ0VzI!-dq|2bz12SG9O?elu_2;!Nezbf6h z9z27iX3Xf}AhQpA->q^pmckvNW5~bkIj5jA{V^mY-F*>F)~Dy3^esqDSq8n&rUdLD zK_saFADLydc3681Lq47#!a1lbcbkJ>4esksaT}dX@cj2wsP3B4z5@L_sm~wy)UTLA$Mvr}JQIG|(XvGJ;tQ0} zhj)-C-IXqb4*#q_AV@>Z^>Drw&HG}5-xu-FEkU8)b&XJ9A$izg&FMt};#-S3xJMO? zZ?oOv7H^;nvpl-GS>sYk?qX8Tq>h`z?QF0AokRzbxL)Y`4&LkT8$$N9f*ZolBM@77 z_BdN-X6{5CX|0>ttY*IherTYJS|n2aGM{=8BQ`9)L($tl{d!ovyHOu}%IYi$9QvmE z2Eq%1<3Jm)>jD5G6aXH50)RM9I!7kX93RKtCh4)V!_%Feg2WDa6ucmKYHW^1*ZFL#uw~Lfovht%s zK^2B|R)hqX?3gvm!}kBo rC5*C$=EZ`K?v4~W{r`+KuJJk3?)EiJxYm{?6&ik@a~dtUFFuZxBs&{@V>@S zk?HO|#u~T$gy`vcoZztqPAvmbxh zqYJcvdT2!GLF9>0eliHfFsdr4^w0JJ5|ym_A5l#lUzGZLw2fyJOCPpg?D?Nx^k8<* zMYz;G&~qQ&0rxK~3@zD``5r;dsi|g9&%tYQEt}ILBk}i7=XJ$H`)F6R#HhjUK(6W43nkJ@j94(F2f0J_7BQ-3zl{H`ETVd|!&l+(d)@AKr!R-WJOaI=iC*+s?P=Ep2MuUpy zls7O+&3xH@bNuspYr;oe{Px{a%&VRB$jOnL;J5}!@Zeysn7St$(iitac_+Om#LF|9 z?`7Ky#792@;;aIyhfO_ms7m8@bO1^9(#iPF>p0{g5p4K_aBOVXgs2zqx_ctD$x_wt zn9Bt)1LtvtrIOdmtZ3o&QiI;h89KVMQw8fI-#@HoE^|C{+)(G2YEDkR8W#4D%$e|_ zb{&3q_p-RZ+uX8U*4)N@XdfvGck>Iw@6(xjX6AJu3$%wF^o5j|1@s2PC_ThmJc>xe z&43{OazlEmNSuqGV9+HZ>(^fL1WtkwlVHB$+ndK_Ea!=hz2Yn0zJo%qGwkA*-Z-uE z%gg*;8fLF%Xg!xvxbgxaf6~sW?vbc5dw@QV8VTBYvXr&Byt*h$4YVW zHNX$Dy$CI3M`|9CyuDsW(5@fJHa| zv;Nc}_92ff#8Sk~tp^?IvETLF>Tom}^2~noIIyyvaCdlnJ=F&2rXF-B`-s0i<`n#3 zdiHyrNLjn2N*rC~^k0eS83UD)pkj=7Hejr~+T^8p!w$G->@@_G2QCk44d#@GNWUOO zuDrIj@x6wB!?Cx}OCaDXoO#@-3S+n7INu^4-x+Z3`8mVu8WL6o9zJi$nrh>G-_1!5 zg5XU;H3sko`VbGoP@CH*K-|z1V}dP3eqY{l3BIm>XTOSz+jO2YaM-O1l})66K^h|a z>qmfXii+ylTJK|9y^X2NkpR6DKj1l?lY}7c=p{-62RJU+g^)lRhw(f<=`2l#1 z2p|>M3a>e^LMOLgfaR1l7Dmc+&$TtaCNr4)r)SmeWeHPVykq}cxV-nJI;}=46nES8 z;mX4aA3SBRtZv4%CURS zlmT@xyBq^$ArPy7tWZ{~kyn)da7{!-X=y0+uB0;_n<*ka+S=v-Lm~NiX1`ubtIZ5X z_phy~qx#a|#pG`X(x0F7Qa(yD4E}G_qOq}Dv101H1?HCfo*#}f?w2M*e{HFvMD_-&A@TMg3&`jTGng_8@0h--2D_q8T1mebRlx=Yagxdt0* z8P3-KRS0?}wI@@Kxp7r!w37Pe4Iu{^?JNcW6 z1~glql8;~oiftq@-v(orZNq|+B%g?t*o2}MF zxo}gv?pt~>AN&(s`~w|Pu(B#mX5$0~@ac}L{w`)!MGLMtjwpbG67)rscoG$pBBv!P3by{A%JKs$qCby`O=)r%hK+y z+~GLH>FYJo%Lzv3_fAinjX#lD>-HmT>WX`q#C}wm#fO71B4JSZ3H7Cwu+P)Et?rlo zZo1=hx7MW0Avp)H?>MsE_S2xk!WAkAzxIGGr>B!q^pg=TK@pzk9>TZuw8oi;j`rk0 z-8FOPe7wB$nV;|fJ(2FHLq}IteNa24tEHkGN0Ri0+5))sqqgSu##A&Vop!(uyz0xA zk#P|GWV_1cb*$EP|JhhgWi-&AIglqJ{>e#r-lp-Q>*oWCRkJW>)qozf%Z2 z>E;AXg&pUbqJZ?%bgr4rW>iKr_9zz_d7_oHe|Tw1_kj~o7|1~6$Q}}8ui5f7pE^Q* znrp^}r9;-_#-)qXbI>dN%V+!j>eh5PrQ*%*7#ezqUbS9(N=-JW>Ol?@tqrcAoyFit zz^}_o7c$QqoB#)|QLh7(jSja&&+ktv%!$dK3HXG101X+RMdTGAu1^ofi0YPS=UJ2s zJPNiF7KYLm*TPRG-#F@*)1(D~Lz_oIdlq7Rc>9wZkkLFkYU6xeT_-1|%%`F-{)4H_ z0=l3tW zCcR}gc;wvFV^wV;MioU9-L9C-5uUjG`-WLIhFlqBm${qd+nwHxB$u^1J1+*JhxGFD z<0u%(<8c7#Hv^5yW2O3rx+*L4iVvnkJ#n$36riCRd-}ohk&@y z>CR!@++1vNcj#;icDC;t%5&e^eVSKUURtd8J5`yCtS({0hK01Yw#wM)TmSX*q5F+> z#)w)zbJ0gNu^w<%0U>p2H5m!`(jGtI41qS0+qm)MYrS*}zK|AUl7RpBry~qajH#6A z;a-MsI_eHqBP9(LCY*b2NnGEX*RocBe{rd55g4HpG&HOAeqgl6Goq2FAgumuGP0(m zdBu_=9yiNpKfB7VHzI4=c|O73{`2UOGMo{KIKz=1WDrk)Ou*kKxADy@vZRqnBNpQf zIi5)8Q`VAtD$J%#1USrk<1Id)J|ank<69|JGz5A111|gx1LeN{zZ_0J?fVztlji1T z|AhoF?yI#~!{hDkfO=7kG6z#vCJR1;BD1f{_xi3k-ZgvX834`%`6q@eD;wB-eQNUK zT$!>h@wu{MXA+D#nx;<8bj>k71sD;45$W?t6F!|k5|y`>0`yKRkgwa!;Y21qI`7Iz zjX}`s`C-3r`99WhGMLajHU5(UQ6D)6fqlntn3{y#n^~oaLx9_V=n{7iR)#9fBV6IN z;xv5uJ?R^!*OUp>*oX}sdUE%=`mm7Sxby5!$ef*UZy9kgMut=G0`W11!$lr)GhQ-O zV?hQ`Zt~^6VShN)y-(UcpL9XPf~{PfEjc(Dg4<{^J4L_rcS>fGuS%$gZ~BQ_X8Gff zRm{LjZG+XN4yNCI_R7ehC3rT+@-F9TRY_|gR0wiXZ4T zd5{ns_PK`l@onrR*fX`B7gae!a`0kj zU+<;r(#3ndPe2n2Ac&aFCqw2;vx$I)`BV(JPBzhTv$GS3_c=UAXXsA8@}Lj~53E?U zN3uHaj(JO-yL+su;ZxD)cRse~h}+M|8-y2~ldcQyBPy@eGd>hW+LcleX&XYZwl%+b zJ;tf5A@{f=0|Y1VAtMtBr}*jga)Cr-U7iOLlIJ^f;q!f*TsIgUa=eK#FjJpIv7&%d z=>>4uQ_EC!b4&w~@9bw3fKXbxh>n$P%raH`%Y%QV1WvphW$;usJ;E9{yZbiw-0~2T z^xx*xY&0W_mac3;En>+ak{(msL7P!SB(~;gv*p40T1c{( zBm`)rnDFmc7p=hxDyalyBQq$U84=h zrTt7pd2T@3VT2;PvMD>1<=w!;A{~a@ACV)2jNJ3RjUs8}bycSvYey`kC!^r%=2Y2) z)>PS8*_r{=yn1wIgb$;Tv!5phWuO8&4D=1Y?BX}7iap66A;8Re9jlgZ^z&s);w}^O z!$TfiF9uPnGGv61CR(QhIO_RI=p1F}AhESwLB(2k+MEdb@&~sF%1b*gvqOw0Jz#h?! zO^N@&5p9hW2rh~dBlN#p@IOqJ#d?*FI@GbFFk>6PS+{3x_2wJ^rejCX#f2+3a6+tJ z>{Ev8zs(vef(PSox;PVrWJFGvZoeW zbk+2<`*HIsm78|_dmO5)g6Brb22a=DyW#M(K|TEb3z{~fKXJQ}1kLLIV$R4iq7dSP zKU7b{^^&W=pWw~*3PMZPiELVPm7kJ@&!Gv0<5Xe6=q0ImB}2?YSA#fc3WjyOIWqlZ z&k~bCC=rVegMP>z++Gr@|E7~dSW1!}Dte?_djV);$PPmb;Rel;4Vo&8`2KLXfxwUT z9&7ahWlz%?S##q(pBVoSWBt{1p9eNzlB_qZUuRI?5UP&M+rMl&2^cKF{eAg^JKR|x z`r!t79?7;qcKCR12drxsh%N&6FO}S36(k>hQZgJPp|A}X$l5VZ5{848MPwo?)(ikH zlrriY?_hK|Ac(*r!_(4MA?>n;*tajisW7ZIQo2X+GtCfN6#kW4$gvFo}{ zvT&33HQ=S8jqq*+0!$iJUsq>hWnKGKw1aMsi~9^d^d)cH*^wXJ0Y=WL%FP!FG%Hs^ zQ={2Clw<}Y#l|9=^chIqLPwO>iJp(}GFh&vvib}h*lC5mc8aJ2YRkr60t4qzh&S2K z&-aE|SyUC(Im9)|q#GO6D-Zv4M21-!iCq@_2z|6 zrc%?U%w>q5O5->}JY$D7WI&9E!MMgdadP3^(KUxq|JE5Fvvt#kiqX>7k=d?Fo${8) zH%v91@jjlBZ-%hzh2&ShkCV=zLYS2#RPc=843JWUO^Hvb9B?)}TarlRM}+FuFt207 z&SpgKZ>%n#3cYXXPe`P~2)oLV#;qC9Q;~b)0|Y@F82ti@9>4uaP8a@wj~$klp%AoJ zJd=6hN_wGchqgGbSJoWRZ-G-xM2OFlnGPaHgGU;CqI(a>n&p2YYp#?o3W8Ql&sd6T zG`}DyuC5}B9H%`+Lk-UQ4p%`$uH#h#{SoZw;c#pU~P3` zM#=Z$qCT&!GwxswQTOM0R~&xU?-rui1CFOHQkEARLpiatuo_8^Vn_^-NqGII|kbqY!})0SmK=;xr$ zOv2m^>eW5e=tWo39Tc@leMx-+ugrv&s#=6Erv8B7*NQ)}{~Akoy2a_UT7-9(hk>v9 zbyd$hgIKXtaA^osz8xNlx1Run`-bq`aI9HMdeildvftzKdx7<*-sgiqcDjMM{Y>#m zNj|t&X$qQVN)8BWU_qia)l(1+dUDhvSwE35wi#?Nav|9?t(3T|qX9{(<4P6mbP+oj ztVXeTFu0fE&Ut0|^%*mw0e{p_$3(-1sC5m0m}aAfEeQFyqROE_mFBqF{$@S6*F;T) zJ-$1ZHY}thZYJFC`H}-UO)Sgyb|z|A+Sq`F@qQ7yWLMhc=}l$zp)b=BD?#V5d1;a$ z9jd{btuFY>t=QvO&V*qWuC9|3LoxdZ)xM-u@!c>4bh`~H4Rs7aR8c}^rgRxERJB`# zxampA$Xth`WdJ7kggCJ(F>yemtFBrVT47~!awZ+*Z=%w)J)fv^ghc2iX^T?|7MUwD zf~TEpfvBFUz}hk}7xo|j@w108!cx^W@fD5usG~_ePr{Nrs@9*x-2$Yn_#K`vTu&^d z#9f@tCHgXJw!bY4t6OnPW+uQC(>*1vk&O{)fs8-WyRvfC{|Fw+?}^pz>n7ZkAR}hz z=4!j{dCAZZXDXwSf6}UjepADQB0Tt``4@Hjy~0MzcAOG=2r^f$f*u1_W@aW;RM3!< zhP&M(45{de?H9z(hMqlJq7|slEEY=9T?64B9lM%fsCNE5WyaiNQbiAxzkBdp^6dM- z@}!dt?JuoX6FlN7JZkAX9El&Jtz{^19?IE>$-Z~;ciHETUL)^PJUN&XQV+L(bMX1~ z-EyG2-=B2hf75)|G@%Wd_50rxY}u|ig?rTbZ3Eh^dwN%?TgQ~>Fz+<#%B=P!oHT32 zn2$(TFWnj&8%HN62UFN2`)oK=->*hKKAjms;2q9_1x4Mjre&#Trm{}iqEmGW}My!MYA_%x>XrxmY$7C+7tMmiLfhJw-4x7u(l zK@)i~p_5{3Da4O>m5P*4C%v!iHPyi7DwnX&M zqcDN^NngUPiIl8Gl461i*#ejS#>$aF0K2!qmAE1@Zfiu(J9OZcX^wsC;A=5sQldNF zJysoM`5hvtqiYfN1;d|Ir1`Fki9he*_4+RX8?4t#t6uQl4lZUcTgP3_DB4z5Y}U-HV2+u(gT_(5jQsuZf@>Yz1lyC67<~p zGe?uX*D(FDYscKcsBZIZC;%o?!5?I@Ov3QPo`qCzRyWuz7$J*g<}j!s$r?<{f|Hfq4=>zya5V&8cV~yK+ce;}(`C-6kYQ9Gn!_u1L@JM?uYYoSKaghKxFf zEP+?rqbw=<->#9;7K7rh+PDmRHTYZu#$0O&@E_#lJg0b^dsKAbQm2!F3SJr-n077M z)z#IB_sPV0bbu;;upUbQ#nHnI1S+T$JPzu~7dlRSd;;GvY6)s^h8(<%j%)%^jx{IB za97N|h+~p=cVZOlfzr{$_cM`nv&u6C^~atC0A7Rm~L}ae=Ocubp^I(!0#Mx zVsM7^=$st<#-^rnaW9r(t$@iK2zZy~M3eM|HlZS{dR#8M9FxA_?;lO1?t>ewaR;Y) zSWzY!uC`#vGEhU`sHCM?PZ1`3{@-2YW?9UKy-rxhTfpb}L~nsp?9wii)x zF0-< zhOx;)A#iF(B268Jcqa-lk{E(s==8g^V!g*7?Q1`%%-8dGjdwNM<|j70QsB$V0Y-vxSik8wn zo>wdu*1jQNATI(FQn(v(ybdP(d5qQ4f{l!C>z5~nD4;<}a7Ey4yWyCjPx}vuWFHOs zl7MS$e&c$^KhzlNI(ZadIkc!a$>0zu#{0{1!j8i+L059IOJbxBB&3CZmIrnrwSjdg zog>6~eDR`A&#D$ICZTM(x6a+%)Ury@ybj!`gzc+XX8aQ!xDdG5)$34JChdYejYXDkt3w;Q5AWlMi8q$vgj)uhK~Iw1^%`MT zNstFU4!N!1D`O#_4nxF8Q6|%ijSj{XoCt6GF*@r~HX`hsmSTb-1bcH!G#cfETcO~X zOQd;^N~Z0U0SxIYo`8R6tCP?N_^*0RS+F{@V+1?G<PgDx~@=^f`$g*_@+{kM2F_EXKE70=E8wRV>rxh@U%P+Vy5N3-!#%(e+h(VHKN1Ks=b8B>~q? z2Lie3@E=vYM+dyiKL^=_hI{=rDES>Uz6BvJa8O_;I?+>J9n4O?0>;XW#+XbUY1#*D zgrJ2b3@~$lgf6x`Em+MLJlOmP&=A-~N_43$VLWU;uIm!KPheGw85`jgVu z#dJ1W)~|nqS$Kn(EUra`^TW4vk2surXIyx4MX&7BZ3K{u8gkI{XJ!#_80720a6RPg$#@ilnj?T45A%tq1JymG2&mkg$Oq-}3MY%8!S?LnCaC%7##) z@{FvD4=k7~J}gn3J5+k%W0k<-qXs;#3e`d*_&%8GvW@RrQ8HGxr`dUK)0_400_&vs z8K?^W17pRmbie$$fv5-I0>tkhc1tCRnis_dli|FA0Yyl$F$s2!@?`qV7|ykZ4h$S2 z7I;ig&!}o<=(lofZf>bQnW^f<=csY;gtEU;;Fso>9&vXaoKWvJv;t75Hj+KGyUEf& zP&pg5ic5InrIhOJZLWUi4BwG72A5zDMhyQCCjBSRHpv-sHs>hUIAlP2+ypJ|YLP0s8Uxkrd z97Ih$&hcmxQGW*pnwUxV-kRW#t+ub~)zO#B}8k8yY?CqjqqKuz1sk zi5%W%#r>@~uqb4a5?rCITtJCdmX+Oj<))SzYZkD~9CoHgZD?HT;t8@Vlu-)@mg89$ zzpNMp?-u|0F}ou!5{z{!_YjD&qB2yW^cKzF~5-W zsHkcSN=%Y+vF~mX= za3DIx#OxoN8?`hP@@f2U=E>R3pHNdV{N{n2f3PGVS!r_hUNb@Y4}tC$Ecy^%@d3K` z?oS=Y&i*kt`Dz`$uEHH!eiS!6-$%t-+%ZEI$S>9~R(Fo2CeiDvaB8~yB+b1clcirK zJeCU3fkQ_nf?7hR-%Mc3L=glI0qV^6LPHd8>X$oxQ62T!Wr7J9iq$aKu}_q_SHd$C z3T92ElY-d|fDq)MEcRkNG5YC&95aylA3wun4!!W;mNC$t!dl%b;}r!jj?dPhw*r-! zrRZe}IueE4Nky7tA#Ks4%Vd$kY^l_3t{;FQpr(;SAA z7)K)eQc6|2))bcXOToK@b>g2LzkqA8ez?Q==%DGh$Y0YuAGxj)z1H+`dylJ)n576r znbq7{O-J_|Tc000Gu^T5){}m-SjU`_4}d8s%g>kt{SY13d0*h*PJzV4D&r`gu;)aG zs3~U{r7k6hh=y4n#NxItFv*dcQL&Sp2R)TXT+)UYQ|Bbv$MT=qtKeExPg=h+h9V%0 z%+6W_Pwl~P%4CT$+W;ZhYd@+hNbou~(;A24Dfu6XmGvp?de)^M(^!HAgmrMe+HP)< zSEj|GDI5!-as>Le$afvV19h z#Zcp^=J{p3aFMnw;#FA~rEWg}4DM?AAwgklg(hwp%8v-330YxcMLG~sZ81$W#* ztmg1R)TxP$)Qjr&{M^*%re5}ziLrPs0QX+b;N1EboT@qE)G!pbzLpI1WR%?(IZvT# za$Z>^GEJEc+QgN!(|ea=kf?AL(zC;ELiiJ$vdGsQBQg>a^Tr#)K+QEU={N@|FM8C< zc?)hnt`SzQ`B;lLIK=CrBSU7+6P_%N|MQAV>=Ub2uf;BN6iS^**qcizE)^LTHdf|+ zO)+A%Awr@&Y1D}6_ zatj6kIY&epR2t}jF^LX3L}+V7F*JlYRR(%;x5W-yD3i8xA#CbJkj*mRk;x&cV<*1= zgYU*fsTX6eykn6xD@Rc`A|zwiEX}v1GTG3f@;EIidaAgRns04k z5sg+3ti?d4YyyK?Js0aKwv+1iKgFIQxPCY{hs<2QZX-P2DmMqkF&?$Uu#%f{AV`&# z266p@9%x5m`z9ffRUy%w)ru0iK>U%bHd#czUPLj@LUaV%{1XhgDvNd*IGUb*(`opU z4?D!C%Mw1$E$)<_H%i=jsm|@vQg?Tj6iBi7J7y%kqD)$l7E3|XDWDy}Lo{u&$+Ux} z!SjM``5?9+Iv^lb2-FA3H{Tcy>0ea->jOK?uFtHd7Pvk#8U&@15dkdZ$#6J>tl9*N zNmOmtQwseRFToIMRG;m+fd33ep9*1sOzl)*<>qbr?a2~WZklmV_%!cD6VReKa2=?n zX5uVm#wRkkFM98NdOH~DVJjA?P^wWYV*g-`> ze7Xeo=D0#-_tdu0NO7YVT<2z*(F}N|p-wwDrUF~gi+I^a(TqYdxYaMJ|Mg9ezh|a< z{wm8LPdETWPfAq}8QI?-h860DDT3Tt{Q9+Vd(i(7!rBZytP8;{^3zAA$EdQ5gq0Gl ze4X2ATZ;}7Hf71G+EapbC-&x6QK!wvBY}iG-oC~!bec{&L+Tia5bSMC^9M_NafM$^h{GFl{C#KEjruXKNxF6)!+;56cEhtMxrtvQ? zq%5Z>IPO*|qA0KyLwv7=aRh0O;hy6IK`o>b->F2~9yf{GGIt<<$md6Km2G#8)I;8QKlTuL@q6hmM-WGwAl zF&3T)M|fhqBj|CmyT%u#3wpbR>qBo{uLzwwS5W}RPsv1XA(*Rv1DRwA!k0_@ZyG_V zlk1ntg20b*hxV`jM>d2=(%K>Jj-Kf8SM%!jA-As_#JH!N(!ZXBTUJKZ6O@N9k3q*= zS`{qgY^%=tvyCul7=|=@r{=sVx*ilLIYf5Uuua$I| zwo~T2Z+g{;_meRBWe&rH2?8G<6FuC$zdvh25Lfh9STkthL-;PN*A#6R1Q1;aBt_3) z^wZ~ld6$#PTVM?q(R+MCwK*O7veAbboWIZ74%MgJnN&{H>H7R?mS>wC8j7wjwM!{Eei>0*2e=7z0U&2aZnHRA_nb7 z#$;K;!B)W$$()!6lqemI9+PIg(Ld;+{tIB4>R*{}r{^P4pNEzqd(9?MmpDZYrC{8L zM`ylJeO_QU&l{vpY?H7=OC-+rmbcVD1Pi_+IRK0lHTwhR*|A83;hxKpiq;Dk$NK(i zwL6IziFKe9(R3&ha6Ti<$dN8{nA@giKUQq>8&5owe3#XVCIK0`RCmzV4rRflbHy*@ zKk-h7qG82h#apooUQKd3ihlT~Sx4m9HbxE=aaUJj243K~8@G5Ofeb;!1%jI;vx$mX z`$Odm{t3u5h>{@U;)X*O-sn|_i0I^pCcM*ntkGBF#a^-k;?a7}n1a~bAs-il{=H+* zb^nEEm>5WDiqD2?5K!)aPB$FYig#u~5&)5hLq+^yI;$$_{# z#HuBJQ3hi)84hbq!rKv&lT+@vO4w(j9gj?%zA;Yox6w+LU@04JG&|jEQQ6pQbL@6` zKloVikW=vI&LkXTy~}Pt_q*?imtg5%Yr+}M9#QXJDX!c&D8^>0-4htTJf8LrMU+^H zOdnCIDXx4X-{EtZ8Ba8(AK0vMb)bU4P4}b>D|~l%d40zI(9xa0&7EY=l4iTRN5so@_c?@AFgma3w;xd9G9l<_X1g{AXy0^n9OZZ zjOIC32Xq5lJnilAIL>eL+|Jn72*viYJBj;lAlcLcXmAOLBG7Q$+;C&A*Zn`e0nz5E zC@XmZ{b%r%EeFb7-1(FE?+;Rh?qZ;hUuS{*A7cxO_r-$W_c;FBX}fMfA`rE`@16t5 z_Xqcs2xm*JfTfF(V%@%-pPccCIFNw|TX-U-f@xKgU8^ zp%Q$M%Nuqxr4`YyY-=oFAk3DD0tt0 zt=g_LXIDuPLnezGfAMfew;VJ%=o|bCP#wkE4>#PLDc1damreL#bl_#k)?1}1Py{6E z>px59Ah@&V`{VyJAv3jf)UiwaoC znP`R!za)@5N>Tn1En0dPr-x}jRc>M)hvI;X(FeC}dvF#>%+Pa7GvaexVY@YgS3~%p z5dIem7fU-LdHI$1Gs&24FB6Q4v79!d;-k+aa8i|>7M=gvH&fB`p{$+EU9~anH=|Kh zpT1&IZl;bO(uJiqEQ89a&BNXsy3Xru9~1HTtqdaS#0I$yLCxOIHy(~bWn7e$CA1eqZ!x+O97l}%%PGeeFV>NkrMy_)@8N2>kT4*pgV z(?LOU-gI!fPKwk4dfqM&uZq~du2R>N;@IrhO$H0k61(my02pXr{#3eg*{gxfTXcW_UWT8lVZN>|u0Kfe6&AN#ru9;k=RY_4Uve>pSm2pke^+=Q zhwsDK{GF;8H*4Y}OB^Nl`2vc!&1u6fwtXwPeb$A^#{-_6IJphsqoE<_`?(o4 z#zxrm8=G2GtmubWzwN_gzRX!wx$_ZfFawxeBlJK`%uFdD$vC1A;H$kPLIk(DAB`;; z&@ioMGHVXtpWADB1M$0pU?LEKrrnX4`O{JupB@r3Fhd8WZ>R1<95BP}$6Ma-6UaUA ee}|C1LE^eaG~9TXQhBsN;C~1Yu|HrB08erg*WE7j|5picIeY*B;>XvWf1_#MO^Z2a{_=}oqHVZkH!u9Z}50{m>|x`$SB){+itDC_MA!yE}A1OAHdh3Hg{ZTzT% z6u_Fr5`(a0^c6u(s1z%Wa}5BXp{<(a!eLOTiDcNr>(F5_BW`vudv?6#&Kkg2O(YN; z@v#nyBmL4~tygMc8k^!%s-^+)olvP33g^=QzAtwJ{7tDHu`{wQpzLPI->>UMJvO%f zUPtD9-Fng4B0{baFA}!}7vX@oow^aEVakuVvYzFKFHwV08I=MsShr+Kd~!o_7ct_q z#%L;#xzSoa;AD3Y03fZcI4Go#w@BjQaKzJq)F$sTGm@laZ=-nY19;-=4X{0DAI7{b z&~6vht|9rAhy%-2OoR-`pWDW)^g*OGHDP9qg$;2TNVV1C3zjh0Wf8aFjn%b_oQY47 zwEp|&S*do@DYSA=C;%7#n9;M@gh9tGzL(q>*-zNRAZr_Y)~urccHcA3xL(_mIzib+ zVYz}R{{7vA*fDr2lySF=+1bWq-c8-zTmHSHarjSpunh+hq)S`|zV9c917lko zydGwInI>!o4!|P%?Il}=_Y1DiYVBWaYBC}wo1=b;zL9X?ix{(Ah>g`=<^_qf-?IwV zN2r4k&sO{tg{{@a)HOivxA6zTsv&k~NF1q*^ZD~6?yA(>*V%fD1Dp@6--{>%8`z?< zBqsQVl5~I|05S2G1Q;v5yk2+ewzLC~J zUcTj-HhVH@H*OiGj+Op8S^g6Nf-T`-*fn7Uc&eRx>%E}3-G23L%Tz@23fFXtcz*4; z>NcQ`JKF^IDw8KfX-MW!UNtWxBhh8&6?+%oYy;<>Cq zdWD%4c0)&pCMCf{eJ4j~I59Y{t^B zX1?o!T3aj99aV6y9GJKr%M#L1EU3J4j&PMgVCT2rV(-MY;BgM>H-%CPq*_Akr_N;F zFKfO?hCFp*!>)D8k!3f*KegE6-qzlB6+fLbxO}P$-vUggqVN?nJKGD^eV^Dx;$bEd zDq5*L<`B<5!}TZX^bFEzCSFCBd3C*Dbtn@G?t0iF#5}Cxr_v&*LZ+R8##t5HLrms7 zI^Qsvxq8byfnUjHcf3*i1YNg~eh3<=2R(Qbzry5h?x^Wz07dIqdbh&Oy*q%zVk%3Q zrSdS}p_RhnwO9*PZ7XeKqOj=x1mM5>uJ8&~Ay?Kf93YNv#fE2e20Q9+l~461n#+^8 z`VQ)KrOj_lT&dM%=@+y7Uisjus<$mzw;fRE#@CxP(`8m`GEgaXY^NzA`24lSzh_YZ zypORTnAJ2E+<=|mw5u&s@cN0mvOW@}qE-U=BA$8;TDD4=hPn4F{g|u<%kX45k+XKa zo6OGi{`H)_b70GqH`sk?0uz|_8Ld23+s$46?OS>Vf-9pI+kf6IV-k!jyXhyumx>X@ zPAds+eym(VI5+T4Dp#Oro%e(;yGpp=B)I-aqWA@! zFo1O%oTniZW`CR!A>*+WAM@> zq^v@dp>wA_t8vzsf)GWE(NXIYWiBvNHuO_%AZG6`mh0c+Ixa?kofmP57v_pNU*B}Z zeS@m>+}7z~?%3gq+7B1|E4YIXIp>7{&kd?(lC=3TPfXFkaa`7N-cbIQ&EC1ZqsvV) z;nnSFf{Loe7YK&iE-UN^sKvq7%><%`cZu=U}{uD5?8f%p`beYpJ zwzdSqrMFr}v`589{l$$hm9O5BumeOZ;ef&l=gTiA>GjSt_m&PgZ`iF!;g)$JPWyoUVDIef zpp~sft?&Uk#Uqx^%|{xG{S^=4D8qRqByi^mjF`eQGZZi;B)(oMHie&n5PusgMJ3QO5P zmr|D~WomRW^4nc!EZSu6D?$bpaZy&|6ICIt_mH*5fOeo9+h6?4)q89I32+t8^|Pch zNnD1(4p?EGO7MFgwrCw6sdiw&=+-qT{T9z)http5&F({B*+^2_uPr3s)KQBsAGS*! zn)>?%x2~_=$o&zQq`y(m3)fJ)or_vjuDrZaI49AF$*f0+^jB z;N^xuO|;{a{!QPnAs8tv1eba4QZPkArRz#+#6K+r-|usS6Z=QLQ8QF)lg*Bkp8ZrZ2rKVb|1#<(ZX{LXJS zG91467~T>^vg(teo|9V%zp>1!BEjs$R@dsi)-1eh2~1i7K9S@)gWPS4m-BZ&H&?I@ z>M2x{_X+%U;*=KoBTTmk(0)757;S9Li_FSHon#5O`<&8rE<5pEfjd$FV@?h=IMMb% zKlk+tuQfO6B})1|p0bT)a4@@$*HKso_MWAz|3=MEd=RtqSjJt^Nj0oE_vV&kCrNR! z(d)M@?f9EuFg_BheNpYq%=T4zTIW3gfiLcVM4u| zM0vfCl*zwuoY5QEfv)n&bVrEfY_3TlxK0^h~gR9~5iO7{s#hPQw* zU31zJx!If_eNH3k5lu{q9MH}N;(99o=RJhn>ESpTZ#6m<9bn4s;rvEMU~Gqz)BfpX zilq^Bs>M3+kUXC-^7jMS5Hd0uTGR%|6kVt_%-U*VmhvtOoM}ZpnBn#0NvyJmNaGMz zSy>b2to9p4@>nwM?2G*f*Uda-&Y!!@_nsYUj6A(e&Im9=qOaY18^|V%`o{_Fhp!+qm^MOwJ_!4y3zv}pu=&qbfQJ@l>XDR!dd^}6 zNkPp|-&2Lk$95PjQOb_wpZV@}CI?efT!S7wYGgPWkJw)q`Rf8CEw&9 z-8kq#5^stgpYCS95tv%rt|A}9-Gg-(zC$YGjA3bPN@nK-Z z>IICB%uh@WWzn|LADdc;t?y+_w@A@;7n^B^8IRq7cE>uZND5RWF~*0avMH+J+n`1n z0%pQlHVWuQ+jx=oRQWXPhmr`z9~+LZko?`dYy$|d41k6~4`|G`O z>Nv4HVs%a?e9QL8NgBa01aTSyOL?@_orlMb>YKMQ8appi3NOn%K0p3`9rSvK_Zv7d z0rwz5ED?-nLqc~cf|CODW%)kjMV(=loR-+%%D9a+yBfF7O4X~(!{H8}{xkRoaNzpI z^^Tr)=7W3LU#DRlgC0JOF*2>Kl+QH^E(cf z9CM*1TOB-5P6G#xpu`u261EWHsKEz>kGb+Q&yv8{@PvRriRtXJ)IwVBHRGBjy+F{WjfkbxOJ+kGMV@F_7U^{ znZGWDD!Jdqf|>p6ccgmFxc zWl18JYUfk(?rAt2A);8lugWmCK1utDYeoC)_fx#~D9-*ToQwY-8b7FjM&Y%BH+fRh zZ{jAl%7*xK;~Y{=dCMCwWU+MuFP<0;*e-D1C%y|8!8<^^iH<_}h`GnvEr-{3aaX22 zXhP@4h%dYhtb>Ybmo-_V@F;}s!PG&JQ0Rs#a@E;<&I!ZY^^1nrGQ{XHT7i&&0_tm(O-yq z8S=WeGGvn5kfw#XUVa8%GT5=tQVH&Ng6a5w>&Jf9`CUh9C&H^N;vc-F(ynnf!0XYjExb zF=`8mFj;oYy?|0I*PU|6wUQ^NJ{X1_edIInWzCrH^CqQndh>ii?_E8jhdzJ&p=+$J{+tB@j#yV043Vc5&lJKY7Rwr)x4(*N~+P8JAM5;!Elup zJ`}H98GeO+H=Mxglc;Lr{`h4@J5<&Lk=h{9#zZ~PaRWu@7ST3z^2SQqd+k!$W34K0 z@Aq)GdQQ?vO>@19Mlgjm#mHNp-(deDQTRkM_XJ7or25p!rrzvO^<)AdYCJ0Z^E1Pv0(7yZJ8x}U*!Q$p;^B`4`;RaLKfE{% z+E;7>Rm(r`Vf{iOF9n`c+VK0Q8}}VTpYXWpUnGCG8<;R=e(JS~EA;FC^&2$4+{3)c z*<3#aHYIPihf2Q`*okBCfXV?X zJAyfbFnH&o)YQvZP-C3-V5i5X8`(UtXOJS&8t-?YG$ahST31YT`C0m@(6 z$afSxny@(TOq}t(c1Cn&Zwqtkh6m=}R0G8_Baq(iI(n>^yYLH1tA14`rKs`Y%QaIw zU;Mv7jB33sM|NL-TIqjkm6{%dA!6r&>wj0@WW88viv6Q8ka*B_CWPrTMddEu$D$t= zmiZTz{c2^3J{&0z>%ZB6I*9&wB%HnW*m{-O2j4IK>_a&748$|#p;wZ!6ym&1}H z81iJjdpZu zyM~Fc7qU4W-6(T~G|3HbaHMgwRWN`Lm(PXRlhy+a13WCf4zg6oL&C1PJ4PV$eOu!~=lJgF_*n=T>ALVk`7nB|EX`#v~qOyD9k$#C6DopJ5w?7rkIh zQ;RPNIy5{o8q&iy@q%5-Vl!1oPTpwt^Z|xk9Lp?96|c1u=xio;^#hDHQpVURoJ2nM zS?|wYt-wvbhZ`ydgf?QWr%ngvS^Pk@(1|N~?xU>xNt6{WY*-#yXmG{qoY;hod4{v}%=er2D6>EH*!nXHt z?sUc@Fv&qQgF>voiSQ%x#d&4ixrU_mRe)9XOk0Sbe^k$CRyyC7F{mELV;iEMg_@J2@Q>YjJtsXdwp2tyi^^tvQR3Uo%iv+AciH$oQ4gyN z>&IyKXB&;89gB@OAWl8b1Vhs7`+c?gY`(xWN;>j~A? zA)e8r`VKo7j%<5*-=gP}ntf9d>^<8?Q`b1GkSgQ!MTjUEjxWYEezyZ9DVfPhs`|8P zuY-nMTT<@1$NZS-3HYJix23EM6rQql=Mybm6wNEZg|ciJeq2-?^D<=ONIOq(DZ;Y} z>n^d2yCCAB5)%SwEK~3JB_X(OfZf*WdNukQ_VpW_`dcAojLM5{^6php!d%nCPNm1o zJ;NT%T$vAO}+ciCV)~B)l6+VllYfle9L>7p}b)dSz~5C+m<(aES9s@34FHb~>s` z__2_EyO_CWF=iJSh#q@8(8k>OJ6?qHCd*nCt1XtL5n-rt2`QCUqdYm zVM+hUd~zA)Ig7W7y~u%;;8=GHeOcCJM!&!9|j(enc!}P%rv(2 z6%zRNdFXqxUuVhdGSzOMi}op57)4cjr`Lra`mE{SSj*AcTgnhOUI zn#wNYl3}-)#J~|5T7N9|#wKHJTsM?3ua<7g4|nU}+!))y7X~GnLb&CqfG<4y3>WXD zm~Ks`rCwkhTAi_iZ$DWTfUzaTv51ksb%=B|xkq}Z4;sc{b{NIUZ*PGq9W^Kfq;?ln zt5S7vZW~)L-7!3WTQ^=_Za#KI(OL}fQcIrMQlRbmR*ie^bls5n>{*L?eA|Xp#cZ3aRJMl0^V(cRchyZ+5Ny%ii7%3B4~Hz5-?l) z9DekB+81&9Cs81Bsg$=7(Em|#^oi*x7a>Tu_Ym0Ni6|^BlASD{52VY?`I79^H}KJY z$OrMjw(O=-!53iuLr(9jS+M%?Fo7WLMu_{Z&`a;FZ0F9r(<8E*D}g@q`kf5)HOKv5 zQ=s^$F^4LGSp}|njO{zWUXA6xsTR?f|x6961!JV5>DYD zMcT5#7R-Gp@qx%K2^QsaFUIU@-L4_XqMEiF8K~I1f?BlHAfVa7Ee)HS9fuHS&mN=& z973bVlx{G&K5GVX3Z>GFwR7%L0ikC-+nAewj{m$LQs!5%C2P|Ffrh$nBS$P?q_;ML z1qnyYLI`6rX-*JbY4YDPe7+=5I}>8}kVl$R8_nn5zTJV@0_q7>TdUyOR2W!-(hYQ7 z*}?Y*en}x@lJ+5dAbt#)E=g#{OGrNc)Peu-#E%AVzM zma}5Jo2C?|aTX^;4D6_Bee24f7ib&q__nlt<(`krs z;yWs`C2-pCzBP0swu#A|pAH}%V+6Pvu(eFq?_Rp?iMvF*&Rgn^pq<%y)3_?pB#W{;Uru-CmR=iibLu25 zp|t(79SPm<&(C4ebs%;kwsviJ*fXd}ir-ab^*9?etGB7|?l8~QYm@zf zBJ%7PDiDv4kuP2U3e`d1w7$UPF8=gFRFBFd-2MD$3L|F?jILosm zAij}={Jn<|J5uk0KHJ=iTFk zpa-SxY%G*5wRc}=dwK+J7qsCkZ?0<-b)S0!=Ude zRtBw9wu+cJdz~QL-@II0p7dD)@v`!ef-OG4PJivmcQ<}8OjUzaU|o&?(=D~}q#Km^ zvYpIM*;M_J`1FcN(uU*1^V;=wh^qMkO18^WW{47tJ=o3n;+J7RF6Sq(y7y~f&*tT~s{F>wV+C{MM&&~LCd zCgK(vFK7KD7h0KdgXw6vV;J%s4S(Pp$JR7|`pr7pRju~a@CC#k*H`cNDBV5b=NP6y z$Hpa$%fMLIqq;HQ_ihT%gpH{xathSy#~KcYemf!F^+zN$l_tiDJe0mNiTx>@z;~Az zaQZp%#6HPCcl7!syx1>J`;rTQhfpSE!3#Cuhe!Qg4RI;m;^%o>m8ogURi=Lnl?t@; zxs%<6=*Fgm4kki(t(YQecOfR2c4&4a1-8(=-VdgS#26>8&!_^?+Wx3ReZ zdbd#TN0Km5sb+z`&%+@1#%mtN&1+R`T*Yzr&x4-ilQMouG|y8ZT%i&+rNHAj#=V8Gn z(iVa7H#daMmI>_RRlX%oe8wPlOKhhZU`LFJ&^AKtA)hN{Hn!9wh7 zzoLBjxDd`hK=hLxJZACMna^%@MKMg}6a_{sn>OUIGN&-)(zEypvShpBR(~fu;~pyh z6me;r2&-%IBWCNAurcl_&QLpR-Uz*K9{2qDl;i9?<&Q_|52Ok?^b!!Ao6`FcA~UwF-#=WBA4o_k)&GV}yg9{y+wDT{tvMd>Xf<3RB&H_>E>iohR>iv9(<5?-P zJSl&>@8>=v8or80u~;|OfRVMJU~X=0h+X?Bch#REvNweA zlMPqUYDupcnT733txc%L{oS@pK8ewM=PliFjw{L<4g7RV(Igmvb_O6+Tp4Wbnsi{o zY|G(9OG@G&Ao3LR-5bc%{1fA;npZbDVk1Wongq`jTD%l_Hy>0G;U^pGTU%x905?6z zA7!eerT99`CNm>CSRbK9xI=yS**-Tq5ZJd$r`guXk&j8>Zhg%m8{UWPhxe)qaZsW5 zfK}KG@<;C>WkYMLb14+Juh%(wilN8e`d)!xiEjcq!omF9#0`!SZhrTjtLT*j`_VPz zHvI2;EZBc=Cb%#C1(I<43RA5ydDV@kXyAajVCd*3_E6LASWBx$=qU zq$@Jv-fRlEbY)$EZ_cD_m!u>WD#nF)F3N}qRp))fYSL^xA`HXh8cQk1~M(rw-KaUm@MQFf$m~?eo}yX`?0RMvqD0?NQvdF;w7m+c zg|8gQ)RgUe>Dgzr$K~!a+miKl$)C`~Q-EF0G-WB6;%O(-BT}N&Dkx)BJCKiWV+y9= zrYobS2iSLEzjH#sZ}0R12?F=@+|M6Gob>WVSkSF2;=Wvw@awy@d*VLKG`h;FjewXE zh*Q|X;>!tD9yGn3rcHpk$yF$uWsB^UHYV3L*?t1QJCI*s(dBK{TDT8iDLDsRTUnBw zrUZq!_Yby%zIVPhhusyZWiE*8>7B+D`I2TfbN|dIvCIb3`Gqa<_d-nWQTfP&>CScP zxDPkr_7aEU@7ykgI$j0Lng9BA^v?%DPp2IMwN|Dv*Pi{xHIzmHL-L2BKKn6TFFPJz zg(;LkUcg!duQV?;)x=)qj6E0zC)UiEU&^GZ@XO+?*Za~jE94vp2%#Q$9nm=U=K$9v zVi#R(@2#SmMQf@Wn(oZL`P|CBIVnK^u~@C6{~Q7M+m!%uxQh{31ctx zyC>cO1gQJ{((oSe;!+nVBBxAPS=o5|6+bzv7Q3n}>GhWd*a4gafYW_9b)~V;03TBX zUSbDfbdow5_b>;ZQnyx>s42M4$^MVO_bhBn65xDi5gPp4}{ zY8sUFb{`6@U)BVhnzzw-(48*Imx&V;XA85H6*V!x3|MZ1efL1RKNDl#OIvp!q^{Ep z|73O@*85>!qH5o9uk$;pm#+7C&-u}330Dnk%w=&aPoCYX;!*_kwduYv_1`PgznQY@ zUbLqw#@TSyyH&8U&U1SWOD$_MITL3(-}wn`J40yU3M_+6q+P>WtLtb)yNXt3#d0-1 znZ*zoPzcR_?(|pO&X{Cp*ay7VAgCAQP^lCcdOV!z$ZE}~BQ9W1Kwtk&q4<6O>O&j( zkYDa&9OOqx-3TgrHGzh8-+q-t+A}rp$4~yHUIP4;kG^f%8an{Z=1ndb&{EUGL0hK0 zlag)0N)BgXejI$&8ixK9_@;N0z8FIvep1ql7jU)>B_(PqDAY)+XwBCKXc;rEJCoPo zY(e{(ljO`(ecY+J5(9KD%|JT{6?hle6Rt>Dx~g7+_VKY`F>l@Lp+7py z;*P&RNn1$pFf6*zMpZZKJFH}c6MZqc*bxDqoIGTxVP0;Az(N(uqLzGuii&@}QPI;A zGZ^FXoU@3}Cw_fTAdF&*ncTgKpQwJp`Oq)xkoKos{gK%lhg@d<+amlBP_UquPG8k!M}@Z2>31irO2?3tlvXg?q*gdU}Z2!>R&gH3jK$`V9s>iD>vmm}?w; zHujuxV%%o#Xy@;ok{%`^J8{%sUfO_u>KkjP57}#&ZV^v1-bUMnbs$zMzUJ2h{resQ zsk0}oTL_r^W)=99sA2+%*4#CS^4_hI_hr)|^Z889eWnfEihzO))xd(Tj*u{6{iS{t z*^z;Lhp(iXL_f)1G%i1|mAE0KYpSnAFyB~~W_~qCx0QJF8tKV0$-?!-gS}5c)KD=spF6M%l1`O+%sxEFuLJmt<)R z46X_fLIt|!=r^jLe8~iN)+OQ2H>X7MI#77DvUPkLT}8_YH(y+{f>|+^HDXkpV5yJ2 zTgU?|b4OrK8&Sk8e5dzC)pze?jFPfUj)|Nux&d4hq@OL9Wu)bmqE{%Qc(+sAh-&Z0 zT!YP{LSU)E)UGqg;i zRWTZaJ6PnXFSG@*NT0O{@r+Nfgh@r$7pLEpX7%@DF-tSGe~T9;ShQKeM;bA!41Seo z*JG%rt#6a9ou1BK_7^Xg3kG*|z{bU=+^zrw3~^A!>FrG2eKX_ItC#$6OReAgk$R^P zzv9FJnYsF|8^oL00{Z}!M0V#f1d`yO=%rR;+LXq5|3!e^DW@~YWa{2#asO`ar0M-u zrpg^91EHy+CgV`~*p5&U;NolMSp_u`E zCtTpzb4*+<9CI+jOYp2>iW6ub7XD(CT{6yqqj{6?avRQ1RHOow+pjQ+ID&;s76E4Y$(kawYx< z|1_ZirpWZgh`*xf@5v6~xSH3Cved4HCU70|iJT+;X==;e^BTu}mjt8jZJJvLHPR+@ zhsooGh`vgJj(}4juW`d~CG0BfRQ?`8Bea6HvCCdN{m2Hh7p4E2KKY2;>GPH`ErVs< zdzRO{|>pZ?G%J`RDlt7uE+jU;yUz2a!F{(`Xx zA^Wqfinj7^Q`Z4afK9LEcZ0_Mff9 z+Ccc+6)6K`?xF11fsVi6_)32Ap60_F6#X=Rg!_D#PZ+3R<{gr8_y~bWtZ2naKeyan zbQuzHTl~u~Y`1lWsO1s4Xf;IDiJG|L9FD-)k}2YSRuPP z%Zncqh>l5+vG_4+2YSYMGcfMM}+%h`*wIqADrFMhxW)G(4^IbQOC8pC- zUlu9!NBzbRV^+_?G`K%9-+T0dK^u2=GM09%G8dM`&f5ka{quk17NhcKVz7EV(Z9rO z+{uya6OTL*ShkoCVn^$v)A@n?ogx8&j>H%f&pB_Fl> zB2T~Im-vx{7adfI)dptg?@x?i+`gMEwwgxD;RaUSh4sg_A)d)o%I5vly{XoRl!&A1 za+3^zB#<5Lit<~&+W{I-!uwKkts@)|2adPuD;J&U6I96~3&K!X1J)wTnVfhILCxP! z^TK0|J%!|P){XI{obL2j>}^4W<@qS%3?U*bMCi$G^R->5zR-~=sf>8vV=0Br%ATc5 zi)VrfD}SRbSQKQ)gZIB!2mqQPa7IJuc}1zH$rIw~+1rwS#Vli)M`tiI_EHpjCXQA3 zy9^_%92%G}>K-CKcGb}k9EgHzL9x2d?3B8Mn%uqVltZ!lI9x@iUEjBoR= zE)v8K6=#PwJHTY6#}#_5g9k{{7E~C+GUNGSe}=$Ifw=rHqpVENzKwOiP%A)s_1eTG zwwp!Nu}@|&*x~+sB~KJp{(MiB_=25r`ZY>1s5sOD3Nsy(r&k3Kx!4i(ck9MBX4m6h zW_xp*1ZTi~Q6(0OhnD=U?0w{4)3QOPNLYRnZUVV>l<6dn+kRY4 zREeA!=lzJ}8X#e!2|ScNQJ$b|Utkayrv{r{MuO0n65u6%W7_U_XD6(yC$$AH6BS$`#2&AJtgFq>#@5eUW+x|El_X`a>kKjl3Z91-2eF z^Er;HkHHmtJz(vi=0%?pEm3!!!EzCvr@CylhZPdU%g(&L@C z$?VI|@*q(?vk~CDRy<~)5$`81q8lJCsw;rK&KUa?KpXbBNqu(zS*`|a5Q^j?_Wf@58JGH@lB{*JKytk9HZV^AElN^He3=$w_{q2G|E2wyW~wv+0gmFPa9K?X$C zwFGe2n>x3>Fq zgw5Gg+9F@`o5`=UkdpZG>!vIpg3!6Cr5yy4kGyA^3(RZQNZYHv&oC6T+V;8sq@)SGqdea3xkLeyDmGY6(BP$#|e! zp*jm!0NxQ2!}#Tiu04qt}i%SL?0)>a9{Z?jep z%o=$dNEl7|w0;$G0_;F}2M;ui_k_?My?XHFm--d{{E46klt6ZBls7TNg(8d`2Q^n^ z@rREF#Ldqc!@?I)euW&mV*c)TU{bVE8zLX4{l!229~VNeKoC=5(C>5)Op+ZWj&ab$PVi5 zT+rc8j(i*);c<`$WRNOVDlItK; zwO1f(Gin{}82Fiqw*HcL1+Xhm+St;gHAaboct_PE4deP<;I93oW)W-j+1Cf0`8{#; z^Z(=LMmUonRZFvTYC~y=!WNS{w&gadD=uafXRr*{DHKJ`^I0^>hhXo(2MzM{I#S+Eev293&Z;|t zLi+$E-}Kia!7WW;u)WLQTwN&@<&z+sy>g%3SqNMcKFeIPBiG9BJqBg<=bnXps|*`w z5hsQP5TZV&Tv2sWa#8>x;CGAmmMH{zH?sAqNk z#2%Q}Rwk2n+vL1WpNoLf7UNe>5=}2;mYSEkfP)|Sp-ng{G9XFL&z48?pk`vR5mNyx z%kU3AQiyASW^ysttXYoW9QKYQ1R{&7$u!C4#pLo=@~p0)bWAR`@m!YQ_f_rDQ>YWq z*WOF^`w7JCV=ol@A`mUIbeS64EX4dE={XCNdJJX2sT{9} z8x$dxY{;Pw!1hE9-18AxO(_~8RZ$?QqeQqkiXVi$!!^hZ-1a;-Wy@O1I@FA`NI#*C ztNHUr<=`qarL1AIi?Q5wggcHcBW*xm2{dF&tJbFqJ%q#^bDY_iT-8 zX?LYNJ|4=?r};|(+Jn#eQZn^L`2JMsUK{x$2KM_j?$*t$`CH0)dp87}Jgtj?1{#{( zGV)Ho%B1UuUyNBpOLO?mXJ(F|4u*I@tr?1q_UJXC-+aVWMCr)1=uv8Ai+@IJj z=SXnTBahyKC0}duDr+k%og07165>Q{>h{A2h_GYGi%IvDoWY+VX5K5b&t_b=3O`}c2hH1_9)@Ym za-hYFO2g5m(XA>4yqRy1JDog{lLLEp^818ZuL0b<6#A+Jx~ZPn83b5id*4rmtGoq?P--N>oGVeUc~#lp~^p$}H@LP7|deR}bMM)Mn- zfoCv~x2n3Ebbz7BT7HGa#3$^89a&13q}_-`^*!x&*8Uf_SO@_}A5XRn!n$TaF~k
      th_v6 z{TaHEX%;GE7|^T74HCP#W(00O7?vL}eDQ7DQ5}IssQTt~e=o?79s_H*^BSzXyjaZE zIfu1%A$|dnZ9K!ed9I z5ry!yN@Zhoh7bq;c;j$|ML+0$_&OMOso4YUE6swhx^t8baT&S zSAMew?>u=a!1(eDEDD)rw}5ej5av7KnylEVB?xVy`kH{95ruxwQ26_d*Oy1QLFKIVTq zvAmCJho%TZ6W(e_*Jc!SOi( zo^XNkmB}n@rHs-OUkZYm(R~{4%1e;wgLs>F#8oNWht;oWbZxDxSg0)2?62KtxzH;p zlYxcFZ#!uLmF|Y#_~cB^Ow?R^Z%NsidmemW=^wo9l>)x(QO{Wkq=^>Jw!j3UA@e^^ z_G9GlBw{AECE*m(TA*O~%`SA#E?52Z;adVFM(yr;u6huH;iL#dWLX$}ps;Mnf8bnz z9_v@{iu$m(faEw^QVczZ()S58ufJf->L`qeu({B+b8_+BA!Po;52r!TgEO~6{{M$B z2>$-TyY~`wp2a(4OWVeEb0!|3xx5bb4QtOgdu3h>$DUt6b z%x@=K%Q65#IZ8n?`->Ujj&I;f=Q7edD&4z*Y8Fl&LJU3?(3#)_)O6$-Th|aF+07WX z6l*>`9UZKCh7(MFTpwp8zGxfyZyjM8K=vN>O>>%Zb<6&Ak)8(dl}{2GIXL+dg)YL9 zLI$ecTM0~oGq&U};^Z%qz})fg{)hlm)2E7dr=Wn$EIsHeUBn=63=NF?#Db&7BZfSeHkb&4ttqYL{b&q8EI&p{}A%>TH2mv zY0O37QzY`&Uy(y)#^DV$mpEN=goEi$mbxtk>PpfaX-CWXiisDLutol82N}2(3}uF* z$eKAlAC2PrebP;Yn<1z>*ogWb@%!3L+=xBonVaC0=f+`uXjzGK)?q+;{18Q8ZdI!n zBleviO^z)X(&VzMUDcGFN_2!+>dG^X9eK@|Bb_WOD}cBYvKBw=@1IFgQ%bcc7nO9q!Cxr0SnW`6(k@iO=- zBl<1=v$rksa`t?Q+WBi9b?evH~&U-T+5^{%XsD1z0E;S|T_jJJ~sP2#L*?*h* z6nSCvtc3s6Bb2Wm2jwq#9Ob^ViIOX_DxZcBvR_*#X4z*66dj?OHWxGsdD=(tHVXaP zE+WBqbp_z2UmIBGzoJ(s(Bsul{@m3rg2X&-uz5q=?3Y2984~V9(Q~`@%tuiS4ggOy$&qQrwEIul=9B zK?4M4x)#n&Kk)(+ zc>?Gb-7K^|-!mZoTo*tfw(BD_pZ_??pS(7q)DG?4UFu$tTl$e_1p57-qophaOx|%L zo)MkxQm97zB(BknA>;@$tcw>nS3q$nEu*BNVm6eXAND-5i`?w$gZBRPQ$BNZfy!Yn zFN4UkvLJJ^nWB}ynv%yqVB-H~Lhzpz!2Gn1ISyTE(fuxZyM91X(qDPjvM=eJCNx@J zb{r8|QIKn^vr~S&b=yA!{}&zRinpL3pTvS(j785Kyc{_f=o$S(_IpMM@;t|cTyXY* z2BJ~uGNNEh^h9-&(&PUfhmAotbKw82^30)~;l2@J*ADvo_a;6*${wLC+s4Uz)Xwtc zhSoD4{|zMEp0jZQlY*@`g9L*r!xT^D`;(kb-v4umhyQ*Z<9W#w+@B3JI6f}|6+1GW zkKZqz*teVoxUI6$gCRn#H)Zzopr E02ZD5p8x;= literal 0 HcmV?d00001 diff --git a/misc/PublicationBulletins/Portail-LeHavre/imgs/plus_img.png b/misc/PublicationBulletins/Portail-LeHavre/imgs/plus_img.png new file mode 100755 index 0000000000000000000000000000000000000000..a9ee6407b2a67b6fdffdf31a495e8eea271c9c16 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4xHBpZ(Y^aoM~0X`wF{~7)RnE?R-4Gj$k4jce- z8W $valeur ) { + $headers_brut .= $nom . ': ' . $valeur . "\r\n"; + } + + return $headers_brut; +} + + +function get_EtudAbs_page($nip, $dept,$beg_date) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $end_date=date("Y-m-d"); + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw, + 'beg_date' => $beg_date, + 'end_date' => $end_date); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Absences/XMLgetAbsEtud', false, $contexte ); + + return ($retour); +} + + +function get_BilletAbs_list($nip, $dept) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw, +); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); +//echo $sco_url . $dept . '/Scolarite/Absences/XMLgetBilletsEtud'. + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Absences/XMLgetBilletsEtud', false, $contexte ); + + return ($retour); +} + + +function Get_EtudAbs_billet($nip, $dept,$begin,$end,$description) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $end_date=date("Y-m-d"); +$justified="0"; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw, + 'description' =>$description, + 'justified' =>$justified, + 'begin' => $begin, + 'end' => $end); + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Absences/AddBilletAbsence', false, $contexte ); + + return ($retour); +} + + +function get_EtudInfos_page($nip, $dept) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + + $donnees = array( + 'code_nip' => $nip, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/XMLgetEtudInfos', false, $contexte ); + + return ($retour); +} + +function get_bulletinetud_page($nip, $sem, $dept) { + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'format' => 'xml', + 'code_nip' => $nip, + 'formsemestre_id' => $sem, + 'version' => 'selectedevals', + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/formsemestre_bulletinetud', false, $contexte ); + + return ($retour); +} + +function get_semestre_info($sem, $dept) +{ + global $user_agent; + global $sco_user; + global $sco_pw; + global $sco_url; + $donnees = array( + 'formsemestre_id' => $sem, + '__ac_name' => $sco_user, + '__ac_password' => $sco_pw ); + + // Création du contenu brut de la requęte + $contenu = http_build_query( $donnees ); + + // Définition des headers + $headers = http_build_headers( array( + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Content-Length' => strlen( $contenu) ) ); + + // Définition du contexte + $options = array( 'http' => array( 'user_agent' => $user_agent, + 'method' => 'POST', + 'content' => $contenu, + 'header' => $headers ) ); + + // Création du contexte + $contexte = stream_context_create($options); + + // Envoi du formulaire POST + $retour = file_get_contents( $sco_url . $dept . '/Scolarite/Notes/XMLgetFormsemestres', false, $contexte ); + + return ($retour); +} + +function get_all_semestres($xml_data) +{ + $data = array(); + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + $sem = (array) $s['formsemestre_id']; + $data[] = $sem[0]; + } + return $data; +} + +function get_current_semestre($xml_data) +{ + $xml = simplexml_load_string($xml_data); + foreach ($xml->insemestre as $s) { + if ($s['current'] == 1){ + $sem = (array) $s['formsemestre_id']; + return ($sem[0]);} + else{$sem = ""; + return ($sem);} + } + +} + +function print_semestres_list($sems, $dept, $sem) +{ + echo 'Semestre : + +Déconnexion +'; +} + +function print_semestre($xml_data, $sem, $dept, $show_moy=False) +{ + global $etudid; + global $nip; + global $sco_user; + global $sco_pw; + global $sco_url; + $modules=array(); + $codesmodules=array(); + $i=0; + if($sem==""){echo '

      Il n'y a pas de semestre courant

      ';} else{ + $xml = simplexml_load_string($xml_data); + $etudid= $xml->etudiant['etudid']; + + $retour = get_semestre_info($sem, $dept); + $xml2 = simplexml_load_string($retour); + $debut=date("Y-m-d",strtotime($xml2->formsemestre['dateord'])); + $finsemestre=$xml2->formsemestre['date_fin_iso']; + $fin=strtotime($finsemestre)+3000000; + $day=strtotime(date("d-m-Y")); + $publie= $xml2->formsemestre['bul_hide_xml']; + + // teste la publication et affiche un message si non publié + // $showmoy teste si on est avant date de fin du semestre + // si la date du jour dépasse de 45 jours la date de fin de semestre on affiche les moyennes + // sinon pas encore + + $sexe=$xml->etudiant['sexe']; + $prenom=$xml->etudiant['prenom']; + $nom=$xml->etudiant['nom']; + $semestre=$xml2->formsemestre['titre_num']; + + if ($publie=="0") { + if (!$show_moy) { + echo '

      Les informations contenues dans ces tableaux sont + provisoires. L'état n'a pas valeur de bulletin de notes.';} + + echo '
      Il vous appartient de contacter vos enseignants + ou votre département en cas de désaccord.

      '; + + echo '

      ' . convertir_utf8($sexe). ' ' . convertir_utf8($prenom). ' ' . convertir_utf8($nom). '

      '; + //echo '
      '; + + echo ''.convertir_utf8($semestre).'
      '; + if (!$show_moy) { echo "vous avez ŕ ce jour ".convertir_utf8($xml->absences['nbabs'])." demi-journées d'absences dont ".convertir_utf8($xml->absences['nbabsjust']).' justifiées';} + + echo ' +'; + echo '
      +

      - Cliquez ici pour afficher/masquer le bulletin de notes

      + +


      +

      Gestion des absences

      +
      Les régles de gestion peuvent actuellement dépendre des départements. La déclaration en ligne ne suffit pas.
      + + + +

      - Cliquez ici pour afficher/masquer la liste des absences du semestre

      '; + + $retourabs = get_EtudAbs_page($nip, $dept,$debut); + $xmlabs = simplexml_load_string($retourabs); + + + + echo ' + '; + $retourbillets = get_BilletAbs_list($nip, $dept); +if($retourbillets!=''){ +echo '

      - Déclaration d'un billet d'absences

      + '; + + + + +if (isset($_POST['submit']) && $_POST['submit'] == "Envoyer"){ +$description=$_POST["description"]; + +$date1 = new DateTime($_POST["begin"]); +$date1->setTime(intval(substr($_POST["begtime"],0,2)), intval(substr($_POST["begtime"],-2)))+1; + +$date2 = new DateTime($_POST["end"]); +$date2->setTime(intval(substr($_POST["endtime"],0,2)), intval(substr($_POST["endtime"],-2))-31); + +if(!$description){$description="Motif: ".$description." - Matičres: " ;} + for($i=1;$i'; + + Get_EtudAbs_billet($nip, $dept,$date1 , $date2 , $description); + } + +// pour tester le renvoi des variables + //print_r($_POST); + +echo ' + +
      - Cliquez ici pour afficher/masquer les billets d'absences déposés
      + +'; +} + +}} +else +{echo '

      Votre département n'a pas autorisé l'affichage des informations de ce semestre

      ';} + +}} + + + +function get_dept($nip) +{ + global $sco_url; + $dept = file_get_contents( $sco_url . 'get_etud_dept?code_nip=' . $nip); + return ($dept); +} + + +// function pour la recuperation des infos ldap +function search_results($info) { + foreach ($info as $inf) { + if (is_array($inf)) { + foreach ($inf as $key => $in) { + if ((count($inf[$key]) - 1) > 0) { + if (is_array($in)) { + unset($inf[$key]["count"]); + } + $results[$key] = $inf[$key]; + } + } + } + } + $results["dn"] = explode(',', $info[0]["dn"]); + return $results; +} + + + +// Programme principal +// $nip="20121713"; + +echo ' + +Portail Webnotes + + + + + + + + + + '; + +echo " + + + + +"; + +//$user = $_SERVER['PHP_AUTH_USER']; +//echo 'USER: '.$user."\n"."
      "; + +//$user = "ei121713"; +//echo "On triche USER = ".$user."\n"."
      "; +/* +$ds = ldap_connect("ldap://ldap"); +if ($ds) { + $r = ldap_bind($ds); + $sr = ldap_search($ds, "ou=people,dc=univ-lehavre,dc=fr", "(&(objectClass=ulhEtudiant)(uid=$user))"); + $info = ldap_get_entries($ds, $sr); + + //echo $info["count"]." IS Search Result(s) for \"".$user."\"\n"; + $results = search_results($info); + // si pas de reponse de l a nnuaire, ce n est pas un etudiant + if ($info["count"] == 0 ) { + echo ' + + getEtud + + +

      Service de consultation des notes

      +
      + Il faut être etudiant de l'IUT pour accéder à ses notes. +
      + + '; + } else { + foreach ($results as $key => $result) { + if ($key == 'supannetuid' ) { + //echo " * ".$key." : \n"; + if (is_array($result)){ + foreach($result as $res){ + //echo " ".$res."\n"; + } + } + //echo "
      "; + $nip=$res; + } + } + } + ldap_close($ds); +}*/ +// Login information of a scodoc user that can access notes +$sco_user = 'lecturenotes'; +$sco_pw = 'lecture2014'; +//$sco_url = 'https://test1-scodoc.iut.univ-lehavre.fr/ScoDoc/'; +$nip="20121713"; +$sco_url = 'https://scodoc-demo.iut.univ-lehavre.fr/ScoDoc/'; +//$sco_url = 'https://scodoc.univ-lehavre.fr/ScoDoc/'; +$user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0'; + +echo ''; +if ($nip) { +$dept = get_dept($nip); +if ($dept) { + $retour = get_EtudInfos_page($nip, $dept); + $sems = get_all_semestres($retour); + $sem_current = get_current_semestre($retour); + if (isset($_POST["sem"])) { + $sem = $_POST["sem"]; + } + else { + $sem = $sem_current; + } + + print_semestres_list($sems, $dept, $sem); + if($sem==""){echo '

      Il n'y a pas de semestre en cours - Choisissez éventuellement dans la liste.

      ';} else{ + $retour = get_bulletinetud_page($nip, $sem, $dept); + if ($sem == $sem_current) { + print_semestre($retour, $sem, $dept, False); + } + else { + print_semestre($retour, $sem, $dept, True); + } + $erreur=0; // Tout est OK +}} +else { + echo "Numéro étudiant inconnu : " . $nip . ". Contactez votre Chef de département."; + +}} + +echo ''; + + +echo ' + +'; + + +?> \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/jQuery/ColVis.js b/misc/PublicationBulletins/Portail-LeHavre/jQuery/ColVis.js new file mode 100755 index 00000000..593ad472 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/jQuery/ColVis.js @@ -0,0 +1,1133 @@ +/* + * File: ColVis.js + * Version: 1.1.0-dev + * CVS: $Id$ + * Description: Controls for column visiblity in DataTables + * Author: Allan Jardine (www.sprymedia.co.uk) + * Created: Wed Sep 15 18:23:29 BST 2010 + * Modified: $Date$ by $Author$ + * Language: Javascript + * License: GPL v2 or BSD 3 point style + * Project: Just a little bit of fun :-) + * Contact: www.sprymedia.co.uk/contact + * + * Copyright 2010-2011 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, available at: + * http://datatables.net/license_gpl2 + * http://datatables.net/license_bsd + */ + +(function($) { + +/** + * ColVis provides column visiblity control for DataTables + * @class ColVis + * @constructor + * @param {object} DataTables settings object + */ +var ColVis = function( oDTSettings, oInit ) +{ + /* Santiy check that we are a new instance */ + if ( !this.CLASS || this.CLASS != "ColVis" ) + { + alert( "Warning: ColVis must be initialised with the keyword 'new'" ); + } + + if ( typeof oInit == 'undefined' ) + { + oInit = {}; + } + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public class variables + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * @namespace Settings object which contains customisable information for ColVis instance + */ + this.s = { + /** + * DataTables settings object + * @property dt + * @type Object + * @default null + */ + "dt": null, + + /** + * Customisation object + * @property oInit + * @type Object + * @default passed in + */ + "oInit": oInit, + + /** + * Callback function to tell the user when the state has changed + * @property fnStateChange + * @type function + * @default null + */ + "fnStateChange": null, + + /** + * Mode of activation. Can be 'click' or 'mouseover' + * @property activate + * @type String + * @default click + */ + "activate": "click", + + /** + * Position of the collection menu when shown - align "left" or "right" + * @property sAlign + * @type String + * @default right + */ + "sAlign": "left", + + /** + * Text used for the button + * @property buttonText + * @type String + * @default Show / hide columns + */ + "buttonText": "Show / hide columns", + + /** + * Flag to say if the collection is hidden + * @property hidden + * @type boolean + * @default true + */ + "hidden": true, + + /** + * List of columns (integers) which should be excluded from the list + * @property aiExclude + * @type Array + * @default [] + */ + "aiExclude": [], + + /** + * Group buttons + * @property aoGroups + * @type Array + * @default [] + */ + "aoGroups": [], + + /** + * Store the original viisbility settings so they could be restored + * @property abOriginal + * @type Array + * @default [] + */ + "abOriginal": [], + + /** + * Show Show-All button + * @property bShowAll + * @type Array + * @default [] + */ + "bShowAll": false, + + /** + * Show All button text + * @property sShowAll + * @type String + * @default Restore original + */ + "sShowAll": "Show All", + + /** + * Show restore button + * @property bRestore + * @type Array + * @default [] + */ + "bRestore": false, + + /** + * Restore button text + * @property sRestore + * @type String + * @default Restore original + */ + "sRestore": "Restore original", + + /** + * Overlay animation duration in mS + * @property iOverlayFade + * @type Integer + * @default 500 + */ + "iOverlayFade": 500, + + /** + * Label callback for column names. Takes three parameters: 1. the column index, 2. the column + * title detected by DataTables and 3. the TH node for the column + * @property fnLabel + * @type Function + * @default null + */ + "fnLabel": null, + + /** + * Indicate if ColVis should automatically calculate the size of buttons or not. The default + * is for it to do so. Set to "css" to disable the automatic sizing + * @property sSize + * @type String + * @default auto + */ + "sSize": "auto", + + /** + * Indicate if the column list should be positioned by Javascript, visually below the button + * or allow CSS to do the positioning + * @property bCssPosition + * @type boolean + * @default false + */ + "bCssPosition": false + }; + + + /** + * @namespace Common and useful DOM elements for the class instance + */ + this.dom = { + /** + * Wrapper for the button - given back to DataTables as the node to insert + * @property wrapper + * @type Node + * @default null + */ + "wrapper": null, + + /** + * Activation button + * @property button + * @type Node + * @default null + */ + "button": null, + + /** + * Collection list node + * @property collection + * @type Node + * @default null + */ + "collection": null, + + /** + * Background node used for shading the display and event capturing + * @property background + * @type Node + * @default null + */ + "background": null, + + /** + * Element to position over the activation button to catch mouse events when using mouseover + * @property catcher + * @type Node + * @default null + */ + "catcher": null, + + /** + * List of button elements + * @property buttons + * @type Array + * @default [] + */ + "buttons": [], + + /** + * List of group button elements + * @property groupButtons + * @type Array + * @default [] + */ + "groupButtons": [], + + /** + * Restore button + * @property restore + * @type Node + * @default null + */ + "restore": null + }; + + /* Store global reference */ + ColVis.aInstances.push( this ); + + /* Constructor logic */ + this.s.dt = oDTSettings; + this._fnConstruct(); + return this; +}; + + + +ColVis.prototype = { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Rebuild the list of buttons for this instance (i.e. if there is a column header update) + * @method fnRebuild + * @returns void + */ + "fnRebuild": function () + { + /* Remove the old buttons */ + for ( var i=this.dom.buttons.length-1 ; i>=0 ; i-- ) + { + if ( this.dom.buttons[i] !== null ) + { + this.dom.collection.removeChild( this.dom.buttons[i] ); + } + } + this.dom.buttons.splice( 0, this.dom.buttons.length ); + + if ( this.dom.restore ) + { + this.dom.restore.parentNode( this.dom.restore ); + } + + /* Re-add them (this is not the optimal way of doing this, it is fast and effective) */ + this._fnAddGroups(); + this._fnAddButtons(); + + /* Update the checkboxes */ + this._fnDrawCallback(); + }, + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Private methods (they are of course public in JS, but recommended as private) + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Constructor logic + * @method _fnConstruct + * @returns void + * @private + */ + "_fnConstruct": function () + { + this._fnApplyCustomisation(); + + var that = this; + var i, iLen; + this.dom.wrapper = document.createElement('div'); + this.dom.wrapper.className = "ColVis TableTools"; + + this.dom.button = this._fnDomBaseButton( this.s.buttonText ); + this.dom.button.className += " ColVis_MasterButton"; + this.dom.wrapper.appendChild( this.dom.button ); + + this.dom.catcher = this._fnDomCatcher(); + this.dom.collection = this._fnDomCollection(); + this.dom.background = this._fnDomBackground(); + + this._fnAddGroups(); + this._fnAddButtons(); + + /* Store the original visbility information */ + for ( i=0, iLen=this.s.dt.aoColumns.length ; i'+this.s.sRestore+'' ); + + $(nButton).click( function (e) { + for ( var i=0, iLen=that.s.abOriginal.length ; i'+this.s.sShowAll+'' ); + + $(nButton).click( function (e) { + for ( var i=0, iLen=that.s.abOriginal.length ; i'+ + ''+sTitle+'' ); + + $(nButton).click( function (e) { + var showHide = !$('input', this).is(":checked"); + if ( e.target.nodeName.toLowerCase() == "input" ) + { + showHide = $('input', this).is(":checked"); + } + + for ( var j=0 ; j < oGroup.aiColumns.length ; j++ ) + { + that.s.dt.oInstance.fnSetColumnVis( oGroup.aiColumns[j], showHide ); + } + }); + + return nButton; + }, + + + /** + * Create the DOM for a show / hide button + * @method _fnDomColumnButton + * @param {int} i Column in question + * @returns {Node} Created button + * @private + */ + "_fnDomColumnButton": function ( i ) + { + var + that = this, + oColumn = this.s.dt.aoColumns[i], + nButton = document.createElement('button'), + nSpan = document.createElement('span'), + dt = this.s.dt; + + nButton.className = !dt.bJUI ? "ColVis_Button TableTools_Button" : + "ColVis_Button TableTools_Button ui-button ui-state-default"; + nButton.appendChild( nSpan ); + var sTitle = this.s.fnLabel===null ? oColumn.sTitle : this.s.fnLabel( i, oColumn.sTitle, oColumn.nTh ); + $(nSpan).html( + ''+ + ''+sTitle+'' ); + + $(nButton).click( function (e) { + var showHide = !$('input', this).is(":checked"); + if ( e.target.nodeName.toLowerCase() == "input" ) + { + showHide = $('input', this).is(":checked"); + } + + /* Need to consider the case where the initialiser created more than one table - change the + * API index that DataTables is using + */ + var oldIndex = $.fn.dataTableExt.iApiIndex; + $.fn.dataTableExt.iApiIndex = that._fnDataTablesApiIndex.call(that); + + // Optimisation for server-side processing when scrolling - don't do a full redraw + if ( dt.oFeatures.bServerSide && (dt.oScroll.sX !== "" || dt.oScroll.sY !== "" ) ) + { + that.s.dt.oInstance.fnSetColumnVis( i, showHide, false ); + that.s.dt.oInstance.fnAdjustColumnSizing( false ); + that.s.dt.oInstance.oApi._fnScrollDraw( that.s.dt ); + that._fnDrawCallback(); + } + else + { + that.s.dt.oInstance.fnSetColumnVis( i, showHide ); + } + + $.fn.dataTableExt.iApiIndex = oldIndex; /* Restore */ + + if ( that.s.fnStateChange !== null ) + { + that.s.fnStateChange.call( that, i, showHide ); + } + } ); + + return nButton; + }, + + + /** + * Get the position in the DataTables instance array of the table for this instance of ColVis + * @method _fnDataTablesApiIndex + * @returns {int} Index + * @private + */ + "_fnDataTablesApiIndex": function () + { + for ( var i=0, iLen=this.s.dt.oInstance.length ; i
      ","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
      ","
      "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
      ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.3.2.min.js b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.3.2.min.js new file mode 100755 index 00000000..55c2e6d7 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.3.2.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"
      ","
      "]||!O.indexOf("
      "]||(!O.indexOf("
      "]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
      ","
      "]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="
      ";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

      ";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
      ";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
      ").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
      ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.8.2.min.js b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.8.2.min.js new file mode 100755 index 00000000..f65cf1dc --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery-1.8.2.min.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.2 jquery.com | jquery.org/license */ +(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
      a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
      t
      ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
      ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
      ",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

      ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
      ","
      "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
      ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.dataTables.js b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.dataTables.js new file mode 100755 index 00000000..27f1c278 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.dataTables.js @@ -0,0 +1,12099 @@ +/** + * @summary DataTables + * @description Paginate, search and sort HTML tables + * @version 1.9.4 + * @file jquery.dataTables.js + * @author Allan Jardine (www.sprymedia.co.uk) + * @contact www.sprymedia.co.uk/contact + * + * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, available at: + * http://datatables.net/license_gpl2 + * http://datatables.net/license_bsd + * + * This source file 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 license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +/*jslint evil: true, undef: true, browser: true */ +/*globals $, jQuery,define,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString,_fnRender,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns*/ + +(/** @lends */function( window, document, undefined ) { + +(function( factory ) { + "use strict"; + + // Define as an AMD module if possible + if ( typeof define === 'function' && define.amd ) + { + define( ['jquery'], factory ); + } + /* Define using browser globals otherwise + * Prevent multiple instantiations if the script is loaded twice + */ + else if ( jQuery && !jQuery.fn.dataTable ) + { + factory( jQuery ); + } +} +(/** @lends */function( $ ) { + "use strict"; + /** + * DataTables is a plug-in for the jQuery Javascript library. It is a + * highly flexible tool, based upon the foundations of progressive + * enhancement, which will add advanced interaction controls to any + * HTML table. For a full list of features please refer to + * DataTables.net. + * + * Note that the DataTable object is not a global variable but is + * aliased to jQuery.fn.DataTable and jQuery.fn.dataTable through which + * it may be accessed. + * + * @class + * @param {object} [oInit={}] Configuration object for DataTables. Options + * are defined by {@link DataTable.defaults} + * @requires jQuery 1.3+ + * + * @example + * // Basic initialisation + * $(document).ready( function { + * $('#example').dataTable(); + * } ); + * + * @example + * // Initialisation with configuration options - in this case, disable + * // pagination and sorting. + * $(document).ready( function { + * $('#example').dataTable( { + * "bPaginate": false, + * "bSort": false + * } ); + * } ); + */ + var DataTable = function( oInit ) + { + + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + var oDefaults = DataTable.defaults.columns; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "sSortingClass": oSettings.oClasses.sSortable, + "sSortingClassJUI": oSettings.oClasses.sSortJUI, + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.oDefaults : iCol + } ); + oSettings.aoColumns.push( oCol ); + + /* Add a column specific filter */ + if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null ) + { + oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch ); + } + else + { + var oPre = oSettings.aoPreSearchCols[ iCol ]; + + /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ + if ( oPre.bRegex === undefined ) + { + oPre.bRegex = true; + } + + if ( oPre.bSmart === undefined ) + { + oPre.bSmart = true; + } + + if ( oPre.bCaseInsensitive === undefined ) + { + oPre.bCaseInsensitive = true; + } + } + + /* Use the column options function to initialise classes etc */ + _fnColumnOptions( oSettings, iCol, null ); + } + + + /** + * Apply options for a column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable etc + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + + if ( oOptions.sType !== undefined ) + { + oCol.sType = oOptions.sType; + oCol._bAutoType = false; + } + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; + var mData = _fnGetObjectDataFn( oCol.mData ); + + oCol.fnGetData = function (oData, sSpecific) { + var innerData = mData( oData, sSpecific ); + + if ( oCol.mRender && (sSpecific && sSpecific !== '') ) + { + return mRender( innerData, sSpecific, oData ); + } + return innerData; + }; + oCol.fnSetData = _fnSetObjectDataFn( oCol.mData ); + + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) + { + oCol.bSortable = false; + } + + /* Check that the class assignment is correct for sorting */ + if ( !oCol.bSortable || + ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortable; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI; + } + else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableAsc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableDesc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( oSettings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( oSettings.oFeatures.bAutoWidth === false ) + { + return false; + } + + _fnCalculateColumnWidths( oSettings ); + for ( var i=0 , iLen=oSettings.aoColumns.length ; i
      ')[0]; + oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable ); + + /* + * All DataTables are wrapped in a div + */ + oSettings.nTableWrapper = $('
      ')[0]; + oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; + + /* Track where we want to insert the option */ + var nInsertNode = oSettings.nTableWrapper; + + /* Loop over the user set positioning and place the elements as needed */ + var aDom = oSettings.sDom.split(''); + var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j; + for ( var i=0 ; i')[0]; + + /* Check to see if we should append an id and/or a class name to the container */ + cNext = aDom[i+1]; + if ( cNext == "'" || cNext == '"' ) + { + sAttr = ""; + j = 2; + while ( aDom[i+j] != cNext ) + { + sAttr += aDom[i+j]; + j++; + } + + /* Replace jQuery UI constants */ + if ( sAttr == "H" ) + { + sAttr = oSettings.oClasses.sJUIHeader; + } + else if ( sAttr == "F" ) + { + sAttr = oSettings.oClasses.sJUIFooter; + } + + /* The attribute can be in the format of "#id.class", "#id" or "class" This logic + * breaks the string into parts and applies them as needed + */ + if ( sAttr.indexOf('.') != -1 ) + { + var aSplit = sAttr.split('.'); + nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); + nNewNode.className = aSplit[1]; + } + else if ( sAttr.charAt(0) == "#" ) + { + nNewNode.id = sAttr.substr(1, sAttr.length-1); + } + else + { + nNewNode.className = sAttr; + } + + i += j; /* Move along the position array */ + } + + nInsertNode.appendChild( nNewNode ); + nInsertNode = nNewNode; + } + else if ( cOption == '>' ) + { + /* End container div */ + nInsertNode = nInsertNode.parentNode; + } + else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) + { + /* Length */ + nTmp = _fnFeatureHtmlLength( oSettings ); + iPushFeature = 1; + } + else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) + { + /* Filter */ + nTmp = _fnFeatureHtmlFilter( oSettings ); + iPushFeature = 1; + } + else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) + { + /* pRocessing */ + nTmp = _fnFeatureHtmlProcessing( oSettings ); + iPushFeature = 1; + } + else if ( cOption == 't' ) + { + /* Table */ + nTmp = _fnFeatureHtmlTable( oSettings ); + iPushFeature = 1; + } + else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) + { + /* Info */ + nTmp = _fnFeatureHtmlInfo( oSettings ); + iPushFeature = 1; + } + else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) + { + /* Pagination */ + nTmp = _fnFeatureHtmlPaginate( oSettings ); + iPushFeature = 1; + } + else if ( DataTable.ext.aoFeatures.length !== 0 ) + { + /* Plug-in features */ + var aoFeatures = DataTable.ext.aoFeatures; + for ( var k=0, kLen=aoFeatures.length ; k') : + sSearchStr==="" ? '' : sSearchStr+' '; + + var nFilter = document.createElement( 'div' ); + nFilter.className = oSettings.oClasses.sFilter; + nFilter.innerHTML = ''; + if ( !oSettings.aanFeatures.f ) + { + nFilter.id = oSettings.sTableId+'_filter'; + } + + var jqFilter = $('input[type="text"]', nFilter); + + // Store a reference to the input element, so other input elements could be + // added to the filter wrapper if needed (submit button for example) + nFilter._DT_Input = jqFilter[0]; + + jqFilter.val( oPreviousSearch.sSearch.replace('"','"') ); + jqFilter.bind( 'keyup.DT', function(e) { + /* Update all other filter input elements for the new display */ + var n = oSettings.aanFeatures.f; + var val = this.value==="" ? "" : this.value; // mental IE8 fix :-( + + for ( var i=0, iLen=n.length ; i=0 ; i-- ) + { + var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ), + oSettings.aoColumns[iColumn].sType ); + if ( ! rpSearch.test( sData ) ) + { + oSettings.aiDisplay.splice( i, 1 ); + iIndexCorrector++; + } + } + } + + + /** + * Filter the data table based on user input and draw the table + * @param {object} oSettings dataTables settings object + * @param {string} sInput string to filter on + * @param {int} iForce optional - force a research of the master array (1) or not (undefined or 0) + * @param {bool} bRegex treat as a regular expression or not + * @param {bool} bSmart perform smart filtering or not + * @param {bool} bCaseInsensitive Do case insenstive matching or not + * @memberof DataTable#oApi + */ + function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart, bCaseInsensitive ) + { + var i; + var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive ); + var oPrevSearch = oSettings.oPreviousSearch; + + /* Check if we are forcing or not - optional parameter */ + if ( !iForce ) + { + iForce = 0; + } + + /* Need to take account of custom filtering functions - always filter */ + if ( DataTable.ext.afnFiltering.length !== 0 ) + { + iForce = 1; + } + + /* + * If the input is blank - we want the full data set + */ + if ( sInput.length <= 0 ) + { + oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + } + else + { + /* + * We are starting a new search or the new search string is smaller + * then the old one (i.e. delete). Search from the master array + */ + if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length || + oPrevSearch.sSearch.length > sInput.length || iForce == 1 || + sInput.indexOf(oPrevSearch.sSearch) !== 0 ) + { + /* Nuke the old display array - we are going to rebuild it */ + oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); + + /* Force a rebuild of the search array */ + _fnBuildSearchArray( oSettings, 1 ); + + /* Search through all records to populate the search array + * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 + * mapping + */ + for ( i=0 ; i').html(sSearch).text(); + } + + // Strip newline characters + return sSearch.replace( /[\n\r]/g, " " ); + } + + /** + * Build a regular expression object suitable for searching a table + * @param {string} sSearch string to search for + * @param {bool} bRegex treat as a regular expression or not + * @param {bool} bSmart perform smart filtering or not + * @param {bool} bCaseInsensitive Do case insensitive matching or not + * @returns {RegExp} constructed object + * @memberof DataTable#oApi + */ + function _fnFilterCreateSearch( sSearch, bRegex, bSmart, bCaseInsensitive ) + { + var asSearch, sRegExpString; + + if ( bSmart ) + { + /* Generate the regular expression to use. Something along the lines of: + * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$ + */ + asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' ); + sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$'; + return new RegExp( sRegExpString, bCaseInsensitive ? "i" : "" ); + } + else + { + sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch ); + return new RegExp( sSearch, bCaseInsensitive ? "i" : "" ); + } + } + + + /** + * Convert raw data into something that the user can search on + * @param {string} sData data to be modified + * @param {string} sType data type + * @returns {string} search string + * @memberof DataTable#oApi + */ + function _fnDataToSearch ( sData, sType ) + { + if ( typeof DataTable.ext.ofnSearch[sType] === "function" ) + { + return DataTable.ext.ofnSearch[sType]( sData ); + } + else if ( sData === null ) + { + return ''; + } + else if ( sType == "html" ) + { + return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" ); + } + else if ( typeof sData === "string" ) + { + return sData.replace(/[\r\n]/g," "); + } + return sData; + } + + + /** + * scape a string such that it can be used in a regular expression + * @param {string} sVal string to escape + * @returns {string} escaped string + * @memberof DataTable#oApi + */ + function _fnEscapeRegex ( sVal ) + { + var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ]; + var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); + return sVal.replace(reReplace, '\\$1'); + } + + + /** + * Generate the node required for the info display + * @param {object} oSettings dataTables settings object + * @returns {node} Information element + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlInfo ( oSettings ) + { + var nInfo = document.createElement( 'div' ); + nInfo.className = oSettings.oClasses.sInfo; + + /* Actions that are to be taken once only for this feature */ + if ( !oSettings.aanFeatures.i ) + { + /* Add draw callback */ + oSettings.aoDrawCallback.push( { + "fn": _fnUpdateInfo, + "sName": "information" + } ); + + /* Add id */ + nInfo.id = oSettings.sTableId+'_info'; + } + oSettings.nTable.setAttribute( 'aria-describedby', oSettings.sTableId+'_info' ); + + return nInfo; + } + + + /** + * Update the information elements in the display + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnUpdateInfo ( oSettings ) + { + /* Show information about the table */ + if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 ) + { + return; + } + + var + oLang = oSettings.oLanguage, + iStart = oSettings._iDisplayStart+1, + iEnd = oSettings.fnDisplayEnd(), + iMax = oSettings.fnRecordsTotal(), + iTotal = oSettings.fnRecordsDisplay(), + sOut; + + if ( iTotal === 0 ) + { + /* Empty record set */ + sOut = oLang.sInfoEmpty; + } + else { + /* Normal record set */ + sOut = oLang.sInfo; + } + + if ( iTotal != iMax ) + { + /* Record set after filtering */ + sOut += ' ' + oLang.sInfoFiltered; + } + + // Convert the macros + sOut += oLang.sInfoPostFix; + sOut = _fnInfoMacros( oSettings, sOut ); + + if ( oLang.fnInfoCallback !== null ) + { + sOut = oLang.fnInfoCallback.call( oSettings.oInstance, + oSettings, iStart, iEnd, iMax, iTotal, sOut ); + } + + var n = oSettings.aanFeatures.i; + for ( var i=0, iLen=n.length ; i'; + var i, iLen; + var aLengthMenu = oSettings.aLengthMenu; + + if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' && + typeof aLengthMenu[1] === 'object' ) + { + for ( i=0, iLen=aLengthMenu[0].length ; i'+aLengthMenu[1][i]+''; + } + } + else + { + for ( i=0, iLen=aLengthMenu.length ; i'+aLengthMenu[i]+''; + } + } + sStdMenu += ''; + + var nLength = document.createElement( 'div' ); + if ( !oSettings.aanFeatures.l ) + { + nLength.id = oSettings.sTableId+'_length'; + } + nLength.className = oSettings.oClasses.sLength; + nLength.innerHTML = ''; + + /* + * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, + * and Stefan Skopnik for fixing the fix! + */ + $('select option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true); + + $('select', nLength).bind( 'change.DT', function(e) { + var iVal = $(this).val(); + + /* Update all other length options for the new display */ + var n = oSettings.aanFeatures.l; + for ( i=0, iLen=n.length ; i oSettings.aiDisplay.length || + oSettings._iDisplayLength == -1 ) + { + oSettings._iDisplayEnd = oSettings.aiDisplay.length; + } + else + { + oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength; + } + } + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Note that most of the paging logic is done in + * DataTable.ext.oPagination + */ + + /** + * Generate the node required for default pagination + * @param {object} oSettings dataTables settings object + * @returns {node} Pagination feature node + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlPaginate ( oSettings ) + { + if ( oSettings.oScroll.bInfinite ) + { + return null; + } + + var nPaginate = document.createElement( 'div' ); + nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType; + + DataTable.ext.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, + function( oSettings ) { + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + ); + + /* Add a draw callback for the pagination on first instance, to update the paging display */ + if ( !oSettings.aanFeatures.p ) + { + oSettings.aoDrawCallback.push( { + "fn": function( oSettings ) { + DataTable.ext.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) { + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } ); + }, + "sName": "pagination" + } ); + } + return nPaginate; + } + + + /** + * Alter the display settings to change the page + * @param {object} oSettings dataTables settings object + * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" + * or page number to jump to (integer) + * @returns {bool} true page has changed, false - no change (no effect) eg 'first' on page 1 + * @memberof DataTable#oApi + */ + function _fnPageChange ( oSettings, mAction ) + { + var iOldStart = oSettings._iDisplayStart; + + if ( typeof mAction === "number" ) + { + oSettings._iDisplayStart = mAction * oSettings._iDisplayLength; + if ( oSettings._iDisplayStart > oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart = 0; + } + } + else if ( mAction == "first" ) + { + oSettings._iDisplayStart = 0; + } + else if ( mAction == "previous" ) + { + oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ? + oSettings._iDisplayStart - oSettings._iDisplayLength : + 0; + + /* Correct for under-run */ + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + } + else if ( mAction == "next" ) + { + if ( oSettings._iDisplayLength >= 0 ) + { + /* Make sure we are not over running the display array */ + if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart += oSettings._iDisplayLength; + } + } + else + { + oSettings._iDisplayStart = 0; + } + } + else if ( mAction == "last" ) + { + if ( oSettings._iDisplayLength >= 0 ) + { + var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1; + oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength; + } + else + { + oSettings._iDisplayStart = 0; + } + } + else + { + _fnLog( oSettings, 0, "Unknown paging action: "+mAction ); + } + $(oSettings.oInstance).trigger('page', oSettings); + + return iOldStart != oSettings._iDisplayStart; + } + + + + /** + * Generate the node required for the processing node + * @param {object} oSettings dataTables settings object + * @returns {node} Processing element + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlProcessing ( oSettings ) + { + var nProcessing = document.createElement( 'div' ); + + if ( !oSettings.aanFeatures.r ) + { + nProcessing.id = oSettings.sTableId+'_processing'; + } + nProcessing.innerHTML = oSettings.oLanguage.sProcessing; + nProcessing.className = oSettings.oClasses.sProcessing; + oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable ); + + return nProcessing; + } + + + /** + * Display or hide the processing indicator + * @param {object} oSettings dataTables settings object + * @param {bool} bShow Show the processing indicator (true) or not (false) + * @memberof DataTable#oApi + */ + function _fnProcessingDisplay ( oSettings, bShow ) + { + if ( oSettings.oFeatures.bProcessing ) + { + var an = oSettings.aanFeatures.r; + for ( var i=0, iLen=an.length ; i 0 ) + { + nCaption = nCaption[0]; + if ( nCaption._captionSide === "top" ) + { + nScrollHeadTable.appendChild( nCaption ); + } + else if ( nCaption._captionSide === "bottom" && nTfoot ) + { + nScrollFootTable.appendChild( nCaption ); + } + } + + /* + * Sizing + */ + /* When x-scrolling add the width and a scroller to move the header with the body */ + if ( oSettings.oScroll.sX !== "" ) + { + nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX ); + nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX ); + + if ( nTfoot !== null ) + { + nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX ); + } + + /* When the body is scrolled, then we also want to scroll the headers */ + $(nScrollBody).scroll( function (e) { + nScrollHead.scrollLeft = this.scrollLeft; + + if ( nTfoot !== null ) + { + nScrollFoot.scrollLeft = this.scrollLeft; + } + } ); + } + + /* When yscrolling, add the height */ + if ( oSettings.oScroll.sY !== "" ) + { + nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY ); + } + + /* Redraw - align columns across the tables */ + oSettings.aoDrawCallback.push( { + "fn": _fnScrollDraw, + "sName": "scrolling" + } ); + + /* Infinite scrolling event handlers */ + if ( oSettings.oScroll.bInfinite ) + { + $(nScrollBody).scroll( function() { + /* Use a blocker to stop scrolling from loading more data while other data is still loading */ + if ( !oSettings.bDrawing && $(this).scrollTop() !== 0 ) + { + /* Check if we should load the next data set */ + if ( $(this).scrollTop() + $(this).height() > + $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap ) + { + /* Only do the redraw if we have to - we might be at the end of the data */ + if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() ) + { + _fnPageChange( oSettings, 'next' ); + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + } + } + } ); + } + + oSettings.nScrollHead = nScrollHead; + oSettings.nScrollFoot = nScrollFoot; + + return nScroller; + } + + + /** + * Update the various tables for resizing. It's a bit of a pig this function, but + * basically the idea to: + * 1. Re-create the table inside the scrolling div + * 2. Take live measurements from the DOM + * 3. Apply the measurements + * 4. Clean up + * @param {object} o dataTables settings object + * @returns {node} Node to add to the DOM + * @memberof DataTable#oApi + */ + function _fnScrollDraw ( o ) + { + var + nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0], + nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0], + nScrollBody = o.nTable.parentNode, + i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis, + nTheadSize, nTfootSize, + iWidth, aApplied=[], aAppliedFooter=[], iSanityWidth, + nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null, + nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null, + ie67 = o.oBrowser.bScrollOversize, + zeroOut = function(nSizer) { + oStyle = nSizer.style; + oStyle.paddingTop = "0"; + oStyle.paddingBottom = "0"; + oStyle.borderTopWidth = "0"; + oStyle.borderBottomWidth = "0"; + oStyle.height = 0; + }; + + /* + * 1. Re-create the table inside the scrolling div + */ + + /* Remove the old minimised thead and tfoot elements in the inner table */ + $(o.nTable).children('thead, tfoot').remove(); + + /* Clone the current header and footer elements and then place it into the inner table */ + nTheadSize = $(o.nTHead).clone()[0]; + o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] ); + anHeadToSize = o.nTHead.getElementsByTagName('tr'); + anHeadSizers = nTheadSize.getElementsByTagName('tr'); + + if ( o.nTFoot !== null ) + { + nTfootSize = $(o.nTFoot).clone()[0]; + o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] ); + anFootToSize = o.nTFoot.getElementsByTagName('tr'); + anFootSizers = nTfootSize.getElementsByTagName('tr'); + } + + /* + * 2. Take live measurements from the DOM - do not alter the DOM itself! + */ + + /* Remove old sizing and apply the calculated column widths + * Get the unique column headers in the newly created (cloned) header. We want to apply the + * calculated sizes to this header + */ + if ( o.oScroll.sX === "" ) + { + nScrollBody.style.width = '100%'; + nScrollHeadInner.parentNode.style.width = '100%'; + } + + var nThs = _fnGetUniqueThs( o, nTheadSize ); + for ( i=0, iLen=nThs.length ; i nScrollBody.offsetHeight || + $(nScrollBody).css('overflow-y') == "scroll") ) + { + o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth() - o.oScroll.iBarWidth); + } + } + else + { + if ( o.oScroll.sXInner !== "" ) + { + /* x scroll inner has been given - use it */ + o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner); + } + else if ( iSanityWidth == $(nScrollBody).width() && + $(nScrollBody).height() < $(o.nTable).height() ) + { + /* There is y-scrolling - try to take account of the y scroll bar */ + o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth ); + if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth ) + { + /* Not possible to take account of it */ + o.nTable.style.width = _fnStringToCss( iSanityWidth ); + } + } + else + { + /* All else fails */ + o.nTable.style.width = _fnStringToCss( iSanityWidth ); + } + } + + /* Recalculate the sanity width - now that we've applied the required width, before it was + * a temporary variable. This is required because the column width calculation is done + * before this table DOM is created. + */ + iSanityWidth = $(o.nTable).outerWidth(); + + /* We want the hidden header to have zero height, so remove padding and borders. Then + * set the width based on the real headers + */ + + // Apply all styles in one pass. Invalidates layout only once because we don't read any + // DOM properties. + _fnApplyToChildren( zeroOut, anHeadSizers ); + + // Read all widths in next pass. Forces layout only once because we do not change + // any DOM properties. + _fnApplyToChildren( function(nSizer) { + aApplied.push( _fnStringToCss( $(nSizer).width() ) ); + }, anHeadSizers ); + + // Apply all widths in final pass. Invalidates layout only once because we do not + // read any DOM properties. + _fnApplyToChildren( function(nToSize, i) { + nToSize.style.width = aApplied[i]; + }, anHeadToSize ); + + $(anHeadSizers).height(0); + + /* Same again with the footer if we have one */ + if ( o.nTFoot !== null ) + { + _fnApplyToChildren( zeroOut, anFootSizers ); + + _fnApplyToChildren( function(nSizer) { + aAppliedFooter.push( _fnStringToCss( $(nSizer).width() ) ); + }, anFootSizers ); + + _fnApplyToChildren( function(nToSize, i) { + nToSize.style.width = aAppliedFooter[i]; + }, anFootToSize ); + + $(anFootSizers).height(0); + } + + /* + * 3. Apply the measurements + */ + + /* "Hide" the header and footer that we used for the sizing. We want to also fix their width + * to what they currently are + */ + _fnApplyToChildren( function(nSizer, i) { + nSizer.innerHTML = ""; + nSizer.style.width = aApplied[i]; + }, anHeadSizers ); + + if ( o.nTFoot !== null ) + { + _fnApplyToChildren( function(nSizer, i) { + nSizer.innerHTML = ""; + nSizer.style.width = aAppliedFooter[i]; + }, anFootSizers ); + } + + /* Sanity check that the table is of a sensible width. If not then we are going to get + * misalignment - try to prevent this by not allowing the table to shrink below its min width + */ + if ( $(o.nTable).outerWidth() < iSanityWidth ) + { + /* The min width depends upon if we have a vertical scrollbar visible or not */ + var iCorrection = ((nScrollBody.scrollHeight > nScrollBody.offsetHeight || + $(nScrollBody).css('overflow-y') == "scroll")) ? + iSanityWidth+o.oScroll.iBarWidth : iSanityWidth; + + /* IE6/7 are a law unto themselves... */ + if ( ie67 && (nScrollBody.scrollHeight > + nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll") ) + { + o.nTable.style.width = _fnStringToCss( iCorrection-o.oScroll.iBarWidth ); + } + + /* Apply the calculated minimum width to the table wrappers */ + nScrollBody.style.width = _fnStringToCss( iCorrection ); + o.nScrollHead.style.width = _fnStringToCss( iCorrection ); + + if ( o.nTFoot !== null ) + { + o.nScrollFoot.style.width = _fnStringToCss( iCorrection ); + } + + /* And give the user a warning that we've stopped the table getting too small */ + if ( o.oScroll.sX === "" ) + { + _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+ + " misalignment. The table has been drawn at its minimum possible width." ); + } + else if ( o.oScroll.sXInner !== "" ) + { + _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+ + " misalignment. Increase the sScrollXInner value or remove it to allow automatic"+ + " calculation" ); + } + } + else + { + nScrollBody.style.width = _fnStringToCss( '100%' ); + o.nScrollHead.style.width = _fnStringToCss( '100%' ); + + if ( o.nTFoot !== null ) + { + o.nScrollFoot.style.width = _fnStringToCss( '100%' ); + } + } + + + /* + * 4. Clean up + */ + if ( o.oScroll.sY === "" ) + { + /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting + * the scrollbar height from the visible display, rather than adding it on. We need to + * set the height in order to sort this. Don't want to do it in any other browsers. + */ + if ( ie67 ) + { + nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth ); + } + } + + if ( o.oScroll.sY !== "" && o.oScroll.bCollapse ) + { + nScrollBody.style.height = _fnStringToCss( o.oScroll.sY ); + + var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ? + o.oScroll.iBarWidth : 0; + if ( o.nTable.offsetHeight < nScrollBody.offsetHeight ) + { + nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+iExtra ); + } + } + + /* Finally set the width's of the header and footer tables */ + var iOuterWidth = $(o.nTable).outerWidth(); + nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth ); + nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth ); + + // Figure out if there are scrollbar present - if so then we need a the header and footer to + // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) + var bScrolling = $(o.nTable).height() > nScrollBody.clientHeight || $(nScrollBody).css('overflow-y') == "scroll"; + nScrollHeadInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px"; + + if ( o.nTFoot !== null ) + { + nScrollFootTable.style.width = _fnStringToCss( iOuterWidth ); + nScrollFootInner.style.width = _fnStringToCss( iOuterWidth ); + nScrollFootInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px"; + } + + /* Adjust the position of the header in case we loose the y-scrollbar */ + $(nScrollBody).scroll(); + + /* If sorting or filtering has occurred, jump the scrolling back to the top */ + if ( o.bSorted || o.bFiltered ) + { + nScrollBody.scrollTop = 0; + } + } + + + /** + * Apply a given function to the display child nodes of an element array (typically + * TD children of TR rows + * @param {function} fn Method to apply to the objects + * @param array {nodes} an1 List of elements to look through for display children + * @param array {nodes} an2 Another list (identical structure to the first) - optional + * @memberof DataTable#oApi + */ + function _fnApplyToChildren( fn, an1, an2 ) + { + var index=0, i=0, iLen=an1.length; + var nNode1, nNode2; + + while ( i < iLen ) + { + nNode1 = an1[i].firstChild; + nNode2 = an2 ? an2[i].firstChild : null; + while ( nNode1 ) + { + if ( nNode1.nodeType === 1 ) + { + if ( an2 ) + { + fn( nNode1, nNode2, index ); + } + else + { + fn( nNode1, index ); + } + index++; + } + nNode1 = nNode1.nextSibling; + nNode2 = an2 ? nNode2.nextSibling : null; + } + i++; + } + } + + /** + * Convert a CSS unit width to pixels (e.g. 2em) + * @param {string} sWidth width to be converted + * @param {node} nParent parent to get the with for (required for relative widths) - optional + * @returns {int} iWidth width in pixels + * @memberof DataTable#oApi + */ + function _fnConvertToWidth ( sWidth, nParent ) + { + if ( !sWidth || sWidth === null || sWidth === '' ) + { + return 0; + } + + if ( !nParent ) + { + nParent = document.body; + } + + var iWidth; + var nTmp = document.createElement( "div" ); + nTmp.style.width = _fnStringToCss( sWidth ); + + nParent.appendChild( nTmp ); + iWidth = nTmp.offsetWidth; + nParent.removeChild( nTmp ); + + return ( iWidth ); + } + + + /** + * Calculate the width of columns for the table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnCalculateColumnWidths ( oSettings ) + { + var iTableWidth = oSettings.nTable.offsetWidth; + var iUserInputs = 0; + var iTmpWidth; + var iVisibleColumns = 0; + var iColums = oSettings.aoColumns.length; + var i, iIndex, iCorrector, iWidth; + var oHeaders = $('th', oSettings.nTHead); + var widthAttr = oSettings.nTable.getAttribute('width'); + var nWrapper = oSettings.nTable.parentNode; + + /* Convert any user input sizes into pixel sizes */ + for ( i=0 ; itd', nCalcTmp); + } + + /* Apply custom sizing to the cloned header */ + var nThs = _fnGetUniqueThs( oSettings, nTheadClone ); + iCorrector = 0; + for ( i=0 ; i 0 ) + { + oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth ); + } + iCorrector++; + } + } + + var cssWidth = $(nCalcTmp).css('width'); + oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ? + cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() ); + nCalcTmp.parentNode.removeChild( nCalcTmp ); + } + + if ( widthAttr ) + { + oSettings.nTable.style.width = _fnStringToCss( widthAttr ); + } + } + + + /** + * Adjust a table's width to take account of scrolling + * @param {object} oSettings dataTables settings object + * @param {node} n table node + * @memberof DataTable#oApi + */ + function _fnScrollingWidthAdjust ( oSettings, n ) + { + if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" ) + { + /* When y-scrolling only, we want to remove the width of the scroll bar so the table + * + scroll bar will fit into the area avaialble. + */ + var iOrigWidth = $(n).width(); + n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth ); + } + else if ( oSettings.oScroll.sX !== "" ) + { + /* When x-scrolling both ways, fix the table at it's current size, without adjusting */ + n.style.width = _fnStringToCss( $(n).outerWidth() ); + } + } + + + /** + * Get the widest node + * @param {object} oSettings dataTables settings object + * @param {int} iCol column of interest + * @returns {node} widest table node + * @memberof DataTable#oApi + */ + function _fnGetWidestNode( oSettings, iCol ) + { + var iMaxIndex = _fnGetMaxLenString( oSettings, iCol ); + if ( iMaxIndex < 0 ) + { + return null; + } + + if ( oSettings.aoData[iMaxIndex].nTr === null ) + { + var n = document.createElement('td'); + n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' ); + return n; + } + return _fnGetTdNodes(oSettings, iMaxIndex)[iCol]; + } + + + /** + * Get the maximum strlen for each data column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column of interest + * @returns {string} max string length for each column + * @memberof DataTable#oApi + */ + function _fnGetMaxLenString( oSettings, iCol ) + { + var iMax = -1; + var iMaxIndex = -1; + + for ( var i=0 ; i/g, "" ); + if ( s.length > iMax ) + { + iMax = s.length; + iMaxIndex = i; + } + } + + return iMaxIndex; + } + + + /** + * Append a CSS unit (only if required) to a string + * @param {array} aArray1 first array + * @param {array} aArray2 second array + * @returns {int} 0 if match, 1 if length is different, 2 if no match + * @memberof DataTable#oApi + */ + function _fnStringToCss( s ) + { + if ( s === null ) + { + return "0px"; + } + + if ( typeof s == 'number' ) + { + if ( s < 0 ) + { + return "0px"; + } + return s+"px"; + } + + /* Check if the last character is not 0-9 */ + var c = s.charCodeAt( s.length-1 ); + if (c < 0x30 || c > 0x39) + { + return s; + } + return s+"px"; + } + + + /** + * Get the width of a scroll bar in this browser being used + * @returns {int} width in pixels + * @memberof DataTable#oApi + */ + function _fnScrollBarWidth () + { + var inner = document.createElement('p'); + var style = inner.style; + style.width = "100%"; + style.height = "200px"; + style.padding = "0px"; + + var outer = document.createElement('div'); + style = outer.style; + style.position = "absolute"; + style.top = "0px"; + style.left = "0px"; + style.visibility = "hidden"; + style.width = "200px"; + style.height = "150px"; + style.padding = "0px"; + style.overflow = "hidden"; + outer.appendChild(inner); + + document.body.appendChild(outer); + var w1 = inner.offsetWidth; + outer.style.overflow = 'scroll'; + var w2 = inner.offsetWidth; + if ( w1 == w2 ) + { + w2 = outer.clientWidth; + } + + document.body.removeChild(outer); + return (w1 - w2); + } + + /** + * Change the order of the table + * @param {object} oSettings dataTables settings object + * @param {bool} bApplyClasses optional - should we apply classes or not + * @memberof DataTable#oApi + */ + function _fnSort ( oSettings, bApplyClasses ) + { + var + i, iLen, j, jLen, k, kLen, + sDataType, nTh, + aaSort = [], + aiOrig = [], + oSort = DataTable.ext.oSort, + aoData = oSettings.aoData, + aoColumns = oSettings.aoColumns, + oAria = oSettings.oLanguage.oAria; + + /* No sorting required if server-side or no sorting array */ + if ( !oSettings.oFeatures.bServerSide && + (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) ) + { + aaSort = ( oSettings.aaSortingFixed !== null ) ? + oSettings.aaSortingFixed.concat( oSettings.aaSorting ) : + oSettings.aaSorting.slice(); + + /* If there is a sorting data type, and a function belonging to it, then we need to + * get the data from the developer's function and apply it for this column + */ + for ( i=0 ; i/g, "" ); + nTh = aoColumns[i].nTh; + nTh.removeAttribute('aria-sort'); + nTh.removeAttribute('aria-label'); + + /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ + if ( aoColumns[i].bSortable ) + { + if ( aaSort.length > 0 && aaSort[0][0] == i ) + { + nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" ); + + var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? + aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0]; + nTh.setAttribute('aria-label', sTitle+ + (nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) ); + } + else + { + nTh.setAttribute('aria-label', sTitle+ + (aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) ); + } + } + else + { + nTh.setAttribute('aria-label', sTitle); + } + } + + /* Tell the draw function that we have sorted the data */ + oSettings.bSorted = true; + $(oSettings.oInstance).trigger('sort', oSettings); + + /* Copy the master data into the draw array and re-draw */ + if ( oSettings.oFeatures.bFilter ) + { + /* _fnFilter() will redraw the table for us */ + _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); + } + else + { + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + oSettings._iDisplayStart = 0; /* reset display back to page 0 */ + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + } + + + /** + * Attach a sort handler (click) to a node + * @param {object} oSettings dataTables settings object + * @param {node} nNode node to attach the handler to + * @param {int} iDataIndex column sorting index + * @param {function} [fnCallback] callback function + * @memberof DataTable#oApi + */ + function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback ) + { + _fnBindAction( nNode, {}, function (e) { + /* If the column is not sortable - don't to anything */ + if ( oSettings.aoColumns[iDataIndex].bSortable === false ) + { + return; + } + + /* + * This is a little bit odd I admit... I declare a temporary function inside the scope of + * _fnBuildHead and the click handler in order that the code presented here can be used + * twice - once for when bProcessing is enabled, and another time for when it is + * disabled, as we need to perform slightly different actions. + * Basically the issue here is that the Javascript engine in modern browsers don't + * appear to allow the rendering engine to update the display while it is still executing + * it's thread (well - it does but only after long intervals). This means that the + * 'processing' display doesn't appear for a table sort. To break the js thread up a bit + * I force an execution break by using setTimeout - but this breaks the expected + * thread continuation for the end-developer's point of view (their code would execute + * too early), so we only do it when we absolutely have to. + */ + var fnInnerSorting = function () { + var iColumn, iNextSort; + + /* If the shift key is pressed then we are multiple column sorting */ + if ( e.shiftKey ) + { + /* Are we already doing some kind of sort on this column? */ + var bFound = false; + for ( var i=0 ; i 0 && sCurrentClass.indexOf(sNewClass) == -1 ) + { + /* We need to add a class */ + nTds[i].className = sCurrentClass + " " + sNewClass; + } + } + } + } + + + + /** + * Save the state of a table in a cookie such that the page can be reloaded + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnSaveState ( oSettings ) + { + if ( !oSettings.oFeatures.bStateSave || oSettings.bDestroying ) + { + return; + } + + /* Store the interesting variables */ + var i, iLen, bInfinite=oSettings.oScroll.bInfinite; + var oState = { + "iCreate": new Date().getTime(), + "iStart": (bInfinite ? 0 : oSettings._iDisplayStart), + "iEnd": (bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd), + "iLength": oSettings._iDisplayLength, + "aaSorting": $.extend( true, [], oSettings.aaSorting ), + "oSearch": $.extend( true, {}, oSettings.oPreviousSearch ), + "aoSearchCols": $.extend( true, [], oSettings.aoPreSearchCols ), + "abVisCols": [] + }; + + for ( i=0, iLen=oSettings.aoColumns.length ; i 4096 ) /* Magic 10 for padding */ + { + for ( var i=0, iLen=aCookies.length ; i 4096 ) { + if ( aOldCookies.length === 0 ) { + // Deleted all DT cookies and still not enough space. Can't state save + return; + } + + var old = aOldCookies.pop(); + document.cookie = old.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+ + aParts.join('/') + "/"; + } + } + + document.cookie = sFullCookie; + } + + + /** + * Read an old cookie to get a cookie with an old table state + * @param {string} sName name of the cookie to read + * @returns {string} contents of the cookie - or null if no cookie with that name found + * @memberof DataTable#oApi + */ + function _fnReadCookie ( sName ) + { + var + aParts = window.location.pathname.split('/'), + sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=', + sCookieContents = document.cookie.split(';'); + + for( var i=0 ; i=0 ; i-- ) + { + aRet.push( aoStore[i].fn.apply( oSettings.oInstance, aArgs ) ); + } + + if ( sTrigger !== null ) + { + $(oSettings.oInstance).trigger(sTrigger, aArgs); + } + + return aRet; + } + + + /** + * JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other + * library, then we use that as it is fast, safe and accurate. If the function isn't + * available then we need to built it ourselves - the inspiration for this function comes + * from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is + * not perfect and absolutely should not be used as a replacement to json2.js - but it does + * do what we need, without requiring a dependency for DataTables. + * @param {object} o JSON object to be converted + * @returns {string} JSON string + * @memberof DataTable#oApi + */ + var _fnJsonString = (window.JSON) ? JSON.stringify : function( o ) + { + /* Not an object or array */ + var sType = typeof o; + if (sType !== "object" || o === null) + { + // simple data type + if (sType === "string") + { + o = '"'+o+'"'; + } + return o+""; + } + + /* If object or array, need to recurse over it */ + var + sProp, mValue, + json = [], + bArr = $.isArray(o); + + for (sProp in o) + { + mValue = o[sProp]; + sType = typeof mValue; + + if (sType === "string") + { + mValue = '"'+mValue+'"'; + } + else if (sType === "object" && mValue !== null) + { + mValue = _fnJsonString(mValue); + } + + json.push((bArr ? "" : '"'+sProp+'":') + mValue); + } + + return (bArr ? "[" : "{") + json + (bArr ? "]" : "}"); + }; + + + /** + * From some browsers (specifically IE6/7) we need special handling to work around browser + * bugs - this function is used to detect when these workarounds are needed. + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnBrowserDetect( oSettings ) + { + /* IE6/7 will oversize a width 100% element inside a scrolling element, to include the + * width of the scrollbar, while other browsers ensure the inner element is contained + * without forcing scrolling + */ + var n = $( + '
      '+ + '
      '+ + '
      '+ + '
      '+ + '
      ')[0]; + + document.body.appendChild( n ); + oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false; + document.body.removeChild( n ); + } + + + /** + * Perform a jQuery selector action on the table's TR elements (from the tbody) and + * return the resulting jQuery object. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter + * criterion ("applied") or all TR elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {object} jQuery object, filtered by the given selector. + * @dtopt API + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Highlight every second row + * oTable.$('tr:odd').css('backgroundColor', 'blue'); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to rows with 'Webkit' in them, add a background colour and then + * // remove the filter, thus highlighting the 'Webkit' rows only. + * oTable.fnFilter('Webkit'); + * oTable.$('tr', {"filter": "applied"}).css('backgroundColor', 'blue'); + * oTable.fnFilter(''); + * } ); + */ + this.$ = function ( sSelector, oOpts ) + { + var i, iLen, a = [], tr; + var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); + var aoData = oSettings.aoData; + var aiDisplay = oSettings.aiDisplay; + var aiDisplayMaster = oSettings.aiDisplayMaster; + + if ( !oOpts ) + { + oOpts = {}; + } + + oOpts = $.extend( {}, { + "filter": "none", // applied + "order": "current", // "original" + "page": "all" // current + }, oOpts ); + + // Current page implies that order=current and fitler=applied, since it is fairly + // senseless otherwise + if ( oOpts.page == 'current' ) + { + for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i + *
    6. 1D array of data - add a single row with the data provided
    7. + *
    8. 2D array of arrays - add multiple rows in a single call
    9. + *
    10. object - data object when using mData
    11. + *
    12. array of objects - multiple data objects when using mData
    13. + * + * @param {bool} [bRedraw=true] redraw the table or not + * @returns {array} An array of integers, representing the list of indexes in + * aoData ({@link DataTable.models.oSettings}) that have been added to + * the table. + * @dtopt API + * + * @example + * // Global var for counter + * var giCount = 2; + * + * $(document).ready(function() { + * $('#example').dataTable(); + * } ); + * + * function fnClickAddRow() { + * $('#example').dataTable().fnAddData( [ + * giCount+".1", + * giCount+".2", + * giCount+".3", + * giCount+".4" ] + * ); + * + * giCount++; + * } + */ + this.fnAddData = function( mData, bRedraw ) + { + if ( mData.length === 0 ) + { + return []; + } + + var aiReturn = []; + var iTest; + + /* Find settings from table node */ + var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); + + /* Check if we want to add multiple rows or not */ + if ( typeof mData[0] === "object" && mData[0] !== null ) + { + for ( var i=0 ; i= oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart -= oSettings._iDisplayLength; + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + } + + if ( bRedraw === undefined || bRedraw ) + { + _fnCalculateEnd( oSettings ); + _fnDraw( oSettings ); + } + + return oData; + }; + + + /** + * Restore the table to it's original state in the DOM by removing all of DataTables + * enhancements, alterations to the DOM structure of the table and event listeners. + * @param {boolean} [bRemove=false] Completely remove the table from the DOM + * @dtopt API + * + * @example + * $(document).ready(function() { + * // This example is fairly pointless in reality, but shows how fnDestroy can be used + * var oTable = $('#example').dataTable(); + * oTable.fnDestroy(); + * } ); + */ + this.fnDestroy = function ( bRemove ) + { + var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); + var nOrig = oSettings.nTableWrapper.parentNode; + var nBody = oSettings.nTBody; + var i, iLen; + + bRemove = (bRemove===undefined) ? false : bRemove; + + /* Flag to note that the table is currently being destroyed - no action should be taken */ + oSettings.bDestroying = true; + + /* Fire off the destroy callbacks for plug-ins etc */ + _fnCallbackFire( oSettings, "aoDestroyCallback", "destroy", [oSettings] ); + + /* If the table is not being removed, restore the hidden columns */ + if ( !bRemove ) + { + for ( i=0, iLen=oSettings.aoColumns.length ; itr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove(); + + /* When scrolling we had to break the table up - restore it */ + if ( oSettings.nTable != oSettings.nTHead.parentNode ) + { + $(oSettings.nTable).children('thead').remove(); + oSettings.nTable.appendChild( oSettings.nTHead ); + } + + if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode ) + { + $(oSettings.nTable).children('tfoot').remove(); + oSettings.nTable.appendChild( oSettings.nTFoot ); + } + + /* Remove the DataTables generated nodes, events and classes */ + oSettings.nTable.parentNode.removeChild( oSettings.nTable ); + $(oSettings.nTableWrapper).remove(); + + oSettings.aaSorting = []; + oSettings.aaSortingFixed = []; + _fnSortingClasses( oSettings ); + + $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') ); + + $('th, td', oSettings.nTHead).removeClass( [ + oSettings.oClasses.sSortable, + oSettings.oClasses.sSortableAsc, + oSettings.oClasses.sSortableDesc, + oSettings.oClasses.sSortableNone ].join(' ') + ); + if ( oSettings.bJUI ) + { + $('th span.'+oSettings.oClasses.sSortIcon + + ', td span.'+oSettings.oClasses.sSortIcon, oSettings.nTHead).remove(); + + $('th, td', oSettings.nTHead).each( function () { + var jqWrapper = $('div.'+oSettings.oClasses.sSortJUIWrapper, this); + var kids = jqWrapper.contents(); + $(this).append( kids ); + jqWrapper.remove(); + } ); + } + + /* Add the TR elements back into the table in their original order */ + if ( !bRemove && oSettings.nTableReinsertBefore ) + { + nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore ); + } + else if ( !bRemove ) + { + nOrig.appendChild( oSettings.nTable ); + } + + for ( i=0, iLen=oSettings.aoData.length ; i
      a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
      t
      ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
      ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
      ",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

      ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
      ","
      "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
      ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.mobile-1.3.1.js b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.mobile-1.3.1.js new file mode 100755 index 00000000..fec04bc4 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/jQuery/jquery.mobile-1.3.1.js @@ -0,0 +1,11191 @@ +/* +* jQuery Mobile 1.3.1 +* Git HEAD hash: 74b4bec049fd93e4fe40205e6157de16eb64eb46 <> Date: Wed Apr 10 2013 21:57:23 UTC +* http://jquerymobile.com +* +* Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors +* Released under the MIT license. +* http://jquery.org/license +* +*/ + + +(function ( root, doc, factory ) { + if ( typeof define === "function" && define.amd ) { + // AMD. Register as an anonymous module. + define( [ "jquery" ], function ( $ ) { + factory( $, root, doc ); + return $.mobile; + }); + } else { + // Browser globals + factory( root.jQuery, root, doc ); + } +}( this, document, function ( jQuery, window, document, undefined ) { +(function( $ ) { + $.mobile = {}; +}( jQuery )); +(function( $, window, undefined ) { + var nsNormalizeDict = {}; + + // jQuery.mobile configurable options + $.mobile = $.extend($.mobile, { + + // Version of the jQuery Mobile Framework + version: "1.3.1", + + // Namespace used framework-wide for data-attrs. Default is no namespace + ns: "", + + // Define the url parameter used for referencing widget-generated sub-pages. + // Translates to to example.html&ui-page=subpageIdentifier + // hash segment before &ui-page= is used to make Ajax request + subPageUrlKey: "ui-page", + + // Class assigned to page currently in view, and during transitions + activePageClass: "ui-page-active", + + // Class used for "active" button state, from CSS framework + activeBtnClass: "ui-btn-active", + + // Class used for "focus" form element state, from CSS framework + focusClass: "ui-focus", + + // Automatically handle clicks and form submissions through Ajax, when same-domain + ajaxEnabled: true, + + // Automatically load and show pages based on location.hash + hashListeningEnabled: true, + + // disable to prevent jquery from bothering with links + linkBindingEnabled: true, + + // Set default page transition - 'none' for no transitions + defaultPageTransition: "fade", + + // Set maximum window width for transitions to apply - 'false' for no limit + maxTransitionWidth: false, + + // Minimum scroll distance that will be remembered when returning to a page + minScrollBack: 250, + + // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts + touchOverflowEnabled: false, + + // Set default dialog transition - 'none' for no transitions + defaultDialogTransition: "pop", + + // Error response message - appears when an Ajax page request fails + pageLoadErrorMessage: "Error Loading Page", + + // For error messages, which theme does the box uses? + pageLoadErrorMessageTheme: "e", + + // replace calls to window.history.back with phonegaps navigation helper + // where it is provided on the window object + phonegapNavigationEnabled: false, + + //automatically initialize the DOM when it's ready + autoInitializePage: true, + + pushStateEnabled: true, + + // allows users to opt in to ignoring content by marking a parent element as + // data-ignored + ignoreContentEnabled: false, + + // turn of binding to the native orientationchange due to android orientation behavior + orientationChangeEnabled: true, + + buttonMarkup: { + hoverDelay: 200 + }, + + // define the window and the document objects + window: $( window ), + document: $( document ), + + // TODO might be useful upstream in jquery itself ? + keyCode: { + ALT: 18, + BACKSPACE: 8, + CAPS_LOCK: 20, + COMMA: 188, + COMMAND: 91, + COMMAND_LEFT: 91, // COMMAND + COMMAND_RIGHT: 93, + CONTROL: 17, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + INSERT: 45, + LEFT: 37, + MENU: 93, // COMMAND_RIGHT + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SHIFT: 16, + SPACE: 32, + TAB: 9, + UP: 38, + WINDOWS: 91 // COMMAND + }, + + // Place to store various widget extensions + behaviors: {}, + + // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value + silentScroll: function( ypos ) { + if ( $.type( ypos ) !== "number" ) { + ypos = $.mobile.defaultHomeScroll; + } + + // prevent scrollstart and scrollstop events + $.event.special.scrollstart.enabled = false; + + setTimeout( function() { + window.scrollTo( 0, ypos ); + $.mobile.document.trigger( "silentscroll", { x: 0, y: ypos }); + }, 20 ); + + setTimeout( function() { + $.event.special.scrollstart.enabled = true; + }, 150 ); + }, + + // Expose our cache for testing purposes. + nsNormalizeDict: nsNormalizeDict, + + // Take a data attribute property, prepend the namespace + // and then camel case the attribute string. Add the result + // to our nsNormalizeDict so we don't have to do this again. + nsNormalize: function( prop ) { + if ( !prop ) { + return; + } + + return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) ); + }, + + // Find the closest parent with a theme class on it. Note that + // we are not using $.fn.closest() on purpose here because this + // method gets called quite a bit and we need it to be as fast + // as possible. + getInheritedTheme: function( el, defaultTheme ) { + var e = el[ 0 ], + ltr = "", + re = /ui-(bar|body|overlay)-([a-z])\b/, + c, m; + + while ( e ) { + c = e.className || ""; + if ( c && ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) { + // We found a parent with a theme class + // on it so bail from this loop. + break; + } + + e = e.parentNode; + } + + // Return the theme letter we found, if none, return the + // specified default. + + return ltr || defaultTheme || "a"; + }, + + // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers + // + // Find the closest javascript page element to gather settings data jsperf test + // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit + // possibly naive, but it shows that the parsing overhead for *just* the page selector vs + // the page and dialog selector is negligable. This could probably be speed up by + // doing a similar parent node traversal to the one found in the inherited theme code above + closestPageData: function( $target ) { + return $target + .closest( ':jqmData(role="page"), :jqmData(role="dialog")' ) + .data( "mobile-page" ); + }, + + enhanceable: function( $set ) { + return this.haveParents( $set, "enhance" ); + }, + + hijackable: function( $set ) { + return this.haveParents( $set, "ajax" ); + }, + + haveParents: function( $set, attr ) { + if ( !$.mobile.ignoreContentEnabled ) { + return $set; + } + + var count = $set.length, + $newSet = $(), + e, $element, excluded; + + for ( var i = 0; i < count; i++ ) { + $element = $set.eq( i ); + excluded = false; + e = $set[ i ]; + + while ( e ) { + var c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : ""; + + if ( c === "false" ) { + excluded = true; + break; + } + + e = e.parentNode; + } + + if ( !excluded ) { + $newSet = $newSet.add( $element ); + } + } + + return $newSet; + }, + + getScreenHeight: function() { + // Native innerHeight returns more accurate value for this across platforms, + // jQuery version is here as a normalized fallback for platforms like Symbian + return window.innerHeight || $.mobile.window.height(); + } + }, $.mobile ); + + // Mobile version of data and removeData and hasData methods + // ensures all data is set and retrieved using jQuery Mobile's data namespace + $.fn.jqmData = function( prop, value ) { + var result; + if ( typeof prop !== "undefined" ) { + if ( prop ) { + prop = $.mobile.nsNormalize( prop ); + } + + // undefined is permitted as an explicit input for the second param + // in this case it returns the value and does not set it to undefined + if( arguments.length < 2 || value === undefined ){ + result = this.data( prop ); + } else { + result = this.data( prop, value ); + } + } + return result; + }; + + $.jqmData = function( elem, prop, value ) { + var result; + if ( typeof prop !== "undefined" ) { + result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value ); + } + return result; + }; + + $.fn.jqmRemoveData = function( prop ) { + return this.removeData( $.mobile.nsNormalize( prop ) ); + }; + + $.jqmRemoveData = function( elem, prop ) { + return $.removeData( elem, $.mobile.nsNormalize( prop ) ); + }; + + $.fn.removeWithDependents = function() { + $.removeWithDependents( this ); + }; + + $.removeWithDependents = function( elem ) { + var $elem = $( elem ); + + ( $elem.jqmData( 'dependents' ) || $() ).remove(); + $elem.remove(); + }; + + $.fn.addDependents = function( newDependents ) { + $.addDependents( $( this ), newDependents ); + }; + + $.addDependents = function( elem, newDependents ) { + var dependents = $( elem ).jqmData( 'dependents' ) || $(); + + $( elem ).jqmData( 'dependents', $.merge( dependents, newDependents ) ); + }; + + // note that this helper doesn't attempt to handle the callback + // or setting of an html element's text, its only purpose is + // to return the html encoded version of the text in all cases. (thus the name) + $.fn.getEncodedText = function() { + return $( "
      " ).text( $( this ).text() ).html(); + }; + + // fluent helper function for the mobile namespaced equivalent + $.fn.jqmEnhanceable = function() { + return $.mobile.enhanceable( this ); + }; + + $.fn.jqmHijackable = function() { + return $.mobile.hijackable( this ); + }; + + // Monkey-patching Sizzle to filter the :jqmData selector + var oldFind = $.find, + jqmDataRE = /:jqmData\(([^)]*)\)/g; + + $.find = function( selector, context, ret, extra ) { + selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" ); + + return oldFind.call( this, selector, context, ret, extra ); + }; + + $.extend( $.find, oldFind ); + + $.find.matches = function( expr, set ) { + return $.find( expr, null, null, set ); + }; + + $.find.matchesSelector = function( node, expr ) { + return $.find( expr, null, null, [ node ] ).length > 0; + }; +})( jQuery, this ); + + +/*! + * jQuery UI Widget v1.10.0pre - 2012-11-13 (ff055a0c353c3c8ce6e5bfa07ad7cb03e8885bc5) + * http://jqueryui.com + * + * Copyright 2010, 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/jQuery.widget/ + */ +(function( $, undefined ) { + +var uuid = 0, + slice = Array.prototype.slice, + _cleanData = $.cleanData; +$.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + try { + $( elem ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + _cleanData( elems ); +}; + +$.widget = function( name, base, prototype ) { + var fullName, existingConstructor, constructor, basePrototype, + namespace = name.split( "." )[ 0 ]; + + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( $.isFunction( value ) ) { + prototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + } + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name + }, prototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); +}; + +$.widget.extend = function( target ) { + var input = slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.widget.extend.apply( null, [ options ].concat(args) ) : + options; + + if ( isMethodCall ) { + this.each(function() { + var methodValue, + instance = $.data( this, fullName ); + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, fullName, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
      ", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + // 1.9 BC for #7810 + // TODO remove dual storage + .removeData( this.widgetName ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( value === undefined ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( value === undefined ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + // accept selectors, DOM elements + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^(\w+)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.widget", { + // decorate the parent _createWidget to trigger `widgetinit` for users + // who wish to do post post `widgetcreate` alterations/additions + // + // TODO create a pull request for jquery ui to trigger this event + // in the original _createWidget + _createWidget: function() { + $.Widget.prototype._createWidget.apply( this, arguments ); + this._trigger( 'init' ); + }, + + _getCreateOptions: function() { + + var elem = this.element, + options = {}; + + $.each( this.options, function( option ) { + + var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) { + return "-" + c.toLowerCase(); + }) + ); + + if ( value !== undefined ) { + options[ option ] = value; + } + }); + + return options; + }, + + enhanceWithin: function( target, useKeepNative ) { + this.enhance( $( this.options.initSelector, $( target )), useKeepNative ); + }, + + enhance: function( targets, useKeepNative ) { + var page, keepNative, $widgetElements = $( targets ), self = this; + + // if ignoreContentEnabled is set to true the framework should + // only enhance the selected elements when they do NOT have a + // parent with the data-namespace-ignore attribute + $widgetElements = $.mobile.enhanceable( $widgetElements ); + + if ( useKeepNative && $widgetElements.length ) { + // TODO remove dependency on the page widget for the keepNative. + // Currently the keepNative value is defined on the page prototype so + // the method is as well + page = $.mobile.closestPageData( $widgetElements ); + keepNative = ( page && page.keepNativeSelector()) || ""; + + $widgetElements = $widgetElements.not( keepNative ); + } + + $widgetElements[ this.widgetName ](); + }, + + raise: function( msg ) { + throw "Widget [" + this.widgetName + "]: " + msg; + } +}); + +})( jQuery ); + + +(function( $, window ) { + // DEPRECATED + // NOTE global mobile object settings + $.extend( $.mobile, { + // DEPRECATED Should the text be visble in the loading message? + loadingMessageTextVisible: undefined, + + // DEPRECATED When the text is visible, what theme does the loading box use? + loadingMessageTheme: undefined, + + // DEPRECATED default message setting + loadingMessage: undefined, + + // DEPRECATED + // Turn on/off page loading message. Theme doubles as an object argument + // with the following shape: { theme: '', text: '', html: '', textVisible: '' } + // NOTE that the $.mobile.loading* settings and params past the first are deprecated + showPageLoadingMsg: function( theme, msgText, textonly ) { + $.mobile.loading( 'show', theme, msgText, textonly ); + }, + + // DEPRECATED + hidePageLoadingMsg: function() { + $.mobile.loading( 'hide' ); + }, + + loading: function() { + this.loaderWidget.loader.apply( this.loaderWidget, arguments ); + } + }); + + // TODO move loader class down into the widget settings + var loaderClass = "ui-loader", $html = $( "html" ), $window = $.mobile.window; + + $.widget( "mobile.loader", { + // NOTE if the global config settings are defined they will override these + // options + options: { + // the theme for the loading message + theme: "a", + + // whether the text in the loading message is shown + textVisible: false, + + // custom html for the inner content of the loading message + html: "", + + // the text to be displayed when the popup is shown + text: "loading" + }, + + defaultHtml: "
      " + + "" + + "

      " + + "
      ", + + // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top + fakeFixLoader: function() { + var activeBtn = $( "." + $.mobile.activeBtnClass ).first(); + + this.element + .css({ + top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 || + activeBtn.length && activeBtn.offset().top || 100 + }); + }, + + // check position of loader to see if it appears to be "fixed" to center + // if not, use abs positioning + checkLoaderPosition: function() { + var offset = this.element.offset(), + scrollTop = $window.scrollTop(), + screenHeight = $.mobile.getScreenHeight(); + + if ( offset.top < scrollTop || ( offset.top - scrollTop ) > screenHeight ) { + this.element.addClass( "ui-loader-fakefix" ); + this.fakeFixLoader(); + $window + .unbind( "scroll", this.checkLoaderPosition ) + .bind( "scroll", $.proxy( this.fakeFixLoader, this ) ); + } + }, + + resetHtml: function() { + this.element.html( $( this.defaultHtml ).html() ); + }, + + // Turn on/off page loading message. Theme doubles as an object argument + // with the following shape: { theme: '', text: '', html: '', textVisible: '' } + // NOTE that the $.mobile.loading* settings and params past the first are deprecated + // TODO sweet jesus we need to break some of this out + show: function( theme, msgText, textonly ) { + var textVisible, message, $header, loadSettings; + + this.resetHtml(); + + // use the prototype options so that people can set them globally at + // mobile init. Consistency, it's what's for dinner + if ( $.type(theme) === "object" ) { + loadSettings = $.extend( {}, this.options, theme ); + + // prefer object property from the param then the old theme setting + theme = loadSettings.theme || $.mobile.loadingMessageTheme; + } else { + loadSettings = this.options; + + // here we prefer the them value passed as a string argument, then + // we prefer the global option because we can't use undefined default + // prototype options, then the prototype option + theme = theme || $.mobile.loadingMessageTheme || loadSettings.theme; + } + + // set the message text, prefer the param, then the settings object + // then loading message + message = msgText || $.mobile.loadingMessage || loadSettings.text; + + // prepare the dom + $html.addClass( "ui-loading" ); + + if ( $.mobile.loadingMessage !== false || loadSettings.html ) { + // boolean values require a bit more work :P, supports object properties + // and old settings + if ( $.mobile.loadingMessageTextVisible !== undefined ) { + textVisible = $.mobile.loadingMessageTextVisible; + } else { + textVisible = loadSettings.textVisible; + } + + // add the proper css given the options (theme, text, etc) + // Force text visibility if the second argument was supplied, or + // if the text was explicitly set in the object args + this.element.attr("class", loaderClass + + " ui-corner-all ui-body-" + theme + + " ui-loader-" + ( textVisible || msgText || theme.text ? "verbose" : "default" ) + + ( loadSettings.textonly || textonly ? " ui-loader-textonly" : "" ) ); + + // TODO verify that jquery.fn.html is ok to use in both cases here + // this might be overly defensive in preventing unknowing xss + // if the html attribute is defined on the loading settings, use that + // otherwise use the fallbacks from above + if ( loadSettings.html ) { + this.element.html( loadSettings.html ); + } else { + this.element.find( "h1" ).text( message ); + } + + // attach the loader to the DOM + this.element.appendTo( $.mobile.pageContainer ); + + // check that the loader is visible + this.checkLoaderPosition(); + + // on scroll check the loader position + $window.bind( "scroll", $.proxy( this.checkLoaderPosition, this ) ); + } + }, + + hide: function() { + $html.removeClass( "ui-loading" ); + + if ( $.mobile.loadingMessage ) { + this.element.removeClass( "ui-loader-fakefix" ); + } + + $.mobile.window.unbind( "scroll", this.fakeFixLoader ); + $.mobile.window.unbind( "scroll", this.checkLoaderPosition ); + } + }); + + $window.bind( 'pagecontainercreate', function() { + $.mobile.loaderWidget = $.mobile.loaderWidget || $( $.mobile.loader.prototype.defaultHtml ).loader(); + }); +})(jQuery, this); + + +// Script: jQuery hashchange event +// +// *Version: 1.3, Last updated: 7/21/2010* +// +// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ +// GitHub - http://github.com/cowboy/jquery-hashchange/ +// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js +// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) +// +// About: License +// +// Copyright (c) 2010 "Cowboy" Ben Alman, +// Dual licensed under the MIT and GPL licenses. +// http://benalman.com/about/license/ +// +// About: Examples +// +// These working examples, complete with fully commented code, illustrate a few +// ways in which this plugin can be used. +// +// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ +// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ +// +// About: Support and Testing +// +// Information about what version or versions of jQuery this plugin has been +// tested with, what browsers it has been tested in, and where the unit tests +// reside (so you can test it yourself). +// +// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 +// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, +// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. +// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ +// +// About: Known issues +// +// While this jQuery hashchange event implementation is quite stable and +// robust, there are a few unfortunate browser bugs surrounding expected +// hashchange event-based behaviors, independent of any JavaScript +// window.onhashchange abstraction. See the following examples for more +// information: +// +// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ +// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ +// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ +// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ +// +// Also note that should a browser natively support the window.onhashchange +// event, but not report that it does, the fallback polling loop will be used. +// +// About: Release History +// +// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more +// "removable" for mobile-only development. Added IE6/7 document.title +// support. Attempted to make Iframe as hidden as possible by using +// techniques from http://www.paciellogroup.com/blog/?p=604. Added +// support for the "shortcut" format $(window).hashchange( fn ) and +// $(window).hashchange() like jQuery provides for built-in events. +// Renamed jQuery.hashchangeDelay to and +// lowered its default value to 50. Added +// and properties plus document-domain.html +// file to address access denied issues when setting document.domain in +// IE6/7. +// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin +// from a page on another domain would cause an error in Safari 4. Also, +// IE6/7 Iframe is now inserted after the body (this actually works), +// which prevents the page from scrolling when the event is first bound. +// Event can also now be bound before DOM ready, but it won't be usable +// before then in IE6/7. +// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug +// where browser version is incorrectly reported as 8.0, despite +// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. +// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special +// window.onhashchange functionality into a separate plugin for users +// who want just the basic event & back button support, without all the +// extra awesomeness that BBQ provides. This plugin will be included as +// part of jQuery BBQ, but also be available separately. + +(function( $, window, undefined ) { + // Reused string. + var str_hashchange = 'hashchange', + + // Method / object references. + doc = document, + fake_onhashchange, + special = $.event.special, + + // Does the browser support window.onhashchange? Note that IE8 running in + // IE7 compatibility mode reports true for 'onhashchange' in window, even + // though the event isn't supported, so also test document.documentMode. + doc_mode = doc.documentMode, + supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); + + // Get location.hash (or what you'd expect location.hash to be) sans any + // leading #. Thanks for making this necessary, Firefox! + function get_fragment( url ) { + url = url || location.href; + return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); + }; + + // Method: jQuery.fn.hashchange + // + // Bind a handler to the window.onhashchange event or trigger all bound + // window.onhashchange event handlers. This behavior is consistent with + // jQuery's built-in event handlers. + // + // Usage: + // + // > jQuery(window).hashchange( [ handler ] ); + // + // Arguments: + // + // handler - (Function) Optional handler to be bound to the hashchange + // event. This is a "shortcut" for the more verbose form: + // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, + // all bound window.onhashchange event handlers will be triggered. This + // is a shortcut for the more verbose + // jQuery(window).trigger( 'hashchange' ). These forms are described in + // the section. + // + // Returns: + // + // (jQuery) The initial jQuery collection of elements. + + // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and + // $(elem).hashchange() for triggering, like jQuery does for built-in events. + $.fn[ str_hashchange ] = function( fn ) { + return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); + }; + + // Property: jQuery.fn.hashchange.delay + // + // The numeric interval (in milliseconds) at which the + // polling loop executes. Defaults to 50. + + // Property: jQuery.fn.hashchange.domain + // + // If you're setting document.domain in your JavaScript, and you want hash + // history to work in IE6/7, not only must this property be set, but you must + // also set document.domain BEFORE jQuery is loaded into the page. This + // property is only applicable if you are supporting IE6/7 (or IE8 operating + // in "IE7 compatibility" mode). + // + // In addition, the property must be set to the + // path of the included "document-domain.html" file, which can be renamed or + // modified if necessary (note that the document.domain specified must be the + // same in both your main JavaScript as well as in this file). + // + // Usage: + // + // jQuery.fn.hashchange.domain = document.domain; + + // Property: jQuery.fn.hashchange.src + // + // If, for some reason, you need to specify an Iframe src file (for example, + // when setting document.domain as in ), you can + // do so using this property. Note that when using this property, history + // won't be recorded in IE6/7 until the Iframe src file loads. This property + // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 + // compatibility" mode). + // + // Usage: + // + // jQuery.fn.hashchange.src = 'path/to/file.html'; + + $.fn[ str_hashchange ].delay = 50; + /* + $.fn[ str_hashchange ].domain = null; + $.fn[ str_hashchange ].src = null; + */ + + // Event: hashchange event + // + // Fired when location.hash changes. In browsers that support it, the native + // HTML5 window.onhashchange event is used, otherwise a polling loop is + // initialized, running every milliseconds to + // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 + // compatibility" mode), a hidden Iframe is created to allow the back button + // and hash-based history to work. + // + // Usage as described in : + // + // > // Bind an event handler. + // > jQuery(window).hashchange( function(e) { + // > var hash = location.hash; + // > ... + // > }); + // > + // > // Manually trigger the event handler. + // > jQuery(window).hashchange(); + // + // A more verbose usage that allows for event namespacing: + // + // > // Bind an event handler. + // > jQuery(window).bind( 'hashchange', function(e) { + // > var hash = location.hash; + // > ... + // > }); + // > + // > // Manually trigger the event handler. + // > jQuery(window).trigger( 'hashchange' ); + // + // Additional Notes: + // + // * The polling loop and Iframe are not created until at least one handler + // is actually bound to the 'hashchange' event. + // * If you need the bound handler(s) to execute immediately, in cases where + // a location.hash exists on page load, via bookmark or page refresh for + // example, use jQuery(window).hashchange() or the more verbose + // jQuery(window).trigger( 'hashchange' ). + // * The event can be bound before DOM ready, but since it won't be usable + // before then in IE6/7 (due to the necessary Iframe), recommended usage is + // to bind it inside a DOM ready handler. + + // Override existing $.event.special.hashchange methods (allowing this plugin + // to be defined after jQuery BBQ in BBQ's source code). + special[ str_hashchange ] = $.extend( special[ str_hashchange ], { + + // Called only when the first 'hashchange' event is bound to window. + setup: function() { + // If window.onhashchange is supported natively, there's nothing to do.. + if ( supports_onhashchange ) { return false; } + + // Otherwise, we need to create our own. And we don't want to call this + // until the user binds to the event, just in case they never do, since it + // will create a polling loop and possibly even a hidden Iframe. + $( fake_onhashchange.start ); + }, + + // Called only when the last 'hashchange' event is unbound from window. + teardown: function() { + // If window.onhashchange is supported natively, there's nothing to do.. + if ( supports_onhashchange ) { return false; } + + // Otherwise, we need to stop ours (if possible). + $( fake_onhashchange.stop ); + } + + }); + + // fake_onhashchange does all the work of triggering the window.onhashchange + // event for browsers that don't natively support it, including creating a + // polling loop to watch for hash changes and in IE 6/7 creating a hidden + // Iframe to enable back and forward. + fake_onhashchange = (function() { + var self = {}, + timeout_id, + + // Remember the initial hash so it doesn't get triggered immediately. + last_hash = get_fragment(), + + fn_retval = function( val ) { return val; }, + history_set = fn_retval, + history_get = fn_retval; + + // Start the polling loop. + self.start = function() { + timeout_id || poll(); + }; + + // Stop the polling loop. + self.stop = function() { + timeout_id && clearTimeout( timeout_id ); + timeout_id = undefined; + }; + + // This polling loop checks every $.fn.hashchange.delay milliseconds to see + // if location.hash has changed, and triggers the 'hashchange' event on + // window when necessary. + function poll() { + var hash = get_fragment(), + history_hash = history_get( last_hash ); + + if ( hash !== last_hash ) { + history_set( last_hash = hash, history_hash ); + + $(window).trigger( str_hashchange ); + + } else if ( history_hash !== last_hash ) { + location.href = location.href.replace( /#.*/, '' ) + history_hash; + } + + timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); + }; + + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + window.attachEvent && !window.addEventListener && !supports_onhashchange && (function() { + // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 + // when running in "IE7 compatibility" mode. + + var iframe, + iframe_src; + + // When the event is bound and polling starts in IE 6/7, create a hidden + // Iframe for history handling. + self.start = function() { + if ( !iframe ) { + iframe_src = $.fn[ str_hashchange ].src; + iframe_src = iframe_src && iframe_src + get_fragment(); + + // Create hidden Iframe. Attempt to make Iframe as hidden as possible + // by using techniques from http://www.paciellogroup.com/blog/?p=604. + iframe = $('':"");inst._keyEvent=false;return html},_generateMonthYearHeader:function(inst,drawMonth,drawYear,minDate,maxDate,selectedDate,secondary,monthNames,monthNamesShort){minDate=(inst.rangeStart&&minDate&&selectedDate "}else{var inMinYear=(minDate&&minDate.getFullYear()==drawYear);var inMaxYear=(maxDate&&maxDate.getFullYear()==drawYear);monthHtml+='"}if(!showMonthAfterYear){html+=monthHtml+((secondary||changeMonth||changeYear)&&(!(changeMonth&&changeYear))?" ":"")}if(secondary||!changeYear){html+=''+drawYear+""}else{var years=this._get(inst,"yearRange").split(":");var year=0;var endYear=0;if(years.length!=2){year=drawYear-10;endYear=drawYear+10}else{if(years[0].charAt(0)=="+"||years[0].charAt(0)=="-"){year=drawYear+parseInt(years[0],10);endYear=drawYear+parseInt(years[1],10)}else{year=parseInt(years[0],10);endYear=parseInt(years[1],10)}}year=(minDate?Math.max(year,minDate.getFullYear()):year);endYear=(maxDate?Math.min(endYear,maxDate.getFullYear()):endYear);html+='"}if(showMonthAfterYear){html+=(secondary||changeMonth||changeYear?" ":"")+monthHtml}html+="
      ";return html},_adjustInstDate:function(inst,offset,period){var year=inst.drawYear+(period=="Y"?offset:0);var month=inst.drawMonth+(period=="M"?offset:0);var day=Math.min(inst.selectedDay,this._getDaysInMonth(year,month))+(period=="D"?offset:0);var date=this._daylightSavingAdjust(new Date(year,month,day));var minDate=this._getMinMaxDate(inst,"min",true);var maxDate=this._getMinMaxDate(inst,"max");date=(minDate&&datemaxDate?maxDate:date);inst.selectedDay=date.getDate();inst.drawMonth=inst.selectedMonth=date.getMonth();inst.drawYear=inst.selectedYear=date.getFullYear();if(period=="M"||period=="Y"){this._notifyChange(inst)}},_notifyChange:function(inst){var onChange=this._get(inst,"onChangeMonthYear");if(onChange){onChange.apply((inst.input?inst.input[0]:null),[inst.selectedYear,inst.selectedMonth+1,inst])}},_getNumberOfMonths:function(inst){var numMonths=this._get(inst,"numberOfMonths");return(numMonths==null?[1,1]:(typeof numMonths=="number"?[1,numMonths]:numMonths))},_getMinMaxDate:function(inst,minMax,checkRange){var date=this._determineDate(this._get(inst,minMax+"Date"),null);return(!checkRange||!inst.rangeStart?date:(!date||inst.rangeStart>date?inst.rangeStart:date))},_getDaysInMonth:function(year,month){return 32-new Date(year,month,32).getDate()},_getFirstDayOfMonth:function(year,month){return new Date(year,month,1).getDay()},_canAdjustMonth:function(inst,offset,curYear,curMonth){var numMonths=this._getNumberOfMonths(inst);var date=this._daylightSavingAdjust(new Date(curYear,curMonth+(offset<0?offset:numMonths[1]),1));if(offset<0){date.setDate(this._getDaysInMonth(date.getFullYear(),date.getMonth()))}return this._isInRange(inst,date)},_isInRange:function(inst,date){var newMinDate=(!inst.rangeStart?null:this._daylightSavingAdjust(new Date(inst.selectedYear,inst.selectedMonth,inst.selectedDay)));newMinDate=(newMinDate&&inst.rangeStart=minDate)&&(!maxDate||date<=maxDate))},_getFormatConfig:function(inst){var shortYearCutoff=this._get(inst,"shortYearCutoff");shortYearCutoff=(typeof shortYearCutoff!="string"?shortYearCutoff:new Date().getFullYear()%100+parseInt(shortYearCutoff,10));return{shortYearCutoff:shortYearCutoff,dayNamesShort:this._get(inst,"dayNamesShort"),dayNames:this._get(inst,"dayNames"),monthNamesShort:this._get(inst,"monthNamesShort"),monthNames:this._get(inst,"monthNames")}},_formatDate:function(inst,day,month,year){if(!day){inst.currentDay=inst.selectedDay;inst.currentMonth=inst.selectedMonth;inst.currentYear=inst.selectedYear}var date=(day?(typeof day=="object"?day:this._daylightSavingAdjust(new Date(year,month,day))):this._daylightSavingAdjust(new Date(inst.currentYear,inst.currentMonth,inst.currentDay)));return this.formatDate(this._get(inst,"dateFormat"),date,this._getFormatConfig(inst))}});function extendRemove(target,props){$.extend(target,props);for(var name in props){if(props[name]==null||props[name]==undefined){target[name]=props[name]}}return target}function isArray(a){return(a&&(($.browser.safari&&typeof a=="object"&&a.length)||(a.constructor&&a.constructor.toString().match(/\Array\(\)/))))}$.fn.datepicker=function(options){if(!$.datepicker.initialized){$(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv);$.datepicker.initialized=true}var otherArgs=Array.prototype.slice.call(arguments,1);if(typeof options=="string"&&(options=="isDisabled"||options=="getDate")){return $.datepicker["_"+options+"Datepicker"].apply($.datepicker,[this[0]].concat(otherArgs))}if(options=="option"&&arguments.length==2&&typeof arguments[1]=="string"){return $.datepicker["_"+options+"Datepicker"].apply($.datepicker,[this[0]].concat(otherArgs))}return this.each(function(){typeof options=="string"?$.datepicker["_"+options+"Datepicker"].apply($.datepicker,[this].concat(otherArgs)):$.datepicker._attachDatepicker(this,options)})};$.datepicker=new Datepicker();$.datepicker.initialized=false;$.datepicker.uuid=new Date().getTime();$.datepicker.version="1.7.2";window.DP_jQuery=$})(jQuery);;/* + * jQuery UI Progressbar 1.7.2 + * + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * ui.core.js + */ +(function(a){a.widget("ui.progressbar",{_init:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this._valueMin(),"aria-valuemax":this._valueMax(),"aria-valuenow":this._value()});this.valueDiv=a('
      ').appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow").removeData("progressbar").unbind(".progressbar");this.valueDiv.remove();a.widget.prototype.destroy.apply(this,arguments)},value:function(b){if(b===undefined){return this._value()}this._setData("value",b);return this},_setData:function(b,c){switch(b){case"value":this.options.value=c;this._refreshValue();this._trigger("change",null,{});break}a.widget.prototype._setData.apply(this,arguments)},_value:function(){var b=this.options.value;if(bthis._valueMax()){b=this._valueMax()}return b},_valueMin:function(){var b=0;return b},_valueMax:function(){var b=100;return b},_refreshValue:function(){var b=this.value();this.valueDiv[b==this._valueMax()?"addClass":"removeClass"]("ui-corner-right");this.valueDiv.width(b+"%");this.element.attr("aria-valuenow",b)}});a.extend(a.ui.progressbar,{version:"1.7.2",defaults:{value:0}})})(jQuery);;/* + * jQuery UI Effects 1.7.2 + * + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/ + */ +jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(var f=0;f
      ');var j=f.parent();if(f.css("position")=="static"){j.css({position:"relative"});f.css({position:"relative"})}else{var i=f.css("top");if(isNaN(parseInt(i,10))){i="auto"}var h=f.css("left");if(isNaN(parseInt(h,10))){h="auto"}j.css({position:f.css("position"),top:i,left:h,zIndex:f.css("z-index")}).show();f.css({position:"relative",top:0,left:0})}j.css(g);return j},removeWrapper:function(f){if(f.parent().is(".ui-effects-wrapper")){return f.parent().replaceWith(f)}return f},setTransition:function(g,i,f,h){h=h||{};d.each(i,function(k,j){unit=g.cssUnit(j);if(unit[0]>0){h[j]=unit[0]*f+unit[1]}});return h},animateClass:function(h,i,k,j){var f=(typeof k=="function"?k:(j?j:null));var g=(typeof k=="string"?k:null);return this.each(function(){var q={};var o=d(this);var p=o.attr("style")||"";if(typeof p=="object"){p=p.cssText}if(h.toggle){o.hasClass(h.toggle)?h.remove=h.toggle:h.add=h.toggle}var l=d.extend({},(document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle));if(h.add){o.addClass(h.add)}if(h.remove){o.removeClass(h.remove)}var m=d.extend({},(document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle));if(h.add){o.removeClass(h.add)}if(h.remove){o.addClass(h.remove)}for(var r in m){if(typeof m[r]!="function"&&m[r]&&r.indexOf("Moz")==-1&&r.indexOf("length")==-1&&m[r]!=l[r]&&(r.match(/color/i)||(!r.match(/color/i)&&!isNaN(parseInt(m[r],10))))&&(l.position!="static"||(l.position=="static"&&!r.match(/left|top|bottom|right/)))){q[r]=m[r]}}o.animate(q,i,g,function(){if(typeof d(this).attr("style")=="object"){d(this).attr("style")["cssText"]="";d(this).attr("style")["cssText"]=p}else{d(this).attr("style",p)}if(h.add){d(this).addClass(h.add)}if(h.remove){d(this).removeClass(h.remove)}if(f){f.apply(this,arguments)}})})}};function c(g,f){var i=g[1]&&g[1].constructor==Object?g[1]:{};if(f){i.mode=f}var h=g[1]&&g[1].constructor!=Object?g[1]:(i.duration?i.duration:g[2]);h=d.fx.off?0:typeof h==="number"?h:d.fx.speeds[h]||d.fx.speeds._default;var j=i.callback||(d.isFunction(g[1])&&g[1])||(d.isFunction(g[2])&&g[2])||(d.isFunction(g[3])&&g[3]);return[g[0],i,h,j]}d.fn.extend({_show:d.fn.show,_hide:d.fn.hide,__toggle:d.fn.toggle,_addClass:d.fn.addClass,_removeClass:d.fn.removeClass,_toggleClass:d.fn.toggleClass,effect:function(g,f,h,i){return d.effects[g]?d.effects[g].call(this,{method:g,options:f||{},duration:h,callback:i}):null},show:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))){return this._show.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"show"))}},hide:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))){return this._hide.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"hide"))}},toggle:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))||(d.isFunction(arguments[0])||typeof arguments[0]=="boolean")){return this.__toggle.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"toggle"))}},addClass:function(g,f,i,h){return f?d.effects.animateClass.apply(this,[{add:g},f,i,h]):this._addClass(g)},removeClass:function(g,f,i,h){return f?d.effects.animateClass.apply(this,[{remove:g},f,i,h]):this._removeClass(g)},toggleClass:function(g,f,i,h){return((typeof f!=="boolean")&&f)?d.effects.animateClass.apply(this,[{toggle:g},f,i,h]):this._toggleClass(g,f)},morph:function(f,h,g,j,i){return d.effects.animateClass.apply(this,[{add:h,remove:f},g,j,i])},switchClass:function(){return this.morph.apply(this,arguments)},cssUnit:function(f){var g=this.css(f),h=[];d.each(["em","px","%","pt"],function(j,k){if(g.indexOf(k)>0){h=[parseFloat(g),k]}});return h}});d.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(g,f){d.fx.step[f]=function(h){if(h.state==0){h.start=e(h.elem,f);h.end=b(h.end)}h.elem.style[f]="rgb("+[Math.max(Math.min(parseInt((h.pos*(h.end[0]-h.start[0]))+h.start[0],10),255),0),Math.max(Math.min(parseInt((h.pos*(h.end[1]-h.start[1]))+h.start[1],10),255),0),Math.max(Math.min(parseInt((h.pos*(h.end[2]-h.start[2]))+h.start[2],10),255),0)].join(",")+")"}});function b(g){var f;if(g&&g.constructor==Array&&g.length==3){return g}if(f=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(g)){return[parseInt(f[1],10),parseInt(f[2],10),parseInt(f[3],10)]}if(f=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(g)){return[parseFloat(f[1])*2.55,parseFloat(f[2])*2.55,parseFloat(f[3])*2.55]}if(f=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(g)){return[parseInt(f[1],16),parseInt(f[2],16),parseInt(f[3],16)]}if(f=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(g)){return[parseInt(f[1]+f[1],16),parseInt(f[2]+f[2],16),parseInt(f[3]+f[3],16)]}if(f=/rgba\(0, 0, 0, 0\)/.exec(g)){return a.transparent}return a[d.trim(g).toLowerCase()]}function e(h,f){var g;do{g=d.curCSS(h,f);if(g!=""&&g!="transparent"||d.nodeName(h,"body")){break}f="backgroundColor"}while(h=h.parentNode);return b(g)}var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]};d.easing.jswing=d.easing.swing;d.extend(d.easing,{def:"easeOutQuad",swing:function(g,h,f,j,i){return d.easing[d.easing.def](g,h,f,j,i)},easeInQuad:function(g,h,f,j,i){return j*(h/=i)*h+f},easeOutQuad:function(g,h,f,j,i){return -j*(h/=i)*(h-2)+f},easeInOutQuad:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h+f}return -j/2*((--h)*(h-2)-1)+f},easeInCubic:function(g,h,f,j,i){return j*(h/=i)*h*h+f},easeOutCubic:function(g,h,f,j,i){return j*((h=h/i-1)*h*h+1)+f},easeInOutCubic:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h+f}return j/2*((h-=2)*h*h+2)+f},easeInQuart:function(g,h,f,j,i){return j*(h/=i)*h*h*h+f},easeOutQuart:function(g,h,f,j,i){return -j*((h=h/i-1)*h*h*h-1)+f},easeInOutQuart:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h*h+f}return -j/2*((h-=2)*h*h*h-2)+f},easeInQuint:function(g,h,f,j,i){return j*(h/=i)*h*h*h*h+f},easeOutQuint:function(g,h,f,j,i){return j*((h=h/i-1)*h*h*h*h+1)+f},easeInOutQuint:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h*h*h+f}return j/2*((h-=2)*h*h*h*h+2)+f},easeInSine:function(g,h,f,j,i){return -j*Math.cos(h/i*(Math.PI/2))+j+f},easeOutSine:function(g,h,f,j,i){return j*Math.sin(h/i*(Math.PI/2))+f},easeInOutSine:function(g,h,f,j,i){return -j/2*(Math.cos(Math.PI*h/i)-1)+f},easeInExpo:function(g,h,f,j,i){return(h==0)?f:j*Math.pow(2,10*(h/i-1))+f},easeOutExpo:function(g,h,f,j,i){return(h==i)?f+j:j*(-Math.pow(2,-10*h/i)+1)+f},easeInOutExpo:function(g,h,f,j,i){if(h==0){return f}if(h==i){return f+j}if((h/=i/2)<1){return j/2*Math.pow(2,10*(h-1))+f}return j/2*(-Math.pow(2,-10*--h)+2)+f},easeInCirc:function(g,h,f,j,i){return -j*(Math.sqrt(1-(h/=i)*h)-1)+f},easeOutCirc:function(g,h,f,j,i){return j*Math.sqrt(1-(h=h/i-1)*h)+f},easeInOutCirc:function(g,h,f,j,i){if((h/=i/2)<1){return -j/2*(Math.sqrt(1-h*h)-1)+f}return j/2*(Math.sqrt(1-(h-=2)*h)+1)+f},easeInElastic:function(g,i,f,m,l){var j=1.70158;var k=0;var h=m;if(i==0){return f}if((i/=l)==1){return f+m}if(!k){k=l*0.3}if(h
      ").css({position:"absolute",visibility:"visible",left:-d*(g/e),top:-f*(c/k)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:g/e,height:c/k,left:l.left+d*(g/e)+(b.options.mode=="show"?(d-Math.floor(e/2))*(g/e):0),top:l.top+f*(c/k)+(b.options.mode=="show"?(f-Math.floor(k/2))*(c/k):0),opacity:b.options.mode=="show"?0:1}).animate({left:l.left+d*(g/e)+(b.options.mode=="show"?0:(d-Math.floor(e/2))*(g/e)),top:l.top+f*(c/k)+(b.options.mode=="show"?0:(f-Math.floor(k/2))*(c/k)),opacity:b.options.mode=="show"?1:0},b.duration||500)}}setTimeout(function(){b.options.mode=="show"?h.css({visibility:"visible"}):h.css({visibility:"visible"}).hide();if(b.callback){b.callback.apply(h[0])}h.dequeue();a("div.ui-effects-explode").remove()},b.duration||500)})}})(jQuery);;/* + * jQuery UI Effects Fold 1.7.2 + * + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * effects.core.js + */ +(function(a){a.effects.fold=function(b){return this.queue(function(){var e=a(this),k=["position","top","left"];var h=a.effects.setMode(e,b.options.mode||"hide");var o=b.options.size||15;var n=!(!b.options.horizFirst);var g=b.duration?b.duration/2:a.fx.speeds._default/2;a.effects.save(e,k);e.show();var d=a.effects.createWrapper(e).css({overflow:"hidden"});var i=((h=="show")!=n);var f=i?["width","height"]:["height","width"];var c=i?[d.width(),d.height()]:[d.height(),d.width()];var j=/([0-9]+)%/.exec(o);if(j){o=parseInt(j[1],10)/100*c[h=="hide"?0:1]}if(h=="show"){d.css(n?{height:0,width:o}:{height:o,width:0})}var m={},l={};m[f[0]]=h=="show"?c[0]:o;l[f[1]]=h=="show"?c[1]:0;d.animate(m,g,b.options.easing).animate(l,g,b.options.easing,function(){if(h=="hide"){e.hide()}a.effects.restore(e,k);a.effects.removeWrapper(e);if(b.callback){b.callback.apply(e[0],arguments)}e.dequeue()})})}})(jQuery);;/* + * jQuery UI Effects Highlight 1.7.2 + * + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * effects.core.js + */ +(function(a){a.effects.highlight=function(b){return this.queue(function(){var e=a(this),d=["backgroundImage","backgroundColor","opacity"];var h=a.effects.setMode(e,b.options.mode||"show");var c=b.options.color||"#ffff99";var g=e.css("backgroundColor");a.effects.save(e,d);e.show();e.css({backgroundImage:"none",backgroundColor:c});var f={backgroundColor:g};if(h=="hide"){f.opacity=0}e.animate(f,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){if(h=="hide"){e.hide()}a.effects.restore(e,d);if(h=="show"&&a.browser.msie){this.style.removeAttribute("filter")}if(b.callback){b.callback.apply(this,arguments)}e.dequeue()}})})}})(jQuery);;/* + * jQuery UI Effects Pulsate 1.7.2 + * + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * effects.core.js + */ +(function(a){a.effects.pulsate=function(b){return this.queue(function(){var d=a(this);var g=a.effects.setMode(d,b.options.mode||"show");var f=b.options.times||5;var e=b.duration?b.duration/2:a.fx.speeds._default/2;if(g=="hide"){f--}if(d.is(":hidden")){d.css("opacity",0);d.show();d.animate({opacity:1},e,b.options.easing);f=f-2}for(var c=0;c').appendTo(document.body).addClass(b.options.className).css({top:d.top,left:d.left,height:f.innerHeight(),width:f.innerWidth(),position:"absolute"}).animate(g,b.duration,b.options.easing,function(){c.remove();(b.callback&&b.callback.apply(f[0],arguments));f.dequeue()})})}})(jQuery);; \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/js/jquery-ui-i18n.js b/misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/js/jquery-ui-i18n.js new file mode 100755 index 00000000..61c769e6 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/jquery-ui/js/jquery-ui-i18n.js @@ -0,0 +1,771 @@ +/* Arabic Translation for jQuery UI date picker plugin. */ +/* Khaled Al Horani -- koko.dw@gmail.com */ +/* خالد الحŮراني -- koko.dw@gmail.com */ +/* NOTE: monthNames are the original months names and they are the Arabic names, not the new months name Ůبراير - يناير and there isn't any Arabic roots for these months */ +jQuery(function($){ + $.datepicker.regional['ar'] = { + closeText: 'إغلاق', + prevText: '<السابق', + nextText: 'التالي>', + currentText: 'اليŮŮ…', + monthNames: ['ŮانŮن الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران', + 'ŘŞŮ…Ůز', 'آب', 'ŘŁŮŠŮ„ŮŮ„', 'تشرين الأŮŮ„', 'تشرين الثاني', 'ŮانŮن الأŮŮ„'], + monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], + dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'], + dayNamesShort: ['سبت', 'ŘŁŘ­ŘŻ', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'], + dayNamesMin: ['سبت', 'ŘŁŘ­ŘŻ', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: true}; + $.datepicker.setDefaults($.datepicker.regional['ar']); +});/* Bulgarian initialisation for the jQuery UI date picker plugin. */ +/* Written by Stoyan Kyosev (http://svest.org). */ +jQuery(function($){ + $.datepicker.regional['bg'] = { + closeText: 'затвори', + prevText: '<назад', + nextText: 'напред>', + nextBigText: '>>', + currentText: 'днеŃ', + monthNames: ['ĐŻĐ˝Ńари','ФеврŃари','Март','Đприл','Май','Юни', + 'Юли','ĐвгŃŃŃ‚','Септември','Октомври','Ноември','Декември'], + monthNamesShort: ['ĐŻĐ˝Ń','Фев','Мар','Đпр','Май','Юни', + 'Юли','Đвг','Сеп','Окт','Нов','Дек'], + dayNames: ['Неделя','Понеделник','Вторник','Сряда','Четвъртък','Петък','Събота'], + dayNamesShort: ['Нед','Пон','Đ’Ń‚Đľ','Сря','Чет','Пет','Съб'], + dayNamesMin: ['Не','По','Đ’Ń‚','Ср','Че','Пе','Съ'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['bg']); +}); +/* Inicialitzaciďż˝ en catalďż˝ per a l'extenciďż˝ 'calendar' per jQuery. */ +/* Writers: (joan.leon@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['ca'] = { + closeText: 'Tancar', + prevText: '<Ant', + nextText: 'Seg>', + currentText: 'Avui', + monthNames: ['Gener','Febrer','Març','Abril','Maig','Juny', + 'Juliol','Agost','Setembre','Octubre','Novembre','Desembre'], + monthNamesShort: ['Gen','Feb','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Oct','Nov','Des'], + dayNames: ['Diumenge','Dilluns','Dimarts','Dimecres','Dijous','Divendres','Dissabte'], + dayNamesShort: ['Dug','Dln','Dmt','Dmc','Djs','Dvn','Dsb'], + dayNamesMin: ['Dg','Dl','Dt','Dc','Dj','Dv','Ds'], + dateFormat: 'mm/dd/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['ca']); +});/* Czech initialisation for the jQuery UI date picker plugin. */ +/* Written by Tomas Muller (tomas@tomas-muller.net). */ +jQuery(function($){ + $.datepicker.regional['cs'] = { + closeText: 'Zavřít', + prevText: '<Dříve', + nextText: 'PozdÄ›ji>', + currentText: 'NynĂ­', + monthNames: ['leden','Ăşnor','bĹ™ezen','duben','kvÄ›ten','ÄŤerven', + 'ÄŤervenec','srpen','září','říjen','listopad','prosinec'], + monthNamesShort: ['led','Ăşno','bĹ™e','dub','kvÄ›','ÄŤer', + 'ÄŤvc','srp','zář','říj','lis','pro'], + dayNames: ['nedÄ›le', 'pondÄ›lĂ­', 'ĂşterĂ˝', 'stĹ™eda', 'ÄŤtvrtek', 'pátek', 'sobota'], + dayNamesShort: ['ne', 'po', 'Ăşt', 'st', 'ÄŤt', 'pá', 'so'], + dayNamesMin: ['ne','po','Ăşt','st','ÄŤt','pá','so'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['cs']); +}); +/* Danish initialisation for the jQuery UI date picker plugin. */ +/* Written by Jan Christensen ( deletestuff@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['da'] = { + closeText: 'Luk', + prevText: '<Forrige', + nextText: 'Næste>', + currentText: 'Idag', + monthNames: ['Januar','Februar','Marts','April','Maj','Juni', + 'Juli','August','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'], + dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'], + dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'], + dateFormat: 'dd-mm-yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['da']); +}); +/* German initialisation for the jQuery UI date picker plugin. */ +/* Written by Milian Wolff (mail@milianw.de). */ +jQuery(function($){ + $.datepicker.regional['de'] = { + closeText: 'schlieĂźen', + prevText: '<zurĂĽck', + nextText: 'Vor>', + currentText: 'heute', + monthNames: ['Januar','Februar','März','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Dezember'], + monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dez'], + dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], + dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['de']); +}); +/* Greek (el) initialisation for the jQuery UI date picker plugin. */ +/* Written by Alex Cicovic (http://www.alexcicovic.com) */ +jQuery(function($){ + $.datepicker.regional['el'] = { + closeText: 'ΚλείĎιμο', + prevText: 'ΠĎοηγούμενος', + nextText: 'Επόμενος', + currentText: 'ΤĎέχων Μήνας', + monthNames: ['ΙανουάĎιος','ΦεβĎουάĎιος','ΜάĎτιος','ΑπĎίλιος','Μάιος','Ιούνιος', + 'Ιούλιος','ΑύγουĎτος','ΣεπτέμβĎιος','ΟκτώβĎιος','ΝοέμβĎιος','ΔεκέμβĎιος'], + monthNamesShort: ['Ιαν','Φεβ','ΜαĎ','ΑπĎ','Μαι','Ιουν', + 'Ιουλ','Αυγ','Σεπ','Οκτ','Νοε','Δεκ'], + dayNames: ['ΚυĎιακή','ΔευτέĎα','ΤĎίτη','ΤετάĎτη','Πέμπτη','ΠαĎαĎκευή','Σάββατο'], + dayNamesShort: ['ΚυĎ','Δευ','ΤĎÎą','Τετ','Πεμ','ΠαĎ','Σαβ'], + dayNamesMin: ['Κυ','Δε','ΤĎ','Τε','Πε','Πα','Σα'], + dateFormat: 'dd/mm/yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['el']); +});/* Esperanto initialisation for the jQuery UI date picker plugin. */ +/* Written by Olivier M. (olivierweb@ifrance.com). */ +jQuery(function($){ + $.datepicker.regional['eo'] = { + closeText: 'Fermi', + prevText: '<Anta', + nextText: 'Sekv>', + currentText: 'Nuna', + monthNames: ['Januaro','Februaro','Marto','Aprilo','Majo','Junio', + 'Julio','AĹ­gusto','Septembro','Oktobro','Novembro','Decembro'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','AĹ­g','Sep','Okt','Nov','Dec'], + dayNames: ['Dimanĉo','Lundo','Mardo','Merkredo','Ä´aĹ­do','Vendredo','Sabato'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Ä´aĹ­','Ven','Sab'], + dayNamesMin: ['Di','Lu','Ma','Me','Ä´a','Ve','Sa'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['eo']); +}); +/* Inicializaciďż˝n en espaďż˝ol para la extensiďż˝n 'UI date picker' para jQuery. */ +/* Traducido por Vester (xvester@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['es'] = { + closeText: 'Cerrar', + prevText: '<Ant', + nextText: 'Sig>', + currentText: 'Hoy', + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['es']); +});/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */ +/* Javad Mowlanezhad -- jmowla@gmail.com */ +/* Jalali calendar should supported soon! (Its implemented but I have to test it) */ +jQuery(function($) { + $.datepicker.regional['fa'] = { + closeText: 'بستن', + prevText: '<قبلي', + nextText: 'بعدي>', + currentText: 'امرŮز', + monthNames: ['ŮرŮردين','ارديبهشت','خرداد','تير','مرداد','شهريŮر', + 'مهر','آبان','آذر','ŘŻŮŠ','بهمن','اسŮند'], + monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], + dayNames: ['يکشنبه','ŘŻŮشنبه','سه‌شنبه','چهارشنبه','پنجشنبه','جمعه','شنبه'], + dayNamesShort: ['ŮŠ','ŘŻ','Řł','Ú†','Ůľ','ج', 'Ř´'], + dayNamesMin: ['ŮŠ','ŘŻ','Řł','Ú†','Ůľ','ج', 'Ř´'], + dateFormat: 'yy/mm/dd', firstDay: 6, + isRTL: true}; + $.datepicker.setDefaults($.datepicker.regional['fa']); +});/* Finnish initialisation for the jQuery UI date picker plugin. */ +/* Written by Harri Kilpiďż˝ (harrikilpio@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['fi'] = { + closeText: 'Sulje', + prevText: '«Edellinen', + nextText: 'Seuraava»', + currentText: 'Tänään', + monthNames: ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu', + 'Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'], + monthNamesShort: ['Tammi','Helmi','Maalis','Huhti','Touko','Kesä', + 'Heinä','Elo','Syys','Loka','Marras','Joulu'], + dayNamesShort: ['Su','Ma','Ti','Ke','To','Pe','Su'], + dayNames: ['Sunnuntai','Maanantai','Tiistai','Keskiviikko','Torstai','Perjantai','Lauantai'], + dayNamesMin: ['Su','Ma','Ti','Ke','To','Pe','La'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['fi']); +}); +/* French initialisation for the jQuery UI date picker plugin. */ +/* Written by Keith Wood (kbwood@virginbroadband.com.au) and StĂ©phane Nahmani (sholby@sholby.net). */ +jQuery(function($){ + $.datepicker.regional['fr'] = { + closeText: 'Fermer', + prevText: '<PrĂ©c', + nextText: 'Suiv>', + currentText: 'Courant', + monthNames: ['Janvier','FĂ©vrier','Mars','Avril','Mai','Juin', + 'Juillet','AoĂ»t','Septembre','Octobre','Novembre','DĂ©cembre'], + monthNamesShort: ['Jan','FĂ©v','Mar','Avr','Mai','Jun', + 'Jul','AoĂ»','Sep','Oct','Nov','DĂ©c'], + dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], + dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'], + dateFormat: 'dd/mm/yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['fr']); +});/* Hebrew initialisation for the UI Datepicker extension. */ +/* Written by Amir Hardon (ahardon at gmail dot com). */ +jQuery(function($){ + $.datepicker.regional['he'] = { + closeText: 'סגור', + prevText: '<הקודם', + nextText: 'הב×>', + currentText: 'היום', + monthNames: ['ינו×ר','פברו×ר','מרץ','×פריל','מ××™','יוני', + 'יולי','×וגוס×','ספ×מבר','×וק×ובר','נובמבר','דצמבר'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['ר×שון','שני','שלישי','רביעי','חמישי','שישי','שבת'], + dayNamesShort: ['×\'','ב\'','×’\'','ד\'','×”\'','ו\'','שבת'], + dayNamesMin: ['×\'','ב\'','×’\'','ד\'','×”\'','ו\'','שבת'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: true}; + $.datepicker.setDefaults($.datepicker.regional['he']); +}); +/* Croatian i18n for the jQuery UI date picker plugin. */ +/* Written by Vjekoslav Nesek. */ +jQuery(function($){ + $.datepicker.regional['hr'] = { + closeText: 'Zatvori', + prevText: '<', + nextText: '>', + currentText: 'Danas', + monthNames: ['SijeÄŤanj','VeljaÄŤa','OĹľujak','Travanj','Svibanj','Lipani', + 'Srpanj','Kolovoz','Rujan','Listopad','Studeni','Prosinac'], + monthNamesShort: ['Sij','Velj','OĹľu','Tra','Svi','Lip', + 'Srp','Kol','Ruj','Lis','Stu','Pro'], + dayNames: ['Nedjalja','Ponedjeljak','Utorak','Srijeda','ÄŚetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sri','ÄŚet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŚe','Pe','Su'], + dateFormat: 'dd.mm.yy.', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['hr']); +});/* Hungarian initialisation for the jQuery UI date picker plugin. */ +/* Written by Istvan Karaszi (jquerycalendar@spam.raszi.hu). */ +jQuery(function($){ + $.datepicker.regional['hu'] = { + closeText: 'bezárás', + prevText: '« vissza', + nextText: 'elĹ‘re »', + currentText: 'ma', + monthNames: ['Január', 'Február', 'Március', 'Ăprilis', 'Május', 'JĂşnius', + 'JĂşlius', 'Augusztus', 'Szeptember', 'OktĂłber', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Már', 'Ăpr', 'Máj', 'JĂşn', + 'JĂşl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'], + dayNames: ['Vasámap', 'HĂ©tfö', 'Kedd', 'Szerda', 'CsĂĽtörtök', 'PĂ©ntek', 'Szombat'], + dayNamesShort: ['Vas', 'HĂ©t', 'Ked', 'Sze', 'CsĂĽ', 'PĂ©n', 'Szo'], + dayNamesMin: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'], + dateFormat: 'yy-mm-dd', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['hu']); +}); +/* Armenian(UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/ +jQuery(function($){ + $.datepicker.regional['hy'] = { + closeText: 'Ő“ŐˇŐŻŐĄŐ¬', + prevText: '<Ő†ŐˇŐ­.', + nextText: 'Ő€ŐˇŐ».>', + currentText: 'Ô±ŐµŐ˝Ö…Ö€', + monthNames: ['Ő€Ő¸Ö‚Ő¶ŐľŐˇÖ€','Ő“ŐĄŐżÖ€ŐľŐˇÖ€','Ő„ŐˇÖ€Őż','Ô±ŐşÖ€Ő«Ő¬','Ő„ŐˇŐµŐ«Ő˝','Ő€Ő¸Ö‚Ő¶Ő«Ő˝', + 'Ő€Ő¸Ö‚Ő¬Ő«Ő˝','Ő•ŐŁŐ¸Ő˝ŐżŐ¸Ő˝','ŐŤŐĄŐşŐżŐĄŐ´Ő˘ŐĄÖ€','Ő€Ő¸ŐŻŐżŐĄŐ´Ő˘ŐĄÖ€','Ő†Ő¸ŐµŐĄŐ´Ő˘ŐĄÖ€','Ô´ŐĄŐŻŐżŐĄŐ´Ő˘ŐĄÖ€'], + monthNamesShort: ['Ő€Ő¸Ö‚Ő¶Őľ','Ő“ŐĄŐżÖ€','Ő„ŐˇÖ€Őż','Ô±ŐşÖ€','Ő„ŐˇŐµŐ«Ő˝','Ő€Ő¸Ö‚Ő¶Ő«Ő˝', + 'Ő€Ő¸Ö‚Ő¬','Ő•ŐŁŐ˝','ŐŤŐĄŐş','Ő€Ő¸ŐŻ','Ő†Ő¸Őµ','Ô´ŐĄŐŻ'], + dayNames: ['ŐŻŐ«Ö€ŐˇŐŻŐ«','ŐĄŐŻŐ¸Ö‚Ő·ŐˇŐ˘Ő©Ő«','ŐĄÖ€ŐĄÖ„Ő·ŐˇŐ˘Ő©Ő«','ŐąŐ¸Ö€ŐĄÖ„Ő·ŐˇŐ˘Ő©Ő«','Ő°Ő«Ő¶ŐŁŐ·ŐˇŐ˘Ő©Ő«','Ő¸Ö‚Ö€Ő˘ŐˇŐ©','Ő·ŐˇŐ˘ŐˇŐ©'], + dayNamesShort: ['ŐŻŐ«Ö€','ŐĄÖ€ŐŻ','ŐĄÖ€Ö„','ŐąÖ€Ö„','Ő°Ő¶ŐŁ','Ő¸Ö‚Ö€Ő˘','Ő·Ő˘Ő©'], + dayNamesMin: ['ŐŻŐ«Ö€','ŐĄÖ€ŐŻ','ŐĄÖ€Ö„','ŐąÖ€Ö„','Ő°Ő¶ŐŁ','Ő¸Ö‚Ö€Ő˘','Ő·Ő˘Ő©'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['hy']); +});/* Indonesian initialisation for the jQuery UI date picker plugin. */ +/* Written by Deden Fathurahman (dedenf@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['id'] = { + closeText: 'Tutup', + prevText: '<mundur', + nextText: 'maju>', + currentText: 'hari ini', + monthNames: ['Januari','Februari','Maret','April','Mei','Juni', + 'Juli','Agustus','September','Oktober','Nopember','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun', + 'Jul','Agus','Sep','Okt','Nop','Des'], + dayNames: ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'], + dayNamesShort: ['Min','Sen','Sel','Rab','kam','Jum','Sab'], + dayNamesMin: ['Mg','Sn','Sl','Rb','Km','jm','Sb'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['id']); +});/* Icelandic initialisation for the jQuery UI date picker plugin. */ +/* Written by Haukur H. Thorsson (haukur@eskill.is). */ +jQuery(function($){ + $.datepicker.regional['is'] = { + closeText: 'Loka', + prevText: '< Fyrri', + nextText: 'Næsti >', + currentText: 'Í dag', + monthNames: ['Janúar','Febrúar','Mars','Apríl','Maí','Júní', + 'Júlí','Ágúst','September','Október','Nóvember','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maí','Jún', + 'Júl','Ágú','Sep','Okt','Nóv','Des'], + dayNames: ['Sunnudagur','Mánudagur','Þriðjudagur','Miðvikudagur','Fimmtudagur','Föstudagur','Laugardagur'], + dayNamesShort: ['Sun','Mán','Þri','Mið','Fim','Fös','Lau'], + dayNamesMin: ['Su','Má','Þr','Mi','Fi','Fö','La'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['is']); +});/* Italian initialisation for the jQuery UI date picker plugin. */ +/* Written by Apaella (apaella@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['it'] = { + closeText: 'Chiudi', + prevText: '<Prec', + nextText: 'Succ>', + currentText: 'Oggi', + monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno', + 'Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], + monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu', + 'Lug','Ago','Set','Ott','Nov','Dic'], + dayNames: ['Domenica','Lunedì','Martedì','Mercoledì','Giovedì','Venerdì','Sabato'], + dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'], + dayNamesMin: ['Do','Lu','Ma','Me','Gio','Ve','Sa'], + dateFormat: 'dd/mm/yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['it']); +}); +/* Japanese initialisation for the jQuery UI date picker plugin. */ +/* Written by Kentaro SATO (kentaro@ranvis.com). */ +jQuery(function($){ + $.datepicker.regional['ja'] = { + closeText: 'é–‰ăă‚‹', + prevText: '<前', + nextText: '次>', + currentText: '今日', + monthNames: ['1ćś','2ćś','3ćś','4ćś','5ćś','6ćś', + '7ćś','8ćś','9ćś','10ćś','11ćś','12ćś'], + monthNamesShort: ['1ćś','2ćś','3ćś','4ćś','5ćś','6ćś', + '7ćś','8ćś','9ćś','10ćś','11ćś','12ćś'], + dayNames: ['日曜日','ćść›ść—Ą','ç«ć›ść—Ą','水曜日','木曜日','金曜日','土曜日'], + dayNamesShort: ['ć—Ą','ćś','ç«','ć°´','木','金','ĺśź'], + dayNamesMin: ['ć—Ą','ćś','ç«','ć°´','木','金','ĺśź'], + dateFormat: 'yy/mm/dd', firstDay: 0, + isRTL: false, + showMonthAfterYear: true}; + $.datepicker.setDefaults($.datepicker.regional['ja']); +});/* Korean initialisation for the jQuery calendar extension. */ +/* Written by DaeKwon Kang (ncrash.dk@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['ko'] = { + closeText: '닫기', + prevText: '이전달', + nextText: '다음달', + currentText: 'ě¤ëŠ', + monthNames: ['1ě›”(JAN)','2ě›”(FEB)','3ě›”(MAR)','4ě›”(APR)','5ě›”(MAY)','6ě›”(JUN)', + '7ě›”(JUL)','8ě›”(AUG)','9ě›”(SEP)','10ě›”(OCT)','11ě›”(NOV)','12ě›”(DEC)'], + monthNamesShort: ['1ě›”(JAN)','2ě›”(FEB)','3ě›”(MAR)','4ě›”(APR)','5ě›”(MAY)','6ě›”(JUN)', + '7ě›”(JUL)','8ě›”(AUG)','9ě›”(SEP)','10ě›”(OCT)','11ě›”(NOV)','12ě›”(DEC)'], + dayNames: ['일','ě›”','í™”','ě','목','ę¸','토'], + dayNamesShort: ['일','ě›”','í™”','ě','목','ę¸','토'], + dayNamesMin: ['일','ě›”','í™”','ě','목','ę¸','토'], + dateFormat: 'yy-mm-dd', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['ko']); +});/* Lithuanian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* @author Arturas Paleicikas */ +jQuery(function($){ + $.datepicker.regional['lt'] = { + closeText: 'UĹľdaryti', + prevText: '<Atgal', + nextText: 'Pirmyn>', + currentText: 'Ĺ iandien', + monthNames: ['Sausis','Vasaris','Kovas','Balandis','Gegužė','BirĹľelis', + 'Liepa','RugpjĹ«tis','RugsÄ—jis','Spalis','Lapkritis','Gruodis'], + monthNamesShort: ['Sau','Vas','Kov','Bal','Geg','Bir', + 'Lie','Rugp','Rugs','Spa','Lap','Gru'], + dayNames: ['sekmadienis','pirmadienis','antradienis','treÄŤiadienis','ketvirtadienis','penktadienis','šeštadienis'], + dayNamesShort: ['sek','pir','ant','tre','ket','pen','šeš'], + dayNamesMin: ['Se','Pr','An','Tr','Ke','Pe','Ĺ e'], + dateFormat: 'yy-mm-dd', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['lt']); +});/* Latvian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* @author Arturas Paleicikas */ +jQuery(function($){ + $.datepicker.regional['lv'] = { + closeText: 'AizvÄ“rt', + prevText: 'Iepr', + nextText: 'NÄka', + currentText: 'Ĺ odien', + monthNames: ['JanvÄris','FebruÄris','Marts','AprÄ«lis','Maijs','JĹ«nijs', + 'JĹ«lijs','Augusts','Septembris','Oktobris','Novembris','Decembris'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','JĹ«n', + 'JĹ«l','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['svÄ“tdiena','pirmdiena','otrdiena','trešdiena','ceturtdiena','piektdiena','sestdiena'], + dayNamesShort: ['svt','prm','otr','tre','ctr','pkt','sst'], + dayNamesMin: ['Sv','Pr','Ot','Tr','Ct','Pk','Ss'], + dateFormat: 'dd-mm-yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['lv']); +});/* Malaysian initialisation for the jQuery UI date picker plugin. */ +/* Written by Mohd Nawawi Mohamad Jamili (nawawi@ronggeng.net). */ +jQuery(function($){ + $.datepicker.regional['ms'] = { + closeText: 'Tutup', + prevText: '<Sebelum', + nextText: 'Selepas>', + currentText: 'hari ini', + monthNames: ['Januari','Februari','Mac','April','Mei','Jun', + 'Julai','Ogos','September','Oktober','November','Disember'], + monthNamesShort: ['Jan','Feb','Mac','Apr','Mei','Jun', + 'Jul','Ogo','Sep','Okt','Nov','Dis'], + dayNames: ['Ahad','Isnin','Selasa','Rabu','Khamis','Jumaat','Sabtu'], + dayNamesShort: ['Aha','Isn','Sel','Rab','kha','Jum','Sab'], + dayNamesMin: ['Ah','Is','Se','Ra','Kh','Ju','Sa'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['ms']); +});/* Dutch (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Mathias Bynens */ +jQuery(function($){ + $.datepicker.regional.nl = { + closeText: 'Sluiten', + prevText: 'â†', + nextText: '→', + currentText: 'Vandaag', + monthNames: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', + 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + monthNamesShort: ['jan', 'feb', 'maa', 'apr', 'mei', 'jun', + 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + dayNames: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], + dayNamesShort: ['zon', 'maa', 'din', 'woe', 'don', 'vri', 'zat'], + dayNamesMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + dateFormat: 'dd/mm/yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional.nl); +});/* Norwegian initialisation for the jQuery UI date picker plugin. */ +/* Written by Naimdjon Takhirov (naimdjon@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['no'] = { + closeText: 'Lukk', + prevText: '«Forrige', + nextText: 'Neste»', + currentText: 'I dag', + monthNames: ['Januar','Februar','Mars','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'], + dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'], + dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'], + dateFormat: 'yy-mm-dd', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['no']); +}); +/* Polish initialisation for the jQuery UI date picker plugin. */ +/* Written by Jacek Wysocki (jacek.wysocki@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['pl'] = { + closeText: 'Zamknij', + prevText: '<Poprzedni', + nextText: 'NastÄ™pny>', + currentText: 'DziĹ›', + monthNames: ['StyczeĹ„','Luty','Marzec','KwiecieĹ„','Maj','Czerwiec', + 'Lipiec','SierpieĹ„','WrzesieĹ„','PaĹşdziernik','Listopad','GrudzieĹ„'], + monthNamesShort: ['Sty','Lu','Mar','Kw','Maj','Cze', + 'Lip','Sie','Wrz','Pa','Lis','Gru'], + dayNames: ['Niedziela','Poniedzialek','Wtorek','Ĺšroda','Czwartek','PiÄ…tek','Sobota'], + dayNamesShort: ['Nie','Pn','Wt','Ĺšr','Czw','Pt','So'], + dayNamesMin: ['N','Pn','Wt','Ĺšr','Cz','Pt','So'], + dateFormat: 'yy-mm-dd', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['pl']); +}); +/* Brazilian initialisation for the jQuery UI date picker plugin. */ +/* Written by Leonildo Costa Silva (leocsilva@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['pt-BR'] = { + closeText: 'Fechar', + prevText: '<Anterior', + nextText: 'Próximo>', + currentText: 'Hoje', + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', + 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sabado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sab'], + dayNamesMin: ['Dom','Seg','Ter','Qua','Qui','Sex','Sab'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['pt-BR']); +});/* Romanian initialisation for the jQuery UI date picker plugin. + * + * Written by Edmond L. (ll_edmond@walla.com) + * and Ionut G. Stan (ionut.g.stan@gmail.com) + */ +jQuery(function($){ + $.datepicker.regional['ro'] = { + closeText: 'ĂŽnchide', + prevText: '« Luna precedentÄ', + nextText: 'Luna urmÄtoare »', + currentText: 'Azi', + monthNames: ['Ianuarie','Februarie','Martie','Aprilie','Mai','Iunie', + 'Iulie','August','Septembrie','Octombrie','Noiembrie','Decembrie'], + monthNamesShort: ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', + 'Iul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['DuminicÄ', 'Luni', 'MarĹŁi', 'Miercuri', 'Joi', 'Vineri', 'SâmbÄtÄ'], + dayNamesShort: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'], + dayNamesMin: ['Du','Lu','Ma','Mi','Jo','Vi','Sâ'], + dateFormat: 'dd MM yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['ro']); +}); +/* Russian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Andrew Stromnov (stromnov@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['ru'] = { + closeText: 'Закрыть', + prevText: '<Пред', + nextText: 'След>', + currentText: 'Сегодня', + monthNames: ['Январь','Февраль','Март','Đпрель','Май','Đюнь', + 'Đюль','ĐвгŃŃŃ‚','Сентябрь','Октябрь','Ноябрь','Декабрь'], + monthNamesShort: ['Янв','Фев','Мар','Đпр','Май','ĐŃŽĐ˝', + 'ĐŃŽĐ»','Đвг','Сен','Окт','Ноя','Дек'], + dayNames: ['воŃкреŃенье','понедельник','вторник','Ńреда','четверг','пятница','ŃŃббота'], + dayNamesShort: ['вŃĐş','пнд','втр','Ńрд','чтв','птн','Ńбт'], + dayNamesMin: ['Đ’Ń','Пн','Đ’Ń‚','Ср','Чт','Пт','Сб'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['ru']); +});/* Slovak initialisation for the jQuery UI date picker plugin. */ +/* Written by Vojtech Rinik (vojto@hmm.sk). */ +jQuery(function($){ + $.datepicker.regional['sk'] = { + closeText: 'ZavrieĹĄ', + prevText: '<PredchádzajĂşci', + nextText: 'NasledujĂşci>', + currentText: 'Dnes', + monthNames: ['Január','Február','Marec','AprĂ­l','Máj','JĂşn', + 'JĂşl','August','September','OktĂłber','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Máj','JĂşn', + 'JĂşl','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Nedel\'a','Pondelok','Utorok','Streda','Ĺ tvrtok','Piatok','Sobota'], + dayNamesShort: ['Ned','Pon','Uto','Str','Ĺ tv','Pia','Sob'], + dayNamesMin: ['Ne','Po','Ut','St','Ĺ t','Pia','So'], + dateFormat: 'dd.mm.yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['sk']); +}); +/* Slovenian initialisation for the jQuery UI date picker plugin. */ +/* Written by Jaka Jancar (jaka@kubje.org). */ +/* c = č, s = š z = ž C = Č S = Š Z = Ž */ +jQuery(function($){ + $.datepicker.regional['sl'] = { + closeText: 'Zapri', + prevText: '<Prejšnji', + nextText: 'Naslednji>', + currentText: 'Trenutni', + monthNames: ['Januar','Februar','Marec','April','Maj','Junij', + 'Julij','Avgust','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljek','Torek','Sreda','Četrtek','Petek','Sobota'], + dayNamesShort: ['Ned','Pon','Tor','Sre','Čet','Pet','Sob'], + dayNamesMin: ['Ne','Po','To','Sr','Če','Pe','So'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['sl']); +}); +/* Albanian initialisation for the jQuery UI date picker plugin. */ +/* Written by Flakron Bytyqi (flakron@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['sq'] = { + closeText: 'mbylle', + prevText: '<mbrapa', + nextText: 'PĂ«rpara>', + currentText: 'sot', + monthNames: ['Janar','Shkurt','Mars','Prill','Maj','Qershor', + 'Korrik','Gusht','Shtator','Tetor','NĂ«ntor','Dhjetor'], + monthNamesShort: ['Jan','Shk','Mar','Pri','Maj','Qer', + 'Kor','Gus','Sht','Tet','NĂ«n','Dhj'], + dayNames: ['E Diel','E HĂ«nĂ«','E MartĂ«','E MĂ«rkurĂ«','E Enjte','E Premte','E Shtune'], + dayNamesShort: ['Di','HĂ«','Ma','MĂ«','En','Pr','Sh'], + dayNamesMin: ['Di','HĂ«','Ma','MĂ«','En','Pr','Sh'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['sq']); +}); +/* Serbian i18n for the jQuery UI date picker plugin. */ +/* Written by Dejan Dimić. */ +jQuery(function($){ + $.datepicker.regional['sr-SR'] = { + closeText: 'Zatvori', + prevText: '<', + nextText: '>', + currentText: 'Danas', + monthNames: ['Januar','Februar','Mart','April','Maj','Jun', + 'Jul','Avgust','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljak','Utorak','Sreda','ÄŚetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sre','ÄŚet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŚe','Pe','Su'], + dateFormat: 'dd/mm/yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['sr-SR']); +}); +/* Serbian i18n for the jQuery UI date picker plugin. */ +/* Written by Dejan Dimić. */ +jQuery(function($){ + $.datepicker.regional['sr'] = { + closeText: 'Затвори', + prevText: '<', + nextText: '>', + currentText: 'ДанаŃ', + monthNames: ['ĐĐ°Đ˝ŃĐ°Ń€','ФебрŃĐ°Ń€','Март','Đприл','МаŃ','ĐŃĐ˝', + 'ĐŃĐ»','ĐвгŃŃŃ‚','Септембар','Октобар','Новембар','Децембар'], + monthNamesShort: ['ĐĐ°Đ˝','Феб','Мар','Đпр','МаŃ','ĐŃĐ˝', + 'ĐŃĐ»','Đвг','Сеп','Окт','Нов','Дец'], + dayNames: ['Недеља','Понедељак','Уторак','Среда','Четвртак','Петак','СŃбота'], + dayNamesShort: ['Нед','Пон','ĐŁŃ‚Đľ','Сре','Чет','Пет','СŃб'], + dayNamesMin: ['Не','По','ĐŁŃ‚','Ср','Че','Пе','СŃ'], + dateFormat: 'dd/mm/yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['sr']); +}); +/* Swedish initialisation for the jQuery UI date picker plugin. */ +/* Written by Anders Ekdahl ( anders@nomadiz.se). */ +jQuery(function($){ + $.datepicker.regional['sv'] = { + closeText: 'Stäng', + prevText: '«Förra', + nextText: 'Nästa»', + currentText: 'Idag', + monthNames: ['Januari','Februari','Mars','April','Maj','Juni', + 'Juli','Augusti','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNamesShort: ['Sön','MĂĄn','Tis','Ons','Tor','Fre','Lör'], + dayNames: ['Söndag','MĂĄndag','Tisdag','Onsdag','Torsdag','Fredag','Lördag'], + dayNamesMin: ['Sö','MĂĄ','Ti','On','To','Fr','Lö'], + dateFormat: 'yy-mm-dd', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['sv']); +}); +/* Thai initialisation for the jQuery UI date picker plugin. */ +/* Written by pipo (pipo@sixhead.com). */ +jQuery(function($){ + $.datepicker.regional['th'] = { + closeText: 'ปิด', + prevText: '« ŕ¸˘ŕą‰ŕ¸­ŕ¸™', + nextText: 'ถัดไป »', + currentText: 'วันนี้', + monthNames: ['มŕ¸ŕ¸Łŕ¸˛ŕ¸„ม','ŕ¸ŕ¸¸ŕ¸ˇŕ¸ ŕ¸˛ŕ¸žŕ¸±ŕ¸™ŕ¸ŕąŚ','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน', + 'ŕ¸ŕ¸Łŕ¸ŕ¸Źŕ¸˛ŕ¸„ม','สิงหาคม','ŕ¸ŕ¸±ŕ¸™ŕ¸˘ŕ¸˛ŕ¸˘ŕ¸™','ตุลาคม','พฤศŕ¸ŕ¸´ŕ¸ŕ¸˛ŕ¸˘ŕ¸™','ŕ¸ŕ¸±ŕ¸™ŕ¸§ŕ¸˛ŕ¸„ม'], + monthNamesShort: ['ม.ค.','ŕ¸.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.', + 'ŕ¸.ค.','ส.ค.','ŕ¸.ย.','ต.ค.','พ.ย.','ŕ¸.ค.'], + dayNames: ['อาทิตย์','ŕ¸ŕ¸±ŕ¸™ŕ¸—ร์','อังคาร','พุŕ¸','พฤหัสบดี','ศุŕ¸ŕ¸ŁŕąŚ','เสาร์'], + dayNamesShort: ['อา.','ŕ¸.','อ.','พ.','พฤ.','ศ.','ส.'], + dayNamesMin: ['อา.','ŕ¸.','อ.','พ.','พฤ.','ศ.','ส.'], + dateFormat: 'dd/mm/yy', firstDay: 0, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['th']); +});/* Turkish initialisation for the jQuery UI date picker plugin. */ +/* Written by Izzet Emre Erkan (kara@karalamalar.net). */ +jQuery(function($){ + $.datepicker.regional['tr'] = { + closeText: 'kapat', + prevText: '<geri', + nextText: 'ileri>', + currentText: 'bugĂĽn', + monthNames: ['Ocak','Ĺžubat','Mart','Nisan','Mayıs','Haziran', + 'Temmuz','AÄźustos','EylĂĽl','Ekim','Kasım','Aralık'], + monthNamesShort: ['Oca','Ĺžub','Mar','Nis','May','Haz', + 'Tem','AÄźu','Eyl','Eki','Kas','Ara'], + dayNames: ['Pazar','Pazartesi','Salı','ÇarĹźamba','PerĹźembe','Cuma','Cumartesi'], + dayNamesShort: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'], + dayNamesMin: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'], + dateFormat: 'dd.mm.yy', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['tr']); +});/* Ukrainian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Maxim Drogobitskiy (maxdao@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['uk'] = { + clearText: 'ОчиŃтити', clearStatus: '', + closeText: 'Закрити', closeStatus: '', + prevText: '<', prevStatus: '', + prevBigText: '<<', prevBigStatus: '', + nextText: '>', nextStatus: '', + nextBigText: '>>', nextBigStatus: '', + currentText: 'Сьогодні', currentStatus: '', + monthNames: ['Січень','Лютий','Березень','Квітень','Травень','Червень', + 'Липень','Серпень','ВереŃень','Жовтень','ЛиŃтопад','Đ“Ń€Ńдень'], + monthNamesShort: ['Січ','Лют','Бер','Кві','Тра','Чер', + 'Лип','Сер','Вер','Жов','ЛиŃ','Đ“Ń€Ń'], + monthStatus: '', yearStatus: '', + weekHeader: 'Не', weekStatus: '', + dayNames: ['неділя','понеділок','вівторок','Ńереда','четвер','п’ятниця','ŃŃбота'], + dayNamesShort: ['нед','пнд','вів','Ńрд','чтв','птн','Ńбт'], + dayNamesMin: ['Нд','Пн','Đ’Ń‚','Ср','Чт','Пт','Сб'], + dayStatus: 'DD', dateStatus: 'D, M d', + dateFormat: 'dd/mm/yy', firstDay: 1, + initStatus: '', isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['uk']); +});/* Chinese initialisation for the jQuery UI date picker plugin. */ +/* Written by Cloudream (cloudream@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['zh-CN'] = { + closeText: 'ĺ…łé—­', + prevText: '<上ćś', + nextText: '下ćś>', + currentText: '今天', + monthNames: ['一ćś','二ćś','三ćś','ĺ››ćś','äş”ćś','ĺ…­ćś', + 'ä¸ćś','ĺ…«ćś','äąťćś','ĺŤćś','ĺŤä¸€ćś','ĺŤäşŚćś'], + monthNamesShort: ['一','二','三','ĺ››','äş”','ĺ…­', + 'ä¸','ĺ…«','äąť','ĺŤ','ĺŤä¸€','ĺŤäşŚ'], + dayNames: ['ćźćśźć—Ą','ćźćśźä¸€','ćźćśźäşŚ','ćźćśźä¸‰','ćźćśźĺ››','ćźćśźäş”','ćźćśźĺ…­'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['ć—Ą','一','二','三','ĺ››','äş”','ĺ…­'], + dateFormat: 'yy-mm-dd', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['zh-CN']); +}); +/* Chinese initialisation for the jQuery UI date picker plugin. */ +/* Written by Ressol (ressol@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['zh-TW'] = { + closeText: 'é—śé–‰', + prevText: '<上ćś', + nextText: '下ćś>', + currentText: '今天', + monthNames: ['一ćś','二ćś','三ćś','ĺ››ćś','äş”ćś','ĺ…­ćś', + 'ä¸ćś','ĺ…«ćś','äąťćś','ĺŤćś','ĺŤä¸€ćś','ĺŤäşŚćś'], + monthNamesShort: ['一','二','三','ĺ››','äş”','ĺ…­', + 'ä¸','ĺ…«','äąť','ĺŤ','ĺŤä¸€','ĺŤäşŚ'], + dayNames: ['ćźćśźć—Ą','ćźćśźä¸€','ćźćśźäşŚ','ćźćśźä¸‰','ćźćśźĺ››','ćźćśźäş”','ćźćśźĺ…­'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['ć—Ą','一','二','三','ĺ››','äş”','ĺ…­'], + dateFormat: 'yy/mm/dd', firstDay: 1, + isRTL: false}; + $.datepicker.setDefaults($.datepicker.regional['zh-TW']); +}); diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/jquery.ui.map.full.min.js b/misc/PublicationBulletins/Portail-LeHavre/libjs/jquery.ui.map.full.min.js new file mode 100644 index 00000000..0c896273 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/jquery.ui.map.full.min.js @@ -0,0 +1,2 @@ +/*! http://code.google.com/p/jquery-ui-map/ | Johan Säll Larsson */ +eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('(3(d){d.a=3(a,b){k c=a.u(".")[0],a=a.u(".")[1];d[c]=d[c]||{};d[c][a]=3(a,b){J.N&&2.13(a,b)};d[c][a].G=d.p({1w:c,1F:a},b);d.1h[a]=3(b){k g="1E"===1G b,f=Q.G.14.15(J,1),i=2;l(g&&"1I"===b.1H(0,1))4 i;2.1A(3(){k h=d.R(2,a);h||(h=d.R(2,a,j d[c][a](b,2)));g&&(i=h[b].18(h,f))});4 i}};d.a("1z.1B",{m:{1D:"1C",1P:5},1O:3(a,b){b&&(2.m[a]=b,2.6("9").C(2.m));4 2.m[a]},13:3(a,b){2.D=b;o.p(2.m,a);2.m.U=2.E(2.m.U);2.Y();2.V&&2.V()},Y:3(){k a=2;a.q={9:j 8.7.1Q(a.D,a.m),H:[],s:[],r:[],S:j 8.7.1S};8.7.x.1R(a.q.9,"1K",3(){d(a.D).1p("1J",a.q.9)});a.y(a.m.1N,a.q.9)},16:3(a){2.6("M",j 8.7.1M).p(2.E(a));2.6("9").1q(2.6("M"));4 2},1r:3(a){k b=2.6("9").1s();4 b?b.1t(a.17()):!1},1x:3(a,b){2.6("9").1y[b].L(2.z(a));4 2},1u:3(a,b,c){a.9=2.6("9");a.W=2.E(a.W);k c=j(c||8.7.1v)(a),e=2.6("H");c.T?e[c.T]=c:e.L(c);c.M&&2.16(c.17());2.y(b,a.9,c);4 d(c)},w:3(a){2.A(2.6(a));2.O(a,[]);4 2},A:3(a){P(k b 10 a)a.Z(b)&&(a[b]t 8.7.1c?(8.7.x.2c(a[b]),a[b].F&&a[b].F(v)):a[b]t Q&&2.A(a[b]),a[b]=v)},2b:3(a,b,c){k a=2.6(a),e;P(e 10 a)a.Z(e)&&c(a[e],b.11&&a[e][b.I]?-1")){P(k e=a.X(/ /g,"").u(">"),d=0;d "+a,[]).L(j 8.7[a](o.p({9:2.6("9")},b))))},21:3(a,b){(!b?2.6("s > B",j 8.7.B):2.6("s > B",j 8.7.B(b,a))).C(o.p({9:2.6("9")},a))},20:3(a,b,c){2.6("s > "+a,j 8.7.23(b,o.p({9:2.6("9")},c)))},26:3(a,b,c){k d=2,g=2.6("r > 1n",j 8.7.1n),f=2.6("r > 1i",j 8.7.1i);b&&f.C(b);g.25(a,3(a,b){"24"===b?(f.1U(a),f.F(d.6("9"))):f.F(v);c(a,b)})},1Y:3(a,b){2.6("9").1X(2.6("r > 1e",j 8.7.1e(2.z(a),b)))},29:3(a,b){2.6("r > 1f",j 8.7.1f).28(a,b)}});o.1h.p({1d:3(a,b){4 2.n("1d",a,b)},1a:3(a){4 2.n("1a",a)},19:3(a,b){4 2.n("19",a,b)},1b:3(a,b){4 2.n("1b",a,b)},1o:3(a,b){4 2.n("1o",a,b)},1k:3(a){4 2.n("1k",a)},1j:3(a){4 2.n("1j",a)},1m:3(a){8.7.x.1p(2[0],a)},n:3(a,b,c){8.7&&2[0]t 8.7.1c?8.7.x.1L(2[0],a,b):c?2.1g(a,b,c):2.1g(a,b);4 2}})})(o);',62,144,'||this|function|return||get|maps|google|map||||||||||new|var|if|options|addEventListener|jQuery|extend|instance|services|overlays|instanceof|split|null|clear|event|_call|_unwrap|_c|FusionTablesLayer|setOptions|el|_latLng|setMap|prototype|markers|property|arguments|LatLng|push|bounds|length|set|for|Array|data|iw|id|center|_init|position|replace|_create|hasOwnProperty|in|delimiter|value|_setup|slice|call|addBounds|getPosition|apply|dblclick|rightclick|mouseover|MVCObject|click|StreetViewPanorama|Geocoder|bind|fn|DirectionsRenderer|dragend|drag|else|triggerEvent|DirectionsService|mouseout|trigger|fitBounds|inViewport|getBounds|contains|addMarker|Marker|namespace|addControl|controls|ui|each|gmap|roadmap|mapTypeId|string|pluginName|typeof|substring|_|init|bounds_changed|addListener|LatLngBounds|callback|option|zoom|Map|addListenerOnce|InfoWindow|name|setDirections|isFunction|Object|setStreetView|displayStreetView|removeData|loadKML|loadFusion|addShape|KmlLayer|OK|route|displayDirections|openInfoWindow|geocode|search|open|find|clearInstanceListeners|indexOf|inArray|refresh|resize|setCenter|destroy|getCenter'.split('|'),0,{})) \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/menu.js b/misc/PublicationBulletins/Portail-LeHavre/libjs/menu.js new file mode 100644 index 00000000..dacad592 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/menu.js @@ -0,0 +1,63 @@ +/* -*- mode: javascript -*- + */ + +function getMouseXY(e) // works on IE6,FF,Moz,Opera7 +{ + if (!e) e = window.event; // works on IE, but not NS (we rely on NS passing us the event) + + if (e) + { + if (e.pageY) + { // this doesn't work on IE6!! (works on FF,Moz,Opera7) + mousey = e.pageY; + algor = '[e.pageX]'; + if (e.clientX || e.clientY) algor += ' [e.clientX] ' + } + else if (e.clientY) + { // works on IE6,FF,Moz,Opera7 +if ( document.documentElement && document.documentElement.scrollTop ) + { + mousey = e.clientY + document.documentElement.scrollTop; + } + + else + { + mousey = e.clientY + document.body.scrollTop; + } + algor = '[e.clientX]'; + if (e.pageX || e.pageY) algor += ' [e.pageX] ' + } + } +} + +var menu_firefox_flicker = false ; + + +var mousey = 0 + +function MenuDisplay(l_element) + { + getMouseXY() + if ( ! menu_firefox_flicker ) + { + l_element.childNodes[1].style.display = 'block' ; + if ( mousey > 600 ) + { + l_element.childNodes[1].style.left = '0px' ; + l_element.childNodes[1].style.display = 'block' ; + l_element.childNodes[1].style.top = - l_element.childNodes[1].offsetHeight + 'px' ; + } + } + else if ( mousey > 600 ) + { + l_element.childNodes[1].style.top = - l_element.childNodes[1].offsetHeight + 'px' ; + } + } + +function MenuHide(l_element) + { + if ( ! menu_firefox_flicker ) + { + l_element.childNodes[1].style.display = 'none' + } + } diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/INSTALL b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/INSTALL new file mode 100755 index 00000000..686431f8 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/INSTALL @@ -0,0 +1,8 @@ +Installing qTip is relatively straight forward. Simply include the both the jQuery and qTip library files e.g. jquery-qtip-1.0.0.min.js using script html element(s), like so: + + + + +Notice the jQuery library is included before qTip. This is _absolutely essential_ for correct functioning of the library and its accompanying methods. + +It is HIGHLY RECOMMENDED that all JavaScript includes like the one above be placed after all your content at the footer of the page, just before the end tag. This ensures that all content is loaded before manipulation of the DOM occurs. \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/LICENSE b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/LICENSE new file mode 100755 index 00000000..e9cd5ecb --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/LICENSE @@ -0,0 +1,7 @@ +Copyright © 2009 Craig Thompson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/REQUIREMENTS b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/REQUIREMENTS new file mode 100755 index 00000000..3c7918fd --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/REQUIREMENTS @@ -0,0 +1,5 @@ +JQUERY IS REQUIRED FOR USE OF THIS PLUGIN + +You can grab the latest version here: http://jquery.com + +We recommend version 1.3 due to its significant speed increases in several areas, but qTip supports the use of jQuery version 1.2.6 and above for all you who have decided not to upgrade. \ No newline at end of file diff --git a/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/jquery.qtip-1.0.0-rc3.min.js b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/jquery.qtip-1.0.0-rc3.min.js new file mode 100755 index 00000000..173885b7 --- /dev/null +++ b/misc/PublicationBulletins/Portail-LeHavre/libjs/qtip/jquery.qtip-1.0.0-rc3.min.js @@ -0,0 +1,15 @@ +/* + * jquery.qtip. The jQuery tooltip plugin + * + * Copyright (c) 2009 Craig Thompson + * http://craigsworks.com + * + * Licensed under MIT + * http://www.opensource.org/licenses/mit-license.php + * + * Launch : February 2009 + * Version : 1.0.0-rc3 + * Released: Tuesday 12th May, 2009 - 00:00 + * Debug: jquery.qtip.debug.js + */ +(function(f){f.fn.qtip=function(B,u){var y,t,A,s,x,w,v,z;if(typeof B=="string"){if(typeof f(this).data("qtip")!=="object"){f.fn.qtip.log.error.call(self,1,f.fn.qtip.constants.NO_TOOLTIP_PRESENT,false)}if(B=="api"){return f(this).data("qtip").interfaces[f(this).data("qtip").current]}else{if(B=="interfaces"){return f(this).data("qtip").interfaces}}}else{if(!B){B={}}if(typeof B.content!=="object"||(B.content.jquery&&B.content.length>0)){B.content={text:B.content}}if(typeof B.content.title!=="object"){B.content.title={text:B.content.title}}if(typeof B.position!=="object"){B.position={corner:B.position}}if(typeof B.position.corner!=="object"){B.position.corner={target:B.position.corner,tooltip:B.position.corner}}if(typeof B.show!=="object"){B.show={when:B.show}}if(typeof B.show.when!=="object"){B.show.when={event:B.show.when}}if(typeof B.show.effect!=="object"){B.show.effect={type:B.show.effect}}if(typeof B.hide!=="object"){B.hide={when:B.hide}}if(typeof B.hide.when!=="object"){B.hide.when={event:B.hide.when}}if(typeof B.hide.effect!=="object"){B.hide.effect={type:B.hide.effect}}if(typeof B.style!=="object"){B.style={name:B.style}}B.style=c(B.style);s=f.extend(true,{},f.fn.qtip.defaults,B);s.style=a.call({options:s},s.style);s.user=f.extend(true,{},B)}return f(this).each(function(){if(typeof B=="string"){w=B.toLowerCase();A=f(this).qtip("interfaces");if(typeof A=="object"){if(u===true&&w=="destroy"){while(A.length>0){A[A.length-1].destroy()}}else{if(u!==true){A=[f(this).qtip("api")]}for(y=0;y0))}if(typeof s.options.show.solo=="object"){z=f(s.options.show.solo)}else{if(s.options.show.solo===true){z=f("div.qtip").not(s.elements.tooltip)}}if(z){z.each(function(){if(f(this).qtip("api").status.rendered===true){f(this).qtip("api").hide()}})}if(typeof s.options.show.effect.type=="function"){s.options.show.effect.type.call(s.elements.tooltip,s.options.show.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.show.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeIn(s.options.show.effect.length,w);break;case"slide":s.elements.tooltip.slideDown(s.options.show.effect.length,function(){w();if(s.options.position.type!=="static"){s.updatePosition(y,true)}});break;case"grow":s.elements.tooltip.show(s.options.show.effect.length,w);break;default:s.elements.tooltip.show(null,w);break}s.elements.tooltip.addClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_SHOWN,"show")},hide:function(y){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"hide")}else{if(s.elements.tooltip.css("display")==="none"){return s}}clearTimeout(s.timers.show);s.elements.tooltip.stop(true,false);x=s.beforeHide.call(s,y);if(x===false){return s}function w(){s.onHide.call(s,y)}s.cache.toggle=0;if(typeof s.options.hide.effect.type=="function"){s.options.hide.effect.type.call(s.elements.tooltip,s.options.hide.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.hide.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeOut(s.options.hide.effect.length,w);break;case"slide":s.elements.tooltip.slideUp(s.options.hide.effect.length,w);break;case"grow":s.elements.tooltip.hide(s.options.hide.effect.length,w);break;default:s.elements.tooltip.hide(null,w);break}s.elements.tooltip.removeClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_HIDDEN,"hide")},updatePosition:function(w,x){var C,G,L,J,H,E,y,I,B,D,K,A,F,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updatePosition")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_POSITION_STATIC,"updatePosition")}}G={position:{left:0,top:0},dimensions:{height:0,width:0},corner:s.options.position.corner.target};L={position:s.getPosition(),dimensions:s.getDimensions(),corner:s.options.position.corner.tooltip};if(s.options.position.target!=="mouse"){if(s.options.position.target.get(0).nodeName.toLowerCase()=="area"){J=s.options.position.target.attr("coords").split(",");for(C=0;CG.dimensions.width){G.dimensions.width=J[C]}if(J[C]G.dimensions.height){G.dimensions.height=J[C]}if(J[C]0){if(L.corner.search(/Left/)!==-1){y.left-=s.options.style.border.radius}else{if(L.corner.search(/Right/)!==-1){y.left+=s.options.style.border.radius}}if(L.corner.search(/Top/)!==-1){y.top-=s.options.style.border.radius}else{if(L.corner.search(/Bottom/)!==-1){y.top+=s.options.style.border.radius}}}if(I){if(L.corner.search(/top/)!==-1){y.top-=I}else{if(L.corner.search(/bottom/)!==-1){y.top+=I}}if(L.corner.search(/left/)!==-1){y.left-=I}else{if(L.corner.search(/right/)!==-1){y.left+=I}}if(L.corner.search(/leftMiddle|rightMiddle/)!==-1){y.top-=1}}if(s.options.position.adjust.screen===true){y=o.call(s,y,G,L)}if(s.options.position.target==="mouse"&&s.options.position.adjust.mouse===true){if(s.options.position.adjust.screen===true&&s.elements.tip){K=s.elements.tip.attr("rel")}else{K=s.options.position.corner.tooltip}y.left+=(K.search(/right/i)!==-1)?-6:6;y.top+=(K.search(/bottom/i)!==-1)?-6:6}if(!s.elements.bgiframe&&f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){f("select, object").each(function(){A=f(this).offset();A.bottom=A.top+f(this).height();A.right=A.left+f(this).width();if(y.top+L.dimensions.height>=A.top&&y.left+L.dimensions.width>=A.left){k.call(s)}})}y.left+=s.options.position.adjust.x;y.top+=s.options.position.adjust.y;F=s.getPosition();if(y.left!=F.left||y.top!=F.top){z=s.beforePositionUpdate.call(s,w);if(z===false){return s}s.cache.position=y;if(x===true){s.status.animated=true;s.elements.tooltip.animate(y,200,"swing",function(){s.status.animated=false})}else{s.elements.tooltip.css(y)}s.onPositionUpdate.call(s,w);if(typeof w!=="undefined"&&w.type&&w.type!=="mousemove"){f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_POSITION_UPDATED,"updatePosition")}}return s},updateWidth:function(w){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateWidth")}else{if(w&&typeof w!=="number"){return f.fn.qtip.log.error.call(s,2,"newWidth must be of type number","updateWidth")}}x=s.elements.contentWrapper.siblings().add(s.elements.tip).add(s.elements.button);if(!w){if(typeof s.options.style.width.value=="number"){w=s.options.style.width.value}else{s.elements.tooltip.css({width:"auto"});x.hide();if(f.browser.msie){s.elements.wrapper.add(s.elements.contentWrapper.children()).css({zoom:"normal"})}w=s.getDimensions().width+1;if(!s.options.style.width.value){if(w>s.options.style.width.max){w=s.options.style.width.max}if(w").get(0).getContext){z=s.elements.tooltip.find(".qtip-tip canvas:first");x=z.get(0).getContext("2d");x.clearRect(0,0,300,300);y=z.parent("div[rel]:first").attr("rel");B=b(y,s.options.style.tip.size.width,s.options.style.tip.size.height);h.call(s,z,B,s.options.style.tip.color||s.options.style.border.color)}else{if(f.browser.msie){z=s.elements.tooltip.find('.qtip-tip [nodeName="shape"]');z.attr("fillcolor",s.options.style.tip.color||s.options.style.border.color)}}}if(s.options.style.border.radius>0){s.elements.tooltip.find(".qtip-betweenCorners").css({backgroundColor:s.options.style.border.color});if(f("").get(0).getContext){A=g(s.options.style.border.radius);s.elements.tooltip.find(".qtip-wrapper canvas").each(function(){x=f(this).get(0).getContext("2d");x.clearRect(0,0,300,300);y=f(this).parent("div[rel]:first").attr("rel");r.call(s,f(this),A[y],s.options.style.border.radius,s.options.style.border.color)})}else{if(f.browser.msie){s.elements.tooltip.find('.qtip-wrapper [nodeName="arc"]').each(function(){f(this).attr("fillcolor",s.options.style.border.color)})}}}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_STYLE_UPDATED,"updateStyle")},updateContent:function(A,y){var z,x,w;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateContent")}else{if(!A){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateContent")}}z=s.beforeContentUpdate.call(s,A);if(typeof z=="string"){A=z}else{if(z===false){return}}if(f.browser.msie){s.elements.contentWrapper.children().css({zoom:"normal"})}if(A.jquery&&A.length>0){A.clone(true).appendTo(s.elements.content).show()}else{s.elements.content.html(A)}x=s.elements.content.find("img[complete=false]");if(x.length>0){w=0;x.each(function(C){f('').load(function(){if(++w==x.length){B()}})})}else{B()}function B(){s.updateWidth();if(y!==false){if(s.options.position.type!=="static"){s.updatePosition(s.elements.tooltip.is(":visible"),true)}if(s.options.style.tip.corner!==false){n.call(s)}}}s.onContentUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_UPDATED,"loadContent")},loadContent:function(w,z,A){var y;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"loadContent")}y=s.beforeContentLoad.call(s);if(y===false){return s}if(A=="post"){f.post(w,z,x)}else{f.get(w,z,x)}function x(B){s.onContentLoad.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_LOADED,"loadContent");s.updateContent(B)}return s},updateTitle:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateTitle")}else{if(!w){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateTitle")}}returned=s.beforeTitleUpdate.call(s);if(returned===false){return s}if(s.elements.button){s.elements.button=s.elements.button.clone(true)}s.elements.title.html(w);if(s.elements.button){s.elements.title.prepend(s.elements.button)}s.onTitleUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_TITLE_UPDATED,"updateTitle")},focus:function(A){var y,x,w,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"focus")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_FOCUS_STATIC,"focus")}}y=parseInt(s.elements.tooltip.css("z-index"));x=6000+f("div.qtip[qtip]").length-1;if(!s.status.focused&&y!==x){z=s.beforeFocus.call(s,A);if(z===false){return s}f("div.qtip[qtip]").not(s.elements.tooltip).each(function(){if(f(this).qtip("api").status.rendered===true){w=parseInt(f(this).css("z-index"));if(typeof w=="number"&&w>-1){f(this).css({zIndex:parseInt(f(this).css("z-index"))-1})}f(this).qtip("api").status.focused=false}});s.elements.tooltip.css({zIndex:x});s.status.focused=true;s.onFocus.call(s,A);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_FOCUSED,"focus")}return s},disable:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"disable")}if(w){if(!s.status.disabled){s.status.disabled=true;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DISABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_DISABLED,"disable")}}else{if(s.status.disabled){s.status.disabled=false;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_ENABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_ENABLED,"disable")}}return s},destroy:function(){var w,x,y;x=s.beforeDestroy.call(s);if(x===false){return s}if(s.status.rendered){s.options.show.when.target.unbind("mousemove.qtip",s.updatePosition);s.options.show.when.target.unbind("mouseout.qtip",s.hide);s.options.show.when.target.unbind(s.options.show.when.event+".qtip");s.options.hide.when.target.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind("mouseover.qtip",s.focus);s.elements.tooltip.remove()}else{s.options.show.when.target.unbind(s.options.show.when.event+".qtip-create")}if(typeof s.elements.target.data("qtip")=="object"){y=s.elements.target.data("qtip").interfaces;if(typeof y=="object"&&y.length>0){for(w=0;w0){s.elements.target.data("qtip").current=y.length-1}else{s.elements.target.removeData("qtip")}s.onDestroy.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DESTROYED,"destroy");return s.elements.target},getPosition:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getPosition")}w=(s.elements.tooltip.css("display")!=="none")?false:true;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x=s.elements.tooltip.offset();if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x},getDimensions:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getDimensions")}w=(!s.elements.tooltip.is(":visible"))?true:false;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x={height:s.elements.tooltip.outerHeight(),width:s.elements.tooltip.outerWidth()};if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x}})}function p(){var s,w,u,t,v,y,x;s=this;s.beforeRender.call(s);s.status.rendered=true;s.elements.tooltip='';s.elements.tooltip=f(s.elements.tooltip);s.elements.tooltip.appendTo(s.options.position.container);s.elements.tooltip.data("qtip",{current:0,interfaces:[s]});s.elements.wrapper=s.elements.tooltip.children("div:first");s.elements.contentWrapper=s.elements.wrapper.children("div:first").css({background:s.options.style.background});s.elements.content=s.elements.contentWrapper.children("div:first").css(q(s.options.style));if(f.browser.msie){s.elements.wrapper.add(s.elements.content).css({zoom:1})}if(s.options.hide.when.event=="unfocus"){s.elements.tooltip.attr("unfocus",true)}if(typeof s.options.style.width.value=="number"){s.updateWidth()}if(f("").get(0).getContext||f.browser.msie){if(s.options.style.border.radius>0){m.call(s)}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color})}if(s.options.style.tip.corner!==false){e.call(s)}}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color});s.options.style.border.radius=0;s.options.style.tip.corner=false;f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.CANVAS_VML_NOT_SUPPORTED,"render")}if((typeof s.options.content.text=="string"&&s.options.content.text.length>0)||(s.options.content.text.jquery&&s.options.content.text.length>0)){u=s.options.content.text}else{if(typeof s.elements.target.attr("title")=="string"&&s.elements.target.attr("title").length>0){u=s.elements.target.attr("title").replace("\\n","
      ");s.elements.target.attr("title","")}else{if(typeof s.elements.target.attr("alt")=="string"&&s.elements.target.attr("alt").length>0){u=s.elements.target.attr("alt").replace("\\n","
      ");s.elements.target.attr("alt","")}else{u=" ";f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.NO_VALID_CONTENT,"render")}}}if(s.options.content.title.text!==false){j.call(s)}s.updateContent(u);l.call(s);if(s.options.show.ready===true){s.show()}if(s.options.content.url!==false){t=s.options.content.url;v=s.options.content.data;y=s.options.content.method||"get";s.loadContent(t,v,y)}s.onRender.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_RENDERED,"render")}function m(){var F,z,t,B,x,E,u,G,D,y,w,C,A,s,v;F=this;F.elements.wrapper.find(".qtip-borderBottom, .qtip-borderTop").remove();t=F.options.style.border.width;B=F.options.style.border.radius;x=F.options.style.border.color||F.options.style.tip.color;E=g(B);u={};for(z in E){u[z]='
      ';if(f("").get(0).getContext){u[z]+=''}else{if(f.browser.msie){G=B*2+3;u[z]+=''}}u[z]+="
      "}D=F.getDimensions().width-(Math.max(t,B)*2);y='
      ';w='
      '+u.topLeft+u.topRight+y;F.elements.wrapper.prepend(w);C='
      '+u.bottomLeft+u.bottomRight+y;F.elements.wrapper.append(C);if(f("").get(0).getContext){F.elements.wrapper.find("canvas").each(function(){A=E[f(this).parent("[rel]:first").attr("rel")];r.call(F,f(this),A,B,x)})}else{if(f.browser.msie){F.elements.tooltip.append('')}}s=Math.max(B,(B+(t-B)));v=Math.max(t-B,0);F.elements.contentWrapper.css({border:"0px solid "+x,borderWidth:v+"px "+s+"px"})}function r(u,w,s,t){var v=u.get(0).getContext("2d");v.fillStyle=t;v.beginPath();v.arc(w[0],w[1],s,0,Math.PI*2,false);v.fill()}function e(v){var t,s,x,u,w;t=this;if(t.elements.tip!==null){t.elements.tip.remove()}s=t.options.style.tip.color||t.options.style.border.color;if(t.options.style.tip.corner===false){return}else{if(!v){v=t.options.style.tip.corner}}x=b(v,t.options.style.tip.size.width,t.options.style.tip.size.height);t.elements.tip='
      ';if(f("").get(0).getContext){t.elements.tip+=''}else{if(f.browser.msie){u=t.options.style.tip.size.width+","+t.options.style.tip.size.height;w="m"+x[0][0]+","+x[0][1];w+=" l"+x[1][0]+","+x[1][1];w+=" "+x[2][0]+","+x[2][1];w+=" xe";t.elements.tip+='';t.elements.tip+='';t.elements.contentWrapper.css("position","relative")}}t.elements.tooltip.prepend(t.elements.tip+"
      ");t.elements.tip=t.elements.tooltip.find("."+t.options.style.classes.tip).eq(0);if(f("").get(0).getContext){h.call(t,t.elements.tip.find("canvas:first"),x,s)}if(v.search(/top/)!==-1&&f.browser.msie&&parseInt(f.browser.version.charAt(0))===6){t.elements.tip.css({marginTop:-4})}n.call(t,v)}function h(t,v,s){var u=t.get(0).getContext("2d");u.fillStyle=s;u.beginPath();u.moveTo(v[0][0],v[0][1]);u.lineTo(v[1][0],v[1][1]);u.lineTo(v[2][0],v[2][1]);u.fill()}function n(u){var t,w,s,x,v;t=this;if(t.options.style.tip.corner===false||!t.elements.tip){return}if(!u){u=t.elements.tip.attr("rel")}w=positionAdjust=(f.browser.msie)?1:0;t.elements.tip.css(u.match(/left|right|top|bottom/)[0],0);if(u.search(/top|bottom/)!==-1){if(f.browser.msie){if(parseInt(f.browser.version.charAt(0))===6){positionAdjust=(u.search(/top/)!==-1)?-3:1}else{positionAdjust=(u.search(/top/)!==-1)?1:2}}if(u.search(/Middle/)!==-1){t.elements.tip.css({left:"50%",marginLeft:-(t.options.style.tip.size.width/2)})}else{if(u.search(/Left/)!==-1){t.elements.tip.css({left:t.options.style.border.radius-w})}else{if(u.search(/Right/)!==-1){t.elements.tip.css({right:t.options.style.border.radius+w})}}}if(u.search(/top/)!==-1){t.elements.tip.css({top:-positionAdjust})}else{t.elements.tip.css({bottom:positionAdjust})}}else{if(u.search(/left|right/)!==-1){if(f.browser.msie){positionAdjust=(parseInt(f.browser.version.charAt(0))===6)?1:((u.search(/left/)!==-1)?1:2)}if(u.search(/Middle/)!==-1){t.elements.tip.css({top:"50%",marginTop:-(t.options.style.tip.size.height/2)})}else{if(u.search(/Top/)!==-1){t.elements.tip.css({top:t.options.style.border.radius-w})}else{if(u.search(/Bottom/)!==-1){t.elements.tip.css({bottom:t.options.style.border.radius+w})}}}if(u.search(/left/)!==-1){t.elements.tip.css({left:-positionAdjust})}else{t.elements.tip.css({right:positionAdjust})}}}s="padding-"+u.match(/left|right|top|bottom/)[0];x=t.options.style.tip.size[(s.search(/left|right/)!==-1)?"width":"height"];t.elements.tooltip.css("padding",0);t.elements.tooltip.css(s,x);if(f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){v=parseInt(t.elements.tip.css("margin-top"))||0;v+=parseInt(t.elements.content.css("margin-top"))||0;t.elements.tip.css({marginTop:v})}}function j(){var s=this;if(s.elements.title!==null){s.elements.title.remove()}s.elements.title=f('
      ').css(q(s.options.style.title,true)).css({zoom:(f.browser.msie)?1:0}).prependTo(s.elements.contentWrapper);if(s.options.content.title.text){s.updateTitle.call(s,s.options.content.title.text)}if(s.options.content.title.button!==false&&typeof s.options.content.title.button=="string"){s.elements.button=f('').css(q(s.options.style.button,true)).html(s.options.content.title.button).prependTo(s.elements.title).click(function(t){if(!s.status.disabled){s.hide(t)}})}}function l(){var t,v,u,s;t=this;v=t.options.show.when.target;u=t.options.hide.when.target;if(t.options.hide.fixed){u=u.add(t.elements.tooltip)}if(t.options.hide.when.event=="inactive"){s=["click","dblclick","mousedown","mouseup","mousemove","mouseout","mouseenter","mouseleave","mouseover"];function y(z){if(t.status.disabled===true){return}clearTimeout(t.timers.inactive);t.timers.inactive=setTimeout(function(){f(s).each(function(){u.unbind(this+".qtip-inactive");t.elements.content.unbind(this+".qtip-inactive")});t.hide(z)},t.options.hide.delay)}}else{if(t.options.hide.fixed===true){t.elements.tooltip.bind("mouseover.qtip",function(){if(t.status.disabled===true){return}clearTimeout(t.timers.hide)})}}function x(z){if(t.status.disabled===true){return}if(t.options.hide.when.event=="inactive"){f(s).each(function(){u.bind(this+".qtip-inactive",y);t.elements.content.bind(this+".qtip-inactive",y)});y()}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.timers.show=setTimeout(function(){t.show(z)},t.options.show.delay)}function w(z){if(t.status.disabled===true){return}if(t.options.hide.fixed===true&&t.options.hide.when.event.search(/mouse(out|leave)/i)!==-1&&f(z.relatedTarget).parents("div.qtip[qtip]").length>0){z.stopPropagation();z.preventDefault();clearTimeout(t.timers.hide);return false}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.elements.tooltip.stop(true,true);t.timers.hide=setTimeout(function(){t.hide(z)},t.options.hide.delay)}if((t.options.show.when.target.add(t.options.hide.when.target).length===1&&t.options.show.when.event==t.options.hide.when.event&&t.options.hide.when.event!=="inactive")||t.options.hide.when.event=="unfocus"){t.cache.toggle=0;v.bind(t.options.show.when.event+".qtip",function(z){if(t.cache.toggle==0){x(z)}else{w(z)}})}else{v.bind(t.options.show.when.event+".qtip",x);if(t.options.hide.when.event!=="inactive"){u.bind(t.options.hide.when.event+".qtip",w)}}if(t.options.position.type.search(/(fixed|absolute)/)!==-1){t.elements.tooltip.bind("mouseover.qtip",t.focus)}if(t.options.position.target==="mouse"&&t.options.position.type!=="static"){v.bind("mousemove.qtip",function(z){t.cache.mouse={x:z.pageX,y:z.pageY};if(t.status.disabled===false&&t.options.position.adjust.mouse===true&&t.options.position.type!=="static"&&t.elements.tooltip.css("display")!=="none"){t.updatePosition(z)}})}}function o(u,v,A){var z,s,x,y,t,w;z=this;if(A.corner=="center"){return v.position}s=f.extend({},u);y={x:false,y:false};t={left:(s.left=f.fn.qtip.cache.screen.width+f.fn.qtip.cache.screen.scroll.left),top:(s.top=f.fn.qtip.cache.screen.height+f.fn.qtip.cache.screen.scroll.top)};x={left:(t.left&&(A.corner.search(/right/i)!=-1||(A.corner.search(/right/i)==-1&&!t.right))),right:(t.right&&(A.corner.search(/left/i)!=-1||(A.corner.search(/left/i)==-1&&!t.left))),top:(t.top&&A.corner.search(/top/i)==-1),bottom:(t.bottom&&A.corner.search(/bottom/i)==-1)};if(x.left){if(z.options.position.target!=="mouse"){s.left=v.position.left+v.dimensions.width}else{s.left=z.cache.mouse.x}y.x="Left"}else{if(x.right){if(z.options.position.target!=="mouse"){s.left=v.position.left-A.dimensions.width}else{s.left=z.cache.mouse.x-A.dimensions.width}y.x="Right"}}if(x.top){if(z.options.position.target!=="mouse"){s.top=v.position.top+v.dimensions.height}else{s.top=z.cache.mouse.y}y.y="top"}else{if(x.bottom){if(z.options.position.target!=="mouse"){s.top=v.position.top-A.dimensions.height}else{s.top=z.cache.mouse.y-A.dimensions.height}y.y="bottom"}}if(s.left<0){s.left=u.left;y.x=false}if(s.top<0){s.top=u.top;y.y=false}if(z.options.style.tip.corner!==false){s.corner=new String(A.corner);if(y.x!==false){s.corner=s.corner.replace(/Left|Right|Middle/,y.x)}if(y.y!==false){s.corner=s.corner.replace(/top|bottom/,y.y)}if(s.corner!==z.elements.tip.attr("rel")){e.call(z,s.corner)}}return s}function q(u,t){var v,s;v=f.extend(true,{},u);for(s in v){if(t===true&&s.search(/(tip|classes)/i)!==-1){delete v[s]}else{if(!t&&s.search(/(width|border|tip|title|classes|user)/i)!==-1){delete v[s]}}}return v}function c(s){if(typeof s.tip!=="object"){s.tip={corner:s.tip}}if(typeof s.tip.size!=="object"){s.tip.size={width:s.tip.size,height:s.tip.size}}if(typeof s.border!=="object"){s.border={width:s.border}}if(typeof s.width!=="object"){s.width={value:s.width}}if(typeof s.width.max=="string"){s.width.max=parseInt(s.width.max.replace(/([0-9]+)/i,"$1"))}if(typeof s.width.min=="string"){s.width.min=parseInt(s.width.min.replace(/([0-9]+)/i,"$1"))}if(typeof s.tip.size.x=="number"){s.tip.size.width=s.tip.size.x;delete s.tip.size.x}if(typeof s.tip.size.y=="number"){s.tip.size.height=s.tip.size.y;delete s.tip.size.y}return s}function a(){var s,t,u,x,v,w;s=this;u=[true,{}];for(t=0;t0){v.tip.size.width+=1}if(v.tip.size.height%2>0){v.tip.size.height+=1}if(v.tip.corner===true){v.tip.corner=(s.options.position.corner.tooltip==="center")?false:s.options.position.corner.tooltip}return v}function b(v,u,t){var s={bottomRight:[[0,0],[u,t],[u,0]],bottomLeft:[[0,0],[u,0],[0,t]],topRight:[[0,t],[u,0],[u,t]],topLeft:[[0,0],[0,t],[u,t]],topMiddle:[[0,t],[u/2,0],[u,t]],bottomMiddle:[[0,0],[u,0],[u/2,t]],rightMiddle:[[0,0],[u,t/2],[0,t]],leftMiddle:[[u,0],[u,t],[0,t/2]]};s.leftTop=s.bottomRight;s.rightTop=s.bottomLeft;s.leftBottom=s.topRight;s.rightBottom=s.topLeft;return s[v]}function g(s){var t;if(f("").get(0).getContext){t={topLeft:[s,s],topRight:[0,s],bottomLeft:[s,0],bottomRight:[0,0]}}else{if(f.browser.msie){t={topLeft:[-90,90,0],topRight:[-90,90,-s],bottomLeft:[90,270,0],bottomRight:[90,270,-s]}}}return t}function k(){var s,t,u;s=this;u=s.getDimensions();t='\ + \ + '); + } + + + diff --git a/static/libjs/jQuery-tagEditor/jquery.caret.min.js b/static/libjs/jQuery-tagEditor/jquery.caret.min.js new file mode 100644 index 00000000..0f9ef48d --- /dev/null +++ b/static/libjs/jQuery-tagEditor/jquery.caret.min.js @@ -0,0 +1,2 @@ +// http://code.accursoft.com/caret - 1.3.3 +!function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return t.selectionStart?t.selectionStart:0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery); diff --git a/static/libjs/jQuery-tagEditor/jquery.tag-editor.css b/static/libjs/jQuery-tagEditor/jquery.tag-editor.css new file mode 100644 index 00000000..949995d2 --- /dev/null +++ b/static/libjs/jQuery-tagEditor/jquery.tag-editor.css @@ -0,0 +1,45 @@ +/* surrounding tag container */ +.tag-editor { + list-style-type: none; padding: 0 5px 0 0; margin: 0; overflow: hidden; border: 1px solid #eee; cursor: text; + font: normal 14px sans-serif; color: #555; background: #fff; line-height: 20px; +} + +/* core styles usually need no change */ +.tag-editor li { display: block; float: left; overflow: hidden; margin: 3px 0; } +.tag-editor div { float: left; padding: 0 4px; } +.tag-editor .placeholder { padding: 0 8px; color: #bbb; } +.tag-editor .tag-editor-spacer { padding: 0; width: 8px; overflow: hidden; color: transparent; background: none; } +.tag-editor input { + vertical-align: inherit; border: 0; outline: none; padding: 0; margin: 0; cursor: text; + font-family: inherit; font-weight: inherit; font-size: inherit; font-style: inherit; + box-shadow: none; background: none; color: #444; +} +/* hide original input field or textarea visually to allow tab navigation */ +.tag-editor-hidden-src { position: absolute !important; left: -99999px; } +/* hide IE10 "clear field" X */ +.tag-editor ::-ms-clear { display: none; } + +/* tag style */ +.tag-editor .tag-editor-tag { + padding-left: 5px; color: #46799b; background: #e0eaf1; white-space: nowrap; + overflow: hidden; cursor: pointer; border-radius: 2px 0 0 2px; +} + +/* delete icon */ +.tag-editor .tag-editor-delete { background: #e0eaf1; cursor: pointer; border-radius: 0 2px 2px 0; padding-left: 3px; padding-right: 4px; } +.tag-editor .tag-editor-delete i { line-height: 18px; display: inline-block; } +.tag-editor .tag-editor-delete i:before { font-size: 16px; color: #8ba7ba; content: "Ă—"; font-style: normal; } +.tag-editor .tag-editor-delete:hover i:before { color: #d65454; } +.tag-editor .tag-editor-tag.active+.tag-editor-delete, .tag-editor .tag-editor-tag.active+.tag-editor-delete i { visibility: hidden; cursor: text; } + +.tag-editor .tag-editor-tag.active { background: none !important; } + +/* jQuery UI autocomplete - code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css */ +.ui-autocomplete { position: absolute; top: 0; left: 0; cursor: default; font-size: 14px; } +.ui-front { z-index: 9999; } +.ui-menu { list-style: none; padding: 1px; margin: 0; display: block; outline: none; } +.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.4; min-height: 0; /* support: IE7 */ } +.ui-widget-content { border: 1px solid #bbb; background: #fff; color: #555; } +.ui-widget-content a { color: #46799b; } +.ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { background: #e0eaf1; } +.ui-helper-hidden-accessible { display: none; } diff --git a/static/libjs/jQuery-tagEditor/jquery.tag-editor.js b/static/libjs/jQuery-tagEditor/jquery.tag-editor.js new file mode 100644 index 00000000..048f5384 --- /dev/null +++ b/static/libjs/jQuery-tagEditor/jquery.tag-editor.js @@ -0,0 +1,370 @@ +/* + jQuery tagEditor v1.0.20 + Copyright (c) 2014 Simon Steinberger / Pixabay + GitHub: https://github.com/Pixabay/jQuery-tagEditor + License: http://www.opensource.org/licenses/mit-license.php +*/ + +(function($){ + // auto grow input (stackoverflow.com/questions/931207) + $.fn.tagEditorInput=function(){var t=" ",e=$(this),n=parseInt(e.css("fontSize")),i=$("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),letterSpacing:e.css("letterSpacing"),whiteSpace:"nowrap"}),s=function(){if(t!==(t=e.val())){i.text(t);var s=i.width()+n;20>s&&(s=20),s!=e.width()&&e.width(s)}};return i.insertAfter(e),e.bind("keyup keydown focus",s)}; + + // plugin with val as parameter for public methods + $.fn.tagEditor = function(options, val, blur){ + + // helper + function escape(tag) { + return tag.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + } + + // build options dictionary with default values + var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this; + + // store regex and default delimiter in options for later use + o.dregex = new RegExp('['+o.delimiter.replace('-', '\-')+']', 'g'); + + // public methods + if (typeof options == 'string') { + // depending on selector, response may contain tag lists of multiple editor instances + var response = []; + selector.each(function(){ + // the editor is the next sibling to the hidden, original field + var el = $(this), o = el.data('options'), ed = el.next('.tag-editor'); + if (options == 'getTags') + response.push({field: el[0], editor: ed, tags: ed.data('tags')}); + else if (options == 'addTag') { + if (o.maxTags && ed.data('tags').length >= o.maxTags) return false; + // insert new tag + $('
    14.  '+o.delimiter[0]+'
    15. ').appendTo(ed).find('.tag-editor-tag') + .html('').addClass('active').find('input').val(val).blur(); + if (!blur) ed.click(); + else $('.placeholder', ed).remove(); + } else if (options == 'removeTag') { + // trigger delete on matching tag, then click editor to create a new tag + $('.tag-editor-tag', ed).filter(function(){return $(this).text()==val;}).closest('li').find('.tag-editor-delete').click(); + if (!blur) ed.click(); + } else if (options == 'destroy') { + el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove(); + } + }); + return options == 'getTags' ? response : this; + } + + // delete selected tags on backspace, delete, ctrl+x + if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e){ + if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) { + try { + var sel = getSelection(), el = document.activeElement.tagName == 'BODY' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0; + } catch(e){ el = 0; } + if (sel.rangeCount > 0 && el && el.length) { + var tags = [], splits = sel.toString().split(el.prev().data('options').dregex); + for (i=0; i').insertAfter(el); + el.addClass('tag-editor-hidden-src') // hide original field + .data('options', o) // set data on hidden field + .on('focus.tag-editor', function(){ ed.click(); }); // simulate tabindex + + // add dummy item for min-height on empty editor + ed.append('
    16.  
    17. '); + + // markup for new tag + var new_tag = '
    18.  '+o.delimiter[0]+'
    19. '; + + // helper: update global data + function set_placeholder(){ + if (o.placeholder && !tag_list.length && !$('.deleted, .placeholder, input', ed).length) + ed.append('
    20. '+o.placeholder+'
    21. '); + } + + // helper: update global data + function update_globals(init){ + var old_tags = tag_list.toString(); + tag_list = $('.tag-editor-tag:not(.deleted)', ed).map(function(i, e) { + var val = $.trim($(this).hasClass('active') ? $(this).find('input').val() : $(e).text()); + if (val) return val; + }).get(); + ed.data('tags', tag_list); + el.val(tag_list.join(o.delimiter[0])); + // change callback except for plugin init + if (!init) if (old_tags != tag_list.toString()) o.onChange(el, ed, tag_list); + set_placeholder(); + } + + ed.click(function(e, closest_tag){ + var d, dist = 99999, loc; + + // do not create tag when user selects tags by text selection + if (window.getSelection && getSelection() != '') return; + + if (o.maxTags && ed.data('tags').length >= o.maxTags) { ed.find('input').blur(); return false; } + + blur_result = true + $('input:focus', ed).blur(); + if (!blur_result) return false; + blur_result = true + + // always remove placeholder on click + $('.placeholder', ed).remove(); + if (closest_tag && closest_tag.length) + loc = 'before'; + else { + // calculate tag closest to click position + $('.tag-editor-tag', ed).each(function(){ + var tag = $(this), to = tag.offset(), tag_x = to.left, tag_y = to.top; + if (e.pageY >= tag_y && e.pageY <= tag_y+tag.height()) { + if (e.pageX < tag_x) loc = 'before', d = tag_x - e.pageX; + else loc = 'after', d = e.pageX - tag_x - tag.width(); + if (d < dist) dist = d, closest_tag = tag; + } + }); + } + + if (loc == 'before') { + $(new_tag).insertBefore(closest_tag.closest('li')).find('.tag-editor-tag').click(); + } else if (loc == 'after') + $(new_tag).insertAfter(closest_tag.closest('li')).find('.tag-editor-tag').click(); + else // empty editor + $(new_tag).appendTo(ed).find('.tag-editor-tag').click(); + return false; + }); + + ed.on('click', '.tag-editor-delete', function(e){ + // delete icon is hidden when input is visible; place cursor near invisible delete icon on click + if ($(this).prev().hasClass('active')) { $(this).closest('li').find('input').caret(-1); return false; } + + var li = $(this).closest('li'), tag = li.find('.tag-editor-tag'); + if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false; + tag.addClass('deleted').animate({width: 0}, o.animateDelete, function(){ li.remove(); set_placeholder(); }); + update_globals(); + return false; + }); + + // delete on right mouse click or ctrl+click + if (o.clickDelete) + ed.on('mousedown', '.tag-editor-tag', function(e){ + if (e.ctrlKey || e.which > 1) { + var li = $(this).closest('li'), tag = li.find('.tag-editor-tag'); + if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false; + tag.addClass('deleted').animate({width: 0}, o.animateDelete, function(){ li.remove(); set_placeholder(); }); + update_globals(); + return false; + } + }); + + ed.on('click', '.tag-editor-tag', function(e){ + // delete on right click or ctrl+click -> exit + if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false; + + if (!$(this).hasClass('active')) { + var tag = $(this).text(); + // guess cursor position in text input + var left_percent = Math.abs(($(this).offset().left - e.pageX)/$(this).width()), caret_pos = parseInt(tag.length*left_percent), + input = $(this).html('').addClass('active').find('input'); + input.data('old_tag', tag).tagEditorInput().focus().caret(caret_pos); + if (o.autocomplete) { + var aco = $.extend({}, o.autocomplete); + // extend user provided autocomplete select method + var ac_select = 'select' in aco ? o.autocomplete.select : ''; + aco.select = function(e, ui){ if (ac_select) ac_select(e, ui); setTimeout(function(){ + ed.trigger('click', [$('.active', ed).find('input').closest('li').next('li').find('.tag-editor-tag')]); + }, 20); }; + input.autocomplete(aco); + } + } + return false; + }); + + // helper: split into multiple tags, e.g. after paste + function split_cleanup(input){ + var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex), + old_tag = input.data('old_tag'), old_tags = tag_list.slice(0), exceeded = false, cb_val; // copy tag_list + for (var i=0; i
       '+o.delimiter[0]+'
      '+escape(tag)+'
      '); + if (o.maxTags && old_tags.length >= o.maxTags) { exceeded = true; break; } + } + input.attr('maxlength', o.maxLength).removeData('old_tag').val('') + if (exceeded) input.blur(); else input.focus(); + update_globals(); + } + + ed.on('blur', 'input', function(e){ + e.stopPropagation(); + var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0])); + if (!tag) { + if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag) === false) { + input.val(old_tag).focus(); + blur_result = false; + update_globals(); + return; + } + try { input.closest('li').remove(); } catch(e){} + if (old_tag) update_globals(); + } + else if (tag.indexOf(o.delimiter[0])>=0) { split_cleanup(input); return; } + else if (tag != old_tag) { + if (o.forceLowercase) tag = tag.toLowerCase(); + cb_val = o.beforeTagSave(el, ed, tag_list, old_tag, tag); + tag = cb_val || tag; + if (cb_val === false) { + if (old_tag) { + input.val(old_tag).focus(); + blur_result = false; + update_globals(); + return; + } + try { input.closest('li').remove(); } catch(e){} + if (old_tag) update_globals(); + } + // remove duplicates + else if (o.removeDuplicates) + $('.tag-editor-tag:not(.active)', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); }); + } + input.parent().html(escape(tag)).removeClass('active'); + if (tag != old_tag) update_globals(); + set_placeholder(); + }); + + var pasted_content; + ed.on('paste', 'input', function(e){ + $(this).removeAttr('maxlength'); + pasted_content = $(this); + setTimeout(function(){ split_cleanup(pasted_content); }, 30); + }); + + // keypress delimiter + var inp; + ed.on('keypress', 'input', function(e){ + if (o.delimiter.indexOf(String.fromCharCode(e.which))>=0) { + inp = $(this); + setTimeout(function(){ split_cleanup(inp); }, 20); + } + }); + + ed.on('keydown', 'input', function(e){ + var $t = $(this); + + // left/up key + backspace key on empty field + if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret() || e.which == 8 && !$t.val()) { + var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag'); + if (prev_tag.length) prev_tag.click().find('input').caret(-1); + else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click(); + return false; + } + // right/down key + else if ((e.which == 39 || !o.autocomplete && e.which == 40) && ($t.caret() == $t.val().length)) { + var next_tag = $t.closest('li').next('li').find('.tag-editor-tag'); + if (next_tag.length) next_tag.click().find('input').caret(0); + else if ($t.val()) ed.click(); + return false; + } + // tab key + else if (e.which == 9) { + // shift+tab + if (e.shiftKey) { + var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag'); + if (prev_tag.length) prev_tag.click().find('input').caret(0); + else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click(); + // allow tabbing to previous element + else { + el.attr('disabled', 'disabled'); + setTimeout(function(){ el.removeAttr('disabled'); }, 30); + return; + } + return false; + // tab + } else { + var next_tag = $t.closest('li').next('li').find('.tag-editor-tag'); + if (next_tag.length) next_tag.click().find('input').caret(0); + else if ($t.val()) ed.click(); + else return; // allow tabbing to next element + return false; + } + } + // del key + else if (e.which == 46 && (!$.trim($t.val()) || ($t.caret() == $t.val().length))) { + var next_tag = $t.closest('li').next('li').find('.tag-editor-tag'); + if (next_tag.length) next_tag.click().find('input').caret(0); + else if ($t.val()) ed.click(); + return false; + } + // enter key + else if (e.which == 13) { + ed.trigger('click', [$t.closest('li').next('li').find('.tag-editor-tag')]); + + // trigger blur if maxTags limit is reached + if (o.maxTags && ed.data('tags').length >= o.maxTags) ed.find('input').blur(); + + return false; + } + // pos1 + else if (e.which == 36 && !$t.caret()) ed.find('.tag-editor-tag').first().click(); + // end + else if (e.which == 35 && $t.caret() == $t.val().length) ed.find('.tag-editor-tag').last().click(); + // esc + else if (e.which == 27) { + $t.val($t.data('old_tag') ? $t.data('old_tag') : '').blur(); + return false; + } + }); + + // create initial tags + var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex); + for (var i=0; i= o.maxTags) break; + var tag = $.trim(tags[i].replace(/ +/, ' ')); + if (tag) { + if (o.forceLowercase) tag = tag.toLowerCase(); + tag_list.push(tag); + ed.append('
    22.  '+o.delimiter[0]+'
      '+escape(tag)+'
    23. '); + } + } + update_globals(true); // true -> no onChange callback + + // init sortable + if (o.sortable && $.fn.sortable) ed.sortable({ + distance: 5, cancel: '.tag-editor-spacer, input', helper: 'clone', + update: function(){ update_globals(); } + }); + }); + }; + + $.fn.tagEditor.defaults = { + initialTags: [], + maxTags: 0, + maxLength: 50, + delimiter: ',;', + placeholder: '', + forceLowercase: true, + removeDuplicates: true, + clickDelete: false, + animateDelete: 175, + sortable: true, // jQuery UI sortable + autocomplete: null, // options dict for jQuery UI autocomplete + + // callbacks + onChange: function(){}, + beforeTagSave: function(){}, + beforeTagDelete: function(){} + }; +}(jQuery)); diff --git a/static/libjs/jQuery-tagEditor/jquery.tag-editor.min.js b/static/libjs/jQuery-tagEditor/jquery.tag-editor.min.js new file mode 100644 index 00000000..1d083d24 --- /dev/null +++ b/static/libjs/jQuery-tagEditor/jquery.tag-editor.min.js @@ -0,0 +1,3 @@ +// jQuery tagEditor v1.0.20 +// https://github.com/Pixabay/jQuery-tagEditor +!function(t){t.fn.tagEditorInput=function(){var e=" ",i=t(this),a=parseInt(i.css("fontSize")),r=t("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:i.css("fontSize"),fontFamily:i.css("fontFamily"),fontWeight:i.css("fontWeight"),letterSpacing:i.css("letterSpacing"),whiteSpace:"nowrap"}),l=function(){if(e!==(e=i.val())){r.text(e);var t=r.width()+a;20>t&&(t=20),t!=i.width()&&i.width(t)}};return r.insertAfter(i),i.bind("keyup keydown focus",l)},t.fn.tagEditor=function(e,a,r){function l(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}var n,o=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(o.dregex=new RegExp("["+o.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),l=i.data("options"),n=i.next(".tag-editor");if("getTags"==e)s.push({field:i[0],editor:n,tags:n.data("tags")});else if("addTag"==e){if(l.maxTags&&n.data("tags").length>=l.maxTags)return!1;t('
    24.  '+l.delimiter[0]+'
    25. ').appendTo(n).find(".tag-editor-tag").html('').addClass("active").find("input").val(a).blur(),r?t(".placeholder",n).remove():n.click()}else"removeTag"==e?(t(".tag-editor-tag",n).filter(function(){return t(this).text()==a}).closest("li").find(".tag-editor-delete").click(),r||n.click()):"destroy"==e&&i.removeClass("tag-editor-hidden-src").removeData("options").off("focus.tag-editor").next(".tag-editor").remove()}),"getTags"==e?s:this}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",function(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){try{var a=getSelection(),r="BODY"==document.activeElement.tagName?t(a.getRangeAt(0).startContainer.parentNode).closest(".tag-editor"):0}catch(e){r=0}if(a.rangeCount>0&&r&&r.length){var l=[],n=a.toString().split(r.prev().data("options").dregex);for(i=0;i
      '+o.placeholder+"
      ")}function i(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(o.delimiter[0])),i||a!=c.toString()&&o.onChange(r,s,c),e()}function a(e){for(var a,n=e.closest("li"),d=e.val().replace(/ +/," ").split(o.dregex),g=e.data("old_tag"),f=c.slice(0),h=!1,u=0;u
       '+o.delimiter[0]+'
      '+l(v)+'
      '),o.maxTags&&f.length>=o.maxTags)){h=!0;break}e.attr("maxlength",o.maxLength).removeData("old_tag").val(""),h?e.blur():e.focus(),i()}var r=t(this),c=[],s=t("
        ').insertAfter(r);r.addClass("tag-editor-hidden-src").data("options",o).on("focus.tag-editor",function(){s.click()}),s.append('
      •  
      • ');var d='
      •  '+o.delimiter[0]+'
      • ';s.click(function(e,i){var a,r,l=99999;if(!window.getSelection||""==getSelection())return o.maxTags&&s.data("tags").length>=o.maxTags?(s.find("input").blur(),!1):(n=!0,t("input:focus",s).blur(),n?(n=!0,t(".placeholder",s).remove(),i&&i.length?r="before":t(".tag-editor-tag",s).each(function(){var n=t(this),o=n.offset(),c=o.left,s=o.top;e.pageY>=s&&e.pageY<=s+n.height()&&(e.pageXa&&(l=a,i=n))}),"before"==r?t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click():"after"==r?t(d).insertAfter(i.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1):!1)}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var a=t(this).closest("li"),l=a.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,l.text())===!1?!1:(l.addClass("deleted").animate({width:0},o.animateDelete,function(){a.remove(),e()}),i(),!1)}),o.clickDelete&&s.on("mousedown",".tag-editor-tag",function(a){if(a.ctrlKey||a.which>1){var l=t(this).closest("li"),n=l.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,n.text())===!1?!1:(n.addClass("deleted").animate({width:0},o.animateDelete,function(){l.remove(),e()}),i(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).text(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),r=parseInt(i.length*a),n=t(this).html('').addClass("active").find("input");if(n.data("old_tag",i).tagEditorInput().focus().caret(r),o.autocomplete){var c=t.extend({},o.autocomplete),d="select"in c?o.autocomplete.select:"";c.select=function(e,i){d&&d(e,i),setTimeout(function(){s.trigger("click",[t(".active",s).find("input").closest("li").next("li").find(".tag-editor-tag")])},20)},n.autocomplete(c)}}return!1}),s.on("blur","input",function(d){d.stopPropagation();var g=t(this),f=g.data("old_tag"),h=t.trim(g.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(h){if(h.indexOf(o.delimiter[0])>=0)return void a(g);if(h!=f)if(o.forceLowercase&&(h=h.toLowerCase()),cb_val=o.beforeTagSave(r,s,c,f,h),h=cb_val||h,cb_val===!1){if(f)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}else o.removeDuplicates&&t(".tag-editor-tag:not(.active)",s).each(function(){t(this).text()==h&&t(this).closest("li").remove()})}else{if(f&&o.beforeTagDelete(r,s,c,f)===!1)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}g.parent().html(l(h)).removeClass("active"),h!=f&&i(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){a(g)},30)});var f;s.on("keypress","input",function(e){o.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){a(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!o.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags||t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!o.autocomplete&&40==e.which)&&i.caret()==i.val().length){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");if(a.length)a.click().find("input").caret(0);else{if(!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags)return r.attr("disabled","disabled"),void setTimeout(function(){r.removeAttr("disabled")},30);t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}return!1}var l=i.closest("li").next("li").find(".tag-editor-tag");if(l.length)l.click().find("input").caret(0);else{if(!i.val())return;s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which)return s.trigger("click",[i.closest("li").next("li").find(".tag-editor-tag")]),o.maxTags&&s.data("tags").length>=o.maxTags&&s.find("input").blur(),!1;if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});for(var h=o.initialTags.length?o.initialTags:r.val().split(o.dregex),u=0;u=o.maxTags);u++){var v=t.trim(h[u].replace(/ +/," "));v&&(o.forceLowercase&&(v=v.toLowerCase()),c.push(v),s.append('
      •  '+o.delimiter[0]+'
        '+l(v)+'
      • '))}i(!0),o.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){i()}})})},t.fn.tagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,removeDuplicates:!0,clickDelete:!1,animateDelete:175,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery); \ No newline at end of file diff --git a/static/libjs/jQuery-tagEditor/readme.md b/static/libjs/jQuery-tagEditor/readme.md new file mode 100644 index 00000000..8990f89d --- /dev/null +++ b/static/libjs/jQuery-tagEditor/readme.md @@ -0,0 +1,119 @@ +jQuery-tagEditor +================ + +A powerful and lightweight tag editor plugin for jQuery. + +Compatible with jQuery 1.7.0+ in Firefox, Safari, Chrome, Opera, Internet Explorer 8+. IE7 technically works, but no care has gone into CSS/layout bugs. +Released under the MIT License: http://www.opensource.org/licenses/mit-license.php + +This plugin was developed by and for [Pixabay.com](https://pixabay.com/) - an international repository for sharing free public domain images. +We have implemented this plugin in production and we share this piece of software - in the spirit of Pixabay - freely with others. + +## Demo and Documentation + +https://goodies.pixabay.com/jquery/tag-editor/demo.html + +## Features + +* Lightweight: 8.5 kB of JavaScript - less than 3.2 kB gzipped +* Edit in place tags +* Intuitive navigation between tags with cursor keys, Tab, Shift+Tab, Enter, Pos1, End, Backspace, Del, and ESC +* Optional jQuery UI sortable +* Optional jQuery UI autocomplete +* Copy-paste or delete multiple selected tags +* Duplicate tags check +* Custom delimiter/s +* Placeholder +* Custom style for faulty tags +* Public methods for reading, adding and removing tags + destroy function +* Callbacks +* Allows tabindex for form navigation +* Graceful degradation if JavaScript is disabled + +## Changelog + +### Version 1.0.20 - 2016/01/30 + +* Fixed #62: tagEditor is blocking key events on other input and textarea elements on page. + +### Version 1.0.19 - 2015/12/02 + +* Fixed #60: Tag editor fails to handle HTML operator chars. + +### Version 1.0.18 - 2015/08/12 + +* Pull #43: Escape HTML special characters on input. + +### Version 1.0.17 - 2015/07/14 + +* Allow beforeTagSave() to return `false` for discarding certain tag values. + +### Version 1.0.16 - 2015/07/01 + +* Fix #5, #35, #37, #38: "TypeError: owner is null" backspace browser history issue. + +### Version 1.0.15 - 2015/05/24 + +* Fix #31, #33, #34: Added maxTags, removeDuplicates, and animateDelete options. + +### Version 1.0.14 - 2015/04/05 + +* Fix #24: Auto-close tag after selecting autocomplete suggestion by mouse click. + +### Version 1.0.13 - 2015/01/26 + +* Fix #9: Added bower support. + +### Version 1.0.12 - 2015/01/16 + +* Fix #17: Make use of tabindex for form navigation. + +### Version 1.0.11 - 2015/01/08 + +* Use beforeTagSave return value for overwriting new tags. + +### Version 1.0.10 - 2015/01/04 + +* Fix for IE8 + +### Version 1.0.9 - 2014/12/17 + +* Optimized internal input autogrow function. + +### Version 1.0.8 - 2014/12/14 + +* Added bower.json file. + +### Version 1.0.7 - 2014/11/26 + +* Removing accursoft's caret plugin (http://code.accursoft.com/caret) from tagEditor source (and adding caret as a dependency). + +### Version 1.0.6 - 2014/10/22 + +* Fixed: Detection for selected field (.tag-editor) on backspace/delete keypress failed in some cases. + +### Version 1.0.5 - 2014/09/30 + +* Merged pull - Added logic for selected field to be .tag-editor only: + https://github.com/jegarts/jQuery-tagEditor/commit/498435b562d72c3e502863312b0b2ccbb9e80cab + +### Version 1.0.4 - 2014/09/24 + +* Merged pull stop aco.select form calling itself: + https://github.com/jegarts/jQuery-tagEditor/commit/fd0340ba46272290cedc8991f58769945d0fc2c2 + +### Version 1.0.3 - 2014/09/13 + +* Removed unnecessary vendor prefixes in CSS stylesheet. + +### Version 1.0.2 - 2014/07/16 + +* Fixed removal of placeholder after calling addTags. + +### Version 1.0.1 - 2014/07/16 + +* Fixed tagEditor for IE8 and IE7. IE7 still has some obvious layout alignment bugs, that can be fixed by conditional CSS rules. + +### Version 1.0.0-beta - 2014/07/15 + +* First release diff --git a/static/libjs/jQuery-tagEditor/tag-editor.jquery.json b/static/libjs/jQuery-tagEditor/tag-editor.jquery.json new file mode 100644 index 00000000..b39c0017 --- /dev/null +++ b/static/libjs/jQuery-tagEditor/tag-editor.jquery.json @@ -0,0 +1,31 @@ +{ + "name": "tag-editor", + "title": "tagEditor", + "description": "A powerful and lightweight tag editor plugin for jQuery.", + "version": "1.0.20", + "dependencies": { + "jquery": ">=1.7", + "caret": ">=1.3.2" + }, + "keywords": [ + "tags", + "keywords", + "editor", + "ui", + "tagging", + "jQuery" + ], + "author": { + "name": "Simon Steinberger", + "url": "https://pixabay.com/users/Simon/", + "email": "simon@pixabay.com" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/mit-license.php" + } + ], + "homepage": "https://goodies.pixabay.com/jquery/tag-editor/demo.html", + "demo": "https://goodies.pixabay.com/jquery/tag-editor/demo.html" +} \ No newline at end of file diff --git a/static/libjs/jinplace-1.2.1.min.js b/static/libjs/jinplace-1.2.1.min.js new file mode 100644 index 00000000..30f81fa3 --- /dev/null +++ b/static/libjs/jinplace-1.2.1.min.js @@ -0,0 +1,12 @@ +/* + Copyright ? 2013, 2014 the jinplace team and contributors. + MIT Licence */ +(function(d,s,n,g){function k(b,a){var c=this.element=d(b),c=this.elementOptions(c);c.activator=d(c.activator||b);this.opts=c=d.extend({},d.fn.jinplace.defaults,a,c);this.bindElement(c)}var p="type url data loadurl elementId object attribute okButton cancelButton inputClass activator textOnly placeholder submitFunction".split(" ");k.prototype={elementOptions:function(b){function a(a){return"-"+a.toLowerCase()}var c={};d.each(p,function(d,f){c[f]=b.attr("data-"+f.replace(/[A-Z]/g,a))});c.elementId= +b.attr("id");c.textOnly&&(c.textOnly="false"!==c.textOnly);return c},bindElement:function(b){b.activator.off("click.jip").on("click.jip",d.proxy(this.clickHandler,this));var a=this.element;""==d.trim(a.html())&&(a.html(b.placeholder),b.placeholder=a.html())},clickHandler:function(b){b.preventDefault();b.stopPropagation();d(b.currentTarget).off("click.jip").on("click.jip",function(a){a.preventDefault()});var a=this,c=a.opts,e=d.extend({},l,d.fn.jinplace.editors[c.type]);a.origValue=a.element.html(); +a.fetchData(c).done(function(b){b=e.makeField(a.element,b);e.inputField||(e.inputField=b);b.addClass(c.inputClass);var d=q(c,b,e.buttonsAllowed);a.element.html(d);d.on("jip:submit submit",function(b){a.submit(e,c);return!1}).on("jip:cancel",function(b){a.cancel(e);return!1}).on("keyup",function(b){27==b.keyCode&&a.cancel(e)});e.activate(d,b);e.blurEvent(b,d,e.blurAction||(c.okButton?c.cancelButton?g:"jip:cancel":"submit"))})},fetchData:function(b){var a;a=b.data?b.data:b.loadurl?b.loadFunction(b): +b.textOnly?d.trim(this.element.text()):d.trim(this.element.html().replace(/&/gi,"&"));var c=function(a){return a==b.placeholder?"":a};a=d.when(a);return a.pipe?a.pipe(c):a.then(c)},cancel:function(b){this.element.html(this.origValue);b.finish();this.bindElement(this.opts)},submit:function(b,a){var c=this,e,f=d.Deferred().reject();try{e=a.submitFunction.call(g,a,b.value()),e===g&&(e=f)}catch(t){e=f}d.when(e).done(function(d,e,f){c.element.trigger("jinplace:done",[d,e,f]);c.onUpdate(b,a,d)}).fail(function(a, +d,e){c.element.trigger("jinplace:fail",[a,d,e]);c.cancel(b)}).always(function(a,b,d){c.element.trigger("jinplace:always",[a,b,d])})},onUpdate:function(b,a,c){this.setContent(c);b.finish();this.bindElement(a)},setContent:function(b){var a=this.element;b?this.opts.textOnly?a.text(b):a.html(b):a.html(this.opts.placeholder)}};var m=function(b,a){var c={id:b.elementId,object:b.object,attribute:b.attribute};d.isPlainObject(a)?d.extend(c,a):a!==g&&(c.value=a);return c};d.fn.jinplace=function(b){return this.each(function(){d.data(this, +"plugin_jinplace")||d.data(this,"plugin_jinplace",new k(this,b))})};d.fn.jinplace.defaults={url:n.location.pathname,type:"input",textOnly:!0,placeholder:"[ --- ]",submitFunction:function(b,a){return d.ajax(b.url,{type:"post",data:m(b,a),dataType:"text",headers:{"Cache-Control":"no-cache"}})},loadFunction:function(b){return d.ajax(b.loadurl,{data:m(b)})}};var q=function(b,a,c){a=d("
        ").attr("style","display: inline;").attr("action","javascript:void(0);").append(a);c&&r(a,b);return a},r=function(b, +a){var c=function(a,c){b.append(a);a.one("click",function(a){a.stopPropagation();b.trigger(c)})},e=a.okButton;e&&(e=d("").attr("type","button").attr("value",e).addClass("jip-button jip-ok-button"),c(e,"submit"));if(e=a.cancelButton)e=d("").attr("type","button").attr("value",e).addClass("jip-button jip-cancel-button"),c(e,"jip:cancel")},l={makeField:function(b,a){return d("").attr("type","text").val(a)},activate:function(b,a){a.focus()},value:function(){return this.inputField.val()}, +blurEvent:function(b,a,c){if(c&&"ignore"!=c)b.on("blur",function(d){var f=setTimeout(function(){b.trigger(c)},300);a.on("click",function(){clearTimeout(f)})})},finish:function(){}};d.fn.jinplace.editorBase=l;d.fn.jinplace.editors={input:{buttonsAllowed:!0},textarea:{buttonsAllowed:!0,makeField:function(b,a){return d("