Aplicatie Software Pentru Gestiunea Resurselor Umane

Cuprins

Domeniul resurselor umane

1.1 Descriere generală

Resursele umane sunt primele resurse strategice ale unei organizații (societate comercială, instituție, asociație, etc), în noua societate informațională capitalul uman înlocuind capitalul financiar ca resursă strategică.

Managementul resurselor umane – MRU – este activitatea de management răspunzătoare de toate deciziile și acțiunile care afectează relația dintre o organizație și membrii ei.

Un management riguros al resurselor umane presupune eficacitate în următoarele activități de management:

managementul fluxului de personal – recrutare, selecție, adaptare pe post, inducție și socializare, mentorat, promovare internă, managementul ieșirii din organizație;

managementul performanței – evaluarea performanțelor, feed-back-uri consistente la atingerea sau nu a obiectivelor personale, de echipă sau organizaționale, recompense și beneficii;

management organizatoric – definirea posturilor, a organigramei, a zonelor de responsabilitate, formarea echipelor sau a grupurilor de muncă și instruirea angajaților;

managementul comunicării – implicarea angajaților în activitățile decizionale, dezvoltarea comunicării ascendente și descendente, păstrarea corectitudinii procedurale și a eticii organizaționale;

Managerul departamentului resurse umane este subordonat direct managerului general, care definește responsabilitatile în mod similar cu celelalte activități importante din organizație.

Managerul departamentului de resurse umane asigură îndeplinirea responsabilităților departamentului condus în ce privește :

Planificarea și managementul resurselor umane în concordanță cu obiectivele și managementul general al organizației;

Recrutarea și angajarea personalului pe principiul competenței;

Elaborarea programelor de calificare și/sau perfecționare profesională;

Elaborarea unui sistem de salarizare care să stimuleze obținerea unor rezultate deosebite;

Elaborarea unui sistem de stimulare nefinanciară a personalului;

Dezvoltarea comunicării organizaționale;

Integrarea rapidă a noilor angajați și elaborarea programelor de carieră pentru întregul personal al organizației.

1.2 Soluții software aplicabile în domeniu

Soluțiile informatice reprezintă o cerință obligatorie pentru un management eficient al resurselor umane.

Ținând cont de importanța managementului resurselor umane pentru companii, sistemele informatice dedicate este necesar sã dispunã de capacitãți pentru exploatarea și integrarea cât mai eficientã a diverselor fluxuri informaționale. În acest sens, soluțiile de acest tip este necesar sã rãspundã tuturor elementelor specifice culturii organizaționale din cadrul companiei.

Soluțiile informatice de HR trebuie sã dispunã de funcții dedicate evaluãrii performanțelor angajaților sau gestiunii eficiente a pregãtirii profesionale. De asemenea, pentru procesele de recrutare sunt alocate în general resurse importante, sistemele informatice specializate putând contribui substanțial la reducerea costurilor aferente acestor procese.

În ultimii ani, piața soluțiilor pentru managementul resurselor umane a înregistrat o evoluție importantã. Creșterea în volum a acestui sector IT a fost însoțitã de diversificarea și creșterea nivelului calitativ al sistemelor informatice pentru managementul resurselor umane. “Metamorfoza” produsã în cazul aplicațiilor de resurse umane a fost desigur o consecințã a cerințelor utilizatorilor unor asemenea soluții și a unei nevoi acute de informații tot mai amănunțite privind resursele umane din cadrul companiilor.

Una dintre tendințele pieței soluțiilor de resurse umane constã în integrarea tot mai profundã a acestora cu sistemele de tip ERP (Enterprise Resource Planning) din cadrul companiilor. Astfel, au fost dezvoltate module specializate care sunt integrate complet în cadrul sistemelor informatice. Integrarea aplicațiilor de acest tip în cadrul sistemelor ERP este mai cu seamã o cerințã pentru modulele de salarizare, în acest mod fiind asigurat un nivel ridicat de operativitate în derularea proceselor informaționale și financiare.  Astfel, vor putea fi generate în mod automat operațiuni contabile în modulele financiare ale sistemului ERP sau vor putea fi  realizate plãțile cãtre salariați.

Bazele de date și depozitele sunt un element omniprezent în cele mai diverse domenii. Fie cã vorbim de soluții din categoria aplicațiilor ERP (Enterprise Resource Planning), fie cã vorbim de alte categorii de aplicații, elementul fundamental pentru funcționarea acestora îl constituie sistemul de gestiune a bazelor de date. Un alt lucru important care trebuie oferit de astfel de solutii îl constituie posibilitatea de dezvoltare simplã a rapoartelor pe baza informațiilor incluse în bazele de date.

Având în vedere aceste considerente, tot mai multe companii care au în portofoliu soluții ERP au dezvoltat module specializate pentru analiza datelor și sintetizarea informațiilor de management al resurselor umane. Integrarea unor tehnologii BI(Business Intelligence) avansate cu soluțiile de baze de date de referințã existente pe piațã a permis companiilor sã implementeze funcții pentru procesarea eficientã a datelor acumulate prin soluțiile de acest tip.

De asemenea, multe produse ERP disponibile pe piața din România dispun de o componentã de raportare extrem de puternicã, care asigurã adaptarea situațiilor de ieșire ale sistemului informatic la necesitãțile de business.

Integrarea unor instrumente sintetizatoare pentru modul de derulare a proceselor de business este o altã trãsãturã pentru aplicațiile de acest tip. Prin intermediul “panourilor de comandã” din aplicațiile ERP, managerii vor putea lua decizii în timp real pentru managerii HR.

Arhitectura client-server constituie o stare de facto pentru majoritatea sistemelor informatice actuale și element de bazã pentru sistemele informatice de resurse umane. Indiferent care este domeniul cãruia îi este dedicat sistemul informatic (economic, social, științific etc.), arhitectura client-server s-a impus ca o condiție sine qua non pentru oferirea unei accesibilitãți cât mai mari la resursele informaționale oferite de sistemele de gestiune a bazelor de date. Rãspândirea Internetului, combinatã cu nevoia tot mai acutã de mobilitate și accesibilitate, au determinat generalizarea acestei arhitecturi în cadrul aplicațiilor de resurse umane.

1.3 Aplicații software existente

Există o gamă variată de soluții software concrete, acestea diferențiindu-se prin gradul de automatizare a procesului de management de resurse umane. Astfel, în timp ce unele aplicații se axează în principal pe un singur aspect din activitatea managerului de resurse umane (aplicații de salarizare, aplicații de gestiune a angajaților), altele vizează oferirea unui tool complex, de tip ERP.

Printre aplicațiile de gestiune a resurselor umane, se numără:

JobOutlook este o suită de aplicații software de asistență în procesul de recrutare online. Joboutlook centralizează și automatizează gestiunea CV-urilor și job-urilor și lucrează cu cele mai importante site-uri de recrutare online din Romania.

Sal este un program de salarii și resurse umane, care îndeplinește următoarele funcții: calculul oricărui element de salariu (taxe angajat, taxe angajator, concedii medicale, concedii de odihnă, avansuri, prime, sporuri, rețineri, etc.), calculul de salarii în valută, generarea de rapoarte standard (declarații de șomaj, CAS, sănătate, fișe fiscale), analize complexe de costuri salariale.

Clarvision este un sistem multicompanie (poate ține evidența pentru un număr nelimitat de sucursale, filiale, puncte de lucru și firme), sistem multilingv (permite configurarea limbii în care vor fi afișate toate meniurile în sistem, pe fiecare stație de lucru în parte), suportă lucrul cu mai multe valute, cu conversia automată a valorii dintr-o valută în alta, conversie automată în unități de măsură alternative, sistem multi-utilizator, permițând adăugarea ulterioară de utilizatori suplimentari

Sincron este un software de recrutare, o soluție dedicată activității companiilor de recrutare și plasare a forței de muncă sau departamentelor HR din cadrul companiilor.

1.4 Aplicația software propusă

Aplicația propusă oferă posibilitatea utilizării mai multor baze de date, fără a impune restricții utilizatorului referitoare la baza de date. Astfel, oferind o interfață care poate interacționa cu orice bază de date, aplicația permite vizualizarea și modificarea de bază a informațiilor din baza de date fără a fi necesar ca utilizatorul (firma de resurse umane) să cunoască informații despre structura internă a bazei de date, pe care să le furnizeze pentru a putea obține informațiile necesare. Tot ce trebuie să „știe” utilizatorul este numele bazei de date, host-ul acesteia și, desigur, username și parolă pentru aceesarea bazei de date.

Aplicația propusă reprezintă, practic, o interfață generică la o bază de date, care permine cu un minimum de efort din partea utilizatorului lucrul acestuia cu baza de date, fără a fi necesare cunoștințe tehnice sau de gestiune a bazelor de date.

Desigur că această interfață generică poate fi folosită pentru orice bază de date, nefiind necesară restricționarea la domeniul resurselor umane. Însă, pentru domeniul resurselor umane, acest gen de aplicație este inovatoare întrucât majoritatea soluțiilor software oferite pe piață în acest domeniu sunt dependente de structura și tipul bazei de date, necesitând efortul conjugat al programatorilor cu al angajaților firmei de resurse umane care cunosc structura bazei de date folosite. Dezavantajul acestei abordări, de creare a unei aplicații software personalizate, care își construiește o bază de date proprie pe care o manipulează este aceea că face necesară, pentru firma utilizatoare, înlocuirea bazei de date folosite până la acel moment cu o nouă bază de date, cea creată de aplicație și pe care o prelucrează aplicația. De regulă firmele de resurse umane manifestă reticență în fața cerinței de a-și înlocui baza de date deja folosită, și care este probabil populată, cu o nouă bază de date.

Aplicația propusă înlătură acest neajuns. Serviciile oferite, de vizualizare, editare (adăugare, ștergere), filtrare, filtrare avansată, printare, sunt disponibile pentru orice bază de date, nefiind necesară înlocuirea bazei de date folosite de firma respectivă cu una nouă.

Descrierea funcționalității aplicației

2.1 Obiectivul aplicației

Aplicația de față se intitulează “RU Manager” și își propune să fie o aplicație inovativă de gestionare a unei baze de date aplicat la și particularizat pe domeniul resurselor umane. Oiectivul propus în realizarea acestui program este de a nu impune, pe cît posibil, nici o restricție utilizatorului în utilizarea ei, fiind pe de o parte intuitivă în utilizare și pe de altă parte foarte flexibilă și adaptabilă resurselor software moștenite de la utilizator.

Elementul de inovație adus de această aplicație îl constituie independența codului și a interfeței de baza de date folosită. Această soluție lasă la latitudinea utilizatorului alegerea de a folosi baza de date internă oferită de aplicație sau propria bază de date sau, de ce nu, mai multe baze de date în același timp.

Această facilitate permite folosirea istoricului de date stocate de utilizator în propriile baze de date cu propriile lor structuri, fără a-l obliga să mute o cantitate posibil imensă de date și să se adapteze la noua bază de date.

Un alt beneficiu al acestei facilități este posibilitatea utilizatorului de a-și gestiona toate bazele de date pe care le folosește sau pe care le păstrează doar pentru istoric cu aceeași interfață.

Un ultim argument în favoarea acestei facilități este orientarea utilizatorului spre reînnoirea bazei de date și a structurii acesteia cu una nouă mai funcțională și mai robustă la orice moment dorește, putînd folosi ambele baze de date (cea nouă și cea veche) simultan pînă ce datele sunt mutate între cele două baz două baze de date sau, de ce nu, oprirea introducerilor de date în baza de date veche și păstrarea acesteia pentru istoric impunînd folosirea bazei de date noi pentru noile date. Toate aceste decizii sunt mult mai facile cînd poți gestiona toate bazele de date cu aceeași interfață, făcînd ca toate soluțiile de baze de date ale utilizatorului să aibă același acoperiș.

În acest fel, o firmă ce se ocupă de gestionarea resurselor umane nu este obligată să foloseasca sau să își recreeze baza de date pentru a se putea folosi de aplicația de față.

După cum am enunțat în capitolul anterior, domeniul resurselor umane nu mai este la începuturi iar firmele ce se ocupă de această gestiune folosesc deja în cea mai mare parte baze de date și în mare parte chiar aplicații de gestiune a acestora. Independența de baza de date însă ar aduce acestei aplicații un avantaj considerabil în persoana adaptabilității maxime la contextul fiecărui utilizator.

Din punct de vedere tehnic aplicația de față își propune să facă un “proof of concept” al ideii prezentate în paragrafele anterioare, astfel că funcționalitatea și mai ales suportul pentru diferite platforme (configurații software, sisteme de operare, browsere) și diferite baze de date este limitat. Descrierea amănunțită a acestor limitări precum și propuneri de evoluție în sensul înlăturării acestora se pot regăsi în capitolul „Limitări și direcții de dezvoltare a aplicației”- capitolul 7 al lucrării de față.

Aplicația de față este testată și funcționează pe următoarele configurație software și cu următoarele restricții:

a) Baze de date

La acest moment nu există suport decît pentru baze de date MySQL Community Server 5.0+; acest lucru nu semnifică faptul că nu se pot folosi și versiuni inferioare de baze de date MySQL sau versiunea comercială, însă aplicația nu a fost testată pe alte versiuni.

b) Browsere

Browserele suportate sunt Internet Explorer 6.0+ și Mozilla Firefox 2.0+. Și în acest caz, aceste browsere au fost cele foslosite pentru a testa aplicația. Acest lucru nu semnifică faptul că aplicația nu poate rula pe alte versiuni ale acestor browsere, doar că aceste versiuni au fost folosite în testare.

Aplicația este cel mai probabil că nu funcționează cu alte tipuri de browsere (Opera, Safari, etc.) datorită incompatibilităților la nivelul javascript și HTML DOM, tehnologii vitale pentru conținutul dinamic al aplicației și implicit pentru funcționalitatea acesteia.

c) Servere Web

Pentru funcționarea scripturilor server-side și în general a funcționalității dinamice a aplicației (cea mai mare parte) este necesar ca Serverul Web pe care rezidă aplicația respectiv browserul utilizat să suporte următoarele Tehnologii Web:

PHP 5.2+

AJAX (HTTP Requests)

Javascript și HTML DOM

CSS 2.0+

2.2 Funcționalități oferite

Principalele funcționalități oferite de aplicația de față în ceea ce ține de necesitățile unui departament sau firme de resurse umane sunt următoarele:

Vizualizarea tuturor tabelelor bazei de date

Adăugarea și ștergerea de date în/din tabelele bazei de date

Generarea de rapoarte pe baza tabelelor bazei de date

Căutarea de înregistrări în baza de date

Voi detalia fiecare din aceste funcționalități în subcapitolele ce urmează.

Vizualizarea bazei de date

În ceea ce ține de vizualizare tabelelor bazei de date, în interfața grafică este posibilă afișarea tuturor tabelelor bazei de date prin aceasta atingîndu-și scopul de vizualizare a angajaților, interviurilor, CV-urilor, candidaților, departamentelor, salariilor precum și a oricăror funcționalități oferite de baza de date folosită (reamintesc faptul că aplicația este independentă de baza de date, astfel că funcționalitatea ei este limitată la informațiile oferite de baza de date utilizată).

Vizualizarea tabelelor bazei de date permite și crearea de vizualizări personalizate pornind de la o tabelă de bază (care se regăsește în baza de date). Astfel, se pot face expandări ale referințelor externe prezente în tabela de bază de la care s-a pornit, se pot șterge coloane din vizualizarea personalizată și se pot redenumi coloanele acestei vizualizări, toate acestea fără a afecta structura sau consistența bazei de date.

Adăugarea și ștergerea datelor în/din baza de date

Cea de-a doua funcționalitate importantă oferită de aplicația de față este posibilitatea de adăugare de date în tabelel bazei de date, posibilitatea de a modifica date existente precum și de a șterge înregistrări din baza de date. Acest lucru este posibil pornind de la o tabelă de bază (prezentă în baza de date) ce este afișată la fel ca în cazul vizualizării, cu posibilitatea în acest caz de a edita datele tabelei.

Această modalitate de editare a tabelelor evită folosirea formularelor clasice în scopul de a familiariza utilizatorul cu un singur tip de reprezentare a tabelelor, care este adeseori cel mai intuitiv: reprezentarea sub formă de tabel cu posibilitatea editării oricărei celule a acestuia.

Rapoarte și listări ale bazei de date

Funcționalitatea Print, sau raportare, permite utilizatorului să afișeze orice tabel, chiar și un tabel expandat, deci obținut din mai multe tabele ale bazei de date, în mod user friendly, pe o pagină html care nu mai conține și meniul de navigare inițial. Altfel spus, această funcționalitate permite maximizarea unui view efectuat pe baza de date, urmând ca, pentru a printa, utilizatorul să folosească opțiunea Print a browser-ului.

Am optat pentru această modalitate de raportare pentru a reduce efortul necesar din partea utilizatorului pentru obținerea unui raport. Astfel, în loc să folosească zone diferite din aplicație pentru editarea bazei de date și pentru raportare, utilizatorul poate să editeze, să-și definească view-uri, să sorteze, să aplice filtre pe tabele, apoi să obțină din tabelul rezultat un raport, folosind funcția Print.

Ajutorul oferit utilizatorului

Aplicația oferă utilizatorului o scurtă prezentarea a funcționalității sale prin intermediul funcției și ecranului Help, care conțin descrierea succintă a celorlalte funcționalități.

Din Help, utilizatorul află care sunt principalele operații pe care le poate executa: View Database, Edit Database, Search Database, Print Tables, precum și descrierea comenzilor disponibile pentru fiecare operație.

2.3 Interfața cu utilizatorul

Interfața cu utilizatorul oferită de aplicația de față este o interfață grafică web dinamică și ergonomică. Aceasta este organizată în cîteva pagini de bază cu conținut dinamic volatil. Toate datele prezentate sunt dependente de baza de date folosită, cea mai mare parte a părților ce asigură funcționalitatea aplicației fiind generate în funcție de baza de date.

Acest tip foarte dinamic de intefață web oferă utilizatorului întregul avantaj al independenței de platforma sau sistemul fizic folosit oferit de intefețele web (fiind nevoie doar de un browser). Autogenerarea elementelor funcționale adaptat la baza de date folosită îi da acestei interfețe un aspect de universalitate și flexibilitate maximă.

Elementele principale ce constituie structura de bază a paginilor web (cu excepția paginii introductive) sunt Meniul Principal, Panel-ul de control, butonul Logout și structura grafică ce dă aspectul ergonomic al aplicației. Acestea sunt singurele elemente statice din această interfață web.

Fig 1: Structura de bază a paginilor interfeței web

Din imaginea de ansamblu de mai sus se remarcă în primă instanță meniul principal ce oferă acces la categoriile de operații generice (independente de baza de date) oferite de aplicația de față.

Fiecare buton direcționează către una din paginile specifice (Pagina Principală, Pagina de vizualizare, Pagina de editare, Pagina de listare, Pagina de căutare și Pagina de ajutor ) ce constituie structura interfeței web a aplicației.

Pe parcursul subcapitolelor următoare este prezentată funcționalitatea oferită de fiecare din aceste pagini.

Fig 2: Meniul principal al interfeței web

Cel de-al doilea element important al structurii de bază îl reprezintă Panel-ul de control prezent pe fiecare pagină ce va afișa controalele specifice generate în mod dinamic în funcție de baza de date și specificul paginii.

Fig 3: Panel-ul de control

Un alt element al structurii de bază îl reprezintă meniul din partea de jos a paginii, care are aceeași funcționalitate ca și meniul principal.

Fig 4: Meniul din partea inferioară a paginilor

Butonul Logout realizează deconectarea de la baza de date și încheierea sesiunii curente. Pagina afișată după folosirea Logout este pagina introductivă.

Pagina introductivă și gestiunea utlilizatorilor

Interfața dispune în primul rînd de o pagină introductivă ce prezintă facilitățile de login precum și un formular pentru definirea conexiunii la una sau mai multe baze de date.

Fig 5: Pagina introductivă

Meniul de configurare si login este disponibil în urma unui click pe logo-ul produsului.

Fig 6: Pagina introductivă – meniul de login

Aplicația de față nu face gestiunea utilizatorilor. Numele de utilizator si parola cerute trebuie să fie cel al bazei de date la care urmează să se conecteze aplicația.

Atunci când nu a fost înregistrată nicio bază de date, valoarea default a elementului drop-down este „No Database Configured”.

Fig 7: Formularul de Login

Pentru configurarea unei baze de date, se folosește butonul „New Database”, care afișeaza o casetă de dialog, cu câmpurile: DB Host, DB Type, DB Name.

Fig 8: Formularul de definire a bazei de date

Butonul Test din caseta de configurare a unui utilizator verifică dacă baza de date configurată, împreună cu username-ul și parola, funcționează. Dacă da, este afișat un mesaj de confirmare:

Fig 9: Mesajul de confirmare a conexiunii definite

Dacă baza de date nu există, mesajul afișat este:

Fig 10: Mesajul afișat pentru o bază de date inexistentă

Butonul Save salvează baza de date configurată, în fișierul „config.txt” din folder-ul action. După apăsarea butonului Save utilizatorului i se solicită printr-o casetă de dialog numele logic al bazei de date.

Fig 11: Caseta de definire a numelui logic al bazei de date

La următoarea folosire a aplicației, bazele de date configurate anterior vor fi citite din fișierul „config.txt”, iar utilizatorul va putea alege după numele logic definit anterior la care bază de date se va loga. Numele logic nu este obligatoriu să fie identic cu numele fizic al bazei de date, astfel că utilizatorul își poate personaliza configurațiile, nefiind nevoit să rețină nume lungi sau nesugestive.

După configurarea bazei de date, se poate trece la logarea la respectiva bază de date.

Fig 12: Logarea la baza de date

Pagina principală – HOME

Fig 13: Pagina HOME

Pagina HOME păstrează elementele statice menționate anterior: meniul principal, panel-ul de control și structura grafică. Pagina conține un mesaj de întâmpinare, oferă detalii despre conexiune (baza de date folosită, tipul și host-ul acesteia) și ghidează utilizatorul succint în folosirea meniului principal.

Fig 14: Detaliile despre conexiune din HOME page

Din pagina HOME se pot folosi direct toate operațiile, cu excepția operației Print, pentru care este necesară deschiderea unui tabel. Dacă utilizatorul apasă Print, va primi un mesaj de ghidare:

Fig 15: Mesaj de direcționare din pagina HOME

Pagina Vizualizări Tabele – VIEW

Fig 16: Pagina View

Pagina VIEW conține elementele statice comune tuturor paginilor (cu excepția paginii introductive): Meniul Principal, Panel-ul de control, butonul Logout și structura grafică.

Aici, panel-ul de control conține butoane generate dinamic care reprezintă tabelele bazei de date. Numărul de butoane depinde de structura bazei de date, mai exact de numărul tabelelor acesteia.

Fig 17: Conținutul panel-ului de control din View pentru o bază de date cu 6 tabele

Pentru a-și defini view-uri personalizate, utilizatorul trebuie să selecteze, inițial, un tabel, executând click pe butonul cu numele tabelului. Tabelul se deschide în dreapta, deasupra acestuia aflându-se, în continuare, detalii despre baza de date folosită.

Fig 18: Vizualizarea de bază a unui tabel

Asupra tabelului utilizatorul poate opera sortare, prin click pe numele unei coloane. De asemenea este posibilă realizarea de join-uri ad-hoc, prin efectuarea unui click pe numele unei coloane, foreign key, ținând în același timp apăsată tasra Ctrl. Coloana foreign key va fi ștearsă din view-ul astfel obținut, pe principiul că expandarea unui tabel folosind foreign key nu necesită pentru utilizator și afișarea valorilor foreign, ci doar afișarea informațiilor la care oferă acces aceste valori, din tabelul referit.

Este posibilă expandarea tuturor coloanelor foreign key din tabelul vizualizat, dar doar din tabelul de bază de la care s-a pornit. Astfel, chiar dacă prin expandare au devenit vizibile coloane din alte tabele, care la rândul lor sunt foreign key-uri, acestea nu pot fi expandate.

Dacă utilizatorul încearcă să expandeze o coloană care nu este foreign key în tabelul de bază, va primi un mesaj explicativ:

Fig 19: Mesaj explicativ, la încercarea de expandare a unei coloane care nu e foreign key

Fiecare coloană are un meniu pop-up, care se deschide prin click pe pictograma-săgeată din stânga numelui, și care permite câteva operații de bază: redenumire, ștergere, sortare.

Fig 20: Meniul pop-up al unei coloane

Selectarea comenzii Remove are ca efect ștergerea coloanei din vizualizarea curentă, dar nu și din baza de date. Principiul este ca modificările de vizualizare să fie strict pentru vizualizare, și să nu altereze structura bazei de date. Desigur, ce se urmărește aici în subsidiar este a nu permite utilizatorilor să șteargă coloane din baza de date, lucru ce ar putea afecta grav integritatea acesteia.

Selectarea comenzii Rename deschide o casetă de dialog care solicită utilizatorului noul nume.

Fig 21: Dialog-box pentru redenumirea unei coloane

Redenumirea operează, de asemenea, doar asupra vizualizării, nu și în baza de date. Este permisă redenumirea coloanelor foreign key însă, în acest caz, utilizatorul nu va mai putea expanda după coloana respectivă, din view-ul curent, întrucât se pierde legătura cu tabelul referit.

Din pagina VIEW este posibilă și editarea datelor, prin click pe butonul Edit, dar această funcționalitate va fi descrisă în continuare.

Pagina Formulare – EDIT

Fig 22: Pagina EDIT

Diferă de pagina VIEW prin faptul că butoanele de sub tabelul afișat sunt toate active, cu excepția butonului Edit, care este inactiv pentru a marca faptul că întreaga pagină curentă este destinată editării. Este posibilă selectarea unei linii prin dublu click într-o celulă a ei.

Fig 23: Bara de butoane de editare

Adăugarea unei linii se face cu ajutorul butonului Add Row. Revine utilizatorului sarcina de a completa cu informație utilă linia nou introdusă. Pentru a opera modificările și asupra bazei de date, se folosește butonul Apply changes.

Butonul Discard changes anulează modificările. Folosirea oricăruia dintre butoanele Apply changes și Discard changes are ca efect schimbarea stării butoanelor de editare. Pentru a reveni în modul de editare, se va folosi butonul Edit.

Fig 24: Bara de butoane după aplicarea sau anularea modificărilor

Ștergerea unei linii se face în următorii pași: se selectează linia, se apasă butonul Mark for deletion. Linia va fi evidențiată pe fundal roșu. Apăsarea butonului Apply changes va opera ștergerea liniei, împreună cu celelalte modificări.

Fig 25: Ștergerea unei linii

În situația prezentată în fig. 25, dacă se va selecta Apply changes, linia 2, marcată cu roșu, va fi ștearsă din tabel, linia 4, care este doar selectată, nu și marcată pentru ștergere va rămâne nemodificată.

Pagina Rapoarte – PRINT

Fig 26: Pagina PRINT

Pagina PRINT afișează tabelul din care a fost apelată, „user-friendly”, în browser, fără a mai afișa meniul principal și structura grafică a celorlalte pagini. Practic, operația Print realizează o maximizare a vizualizării selectate (tabel simplu, tabel expandat). Utilizatorul poate printa folosind meniurile browser-ului.

Revenirea din pagina PRINT în aplicație se face cu ajutorul butonului BACK.

Pagina Căutare – SEARCH

Căutarea într-un tabel al bazei de date (tabel simplu sau expandat) este disponibilă utilizatorului atât din pagina SEARCH, cât și din paginile VIEW și EDIT, prin intermediul butonului Search, care deschide următoarea casetă de dialog:

Fig 27: Fereastra de căutare

Căutarea se poate face case-sensitive, dacă se activează opțiunea Match case sau după întregul cuvânt specificat, dacă se activează opțiunea Match whole word. Utilizatorul poate selecta în care coloane să efectueze căutarea, default căutarea se face în toate coloanele vizualizării curente. Dacă după obținerea unui rezultat se continuă căutarea, cu un alt criteriu, acesta este aplicat, implicit, tabelului rămas după prima căutare, astfel că, prin definiri succesive de criterii de căutare se realizează o filtrare pe mai multe niveluri. Utilizatorul poate căuta începând cu criteriul cel mai general, apoi specializând, progresiv, criteriul.

Rezultatul căutării este evidențiat prin ștergerea din vizualizare a liniior care nu satisfac criteriul. De asemenea, fereastra de căutare afișează numărul liniilor găsite.

Fig 28: Un rezultat de căutare

În cazul în care utilizatorul apasă Cancel, se va reveni la tabelul inițial, eliminându-se toate filtrele.

Elemente de gestiunea bazelor de date

3.1 Sisteme de Gestiune a Bazelor de Date

Un sistem de gestiune al bazei de date (SGBD – Data Base Management System) este un produs software care asigură interacțiunea cu o bază de date, permițând definirea, consultarea și actualizarea datelor din baza de date.

Datele organizate într-o bază de date sunt structurate pe mai multe niveluri de abstractizare și de percepție:

Nivelul fizic (intern) este descris de schema fizică a datelor (bit, octet, adresă).

Nivelul conceptual este descris de schema conceptuală a datelor (articol, înregistrare, zonă) și reprezintă viziunea programatorilor de sistem asupra datelor.

Nivelul logic este descris de una din schemele logice posibile ale datelor și reprezintă viziunea programatorului de aplicație asupra datelor.

Nivelul virtual (extern) reprezintă viziunea utilizatorului final asupra datelor.

Un alt concept legat de gestiunea bazelor de date este independența datelor, care poate fi:

Independență fizică: posibilitatea modificării schemei fizice a datelor fără ca aceasta să implice modificarea schemei conceptuale, a schemei logice și a programelor de aplicație. O modificare a structurii fizice nu va afecta aplicația și reciproc, modificări ale aplicației vor lăsa nealterată structura fizică de date.

Independență logică: posibilitatea modificării schemei conceptuale a datelor fără ca aceasta să implice modificarea schemei logice și a programelor de aplicație.

Independență față de strategiile de acces: care permite programului să precizeze data pe care dorește să o acceseze, dar nu modul cum accesează această dată. SGBD-ul va stabili drumul optim de acces la date.

Utilizatorii unei baze de date pot fi:

utilizatori nespecialiști (conversaționali), care folosesc baza de date doar pentru obținerea unor rezultate, fără a cunoaște mecanismele interne de funcționare a bazei de date;

utilizatori specialiști, care cunosc atât noțiuni de utilizare, cât și de programare a unei baze de date;

administratorul bazei de date.

Obiectivele fundamentale ale unui SGBD sunt:

Independența fizică.

Independența logică.

Manipularea datelor de către neinformaticieni.

Administrarea centralizată a datelor.

Neredundanța datelor.

Coerența datelor.

Partajabilitatea datelor.

Securitatea și confidențialitatea datelor.

Arhitectura unui SGBD este constituită din 3 nivele:

nivelul intern (baza de date fizică structura internă de stocare a datelor fișiere care conțin datele, articolele din fișiere, drumurile de acces la articole etc.);

nivelul conceptual (modelul conceptual, schema conceptuală);

nivelul extern (modelul extern, subschema, vizualizarea).

Pentru o bază de date particulară există o singură schemă internă, o singură schemă conceptuală, dar pot exista mai multe scheme externe.

3.2 Standarde existente

SQL (Structured Query Language – Limbaj Structurat de Interogare) este un limbaj de programare specific lucrului cu bazele de date, devenit un standard în domeniu (standardizat ANSI-ISO), fiind cel mai popular limbaj utilizat pentru creearea, modificarea, regăsirea și manipularea datelor de către SGBD-urile (Sistemele de Gestiune a Bazelor de Date) relaționale.

Atât ANSI, cât și ISO s-au ocupat de stabilirea de norme pentru a consacra SQL-ul ca limbaj standard. Sunt de amintit implementările ANSI X3.135 (1992), ANSI X3.168 (1989), ANSI/ISO/IEC 9075-3 (1995), SQL2/ISO, SQL/400. (Uneori, aceste emanări de standarde sunt referite prin anul apariției.) În activitatea de reglementare a standardelor SQL sunt desigur implicate și marile firme producătoare de IT (IBM, ORACLE, Informix, Microsoft).

3.3 Baze de Date MySQL

MySQL este un sistem de gestiune a bazelor de date relațional, produs de compania suedeză MySQL AB și distribuit sub Licența Publică Generală GNU. Este cel mai popular SGBD open-source la ora actuală, fiind o componentă cheie a stivei LAMP (Linux, Apache, MySQL, PHP).

Cu MySQL se pot construi aplicații în orice limbaj major. Există multe scheme API disponibile pentru MySQL ce permit scrierea aplicațiilor în numeroase limbaje de programare pentru accesarea bazelor de date MySQL, cum are fi: C, C++, C#, Borland Delphi, Java, Perl, PHP, Python, FreeBasic, etc., fiecare dintre acestea folosind un tip spefic API. O interfață de tip ODBC denumită MyODBC permite altor limbaje de programare ce folosesc această interfață, să interacționeze cu bazele de date MySQL cum ar fi ASP sau Visual Basic.

Pentru a administra bazele de date MySQL se poate folosi modul linie de comandă sau, interfețe grafice, cum sunt: MySQL Administrator și MySQL Query Browser. Un alt instrument de management al acestor baze de date este aplicația phpMyAdmin.

MySQL poate fi rulat pe multe dintre platformele software existente: AIX, FreeBSD, GNU/Linux, Mac OS X, NetBSD, Solaris, SunOS, Windows 9x/NT/2000/XP/Vista.

Elemente de Interfețe Web pentru baze de date

4.1 Construirea unei interfețe web eficiente și ergonomice

Un aspect important al design-ului unei interfețe web îl reprezintă ergonomia acesteia, ușurința, eficiența și rapiditatea cu care utilizatorul poate obține informațiile necesare. De aceea prima etapă în proiectarea unui site trebuie să ofere o soluție pertinentă întrebării: Cum va naviga utilizatorul?

De asemenea trebuie stabilit setul de culori pentru background/fonturi/link-uri. E de dorit să se folosească puține culori, iar acestea sã nu agreseze vizitatorul. Logo-ul site-ului este recomandat să fie o imagine simplã, sugestivã si de dimensiuni cât mai mici. Prezenta unui logo nu îngreuneazã accesarea sitului, deoarece acesta se încarcã în memoria cache a browserului la accesarea primei pagini. E preferabilă folosirea unui singur logo în toate paginile, pentru familiarizarea utilizatorului cu logo-ul, dar și pentru coerența mesajului. Numãrul de nivele al site-ului nu trebuie sã fie prea mare, pentru a nu plictisi vizitatorul.

Conform unor statistici referitoare la persoanele care utilizează des interfețe web, surfer-ilor nu le place scroll-ul, adicã nu le place sã coboare în josul paginii, de aceea este recomandată folosirea cu precauție a paginilor ample, care necesită derulare. De asemenea, surfer-ilor nu le place sã aștepte, iar așteptarea îi face adesea sã pãrãseascã site-ul, astfel că o interfață web cu utilizatorul trebuie să se încarce repede. Viteza de citire de pe monitor este cu 25% mai micã decât viteza de citire de pe hârtie, aspect care trebuie avut în vedere la proiectarea unei interfețe web interactive, care cere utilizatorului să completeze formulare sau să răspundă unor chestionare.

Există mai mulți indicatori care arată gradul de ergonomie al unei interfețe web, cum ar fi: prezența instrumentelor de navigare, adicã a legãturilor cu:
pagina anterioarã, pagina urmãtoare, partea de sus a paginii sau cu
principalele sectiuni ale sitului, punerea la dispoziția utilizatorului a motoarelor de cãutare ( search engines ) în interfață sau pe web, timpul de încărcare a unei pagini, care nu trebuie sã fie mai lung de 15 secunde, indiferent de conexiune, gradul de accesibilitate al informației utile (informatia utilã nu trebuie sã fie la o distantã mai mare de douã click-uri), gradul de vizibilitate al paginii (folosirea redusă a scroll-ului), numărul nivelelor interfeței, dispunerea textului, care nu trebuie aranjat ca un bloc unitar de informatie, ci divizat prin linii orizontale sau imagini (frazele rezumat pot fi prezentate pe un fundal diferit colorat, eventual încadrate), modul de prezentare a informației, astfel încât efortul depus de utilizator pentru întelegerea și folosirea informației sã fie minim.

4.2 Tehnologii Web de bază

HTML

Unul din primele elemente fundamentale ale WWW ( World Wide Web ) este HTML (Hypertext Markup Language), care descrie formatul primar în care documentele sunt distribuite și văzute pe Web. Multe din trăsăturile lui, cum ar fi independența față de platformă, structurarea formatării și legăturile hipertext, fac din el un foarte bun format pentru documentele Internet și Web.

Primele specificații ale Web-ului au fost HTML, HTTP si URL.
HTML a fost dezvoltat inițial de Tim Berners-Lee la CERN în 1989. HTML a fost văzut ca o posibilitate pentru fizicienii care utilizează computere diferite de a schimba între ei informație utilizând Internetul. Erau prin urmare necesare câteva trăsături: independența de platformă, posibilități hypertext și structurarea documentelor. Independența de platformă înseamnă că un document poate fi afișat în mod asemănător de computere diferite (deci cu fonturi, grafică și culori diferite), lucru vital pentru o audiență atât de variată.

Hipertext înseamnă că orice cuvânt, frază, imagine sau alt element al documentului văzut de un utilizator (client) poate face referință la un alt document, ceea ce ușurează mult navigarea între multiple documente sau chiar în interiorul unui aceluiași document. Structurarea riguroasă a documentelor permite convertirea acestora dintr-un format în altul precum și interogarea unor baze de date formate din aceste documente.

SGML si HTML

Tim Berners-Lee a utilizat ca model SGML (Standard Generalized Markup Language), un standard internațional în plină dezvoltare. SGML avea avantajul unei structurări avansate și al independenței de platformă dar proiectarea lui a avut în vedere mai mult structura semantică a documentului decât modul de formatare. Flexibil, SGML putea fi descris ca o specificare pentru descrierea altor formate. Utilizatorii puteau crea noi formate (DTD, Document Type Definitions) care puteau fi înțelese de orice produs soft SGML pur și simplu prin citirea mai intâi a definițiilor noilor formate.

HTML este pur și simplu un DTD, deci o aplicație a SGML. În primii ani de evoluție HTML a crescut lent, în principal pentru că îi lipseau posibilitățile de a descrie publicații electronice profesionale; limbajul permitea un oarecare control asupra fonturilor dar nu permitea inserarea graficii. În 1933, NCSA a îmbogățit limbajul pentru a permite inserarea graficii și au construit primul navigator grafic, Mosaic. Au urmat apoi contribuții ad hoc ale diverselor firme care au adus adăugiri limbajului HTML (adăugiri și nu îmbogățiri pentru că unele taguri nu erau în conformitate cu principiile generale ale SGML) astfel încât, prin 1994 limbajul părea scăpat de sub control. Urmarea a fost ca la prima conferinta WWW din Geneva (Elveția) s-a constituit un grup (HTML Working Group) a cărui primă misiune a fost formalizarea HTML într-un DTD al SGML, lucru care s-a concretizat în HTML Level 2 (sau HTML 2.0; Nivelul 1, deci HTML 1.0, a fost proiectat de Tim Berners-Lee). Importanta acțiunii acestui grup constă în faptul că, odată standardizat, limbajul poate fi apoi extins într-un mod mai controlat la alte nivele.

CSS

O dată cu HTML 4.0 organizația World Wide Web Consortium (W3C) a introdus conceptul de “stil” (engl. STYLE), iar în scurt timp toate browser-ele au adoptat această schimbare majoră, dar care a avut un efect foarte bun asupra dezvoltării ulterioare ale site-urilor. Developerii web și designerii pot modifica foarte ușor acum site-urile din punctul de vedere al stilului, folosind fisiere .css .

CSS sau “Cascading Style Sheets” ajută la afișarea elementelor de HTML în funcție de instrucțiunile pe care browser-erele le găsesc în locurile indicate. Se pot defini în patru locuri diferite stilurile pe care le vor respecta elementele de HTML ale unei pagini și, exact ca în programarea orientată pe obiect, se moștenesc și se pot suprascrie. Primul nivel este cel ar browser-ului, care este stilul implicit. Pe nivelul doi se găsește stilul declarat într-un fișier extern, salvat cu extensia .css. Stilul declarat în interiorul paginii, în cadrul elementului <head> este pe nivelul 3. Iar ultimul nivel, cel care este cel mai aproape de elementul HTML de afișat, este cel definit chiar în cadrul elementului în cauză.

Folosirea foilor de stil CSS într-un document HTML Web are numeroase avantaje, cum ar fi:

reduce considerabil efortul depus atunci când se dorește modificarea aspectului și aranjării elementelor din paginile web; în loc de parcurgerea fiecărui document în parte și modificări asupra fiecărui element, e suficientă operarea de modificari doar asupra foii de stiluri, care controlează aceste elemente; modificările se vor reflecta asupra oricărui segment de text care a fost formatat cu stilul respectiv;

oferă control crescut asupra aspectului și plasării textului în pagină;

reduce "încurcătura" produsă de multitudinea de deschideri și închideri ale etichetelor html care descriu elementele individuale ale textului;

procesul de modificare a diferitelor elemente din pagina se simplifică întrucât modificarea ulterioară a unui stil se reflectă automat în toate zonele în care a fost folosit;

pot fi create și aplicate foi css nu doar pentru controlul aspectului fonturilor, ci și a altor elemente de formatare ale paginii.

4.3 Standarde existente

Standarde HTML

Standardul oficial HTML este World Wide Web Consortium (W3C), care este afiliat la Internet Engineering Task Force (IETF). W3C a enunțat câteva versiuni ale specificației HTML, printre care și HTML 2.0, HTML 3.0, HTML 3.2, HTML 4.0 HTML 4.01. În același timp, autorii de browsere, cum ar fi Netscape și Microsoft, au dezvoltat adesea propriile "extensii" HTML în afara procesului standard și le-au încorporat în browserele lor. În unele cazuri, cum ar fi cazul Netscape, aceste extensii au devenit standarde de facto adoptate de autorii de browsere.

HTML 2.0, elaborat în iunie 1994, este standardul pe care ar trebui să-l suporte toate browserele curente – inclusiv cele mod text. HTML 2.0 reflectă concepția originală a HTML ca un limbaj de marcare independent de obiectele existente pentru așezarea lor în pagină, în loc de a specfica exact cum ar trebui să arate acestea.

Specificatia HTML 3.0, enunțată în 1995, a încercat să dezvolte HTML 2.0 prin adăugarea unor facilități precum tabelele și un mai mare control asupra textului din jurul imaginilor. Deși unele din noutățile HTML 3.0 erau deja folosite de autorii de browsere, multe nu erau încă. În unele cazuri, taguri asemănătoare implementate de autorii de browsere au devenit mai răspândite decât tagurile "oficiale". Specificația HTML 3.0 acum a expirat, deci nu mai este un standard oficial.

În mai 1996, W3C a scos pe piata specificatia HTML 3.2, care era proiectată să reflecte și să standardizeze practicile acceptate la scară largă. Deci, HTML 3.2 include tagurile HTML 3.0 ce erau adoptate de autorii de browsere ca Netscape și Microsoft plus extensii HTML răspândite. În Bilantul asupra HTML, W3C recomandă ca providerii de informații să utilizeze specificația HTML 3.2.Versiunile curente ale majorității browserelor ar trebui să suporte toate, sau aproape toate aceste taguri.

De asemenea există extensii Netscape și Microsoft care nu fac parte din specificația HTML 3.2, ori pentru că sunt mai puțin utilizate, ori au fost omologate după apariția HTML 3.2. Pentru că navigatorul Netscape a fost printre primele browsere care suportă anumite taguri HTML 3.0, iar Netscape deține în jur de 70% din piața de browsere, mulți au crezut eronat că toate extensiile Netscape (incluzând taguri ca <BLINK> și facilități ca ferestrele) fac parte din HTML 3.0 sau HTML 3.2.

HTML 4.0 este larg utilizat și au fost deja publicate specificațiile HTML 4.01.

Documentele HTML sunt documente în format ASCII și prin urmare pot fi create cu orice editor de texte. Au fost însă dezvoltate editoare specializate care permit editarea într-un fel de WYSIWYG deși nu se poate vorbi de WYSIWYG atâta vreme cât navigatoarele afișează același document oarecum diferit, în functie de platforma pe care rulează.

Au fost de asemenea dezvoltate convertoare care permit formatarea HTML a documentelor generate (și formatate) cu alte editoare. Evident conversiile nu pot păstra decât parțial formatările anterioare deoarece limbajul HTML este încă incomplet.

4.4 Limbaje de programare web

JavaScript

JavaScript a fost creat în 1995 de Netscape pentru a aduce un plus de dinamism paginilor web. JavaScript oferă un limbaj simplu de scripting pentru a aduce modificări „Live” (numele inițial al limbajului a fost LiveScript) paginilor web html pe partea de client.

În primii ani de viață utilizarea JavaScript era o aventură datorită lipsei unui standard universal, datorită evoluției rapide în lumea browserelor si mai ales datorită evoluției pe drumuri divergente a principalilor producători (Microsoft, Netscape și Opera). Codul scris în JavaScript pentru Internet Explorer, Netscape și Opera nu producea decât rareori același rezultat. Existau deseori diferențe mari și între rezultatele produse de versiuni succesive ale aceluiași browser. Din aceste motive majoritatea scripturilor trebuiau scrise în mai multe variante similare, ceea ce era deosebit de neplăcut pentru developeri. De asemenea a durat mult timp până au apărut unele medii de dezvoltare și debugging avansate. Astfel a durat destul de mult până când tehnologia a ajuns la maturitate și a ajuns destul de avansată pentru a fi folosită pe scară largă. Astăzi, o gamă largă de aplicații web (Google Mail, Yahoo Mail) folosesc această tehnologie.

Scripturile Javascript se execută de către browser și sunt incluse deci în pagina HTML ce se afișează pe calculatorul clientului. Scripturile pot fi incluse complet în pagina HTML sau pot fi create în fisiere separate și referite în pagina HTML. În ambele cazuri, marcajul HTML folosit este <script>.

Marcajul <script> poate fi inclus atât în interiorul marcajului <head>, cât si în cadrul marcajului <body>. Diferența este că în primul caz scriptul se execută la încărcarea paginii, în timp ce în al doilea caz se execută în momentul întâlnirii marcajului. Din acest motiv în secțiunea <head> sunt incluse funcțiile ce vor fi folosite în restul paginii iar în <body> sunt în general apelurile funcțiilor.

Un script javascript poate conține definiții de funcții, definiții de clase (cu mențiunea că Javascript nu este un limbaj orientat obiect în adevăratul sens al cuvântului neavând o mare parte din mecanismele unui limbaj orientat obiect), apeluri ale funcțiilor definite sau ale funcțiilor oferite de browser.

Sintaxa Javascript este foarte asemănătoare cu sintaxa Java. Cuvintele cheie sunt cu mici excepții aceleași. Javascript este un limbaj cu tipare dinamică – verificarea tipului datelor efectuându-se la rulare. Astfel la declararea unei variabile nu se va specifica tipul acesteia ci doar că este vorba de o variabilă. Se foloseste cuvântul cheie var.

Declararea unei funcții se face folosind cuvântul cheie function urmat de numele funcției, de lista de parametri și de un bloc ce conține codul funcției. Ca și în Java, cuvântul cheie return este folosit pentru a întoarce rezultatul funcției.

Metodele de iterare în Javascript sunt aproape identice cu cele din Java. Sintaxa pentru instrucțiunile for, while și do..while este identică cu cea din Java. În plus față de Java, Javascript oferă instrucțiunea foreach ce iterează pe proprietățile unui obiect spre deosebire de alte limbaje de programare (C#, PHP) unde foreach iterează pe elementele unei colecții.

Javascript pune la dispoziție și un număr mare de obiecte ce pot fi folosite în marea majoritate a browserelor printre care Array, Math, Date, String.

PHP

Acronimul PHP provine de la "PHP Hypertext Preprocessor". PHP reprezintă un popular limbaj "server-side" prezent în jumătate din serverele Apache existente. PHP a fost creat de către Rasmus Lerdorf în 1994 pentru a-și monitoriza CV-ul său online. Rasmus și-a distribuit free "tool-ul", denumit inițial Personal Home Page. PHP a devenit în timp un puternic limbaj de scripting server-side, open-source și multi-platformă.
PHP poate fi încapsulat în HTML și se folosește în conjuncție cu acesta pentru a realiza aplicații web cu un conținut generat dinamic. Spre deosebire de JavaScript, codul PHP este procesat pe server, clientul primește doar cod HTML.
PHP este prezent pe Linux, Windows, HP-UX, Solaris, Mac OS X, OpenBSD. Suportul este oferit de servere web care includ: Apache, IIS, PWS, Netscape, iPlanet, Xitami, OmniHTTPd și altele.

PHP poate fi obținut gratuit, fiind un produs al mișcării open-source. PHP oferă suport pentru acces la aproape 20 de servere de baze de date diferite printre care: MySQL, Oracle, MS-SQL, Sybase, Informix, Adabas D, DB2, dBase, Ingres, PostgreSQL, FilePro. PHP suportă de asemenea ODBC, permițându-i să se conecteze la servere de baze de date prin intermediul driver-ului ODBC.

PHP prezintă suport pentru XML, atât prin intermediul SAX, cât și DOM. De asemenea permite crearea "on-the-fly" de fișiere PDF. Comunicarea cu alte servicii este permisă prin COM, HTTP, IMAP, LDAP, NNTP, POP3, SNMP și altele.

PHP-ul este unul din cele mai folosite limbaje de programare server-side, conform unui studiu efectuat de Netcraft în aprilie 2002, apărând pe 9 din cele 37 milioane de domenii cercetate în studiu. De asemenea, există un grafic al creșterii folosirii PHP-ului pe site-ul oficial. Popularitatea de care se bucură acest limbaj de programare se datorează următoarelor caracteristici :

Familiaritatea: sintaxa limbajului este foarte ușoară combinând sintaxele unora din cele mai populare limbaje Perl sau C;

Simplitatea: sintaxa limbajului este destul de liberă. Nu este nevoie de includere de biblioteci sau de directive de compilare, codul PHP inclus într-un document executându-se între marcajele speciale;

Eficiența: PHP-ul se folosește de mecanisme de alocare a resurselor, foarte necesare unui mediu multiutilizator, așa cum este web-ul;

Securitatea: PHP-ul pune la dispoziția programatorului un set flexibil și eficient de măsuri de siguranță;

Flexibilitatea: fiind apărut din necesitatea dezvoltării web-ului, PHP a fost modularizat pentru a ține pasul cu dezvoltarea diferitelor tehnologii. Nefiind legat de un anumit server web, PHP-ul a fost integrat pentru numeroasele servere web existente: Apache, IIS, Zeus, server, etc.;

Gratuitatea: Dezvoltarea PHP-ului sub licența open-source a determinat adaptarea rapidă a PHP-ului la nevoile web-ului, eficientizarea și securizarea codului.

HTML DOM

Javascript este utilizat în special pentru a modifica modul de afisare sau conținutul unei pagini web. Javascript trebuie să poată accesa și modifica structura documentului de afisat. În acest scop este utilizat DOM.

DOM (Document Object Model) reprezintă un API standardizat de W3C pentru a manipula documente HTML sau XML valide. Prin intermediul Javascript se poate accesa dinamic arborele DOM al unei pagini web.

Obiectul rădăcină al acestui arbore este document. Prin intermediul acestui obiect putem accesa orice alt obiect sau marcaj din document. Metoda write este folosită pentru a scrie un sir de caractere în locația curentă a documentului. O altă metodă foarte des utilizată a acestui obiect este getElementById(id). Această metodă întoarce nodul (marcajul) care are marcajul id. În cazul în care sunt folosite id-uri în document putem găsi foarte rapid un anumit nod. O dată găsit acest nod putem să-i accesăm sau să-i schimbăm proprietățile.

Toate elementele unei pagini web au asociate o listă de evenimente pe care le pot recepționa. Un element oarecare nu poate recepționa toate aceste evenimente. Pentru a vedea ce evenimente pot fi gestionate de un anumit element trebuie consultată referința în DOM pentru elementul respectiv. Două exemple de evenimente destul de comune sunt:

onmouseover: se declansează atunci când cursorul mouse-ului intră în zona elementului

onmouseout: se declansează când cursorul mouse-ului părăsește zona elementului.

Fiecărui astfel de tip de eveniment i se poate asocia un cod Javascript.

AJAX

AJAX – Asynchronous Javascript and XML este o tehnică de programare web ce permite efectuarea unor cereri http către serverul web, prin intermediul cărora se poate actualiza o pagină web fără a se efectua reîncărcarea sa completă.

Obiectul Javascript care permite efectuarea acestor cereri asincrone se numeste XMLHttpRequest. Deși comportarea acestui obiect este standard în fiecare browser inițializarea sa este diferită pentru fiecare browser.

Principalele metode, proprietăŃi si evenimente oferite de XMLHttpRequest sunt:

open – creează o conexiune GET sau POST către un url dat ca parametru

send – efectuează cererea către server

onreadystatechange – evenimentul care este declanșat de schimbarea valorii proprietății readystate, proprietate ce poate avea următoarele valori (conform standardului):

UNSENT = 0

OPENED = 1

HEADERS_RECEIVED = 2

LOADING = 3

DONE = 4

Printre avantajele Ajax se numără:

Lățimea de bandă folosită

Datorită faptului că generează local pagina HTML și downloadează doar scriptul JavaScript și datele, paginile web Ajax pot părea că se încarcă relativ repede. De asemenea, mulțumită funcționalității "load on demand" a conținutului, unele pagini web încarcă stub-uri ale event handler-elor iar apoi rulează funcțiile "on the fly". Această tehnică reduce considerabil lățimea de bandă folosită pentru aplicații web. În plus, clientul de Ajax împarte workload-ul cu serverul, astfel încât încărcarea acestuia din urma este redusă.

Separarea în data, format, style, și function

Un alt beneficiu de ordin mai puțin pragmatic este că Ajax tinde să încurajeze programatorii să separe clar metodele, funcțiile și format-urile folosite în diferite aspecte ale transferului de informații pe web. Deși Ajax poate părea ca o amestecătură de multiple limbaje și tehnici, iar programatorii sunt liberi să le aleagă pe cele pe care le preferă, aceștia sunt adeseori împinși către a face o distincție clară între:

– datele brute sau conținutul de transferat, care în mod uzual este îmbrăcat în XML iar uneori obținut dintr-o bază de date server side.

– formatul sau structura paginii web, care este aproape întotdeauna HTML sau XHTML, care este apoi disponibilă spre a fi manipulată dinamic prin DOM.

– elementele de stil, ale paginii, totul, de la fonturi la plasarea imaginilor sunt configurate prin CSS.

– funcționalitatea paginii, obținută printr-o combinație de:

Javascript pe browserul client (DHTML),

HTTP și XMLHttp standard sau comunicație client la server, Server side scripting și/sau programe în orice limbaj pentru a primi și a răspunde la requesturi corespunzător.

Printre dezavantajele Ajax se numără:

Integrarea în browser

Pagina încărcată dinamic nu apare în history-ul browser-ului, astfel încât butonul Back nu se va comporta dupa așteptări. S-au propus variate soluții la această problemă, precum folosirea de IFRAME-uri care să înregistreze în history schimbările.

O alta problemă este că update-ul paginii dinamice face dificilă folosirea de bookmark-uri. O soluție propusă la această problemă este folosirea URL-ului, mai specific a acelei părți ce determină porțiunea dinamică, pentru a permite întoarcerea la starea inițială a paginii. Această soluție îmbunătățește și comportamentul butonului Back.

Timpi de răspuns

Intervalul de timp dintre request și response trebuie avut în vedere când se folosește Ajax. Se poate întâmpla ca vizitatorii paginii să observe o întârziere în încărcarea interfeței aplicației web, întârziere la care nu s-ar aștepta și pe care nu ar înțelege-o. În plus, când o întreagă pagină este încărcată există un glitch în afișare când se schimbă conținutul. Folosirea de tool-uri care să informeze utilizatorul că se desfășoară activități în background este deseori folosită ca soluție la astfel de probleme de latență.

Motoarele de căutare

Site-urile care folosesc Ajax trebuie să pună la dispoziție motoarelor de căutare un Sitemap la o locație publică pe care motorul o poate citi, întrucât motoarele de cautare de obicei nu execută codul javascript din pagină.

Necesitatea folosirii JavaScript

Ajax depinde de JavaScript pentru funcționare, iar JavaScript este deseori implementat în moduri diferite pe diferite browsere. Din această cauză site-urile care folosesc JavaScript trebuie testate în mai multe browsere pentru a verifica să nu apară probleme de compatibilitate. Sunt multe cazuri în care codul JavaScript trebuie scris de două ori, o versiune pentru IE, de exemplu, și alta pentru Mozilla. Astfel de cazuri însă sunt mai rare odată cu apariția de librării de abstractizare a JavaScript precum Prototype JavaScript Framework sau Jquery. Aceste librării abstractizează limbajul rezolvând transparent problemele de compatibilitate între diferite browsere.

Java Servlets

Atunci când un browser trimite o cerere către un server, răspunsul pe care îl primește poate fi unul static (o simplă pagină web care este stocată pe serverul de web) sau unul dinamic, generat eventual în urma consultării unei baze de date.

Pentru trimiterea unui răspuns dinamic către client se pot folosi servleții.

Servleții reprezinta clase JAVA, al căror cod sursă este scris de programator, după care, prin compilare, se va obține fișierul având extensia .class, ce va fi copiat în anumite directoare specifice fiecărui server de Web în parte. Un servlet poate fi invocat prin intermediul unui URL, caz în care se va executa o metodă a acestei clase și va avea ca efect construirea răspunsului și transmiterea acestuia către client intr-un anumit format.

Pașii realizării unui servlet sunt urmatorii:

Se vor importa, obligatoriu, pachetele java.io.*; javax.servlet.* și javax.servlet.http.*. Utimele două pachete se află in servlet.jar ce este distribuit odată cu Tomcat-ul și nu cu JDK-ul.

Se va scrie o clasă ce va extinde obligatoriu clasa HttpServlet.

Se va suprascrie următoarea metodă a acestei clase:

void doGet (HttpServletRequest, HttpServletResponse) throws IOException, ServletException.

În cadrul acestei metode, se va apela următoarea metodă a clasei HttpServletResponse: PrintWriter getWriter(), în scopul de a obține obiectul prin care se va transmite ieșirea către client. După aceasta se va trimite ieșirea cu ajutorul metodei println a clasei PrintWriter.

Un lucru important este că ciclul de viață al unui servlet este de la prima invocare de către oricare din clienți până la oprirea serverului de Web, în sensul că aceste clase ce extind HttpServlet se instanțiază doar o singură dată după care la invocarea de metode cum ar fi doGet se execută în fire de execuție (thread-uri) diferite. Exceptie face cazul în care servlet-ul implementează interfața SingleThreadModel. În acest din urmă caz orice cerere trebuie să aștepte ca tratarea celorlaltor cereri anterioare să se fi terminat, lucru ce degradează drastic performanțele aplicației. Din acest motiv utilizarea SingleThreadModel nu este recomandată, mai ales că implementarea sa nici măcar nu garantează eliminarea tuturor problemelor generate de lucrul cu mai multe thread-uri. Se recomandă alternativa implementării unui cod cât mai thread-safe care nu folosește interfața SingleThreadModel.

CGI Script

CGI, prescurtare de la Common Gateway Interface, este un protocol standard de comunicare între documentele Web și aplicațiile localizate pe serverul Web.

Scripturile CGI sunt programe care respectă acest protocol. Un script CGI este, deci, un program care comunică într-un anumit mod cu o pagină web. Existența acestui protocol de comunicare între programele de pe server și documentele Web permite crearea unor pagini interactive și dinamice, lucru care nu poate fi făcut folosind doar HTML.

Atunci când browserul solicită un script CGI aflat pe server, serverul lansează în execuție scriptul și îi transmite acestuia headerele HTTP de cerere primite de la browser. După ce execuția scriptului se încheie, rezultatele sunt transmise serverului, care formatează headerele de răspuns și le transmite browserului pentru ca acesta să afișeze rezultatele. O altă posibilitate este ca scriptul să conțină instrucțiuni prin care headerele de răspuns sunt configurate chiar de script și transmise de acesta direct browserului.

Indiferent dacă solicită un document Web sau un script, browserul trebuie să cunoască locația serverului Web și numele fișierului solicitat. Această informație îi este transmisă browserului prin intermediul atributului action al etichetei <FORM> care primește drept valoare adresa URL a scriptului stocat pe server. De obicei, scripturile CGI sunt stocate pe server într-un director special destinat lor, care se numește cgi-bin.

Datele sunt colectate prin intermediul formularelor și transmise de către browser la serverul pe care este instalat scriptul CGI prin intermediul headerelor HTTP de cerere.

Cele mai importante headere HTTP de cerere sunt Get și Post. Prin intermediul atributului method al etichetei <FORM> este specificat ce tip de header HTTP de cerere este utilizat pentru a transfera datele la server. Dacă metoda folosită este Get, datele sunt transmise prin intermediul adresei URL. Dacă este folosită metoda Post datele sunt transmise sub forma unui fișier separat.

Este recomandată folosirea metodei Post atunci când volumul de date transmise este mare (depășește 1024 de octeți, lungimea maximă a unui URL). După ce metoda de transmitere este stabilită, browserul creează un header HTTP de cerere prin care este identificată localizarea scriptului pe server și apoi transmite headerul la server. Serverul recepționează headerul de cerere și apelează scriptul CGI.

Odată datele ajunse la server, scriptul este lansat în execuție iar datele îi sunt transmise acestuia prin intermediul unui tip special de variabile numite variabile de mediu (dacă folosiți metoda Get) sau prin intermediul fișierului standard de intrare (dacă folosiți metoda Post).

Scriptul CGI execută sarcinile pentru care a fost conceput și obține anumite rezultate care urmează a fi transmise browserului prin intermediul unor headere HTTP de răspuns. De obicei, scriptul configurează un singur header de răspuns, și anume Content-Type. Acest header comunică browserului tipul de date care îi vor fi returnate: documente HTML, imagini sau alt tip de fișiere. Serverul adaugă și el headere HTTP de răspuns suplimentare, apoi strânge toate rezultatele și headerele de răspuns într-un pachet care este transmis browserului. Browserul îl recepționează și afișează informațiile primite în modul descris de headerele de răspuns.

După lansarea în execuție, scriptul CGI execută sarcinile pentru care a fost construit. Sarcinile pe care le poate realiza un script sunt diverse și numeroase. Iată câteva exemple:  

manipularea informațiilor introduse prin intermediul formularelor

manipularea hărților de imagini

generarea contoarelor care monitorizează numărul de accesări ale paginii (hit counters)

construirea de motoare de căutare

administrarea licitațiilor on-line

crearea de aplicații interactive cum ar fi forumurile sau camerele de chat

crearea și manipularea bazelor de date

Structura unui script CGI conține următoarele secțiuni: citirea datelor introduse prin intermediul formularului, prelucrarea datelor și efectuarea sarcinilor impuse de programator, afișarea rezultatelor.

În general, răspunsurile pe care scriptul le transmite serverului se împart în două categorii:

Sarcina a fost îndeplinită cu succes

Sarcina nu a fost îndeplinită, au apărut erori.

Dacă execuția scriptului este s-a desfășurat cu succes iar sarcinile sale au fost îndeplinite, acesta transmite serverului un răspuns în consecință. Dacă, din diverse motive, au apărut erori la execuția scriptului iar acesta nu și-a dus sarcinile la bun sfârșit, serverului îi este prezentat un mesaj de eroare, care trebuie să conțină informații despre natura erorii apărute.

Tehnologii folosite

În conceperea aplicației de față a fost necesară utilizarea unei game relativ vaste de tehnologii web cu diverse aplicabilități și utilizări în cadrul acestui proiect. Pe parcursul acestui capitol voi elabora impactul, necesitatea și scopurile folosirii pentru fiecare tehnologie web ce și-a adus aportul în acest proiect.

Se vor prezenta și secvențe de cod, API-uri, seturi de comenzi și scenarii de utilizare, precum și apresieri generale la adresa acestor tehnologii web în contextul proiectului.

5.1 Baze de Date MySQL

Rolul în proiect

Aplicația ce face obiectul lucrării de față își propune, așa cum am menționat în capitolele introductive, să fie prin excelență o interfață de gestionare a bazelor de date. În cadrul acestui proiect, baza de date ce a fost utilizată și pe care aplicația a fost și testată este MySQL Community Server 5.0

Este lesne de înțeles rolul de sursă de informație și actor în relația cu utilizatorul pe care motorul MySQL îl are în cadrul acestui proiect.

În contextul suportului ce poate fi găsit în tehnologiile web pentru acest tip de bază de date, acest suport este variat și în marea majoritate open-source. În cadrul acestui proiect layer-ul de interfațare între codul web și serverul de baze de date a fost ales ca modulul MySQLi (MySQL improved) pus la dispoziție de PHP. Acest modul pune la dispoziția unui programator în PHP un API complet de metode de lucru atît de administrare cît și de interogare a acestui tip de baze de date.

În continuare voi încerca, împreună cu o clasificare a funcțiilor din această librărie, și o descriere a utilității lor în cadrul librăriei precum și a utilizării în cadrul proiectului.

De asemenea se vor mai pune în evidență expresiiile SQL standard sau particulare MySQL ce s-au utilizat în cadrul acestui proiect.

Librăria MySQLi

Modalitatea de interfațare cu apicația web a bazelor de date MySQL este făcută în acest proiect pe baza librăriei PHP de extensie numită mysqli (mysql improved).

Aceasta se dorește a fi o evoluție a librăriei inițiale (mysql) ce oferă ca și distincție principală de predecesorul său, accesul la aceeași funcționalitate folosind o abordare orientată pe obiect.

MySQLi, deși păstrează și metodele procedurale care erau folosite de predecesorul său, nu păstrează și backward compatibility cu acesta, numele funcțiilor procedurale, de altfel cu aceeași funcționalitate, avînd nume diferite.

Tot dialogul între baza de date și aplicația web RU manager se face prin intermediul scripturilor PHP avînd ca element de interfațare extensia MySQLi.

Pentru detalii despre implementarea acestei librării, funcții disponibile studiați capitolul „Detalii de implementare”.

Registru de comenzi SQL specifice MySQL

MySQL poate fi numit, pe bună dreptate, un server de baze de date ce folosește ca limbaj SQL standard. Pe lîngă suportarea standardului însă, acest server de baze de date suportă și extensii specifice de limbaj SQL. Niiunee din acestea nu afectează standardul ci doar oferă unelte suplimentare utilizatorului. În multe cazuri pe parcursul dezvoltării bazate pe MySQL acestea se dovedesc extrem de utile adăugînd la intuitivitatea limbajului SQL utilizat.

Aplicația software de față încearcă să profite la maxim de aceste specficități de limbaj ce permit scrierea de cod de complexitate redusă pentru implementarea diferitelor funcționalități precum generarea Panel-ului de control bazat pe o listare a tabelelor bazei de date.

Acestea sunt cîteva din comenzile SQL specifice bazelor de date MySQL și sunt folosite în marea majoritate și în codul aplicației de față:

Instrucțiunea SHOW

Această instrucțiune are o sintaxă complexă ce permite utilizatorului să o utilizeze pentru o gamă variată de scopuri. Printre aceste scopuri posibile regăsim obținerea de informații despre bazele de date, tabele, coloane, precum și informații despre starea serverului. Specificația de sitaxă este următoarea:

SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [LIKE 'pattern']

SHOW CREATE DATABASE db_name

SHOW CREATE FUNCTION funcname

SHOW CREATE PROCEDURE procname

SHOW CREATE TABLE tbl_name

SHOW DATABASES [LIKE 'pattern']

SHOW ENGINE engine_name {LOGS | STATUS }

SHOW [STORAGE] ENGINES

SHOW ERRORS [LIMIT [offset,] row_count]

SHOW FUNCTION CODE sp_name

SHOW FUNCTION STATUS [LIKE 'pattern']

SHOW GRANTS FOR user

SHOW INDEX FROM tbl_name [FROM db_name]

SHOW INNODB STATUS

SHOW PROCEDURE CODE sp_name

SHOW PROCEDURE STATUS [LIKE 'pattern']

SHOW [BDB] LOGS

SHOW MUTEX STATUS

SHOW PRIVILEGES

SHOW [FULL] PROCESSLIST

SHOW PROFILE [types] [FOR QUERY n] [OFFSET n] [LIMIT n]

SHOW PROFILES

SHOW [GLOBAL | SESSION] STATUS [LIKE 'pattern']

SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern']

SHOW [OPEN] TABLES [FROM db_name] [LIKE 'pattern']

SHOW TRIGGERS

SHOW [GLOBAL | SESSION] VARIABLES [LIKE 'pattern']

SHOW WARNINGS [LIMIT [offset,] row_count]

Instrucțiunea DESCRIBE

{DESCRIBE | DESC} tbl_name [col_name | wild]

DESCRIBE oferă informații despre coloanele unui tabel, avînd o funcționalitate similară cu cea a instrucțiunii SHOW COLUMNS. Începînd cu MySQL 5.0.1, această instrucțiune oferă informații și despre view-uri.

mysql> DESCRIBE city;

+––––+–––-+––+––+–––+–––––-+

| Field | Type | Null | Key | Default | Extra |

+––––+–––-+––+––+–––+–––––-+

| Id | int(11) | NO | PRI | NULL | auto_increment |

| Name | char(35) | NO | | | |

| Country | char(3) | NO | UNI | | |

| District | char(20) | YES | MUL | | |

| Population | int(11) | NO | | 0 | |

+––––+–––-+––+––+–––+–––––-+

5 rows in set (0.00 sec)

Instrucțiunea EXPLAIN

EXPLAIN tbl_name

sau:

EXPLAIN [EXTENDED] SELECT select_options

Instrucțiunea EXPLAIN poate fi folosită fie ca sinonim al instrucțiunii DESCRIBE, fie ca o metodă d a obține informații despre cum execută serverul MySQL o instrucțiune SELECT:

EXPLAIN tbl_name este sinonimă cu DESCRIBE tbl_name și SHOW COLUMNS FROM tbl_name.

Cînd o instrucțiune SELECT este precedată de EXPLAIN, MySQL afișează informații despre planul de execuție al interogării. Astfel, MySQL explică exact cum va procesa instrucțiunea SELECT, inclusiv informații despre joinurile existente între tabele și în ce ordine vor fi realizate acestea.

Instrucțiunea HELP

HELP 'search_string'

Instrucțiunea HELP oferă informații inline din manualul de referință MySQL. Pentru o funcționare corectă trebuie verificat faptul că tabelele de Help de pe serverul MySQL din baza de date mysql sunt populate cu infomațiile de indexare a capitolelor și topicurilor de documentație.

Cu ajutorul HELP se caută în tabelele menționate în paragraful anterior informația cerută oferindu-se inline răspunsuri pe topicuri legate de mysql. Căutarea nu este case-sensitive.

Instrucțiunea HELP înțelege mai multe tipuri de stringuri de căutare:

La nivelul cel mai general, se poate folosi contents pentru a obține o listă a topicurilor generale:

HELP 'contents'

Pentru a obține o listă de topicuri dintr-o anumită categorie, de exemplu Data Types, trebuie specificat numele categoriei:

HELP 'data types'

Exemplu: HELP 'replace' dă următorul rezultat:

name: REPLACE

description: Syntax:

REPLACE(str,from_str,to_str)

Returns the string str with all occurrences of the string from_str replaced by the string to_str. REPLACE() performs a case-sensitive match when searching for from_str.

example: mysql> SELECT REPLACE('www.mysql.com', 'w', 'Ww');

-> 'WwWwWw.mysql.com'

Alte instrucțiuni și subiecte ce prezintă interes dintre particularitățile MySQL sunt următoarele:

Tabelele INFORMATION_SCHEMA

Instrucțiunea REPLACE

Instrucțiunea TRUNCATE

5.2 PHP

Limbajul PHP este folosit extensiv în aplicația de față pentru lucrul cu baza de date. Toată interfațarea cu baza de date se face prin intermediul unor programe PHP.

De asemenea PHP este folosit și pentru mecanismul de login al aplicației și pentru suportul variabilelor superglobale ce oferă un environment de variabile de interes general în cadrul diverselor scripturi.

5.3 Javascript și HTML DOM

Javascript este folosit pentru suportul HTML DOM în schimbarea dinamică a conținutului paginilor aplicației Web.

Conținutul și aspectul dinamic al aplicației RU manager este dat de un set de funcții javascript ce generează astfel, în mod dinamic, interfața utilizatorului.

Această tehnologie este folosită și ca interfață la scripturile PHP prin intermediul AJAX.

5.4 CSS

În mare parte, aspectul grafic al aplicației este dat de stiluri setate prin CSS. Fișierul mo06f.css conține o varietate de setări pentru elemente de bază HTML atît în mod direct cît și prin definirea de clase de stil.

5.5 AJAX

Această tehnologie este liantul între interfața grafică și interfața cu baza de date. Din funcțiile javascript, prin intermediul AJAX se transmit cereri HTTP asincrone la server, acesta din urmă executînd scripturi PHP pentru extragerea sau depunerea de informații în / din baza de date.

Detalii de implementare

Bază de date MySQL gestionabilă prin RU Manager

Întrucât scopul aplicației este independența de baza de date folosită, teoretic, aplicația se poate folosi cu orice model de bază de date. În scopul exemplificării funcționalității, în lucrarea de față am folosit o bază de date cu următoarea structură:

Interfațarea cu baze de date MySQL folosind librăria MYSQLi

Această librărie pune la dispoziție un API orientat pe obiecte, anume o ierarhie de clase ce oferă metodele necesare, precum și un API de metode procedurale clasice.

Prezint în continuare mnemonica principalelor metode procedurale disponibile prin librăria MySQLi:

mysqli_affected_rows — numărul de rînduri afectate de ultima operație

mysqli_autocommit — setează opțiunea de autocommit a tranzacțiilor

mysqli_bind_param — Alias pentru mysqli_stmt_bind_param()

mysqli_bind_result — Alias pentru mysqli_stmt_bind_result()

mysqli_change_user — schimbă utilizatorul curent

mysqli_close — închide o conexiune la baza de date deschisă cu mysqli_connect

mysqli_commit — comitează tranzacția curentă

mysqli_connect_errno — codul de eroare în urma ultimei încercări de conectare

mysqli_connect_error — mesajul de eroare în urma ultimei încercări de conectare

mysqli_connect — funcția de conectare la baza de date

mysqli_errno — codul de eroare în urma ultimei operații

mysqli_error — mesajul de eroare în urma ultimei operații

mysqli_execute — Alias pentru mysqli_stmt_execute()

mysqli_fetch_array — întoarce rezultatul unei interogări sub forma unei matrici cu index numeric, asociativ sau mixt

mysqli_fetch_assoc — întoarce rezultatul unei interogări sub forma unei matrici cu index asociativ

mysqli_fetch_field — întoarce următorul cîmp din rezultat

mysqli_fetch_fields — întoarce o matrice de obiecte reprezentînd cîmpurile din rezultat

mysqli_fetch_lengths — întoarce o matrice cu dimensiunile cooanelor rezultatului

mysqli_fetch_object — întoarce rîndul curent din rezultat sub forma unui obiect

mysqli_fetch_row — întoarce un rînd din rezultat sub forma unei matrici

mysqli_fetch — Alias pentru mysqli_stmt_fetch()

mysqli_field_count — numărul de coloane din rezultat

mysqli_field_seek — mută pointerul rezultat la offsetul dorit în cîmpurile rezultatului

mysqli_field_tell — întoarce offsetul curent al pointerul rezultat în cîmpurile rezultatului

mysqli_free_result — eliberează memoria alocată pentru rezultat

mysqli_get_client_info — întoarce versiunea de client MySQL sub formă de string

mysqli_get_client_version — întoarce informații despre clientul MySQL

mysqli_get_host_info — tipul conexiunii subformă de string

mysqli_get_proto_info — versiunea protocolului MySQL folosit

mysqli_get_server_info — versiunea serverului MySQL folosit sub formă de string

mysqli_get_server_version — versiunea serverului MySQL folosit sub formă de număr întreg

mysqli_get_warnings – warning-urile obținute în urma ultimei operații

mysqli_info — informații despre ultima interogare

mysqli_init — inițializează MySQLi și întoarce o resursă de folosit ulterior pentru apel la metoda mysqli_real_connect()

mysqli_insert_id — valoarea id-ului autogenerat în ultima interogare

mysqli_kill — se face cerere la server pentru a trimite semnal de kill la un thread mysql

mysqli_more_results — verifică dacă mai sunt rezultate disponibile în urma unei interogări multiple

mysqli_multi_query — execută o interogare multiplă

mysqli_next_result — întoarce următorul rezultat al interogării multiple

mysqli_num_fields — numărul de cîmpuri dintr-un rezultat

mysqli_num_rows — numărul de rînduri dintr-un rezultat

mysqli_options — setare opțiuni

mysqli_param_count — Alias pentru mysqli_stmt_param_count()

mysqli_ping — face ping pe o conexiune și încearcă să se reconecteze dacă a picat conexiunea

mysqli_prepare — pregătește un statement pentru execuție

mysqli_query — interogare pe baza de date

mysqli_real_connect — deschide o conexiune cu un server MySQL

mysqli_real_query — execută o interogare SQL

mysqli_rollback — rollbaxk pe tranzacția curentă

mysqli_select_db — alege baza de date default

mysqli_set_opt — Alias pentru mysqli_options()

mysqli_sqlstate — eroarea SQLSTATE în urma ultimei operații

mysqli_ssl_set — pentru stabilirea de conexiuni securizate folosind SSL

mysqli_stat — întoarce informații despre starea sistemului

mysqli_stmt_affected_rows — numărul de rînduri modificate, șterse sau adăugate de execuția ultimului statement

mysqli_stmt_bind_param — Binds asignează variabile statement-ului curent ca și parametri

mysqli_stmt_bind_result — asignează variabile statement-ului curent ca și variabile de ieșire

mysqli_stmt_close — închide un statement pregătit

mysqli_stmt_data_seek — mută pointerul la locul dorit în rezultat

mysqli_stmt_errno — codul de eroare în urma execuției ultimului statement

mysqli_stmt_error — mesajul de eroare în urma execuției ultimului statement

mysqli_stmt_execute — execută o interogare pregătită (statement)

mysqli_stmt_fetch — întoarce rezultatul în variabilele de ieșire asignate

mysqli_stmt_field_count — numărul de cîmpuri din statement

mysqli_stmt_free_result — eliberează memoria entru statement

mysqli_stmt_get_warnings – warning-urile generate în urma execuției unui statement

mysqli_stmt_init — inițializează un statement și întoarce un obiect cu scopul folosirii acestuia în apelul ulterior al metodei mysqli_stmt_prepare

mysqli_stmt_insert_id — întoarce id-ul generat în urma ultimei operații INSERT

mysqli_stmt_num_rows — numărul de rînduri din rezultatul unui statement

mysqli_stmt_param_count — numărul de paramtri aiunui statement

mysqli_stmt_prepare — pregătește un statement SQL pentru execuție

mysqli_stmt_reset — resetează un statemnt pregătit

mysqli_stmt_sqlstate — eroarea SQLSTATE în urma operării ultimului statement

mysqli_stmt_store_result — transferă rezultatul statement-ului

mysqli_store_result — transferă rezultatul ultimei interogări

mysqli_thread_id — id-ul thread-ului conexiunii curente

mysqli_use_result — inițiază întoarcerea rezultatului

mysqli_warning_count — numărul de warningurigenerate de ultima interogare pe conexiunea dată

Folosirea funcțiilor procedurale de bază ale librăriei este ilustrată de următorul cod PHP:

<?php

/* Conectarea la un server MySQL */
$conexiune = mysqli_connect(
             'localhost',  /* Host-ul pe care este baza de date */
             'user',       /* Username-ul pentru baza de date */
             'password',   /* Passwordul corespunzător */
            'world');     /* baza de date ce va fi default */

if (!$link) {
   printf("Nu se poate face conexiunea cu serverul MySQL. Mesaj de eroare: %s\n", mysqli_connect_error());
   exit;
}

/* Operarea unei interogări pe baza de date */
if ($result = mysqli_query($conexiune, 'SELECT Name, Population FROM City ORDER BY Population DESC LIMIT 5')) {

   /* Obținerea rezultatului interogărilor */
    while( $row = mysqli_fetch_array($rezultat, MYSQLI_ASSOC) ){
        printf("%s (%s)\n", $row['Name'], $row['Population']);
    }

    /* Eliberarea memoriei ocupate de rezultatul interogării */
    mysqli_free_result($rezultat);
}

/* Închiderea conexiunii */
mysqli_close($conexiune);
?>

Codul următor arată cum se pot face aceleași operații de bază în varianta pe obiecte a librăriei.

<?php

/* Conectarea la un server MySQL */
$mysqli = new mysqli('localhost', 'user', 'password', 'world');

if (mysqli_connect_errno()) {
   printf("Nu se poate face conexiunea cu serverul MySQL. Mesaj de eroare: %s\n", mysqli_connect_error());
   exit;
}

/* Operarea unei interogări pe baza de date */
if ($rezultat = $mysqli->query('SELECT Name, Population FROM City ORDER BY Population DESC LIMIT 5')) {

    /* Obținerea rezultatului interogărilor */
    while( $row = $rezultat->fetch_assoc() ){
        printf("%s (%s)\n", $row['Name'], $row['Population']);
    }

    /* Eliberarea memoriei ocupate de rezultatul interogării */
    $result->close();
}

/* Închiderea conexiunii */
$mysqli->close();
?>

Conținut dinamic în pagini HTML

Bară de butoane generabilă dinamic folosind CSS

CSS în mo06f.css

/* =-=-=-=-=-=-=-[Menu Eight]-=-=-=-=-=-=-=- */

#menu8 ul {

font-family: Verdana, Arial, Helvetica, sans-serif;

font-size: 80%;

font-weight: bold;

list-style: none;

margin: 0;

padding: 0;

}

#menu8 {

width: 200px;

margin-top: 10px;

}

#menu8 li a {

text-decoration: none;

height: 32px;

voice-family: "\"}\"";

voice-family: inherit;

height: 24px;

}

#menu8 li a:link, #menu8 li a:visited {

color: #777;

display: block;

background: url(images/menu8.gif);

padding: 8px 0 0 20px;

}

#menu8 li a:hover {

color: #257EB7;

background: url(images/menu8.gif) 0 -32px;

padding: 8px 0 0 25px;

}

#menu8 li a:active {

background: url(images/menu8.gif) 0 -64px;

padding: 8px 0 0 25px;

}

HTML generat folosind interogări pe baza de date în SHOW_TABLES.PHP

$con = mysqli_connect($host, $usr, $pass);

if (!$con)
{
die('Could not connect: ' . mysqli_connect_error());
}

mysqli_select_db($con, $dbname);

$sql="SHOW TABLES";

$result = mysqli_query($con, $sql);

echo "<div id='menu8'><ul>";

while($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
{
foreach($row as $k => $v)
{
echo "<li><a href='#1' title='Table $v' onclick=\"loadTable('$v')\">$v</a></li>";
}
}

echo "</ul></div>";

mysqli_free_result($result);
mysqli_close($con);

Meniu drop-down generabil dinamic folosind Javascript

JAVASCRIPT în ACTION.JS

/*–––– dropdown MENU action –––––*/
var timer = null;

function showMenu(x, y, menuId)
{
if (document.getElementById(menuId))
{
document.getElementById(menuId).style.visibility = 'visible';
if (timer) clearTimeout(timer);
timer = null;
return
}

contents = document.getElementById("contents");

var Container = document.createElement('div');
contents.appendChild(Container);
Container.id = menuId;
Container.className = 'dropDownMenu';
Container.style.left = (x – 10) + 'px';
Container.style.top = (y – 10) + 'px';
Container.addEventListener('mouseout', mouseOutMenu, false);
Container.addEventListener('mouseover', mouseOverMenu, false);

var Menu = document.createElement('ul');
Container.appendChild(Menu);

var removeBtn = document.createElement('li');
removeBtn.innerHTML = "<a href='#1' onclick=\"removeColumn('" + menuId.substr(5) + "')\">Remove</a>";

var renameBtn = document.createElement('li');
renameBtn.innerHTML = "<a href='#1' onclick=\"renameColumn('" + menuId.substr(5) + "')\">Rename</a>";;

var sortBtn = document.createElement('li');
sortBtn.innerHTML = "<a href='#1' onclick=\"sortTable('" + menuId.substr(5) + "')\">Sort</a>";;

Menu.appendChild(removeBtn);
Menu.appendChild(renameBtn);
Menu.appendChild(sortBtn);
}

function mouseOverMenu()
{
this.style.visibility = 'visible';
if (timer) clearTimeout(timer);
timer = null;
}

function mouseOutMenu()
{
if (timer)
{
clearTimeout(timer);
}
timer = setTimeout("document.getElementById('" + this.id + "').style.visibility = 'hidden'", 1000);
}

CSS în mo06f.css

/*- Menu 3––––––––– */

.dropDownMenu {
width: 80px;
margin: 10px;
border-style: solid solid solid solid;
border-color: #000080;
border-size: 1px;
border-width: 1px;
font-size: 80%;
font-weight: bold;
position:absolute;
z-index: 3;
visibility:visible;
}

.dropDownMenu ul {
list-style: none;
margin: 0;
padding: 0;
visibility:inherit;
}

.dropDownMenu li a {
height: 32px;
height: 24px;
text-decoration: none;
height: 1%;
visibility: inherit;
}

.dropDownMenu li a:link, .dropDownMenu li a:visited {
color: #8080FF;/*#8BADCF;*/
display: block;
background: url(images/menu3.gif);
padding: 8px 0 8px 10px;
}

.dropDownMenu li a:hover {
color: #000080;/*#627EB7;*/
background: url(images/menu3.gif) 0 -32px;
padding: 8px 0 8px 10px;
}

/*– End –*/

HTML generat în LOAD_TABLE.PHP

$dropdown = "<img valign='center' width='15' height='15' id='dropdown_$k' src='images/dropdown_btn.gif' onmouseover=\"this.src='images/dropdown_btn_.gif'\" onmouseout=\"this.src='images/dropdown_btn.gif'\" onclick=\"showMenu(event.clientX, event.clientY, 'menu_$k')\" />";

Obținerea unei tabele din baza de date folosind AJAX

JAVASCRIPT în ACTION.JS

function loadTable(tableName)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

currentTable = tableName

var url="action/load_table.php"
url=url+"?q="+tableName
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertTable
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function insertTable()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
document.getElementById("contents").innerHTML=xmlHttp.responseText
}
}

PHP în LOAD_TABLE.PHP

$con = mysqli_connect($host, $usr, $pass);
if (!$con)
{
die('Could not connect: ' . mysqli_connect_error());
}

mysqli_select_db($con, $dbname);

$sql="SELECT * FROM ".$q;

$result = mysqli_query($con, $sql);

echo "<table id='$q' border='1' cellpadding='5' align='center'>";

$header_printed = FALSE;

while($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
{
if (! $header_printed)
{
echo "<tr>";
foreach($row as $k => $v)
{
echo "<th>$k</th>";
}
echo "</tr>";
$header_printed = TRUE;
}

echo "<tr>";
foreach($row as $k => $v)
{
echo "<td name='$k'><input name='tblCell' type='text' readonly='true' value='$v'/></td>";
}
echo "</tr>";
}
echo "</table>";

mysqli_free_result($result);
mysqli_close($con);

Obținerea tabelelor unei baze de date folosind AJAX

JAVASCRIPT în ACTION.JS

var xmlHttp
var currentTable

function GetXmlHttpObject()
{
var xmlHttp=null;
try
{
// Firefox, Opera 8.0+, Safari
xmlHttp=new XMLHttpRequest();
}
catch (e)
{
//Internet Explorer
try
{
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}

function showTables()
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/show_tables.php"
url=url+"?q="
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertTableButtons
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function insertTableButtons()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText.length > 0)
{
document.getElementById("tableButtons").innerHTML=xmlHttp.responseText
}
}
}

PHP în SHOW_TABLES.PHP

$con = mysqli_connect($host, $usr, $pass);

if (!$con)
{
die('Could not connect: ' . mysqli_connect_error());
}

mysqli_select_db($con, $dbname);

$sql="SHOW TABLES";

$result = mysqli_query($con, $sql);

echo "<div id='menu8'><ul>";

while($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
{
foreach($row as $k => $v)
{
echo "<li><a href='#1' title='Table $v' onclick=\"loadTable('$v')\">$v</a></li>";
}
}

echo "</ul></div>";

mysqli_free_result($result);
mysqli_close($con);

Obținerea tabelei și a coloanei referite de o cheie externă folosind PHP

EXPAND_TABLE_REFERENCE.PHP

$con = mysqli_connect($host, $usr, $pass);
if (!$con)
{
die('Could not connect: ' . mysqli_connect_error());
}

mysqli_select_db($con, $dbname);

// determine names
$reftbl = $tbl;
$newtbl = $tbl;
$isview = false;
if (!startsWith($tbl,"v_"))
{
$newtbl = "v_$tbl";
}
else
{
$reftbl = substr($tbl, 2);
$isview = true;
}

// find referenced table
$sql="SELECT REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$reftbl' AND COLUMN_NAME='$col'";

$result = mysqli_query($con, $sql);
$tbl2 = NULL;
$col2 = NULL;
if ($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
{
$tbl2 = $row['REFERENCED_TABLE_NAME'];
$col2 = $row['REFERENCED_COLUMN_NAME'];
}

mysqli_free_result($result);

Limitări și direcții de dezvoltare a aplicației

7.1 Evoluția interfeței cu utilizatorul

Direcțiile în care se poate specializa interfața cu utilizatorul sunt: adăugarea de funcționalități de creare de rapoarte, filtrări complexe, lucrul cu mai multe tabele în același timp, editarea view-urilor cu efecte în tabelele de bază, extinderea suportului pentru mai multe browsere si servere web.

Suportul oferit în această versiune a aplicației pentru rapoarte este limitat, utilizatorul poate doar să obțină un view maximizat al unui tabel. Ca îmbunărățire, se pot oferi utilizatorului template-uri de rapoarte, cât și operații complexe cu datele din rapoarte (sume, medii, sortări după mai multe criterii, etc).

Filtrarea (funcționalitatea Search) se poate face după mai multe criterii succesive, întrucât după aplicarea unui filtru (prima folosirea a butonului Search), tabelul rezultat este cel asupra căruia se va aplica filtrul următor. Se poate îmbunătăți complexitatea filtrării prin adăugarea la interfață a unui modul de filtrare, care să permită descrierea concomitentă a mai multor criterii, și posibilitatea grupării acestora prin operații logice (and, or, not). De asemenea se poate îmbunătăți filtrarea oferind suport pentru căutarea în toată baza de date a unei informații, nu numai în tabelul curent, cum se întâmplă în versiunea curentă a aplicației.

Pentru lucrul cu mai multe tabele în același timp, o versiune ulterioară ar trebui să permită lucrul cu tabele în mai multe frame-uri (2 sau 4), pentru a permite utilizatorului paralelizarea acțiunii de vizualizare a bazei de date.

Editările care se fac asupra view-urilor (redenumire, sortare, ștergere) nu se reflectă în baza de date, astfel încât utilizatorul să nu poată altera din greșeală datele. Se poate însă oferi și opțiunea de a opera modificările făcute într-o vizualizare asupra bazei de date.

7.2 Suport extins pentru baze de date

Extinderea suportului pentru baze de date se poate face prin folosirea unui standard SQL pentru scrierea interogărilor, suportat de mai multe baze de date. După cum se știe, sistemele de gestiune a bazelor de date se diferențiază sensibil prin sintaxa interogărilor sql pe care le folosesc. Suportul pentru alte baze de date decât Mysql implică testarea pe alte baze de date și particularizarea codului la acestea.

De asemenea trebuie considerat faptul că, deși dinamica sporită a aplicației oferă multe beneficii, precum adaptarea la baza de date, scriptingul aduce și o mulțime de inconveniente, legate în special de probleme de compatibilitate. Scripturile sunt puternic dependente de browserul utilizat, de gestiunea erorilor făcută distinct pentru fiecare bază de date, fiecare limbaj în parte precum și, la modul genereal, de fiecare librărie folosită.

Aceste aspecte suportă ideea necesității standardizării tehnologiilor web la un mod mult mai extins și generalizat decît la acet moment. O cale de urmat în acest sens sunt recomandările W3C despre care se poate presupune că multe din software-urile existente au luat aminte și au încercat, măcar în parte, să le respecte.

7.3 Gestiunea erorilor

În ceea ce privește gestiunea erorilor, se pot aduce îmbunătățiri prin crearea unui fisier header cu coduri de eroare, prin definirea unui protocol de transmitere a erorilor între PHP – JS, cît și prin introducerea de mesaje de ghidare pentru mai multe cazuri decât suportă aplicația în acest moment.

După cum am menționat și în subcapitolul anterior, gestiunea erorilor este făcută de o manieră descentralizaă cînd vine vorba de domeniul tehnologiilor și aplicațiilor web. Se pot remarca diferențe considerabile între versiuni diferite de browsere, de limbaje web (precum PHP) ce nu țin cont într-o măsură acceptabilă de backward compatibility, între servere web diferite, precum și în cadrul programelor web pentru fiecare librărie în parte (de exemplu între mysql și mysqli).

În acest context se cere ca o aplicație ce își propune să fie compatibilă cu majoritatea sistemelor ce pot fi folosite de un utilizator curent, să își propună și țelul uniformizării gestiunii erorilor prin construirea de fișiere header specifice ce conțin un set coerent și uniform de coduri și mesaje de eroare.

7.4 Îmbunătățirea securității

Îmbunătățiri legate de securitate implică: folosirea conexiunii securizate SSL cu serverul de baze de date, evitarea folosirii sesiunilor și a lucrului cu sistemul de fișiere. Organizarea scripturilor și codului PHP în vederea evitării SQL injection și alte tipuri de atacuri bazate pe scripting.

Modalitatea de login trebuie îmbunătățită din acest punct de vedere, aceasta făcîndu-se la acest moment fără criptarea datelor la transmiterea acestora între pagina introductivă și pagin principală. De asemenea, conform recomandărilor php, există o serie de recomandări printre care și aceea de a nu mai folosi variabile superglobale cum sunt variabilele de sesiune și înlocuirea acestora prin formulare.

De asemenea, în cazul acestei aplicații, se remarcă și aspectul confidențial al datelor cu care se lucrează (tabelele de date ale companiei) ce necesită o atenție deosebită pentru preîntîmpinarea posibilității de furt de informație.

Fișierul de configurare folosit la acest moment pentru a ține în clar configurări de conexiui la baze de date nu prezintă suficiente prevederi din punctul de vedere al securității informației. Deși aceste informații nu ar fi suficiente pentru un atac de hacking, ele relevă locația bazelor de date pe care le deține compania, ceea ce ar putea dăuna intereselor firmei utilizatoare și încurajează cel puțin încercările de atac din partea unor terți.

ANEXA 1 Fragmente din codul aplicației

Fișierul index.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?php
session_start();
if(!isset($_SESSION["usr"]))
{
header("location:intro.html");
}
?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Gestiune Resurse Umane</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="keywords" content="hr, rh, ru, resurse, umane, gestiune, personal, angajati, concediu, salariu, raport " />
<meta name="description" content="Gestiunea facila a resurselor umane la un click distanta" />

<script type="text/javascript">
<!–
// preload stuff
function newImage(arg) {
if (document.images) {
rslt = new Image();
rslt.src = arg;
return (rslt)
}
}

function changeImages() {
if (document.images && (preloadFlag == true)) {
for (var i=0; i<changeImages.arguments.length; i+=2) {
document[changeImages.arguments[i]].src = changeImages.arguments[i+1];
}
}
}

var preloadFlag = false;
function preloadImages() {
if (document.images) {
home_over = newImage("images/home_.jpg");
about_over = newImage("images/about_.jpg");
news_over = newImage("images/news_.jpg");
portfolio_over = newImage("images/portfolio_.jpg");
services_over = newImage("images/services_.jpg");
contact_over = newImage("images/contact_.jpg");

preloadFlag = true;
}
}
// –>
</script>

<script type="text/javascript" src="action/action.js"></script>
<link href="mo06f.css" rel="stylesheet" type="text/css" />
</head>

<body onload="preloadImages(); showTables();" >
<!– MAIN TABLE –>
<table border="0" cellpadding="0" cellspacing="0" width="950">
<tr>
<td colspan="2"><img name="top" src="images/top.jpg" width="950" height="9" border="0" alt="" /></td>
</tr>
<!– TOP ROW –>
<tr>
<!– LEFT COLUMN –>
<td align="center" valign="top" class="left_bgr">
<!–MAIN MENU–>
<table width="332" border="0" cellpadding="0" cellspacing="0">
<tr>
<td rowspan="7" valign="top"><img src="images/menu_left.jpg" alt="menu_left" width="74" height="385" /></td>
<td valign="top"><a href="index.php" onmouseover="changeImages('home', 'images/home_.jpg'); return true;" onmouseout="changeImages('home', 'images/home.jpg'); return true;"><img src="images/home.jpg" name="home" alt="Home" width="215" height="52" border="0" /></a></td>
<td rowspan="7" valign="top"><img src="images/menu_right.jpg" alt="menu_right" width="43" height="385" /></td>
</tr>
<tr>
<td valign="top"><a href="#" onclick="view()" onmouseover="changeImages('about', 'images/about_.jpg'); return true;" onmouseout="changeImages('about', 'images/about.jpg'); return true;"><img src="images/about.jpg" name="about" alt="About Us" width="215" height="42" border="0" /></a></td>
</tr>
<tr>
<td valign="top"><a href="#" onclick="edit()" onmouseover="changeImages('news', 'images/news_.jpg'); return true;" onmouseout="changeImages('news', 'images/news.jpg'); return true;"><img src="images/news.jpg" name="news" alt="News" width="215" height="45" border="0" /></a></td>
</tr>
<tr>
<td valign="top"><a href="#" onclick="print()" onmouseover="changeImages('portfolio', 'images/portfolio_.jpg'); return true;" onmouseout="changeImages('portfolio', 'images/portfolio.jpg'); return true;"><img src="images/portfolio.jpg" name="portfolio" alt="Portfolio" width="215" height="45" border="0" /></a></td>
</tr>
<tr>
<td valign="top"><a href="#" onclick="searchPage()" onmouseover="changeImages('services', 'images/services_.jpg'); return true;" onmouseout="changeImages('services', 'images/services.jpg'); return true;"><img src="images/services.jpg" name="services" alt="Services" width="215" height="43" border="0" /></a></td>
</tr>
<tr>
<td valign="top"><a href="#" onclick="help()" onmouseover="changeImages('contact', 'images/contact_.jpg'); return true;" onmouseout="changeImages('contact', 'images/contact.jpg'); return true;"><img src="images/contact.jpg" name="contact" alt="Contact Us" width="215" height="47" border="0" /></a></td>
</tr>
<tr>
<td valign="top"><img src="images/menu_bottom.jpg" alt="menu_bottom" width="215" height="111" /></td>
</tr>
</table>
<!– END MAIN MENU –>
<br />
<div style="position:absolute; top:330px; left:165px; font-variant:small-caps;"><a href="#" onclick="cleanup(); window.location='intro.html';">Logout</a></div>
<!– PANEL –>
<div id="panel" style="visibility:hidden;">
<table width="200" border="0" cellpadding="0" cellspacing="0">
<tr>
<td><img src="images/panel_top.gif" alt="" width="277" height="8" /></td>
</tr>
<tr>
<td class="panBgr" id="tableButtons">
<h4>No Tables Available</h4>
</td>
</tr>
<tr>
<td><img src="images/panel_bottom.gif" alt="p2" width="277" height="10" /></td>
</tr>
</table>
</div>
<!– END PANEL –>
<br />
</td>
<!– END LEFT COLUMN –>
<!– RIGHT COLUMN –>
<td width="618" valign="top" class="bottom_bgr">
<table width="618" border="0" cellpadding="0" cellspacing="0">
<tr>
<td><img src="images/company_rumanager.jpg" alt="Management facil al resurselor umane" width="618" height="114" onclick="javascript:void(showMenu(400,400))" /></td>
</tr>
<tr>
<td valign="top" class="content_bgr">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td valign="middle"><img src="images/vertical_line.jpg" alt="vertical line" width="2" height="323" /></td>
<td valign="top" class="content">
<script type='text/javascript' src='action/menu_items.js'></script>
<div id="contents">
<h1>Welcome <b><?php echo $_SESSION["usr"]; ?></b></h1>
<hr />
<p>Using Database: <b style="font-style:oblique; font-variant:small-caps; color:blue;"><?php echo $_SESSION["dbname"]; ?></b></p>
<p>Of Type: <b style="font-style:oblique; font-variant:small-caps; color:blue;"><?php echo $_SESSION["dbtype"]; ?></b></p>
<p>At location: <b style="font-style:oblique; font-variant:small-caps; color:blue;"><?php echo $_SESSION["host"]; ?></b></p>
<hr />
<p>Click on <b>View</b> to start viewing tables from your database</p>
<p>Click on <b>Edit</b> to start editing tables from your database</p>
<p>Click on <b>Search</b> to start searching information in the tables from your database</p>
<p>Click on <b>Help</b> to get a description of how to use the main functionalities of <b>RU Manager</b></p>
<p>&nbsp;</p>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<!– END RIGHT COLUMN –>
</tr>
<!– END TOP ROW –>
<!– BOTTOM ROW –>
<tr>
<td colspan="2" align="left">
<div class="copyrights">
<a href="index.php">Home</a>&nbsp;|&nbsp;
<a href="#" onclick="view()">View</a>&nbsp;|&nbsp;
<a href="#" onclick="edit()">Edit</a>&nbsp;|&nbsp;
<a href="#" onclick="print()">Print</a>&nbsp;|&nbsp;
<a href="#" onclick="searchPage()">Search</a>&nbsp;|&nbsp;
<a href="#" onclick="help()">Help</a>
<div>&copy; 2008 Iulia Trifan</div>
</div>
</td>
<td>&nbsp;</td>
</tr>
<!– END BOTTOM ROW –>
</table>
<!– END MAIN TABLE –>
<p>
<a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10-blue" alt="Valid XHTML 1.0 Transitional" height="31" width="88" style="border:0"/></a>
<a href="http://jigsaw.w3.org/css-validator/"><img style="border:0;width:88px;height:31px" src="http://jigsaw.w3.org/css-validator/images/vcss-blue" alt="CSS Valide !" /></a>
</p>
</body>
</html>

Fișierul action.js

//<script type='text/javascript'>
var xmlHttp
var currentTable

function GetXmlHttpObject()
{
var xmlHttp=null;
try
{
// Firefox, Opera 8.0+, Safari
xmlHttp=new XMLHttpRequest();
}
catch (e)
{
//Internet Explorer
try
{
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}

function showTables()
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/show_tables.php"
url=url+"?q="
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertTableButtons
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function insertTableButtons()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText.length > 0)
{
document.getElementById("tableButtons").innerHTML=xmlHttp.responseText
}
}
}

function loadTable(tableName)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

currentTable = tableName

var url="action/load_table.php"
url=url+"?q="+tableName
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertTable
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function insertTable()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
document.getElementById("contents").innerHTML=xmlHttp.responseText
}
}

function sortTable(column)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/sort_table.php"
url=url+"?tbl="+currentTable
url=url+"&q="+column
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertTable
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function expandTableReference(column)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/expand_table_reference.php"
url=url+"?tbl="+currentTable
url=url+"&col="+column
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertExpandedTableReference
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function insertExpandedTableReference()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText.length <= 0)
{
alert("Column is not a reference");
return
}

if (currentTable.indexOf("v_") != 0)
currentTable = "v_"+currentTable

document.getElementById("contents").innerHTML = xmlHttp.responseText
}
}

function removeColumn(column)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/remove_column.php"
url=url+"?tbl="+currentTable
url=url+"&col="+column
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertAlteredTable
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function insertAlteredTable()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText.length <= 0)
{
alert("Error performing operation");
return
}

if (currentTable.indexOf("v_") != 0)
currentTable = "v_"+currentTable

document.getElementById("contents").innerHTML = xmlHttp.responseText
}
}

function renameColumn(column)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var newColumnName = prompt("Please enter new column name","");
if (newColumnName == null || newColumnName == "")
{
alert("Rename column aborted: You must specify a new column name");
return
}

if (document.getElementById('header_'+newColumnName))
{
alert("Column '" + newColumnName + "' already exists. Please specify a unique column name");
return
}

var url="action/rename_column.php"
url=url+"?tbl="+currentTable
url=url+"&oldcol="+column
url=url+"&newcol="+newColumnName
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=insertAlteredTable
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function cleanup()
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/cleanup.php"
url=url+"?sid="+Math.random()
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

/*–––– dropdown MENU action –––––*/
var timer = null;

function showMenu(x, y, menuId)
{
if (document.getElementById(menuId))
{
document.getElementById(menuId).style.visibility = 'visible';
if (timer) clearTimeout(timer);
timer = null;
return
}

contents = document.getElementById("contents");

var Container = document.createElement('div');
contents.appendChild(Container);
Container.id = menuId;
Container.className = 'dropDownMenu';
Container.style.left = (x – 10) + 'px';
Container.style.top = (y – 10) + 'px';
Container.addEventListener('mouseout', mouseOutMenu, false);
Container.addEventListener('mouseover', mouseOverMenu, false);

var Menu = document.createElement('ul');
Container.appendChild(Menu);

var removeBtn = document.createElement('li');
removeBtn.innerHTML = "<a href='#1' onclick=\"removeColumn('" + menuId.substr(5) + "')\">Remove</a>";

var renameBtn = document.createElement('li');
renameBtn.innerHTML = "<a href='#1' onclick=\"renameColumn('" + menuId.substr(5) + "')\">Rename</a>";;

var sortBtn = document.createElement('li');
sortBtn.innerHTML = "<a href='#1' onclick=\"sortTable('" + menuId.substr(5) + "')\">Sort</a>";;

Menu.appendChild(removeBtn);
Menu.appendChild(renameBtn);
Menu.appendChild(sortBtn);
}

function mouseOverMenu()
{
this.style.visibility = 'visible';
if (timer) clearTimeout(timer);
timer = null;
}

function mouseOutMenu()
{
if (timer)
{
clearTimeout(timer);
}
timer = setTimeout("document.getElementById('" + this.id + "').style.visibility = 'hidden'", 1000);
}

/*–––––– TABLE EDIT ACTIONS –––––––-*/
var alteredRows = null
var error = null
var deletedRows = null

function contains(arr, elem)
{
for (i=0;i<arr.length;i++)
{
if (arr[i] == elem)
return true;
}

return false;
}

function editTable()
{
cells = document.getElementsByName("tblCell");

for (i = 0; i < cells.length; i++)
{
cells[i].readOnly = false;
}

alteredRows = new Array();
deletedRows = new Array();
document.getElementById("editBtn").disabled = true;
document.getElementById("addBtn").disabled = false;
document.getElementById("delBtn").disabled = false;
document.getElementById("applyBtn").disabled = false;
document.getElementById("discardBtn").disabled = false;
}

function addRow()
{
tbl = document.getElementById(currentTable);
row = tbl.insertRow(tbl.rows.length);
row.innerHTML = tbl.rows[1].innerHTML;
row.setAttribute('onclick', "if (alteredRows && !contains(alteredRows, this)) alteredRows.push(this);");

for (i = 0; i< row.cells.length; i++)
{
row.cells[i].firstChild.value = "";
}
}

function delRow()
{
for (i = 0; i<deletedRows.length; i++)
{
deletedRows[i].style.backgroundColor = "LightCoral";
}
}

function applyChanges()
{
var row;
var cells;

while (alteredRows.length > 0)
{
row = alteredRows.shift();
if (row.style.backgroundColor == "LightCoral")
continue;

cells = row.getElementsByTagName("input");

var allNULL = true;

if (cells[0].value.length > 0)
{
updStr = "\"" + cells[0].value + "\"";
allNULL = false;
}
else
updStr = "NULL";

for (j = 1; j < cells.length; j++)
{
if (cells[j].value.length > 0)
{
updStr += ", \"" + cells[j].value + "\"";
allNULL = false;
}
else
updStr += ", NULL";
}

if (allNULL) continue;
updateRow(updStr);

if (error)
{
res = confirm("Error updating row with values " + updStr + ": " + error + "\n Continue updating?");
error = null;
if (!res) return;
}
else
{
for (j = 1; j < cells.length; j++)
{
cells[j].defaultValue = cells[j].value;
}
}
}

var delStr;
while (deletedRows.length > 0)
{
row = deletedRows.shift();

if (row.style.backgroundColor != "LightCoral")
continue;

cells = row.getElementsByTagName("input");

delStr = "";

if (cells[0].value.length == 0)
{
delStr = cells[0].parentNode.getAttribute("name") + "=NULL";
}
else
{
delStr = cells[0].parentNode.getAttribute("name") + "=\"" + cells[0].value + "\"";
}

for (j = 1; j < cells.length; j++)
{
if (cells[j].value.length > 0)
delStr += " AND " + cells[j].parentNode.getAttribute("name") + "=\"" + cells[j].value + "\"";
else
delStr += " AND " + cells[j].parentNode.getAttribute("name") + "=NULL";
}

deleteRow(delStr);

if (error)
{
res = confirm("Error deleting row with values " + delStr + ": " + error + "\n Continue updating?");
error = null;
if (!res) return;
}
}

cells = document.getElementsByName("tblCell");

for (i = 0; i < cells.length; i++)
{
cells[i].readOnly = true;
}

alteredRows = null;
deletedRows = null;
document.getElementById("editBtn").disabled = false;
document.getElementById("applyBtn").disabled = true;
document.getElementById("discardBtn").disabled = true;
document.getElementById("addBtn").disabled = true;
document.getElementById("delBtn").disabled = true;
}

function deleteRow(str)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/delete_row.php"
url=url+"?tbl="+currentTable
url=url+"&row="+str
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=getDeleteResult
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function getDeleteResult()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText.length > 0)
{
error = xmlHttp.responseText;
loadTable(currentTable);
}
}
}

function updateRow(str)
{
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null)
{
alert ("Browser does not support HTTP Request")
return
}

var url="action/update_row.php"
url=url+"?tbl="+currentTable
url=url+"&row="+str
url=url+"&sid="+Math.random()
xmlHttp.onreadystatechange=getUpdateResult
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}

function getUpdateResult()
{
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
{
if (xmlHttp.responseText.length > 0)
{
error = xmlHttp.responseText;
loadTable(currentTable);
}
}
}

function discardChanges()
{
loadTable(currentTable);

alteredRows = null;
document.getElementById("editBtn").disabled = false;
document.getElementById("applyBtn").disabled = true;
document.getElementById("discardBtn").disabled = true;
document.getElementById("delBtn").disabled = true;
document.getElementById("addBtn").disabled = true;
}

function search()
{
searchForm = document.getElementById("searchform");
if (searchForm)
{
searchForm.style.visibility = 'visible';
}
else
{
cont = document.getElementById("contents");
cont.innerHTML += "<table id='searchform' style='border:thin; border-style:dashed; padding:10px;'> \
<tr><td colspan='4'><input type='text' id='txt' size='40' /></td></tr> \
<tr> \
<td>Match case</td><td><input type='checkbox' id='case' /></td> \
<td>Match whole word</td><td><input type='checkbox' id='word' /></td> \
</tr> \
<tr><td>Columns</td><td colspan='3'><select id='searchCols' multiple='true' size='5' /></td></tr> \
<tr> \
<td><input type='button' value='Search' onclick='doSearch()' /></td> \
<td><input type='button' value='Cancel' onclick='cancelSearch()' /></td> \
</tr> \
<tr><td id='matches'></td></tr> \
</table>";
}

cells = document.getElementsByName("headerCell");
list = document.getElementById("searchCols");
list.options[0] = new Option("All");
list.options[0].selected = true;
for (i = 0; i < cells.length; i++)
{
list.options[i + 1] = new Option(cells[i].innerHTML);
}

document.getElementById("searchBtn").disabled = true;
}

function cancelSearch()
{
loadTable(currentTable);
}

function doSearch()
{
document.getElementById("matches").innerHTML = '';

text = document.getElementById("txt");
if (text.value.length == 0) return;

cells = new Array();
searchCols = document.getElementById("searchCols");
if (searchCols.options[0].selected)
cells = document.getElementsByName("tblCell");
else
{
for (i = 0; i < searchCols.options.length; i++)
{
if (searchCols.options[i].selected)
{
newcells = document.getElementsByName(searchCols.options[i].text);
for (j = 0; j < newcells.length; j++)
cells.push(newcells[j].firstChild);
}
}
}

resultRows = new Array();

var i = 0;

for (i = 0; i < cells.length; i++)
{
if (document.getElementById("case").checked)
idx = cells[i].value.indexOf(text.value, 0);
else
{
lowerCell = cells[i].value.toLowerCase();
lowerText = text.value.toLowerCase();
idx = lowerCell.indexOf(lowerText, 0);
}

if (idx < 0) continue;

if (document.getElementById("word").checked)
{
if (idx > 0 && isAlphanum(cells[i].value.charAt(idx-1)))
{
continue;
}

upperLimit = idx + text.value.length;

if (upperLimit < cells[i].value.length && isAlphanum(cells[i].value.charAt(upperLimit+1)))
{
continue;
}
}

resultRows.push(cells[i].parentNode.parentNode); // parent row
}

document.getElementById("matches").innerHTML = "Got " + resultRows.length + " matches.";

var tbl = document.getElementById(currentTable);
var sz = tbl.rows.length;
var i = 0;
for (i = 1; i < sz; i++)
{
if (!contains(resultRows, tbl.rows[sz-i]))
tbl.deleteRow(sz – i);
}
}

var numb = '0123456789';
var lwr = 'abcdefghijklmnopqrstuvwxyz';
var upr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

function isValid(parm,val)
{
if (parm == "") return true;

for (i=0; i<parm.length; i++)
{
if (val.indexOf(parm.charAt(i),0) == -1) return false;
}

return true;
}

function isNum(parm) {return isValid(parm,numb);}
function isLower(parm) {return isValid(parm,lwr);}
function isUpper(parm) {return isValid(parm,upr);}
function isAlpha(parm) {return isValid(parm,lwr+upr);}
function isAlphanum(parm) {return isValid(parm,lwr+upr+numb);}

/********** PAGES *************/
function view()
{
document.getElementById("panel").style.visibility = 'visible';
if (currentTable && document.getElementById(currentTable))
{
editBtn = document.getElementById("editBtn");
if (!editBtn || !editBtn.disabled) return;

discardBtn = document.getElementById("discardBtn");
discardBtn.click();
}
else
{
document.getElementById("contents").innerHTML = "<h1>View Database</h1><p>To start viewing your database, select a base table from the panel on the left side of the screen</p>";
}
}

function edit()
{
document.getElementById("panel").style.visibility = 'visible';
if (currentTable && document.getElementById(currentTable))
{
editBtn = document.getElementById("editBtn");
if (!editBtn)
{
alert("You cannot edit a View. Editing can be performed only on base tables");
return;
}
editBtn.click();
}
else
{
document.getElementById("contents").innerHTML = "<h1>Edit Database</h1><p>To start editing your database, select a base table from the panel on the left side of the screen and press the <b>Edit button</b> at the bottom of the displayed table</p>";
}
}

var savePage

function print()
{
if (!currentTable || !document.getElementById(currentTable))
{
alert("No table selected. You must first start Viewing, Editing or Searching a table");
return;
}

savePage = document.body.innerHTML;
document.body.innerHTML = document.getElementById("contents").innerHTML + "<div style='text-align:center; width:100%; ba'><a href='#' onclick='document.body.innerHTML = savePage; savePage = null;'> BACK </a></div>";
}

function searchPage()
{
document.getElementById("panel").style.visibility = 'visible';
if (currentTable && document.getElementById(currentTable))
{
searchBtn = document.getElementById("searchBtn");
if (!searchBtn)
{
alert("Search unavailable");
return;
}
searchBtn.click();
}
else
{
document.getElementById("contents").innerHTML = "<h1>Search Database</h1><p>To start searching information in your database, select a base table from the panel on the left side of the screen and press the <b>Search button</b> at the bottom of the displayed table</p>";
}
}

function help()
{
document.getElementById("panel").style.visibility = 'hidden';
document.getElementById("contents").innerHTML = " <h1> Help Page </h1> \
<p>The main actions you can perform are <ul><li>View Database</li> \
<li>Edit Database</li> \
<li>Print Tables</li> \
<li>Search Database</li></ul></p> \
<h1>View Database</h1> \
<p>To start viewing your database, select a base table from the panel \
on the left side of the screen</p> \
<p>You can use the column dropdown menu to Rename or Remove a column or \
sort the table by that column</p> \
<p>Note that the first two actions are only viewing actions, \
meaning changes will not be reflected on the database</p> \
<h1>Edit Database</h1> \
<p>To start editing your database, select a base table from the panel \
on the left side of the screen and press the <b>Edit button</b> at the bottom \
of the displayed table</p> \
<p>You can edit any cell of the table and add new rows. These changes WILL BE \
reflected on the database</p> \
<p>Note also that only changes allowed by the database struture and design are permitted</p> \
<p>An empty cell is considered to have NULL value</p> \
<p>To make the changes permanent, you need to press the <b>Apply Changes button</b> \
at the bottom of the table. Changes are made to the database row by row, so if an error \
occurs while updating a row, the systemwill ask you if you want to continue updating the rest of the rows \
by prompting a confirm box</p> \
<p>If you want to discard your changes, you need to press the <b>Discard Changes</b> button \
at the bottom of the table. Note that changes that were Applied cannot be rolled back</p> \
<h1>Print Tables</h1> \
<p> This funcionality provides you the possibility to print out tables, views and searches you\
are currently working on by leaving on the page only the useful information</p> \
<h1>Search Database</h1> \
<p>To start searching information in your database, select a base table from the panel \
on the left side of the screen and press the <b>Search button</b> at the bottom of the \
displayed table</p> \
<p> You can choose from the presented combo box in the search form which columns of the table \
you want to search in. You can select several columns.</p>";
}
/* END PAGES */

BIBLIOGRAFIE

Cursul de Interfețe evoluate

Cursul de Baze de Date

http://www.elfconsulting.ro/showdef.php?nrdef=38

http://hosted.regionalnet.org/asper/managementul_resurselor_umane.html

http://www.pcworld.ro/index.php?id=9301&page=node&print=true

http://www.joboutlook.ro/

http://www.sal.ro/

http://www.ebsromania.ro/produse/clarvision4.htm

http://www.sincronhr.ro/

http://www.mysql.com

http://www.mozilla.com

http://www.php.net

http://html-tutor.net/ceestehtml/#sgml

http://www.ici.ro/RRIA/ria2005_3/art04.html

http://www.phpromania.net/info/desprephp/

http://www.softpageinternet.ro/manual_html/Capitole/Capitolul%2012.htm

http://ro.wikipedia.org/wiki/MySQL

Similar Posts