Année 2004 - 2005
Julien Bauduf Thibault Normand
Institut Universitaire des Technologies de Nice Licence Professionnelle Administration des Sytêmes en Réseaux
KAROBAS Mise en place d'une infrastucture commerciale et migration technologique.
Durée : 4 Mois Tuteur de stage : M. Michel SYSKA Tuteur d'entreprise : M. Patrice CHEREAU Karobas.fr
1 / 93
Année 2004 - 2005
REMERCIEMENTS
Nous tenons tout d’abord à remercier Monsieur Patrice CHEREAU, Président Directeur Général de la société KAROBAS qui a permis la réalisation notre stage dans sa société.
Puis, les membres du personnel qui nous ont réservés un accueil chaleureux, ce qui a permis de nous intégrer dans l’entreprise et effectuer notre stage de Licence Professionnelle Informatique dans d’excellentes conditions.
Nous remercions tout particulièrement nos maîtres de stage, Monsieur Olivier CHEREAU, Directeur de Production et Monsieur Nicolas VALENTINO, pour l’aide apportée tant dans l’élaboration que dans la réalisation des divers projets.
Nous remercions enfin, Monsieur Michel SYSKA, responsable de la section Administration Systèmes et Réseaux et professeur à l’I.U.T. de Nice – SophiaAntipolis, de s’être assuré du bon déroulement de notre stage.
Karobas.fr
2 / 93
Année 2004 - 2005
INTRODUCTION
Pour le déroulement de notre stage, nous avons été accueillis par l’entreprise Karobas. Cette entreprise ambitieuse désire mettre toutes les chances de son coté afin de réussir. Dans cette optique, l’entreprise nous a sollicités pour effectuer plusieurs tâches en vue de leur apporter des atouts pour pouvoir évoluer sur des bases saines. Tout d’abord soucieuse de sa sécurité, l’entreprise nous a demandé d’entreprendre une restructuration de leur réseau afin de protéger et d’améliorer leurs systèmes informatiques, tant au niveau du réseau interne que de leur serveur externe et les possibilités de communications avec les deux. Puis dans un esprit d’efficacité, la société exprime le besoin de facilité les échanges du personnel au quotidien, de ce fait les nouveaux systèmes de communications et leurs avantages sont à étudier. Et afin de mieux répondre aux futures évolutions, l’entreprise désire remettre à plat la base de données liée à une partie majeure de leur activité, ainsi que le site Internet qui s’y rattache. Au cours de ce mémoire, nous présenterons de manière générale, l’évolution des différents travaux entrepris en nous arrêtant sur les difficultés techniques que nous avons rencontrées. Pour conclure, nous réalisons un bilan de ce stage en tenant compte du travail réalisé.
Karobas.fr
3 / 93
Année 2004 - 2005
PRESENTATION DE L’ENTREPRISE Karobas est une petite société qui modélise des environnements jouables en 3-Dimentions. Les lieux matérialisés sont issue du monde réel ou de projets. Ces simulations
apparaissent
comme
fortement
intéressantes
pour
certaines
constructions dont le résultat peut être présenté avant son aboutissement. Il est possible de citer des exemples, comme le nouvel aéroport de la ville de Marseille ou le futur hôpital de la ville de Nice. Mais l’activité principale est la conception d’un jeu, gratuit et jouable en ligne destiné à devenir à terme une vitrine pour l’entreprise. Il permet à l’entreprise d’y faire connaître son potentiel et toutes ses innovations dans un environnement ludoéducatif qu’y invite les enfants à s’amuser tout en apprenant. On y conduit un ’petit bonhomme’ qui visite et se retrouve face à des scénarios qui lui permettent de découvrir des lieux issus de la région P.A.C.A ainsi que leur histoire, ou de réfléchir sur des situations précises de la vie courante. Pour l’instant disponible en mono utilisateur sur la région P.A.C.A, il existe une cellule de test Multi-Utilisateurs sur la région Rhône-Alpes où s’est créé des partenariats avec des groupes scolaires intéressés par les avantages et les possibilités au niveau de l’éducation.
Karobas.fr
4 / 93
Année 2004 - 2005
Sommaire La sécurité............................................................................................................................................ 6 Protection du réseau de la société du réseau Internet et inversement.............................................. 8 Sécurité des accès distants............................................................................................................. 10 Sécurité des serveurs internes à la société..................................................................................... 12 Centralisation des informations..................................................................................................... 14 L’Informatique au service de la Communication............................................................................... 16 Etude des solutions........................................................................................................................ 17 OpenGroupware........................................................................................................................ 18 MDaemon..................................................................................................................................19 Small Business Server...............................................................................................................20 Choix de la solution....................................................................................................................... 21 Installation et Configuration.......................................................................................................... 22 Le serveur Small Business Server................................................................................................. 28 Le développement : La base de données et le site..............................................................................31 Analyse de la base données éxistante............................................................................................ 32 Analyse des possiblités de migration.............................................................................................32 Méthode d'abstraction à la base de données.................................................................................. 34 Utilisation de la méthode d'abstraction et concepts du site .......................................................... 41 Fonctionnalités du script................................................................................................................44 Systême de traitements automatisés.............................................................................................. 50
Karobas.fr
5 / 93
Année 2004 - 2005
I – La sécurité
Karobas.fr
6 / 93
Année 2004 - 2005
Le réseau de l'entreprise comprends 6 ordinateurs appartenant à la société, et 7 ordinateurs provenants des stagiaires présents dans l'entreprise. L'accès au réseau internet est controllé par l'utilisateur d'un routeur/firewall connecté à un modem ADSL par liason PPPoE (PPP over Ethernet) procurant une connection internet de type débit max 8Mo Wanadoo. Voici la configuration du réseaux de l'entreprise :
fig 1. Connectivité Internet initiale
Cette architecture convient très bien pour une utilisation normale du réseaux internet, mais elle devient limitée lorsque l'on décide de fournir des services à des clients, tels que un accès VPN sécurisé, un serveur de mail, etc ... . Passons maintenant à la configuration du serveur d'application Virtools, elle quasiment identique car elle ne comporte qu'une machine et un routeur firewall de même marque que celui utilisé par le réseaux interne de l'entreprise. Après analyse des necessité du serveur d'application Virtools, il a été retenu qu'il necessite l'ouverture de port bien spécifique tels que : 18005 (TCP) pour la connection client, et 50000 – 50100 (UDP) pour les requêtes de mise à jour du mediacache du client; (le mediacache correspond aux fichiers mis en cache sur l'ordinateur du client, ainsi il execute locallement l'application virtools ce qui evite par ailleurs de devoir retélécharger les fichiers à chaque utilisation). Le serveur d'application héberge deux sites web karobas.net et karobas.fr, tous les deux sur la même machine; karobas.net correspond au site commercial de l'entreprise (c'est sa vitrine commercialle), et karobas.fr : portail d'accès et d'inscription au jeu mis en ligne. Ces deux sites web utilise IIS intégré à Microsoft Windows Server 2003 et son codés en ASP. Pour gérer la quantité d'informations necessaire au bon fonctionnement du jeu et de la gestion Karobas.fr
7 / 93
Année 2004 - 2005 des utilisateurs, le serveur héberge une base de données de type MsSQL regroupant base de données du jeu et des sites. Protection du réseau de la société du réseau Internet et inversement : Il est évident que le serveur d'application représente un plus gros risque face aux attaques événtuelles de personnes mal intentionnées; pour cela, il a fallut mettre en place un firewall dédié en amont du serveur d'application afin de rendre plus sûr les connections et d'avoir des traces d'activités illicites. Nous avons décidé de mettre en place deux firewalls (un pour la société dans le but de repondre aux attentes, et un pour le serveur d'application); nous avons dans un premier temps chercher des solutions embarquées autonomes, mais cela représentait un surcout trop elevé. La société possédait de vieux ordinateurs tels que des pentium-1 100 Mhz, pentium-2 266, etc ; nous avons ainsi opté pour un firewall léger embarqué sur une machine dédiée : IPCop (http://www.ipcop.org) basée sur LFS [ Linux from Scratch ], Smoothwall basée sur Red Hat(http://www.smoothwall.com), M0n0Wall basée sur une BSD. Après analyse de chacune de ces distributions, nous avons retenu IPCop car c'est une distribution qui offre de nombreux avantages tels que l'extensibilité par addons et plugins divers, une interface de gestion clair par site web, surtout par la faible consommation de ressources pour les fonctionnalités offertes. La mise en place du firewall IPCop de la société a été très simple, tout d'abord installation de la distribution à partir d'un CD (40 Mo), execution et installation par assistant sous shell unix. La distribution fonctionne sur le principe de modèle de réseau, ainsi sont nommés les réseaux par un code de couleur RED (rouge) pour l'interface extèrieure, ORANGE pour l'interface DMZ (Zone d'accès internet controllée c'est en général dans cette zone que l'on place les serveurs réseaux), BLUE (bleu) pour l'interface WiFi, et GREEN (vert) pour le réseaux interne. Dans notre cas, la configuration RED + GREEN était la mieux adaptée : RED, interface connectée au modem Alcatel et GREEN connectée au réseau interne. Afin de pouvoir passer le firewall en tête de ligne dans l'optique d'exercer un contrôle d'accès, il a fallut mettre à jour le modem Alcatel SpeedTouch Home en version Pro pour qu'il puisse gérer de lui même la connection, ainsi le modem c'est vu devenir lui
Karobas.fr
8 / 93
Année 2004 - 2005 un modem routeur NAT, ce qui a permis de réduire les problêmes de déconnection ponctuelle rencontrés avec la configuration précédente. Voici la configuration actuelle du réseau de la société :
fig 2. Connectivité internet actuelle
La machine IPCop possède deux interfaces réseaux chacune connectée de part et d'autre du firewall. Ainsi le firewall devient une passerelle Internet, il ne gère pas la connection internet car elle est autogérée par le modem ST-Pro, c'est comme si le firewall était un pont entre deux réseaux distincts. Afin de rendre plus agréable la navigation et d'economiser de la bande passante sortante et entrante, le firewall a été pourvu d'un système de Proxy Cache permettant de mettre en cache les pages les plus visitées afin d'accélérer la navigation. En effet, lorsque l'utilisateur fait une requête vers un site internet, il passe par sa passerelle internet (qui est le firewall je rappel), passe de manière transparente par Squid qui va aller à la place de l'utilisateur chercher la page puis la mettre en cache, et enafin re-expédier le contenu eventuellement traité / filtré à l'utilisateur. Lorsque l'utilisateur redemande la même page, il effectue la même procedure à un détail près : c'est le proxy cache qui réponds directement et non le site web extérieur; par conséquent cela permet d'économiser des requêtes, du temps et de la bande passante. Pour rendre la configuration transparente aux yeux de l'utilisateur, le proxy cahce est configuré en mode transparent, c'est à dire que tous les paquets à destination du port TCP 80 (HTTP) sont redirigés vers Squid (par défaut 3128 TCP); pour mettre en place cela il suffit d'ajouter une règles dans le firewall à l'aide de l'outil de configuration de firewall iptables. Toujours dans le but d'accélerrer la navigation et donc la satisfaction de l'utilisateur, le firewall possède un Karobas.fr
9 / 93
Année 2004 - 2005 cache DNS, permettant d'accélérer les réponses au requêtes de résolution de nom, il fonctionne sur le même principe que Squid avec le cache HTTP. Concernant les règles de base du firewall, nous avons adopté la régle la plus sécurisée qui dit « Tout ce qui n'est pas explicitement autorisé est interdit », c'est à dire le firewall bloque tout le trafic sauf ce qui est autorisé à passer dans le sens RED vers GREEN (internet vers réseau local), dans l'autre sens tout est autorisé ormis l'IP-spoofing; les paquets ayant une adresse source externe provenant de l'interieur sont automatiquement filtrés; comme les paquets provenant de réseaux privés provenant de l'extérieur. Pour concerver, des traces d'attaques eventuelles, le firewall est equipé d'un système de detection d'intrusion appelé Snort, il fonctionne sur le principe de règle de detections prédéfini permettant d'identifier une attaque eventuelle; il ne réagit pas aux attaques, il enregistre simplement les activités jugées étranges du réseaux; il permet d'avoir des informations sur ce qu'il transite sur le réseau. Sécurité des accès distants : Par la suite, nous verrons qu'il a été necessaire de mettre en place un système de contrôle permettant l'accès par VPN (Virtual Private Network), qui est un système utilisé pour rallier deux réseaux de plages d'adresses différentes à un même réseau appellé dans ce cas VPN N2N (pour Network to Network), c'est utilisé pour la communication entre deux routeurs qui doivent ainsi gérer 3 plages d'adresses différentes : 2 pour les adresses des réseaux, plus une pour le réseau VPN. Un VPN peut être une solution à l'accès d'un ordinateur à des ressources comprises dans un réseaux interne, il est appelé dans ce cas S2N (pour system to network ou RoadWarrior); ainsi la personne authentifiée et connectée fait partie intégrante du réseau interne de la société alors qu'elle peut se trouver chez elle. Le problème majeur du VPN est que les classes d'adresses doivent être différentes de chaque coté du VPN par conséquent, il a été nécessaire de changer la classe d'adresse du réseau interne; nous avons choisi une adresse privée de classe B pour un usage en classe C. Voici le schéma d'implentation du VPN dans le réseau de l'entreprise:
Karobas.fr
10 / 93
Année 2004 - 2005
fig 3. Implentation du VPN
Comme il est indiqué sur le schéma, nous avons procédé à l'installation de la solution OpenVPN, qui est un programme basé sur les liasons TLS / SSLv3 contrairement au VPN habituels qui utilise un cryptage IPSec sur un mode de transfert TCP (appelé en capsulation TCP) alors que OpenVPN utilise le mode de connection UDP (appelé mode non connecté), il est baucoup plus rapide de ce fait, d'après des mesures avec un P2 266, on atteind un taux de transfert de 1,45 Mo/s avec une clé de 1024bits contre 400 Ko/s avec une solution IPSec ce qui est très raisonnable du fait que l'on utilisera jamais le taux de transfert maximal sachant les limitations de la ligne Internet. OpenVPN est basé sur un mode client / serveur, c'est à dire qu'il est necessaire d'installer un serveur accessible en écoute (peut être multi – client ou pas, peut fonctionner en mode P2P) pour que les clients puissent utiliser le VPN. La phase d'authentification s'execute sur un canal crypté basé sur TLS avec un clé variable, pour s'identifier un client doit posséder
un certificat personnel et valide auprès du
serveur VPN, à tout cela peut s'ajouter un mcanisme de chiffrement simple ou complexe : va du Blowfish, à DSA en passant par CAST5-CBC. Tout en sachant que la clé de connection est renouvellée tous les intervalles définis (par défaut 60 minutes). OpenVPN représente une solution de sécurité pratique sûre et simple à mettre en oeuvre. Seule ombre au tableau la différence de classe d'adresse mais ce défaut est commun avec toutes les technologies VPN existantes à l'heure actuelle.
Karobas.fr
11 / 93
Année 2004 - 2005
Sécurité des serveurs internes à la société : Suite à l'installation de la suite groupware, nous nous sommes aperçu que le firewall était mal dimensionné pour les besoins de la société, par conséquent, il a été necessaire de renouveler la machine sur lequel tournait la distribution dédiée IPCop. Suite à la rencontre de quelques problèmes concernant la configuration d'une application, en l'occurence Squid en mode Reverse proxy afin de masquer et contrôler la connection au webmail du logiciel de groupware retenu, nous avons choisi d'installer une distribution linux traditionnelle. Notre choix c'est porté sur le système Gentoo; pourquoi Gentoo ? Pour plusieurs raisons, l'habitude de ce système, mais aussi l'adaptabilité extrème et la sureté du système, en effet nous avons mis en place le mode de sécurité Hardened de Gentoo, qui est un noyau incluant de nombreux dispositifs anti-piratage, et contrôle du système. Nous avons choisi d'activer les fonctionnalités SSP (Smash Stack Protector) et PIC permmettant la protection des executables et piles d'execution pour eviter ainsi la première raison de succès de piratage, les attaques par Buffer Overflow (Dépassement de capacité). Nous avons mis en place les mêmes logiciels installés sur IPCop tels que Squid, DHCPD, DnsMasq, Snort. Concernant la facilité d'administration, il est clair que Gentoo est baucoup plus difficile à configurer en passerelle internet qu'une distribution spécialisée / pré-configurée comme IPCop mais l'accent est mis sur la modularité et l'evolutivité du système Gentoo. De plus, Gentoo est réputé très stable quand elle est bien configurée, c'est à dire qu'une fois mis en place, elle ne sera plus un problème, seule ombre les crash matériels. Nous avons procédé à l'installation de Squid en Reverse-Proxy, afin de pouvoir traiter les demandes des clients autorisé à accéder au webmail du groupware. Un transfert de port ne peut suffir car le serveur web fournit d'autres services au réseaux local qui deviennent accessible depuis l'extérieur, par conséquent la mise en place d'un reverse-proxy a été necessaire pour eviter que le client navigue et accède au dossier, pour lui il sera comme 'chrooté', c'est à dire que la racine pour le client sera un dossier du serveur interne.
Karobas.fr
12 / 93
Année 2004 - 2005
Fig 4. Implentation du reverse proxy
Suite à une mise à jour de Exchange 2003, nous avons du changer de programme de rédirection pour finalement installer et configurer Apache en reverseproxy SSL. En effet il y a un problème avec l'authentification intégrée MS. En passant par Apache nous perdons tous les avantages de la mise en cache des pages permettant d'accélérer les requêtes clients. Voici la structure final, du firewall servant à protéger la société des attaques extérieures:
fig 5. Structure du firewall Gentoo (cf annexes)
Karobas.fr
13 / 93
Année 2004 - 2005
Ce schéma disponible en version complète dans les annexes, montre le filtrage entrant et sortant des paquets. En effet, le par-feu utilise la règle : "Tout ce qui n'est pas autorisé et refusé" qui est la règle la plus sécurisée; cette règle est utilisée pour le traffic entrant. Pour le controle et le suivant du traffic sortant, de différent dispositif ont été mis en place tels que : Proxy HTTP, filtrage mail (Antispam / Antivirus), une passerelle de connection SSL. Ce systême nous a permis de tracer l'activité d'un virus présent dans la société à notre arrivée et non detecté par les système anti-virus client. Centralisation des informations : Afin de palier aux problèmes d'accès des utilisateurs en groupe de travail, car les droits sont multiples et stockés sur chaque ordinateurs clients, nous avons mis en place un domaine Microsoft Active Directory 2003 dirigé par un contrôleur de domaine primaire Windows 2003 Server. Cette architecture permet de centraliser les accès aux ressources, périphériques, et rends le réseaux extensible à souhait; il suffit d'inscrire la machine au domaine, elle sera configurée automatiquement, les logiciels manquants seront installés à la connection de l'utilisateur. Par exemple, un utilisateur non connecté au domaine doit à chaque accès à une ressource du domaine s'identifier comme étant un utilisateur du domaine, alors que dans le cas d'un passage complet de l'ordinateur l'authentification s'effectue dès l'ouverture de session, par identification sur le contôleur de domaine. Par la suite, nous verrons l'utilité du domaine lié à la solution groupware retenue. Le passage en domaine sera expliqué de manière plus précise dans la prochaine partie traitant des solutions GroupWare étudiées. Le domaine apporte donc la centralisation des informations utilisateurs, de l'administration; en effet un administrateur du domaine est administrateur sur chaque machines clientes. Par ailleurs, la société a émis un voeux à propos des droits d'accès sur chaque machines, nous avons du mettre en place une politique qui énonce qu'un utilisateur est un simple utilisateur du domaine mais il doit être administrateur local de sa machine pour garder la possibilité de pouvoir contrôler les applications installées et à
Karobas.fr
14 / 93
Année 2004 - 2005 installer sur sa propre machine. Pour cela chaque compte utilisateur du domaine possède un alias local sur la machine de l'utilisateur comme étant Administrateur de sa machine.
Nous venons de mettre en place un réseau et des politiques d'accès plus sécurisés que dans la structure antérieure. Certes nous devons nous attendre à quelques plaintes concernant ces restrictions mais "on a pas rien sans rien !". La mise en place des firewalls a constitué une étape importante de l'évolution de la sécurité : maintenant nous filtrons les attaques sur les services réseaux ouvert aux public (IIS, FTP, ...), nous concervons des traces d'activités par toute une série de logs et audit système.
Nous allons traiter maintenant de l'objectif principal du stage qui était de mettre en place une solution de groupware dans la société afin de coordonner les activités exterieures des commerciaux (prises de rdv, demande reunion, email de suivi, etc ...). Cette étape a constitué et constitu encore une somme de travail hebdomadère, du à l'autoformation à la suite, à la formation des utilisateurs, l'administration, la maintenance du serveur.
Karobas.fr
15 / 93
Année 2004 - 2005
II – L’Informatique au service de la Communication
Karobas.fr
16 / 93
Année 2004 - 2005 L’un des objectifs, est la mise en place au sein de l’entreprise d’un système collaboratif. L’une des ces principales fonctionnalités est la gestion des calendriers et contacts partagés. Ce système est nécessaire car l’ensemble des collaborateurs étant que très rarement réunit, la communication s’avère difficile. Une seule contrainte est donnée : l’interopérabilité avec l’outil Ms Outlook qui est massivement utilisé et obligatoire pour une synchronisation de leurs différents appareils Windows Mobile. Afin de déterminer le logiciel le plus adapté vis-à-vis de l’entreprise, nous avons testé quelques grands noms dans le domaine Groupware. Ce qui nous a permis d’obtenir les avantages et les inconvénients de chacun, face au désir de l’entreprise et à ses contraintes. Le Groupware renvoie à des applications permettant à des utilisateurs géographiquement éloignés de travailler en équipe. Le travail en équipe peut se concrétiser par le partage d’informations, ou la création et l’échange de données informatisées.
1. Etude des solutions L’étude porte sur trois systèmes, de styles différents, mais avec des fonctionnalités assez rapprochées : OpenGroupare, Mdeamon Groupware et Exchange. Ces trois systèmes, sélectionnés par nos soins, possèdent un système permettant une communication via Outlook.
Karobas.fr
17 / 93
Année 2004 - 2005
OpenGroupware OpenGroupware est l’un des meilleurs Groupware issue du monde libre. La société SKYRIX supporte et commercialise cette application en Allemagne, mais a transformé son code en projet sous licence G.P.L. en 2000. Cette solution est considérée comme la meilleure alternative libre au couple propriétaire Exchange / Outlook. OpenGroupware est une véritable solution de messagerie qui facilite la collaboration au sein de l’entreprise, grâce à des fonctionnalités telles que le partage de documents, d’agendas, de carnets d’adresses et de messagerie. Cet outil, fonctionne sous linux et s’installe assez facilement pour une personne ayant quelques connaissances dans ce système d’exploitation. L’ensemble est développé en Objective C et est basé sur une base de données travaillant sous PostGrey et sur le serveur web Apache. L’accès à ses différentes fonctionnalités s’effectue par un client léger comme Internet Explorer ou Mozilla, ou par des clients lourds tel que Thunderbird ou Outlook, via un connecteur. Le point négatif est que le connecteur Outlook, ZideLook, est payant et reste assez onéreux, dans les 60 € par poste. Zidelook ne gère pas complètement l’ensemble des fonctionnalités d’Outlook comme par exemple la notion de rendez-vous ou de contacts privés. Sinon son administration par interface web reste simple à prendre en mains. Son environnement n’est pas totalement finalisé et le projet reste encore en version bêta, mais dans le futur cette application peut séduire beaucoup de société.
Karobas.fr
18 / 93
Année 2004 - 2005
MDaemon
MDaemon, fonctionnant sous Windows, est un serveur de messagerie professionnel doté des technologies les plus récentes pour le traitement, la sécurisation des communications électroniques, la gestion des comptes utilisateurs et le partage d’informations. Ainsi, elle intègre des services anti-Spam, Anti-Phishing auquel on peut y ajouter un module antivirus disponible dans la gamme. En configuration Internet ou Intranet, MDaemon est une solution complète et entièrement personnalisable : services SMTP, POP, IMAP (version Pro), listes de diffusion, annuaire centralisé, partage de calendriers, de contacts et de tâches, dossiers publics, interface WebMail (WorldClient), messagerie instantanée sécurisée (ComAgent), etc. Ce système s’intègre au système Active Directory si l’on dispose d’un domaine Microsoft, où à différents systèmes L.D.A.P. Sinon, il possède sa propre console d’administration pour la gestion des utilisateurs et de leurs autorisations. Cette console existe sous forme web, appelé WebAdmin.
Sa version professionnelle
intègre la notion de travail collaboratif qui permet de gérer les dossiers Courrier, Calendrier, Contacts et Tâches à partir d'un simple navigateur Web, ou associé au module Outlook Connector. Après quelques tests, cet outil semble être très pratique pour son intégration et son utilisation, mais son connecteur pour Outlook reconnaît des problèmes au niveau de la synchronisation. Il ne gère pas les accès concurrents et ne permet pas d’intégrer par exemple deux rendez-vous simultanés.
Karobas.fr
19 / 93
Année 2004 - 2005
Small Business Server
Beaucoup de petites et moyennes entreprises connaissent une politique de réduction des coûts. Dans cette optique, Microsoft a développé Small Business Server. Ce système basé sur la plateforme 2003 serveur, intègre un ensemble de fonctionnalités adapté à ce type d’entreprise. Small Business Server est composé de : -
Microsoft 2003 Server, système très utilisé dans les entreprises qui permet d’engendrer la sécurité et les avantages de la gestion sous forme de domaine.
-
Echange 2003 Server, une solution de messagerie électronique professionnelle reconnue pour sa simplicité d’administration, et son potentiel.
-
Outlook 2003, produit indispensable du serveur Exchange permettant d’implanter ses fonctionnalités. Il gère et organise des messages électroniques, des planifications, des tâches, des notes, des contacts et d'autres informations de manière centralisée.
-
Windows SharePoint Services, rassemble un ensemble d’outils destiné au travail collaboratif. C’est un moteur de création de site web destiné à la communication de groupe.
Karobas.fr
20 / 93
Année 2004 - 2005
2. Choix de la solution L’ensemble des solutions ont été présenté à l’entreprise, en y évoquant les avantages et les inconvénients de chacune. OpenGroupware n’a pas reçu un très bon accueil, soit disant gratuit, mais enfin de compte payant et cher pour une simple comptabilité avec Outlook. De plus l’installation nécessite un serveur sous linux, qui à part le parfeux, n’est pas considéré comme une priorité. Aucune personne n’ayant beaucoup travaillé avec ce système, son administration en serait difficile. L’application est bien adaptée à Thunderbird mais l’entreprise n’est pas prête pour migrer vers cette solution. MDaemon Professionnel est à un prix avoisinant Small Business Server pour un petit nombre de licences. En effet, ce système est plus intéressant pour une grosse société désirant acquérir un plus grand nombre de licence car le prix des licences supplémentaires est moins élevé que pour le serveur de chez Microsoft. De plus, le connecteur n’étant pas totalement opérationnel, l’entreprise n’est pas près à investir dans un système qui manque de stabilité. Small Business Server est la solution choisie. En effet cette solution, destinée au P.M.E., intègre un serveur de domaine et un service de messagerie évolué pour un coût avoisinant celui d’une simple licence 2003 Server. Ce système nécessite une machine dédiée avec beaucoup de capacité, mais regroupe des fonctionnalités attrayantes comme Outlook Web Access qui rassemble la majorité des possibilités d’Outlook dans un Webmail, ainsi que CompanyWeb qui est un site intranet entièrement modulaire pouvant très facilement s’adapter à l’entreprise. Le point négatif retenu est le coût de la licence supplémentaire qui reste élevé. En conclusion, même si le prix de certains produits est attrayant, la société désire acquérir un produit fiable et réputé afin d’éviter certaines mauvaises surprises après sa mise en place.
Karobas.fr
21 / 93
Année 2004 - 2005
3. Installation et Configuration
L’une des principales possibilités réunissant les trois produits testés reste le serveur mail. Mais l’entreprise utilisant un service externe pour effectuer cette fonctionnalité, la mise en place de la solution s’avère être source de problèmes et de discussions pour une utilisation en fonction des souhaits et des possibilités demandés par l’entreprise. Suite à ces contraintes, notre système a subi quelques étapes pendant sa mise en place avant d’être totalement opérationnel. A notre arrivée, la messagerie de l’entreprise fonctionne tout simplement grâce à un système de relais de mail. Cette technologie est un service offert par le fournisseur du nom de domaine « karobas.fr » (ici le fournisseur est « lerelaisinternet.com »). Il permet de créer des adresses mails « @karobas.fr » directement relayés vers une adresse destinataire de leur choix. En pratique, l’émetteur du message envoie un mail à une adresse du domaine, ce message arrive sur un serveur de l’I.S.P. qui le relaie à l’adresse choisie par le destinataire, ici des comptes créés sur un service tiers « laposte.net ». Le destinataire n’a plus qu’à consulter son adresse « laposte.net » pour découvrir tous les mails qui lui sont destinés. En théorie, ce système fonctionne très bien et décharge l’entreprise de toute gestion de messagerie, seul un problème persiste, un employé de l’entreprise ne peut envoyer de mail avec son adresse « @karobas.fr » car le service laposte.net ne fournit pas de telle possibilité. Pour conserver ce système qui n’impose aucune maintenance au niveau messagerie, le serveur Exchange doit s’adapter.
Karobas.fr
22 / 93
Année 2004 - 2005 Dans un premier temps, le serveur Exchange doit offrir la possibilité de partager l’agenda et les contacts de chacun des employés. Cette fonctionnalité n’oblige pas l’utilisation du service de messagerie dans le cadre de l’externe. L’entreprise souhaite alors conserver son ancien système de gestion des mails « @karobas.fr » par crainte de connaître des problèmes de maintenance. La première installation et configuration du produit est dans l’optique de séparer la messagerie externe de l’interne. De ce fait, l’ensemble de la configuration utilise un nom de domaine de type « karobas.local ». Ce nom de domaine, un peu particulier, est conseillé lors de l’installation du serveur par Microsoft. L’extension .local est suggérée lorsque qu’on ne désire pas contrôler le domaine sur Internet. La solution, comme décrite sur le schéma ci-dessous, ne bouleverse pas l’utilisateur quant à l’utilisation de son service de mail externalisé. Mais l’apparition d’un nouveau compte sous Outlook communicant avec le serveur Exchange offre beaucoup de fonctionnalités non connues pour un utilisateur ne maîtrisant pas le produit. La connexion est immédiate pour un ordinateur fixe présent dans les murs de la société, mais un problème se pose pour les utilisateurs nomades. D’où la mise en place du service OpenVPN sur le Firewall de la société, qui permet à l’utilisateur d’être relié au réseau interne via un tunnel sécurisé. Grâce à son client graphique, les utilisateurs du VPN s’adaptent très bien à cet outil.
Karobas.fr
23 / 93
Année 2004 - 2005
Dès maintenant, les utilisateurs disposent d’un moyen de communication interne. Ce système complètement intégré à l’outil d’organisation Outlook impose une formation aux utilisateurs. Tout d’abord, l’outil s’avère comme la plupart des produits Microsoft assez simple de fonctionnement et très bien adapté au milieu de l’entreprise, mais seulement pour une utilisation courante. Ainsi l’utilisation de prise de rendez-vous en groupe, sous forme de réunion, a été rapidement assimilée après une démonstration individuelle lors de la nouvelle configuration du produit sur chacun des ordinateurs. La configuration passe par la notion de partage, où chaque utilisateur donne les droits qu’il désire à ces collègues. Car dans ce système chaque utilisateur donne un droit entièrement personnalisable à un autre utilisateur en fonction des besoins sur ses contacts et son calendrier, tout en gardant la possibilité de conserver une partie de vie privée. Puis toujours dans le cadre de la formation, chaque cas particulier envisagé par l’utilisateur devient pour nous même un cas de recherche et d’autoformation. Mais suivant une utilisation courante testée par un certain nombre d’utilisateurs, les conclusions s’avèrent être plus ou moins convaincantes. N’est pas remise en question l’utilisation des fonctionnalités mais la difficulté à jongler entre les comptes, soit pour l’externe, soit pour l’interne. Sachant que lors d’un envoi au personnel de l’entreprise le compte « karobas.local » doit être forcément utilisé pour conserver le passage et le filtrage par le serveur Exchange, celui-ci conservant les Karobas.fr
24 / 93
Année 2004 - 2005 notions de rendez-vous et autres possibilités d’Outlook. Ainsi, après des pertes lors d’envoie de mail via le compte « karobas.local » sur les comptes de correspondants externes, ou l’envoi de prise de discutions interne via les comptes « laposte.net », il est obligatoire d’évoluer vers un environnement plus simplifié.
Ainsi pour améliorer le confort des utilisateurs, nous réfléchissons de nouveau sur la configuration du serveur Exchange. Tout en conservant leur système externe de messageries, nous proposons à la société de modifier le domaine du serveur Small Business Server, pour qu’il devienne « karobas.fr ». Ainsi, dans cette configuration le système permet l’envoi de mail via le serveur Exchange à un correspondant extérieur. Le mail est acheminé en possédant
une
adresse
« @karobas.fr »
et
non
comme
anciennement
« @karobas.local ». Afin de permettre la migration du nom de domaine, nous devons veiller à ce que l’ensemble du domaine soit toujours accessible. Car une fois migré, le serveur Small Business Server se comporte comme s’il est le maître du domaine et qu’il possède l’ensemble de sa gestion. Cela provoque le fait, que l’ensemble des éléments portant un nom « .karobas.fr » sur Internet ne sont plus joignable par le réseau interne, car le serveur étant le serveur DNS du domaine, il pense que tout ceux qui portent son nom se trouvent dans son réseau. De ce fait, l’ensemble des noms comme www.karobas.fr, ftp.karobas.fr, qui pointent vers un serveur externe, doivent être inscrit manuellement dans le serveur DNS. De plus, l’ensemble des adresses mails doivent être identique aux différentes adresses mails relayées par le fournisseur du nom de domaine « lerelaisinternet.com » sous peine que le destinataire d’un mail réponde à une adresse inexistante du monde Internet.
Karobas.fr
25 / 93
Année 2004 - 2005
Dans cette configuration, tous les mails conservent le même sens de circulation, les utilisateurs disposent toujours de deux comptes mais un pour la réception depuis l’extérieur, et un pour l’envoi, filtré par le serveur Exchange qui conserve l’ensemble des mails destinés à être redistribués sur le réseau interne. En définitive, le système étant opérationnel, il est alors possible de passer à une autre étape, la mise en place d’un seul compte sous Outlook, celui du serveur Exchange. En fait, le serveur offre la possibilité de ramasser les mails de boîtes externes, et donc récupérer l’ensemble des mails reçus par les utilisateurs provenant des différents comptes mails relais d’adresse « @karobas.fr ». Ce service s’appelle POP3Connector. Le seul désavantage à ce système est qu’une personne nomade, qui n’a pas accès à Internet avec son ordinateur, ne puisse plus consulter ses mails, comme par Webmail, service offert par laposte.net, tant que les mails ne sont pas ramassés par Outlook. Le serveur Exchange possède un outil de ce type Outlook Web Access, qui fonctionne à partir du serveur Web I.I.S. Donc ce système doit être mis en ligne sur Internet. Pour éviter d’exposer le serveur de messagerie et de domaine directement sur Internet, nous décidons de le protéger en installant un Proxy inversé. Ce Proxy permet de servir de passerelle pour aller communiquer avec le site interne de Small Business Server. La solution choisie pour assurer ce travail est le serveur Apache, qui est en version définitive un des seuls programmes pouvant servir comme Proxy Inversé assurant le transport de type S.S.L. du serveur Outlook Karobas.fr
26 / 93
Année 2004 - 2005 Web Access jusqu’au client. Ensuite, le service POP3Connector relève les mails toutes les quinze minutes au minimum, ce qui ai jugé beaucoup trop long. Après quelques recherches, on a découvert PullMail un outil alternatif à POP3Connector. Cet outil gratuit ne relève les mails que par POP3 et non POP-S, et s’utilise en environnement de commande. Donc, on installe et configure Stunnel pour permettre de créer un service fonctionnant sur le port 110 qui implémente la connexion S.S. L. jusqu’à un port POPS distant, dans notre cas le serveur de messagerie GMAIL. Donc, après la conception d’un fichier de commande .bat qui lance PullMail paramétré en fonction des différents comptes mails, et la création d’une tache planifiée exécutant ce script tous les cinq minutes, notre serveur est opérationnel au niveau de la messagerie.
Le fonctionnement est un peu complexe, mais l’entreprise peut continuer d’utiliser son service de messagerie externe même si le serveur Exchange tombe en panne. Karobas.fr
27 / 93
Année 2004 - 2005 4 Le serveur Small Business Server
Le système de messagerie étant opérationnel, l’ensemble des ordinateurs peuvent se joindre au domaine créé par l’environnement 2003 Server. Afin de conserver les habitudes de l’entreprise, chaque utilisateur doit rester administrateur de sa machine pour y installer et configurer l’ensemble des logiciels nécessaires au travail. Donc, lors du passage d’un ordinateur sur le domaine, son utilisateur, inscrit dans l’Active Directory,
est inséré dans le groupe local
administrateur de la machine dont il a la gestion. Il devient administrateur local de sa machine tout en étant utilisateur du domaine. Ayant rejoint le domaine, l’ensemble des utilisateurs ont découvert le système Windows SharePoint Services. Cet intranet appelé « CompanyWeb », très simple à utiliser et à modifier, s’adapte à beaucoup de situations, comme par exemple, un reporting des tâches à effectuer, des forums, calendriers, listing, l’ensemble très modelable pour permettre de communiquer et de s’organiser. Cet outil devient communément utilisé par l’ensemble de la société.
Karobas.fr
28 / 93
Année 2004 - 2005
Au niveau de son administration, Small Business Server possède un système basé lui aussi sur SharePoint. Ce dernier affiche une nouvelle forme de console d’administration différente de celle d’un Server 2003. Au lieu de retrouver au démarrage d’une session, une interface permettant de gérer les rôles du serveur, c'est-à-dire d’installer et de configurer l’ensemble des fonctions du serveur tel que le service Terminal Server ou le service de D.N.S. par exemple, ce système plus abouti permet la gestion entière du serveur tel que l’ensemble des utilisateurs, les différentes stratégies et politiques de groupe. Cette application remplace les principaux éléments lié à la console MMC.
Karobas.fr
29 / 93
Année 2004 - 2005
Pour la protection du système, au lieu d’une consultation des différents journaux d’alertes, le système envoie un mail quotidien qui prévient de l’état général du serveur. Dans ce rapport détaillé, on y consulte par exemple l’espace disque dur restant, l’ensemble des mémoires utilisées, la totalité des journaux qui suivent l’activé quotidienne du serveur.
Karobas.fr
30 / 93
Année 2004 - 2005
En définitive, l’ensemble de ses aides simplifie la configuration et l’administration de ce serveur destiné aux petites sociétés.
Karobas.fr
31 / 93
Année 2004 - 2005
III – Le développement : La base de données et le site
Karobas.fr
32 / 93
Année 2004 - 2005
Le jeu hebergé sur le serveur d'applications necessite une base de données éxécutée par MsSQL. Pour pouvoir s'incrire l'utilisateur doit utiliser un portail d'inscription en ligne disponible à l'adresse http://www.karobas.net; ce site a été codé en ASP par un ancien employé de la société. Pour des raisons financières, la société décide d'abandonner l'utilisation du serveur MsSQL pour passer sur un serveur gratuit MySQL, et de profiter de la migration de la base de données pour changer la technologie du site vers PHP, et d'adapter la base à leur nouveau besoins. Cette migration va entrainer la refonte complète du site Internet et l'adaptation du jeu à la nouvelle base. Analyse de la base données éxistante : La base de données actuelle heberge deux jeux : TaRégionPACA et RhonesSud; en une seule base unique. RhonesSud est un laboratoire de test pour la future version multijoueur du jeu, en effet pour l'instant le jeu (TaRegionPACA) est mono-utilisateur : les utilisateurs d'un même monde ne peuvent communiquer, ni se voirent. Un des problèmes majeurs de cette base de données c'est qu'elle n'a pas été prévu de manière ouverte et extensible, par concéquent au fur et à mesure des modifications, ajout de tables dans la base, nous sommes arrivés à un modèle desorganisé et ne répondant plus aux besoins de la société. (cf schéma de l'ancienne base.) Du fait que la base stocke des informations différentes dans des modèles structurels identiques, les informations deviennent de plus en plus ingérables, par exemple : les utilisateurs de RhonesSud possèdent un champs Activation qui est mis à null pour indiquer qu'il ne sont pas activés, alors que dans TaRégionPACA le champs est à 0 pour indiquer le même état. Il résulte une reconception complète de la base de donnée dans le but de rendre plus claire son utilisation dans le jeu. Analyse des possiblités de migration : Avant de décider du serveur de base données de remplacement, il fallut
Karobas.fr
33 / 93
Année 2004 - 2005 analyser les besoins actuels du jeux, et la possibilté de connectivité du serveur Virtools avec les bases de données disponibles. Dans un premier temps, nous avons étudié si la base utilisait des fonctionnalités spécifiques telles que les déclencheurs (triggers), procédures stockées, liaisons de tables, pour pouvoir adapter la nouvelle base en fonction des possibilités de la base de destination. La base de données n'utilise aucunes contraintes d'intégrité, ni procédures stockées, c'est ainsi que c'est porté notre choix sur la base de données libre MySQL dans sa version stable 4.1.13. La migration des données risque de poser quelques problèmes du fait de la restructuration complète de la base de données. En effet certains champs ont été renommés, déplacés, voir même supprimés du contexte de leur utilisation. Nous avons choisi de mettre en place des contraintes d'intégrité (liaisons Clés étrangères), de réfléchir sur les types de champs à insérer plus adéquat (type Date, Integer, ...) pour assure une intégrité des données; ces mécanismes ont été mis en place par l'utilisation du moteur InnoDB : moteur de base de données permettant les transactions et les liaisons de tables. La mise en place de clès étrangères permet dans un premier temps d'assurer la présence des informations, et d'eviter les insertions orphelines (sans relations) lors de manipulation de suppression, ou de mise à jour d'informations. Dans l'objectif de l'extension futur du jeu vers un jeu mondial, il fallait gérer les différentes collections de caractères par conséquent les informations sont stockées sont forme encodées en UTF-8 qui est transparent pour l'utilisateur de la base de données, et tous ses clients instantanés. Ce codage de caractères permet des stocker des caractères complexes comme ceux de la langue japonaise, choinoises, ou russe. Nous avons noté par ailleurs, un problème de compatibilité avec le serveur d'application virtools dans le module de connection de base. En effet, après une série de tests, le serveur virtools n'accepte pour l'instant qu'unseul type de codage UTF-8 : le cadoge marqué, c'est à dire qu'un bit est resevé pour marqué le codage. Des symptomes comme la suppression de caractères dont la position dans la chaine est un multiple de 4 apparait lorsque l'on utilise des carctère hors table ASCII (par exemple : P_Garçon.nmo -> P_Garçon.nm, la 8eme lettre est supprimée; si on ajoute une lettre 'P_Garçon.nmoa' l'affichage est correcte). Dans un premier temps nous avons pensé que cela venait du pilote MyODBC de connection à la base MySQL;
Karobas.fr
34 / 93
Année 2004 - 2005 mais après analyse approfondie nous en avons déduit que cela venait du controleur ODBC de virtools server. Ce problème est reconnu dans la communauté de Virtools, et sera fixé dans les prochaines versions. Une contrainte technique vient s'ajouter à la migration technologique du serveur dûe au passage vers PHP/MySQL. Le personnel de la société n'étant pas habitué à manipuler ces logiciels, il fallut developper une méthode d'abstraction objet à la base de données dans le but de rapprocher la programmation PHP à celle de Java. Méthode d'abstraction à la base de données : Lors de la conception de la base de données nous avons déduis qu'il avait un ensemble d'objets à gérer, ils sont au nombre de 5 : des utilisateurs, des productions, des quartiers, des missions, et des panneaux. La conception du site utilise le modèle objet de PHP5 pour la programmation. Nous avons conçu une méthode de programmation permettant de s'abstraire de la base de données et de l'utiliser de manière transparente pour le programmeur. En effet, l'accès à la base de données est générée par l'éxécution du script Python permettant de transcrire une base de données MySQL relationnelle en un ensemble de classe PHP écrite par l'exécution du script Python.
Fig 1. Fonctionnement général du script
Le script python est composé de deux parties : la première, un analyseur syntaxique permettant de créer une structure exploitable; et la seconde partie Karobas.fr
35 / 93
Année 2004 - 2005 permmettant la génération des fichiers. Pour le bon fonctionnement du script, il est nécessaire des respecter quelques règles : •
Normalisation des nom des champs : tous les identifiants de la table et des tables étrangères doivent commencer par 'ID_'; par exemple, la table Utilisateur possède un identifiant ID_UTILISATEUR; Tous les noms des champs sont stockés en majuscule pour eviter les confusions sur les logiciels sensible à la casse de caractères.
•
Les clès étrangères doivent posséder leur cardinalité sous forme '[x:y]' dans l'espace de commentaire; cette étape permet de définir si l'objet référant doit gérer une collection de références ou un alias unique.
•
La detection de certains types : pour le type boolean, il est necessaire d'ajouter une marque '[b]' dans le commentaire.
•
Les clés étrangères doivent porter le même nom que l'identifiant de la table qu'il représente, et doivent être positionné en fin de table. (Contrainte optionnelle mais recommandée) Chaque fichiers générés correspond à une table existante de la base de
données, étant donné que les relations entre classes découlent directement de la base de données, les erreurs sont localisées directement dans la conception de celle-ci (sauf erreurs eventuelles de script, dans ce cas toutes les classes sont affectées). Nous allons étudier plus en détail le principe de fonctionnement du script nommée sql2phpclass. Dans un premier temps,
nous avons construit et mis en place la base de
données sur un serveur MySQL, puis à l'aide d'un logiciel tiers ou la commande mysqldump, nous avons créé un fichier script SQL correspondant à la base de donnée récement developpée.
Karobas.fr
36 / 93
Année 2004 - 2005
Exemple de script SQL : CREATE TABLE `continent` ( `ID_CONTINENT` int(10) NOT NULL auto_increment COMMENT 'Identifiant d''un continent', `NOM` varchar(50) default NULL COMMENT 'Nom du continent', `ID_SURVOL3D` int(10) default NULL COMMENT '[0:1] Un continent a un survol', `ID_PLANETE` int(10) NOT NULL default '0' COMMENT '[0:1] Un continent a un survol', PRIMARY KEY (`ID_CONTINENT`), KEY `FK_PLANETE` (`ID_PLANETE`), KEY `ID_SURVOL3D` (`ID_SURVOL3D`), CONSTRAINT `continent_ibfk_1` FOREIGN KEY (`ID_SURVOL3D`) REFERENCES `survol3d` (`ID_SURVOL3D`) ON DELETE NO ACTION, CONSTRAINT `FK_PLANETE` FOREIGN KEY (`ID_PLANETE`) REFERENCES `planete` (`ID_PLANETE`) ON DELETE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Unité d''organisation des continents; InnoDB free: 6144 kB; ';
Ce script ordonne la création d'une table nommée 'continent', contenant 4 champs : 1 identifiant permettant de s'assurer de l'unicité complète de l'enregistrement par le fait qu'il s'auto-augmente à chaque insertion, un champs clé étrangère indiquant dans ce cas qu'un objet 'continent' possède / ou ne possède pas un 'survol3d', enfin un autre champs clé étrangère indiquant qu'un objet 'continent' appartient à un objet 'planete'. Pendant l'execution du script, l'application crée un arbre syntaxique représenté sous forme de tableau imbriqués : fields [ [NOM, TYPE, NULLIFY, DEFAULT_VALUE, COMMENT] ['ID_CONTINENT', 'int', 0, None, 'Identifiant d'un continent'], ['NOM', 'string', 0, 'null', 'Nom du contient'], ['ID_SURVOL3D', 'int', 0, 'null', '[0:1] Un continent a un survol'], ['ID_PLANETE', 'int', 0, 0, '[1:1] Un continent appartient à une planète'] ] A cette étape, le script possède les informations nécessaires sur les champs de la table.
Karobas.fr
37 / 93
Année 2004 - 2005
fk_feilds [ [NOM, REF_TABLE, DEST_ID] ['ID_SURVOL3D', 'SURVOL3D', 'ID_SURVOL3D'], ['ID_PLANETE', 'PLANETE', 'ID_PLANETE'] ] Nous venons de créer une structure contenant les références aux tables externes pour les inclusions de classes dans les fichiers générés. Par la suite nous associons dans une structure de type dictionnaire (HashMap : clé => valeur), les informations traitées précédements. Dict = [ “PLANETE” : [fields, fk_feilds], “CONTINENT” : [fields, fk_feilds], ....... (jusqu'à la fin du fichier script SQL) ] Une fois la structure créée, il suffit de parcourir celle-ci pour générer les fichiers classes associés. Les clés du dictionnaire correspondent aux noms des classes à générer, ainsi chaque classes possèdent des propriétés, des accesseurs, des méthodes générées en fonction de l'analyse du script. Exemple d'une classe générée :
$id_continent = -1; $nom = ''; $id_survol3d = 0; $id_planete = 0; $survol3d = null; $planete = null;
/***************************************** * Constructeurs
Karobas.fr
38 / 93
Année 2004 - 2005 *****************************************/ function Continent($id = -1) { $this->id_continent = (int)$id; } /***************************************** * Accesseurs *****************************************/ function function function function
getId_continent() { return (int)$this->id_continent; } getNom() { return (string)$this->nom; } getId_survol3d() { return (int)$this->id_survol3d; } getId_planete() { return (int)$this->id_planete; }
function getSurvol3d() { if($this->id_survol3d != 0) { if($this->id_survol3d != null) { unset($this->survol3d); } $this->survol3d = new Survol3d((int)$this->id_survol3d); $this->survol3d->fill(); return $this->survol3d; } } function getPlanete() { if($this->id_planete != 0) { if($this->id_planete != null) { unset($this->planete); } $this->planete = new Planete((int)$this->id_planete); $this->planete->fill(); return $this->planete; } } function getPaysCol($init = 0) { $ret = array();
}
$sql = "SELECT ID_PAYS FROM pays WHERE ID_CONTINENT='".$this->id_continent."'"; $res = DB_query($sql); while($row = DB_fetchArray($res)) { $temp = new pays((int)$row[0]); $temp->setId_continent($this->id_continent); if($init != 0) $temp->fill(); $ret[] = $temp; } if(count($ret) == 1) return $temp; else return $ret;
function setNom($newvalue) { if($newvalue != $this->nom && !empty($newvalue)) { $this->nom = (string)$newvalue; return (string)mysql_escape_string($newvalue); } else return (string)''; } function setId_survol3d($newvalue) { if($newvalue != $this->id_survol3d && !empty($newvalue)) { $this->id_survol3d = (int)$newvalue; return (int)mysql_escape_string($newvalue); } else return (int)0;
Karobas.fr
39 / 93
Année 2004 - 2005 } function setId_planete($newvalue) { if($newvalue != $this->id_planete && !empty($newvalue)) { $this->id_planete = (int)$newvalue; return (int)mysql_escape_string($newvalue); } else return (int)0; } /***************************************** * Méthodes *****************************************/ /** * Ordonne le remplissage des propriétés de la classe en utilisant les champs de la table ville3d * @param int L'identifiant de la table continent soit ID_CONTINENT. */ function fill($autofill = 0) { if($this->id_continent < 0) return -1; $sql = "SELECT * FROM continent WHERE ID_CONTINENT='".(int)$this->id_continent."'"; $res = DB_query($sql); if(DB_numRows($res) == 1) { $row = DB_fetchArray($res); $this->nom = (string)$row['NOM']; $this->id_survol3d = (int)$row['ID_SURVOL3D']; $this->id_planete = (int)$row['ID_PLANETE']; if($autofill != 0) { /* Un continent a un survol */ if($this->id_survol3d != 0) { $this->survol3d = new Survol3d((int)$this->id_survol3d); $this->survol3d->fill($autofill); } if($this->id_planete != 0) { $this->planete = new Planete((int)$this->id_planete); $this->planete->fill($autofill); } }
}
} /** * Ordonne la synchronisation des propriétés de la classe en cours d'utilisation avec la base de données. */ function commit() { if($this->id_continent == -1 || empty($this->id_continent)) { $sql = "INSERT INTO continent VALUES ('' ,'".$this->nom."' ,'".$this>id_survol3d."' ,'".$this->id_planete."')"; } else $sql = "UPDATE continent SET NOM = '".$this->nom."', ID_SURVOL3D = '".$this>id_survol3d."', ID_PLANETE = '".$this->id_planete."' WHERE ID_CONTINENT = '" . $this>id_continent . "'";
}
$res = DB_query($sql); $this->id_continent= (int)DB_insertId($res); return $res;
/** * Ordonne la suppression de l'enregistrement de la base de données. */ function _delete() {
Karobas.fr
40 / 93
Année 2004 - 2005 $sql = "DELETE FROM continent WHERE ID_CONTINENT='".$this->id_continent."'"; $res = DB_query($sql); return $res; } /***************************************** * Exportation *****************************************/ function toXML() { if($this->id_continent == -1 || empty($this->id_continent)) return null; $xmlstr $xmlstr $xmlstr $xmlstr $xmlstr
= "
"; .= "\t<nom>".$this->nom."\n"; .= "\t".$this->id_survol3d."\n"; .= "\t".$this->id_planete."\n"; .= "";
return $xmlstr; }
} ?>
Voici un exmple complet d'une classe générée par l'execution du script sur une table nommée 'continent'. Chaque classes possèdent des méthodes communes telles que : •
commit(), utilisée pour inserer les informations relatives à l'objet instancié dans la base de données;
•
update(), utilisée pour mettre à jour les informations de la base;
•
toXML(), méthode de réalisation de l'arbre XML associé à la classe;
•
_delete(), qui ordonne la suppression de l'enregistrement associé et de toutes relations de la base.
Le script génère des méthodes en utilisant une nomenclature bien précise : •
get + Nom du champs : Accesseur en lecture du champs selectionné, retourne la valeur transtypé dans son de base ( int, string, ... );
•
set + Nom du champs : Accesseur en écriture du champs selectionné, chaque valeur est verifiée avant insertion plus éviter les failles de sécurité comme les sql injections;
•
get + Obj relatif + Col : Retourne la collection d'objet étrangers à l'objet en cours;
•
create + Objet relatif : Permet la création d'un objet relatif à la classe en cours; Concernant la protection des données de la base de données, vous pouvez voir
dans les accesseurs que tous les types sont forcés et protégés par l'execution Karobas.fr
41 / 93
Année 2004 - 2005 intermédiaire de la méthode PHP mysql_escape_string($string), utiliser pour protéger les caractères d'echappement, les quotes qui peuvent aparraitre dans le contenu des variables. Chaque variable est transtypé vers son vrai type afin d'eviter qu'une chaine de caratère soit affectée à un entier, ce qui est possible en php car tous les types sont dynamique mais peut représenter une menace pour la base de données. Exemple :
Avec cette méthode le programmeur n'a plus besoin de se soucier des accès à la base de données, car il a accès à des objets permmettant de s'abstraire de la base de données tout en exerçant des controles de types avant insertion. Maintenant nous allons traiter de l'utilisation de ce système mis en place pour la migration du site vers PHP. La méthode d'abstraction à la base de données permet lors de modifications sur la base, d'appliquer les modifications sur le site sans être obligé de se replonger dans le code de la page. Utilisation de la méthode d'abstraction et concepts du site Comme il a été expliqué précédement le programmeur à accès à toutes les informations contenues dans les tables matérialisées par des objets. L'idée général du site est d'avoir un objet persistent en session qui est en fait les informations de l'utilisateur. Cet objet est instancié à la première connection de l'utilisateur puis l'objet reste en session HTTP par le biais de la méthode de sérialisation d'objet ainsi il reste accessible pendant la validité de la session HTTP, ce qui économise les accès à la Karobas.fr
42 / 93
Année 2004 - 2005 base de données. Après mesure effectuée entre l'ancien site et le nouveau, nous avons diminué les accès redondant à la base, tout en augmentant le stockage d'informations dans la base de données. Le principe d'utilisation est simple, lorsque le programmeur necessite l'accès à un objet (en général UTILISATEUR), il suffit de désérialiser l'objet stocké dans la session. Ainsi le programmeur retrouve tout accès aux données associées à l'utilisateur. L'initialisation de l'utilisateur au niveau de la base de données s'effectue biensur pendant l'inscription de celui-ci, par contre l'initialisation de l'objet s'effectue lors de l'authentification de l'utilisateur, pour instancier puis stocker dans la session HTTP. Concernant la synchronisation de l'objet et de la base de données, elle n'est pas automatique; cependant il existe une méthode commit() dérivée du modèle transactionnel permettant l'insertion et update() pour la mise à jour de la base de données
lors
de
son
appel.
Cette
insertion/modification
n'est
pas
faite
automatiquement pour eviter d'accéder à la base de données de trop nombreuses fois : cela génère une requète d'insertion / modification incluant toutes les informations. Exemple : page d'authentification
Karobas.fr
43 / 93
Année 2004 - 2005 } else { // Initialisation de l'objet avec l'identifiant trouvé $temp_user = new Utilisateur((int)$row['ID_Utilisateur']); if($temp_user != null) // Si l'initialisation s'est correctement effectuée { $_SESSION['CLEAN'] = 1; // Marquage de la session // On rempli l'objet Utilisateur $temp_user->fill(); // Sérialisation pour stockage dans la variable de session $_SESSION['OBJUTIL'] = serialize($temp_user); refresh('?p=30'); } else { // Affichage de l'erreur 3 : Initialisation échouée $error = 3; } } } else { // Affichage de l'erreur 2 : Aucun utilisateur trouvé $error = 1; } } }?>
Dans ce script, nous effectuons une recherche correspondant au couple [Pseudo, Mot de passe crypté]; cette selection retourne l'identifiant unique de l'utilisateur, par la suite nous instancions un objet PHP Utilisateur avec l'identifiant précédement selectioné. C'est une des seules pages qui ont été codées 'à la main'. Les accès suivants sont fait par désérialisation de l'objet Utilisateur stocké en session HTTP, puis utilisation des accesseurs en lecture / écriture. La synchronisation ne s'effectue qu'en cas de modification de l'objet. Si l'objet n'est pas modifié alors il n'y a pas de requète qui sont executées pour économiser les accès BD. Exemple de code : // On déserialize l'objet de session $obj = unserialize($_SESSION['OBJUTIL']); // Affichage des champs print $obj->getNom();
Karobas.fr
44 / 93
Année 2004 - 2005 print $obj->getPrenom(); // Modifications et insertion dans la base $obj->setNom('Thibault'); $obj->commit() ?>
Chaque accesseurs en écriture effectue un contrôle sur la validité de l'informations en temps que type, c'est à dire : si le champs de la base attend un entier et que l'utilisateur entre une chaine de caractères, la valeur entrée sera transtypé puis traitée de manière à eviter les attaque par injection SQL. Ainsi nous obtenons une abstraction à la base de données complète, puisque l'utilisateur n'utilise plus de requêtes SQL de lui même dans le but d'obtenir / traiter des informations. L'accès en devient simplifié, propre, et sécurisé. Cet ensemble de classe est généré par le biais d'un script developpé en Python que nous allons developper maintenant dans la prochaine partie. Fonctionnalités du script Le script permet de générer des classes d'abstractions à la base mais aussi du fait de la complète représentation de la partie de gestion codée en php et de la base de données; le script genère la partie de gestion de la base de données appelée partie administration. La procédure permettant de créer la partie d'administration s'appuie sur le script de création de la base de données. En fonction de cela, nous obtenons une toile de pages contenant des pages types telles que la modification, la création et recherche d'informations sur les entités selectionées.
Karobas.fr
45 / 93
Année 2004 - 2005
Options proposées par le script : ?> python sql2phpclass.py -h Usage : sql2phpclass [-vh] table1, table2, ... -v, --verbose
Mode verbeux.
-h, --help
Affiche ce message.
-a, --build-adminpages
Creer les pages d'administrations de la base.
-c, --build-classfiles
Creer les classes de la base.
-m, --build-menu
Creer le menu de la partie administration.
-d, --database
Nom de la base (si pas dans le script SQL).
table1,table2, ...
Liste des tables a traiter (par defaut
toutes).
Exemple d'utilisation standart : # Exporte la base de données dans un script SQL ?> mysqldump -u root -p -B kidfrance -e > Creates.sql password : # Execute le script pour générer les classes et les pages d'administration ?> python sql2phpclass.py -acvm -d kidfrance --------------------------------------------------------------------------Creation de la structure de traitement.... -----------------------------------------------------------------------------------------------------------------Traitement de la table : affectation_affichage ---------------------------------------['ID_AFFECTATION_AFFICHAGE', 'int', 'null', None, "Identifiant de gestion de l''objet affichage"] ['ID_AFFICHAGE', 'int', 'null', None, "Identifiant d''un affichage"] ['ID_UTILISATEUR', 'int', 'null', None, "Identifiant d''un utilisateur pouvant modifier l''affichage)"] ---------------------------------------Traitement de la table : affectation_production ---------------------------------------['ID_AFFECTATION_PRODUCTION', 'int', 'null', None, 'Identifiant de gestion des productions'] ['ID_PRODUCTION', 'int', 'null', None, 'Identifiant de la production']
Karobas.fr
46 / 93
Année 2004 - 2005 ['ID_UTILISATEUR', 'int', 'null', None, "Identifiant de l''utilisateur (animateur)"] ---------------------------------------Traitement de la table : affichage ---------------------------------------['ID_AFFICHAGE', 'int', 'null', None, "Identifiant de l''affichage"] ['IMAGE', 'string', 'null', None, 'Image ? afficher dans le cadrant'] ... --------------------------------------------------------------------------Generation des fichiers .... --------------------------------------------------------------------------Creation de la classe departement Administration de la table departement Creation de la liste d'administration admin_departement_list Creation de la page de modification admin_departement_modif Creation de la page de modification admin_departement_create Creation de la page de recherche admin_departement_search Creation de la classe planete Administration de la table planete Creation de la liste d'administration admin_planete_list Creation de la page de modification admin_planete_modif Creation de la page de modification admin_planete_create Creation de la page de recherche admin_planete_search ... ?>
Nous venons de créer la structure d'abstraction à la base de données, et la partie d'administration de la partie BD. L'utilisation du script de génération a mis en évidence quelques problèmes entre la partie générée par le script et la partie statique éditée par le developpeur; dans le cas de modification de signature de table, qui influence par conséquent la signature des fonctions générées par le script, ce qui abouti à un disfonctionnement dans la partie non générée. Pour cela, nous avons mis en place une fonctionnalité inexistante en PHP, qui consiste à passer des paramètres nommés, très courrament Karobas.fr
47 / 93
Année 2004 - 2005 utilisé en environnement Python. Cela consiste à passer un nom de paramètre et sa valeur, au lieu de mettre les valeurs au bonne endroit d'un appel de fonction. Exemple :
Pour faire appel à cette fonction, il suffit de passer les paramètres aux
bons
endroits, c'est à dire : foo(1,2). Le troisième paramètre est un paramètre optionnel, il a pour valeur par défaut 1 mais si on veut lui affecter une autre valeur il suffit d'appeler la fonction de cette manière : foo(1,2,3). La fonctionnalité de paramètres optionnels oblige de les mettre en fin de signature, ce qui implique aussi de remplir tous les paramètres pour avoir un appel qui fonctionne. Python: def foo(a,b,c=1): return a + b + c
Avec python, nous pouvons appeler cette fonction en utilisant le format standart d'appel: foo(1,2,3); mais aussi en utilisant les paramètres nommés : foo(a=2, b=3, c=40). Par conséquent en imaginant une fonction contenant que des paramètres optionnels, il est possible de modifier un paramètre sans pour autant être obligé de suivre la signature. Exemple: def foo(a=1, b=2, c=3): return a + b + c print foo() > 6 print foo(4, 5, 1) a = 4 b = 5 c = 1 > 10
Karobas.fr
48 / 93
Année 2004 - 2005 print foo(b=8) a = 1 b = 8 c = 1 > 10
Voila tout l'intêret de ce type de signature, elle peut s'adapter dynamiquement aux paramètres de l'utilisateur. Por pouvoir faire fonctionner cela en PHP, il suffit de remplir un dictionnaire de clé=>valeur soit paramètre => valeur, puis de passer le tableau à la fonction. null, 'id_type_event' => 1, 'data' => null, 'id_quartier3d' => null, 'id_affichage' => null, 'id_survol3d' => null, ); /* récupération du tableau de paramètres */ $gparams = func_get_args(); if(!is_array($gparams[0])) { print "Le paramètre doit être un tableau"; return -1; } /* Parcours du tableau pour modification des valeurs par défaut */ foreach($gparams[0] as $key => $value) $params[$key] = $value; /* Appel de la vrai fonction de création */ return $this->real_createStats($params['date_heure'], $params['id_type_event'], $params['data'], $params['id_quartier3d'], $params['id_affichage'], $params['id_survol3d'] ); } function real_createStats( &$s_date_heure, &$s_id_type_event, &$s_data, &$s_id_quartier3d, &$s_id_affichage, &$s_id_survol3d ) { $obj = new Stats(); $obj->setId_utilisateur((int)$this->id_utilisateur); $obj->setDate_heure($s_date_heure); $obj->setId_type_event($s_id_type_event); $obj->setData($s_data); $obj->setId_quartier3d($s_id_quartier3d);
Karobas.fr
49 / 93
Année 2004 - 2005
}
$obj->setId_affichage($s_id_affichage); $obj->setId_survol3d($s_id_survol3d); $obj->commit(); return $obj;
Exemple d'utilisation: createStats(array( 'date_heure' => Date('Y-m-d H:i:s'), 'id_type_event' => 3 ) ); // Login ?>
Pour utiliser le script Sql2phpclass convenablement, il est necessaire d'ajouter quelques informations supplémentaires dans les espaces commentaires de la base de données. Ils existe deux type de commentaires : ceux de la table, et ceux des champs. Chacuns d'eux ont une influence lors de la génération des classes PHP.
➔
Commentaires de table: Permettent de définir des options sur la table telles que son type : Administrable [A], Liste [L];
[A] : Permet de rendre l'objet administrable ce qui a pour effet de créer la section administration associée à la table. Ces objets seront des noeuds racines du menu de l'administration.
[L] : Défini la table comme étant une liste d'identifiant / description; cela pour effet d'ordonner la génération des composants liste déroulante HTML associée stocké dans le fichier nommé 'lib-combo.php' •
Génère les méthodes combo + Objet Liste, pour l'affichage sous forme de liste déroulante,
•
getID_ + Objet Liste + Str qui retourne la description d'un champs possédant l'ID passé en paramètre.
➔
Commentaires de champs: Permettent de définir la nature du champs, ou ses propriétés telles que [b] pour une case à cocher, [d] pour un controle date, [l] pour une liste deroulante de la table associée, [r] pour un champs en read-only, [s] pour un nom de ville.
Karobas.fr
50 / 93
Année 2004 - 2005
[b] : Associé au type booléen. Affiche une case à cocher.
[d] : Associé à une date. Affiche le controle Date.
[l] : Associé à une liste déroulante de la table étrangère.
[r] : Définit le champs comme en lecture-seule.
[s] : Spécifique à un id de ville.
Le script utilise un système dynamique d'intégration de code dans les classes pour ajouter des lignes qui ne peuvent être générées. Les fichiers patch doivent être mis dans le repértoire 'custom_code' avec pour nomenclature le nom de la classe à générer suivi de '.custom'. Exemple de fichier : utilisateur.custom #/-- properties --> #!-- constructors --> #/-- constructors --> #!-- read_accessors --> function isAdmin() { return (bool)($this->id_type_statut == 1); } function isCitoyen() { return (bool)($this->id_type_statut == 2); } function isAnimateur() { return (bool)($this->id_type_statut == 3); } #/-- read_accessors --> #!-- write_accessors --> function setAdmin() { $this->id_type_statut = 1; } function setCitoyen() { $this->id_type_statut = 2; } function setAnimateur() { $this->id_type_statut = 3; } #/-- write_accessors -->
?>
#!-- methods --> #/-- methods -->
Ce fichier est composé de 5 sections de code, qui correspondent aux emplacements d'une classe tels que les propriétés, les constructeurs, les accesseurs, et les méthodes. Ce fichier est analysé à l'étape de création du fichier classe PHP, et les éléments à ajouter sont inscrits dans la classe aux endroits indiqués après le code généré. Ce qui ajoute une flexibilité au code généré. Nous avons mis en place un système de nettoyage d'informations et de Karobas.fr
51 / 93
Année 2004 - 2005 sauvegarde, dans l'objectif de gardé une base saine. Systême de traitements automatisés Dans le but de maintenir en état la base de données, nous avons mis en place des scripts de maintenance de base : défragmentation, sauvegarde journalière, suppression des informations obsolètes. Ces tâches sont executées par le gestionnaire de tâches du serveur d'applications (hébergé chez completel). Ils sont au nombres de trois : ➔
DB_flush.py : un script python executant une sauvegarde de la base puis supprime les utilisateurs et toutes entités associées, lorsque l'utilisateur est marqué pour suppression. Ce script est executé tous les 1 du mois.
➔
DB_backup.bat : un autre script permettant la sauvegarde journalière SET DATE= FOR /F "delims=/ tokens=1,2" %%i IN ('DATE /T') DO (SET DATE=%%i%%j.sql.7z) mysqldump --default-character-set=latin1 -u root --password=55KS3E35 -e -B kidfrance | 7za a -si -m0=PPMd DB_SAVE_daily\kidfrance_%DATE% SET DATE=
➔
DB_backup_month.bat : un script de sauvegarde mensuelle, il supprime les sauvegardes journalières. SET DATE= del /F /Q DB_SAVE_daily\*.* FOR /F "delims=/ tokens=2,3" %%i IN ('DATE /T') DO (SET DATE=%%i%%j.sql.7z) mysqldump --default-character-set=latin1 -u root --password=XXXXXXXX -e -B kidfrance | 7za a -si -m0=PPMd DB_SAVE_monthly\kidfrance_%DATE% SET DATE=
➔
site_save.bat : Sauvegarde tous les vendredi le portail d'inscription au
Karobas.fr
52 / 93
Année 2004 - 2005 format
7z
et
l'upload
sur
le
Ftp
de
la
société.
del karobas_net.7z.old ren karobas_net.7z karobas_net.7z.old mysqldump --default-character-set=latin1 -u root --password=XXXXXXXX -e -B kidfrance > karobas_net.sql 7za a -t7z karobas_net.7z ..\htdocs_karobas_net ./karobas_net.sql -m0=PPMd del karobas_net.sql ftp -s:ftp_script.ftp ftp.karobas.fr
Cet ensemble de scripts executés périodiquement veillent à la sauvegarde et le maintient de la base de données représentant pour la société le coeur de leur jeu.
Nous venons de discuter de la conception de la base de données, de sa maintenance quotidienne, et des méthodes mis en place pour le developpemement et l'adaptation de la nouvelle architecture au site mais aussi au jeu. Reste encore quelques parties du site qui sont encore au stade minimal (travaux sur la mis en forme, esthétique, ....), qui doivent être developpée. Cela est dûe au temps restant et à la charge de travail croissante. Nous avons essayé d'apporter à l'entreprise un savoir nouveau, de nouvelles compétences, de nouvelles méthodes de travail avec l'apport de système informatique (Firewall Linux), de techniques nouvelles de programmation (Php & MySQL), mais surtout de répondre aux besoins de l'entreprise. Aujourd'hui, la société possède une base nouvelle répondant à leurs attentes actuelles, mais aussi futures dans la conception d'une base sensiblement évolutive et ouverte aux modifications, sans pour autant affecter directement les logiciels dépendants.
Karobas.fr
53 / 93
Année 2004 - 2005 Conclusion Cette période passée dans l’entreprise Karobas, nous a permis de progresser dans différents domaines : -
La mise en pratique des cours :
Il est valorisant de pouvoir utiliser les connaissances acquises au cours de cette année d’étude pour aboutir à la mise en place d’outils pour faciliter et aider les personnes à travailler d’en de bonnes conditions. -
Le niveau relationnel :
L’entreprise n’ayant pas d’administrateur nous dictant l’ensemble des tâches à effectuer, les principaux travaux ont dût subir l’aval de l’entreprise. D’où une présentation des avantages et des inconvénients de chacun en fonction des souhaits et des possibilités de la société. -
Le travail en groupe et la prise de décision :
L’étude des différentes possibilités lors de la mise en place d’un produit, nous a donné la possibilité de réfléchir et de s’aider profitant de l’expérience de chacun. -
L’ensemble des outils mis en place :
Partant d’un réseau peu développé, notre stage nous a permis d’implémenter et de concevoir un nouveau réseau mieux adapté à l’entreprise. Ainsi, le passage en domaine, la protection au niveau part-feux et formation du personnel au niveau des risques, la mise en place du système de messagerie, l’énorme travail au niveau conception et développement de la base de données et du site, permet d’obtenir un stage diversifié.
Karobas.fr
54 / 93
Année 2004 - 2005
Annexes
Karobas.fr
55 / 93
Année 2004 - 2005 Index des annexes :
A.Sécurité de l'entreprise I. Schéma du réseau
B.Schéma relationnel de la nouvelle base de données I. Vue d'ensemble II. Zone 1 : Espace géographique III.Zone 2 : Utilisateur IV.Zone 4 : Objet 3D V. Zone 5 : Missions VI.Zone 6 : Quartiers modélisés VII.Zone 7 : Avatar
Karobas.fr
56 / 93
Année 2004 - 2005
Réseau de l'entreprise
Karobas.fr
57 / 93
Année 2004 - 2005
Karobas.fr
58 / 93
Année 2004 - 2005
Schéma conceptuel de la nouvelle base de données
Karobas.fr
59 / 93
Année 2004 - 2005
Karobas.fr
60 / 93
Année 2004 - 2005
Karobas.fr
61 / 93
Année 2004 - 2005
Karobas.fr
62 / 93
Année 2004 - 2005
Karobas.fr
63 / 93
Année 2004 - 2005
Karobas.fr
64 / 93
Année 2004 - 2005
Karobas.fr
65 / 93
Année 2004 - 2005
Karobas.fr
66 / 93
Année 2004 - 2005
Cahier des charges Objectifs du developpement (documents fournis par l'entreprise)
Karobas.fr
67 / 93
Année 2004 - 2005 Proposition d’architecture globale du Monde de Karobas Lien par survol interplanétaire (plus tard…)
Lien par survol planète (interface spécifique : globe rotatif) Interface spécifique SURVOL : prévoir boutons de changement d’altitude optionnel selon la présence ou non de niveaux intermédaires. Lien par survol continental (ex. Europe)
Lien par survol national
Lien par survol régional
Lien par survol « moyenne altitude » : département
Lien par survol « basse altitude » : ville ou agglo Interface « normale » avec avatar : Lien par clic-pancarte, bus, etc. Lien simple par « proximité »
Mondes avec salle(s) intérieure(s)
Karobas.fr
Mondes simples
68 / 93
Année 2004 - 2005 Description des types d’ « objets dynamiques » du jeu Panneau d’exposition - visible / invisible - vignette taille : - titre en roll-over (variables possibles : langue, âge) - en cliquant (variables possibles : langue, âge) => production « format Karobas », fenêtre web, vidéo ? - administrateurs : Karobas + répartition pour chaque panneau Panneau affichage texte - toujours visible - 1 affichage = 6 lignes de textes + 1 adresse URL - n affichages possibles (variables possibles : langue, âge) - administrateurs : Karobas + répartition pour chaque affichage Panneau abribus - toujours visible - 1 affichage = photo + 1 adresse URL - n affichages possibles (variables possibles : langue, âge) - administrateurs : Karobas + répartition pour chaque affichage Panneau pub 4x3 - toujours visible - 1 affichage = photo + 1 adresse URL - n affichages possibles (variables possibles : langue, âge) - administrateurs : Karobas + répartition pour chaque affichage
Panneau TV possible ? Objet dynamique - visible / invisible - fixe ou mobile (parcours dynamique ?) - personnage (avatar), animal, véhicule ou objet quelconque - en cliquant (variables possibles : langue, âge) => bulle de texte, dialogue, son, production « format Karobas », fenêtre web, vidéo ? - administrateurs : Karobas + répartition pour chaque objet
Karobas.fr
69 / 93
Année 2004 - 2005 Actions possibles à effectuer par un administrateur : Administrateur KAROBAS -
Base de photos en gestion multicritères (prévoir achat base dédiée ?)
-
Base de productions : affichage de type liste multicritère (nom production, origine, type, langue, âge, mots-clés, dates de mise à jours, compteurs, nom administrateur). En option ; affichage de la vignette générée automatiquement + modification interactive de la vignette (imposer la taille en pixels) et des autres critère (surtout le nom qui s’affiche en roll-over). Les dates de mise à jour et nom administrateur sont remplis automatiquement après validation ainsi que les compteurs dans le jeu.
-
Base d’objets dynamiques : affichage de type liste multicritère (nom monde élémentaire, type d’objet, emplacement dans le monde, mots-clés, dates de mise à jour, compteurs, nom administrateur). Sur un objet sélectionné, afficher les n affichages en cours (dont « vide ») avec textes défilant ou vignette, liens web ou prod et critères « âge » et « langue » + date de mise en ligne + nom administrateur ayant fait la dernière mise à jour + historique des mises à jour. ? Base de vignettes des différents types (exposition, abribus, etc.) ou accès à une arborescence de dossiers images ? Changement dynamique de tous ces éléments sauf vignettes (à faire sous Photoshop ou équivalent avec des tailles imposées par type). Prévoir aussi 2 compteurs de consultation dans le jeu (mensuel et cumulé) pour chaque affichage de chaque objet dynamique (on doit pouvoir sortir des statistiques du type : nb de consultations sur tel mois ou depuis le début de l’affichage selon langue et par âge…) . Ces compteurs sont remis à 0 à chaque modification du lien ou par volonté de l’administrateur.
-
Base de missions : affichage de type liste multicritère + vignettes en option (nom mission, type, origine, âge, langue, code postal, mots-clés, dates de mise à jour, compteurs, liste d’objets dynamiques concernés). Sur une mission sélectionnée, afficher le contenu + tous les critères. Pour une nouvelle mission : choisir type + saisie de tous les éléments.
-
Base d’expositions temporaires : affichage de type liste multicritère + vignettes en option (nom exposition, origine, âge, langue, mots-clés, dates de début et de fin, liste d’objets dynamiques concernés, compteurs avec historique détaillé). Sur une exposition sélectionnée, afficher le contenu + tous les critères. Pour une nouvelle exposition : choisir les objets dynamiques concernés dans liste multicritère + saisie date de fin + détails de l’historique.
-
Base de campagnes d’affichage : affichage de type liste multicritère + textes ou vignette concernée (nom campagne, origine, âge, langue, mots-clés, dates de début et de fin, liste d’objets dynamiques concernés, compteurs avec historique détaillé). Sur une campagne sélectionnée, afficher le contenu + tous les critères. Pour une nouvelle campagne : choisir les objets dynamiques concernés dans liste multicritère + saisie date de fin + détails de l’historique.
Karobas.fr
70 / 93
Année 2004 - 2005 Administrateurs délégués (accès par mot de passe) : Idem mais avec autorisations choisies dans une liste multicritère (par exemple, la Ville de Cannes pourrait modifier en direct 2 ou 3 affichages textes + 1 pub abribus + 2 pubs 4x3 + 8 panneaux d’exposition, etc. en donnant les possibilités d’agir sur tel ou tel monde appartenant à Cannes et sur l’un ou l’autre des critères liés à l’âge et à la langue (code postal possible uniquement pour les missions).
Karobas.fr
71 / 93
Année 2004 - 2005
Script SQL2Phpclass (disponible à l'adresse : http://thibault.normand.free.fr/projects/)
Karobas.fr
72 / 93
Année 2004 - 2005 #! /usr/bin/python # -*- coding: utf_8 -*import os, sys, time, getopt from xml.dom.minidom import * ''' Utilitaire de création de classe PHP à partir de scripts SQL, contenant les directives CREATE TABLE. ''' class sql2php: file = None buffer = '' classname = '' inclass = 0 fk_key = {} feilds = [] fk_table = [] def __init__(self): self.file = None self.sqlfile = open('Creates.sql', 'r') self.build_admin = False self.build_class = False self.build_menu = False self.build_stats = False self.verbose = False self.database_name = "" if not os.path.exists('./include'): os.mkdir('include') if not os.path.exists('./pages'): os.mkdir('pages') if not os.path.exists('./custom_code'): os.mkdir('custom_code') self.tables = {} self.rel = {} def __del__(self): self.sqlfile.close() def run(self): try: opts, args = getopt.getopt(sys.argv[1:], "ho:acvmsd:", ["help", "output=", "buildadminpages", "build-classfiles", "build-menu", "build-stats", "database-name"]) except getopt.GetoptError: usage() sys.exit(2) for o, a in opts: if o == "-v": self.verbose = True if o in ("-h", "--help"): usage() sys.exit() if o in ("-o", "--output"): pass if o in ("-a", "--build-adminpages"): self.build_admin = True if o in ("-c", "--build-classfiles"): self.build_class = True if o in ("-m", "--build-menu"): self.build_menu = True if o in ("-s", "--build-stats"): self.build_stats = True if o in ("-d", "--database-name"):
Karobas.fr
73 / 93
Année 2004 - 2005 self.database_name = a if not os.path.exists('./include/DB_' + self.database_name): os.chdir('include') os.mkdir('DB_' + self.database_name) os.chdir('..') print '-'*80 print 'Creation de la structure de traitement....\n' print '-'*80 self.buffer = self.sqlfile.readline() while self.buffer != '': self.processBuffer(self.buffer) self.buffer = self.sqlfile.readline() print print print print
'\n' '-'*80 'Generation des fichiers ....\n' '-'*80
for name, class_ in self.tables.items(): self.in_custom_properties = 0 self.custom_properties_code = "" self.in_custom_constructors = 0 self.custom_constructors_code = "" self.in_custom_read_accessors = 0 self.custom_read_accessors_code = "" self.in_custom_write_accessors = 0 self.custom_write_accessors_code = "" self.in_custom_methods = 0 self.custom_methods_code = "" if os.path.exists('./custom_code/' + name.lower() + '.custom'): self.parseCustomCode(name) if self.build_class: print 'Creation de la classe ' + name ''' Crꢴion des classes de gestion de la base de donn꦳ ''' self.buildClassFile(name, class_[0], class_[1], class_[1], class_[4]) if self.build_admin: print "Administration de la table " + name if self.verbose: print 'Creation de la liste d\'administration admin_' + name + '_list' ''' Crꢴion des pages de listings ''' self.buildAdminListPages(name, class_[0], class_[1], class_[1], class_[4]) if self.verbose: print 'Creation de la page de modification admin_' + name + '_modif' ''' Crꢴion des pages de modification des objets ''' self.buildAdminModifPages(name, class_[0], class_[1], class_[1], class_[4]) if self.verbose: print 'Creation de la page de modification admin_' + name + '_create' ''' Crꢴion des pages d'enregistrement ''' self.buildAdminCreatePages(name, class_[0], class_[1], class_[1], class_[4]) if self.verbose: print 'Creation de la page de recherche admin_' + name + '_search' self.buildAdminSearchPages(name, class_[0], class_[1], class_[1], class_[4]) if self.build_menu: print "Creation du menu" self.buildAdminMenuPage() if self.build_stats: self.buildStatsPage() self.buildComboTypes()
Karobas.fr
74 / 93
Année 2004 - 2005 ''' ------------------------------------------------------------------------------------------------------------------ ''' ''' Crꢴion de la structure de traitement / Parsing ''' ''' ------------------------------------------------------------------------------------------------------------------ ''' def processBuffer(self, str): if str.startswith(")"): self.inclass = 0 comm_pos = str.strip().find('COMMENT=') comment_table = str.strip()[comm_pos + 8:].strip('\'') obj_opt = 0 if comment_table.lower().startswith('[a]'): obj_opt = 1 if comment_table.lower().startswith('[l]'): obj_opt = 2 self.tables[self.classname] = [self.feilds, self.fk_key, self.fk_table, obj_opt,
self.pk]
self.feilds = [] self.fk_key = {} self.fk_table = [] if str.startswith("USE"): if self.database_name == "": self.database_name = str[4:] return if str.startswith("CREATE TABLE"): ''' Extraction du nom de la classe ''' str = str.split(' ',2) self.classname = str[2][1:-4] print "-"*40 print "Traitement de la table : " + self.classname print "-"*40 self.inclass = 1 return if self.inclass == 1: ''' Extraction des champs ''' if str.strip().startswith("PRIMARY KEY"): temp = str.strip().split(' ', 2)[2][2:-2] self.pk = temp.replace('`', '').split(",") return elif str.strip().startswith("KEY"): temp = str.strip().split(' ', 2)[2][2:-2] self.idx = temp.replace('`', '').split(",") return elif str.strip().startswith("CONSTRAINT"): temp = str.strip().split(' ', 8) self.fk_key[temp[4].strip()[2:-2]] = [temp[6][1:-1], temp[7].strip(",")[2:-2]] self.fk_table.append(temp[6][1:-1]) if not self.rel.has_key(temp[6][1:-1]): self.rel[temp[6][1:-1]] = [] self.rel[temp[6][1:-1]].append([self.feilds[0][0], self.classname, temp[7].strip(",")[2:-2]]) return elif str.strip().startswith('UNIQUE'): return try:
name, type, opts = str.strip().split(' ',2)
Karobas.fr
75 / 93
Année 2004 - 2005 except: name, type = str.strip().split(' ',1) opts = "" default = '0' popts = self.parseOptions(opts) if popts[0] == 0: if popts[1] is not None: default = popts[1].strip().strip(',').strip('\'') else: default = 'null' else: default = '0' #~ try: typ, nullify = self.getValidphpTypes(type, popts[0], default) #~ except: #~ print "Erreur d'extraction d\'information : type = '"+ typ +"' nullify = '" +
nullify +"'"
#~ sys.exit(1) #~ if name[1:-1].startswith('ID_'): #~ ''' Si le champs est un identifiant, on fixe la valeur par dꧡut #~ nullify = 1
'''
field = [name[1:-1], typ, nullify, popts[3], popts[2]] if self.verbose: print field self.feilds.append(field) def parseOptions(self, options): nullify = 1 val = None comment = None card = None if options.startswith("NOT NULL"): nullify = 0 options = options[8:].strip() if options.startswith("default"): defo = options.split(" ") val = defo[1].strip('\'') options = options[9 + len(val):].strip('\'').strip() if val.lower() == 'null': nullify = 0 val = "null" if options.startswith("auto_increment"): options = options[15:] if options.startswith('COMMENT'): options = options[7:] comment = options.strip().strip(",")[1:-1] if comment.startswith('['): card = comment.split(" ",1)[0] comment = comment.split(" ",1)[1] return [nullify, val, comment, card] def getValidphpTypes(self, typ, Null, default = ''): typ = typ.lower() if typ.startswith('longtext') or typ.startswith('text'): return "string", not Null and "" or 'null' if typ.startswith('varchar') or typ.startswith('date') or typ.startswith('enum') or typ.startswith('char'): if default != 'null': default = "\'" + default + "\'" return "string", not Null and default or 'null' if typ.startswith('int') or typ.startswith('bigint') or typ.startswith('tinyint'): return "int", not Null and default or '0' if typ.startswith('double'):
Karobas.fr
76 / 93
Année 2004 - 2005 return "double", not Null and default or 'null' if typ.startswith('float'): return "float", not Null and default or '0.0' ''' ------------------------------------------------------------------------------------------------------------------ ''' ''' Traitement des fichiers custom ''' ''' ------------------------------------------------------------------------------------------------------------------ ''' def parseCustomCode(self, name): try:
custom_file = open('./custom_code/' + name.lower() + '.custom' , 'r') except: print 'Erreur à l\'ouverture du fichier CusTOM : ' + name.lower() + '.custom' return -1 buffer = custom_file.readline() while buffer != '': self.processCustom(buffer) buffer = custom_file.readline() def processCustom(self, buffer): if buffer.startswith('#!-- properties -->'): self.in_custom_properties = 1 return if buffer.strip().startswith('#/-- properties -->'): self.in_custom_properties = 0 return if self.in_custom_properties: if buffer != "": self.custom_properties_code += buffer return if buffer.startswith('#!-- constructors -->'): self.in_custom_constructors = 1 return if buffer.strip().startswith('#/-- constructors -->'): self.in_custom_constructors = 0 return if self.in_custom_constructors: if buffer != "": self.custom_constructors_code += buffer return if buffer.startswith('#!-- read_accessors -->'): self.in_custom_read_accessors = 1 return if buffer.strip().startswith('#/-- read_accessors -->'): self.in_custom_read_accessors = 0 return if self.in_custom_read_accessors: if buffer != "": self.custom_read_accessors_code += buffer return if buffer.startswith('#!-- write_accessors -->'): self.in_custom_write_accessors = 1 return if buffer.strip().startswith('#/-- write_accessors -->'): self.in_custom_write_accessors = 0 return
Karobas.fr
77 / 93
Année 2004 - 2005 if self.in_custom_write_accessors: if buffer != "": self.custom_write_accessors_code += buffer return if buffer.startswith('#!-- methods -->'): self.in_custom_methods = 1 return if buffer.strip().startswith('#/-- methods -->'): self.in_custom_methods = 0 return if self.in_custom_methods: if buffer != "": self.custom_methods_code += buffer return ''' ------------------------------------------------------------------------------------------------------------------ ''' ''' Crꢴion des fichiers de classe ''' ''' ------------------------------------------------------------------------------------------------------------------ ''' def generateConstructor(self, classname, feilds, fk_key, fk_table, pk_key): ret = "" params = [] for f in feilds: if f[0] in pk_key: params.append('$def_' + f[0].lower() + ' = -1') ret += "\tfunction " + classname.capitalize() + "( " + ", ".join(params) + " )\n" ret += "\t{\n" for f in feilds: if f[0] in pk_key: ret += "\t\t$this->" + f[0].lower() + " = (int)$def_" + f[0].lower() + ";\n" ret += "\t}\n" if self.custom_constructors_code != "": ret += "\n" ret += self.custom_constructors_code return ret def createHeritedConstructors(self, classname): ret = "" if self.rel.has_key(classname): for p_id, table, c_id in self.rel[classname.lower()]: ret += "\t/**\n" ret += "\t * Constructeur hérité de la table " + table + "\n" ret += "\t */\n" ret += "\tfunction create" + table.capitalize() + "()\n" ret += "\t{\n" ret += "\t\t$params = array(\n" for f in self.tables[table.lower()][0][1:]: if not f[0].lower() == c_id.lower(): ret += "\t\t\t'" + f[0].lower() + "' => " + str(f[2]) + ",\n" ret += "\t\t);\n" ret ret ret ret ret
Karobas.fr
+= += += += +=
"\t\t$gparams = func_get_args();\n" "\t\tif(!is_array($gparams[0])) {\n" "\t\t\tprint \"Le paramètre doit être un tableau\";\n" "\t\t\treturn -1;\n" "\t\t}\n"
78 / 93
Année 2004 - 2005 ret += "\t\tforeach($gparams[0] as $key => $value)\n" ret += "\t\t\t$params[$key] = $value;\n" ret += "\t\treturn $this->real_create" + table.capitalize() params1 = "" params = "" for f in self.tables[table.lower()][0][1:]: if not f[0].lower() == c_id.lower(): params1 += ", $params['" + f[0].lower() + "']" params += ", &$s_" + f[0].lower() ret += "(" + params1[1:] + " );\n" ret += "\t}\n\n" ret += "\tfunction real_create" + table.capitalize() + "(" + params[1:] + " )\n" ret += "\t{\n" ret += "\t\t$obj = new " + table.capitalize() + "();\n" ret += "\t\t$obj->set" + c_id.capitalize() + "((int)$this->" + c_id.lower() + ");\n" for f in self.tables[table.lower()][0][1:]: if not f[0].lower() == c_id.lower(): ret += "\t\t$obj->set" + f[0].capitalize() + "($s_" + f[0].lower() + ");\n" ret ret ret ret
+= += += +=
"\t\t$obj->commit();\n" "\t\treturn $obj;\n" "\t}\n" "\n"
return ret def generateProperties(self, classname, feilds, fk_key, fk_table, pk_key): ret = "" ret1 = "" ret2 = "" for feild in feilds: if feild[0] in pk_key: ret += "\t/**\n" ret += "\t * Identifiant de la table " + classname.capitalize() + "\n" if feild[4] != None: ret += "\t * " + feild[4] + "\n" ret += "\t * @access private\n" ret += "\t * @var " + feild[1] + "\n" ret += "\t */\n" ret += "\tvar $" + feild[0].lower() + " = -1;\n\n" else: ret1 += "\t/**\n" ret1 += "\t * @access private\n" if feild[4] != None: ret1 += "\t * " + feild[4] + "\n" ret1 += "\t * @var " + ((feild[3] == '[b]') and "bool" or feild[1]) + "\n" ret1 += "\t */\n" ret1 += "\tvar $" + feild[0].lower() + " = " + str(feild[2]) + ";\n\n" if feild[0].upper() in fk_key.keys(): ret2 += "\t/**\n" ret2 += "\t * @access private\n" if feild[4] != None: ret2 += "\t * " + feild[4] + "\n" ret2 += "\t * @var " + ((feild[3] == '[b]') and "bool" or feild[1]) + "\n" ret2 += "\t */\n" ret2 += "\tvar $" + feild[0][3:].lower() + " = null;\n\n" return ret + ret1 + ret2 + "\n" + str(self.custom_properties_code) def generateAccessRead(self, classname, feilds, fk_key, fk_table, pk_key): ret = "" for feild in feilds: ret += "\t/**\n" ret += "\t * Accesseur en lecture du champs " + feild[0] + " \n" ret += "\t * @access public\n" ret += "\t * @return " + feild[1] + " Retourne la valeur actuelle du champs " + feild[0]
Karobas.fr
79 / 93
Année 2004 - 2005 + "\n" ret += ret += feild[0].lower() + feild[0].lower() +
"\t */\n" "\tfunction get" + feild[0].capitalize() + "() { return (is_null($this->" + "))?(null):((" + ((feild[3] == '[b]') and "bool" or feild[1]) + ")$this->" + "); }\n\n"
for f in feilds: if f[0].upper() in fk_key.keys(): ret += "\t/**\n" ret += "\t * Accesseur en lecture du champs " + f[0] + " \n" ret += "\t * @access public\n" ret += "\t * @return " + f[1] + " Retourne l'objet " + f[0] + " initialisé" ret += "\t */\n" ret += "\tfunction get" + f[0][3:].capitalize() + "()\n" ret += "\t{\n" ret += "\t\tif($this->" + f[0].lower() + " > 0) {\n" ret += "\t\t\tif($this->" + f[0][3:].lower() + " != null) { unset($this->" + f[0][3:].lower() + "); }\n" ret += "\t\t\t$this->" + f[0][3:].lower() + " = new " + fk_key[f[0].upper()][0].capitalize() + "((int)$this->" + f[0].lower() + ");\n" ret += "\t\t\t$this->" + f[0][3:].lower() + "->fill();\n" ret += "\t\t\treturn $this->" + f[0][3:].lower() + ";\n" ret += "\t\t}\n" ret += "\t\treturn null;\n" ret += "\t}\n" ret += "\n" if self.rel.has_key(classname): for p_id, table, c_id in self.rel[classname]: ret += "\t/**\n" ret += "\t * Accesseur en lecture de la collection d'objet " + f[0] + " (d 걥 nds de la cardinalit 骜 n" ret += "\t * @param bool $init Dꧩnit le remplissage des objets de collections.\n" ret += "\t * @access public\n" ret += "\t * @return array Retourne la collection d'objets " + f[0] + " non initialisꝮ" ret += "\t */\n" ret += "\tfunction get" + table.capitalize() + "Col($init = 0)\n" ret += "\t{\n" ret += "\t\t$ret = array();\n\n" ret += "\t\t$sql = \"SELECT " + p_id + " FROM " + table + " WHERE " + c_id + "='\".$this->" + feilds[0][0].lower() + ".\"'\";\n" ret += "\t\t$res = DB_query($sql);\n" ret += "\t\twhile($row = DB_fetchArray($res)) {\n" ret += "\t\t\t$temp = new " + table + "((int)$row[0]);\n" ret += "\t\t\t$temp->set" + c_id.capitalize() + "($this->" + feilds[0][0].lower() + ");\n" ret += "\t\t\tif($init != 0)\n" ret += "\t\t\t\t$temp->fill();\n" ret += "\t\t\t$ret[] = $temp;\n" ret += "\t\t}\n" ret += "\t\treturn $ret;\n" ret += "\t}\n" ret += "\n" ret += "\n" if self.custom_read_accessors_code != "": ret += self.custom_read_accessors_code + "\n" return ret def generateAccessWrite(self, classname, feilds, fk_key, fk_table, pk_key): ret = "" for feild in feilds: ret += "\t/**\n" ret += "\t * Accesseur en ꤲiture du champs " + feild[0] + " \n" ret += "\t * @access public\n" ret += "\t * @param " + str(feild[1]) + " $newvalue Affecte une nouvelle valeur au champs " + str(feild[0]) + "\n" ret += "\t * @return " + str(feild[1]) + " Retourne $newvalue si succés sinon " + str(feild[2]) + " s'il n'y a pas de changement\n"
Karobas.fr
80 / 93
Année 2004 - 2005 ret += "\t */\n" ret += "\tfunction set" + feild[0].capitalize() + "($newvalue = " + feild[2].lower() +
")\n"
ret += "\t{\n" if feild[2].lower() == 'null': ret += "\t\tif($newvalue == null) {\n" ret += "\t\t\t$this->" + feild[0].lower() + " = null;\n" ret += "\t\t\treturn null;\n" ret += "\t\t}\n" ret += "\t\n" ret += "\t\tif($newvalue != $this->" + feild[0].lower() + " && !empty($newvalue))\n" ret += "\t\t{\n"; ret += "\t\t\t$this->" + feild[0].lower() + " = (" + ((feild[3] == '[b]') and "bool" or feild[1]) + ")$newvalue;\n" ret += "\t\t\treturn (" + ((feild[3] == '[b]') and "bool" or str(feild[1])) + ")protectVar($newvalue);\n" ret += "\t\t }\n" ret += "\t\treturn (" + ((feild[3] == '[b]') and "bool" or str(feild[1])) + ")" + str(feild[2]) +";\n" ret += "\t}\n" if self.custom_write_accessors_code != "": ret += self.custom_write_accessors_code + "\n\n" return ret def generateMethod(self, classname, feilds, fk_key, fk_table, pk_key): ret = "" ret += "\t/**\n" ret += "\t * Ordonne le remplissage des propri 굩 s de la classe en utilisant les champs de la table " + self.classname + "\n" ret += "\t * @param int L'identifiant de la table " + classname + " soit " + feilds[0][0] + ".\n" ret += "\t */\n" ret += "\tfunction fill($autofill = 0)\n" ret += "\t{\n" ret += "\t\tif($this->" + pk_key[0].lower() + " < 0)\n" ret += "\t\t\treturn -1;\n" for f in pk_key[1:]: ret += "\t\tif($this->" + f.lower() + " < 0)\n" ret += "\t\t\treturn -1;\n" ret += "\n" ret += "\t\t$sql = \"SELECT * FROM " + classname + " WHERE \";\n" ret += "\t\tif($this->" + pk_key[0].lower() + " > 0)\n" ret += "\t\t\t$sql .= \"" + feilds[0][0] + "='\".(int)$this->" + feilds[0][0].lower() + ".\"'\";\n" for f in pk_key[1:]: ret += "\t\tif($this->" + f.lower() + " > 0)\n" ret += "\t\t\t$sql .= \" AND " + f + "='\".(int)$this->" + f.lower() + ".\"'\";\n" ret ret ret ret
+= += += +=
"\n" "\t\t$res = DB_query($sql);\n" "\t\tif(DB_numRows($res) == 1) {\n" "\t\t\t$row = DB_fetchArray($res);\n\n"
for f in feilds: ret += "\t\t\t$this->" + f[0].lower() + " = (is_null($row['" + f[0] + "']))?(null):((" + ((f[3] == '[b]') and "bool" or f[1]) + ")$row['" + f[0] + "']);\n" ret += "\n\t\t\tif($autofill != 0) {\n" for f in feilds: if not f[0] in pk_key: if f[0].upper() in fk_key.keys(): if f[4] != None: ret += "\n\t\t\t\t/* " + f[4] + " */\n" ret += "\t\t\t\tif($this->" + f[0].lower() + " != " + str(f[2]) + ") {\n" ret += "\t\t\t\t\t$this->" + f[0][3:].lower() + " = new " + fk_key[f[0].upper()][0].capitalize() + "((int)$this->" + f[0].lower() + ");\n"
Karobas.fr
81 / 93
Année 2004 - 2005 ret += "\t\t\t\t\t$this->" + f[0][3:].lower() + "->fill($autofill);\n" ret += "\t\t\t\t}\n" ret += "\t\t\t}\n" ret += "\t\t}\n" ret += "\t\telse return -1;\n" ret += "\n" ret += "\treturn 0;\n" ret += "\t}\n" ret += "\n" ret += "\t/**\n" ret += "\t * Ordonne la synchronisation des propri 굩 s de la classe en cours d'utilisation avec la base de donn꦳.\n" ret += "\t */\n" ret += "\tfunction commit()\n" ret += "\t{\n" ret += "\t\t$sql = \"INSERT INTO " + classname + " VALUES (" if len(pk_key) == 1: ret += "''" else: ret += "\".protectVar($this->" + pk_key[0].lower() + ").\"" for f in pk_key[1:]: ret += " ,\".protectVar($this->" + f.lower() + ").\"" for f in feilds: if not f[0] in pk_key: ret += " ,\".protectVar($this->" + f[0].lower() + ").\"" ret ret ret ret ret ret ret
+= += += += += += +=
")\";\n" "\t\t$res = DB_query($sql);\n" "\t\t$id = DB_insertId();\n" "\t\t$this->" + feilds[0][0].lower() + " = $id;\n" "\t\treturn $id;\n" "\t}\n" "\n"
ret += "\tfunction update()\n" ret += "\t{\n" ret += "\t\t$sql = \"UPDATE " + classname + " SET " ret += feilds[len(pk_key)][0] + " = \".protectVar($this->" + feilds[len(pk_key)][0].lower()
+ ").\""
for f in feilds[len(pk_key) + 1:]: ret += ", " + f[0] + " = \".protectVar($this->" + f[0].lower() + ").\"" ret += " WHERE " + pk_key[0] + " = '\" . $this->" + pk_key[0].lower() + " . \"'" for f in pk_key[1:]: ret += " AND " + f + " = '\" . $this->" + f.lower() + " . \"'" ret += "\";\n" ret += "\t\t$res = DB_query($sql);\n" ret += "\t\treturn DB_affectedRows($res);\n" ret += "\t}\n" ret += "\n" ret += "\t/**\n" ret += "\t * Ordonne la suppression de l'enregistrement de la base de donn꦳.\n" ret += "\t*/\n" ret += "\tfunction _delete() {\n" ret += "\t\t$sql = \"DELETE FROM " + classname +" WHERE " + feilds[0][0] + "='\".$this->" + feilds[0][0].lower() + ".\"'\";\n" ret += "\t\t$res = DB_query($sql);\n" ret += "\t\treturn $res;\n" ret += "\t}\n" if self.custom_methods_code != "": ret += self.custom_methods_code + "\n" return ret def generateToXML(self, classname, feilds, fk_key, fk_table, pk_key): ret = "" ret += "\t/**\n" ret += "\t * Exportation de l'instance en cours vers un schꮡ XML exploitable\n"
Karobas.fr
82 / 93
Année 2004 - 2005 ret += "\t * @return string Le schꮡ XML compl 굩 de l'instance en cours.\n" ret += "\t */\n" ret += "\tfunction toXML()\n" ret += "\t{\n" ret += "\t\tif($this->" + feilds[0][0].lower() + " == -1 || empty($this->" + feilds[0][0].lower() + "))\n" ret += "\t\t\treturn null;\n\n" ret += "\t\t$xmlstr = \"<" + classname.lower() + " id='\".$this->" + feilds[0][0].lower() + ".\"'>\";\n" for f in feilds[1:]: ret += "\t\t$xmlstr .= \"\\t<" + f[0].lower() + ">\".$this->" + f[0].lower() + ".\"" + f[0].lower() + ">\\n\";\n" ret += "\t\t$xmlstr .= \"" + classname.lower() + ">\";\n\n" ret += "\t\treturn $xmlstr;\n" ret += "\t}\n" return ret def buildClassFile(self, classname, feilds, fk_key, fk_table, pk_key): os.chdir('./include/DB_' + self.database_name) self.cfile = open(classname.lower() + '.class.php', 'w') content content content content sql2phpclass\n" content content content
= += += +=
"
+= += +=
" * Sql2Phpclass v 2.02 (
[email protected])\n" " * @package KarobasDB\n" " */\n\n"
if len(fk_table) > 0: for k, v in fk_table.items(): content += "/* Classe d'acc 鳠 a table " + v[0].capitalize() +" R*/\n" content += "require_once('" + v[0] + ".class.php');\n" if self.rel.has_key(classname): for p_id, table, c_id in self.rel[classname]: content += "/* Classe d'acc 鳠 a table " + table.capitalize() +" */\n" content += "require_once('" + table.lower() + ".class.php');\n" content content content content content content content content content content content content content content content content
+= += += += += += += += += += += += += += += +=
"\n" "class " + classname.capitalize() + "\n{\n" "\n" "\t/*" + "*"*40 + "\n" "\t * Propi 굩 s\n" "\t " + "*"*40 + "*/\n\n" self.generateProperties(classname, feilds, fk_key, fk_table, pk_key) "\n\n" "\t/*" + "*"*40 + "\n" "\t * Constructeurs\n" "\t " + "*"*40 + "*/\n\n" self.generateConstructor(classname, feilds, fk_key, fk_table, pk_key) "\n\n" "\t/*" + "*"*40 + "\n" "\t * Constructeurs Hérités\n" "\t " + "*"*40 + "*/\n\n"
content += self.createHeritedConstructors(classname) content content content content content content content content content content content content
Karobas.fr
+= += += += += += += += += += += +=
"\n\n" "\t/*" + "*"*40 + "\n" "\t * Accesseurs\n" "\t " + "*"*40 + "*/\n\n" self.generateAccessRead(classname, feilds, fk_key, fk_table, pk_key) "\n" self.generateAccessWrite(classname, feilds, fk_key, fk_table, pk_key) "\n" "\t/*" + "*"*40 + "\n" "\t * Méthodes\n" "\t " + "*"*40 + "*/\n\n" self.generateMethod(classname, feilds, fk_key, fk_table, pk_key)
83 / 93
Année 2004 - 2005 content += "\n" content += "\t/*" + "*"*40 + "\n" content += "\t * Exportation\n" content += "\t " + "*"*40 + "*/\n\n" content += self.generateToXML(classname, feilds, fk_key, fk_table, pk_key) content += "}\n" content += "?>\n" self.cfile.write(content) self.cfile.close() os.chdir('../../') ''' ------------------------------------------------------------------------------------------------------------------ ''' ''' Crꢴion des fichiers d'administration ''' ''' ------------------------------------------------------------------------------------------------------------------ ''' def buildAdminListPages(self, classname, feilds, fk_key, fk_table, pk_key): os.chdir('./pages') self.filename = 'admin_' + classname.lower() + '_list.php' self.cfile = open(self.filename, 'w') tab = "$tri_tab = array(" var = "$vars = array(" idx = 0 content content content content content content content
= " include('../header.inc');\n" += "define ('GZIP', FALSE);\n" += "require_once \"tpl.class.php\";\n" += "require_once \"dcc.class.php\";\n" += "\n" += "$tpl = new dcc(\"" + classname.lower() + "\", \"listing\");\n" += "if ($tpl->cache_start()) {\n"
var += "\n\t\t'page_header' => \"\\n\"," ''' Crꢴion de l'ent 뵥 du tableau ''' var += "\n\t\t'table_header' => \"
Actions | " for f in feilds[1:]: var += "(A/D) " + f[0].capitalize() + " | " tab += "" + str(idx) + " => \"" + f[0].lower() + "\", " idx = idx + 1 var += "
\",\n" tab += ");" var += "\t);" content += "\t" + var + "\n" content += "\t$tpl->assign($vars);\n\n" content += "\t" + tab + "\n" content += "\t$sql = \"SELECT * FROM " + classname.capitalize() + "\";\n" content += "\t$sql .= \" ORDER BY \".$tri_tab[abs($tri) % count($tri_tab)];\n" content += "\t$sql .= ($tri>0)?(\" DESC\"):(\" ASC\");\n" content += "\t$sql .= \" LIMIT \".(((int)$pag)*$nb).\", \".$nb;\n" content += "\t$res= DB_query($sql);\n" content += "\t$table_rows = array();\n" content += "\tif(DB_numRows($res) > 0) {\n" content += "\t\t$idx = 0;\n" content += "\t\twhile ( $row = DB_fetchArray($res) ) {\n" content += "\t\t\t$ligne = array();\n" content += "\t\t\t$ligne['row_data'] = \"
\\n\";\n" content += "\t\t\t$ligne['row_data'] .= \"
Karobas.fr
84 / 93
Année 2004 - 2005 border=\\\"0\\\"> | \\n\";\n" content += "\t\t\t$ligne['row_data'] .= \"" for f in feilds[1:]: content += "\".protectVar($row['"+ f[0] + "']).\" | " content += content += content += content += content += content += '''content
"\";\n" "\t\t\t$ligne['row_data'] .= \"
\\n\";\n" "\t\t\t$table_rows[] = $ligne;\n" "\t\t}\n" "\t}\n" "\t$tpl->assign(\"table_rows\");\n" += "\t$tpl->cache_stop();\n"'''
content += "}\n" content += "$tpl->done();\n" content += "\n" content += "include('../footer.inc'); ?>\n" self.cfile.write(content) self.cfile.close() os.chdir('..') def buildAdminCreatePages(self, classname, feilds, fk_key, fk_table, pk_key): os.chdir('./pages') self.filename = 'admin_' + classname.lower() + '_create.php' self.cfile = open(self.filename, 'w') var = "$vars = array(" content content content content content content content content content content content content
= " include('../header.inc');\n" += "\tdefine ('GZIP', FALSE);\n" += "\trequire_once \"tpl.class.php\";\n" += "\trequire_once \"dcc.class.php\";\n" += "\n" += "\t$feilds = array();\n" += "\t$tpl = new dcc(\"" + classname.lower() + "\", \"modif\");\n" += "\tif ($tpl->cache_start()) {\n" += "\t\t$obj = new " + classname.capitalize() + "();\n" += "\t\t$obj->fill();\n" += "\n" += "\t\tif($_POST['submit'] == 'Ajouter') {\n"
for f in feilds[1:]: content += "\t\t\t$obj->set" + f[0].capitalize() + "($_POST['sub_" + f[0].lower() + "']);\n" content += "\t\t\t$obj->commit();\n" content += "\t\t}\n\n" var += "\n\t\t\t\t'page_header' => \"" var += "\",\n" var var var var var var
+= += += += += +=
"\t\t\t\t'uid' => (int)$uid," "\n\t\t\t\t'vid' => (int)$vid," "\n\t\t\t\t'tri' => (int)$tri," "\n\t\t\t\t'page' => (int)$pag," "\n\t\t\t\t'nbppage' => (int)$nb," "\n\t\t);\n\n"
content += "\t\t" + var content += "\t\t$tpl->assign($vars);\n\n" ''' content += "