Dezvoltarea Unei Aplicatii Pentru Ghidarea Utilizatorilor Asupra Atractiilor Turistice

DEZVOLTAREA UNEI APLICAȚII PENTRU GHIDAREA UTILIZATORILOR ASUPRA ATRACȚIILOR TURISTICE

Cuprins

Cuprins

1. Considerații

1.1 Rezumat

1.2 Descrierea aplicației

1.2.1 Conținutul site-ului

1.2.2 Utilizatorii

1.2.3 Design-ul site-ului

1.3 Alegerea tehnologiilor

1.3.1 Despre Drupal

1.3.2 Alte tehnologii folosite

2.1 Alegerea modulelor necesare

2.2 Alegerea unui design

2.3 Crearea tipurilor de conținut

2.4 Crearea paginilor folosind modulul views

3. Crearea unui modul de listare a punctelor de interes

3.1 Structura fișierului .info

3.2 Structura fișierului .install

3.3 Fișierul .module

4. Sitemul de template-uri din Drupal

5. Administrarea site-ului

5.1 Administrarea utilizatorilor

5.2 Administrarea conținutului

5.3 Administrarea meniului

5.4 Administrarea termenilor de taxonomie

5.5 Administrarea view-urilor

5.6 Administrarea temelor și a modulelor

5.8 Administrarea altor componente

5.9 Rapoarte

6. Finalizarea și publicarea site-ului

6.1 Imagini din site

Bibliografie

1

1. Considerații

1.1 Rezumat

Lucrarea este împărțită în șase capitole:

prezintă date generale despre aplicație și tehnologiile folosite

cuprinde dezvoltarea site-ului folosind module existente în Drupal

tratează crearea modulelor si interacțiunea lor cu Drupal

explică sistemul de template-uri din Drupal

descrie modalitatea de administrare a site-ului

prezintă site-ul final

Descrierea aplicației

Lucrarea prezintă dezvoltarea unui director web care cuprinde principalele puncte de interes ale minicipiului Oradea: obiective turistice, restaurante, hoteluri, etc. Site-ul este util îndeosebi turiștilor care vizitează Oradea, având posibilitatea de a-și exprima părerea referitor la punctele de interes.

Aplicația web poate fi descompusă în mai multe părți individuale, prezentăm în continuare cele mai importante aspecte.

1.2.1 Conținutul site-ului

Având în vedere natura site-ului se pot distinge câteva tipuri de conținut:

articol

permite crearea unei pagini având titlu, descriere și imagini

folosit pentru diverse informații pe site

obiectiv

permite crearea unei pagini care conține detalii despre obiectivul turistic, imagini, locația pe harta

2

permite comentarii

permite clasificarea obiectivului într-o anumită categorie

utilizatorii pot să evalueze obiectivul (de la 1 la 5 stele)

Obiectivele turistice pot fi găsite cu ajutorul căutarilor pe site. Sunt necesare două tipuri de căutari:

listare

fiecare obiectiv va fi listat sub o formă minimală

lista poate avea filtre pe categorie sau pe text

lista are paginare în cazul în care numărul de obiective pe o pagină este prea mare

pe hartă

fiecare obiectiv va apărea pe hartă în locația lui exactă

poate avea filtre pe categorie sau text

adițional, pe hartă va apărea și locația curentă a utilizatorului (cu aproximație)

Utilizatorii

Utilizatorii pot fi împărțiți în diferite categorii, fiecare având roluri si permisiuni diferite:

utilizatorul cu rolul de administrator

nu are nicio restricție

poate administra orice aspect al site-ului

utilizatorul înregistrat în site-ul web

are acces la propriul profil

poate adăuga conținut

poate adăuga comentarii

poate evalua obiective

utilizatorul anonim

poate vizualiza conținutul publicat pe site

poate să își creeze cont pe site devenind astfel utilizator înregistrat

3

Având în vedere rolurile prezentate mai sus se poate subînțelege existența formularelor de conectare la site (login), înregistrare în site (register) sau recuperare parolă (password request).

1.2.3 Design-ul site-ului

Design-ul unui site web este o componentă foarte importantă. Având în vedere că noile tendințe sunt telefoanele și tabletele este esențial ca design-ul să fie unul responsiv, în acest fel site-ul poate fi vizualizat cu ușurință atât de pe laptop/desktop cât și de pe dispozitivele mobile telefon/tabletă.

1.3 Alegerea tehnologiilor

Pentru a alege tehnologiile potrivite trebuie să evaluăm problemele sau necesitățile noastre în crearea aplicației. În mare parte aplicația trebuie să fie un CMS (Content Management System) dar și un CMF (Content Management Framework) pentru a o putea extinde ușor. De asemenea limbajul de programare trebuie sa fie unul specific pentru web, astfel pentru acest site am ales Drupal 7 având ca bază de date MySQL.

1.3.1 Despre Drupal

Drupal este un proiect open source scris în PHP care permite crearea și administrarea conținutului în numeroase moduri. A început în anul 1999 ca un simplu forum, iar pe parcursul anilor s-a extins foarte mult, fiind folosit de companii și instituții de nivel global (AOL, Twitter, Examiner.com, White House, MTV, Harvard, MIT, etc.)[1]. Numeroasa comunitate din spatele Drupal aduce mereu îmbunătățiri prin intermediul modulelor, repară și previne erorile de securitate. Sistemul din spatele Drupal este unul abstractizat și permite multe configurări folosind doar interfața de administrare a site-ului.

Alte tehnologii folosite

Design site

HTML

CSS (incluzând un sistem de grid)

Adobe Photoshop (elemente de design + logo)

4

Module Drupal

PHP

JavaScript + jQuery

Baze de date

MySQL + phpMyAdmin

Server web

Apache 2

Editor text

Komodo Edit

Sistem versionare

Git

5

Dezvoltarea aplicației folosind module existente

Alegerea modulelor necesare

Drupal oferă în nucleul său numeroase module standard, dar de cele mai multe ori comunitatea pune la dispoziție module ce extind functionalitățile site-ului.

Pentru a ușura dezvoltarea site-ului sunt necesare anumite module create special pentru programator sau administrator:

admin_menu – https://drupal.org/project/admin_menu

oferă un meniu de administrare mai ușor de folosit decât meniul Drupal standard

module_filter – https://drupal.org/project/module_filter

oferă posibilitatea de a căuta în timp real un modul existent în site

devel – https://drupal.org/project/devel

numeroase instrumente pentru dezvoltator în scopul depanării site-ului

drush

nu este un modul, este un program ce permite descărcarea și instalarea automată a modulelor folosind comenzi scurte în linia de comandă

Pentru a abstractiza cât mai mult componentele site-ului și pentru a folosi metode standard adoptate de întreaga comunitate Drupal se folosesc modulele:

entity – https://www.drupal.org/project/entity

oferă un API central pentru a lucra cu entitățile din Drupal

ctools – https://www.drupal.org/project/ctools

oferă un API pentru a crea plugin-uri, contexte, pagini cu AJAX, formulare, ferestre modale, etc.

libraries – https://www.drupal.org/project/libraries

permite adăugarea de biblioteci externe în Drupal

jquery_update – https://www.drupal.org/project/jquery_update

oferă posibilitatea de a folosi diverse versiuni ale bibliotecii jQuery (JavaScript)

6

Având în vedere că site-ul trebuie să prezinte locația obiectivului pe hartă sunt necesare anumite module ce oferă această posibilitate. Așadar, vom folosi următoarele module ce țin de geolocație:

addressfield – https://www.drupal.org/project/addressfield

definește un nou tip de câmp ce poate fi atașat entităților prin care se poate introduce o adresă

geofield – https://www.drupal.org/project/geofield

ajută la stocarea datelor de tip geo (punct, linie, poligon, etc.)

oferă diferite moduri de formatare

geofield_gmap – https://www.drupal.org/project/geofield_gmap

oferă widget și stil de formatare pentru Google Maps

integrat cu modulul geofield

geocoder – https://www.drupal.org/project/geocoder

transformă orice adresă sau locație într-o geolocație

integrat cu modulele addressfield și geofield

ip_geoloc – https://www.drupal.org/project/ip_geoloc

integrat cu numeroase API-uri pentru geolocație (Leaflet, Google)

oferă posibilitatea de a găsi geolocația utilizatorului după adresa IP

leaflet – https://www.drupal.org/project/leaflet

serviciu open source ce oferă hărți interactive fără a avea nevoie de API key

integrat cu browserele mobile

leaflet_more_maps – https://www.drupal.org/project/leaflet_more_maps

conține numeroase versiuni de hărți

Pentru a permite evaluarea punctelor de interes de către utilizatori vom folosi modulul fivestar (https://www.drupal.org/project/fivestar). Acest modul este scris peste modulul votingapi (https://www.drupal.org/project/votingapi) ce ofera o modalitate standard de evaluare a conținutului de pe site.

Crearea de pagini dinamice folosind tipuri de conținut existente se face cu ajutorul modulului views (https://www.drupal.org/project/views). O interfață de administrare a paginilor ne este oferită de către sub-modulul views_ui. Cu ajutorul modulului views putem configura în cele

7

mai mici detalii modul în care este afișat un conținut sau o listare de conținut. Avem chiar și posibilitatea de a filtra, grupa sau sorta conținutul.

2.2 Alegerea unui design

Drupal oferă un sistem ce permite schimbarea design-ului unui site prin aplicarea de teme (themes). Fiecare temă oferă regiuni – locuri unde diferite bucăți din conținutul site-ului (blocks) pot fi plasate (fig. 2.2.1).

Fig. 2.2.1 – Demonstrarea regiunilor pentru tema Corporate Clean

Cu toate că putem oricând schimba tema site-ului este bine ca aceasta să fie utilizată chiar de la început pentru a putea vizualiza mai ușor dezvoltarea site-ului. Ca temă de bază pentru acest site am ales Corporate Clean (https://www.drupal.org/project/corporateclean) deoarece este responsivă (fig. 2.2.2 și 2.2.3), culorile ei pot fi configurate ușor (fig 2.2.4) și oferă multe alte setări, toate disponibile în interfața de administrare a site-ului.

8

Fig. 2.2.2 – Varianta desktop Fig. 2.2.3 – Varianta mobile

Fig. 2.2.4 – Configurarea culorilor pentru tema Corporate Clean

9

2.3 Crearea tipurilor de conținut

Aproape orice conținut din Drupal este abstractizat de entități: utilizatori, termeni de taxonomie, articole, etc. La baza lor, entitățile sunt formate din proprietăți și câmpuri (field-uri) de diverse tipuri. O proprietate poate avea o singură valoare scalară, pe când field-urile pot avea multiple valori compuse. Fiecare tip de entitate poate oferi diferite variante (bundles) ce contin diferite field-uri. Cele mai importante setări pentru un field sunt:

tipul de widget – modul in care datele sunt introduse în field

formatter – modul în care datele sunt afișate pe pagină într-un anumit view mode – fiecare bundle poate avea diverse moduri de vizualizare (view modes)

numărul de valori acceptate

Entitatea de bază pentru conținut în Drupal este node-ul, el oferă implicit două bundle-uri:

basic page – conține doar proprietatea de titlu și field-ul pentru descriere

article – conține titlu, descriere, imagini și termeni de taxonomie pentru a categoriza articolul

Adițional la node-uri se pot atașa comentarii dacă se dorește.

În continuare vom crea un nou bundle pentru node ce va conține field-urile necesare unui punct de interes:

Fig. 2.3.1 – Widget field Location

10

Term

Category Select list reference

Fig. 2.3.3 – Widget field Category

Fig. 2.3.4 – Widget field Body

11

Field-ul Location salvează informații despre locația punctului de interes: țară, adresă, oraș, cod poștal. Toate aceste câmpuri pot fi configurate cu ajutorul interfeței de administrare.

Field-ul Geofield salvează informații despre geolocația punctului de interes: latitudine și longitudine. Deoarece pentru utilizatorii normali este dificilă introducerea acestor informații, ele se autocompletează cu ajutorul modulului geocoder folosind valorile field-ului Location.

Field-ul Images poate conține un număr nelimitat de imagini ale punctului de interes. În mod obligatoriu, orice punct de interes trebuie să conțină cel puțin o imagine.

Field-ul Vote stochează rating-ul dat de utilizatori punctului de interes. Valoarea lui este actualizată în mod automat atunci când un utilizator acordă un rating.

Field-ul Category referențiază diverși termeni de taxonomie pentru a specifica din ce categorie face parte punc1

Field-ul Location salvează informații despre locația punctului de interes: țară, adresă, oraș, cod poștal. Toate aceste câmpuri pot fi configurate cu ajutorul interfeței de administrare.

Field-ul Geofield salvează informații despre geolocația punctului de interes: latitudine și longitudine. Deoarece pentru utilizatorii normali este dificilă introducerea acestor informații, ele se autocompletează cu ajutorul modulului geocoder folosind valorile field-ului Location.

Field-ul Images poate conține un număr nelimitat de imagini ale punctului de interes. În mod obligatoriu, orice punct de interes trebuie să conțină cel puțin o imagine.

Field-ul Vote stochează rating-ul dat de utilizatori punctului de interes. Valoarea lui este actualizată în mod automat atunci când un utilizator acordă un rating.

Field-ul Category referențiază diverși termeni de taxonomie pentru a specifica din ce categorie face parte punctul de interes. Pentru acest lucru am creat un nou vocabular numit Categories în care se pot adăuga termeni de taxonomie. Valorile ce le poate avea acest câmp pot fi doar din vocabularul Categories.

Fig. 2.3.5 – Administrarea termenilor de taxonomie pentru vocabularul Categories

12

Field-ul Body salvează descrierea obiectivului. Widget-ul său este de tip WYSIWYG (what you see is what you get), acesta permițând stilizarea textului.

Proprietate Title salvează titlul paginii punctului de interes, fiind implicită în entitatea de tip node.

Acum că avem definit tipul nostru de conținut puteam adăuga câteva obiective de test, deoarece pasul următor este crearea a diferite moduri de vizualizare pentru node. Drupal vine cu două view mode-uri prestabilite pentru node: default (pagina node-ului) și teaser (o bucată reprezentativă din node ce este folosită de obicei în listări).

În view mode-ul default vom afișa toate câmpurile entității împreună cu setăriile din fig. 2.3.6:

Fig. 2.3.6 – Default view mode

Observăm că pentru field-ul Vote s-a folosit formatter-ul As Stars ce ne va arăta media rating-ului dat de utilizatori, pentru Geofield s-a folosit o hartă cu diverse setări de control, pentru Images o galerie jQuery, iar pentru Category un link către termenul de taxonomie.

În view mode-ul teaser vom ascunde doar câmpul Geofield, setările sunt prezente în fig. 2.3.7:

Fig. 2.3.7 – Teaser view mode

13

2.4 Crearea paginilor folosind modulul views

Prima pagină creată cu views va fi o hartă ce conține toate punctele de interes înregistrate în sistem, iar acestea vor putea fi filtrate după titlu. Începem prin a crea un nou view:

navigăm pe pagina /admin/structure/views

alegem opțiunea “Add new view”

ca și nume folosim “Obiective turistice”

alegem “Show Content of type Obiectiv sorted by Newest first”

bifăm opțiunea “Create a page” și setăm titlul paginii “Obiective turistice Oradea”

path-ul va fi obiective-turistice

display format-ul va fi “Map (Leaflet API, via IPGV&M)”

numarul de itemi afișați va fi 100 și se debifează opțiunea de a crea paginare

Fig. 2.4.1 – Crearea unui view folosind interfața

după salvare, adăugăm trei field-uri în view:

Content: Title – titlul obiectivului, la setări alegem opțiunea “Link this field to the original piece of content” pentru a crea o legătură către pagina obiectivului și debifăm opțiunea “Create a label”

14

Content: Geofield – acest field nu va fi afișat dar este necesar pentru a extrage geolocația, astfel vom alege opțiunea “Exclude from display” iar ca formatter “Well Known Text (WKT)”

Content: Location – adresa obiectivului

modificăm configurația de formatare “Map (Leaflet API, via IPGV&M)” a view-ului:

Map – Google roadmap 0..18 (tipul de hartă)

Map height – 400px (dimensiune hartă)

Name of latitude field in Views query – Content: Geofield (preluare geolocație din field-ul Geofield al node-ului)

Name of longitude field in Views query – <none>

Default location marker – blue (culoarea marker-ului care indică obiectivul)

Map centering options – Center the map on the visitor's current location (harta va fi centrată în funcție de locația utilizatorului detectată cu ajutorul modulului ip_geoloc)

Visitor marker – orange (culoarea marker-ului care indică locația utilizatorului)

adăugăm filtre pentru conținut

Content: Published = Yes (afișăm doar conținut publicat)

Content: Type = Obiectiv (se afișează doar node-urile care au bundle Obiectiv)

Content: Title (exposed) – pune acest filtru la dispoziția utilizatorului care va putea introduce text pentru a căuta puncte de interes

se bifează opțiunea “Expose this filter to visitors, to allow them to change it”

se alege ca și operator “contains”

se pune la dispoziția oricărui utilizator

se salvează setările pentru field apoi în views se alege “Exposed form style” ca fiind “basic” fără a folosi “Expose sort order”

se salvează view-ul și se testează pagina

Pe lângă această pagină se vor crea și altele în mod similar.

15

Fig. 2.4.2 – Configurația finală a view-ului

Fig. 2.4.3 – Harta creată cu ajutorul modulului views

16

3. Crearea unui modul de listare a punctelor de interes

Pentru a crea un modul compatibil cu Drupal trebuie urmate câteva reguli simple și clare:

directorul în care se află modulul are același nume ca și modulul și se va pune în directorul sites/all/modules

fiecare modul are un fișier .info unde se descrie pe sine

opțional, codul de instalare se pune în fișierul .install

codul principal al modulului va fi într-un fișier .module

codul trebuie scris respectând standardele

În următoarele pagini vom descrie fiecare pas pentru crearea unui modul de listare a punctelor de interes având două tipuri de filtre. Numele modulului va fi licenta.

3.1 Structura fișierului .info

Exemplu de fișier .info:

name = Super Modul description = Descriere modul core = 7.x

package = Module licenta

configure = admin/config/super_module

dependencies[] = module_x dependencies[] = module_y

files[] = path/NumeClasa.inc files[] = path/NumeClasaX.inc

scripts[] = path/javascript.js scripts[] = path/javascript2.js

stylesheets[all][] = path/style.css stylesheets[all][] = path/style2.css

php = 5.3

; nu este indicată scrierea manuală a versiunii version = 1.0

17

Proprietăți ce se pot folosi în fișierul .info[2]:

name (obligatoriu)

numele modulului care dorești să apară în pagina de administrare a modulelor

fiecare cuvând din nume trebuie să înceapă cu majuscule

trebuie să fie cât mai sugestiv și să fie ușor de citit

description (obligatoriu)

o scurtă descriere de maximum 255 de caractere care explică utilitatea modulului

trebuie să fie pe o singură linie

poate conține link-uri

core (obligatoriu)

versiunea de Drupal pentru care este acest modul

pentru Drupal 7 aceasta trebuie să fie 7.x (7.2 nu este corect)

package (opțional, implicit este Others)

categoria din care face parte modulul

modulele ce sunt în aceeași categorie servesc un scop comun

configure (opțional)

calea către pagina de configurare a modulului

dependencies (opțional)

modulele de care depinde acest modul

files (opțional)

fișiere PHP folosite cu auto-loading (se încarcă automat doar atunci când este nevoie de ele)

stylesheets (opțional)

fișiere CSS care trebuie încărcate pe fiecare pagină

scripts (opțional)

fișiere JavaScript care trebuie încărcate pe fiecare pagină

atât fișierele JavaScript cât și cele CSS pot fi agregate dacă se folosește această metodă

php (opțional)

versiunea minimă de PHP în care modulul funcționează corect

version (descurajat)

versiunea modulului – aceasta se poate obține automat folosind git deploy

18

Având aceste informații putem crea fișierul licenta.info în interiorul directorului licenta.

name = Listarea Obiectivelor

description = Listarea obiectivelor folosind filtre pe titlu si pe categorie package = Licenta

core = 7.x

3.2 Structura fișierului .install

Fișierul .install conține un număr limitat de hook-uri ce ajută la instalare/dezinstalarea, activarea/dezactivare și actualizarea modulului.

Un hook este o funcție în PHP care extinde funcționalitatea Drupalului sau a altor module. Hook-urile începe cu numele modulului în care se implementează urmat de numele hook-ului, iar fiecare hook are un set specific de parametri precum și o valoare specifică de return.

Hook-uri utile în fișierul .install:

hook_install()

apelat atunci când modulul se instalează

hook_uninstall()

apelat la dezinstalarea modulului

hook_schema()

apelat la instalarea modulului

returnează o schemă ce definește tabelele SQL necesare modulului

hook_enable()

apelat la activarea modulului

hook_disable()

apelat la dezactivarea modulului

hook_update_N()

ajută la actualizarea modulelor

19

Exemplu de hook_schema()[3] care creează două tabele cu diverse câmpuri, indecși și legături:

/**

* Implemeents hook_schema() */

function mymodule_schema() { $schema = array();

$schema['people'] = array(

'description' => 'Table containing users', 'fields' => array(

'id' => array(

'description' => 'Primary key for user', 'type' => 'serial', //auto-increment 'unsigned' => TRUE,

'not null' => TRUE,

),

'name' => array(

'description' => 'User name', 'type' => 'varchar',

'length' => 255, 'not null' => TRUE, 'default' => '',

),

'age' => array(

'description' => 'User age', 'type' => 'int',

'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0,

),

),

'primary key' => array('id'),

);

$schema['friends'] = array(

'description' => 'Friendship relations among people', 'fields' => array(

'pid' => array(

'description' => 'User id from {people} table', 'type' => 'int',

'unsigned' => TRUE, 'not null' => TRUE,

),

'fid' => array(

'description' => 'Friend id from {people} table', 'type' => 'int',

'unsigned' => TRUE, 'not null' => TRUE,

20

),

'since' => array(

'description' => 'Timestamp indicating when users became friends', 'type' => 'int',

'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0,

),

),

'indexes' => array(

'user_id' => array('pid'), 'friend_id' => array('fid'),

),

'foreign keys' => array( 'people' => array(

'table' => 'people', 'columns' => array(

'pid' => 'id', 'fid' => 'id',

),

),

),

);

return $schema;

}

În acest mod putem avea aceeași structură de tabele pe diferite sisteme de gestiune a bazelor de date (MySQL, PostgreSQL, SQLite, etc.) fără a fi nevoiți să schimbăm codul.

Modulul licenta nu are nevoie de un fișier de install.

3.3 Fișierul .module

În fișierul .module se scrie cea mai mare parte din logica modulului. Începem prin a defini o rută deniumită list către pagina de listare folosind hook_menu().

/**

* Implements hook_menu(). */

function licenta_menu() { $items['list'] = array( 'title' => '',

'description' => '',

21

'page callback' => 'licenta_list_render', 'access callback' => TRUE,

'type' => MENU_CALLBACK, );

return $items;

}

Page callback definește funcția ce va fi apelată atunci când se accesează ruta list, în cazul nostru funcția va fi licenta_list_render(). Access callback specifică faptul ca ruta poate fi accesată de oricine.

/**

* Menu callback for /list path. */

function licenta_list_render() {

$category = (int) @$_GET['categ']; $title = trim(@$_GET['item']);

// Get results.

$data = licenta_list($title, $category, 5);

// No results.

if (!$data['count']) {

return t('Sorry! There are no results for your search!');

}

Get category name. if ($category) {

$category = $category->name;

}

Render each result.

foreach ($data['results'] as &$node) {

Get the renderable array of teaser view mode. $node = node_view($node, 'teaser');

Keep only the first image.

$one_img = $node['field_images']['#items'][0]; unset($node['field_images']['#items']); $node['field_images']['#items'][] = $one_img; // Render the node.

$node = drupal_render($node);

}

// Get the safe page title. if ($title) {

if ($category) {

22

$safe_title = t("@count results for '@search' in @category", array( '@count' => $data['count'],

'@search' => $title, '@category' => $category, ));

}

else {

$safe_title = t("@count results for '@search'", array( '@count' => $data['count'],

'@search' => $title, ));

}

}

elseif ($category) {

$safe_title = t("@count results in @category", array( '@count' => $data['count'],

'@category' => $category, ));

}

else {

$safe_title = t("@count results", array( '@count' => $data['count'],

));

}

// Add page title.

$output = "<h1 class='list-ob-title'>"; $output .= $safe_title;

$output .= "</h1>";

// Add result list.

$output .= theme('item_list', array( 'items' => $data['results'],

'type' => 'ul',

'attributes' => array('id' => 'menu_bar_links'), ));

// Add pager.

$output .= theme('pager');

return $output;

}

Această funcție prezintă modul în care sunt tratate textele ce pot fi traduse cu ajutorul funcției t(), modul în care putem randa node-uri folosind un view mode specific dar și diverse funcții de tema oferite de Drupal (cum ar fi item_list) sau crearea unui paginator. Un alt lucru notabil este

23

faptul ca Drupal lucrează cu așa numitele array-uri randabile, permițând astfel stilizarea lor într-un mod foarte granularizat.

De asemenea, observăm ca am folosit o nouă funcție denumită licenta_list(), scopul acesteia este de a returna numărul total de rezultate ce l-a avut căutarea și primele N node-uri având bundle-ul Obiectiv.

Acest lucru este posibil datorită abstractizării modului în care se pot căuta entități având diverse proprietați folosind clasa EntityFieldQuery. Cu EntityFieldQuery[4] putem adăuga filtre pentru tipul și bundle-ul entității sau pentru proprietăți și field-uri. Evident, avem posibilitatea de a sorta rezultatele după diferite proprietăți sau field-uri sau de a limita numărul lor oferindu-ne totodată și posibilitatea de a crea o paginare pentru această interogare.

/**

Returns all nodes using category and title filter.

@param string $title Title filter

@param int $category Taxonomy term id

@param int $limit Maximum number of results

@return array An array containing the result count and entities. */

function licenta_list(&$title = NULL, &$category = 0, $limit = 5) {

Create a new EntityFieldQuery in order to find entities. $query = new EntityFieldQuery();

Filter by entity type and bundle.

$query->entityCondition('entity_type', 'node') ->entityCondition('bundle', 'obiectiv');

// Search only published nodes. $query->propertyCondition('status', 1);

Add title filter if any. if ($title) {

$query->propertyCondition('title', '%' . $title. '%', 'like');

}

Add category filter if any.

if ($category > 0 && $category = taxonomy_term_load($category)) { $query->fieldCondition('field_categorie', 'tid', $category->tid, '=');

24

}

else {

$category = NULL;

}

// Sort results by creation time descending. $query->propertyOrderBy('created', 'DESC');

// Create pager. $query->pager($limit);

// Count the results.

$count = $query->count()->execute();

if ($count > 0) { // Get the results.

$query->count = FALSE; $nodes = $query->execute(); unset($query);

$nodes = reset($nodes);

$nodes = node_load_multiple(array_keys($nodes));

}

else {

$nodes = array();

}

return array( 'count' => $count, 'results' => $nodes, );

}

În acest moment avem o pagină funcțională dar problema este că trebuie să adăugăm parametri manual în URL pentru a vedea filtrarea în acțiune. Următorul pas este cel de a crea un formular de căutare și de a-l expune utilizatorului.

Crearea de formulare se face cu ajutorul Drupal Form API[5], acesta fiind bazat pe array-uri randabile. Form API oferă o mulțime de elemente: textbox, textarea, selectbox, checkbox, radio, file upload, etc. dar permite și definirea de noi elemente implementând hook_element_info().

25

/**

* Search form for /list route. */

function licenta_search_form($form, &$form_state) {

// Use the GET method. $form['#method'] = 'GET';

Set the action to our list router. $form['#action'] = '/list';

Add a css class to form.

$form['#attributes']['class'][] = 'input-append';

Create the title filter. $form['item'] =array(

'#type' => 'textfield', '#title' => '',

'#default_value' => trim(@$_GET['item']), '#attributes' => array('class' => array('grid_3'))

);

Create a hidden category which will be populated in other way. $form['categ'] =array(

'#type' => 'hidden', '#title' => '',

'#default_value' => @$_GET['categ'],

);

Add a custom submit button.

$form['search'] = array( '#type' => 'markup',

'#markup' => '<div class="search-abs grid_3"><i class="btn hidden-phone add-on icon-search"></i></div>',

);

return $form;

}

Deoarece Drupal securizează formularele cu ajutorul unor token-uri, URL-ul rezultat este destul de lung, prin urmare putem elimina aceste măsuri de securitate deoarece funcția licenta_list() tratează problemele de securitate. Așadar, vom implementa hook_form_FORM_ID_form_alter() pentru a șterge elementele nedorite din formular.

26

/**

* Implements hook_form_FORM_ID_form_alter(). */

function licenta_form_licenta_search_form_alter(&$form, &$form_state) { $remove = array('form_build_id', 'form_token', 'form_id', 'op');

foreach ($remove as $property) { unset($form[$property]);

}

}

Afișarea formularului și a filtrelor pentru categorie se va face folosind block-uri. După cum precizam anterior, block-urile sunt bucăți de conținut ce se pot plasa în diferite regiuni ale temei.

Pentru a crea block-uri trebuie să informăm Drupalul de existența lor, utilizând hook_block_info():

/**

* Implements hook_block_info(). */

function licenta_block_info() {

// Search form block. $blocks['licenta_search_item'] = array(

Info about block, shown in blocks manager. 'info' => t('Search form'),

Desired theme region, can be changed from UI. 'region' => 'search_area',

Block is active.

'status' => 1,

);

// Category filter block. $blocks['licenta_categories_facet'] = array(

'info' => t('Categories'), 'region' => 'sidebar_first', 'status' => 1,

);

return $blocks;

}

27

Conținutul propriu-zis al block-urilor se creează folosind hook_block_view(). Acest hook primește ca parametru numele block-ului, astfel fiecare block din hook_block_info() trebuie tratat separat.

Cea mai indicată soluție în acest caz este folosirea instrucțiunii switch din PHP.

/**

* Implements hook_block_view(). */

function licenta_block_view($block_name) {

// Treat each block individually. switch ($block_name) {

// Category filter block.

case 'licenta_categories_facet': // Get the vocabulary id.

$vocabulary = taxonomy_vocabulary_machine_name_load('categorie'); // Load all taxonomy terms from vocabulary.

$terms = taxonomy_term_load_multiple(array(), array('vid' =>

$vocabulary->vid));

Here will be added all term links. $items = array();

Add All as first filter, like a reset. $items[] = l(t('All'), '/list');

Add each term as a filter. foreach ($terms as $term) {

$items[] = l($term->name, '/list', array( 'query' => array('categ' => $term->tid),

));

}

return array(

// Block title.

'subject' => t('Categories'), // Block content.

'content' => theme('item_list', array( 'items' => $items,

'type' => 'ul',

'attributes' => array('id' => 'categories-facet'),

)),

);

// Form filter block.

case 'licenta_search_item': // Get form by name.

$form = drupal_get_form('licenta_search_from');

return array(

28

No title. 'subject' => '',

Render the form.

'content' => drupal_render($form)

);

}

}

Cu acestea am încheiat procesul de creare a modulului licenta. În următorul capitol vom analiza partea de template-uri a sistemului de teme din Drupal. Rezultatul obținut ar trebui să fie ca în fig. 3.3.1.

Fig. 3.3.1 – Listarea creată de modulul licenta

29

4. Sitemul de template-uri din Drupal

Temele din Drupal permit editarea fiecărei părți din site prin fișiere de template. Fișiere de template au extensia .tpl.php, iar numele lor este legat de numele entităților, a view mode-urilor, a block-urilor sau a altor elemente. De obicei temele sunt salvate în directorul sites/all/themes, având o structură asemănătoare modulelor. Figura 4.1 ne prezintă într-un mod detaliat structura de fișiere și corespondența lor în pagina web.

Fig. 4.1 – Structura unei teme Drupal

30

În următoarele pagini vom arăta două exemple de fișiere de template, restul fișierelor având o structură asemănătoare. Tema aleasă este Corporate Clean, deci fișierele de template se vor afla în directorul sites/all/themes/corporateclean.

Template-ul page.tpl.php cuprinde tot corpul site-ului mai puțin partea de head unde se declară titlul pagini, dependințe de stil, scripturi sau alte meta-informații. Acest template este foarte important deoarece aici se vor randa regiunile pe care tema le pune la dispoziție prin declararea lor în fișierul .info. Un exemplu simplu al acestui template poate fi găsit în interiorul temei Garland oferită de Drupal 7:

<?php print render($page['header']); ?> <div id="wrapper">

<div id="container" class="clearfix">

<div id="header">

<div id="logo-floater">

<?php if ($logo || $site_title): ?> <?php if ($title): ?>

<div id="branding"><strong><a href="<?php print $front_page ?>"> <?php if ($logo): ?>

<img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" />

<?php endif; ?>

<?php print $site_html ?> </a></strong></div>

<?php else: /* Use h1 when the content title is empty */ ?>

<h1 id="branding"><a href="<?php print $front_page ?>"> <?php if ($logo): ?>

<img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" />

<?php endif; ?>

<?php print $site_html ?> </a></h1>

<?php endif; ?> <?php endif; ?> </div>

<?php if ($primary_nav): print $primary_nav; endif; ?> <?php if ($secondary_nav): print $secondary_nav; endif; ?>

</div> <!– /#header –>

<?php if ($page['sidebar_first']): ?>

<div id="sidebar-first" class="sidebar"> <?php print render($page['sidebar_first']); ?> </div>

31

<?php endif; ?>

<div id="center"><div id="squeeze"><div class="right-corner"><div class="left-corner">

<?php print $breadcrumb; ?>

<?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?>

<a id="main-content"></a>

<?php if ($tabs): ?><div id="tabs-wrapper" class="clearfix"><?php endif; ?> <?php print render($title_prefix); ?>

<?php if ($title): ?>

<h1<?php print $tabs ? ' class="with-tabs"' : '' ?>><?php print

$title ?></h1>

<?php endif; ?>

<?php print render($title_suffix); ?>

<?php if ($tabs): ?><?php print render($tabs); ?></div><?php endif; ?> <?php print render($tabs2); ?>

<?php print $messages; ?>

<?php print render($page['help']); ?> <?php if ($action_links): ?>

<ul class="action-links"><?php print render($action_links); ?></ul> <?php endif; ?>

<div class="clearfix">

<?php print render($page['content']); ?> </div>

<?php print $feed_icons ?>

<?php print render($page['footer']); ?>

</div></div></div></div> <!– /.left-corner, /.right-corner, /#squeeze, /#center –>

<?php if ($page['sidebar_second']): ?>

<div id="sidebar-second" class="sidebar"> <?php print render($page['sidebar_second']); ?> </div>

<?php endif; ?>

</div> <!– /#container –> </div> <!– /#wrapper –>

Se poate observa că în interiorul template-urilor sintaxa PHP este puțin diferită, într-un mod natural și nu cu acolade. Acest lucru este foarte util deoarece permite adăugarea de wrappere elementelor foarte ușor. Diferențele se pot observa în următoarele două exemple:

<?php foreach ($items as $item) { ?> <div class="wrapper-big">

<?php if ($item != $except) { ?> <div class="wrapper-small">

32

<?php echo $item;?>

</div> <?php}?>

</div> <?php}?>

Problema cu această variantă e ca nu se distinge foarte clar care instrucțiune este închisă de acolade. Datorită faptului că PHP are și o sintaxă specială pentru aceste cazuri problema noastră se rezolvă foarte simplu:

<?php foreach ($items as $item): ?> <div class="wrapper-big">

<?php if ($item != $except): ?> <div class="wrapper-small">

<?php echo $item;?>

</div> <?phpendif;?>

</div>

<?phpendforeach;?>

Pentru a adapta după bunul plac un view mode al unui node se poate crea un template cu structura numelui node–[bundle]–[view-mode].tpl.php. În cazul nostru vom edita view mode-ul teaser al node-ului cu bundle Obiectiv, adică cel care se folosește în listări. Urmând tiparul descris, numele fișierului va fi node–obiectiv-teaser.tpl.php (pentru a edita template-ul pentru întreg bundle-ul se exclude numele view mode-ului: node-obiectiv.tpl.php).

În acest template trebuie tratate individual proprietățile și field-urile pentru view mode-ul teaser folosind sistemul de grid oferit de temă, deoarece putem aranja foarte ușor conținutul.

<?php

// node–obiectiv–teaser.tpl.php global $basePath;

?>

<div class="item grid_8 list-teaser">

<div id="image-teaser" class="grid_2 hidden-phone"> <?php if (!empty($content['field_images'])): ?>

<?php echo drupal_render($content['field_images']);?> <?php endif; ?>

</div>

<div id="info-teaser" class="grid_6"> <div class="border">

33

<div class="title grid_4"> <?php if (!empty($title)): ?>

<h3><a href="<?php echo $basePath . $node_url;?>"><?php echo $title;?></a></h3> <?php endif; ?>

</div>

<div class="categ grid_2 hidden-phone">

<?php if (!empty($content['field_categorie'])): ?>

<?php echo drupal_render($content['field_categorie']);?>

<?php endif; ?> </div>

</div>

<div class="row2-loc-rat"> <div class="rating grid_2">

<?php if (!empty($content['field_vote'])): ?>

<?php echo drupal_render($content['field_vote']);?>

<?php endif; ?> </div>

<div class="loc grid_3">

<?php if (!empty($content['field_location'])): ?> <?php

$location = $content['field_location']['#items'][0]; $address = $location['locality'] . $location['postal_code'];

if (!empty($location['thoroughfare'])) { $address .= ', ' . $location['thoroughfare'];

}

echo drupal_render($content['field_categorie']); ?>

<i class="icon-map-marker"> <?php echo $address; ?></i> <?php endif; ?>

</div>

<div class="desc grid_6">

<?php if (!empty($content['body'])): ?>

<?php echo drupal_render($content['body']);?> <?php endif; ?>

</div> </div> </div>

</div>

Rezultatul acestui template este asemănător cu fig. 4.2.

34

Fig. 4.2 – Teaser view mode creat cu template-ul node–obiectiv–teaser.tpl.php

Desigur, în componența site-ului sunt și alte fișiere de template, dar am ales să nu le prezentăm deoarece implementarea lor se face în mod similar.

35

5. Administrarea site-ului

Administrarea site-ului se face cu foarte mare ușurința deoarece toate componentele pot fi configurate folosind o interfață web. Evident, doar utilizatorul cu drepturi de administrare poate face acest lucru.

5.1 Administrarea utilizatorilor

Utilizatorii înregistrați pot fi administrați accesând /admin/people. Avem posibilitatea de a adăuga, șterge, modifica sau bloca utilizatori. Totodată avem posibilitatea de a adăuga diferite roluri și de a defini permisiuni pentru fiecare rol în parte.

5.2 Administrarea conținutului

Administrarea conținutului se poate face accesând /admin/content. Avem posibilitatea de a adăuga, șterge și edita conținutul sau comentariile atașate. Pentru a modifica structura un tip de conținut este necesară accesarea /admin/structure/types. Aici vom putea modifica field-urile pentru fiecare tip de conținut în parte.

5.3 Administrarea meniului

Meniul poate fi configurat accesând /admin/structure/menu. Există diferite tipuri de meniuri, cel mai important fiindmeniul principal. Putem adăuga, șterge și modifica meniul principal sau putem adăuga unii itemi din meniu în meniul secundar, precum se poate observa în fig. 5.3.1.

Fig. 5.3.1 – Administrarea meniului principal

36

5.4 Administrarea termenilor de taxonomie

Termenii de taxonomie ne ajută să clasificăm conținutul, să-l punem în diverse categorii. Termenii sunt atașați unor vocabulare care au rolul de a explica natura lor. Pentru a administra taxonomiile vom accessa /admin/structure/taxonomy apoi alegem vocabularul în care dorim să adăugăm, ștergem sau modificăm diferiți termeni. Având în vedere că și termenii de taxonomie sunt entități, le pot fi atașate diverse field-uri, editând un vocabular anume.

5.5 Administrarea view-urilor

Pentru a crea diferite pagini sau blocuri putem folosi modulul views. Acesta se poate accesa la /admin/structure/views, undeva putem alege din listă un view existăm ca sa-l edităm sau putem crea unul nou. Structura unui view poate fi destul de complexă după cum se poate observa și în capitolul 2.4.

Fig. 5.5.1 – Administrarea view-urilor

37

5.6 Administrarea temelor și a modulelor

Temele pot fi administrate accesând /admin/apperance. Putem schimba setarile temelor cum ar fi paleta de culor, dimensiunea fonturilor, etc. sau putem alege o altă tema. De reținut este faptul că administratorul are de obicei o altă temă.

Modulele pot fi instalate/dezinstalate accesând /admin/modules. Aici vedem descrierea fiecărui modul în parte precum și dependințele. Modulele care nu satisfac toate dependințele nu pot fi instalate. Pentru a dezactiva un modul trebuie întâi să dezactivăm toate modulele ce depind de el.

Dezactivarea unui modul nu implică ștergerea datelor create de modul, acest lucru se întâmplă doar la dezinstalare.

Fig. 5.6.1 – Administrarea modulelor

38

5.8 Administrarea altor componente

Pe lângă componentele menționate anterior, în Drupal mai există numeroase setări. Ele pot fi configurate accesând /admin/config. O scurtă listă a acestor setări cuprinde:

setări de performanță, caching, agregare de fișiere css sau js

setări de regiune și timp

setări pentru URL-uri

setări pentru limba site-ului sau traduceri

setări pentru fișiere sau stiluri de imagini

setări de tip firewall

Fig. 5.8.1 – Interfața de configurare Drupal

39

5.9 Rapoarte

Pentru a întreține un site web este obligatoriu nevoie de rapoarte. În Drupal aceste rapoarte conțin diverse informații: de la erori în codul PHP pâna la rapoarte ale serverului web. În mod implicit există următoarele tipuri de rapoarte:

starea site-ului – dacă modulele sunt actualizate, pentru a preveni găurile de securitate

loguri – mesaje de informare, atenționare sau erori în cod

404 – pagini inexistente

403 – pagini la care un utilizator nu are acces

top căutari – care au fost cuvintele cele mai căutate pe site

40

6. Finalizarea și publicarea site-ului

Ultimii pași în finalizarea unui site o reprezintă conținutul și traducerea acestuia în diferite limbi (dacă este cazul). Deoarece Drupal este format dintr-o comunitate foarte mare, găsim traduceri pentru aproape toate limbile, inclusiv limba română[6]. Traducerile se importă în site foarte simplu folosind modulul i18n (https://drupal.org/project/i18n) iar pentru textele rămase netraduse se poate folosi modulul l10n_client (https://drupal.org/project/l10n_client) care oferă o interfață de traducere a textului din pagina pe care se află utilizatorul. Acest lucru este foarte important datorită faptului că traducătorul poate vedea exact contextul în care s-a folosit o propoziție sau un cuvânt.

După ce site-ul a fost testat se poate publica pe server pentru a fi accesibil utilizatorilor. Există diferite metode de publicare: de la folosirea sistemelor de versionare până la folosirea programului drush.

6.1 Imagini din site

Prima pagină a site-ului prezintă un slideshow și un scurt istoric al orașului Oradea (fig. 6.1.1).

Fig. 6.1.1 – Prima pagină

41

Pagina unde utilizatorii pot să-și creeze un cont pe site sau să se autentifice (fig. 6.1.2). În cazul în care utilizatorul nu-și mai amintește parola, poate solicita recuperarea ei prin e-mail.

Fig. 6.1.2 – Pagina de autentificare

Profilul utilizatorului înregistrat (fig. 6.1.3). Aici utilizatorul poate modifica datele profilului său, cum ar fi avatarul.

Fig. 6.1.3 – Profilul utilizatorului

Pagina unui obiectiv (fig. 6.1.4) ne prezintă descrierea obiectivului împreună cu un slideshow de iamgini și locația lui exactă pe hartă. La apăsarea marker-ului de pe hartă se afișează adresa completă.

42

Fig. 6.1.4 – Pagina unui punct de interes

Tot pe această pagină, utilizatorii evaluează obiectivul oferind un număr de stele cuprins între unu și cinci sau lasă comentarii (fig. 6.1.5).

Fig. 6.1.5 – Adăugare comentarii

43

Bibliografie

Drupal 7 Module Development – Matt Butcher, Matt Farina, Ken Rickard, Greg Dunlap, Larry Garfield, John Albin Winkins – ISBN 1849511160

Drupal 7 Themes – Ric Shreves – ISBN 1849512760

Drupal 7 Views Cookbook – J. Ayen Green – ISBN 1849514348

Corporate Clean Theme – https://www.drupal.org/project/corporateclean

https://drupal.org/about

https://www.drupal.org/node/542202

https://api.drupal.org/api/drupal/modules!system!system.api.php/function/hook_schema/7

https://api.drupal.org/api/drupal/includes!entity.inc/class/EntityFieldQuery/7

https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/7

https://localize.drupal.org/translate/languages/ro

44

Bibliografie

Drupal 7 Module Development – Matt Butcher, Matt Farina, Ken Rickard, Greg Dunlap, Larry Garfield, John Albin Winkins – ISBN 1849511160

Drupal 7 Themes – Ric Shreves – ISBN 1849512760

Drupal 7 Views Cookbook – J. Ayen Green – ISBN 1849514348

Corporate Clean Theme – https://www.drupal.org/project/corporateclean

https://drupal.org/about

https://www.drupal.org/node/542202

https://api.drupal.org/api/drupal/modules!system!system.api.php/function/hook_schema/7

https://api.drupal.org/api/drupal/includes!entity.inc/class/EntityFieldQuery/7

https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/7

https://localize.drupal.org/translate/languages/ro

Similar Posts