102
VISOKA ŠKOLA STRUKOVNIH STUDIJA ZA INFORMACIONE I KOMUNIKACIONE TEHNOLOGIJE INTERNET TEHNOLOGIJE CV Creator (MEAN Stack) Z a v r š n i r a d 1

Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

  • Upload
    buikiet

  • View
    221

  • Download
    4

Embed Size (px)

Citation preview

Page 1: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

VISOKA ŠKOLA STRUKOVNIH STUDIJAZA INFORMACIONE I KOMUNIKACIONE TEHNOLOGIJE

INTERNET TEHNOLOGIJE

CV Creator (MEAN Stack) Z a v r š n i r a d

Mentor: Student:

Dr Nenad Kojić, dipl. inž. Marko Pavlović 57/13

Beograd, 2016.

1

Page 2: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

VISOKA ŠKOLA STRUKOVNIH STUDIJAZA INFORMACIONE I KOMUNIKACIONE TEHNOLOGIJE

INTERNET TEHNOLOGIJE

Predmet: Web dizajn

Tema: CV Creator (MEAN stack)

Ocena ___ ( )

Članovi komisije:

______________________

______________________

______________________

2

Page 3: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

Sadržaj:

1. UVOD.............................................................................................................................................................6

2. RADNO OKRUŽENJE...............................................................................................................................7-8

3. ORGANIZACIJA...........................................................................................................................................9

3.1 Logovanje...............................................................................................................................................10

3.2 Registracija..............................................................................................................................................11

3.3 Home.................................................................................................................................................11-12

3.3.1 Personalne Inforamcije........................................................................................................................13

3.3.2 Veštine.................................................................................................................................................14

3.3.3 Projekti...........................................................................................................................................15-16

3.4 Pretraga korisnika...................................................................................................................................17

3.5 Pregled korisničkh profila.......................................................................................................................18

4. KOD..............................................................................................................................................................19

4.0.0 server.js...............................................................................................................................................19

4.0.1 app.js............................................................................................................................................19-20

4.1.0 index.html....................................................................................................................................20-24

4.1.1 app.js.................................................................................................................................................24

4.1.2 appRoutes.js.................................................................................................................................24-25

4.2.0 AuthService.js..............................................................................................................................26-27

4.2.1 ErrorService.js..................................................................................................................................28

4.2.2 ProjectService.js..........................................................................................................................28-30

4.2.3 SearchService.js...........................................................................................................................30-31

4.2.4 SkillService.js..............................................................................................................................31-33

4.2.5 UserService.js..............................................................................................................................33-36

4.2.6 ValidationService.js.....................................................................................................................36-37

4.3.0.1 ErrorCtrl.js.....................................................................................................................................37

4.3.0.2 errorDirectives.js............................................................................................................................38

4.3.0.3 errorTemplate.html........................................................................................................................38

4.3.1.0 HomeCtrl.js...............................................................................................................................38-40

4.3.1.1 home.html.................................................................................................................................40-41

4.3.1.2 InfoCtrl.js..................................................................................................................................41-44

3

Page 4: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

4.3.1.3 InfoDirectiva.js..............................................................................................................................44

4.3.1.4 infoData.html.................................................................................................................................45

4.3.1.5 infoDataEdit.html.....................................................................................................................45-46

4.3.1.6 infoDataShow.html...................................................................................................................46-47

4.3.1.7 ProjectCtrl.js.............................................................................................................................47-50

4.3.1.8 ProjectDirectiva.js.....................................................................................................................50-51

4.3.1.9 projectAdd.html.............................................................................................................................52

4.3.1.10 projectData.html...........................................................................................................................52

4..3.1.11 projectEdit.html...........................................................................................................................53

4.3.1.12 projectsData.html.........................................................................................................................53

4.3.1.13 projectShow.html....................................................................................................................53-54

4.3.1.14 SkillCtrl.js...............................................................................................................................54-56

4.3.1.15 SkillDirectiva.js......................................................................................................................56-57

4.3.1.16 skillData.html...............................................................................................................................57

4.3.1.17 skillDataEdit.html........................................................................................................................57

4.3.1.18 skillDataShow.html.................................................................................................................57-58

4.3.2.0 login.html..................................................................................................................................58-60

4.3.2.1 LoginCtrl.js...............................................................................................................................60-61

4.3.3.0 NavCtrl.js.......................................................................................................................................61

4.3.4.0 register.html..............................................................................................................................62-64

4.3.4.1 RegisterCtrl.js...........................................................................................................................64-66

4.3.5.0 search.html.....................................................................................................................................66

4.3.5.1 SearchCtrl.js..............................................................................................................................67-68

4.3.5.2 profile.html....................................................................................................................................69

4.3.5.3 ProfileCtrl.js...................................................................................................................................69

4.3.6 style.css...........................................................................................................................................70

4.4.0 validation.js................................................................................................................................70-71

4.4.1.0 clsss/project.js.........................................................................................................................71-72

4.4.1.1 clsss/skill.js...................................................................................................................................72

4.4.1.2 clsss/user.js.............................................................................................................................72-74

4.4.2.0 template.ejs.............................................................................................................................75-77

4.4.2.1 toPDF.js..................................................................................................................................77-79

4

Page 5: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

4.4.3.0 model/skill.js................................................................................................................................79

4.4.3.1 model/user.js................................................................................................................................80

4.4.4.0 route/project.js........................................................................................................................80-82

4.4.4.1 route/skill.js.............................................................................................................................82-83

4.4.4.2 route/user.js.............................................................................................................................83-90

4.4.4.3 route/index.js...........................................................................................................................90-92

5. BAZA PODATAKA.....................................................................................................................................93

5.1 Struktura baze podataka ....................................................................................................................93

6.ZAKLJUČAK................................................................................................................................................94

7. LITERATURA.............................................................................................................................................95

5

Page 6: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

1.UVOD

CV Creator je dinamički sajt koji služi za pravljenje online CV-a koji je rađen u MEAN Stak-u. MEAN stack su skup tehnologija MongoDB, ExpressJS, AngularJS i NodeJS. Korišćen je npm (Node Package Manager) za serveske dependencis I bower za klijenske dependencis. Sajt je napravljen sa ciljem učenja novih tehnologija. Projekat se takođe nalazi na bitbuket-u I githabu.

6

Page 7: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

2. RADNO OKRUŽENJECeo sajt je rađen u programu Visual Studio Code. Pored toga, na serverskoj strani

podaci su skladišteni u MongoDB bazi podataka kojom se upravlja preko terminal ili robomongo-a.

Na serverskoj strain koristi se: NodeJS ExpressJS Express-validator za validaciju request parametra. Passport za autetifikaciju koi sadrzi preko 300 strategija za autetifikaciju. Cookie session za sesiju korisnika. Ejs I html-pdf za pripremu template I parsiranje template u pdf file. Connect-multiparty da bi omogućili slanje fajlova preko requesta. body-parser za slanje podataka u requesta body. Bcrypt za kriptovanje sifre Loadash pomoćna bibloteka gde omogućava lakši rad sa kolekcijam I nizovima. Moment rad sa vremenom I datumom. RandomJS za generisanje random broja

Na klijenskoj strani se koristi: AngularJS. Angular route za front end rutiranje. Ng-file-upload za rad sa fajlovima. Bootstrap za dizajn sajta. Font-awsome I bootstrap za ikone sa društvenih mreža.

Baza: MongoDB. Mongoose za praljenje šema baze. Robomongo za lakše upravljane baze

- MongoDB

Je dokument baza podataka (Document Database), gde su strukture podataka uparene kljuc I vresnsto parovi (key and value pairs). MongoDB dokumenti su slinči JSON objektim. Vrednost polja može da sadrži nove dokumente, nizove, nizove dokumenta I proste vrednosti.

Bogat query jezik, podržava sve CRUD operacije. Spada u NoSQL baze.

- ExpressJS

7

Page 8: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

Je light-weight web framework koji omogucava organizaciju naše web aplikacije na serverskoj strani.

- AngularJS

Veliku podršku i zajednicu. Ako imate neki problem velika verovatnoća da je neko već to rešio. Korišćenje direktiva. Directive se dodaju u kod kako bi proširile informacije elementa. Takodje

directive su fokusirane na određenu logiku što vam omogućava produktivnost, a I možete ih koristiti na više mesta.

Sa pravim pristupom mogu ubrzava razvoj aplikacija, sa minimalnim kodom. Koristi MVC patern gde se odvaja logika aplikacije od prikaza podataka. Modularnost, koji nam omogućava da pravimo aplikaciju spajanjem vise modula. Tako da moduli

zavise jednih od drugih. Ima već dosta rešenja koja mogu da se iskoriste kao npr. Za rutiranje ui-router ili ui-grid I ng-

table za rad sa tablama. Two-way data binding, svaka promena na user interfejsu automatski utice na objekat aplikacije I

obrnuto.

- NodeJS

Je JavaScript runtime koji koristi V8 enigine. V8 je open source JavaScript engine pisan u C++, koji koristi JavaScript kod I kompajlira ga u mašinski kod, koristi ga NodeJS I Chrom pretraživač.

NPM je najveća I najbogatija open source skladište (repository). Neverovatno brz, I ako ima jednu nit, asihrono ponašanje nadoknađuje to. Pošto je ima jednu nit

I nema ostalih niti koje bih zahtevale resurse. Olakšana komunikacija u realnom vrmeneu, pored event-loop koji može da zadovoljava više

korisnika od jednom. Postoje websoket protokoli koji pojednostavljaju kanal za dvostruku komunikaciju izmedju klijenta I server.

8

Page 9: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3. ORGANIZACIJA

Slika 1. Organizacija web stranica

9

Page 10: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.1 Logovanje

Header stranice se sastoji od logo-a I navigacije, koja sadže dva dugmeta ‘Register’ I ‘Login’. Dugme ‘Register’ void korisnika na stranicu za registraciju, dok dugme ‘Login’ vodi korisnika na stranicu za logovanje.

Centralni deo strane sadži se od tri dugmeta ‘Register’, ‘Login’ I ‘Reset’. Klikom na dumge ‘Register’, bićecmo preusmerni na stranicu za registraciju. Klikom na ‘Login’ ako je korisnik popunio sva polja počeće proces autetifikacije. Klikom na dumge ‘Reset’ podaci će biti restartovani.

Footer sadrži ko je o autoru sajta, kao I kako kontaktirati autora.

Slika 2. Login

10

Page 11: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.2 Registracija

Na ovoj strain se nalazi forma za registraciju gde se korisnik ima opciju da se registruje. Validacija svih polja se radi na klijenskoj I serverskoj strani.

Slika 2. Registration

3.3 Home

Home strana sadrži sve informacije o korisniku. Takodje nudi mogućnost ažuriranja, dodavanja I brisanje trenutno prikazanih informacija.

11

Page 12: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

Slika 1. Home strana

12

Page 13: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.3.1 Personalne informacije

Personalne informacije su infomacije korisnka. Pritiskom na dugme ‘Edit’ korisnik prelazi u ‘Edit mode’ gde će imati mogućnost ažuriranje trenutnih informacija. Kada korisnik ude u ‘Edit mode’ imaće dve opcije ‘Save’ I ‘Cancle’. Dugme ‘Save’ sačuvaće izmenjene infomacije korisnika, dok će dugme ‘Cancle’ da ih poništi.

Slike 3. Info show

.

Slike 4. Info edit

13

Page 14: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.3.2 Veštine

Veštine kao I personalne informacije imeju opciju ‘Edit’, gde kada korisnik klikne na dugme ‘Edit’ prelazi u ‘Edit mode’. Tokom ‘Edit mode’ korisnik može da pretražuje ponuđene veštine, a kao I u personalne infomacije ima opcije ‘Save’ I ‘Cancle’ koje rade isto kao I kod presonalnih informacija. Takodje kada se korisnik nalazi u ‘Edit mode’ im opciju da uklanja veštine preko X koji se nalazi pred imena veštine. ‘Add’ dugme ili klikom na ‘Enter’ suži da se doda nova veština, nakon dodavanja novih veština korisnik mora da ih snimi klikom na ‘Save’ ili u će one biti poništene.

Slika 5. Skill show

Slika 6. Skill edit

14

Page 15: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.3.3 Projekti

Kao I personalne infomacije I veštine ima opciu ‘Edit’ gde ulazimo u ‘Edit mode’, ali takodje ima opciju ‘Add New Project’. ‘Edit’ dugme radi identicno kao I prethodne dve sekcije. Dok ‘Add New Project’ služi kao što mu I samo ime kaže dodavanje novog projekta.

Slika 7. Project show

15

Page 16: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

Slika 7. Project Add

Slika 7. Project edit

16

Page 17: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.6 Pretraga korisnika

Pretraga korisnika omogućava pretragau svih korisnika u bazi. Pretrga moze da se vrši preko korisničkog imena, imena, prezimena ili neko od mogućih kombinacija od ta tri. Rezultat pretrage se sastoji od paginacije koja se radi na serverskoj strani. Takođe postoji dugme ‘See profile’ gde ima mogućnost da se vidi profil oređenog korisnika.

Slika 8. Serach people

17

Page 18: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

3.7 Pregled korisničkog profila

Stranica sadrži informacije o određenom korisniku. Kao I dugme ‘Download (username) CV’ gde se preuzima pdf format profila korisnika.

Slika 9. User profile

18

Page 19: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

4. KOD

4.0.0 server.js

var app = require('./app');

app.set('port', process.env.PORT || 3000)

app.listen(app.get('port'), function(){ console.log('Server started on http://localhost:' + app.get('port'));});

4.0.1 app.js

// dependencies

var cookieSession = require('cookie-session')var express = require('express');var path = require('path');var favicon = require('serve-favicon');var cookieParser = require('cookie-parser');var bodyParser = require('body-parser');var expressValidator = require('express-validator');var mongo = require('mongodb');var mongoose = require('mongoose');var multipart = require('connect-multiparty');var moment = require('moment');

//Connect to mongo db mongoose.Promise = global.Promise; mongoose.connect('mongodb://localhost/cvcreator');

var db = mongoose.connection;var passport = require('passport');var LocalStrategy = require('passport-local').Strategy;

// require routesvar router = require('./app/routes/index.js');var user = require('./app/routes/user.js');var skill = require('./app/routes/skill.js');var project = require('./app/routes/project.js');var toPDF = require('./app/download/toPDF.js');var validation = require('./app/validation.js');

// create instance of express

19

Page 20: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

var app = express();

//BodyParser middlewareapp.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: false }));app.use(cookieParser());

//Set Static Folderapp.use(favicon(path.join(__dirname, '/public/favicon.ico')));app.use(express.static(path.join(__dirname + '/public')));

//CookieSessionapp.use(cookieSession({ name: 'session', secret: 'keyboard cat'}));

//Passport initapp.use(passport.initialize());app.use(passport.session());

//Express Validatorapp.use(expressValidator(validation));

// routesapp.use('/', router);app.use('/user', user);app.use('/skill', skill);app.use('/project', project);app.use('/download', toPDF);

app.use(function (err, req, res, next) { res.status(500).json(err);});

module.exports = app;4.1.0 index.html

<!doctype html><html lang="en" ng-app="cvCreater"><head>    <meta charset="UTF-8">

    <title>CVCreator</title>

20

Page 21: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

    <!-- CSS -->    <link rel="stylesheet" href="/libs/bootstrap/dist/css/bootstrap.min.css">    <link rel="stylesheet" href="/libs/components-font-awesome/css/font-awesome.css">    <link rel="stylesheet" href="/libs/bootstrap-social/bootstrap-social.css">    <link rel="stylesheet" href="/css/style.css">        <!-- JS -->    <script src="/libs/angular/angular.min.js" type="text/javascript"></script>    <script src="/libs/angular-route/angular-route.min.js" type="text/javascript"></script>    <script src="/libs/angular-bootstrap/ui-bootstrap-tpls.min.js" type="text/javascript"></script>    <script src="/libs/ng-file-upload/ng-file-upload.min.js" type="text/javascript"></script>

    <!-- ANGULAR APP -->    <script src="/app.js" type="text/javascript"></script>    <script src="/appRoutes.js" type="text/javascript"></script>

    <!-- ANGULAR CONTROLLERS -->    <script src="/pages/home/HomeCtrl.js" type="text/javascript"></script>    <script src="/pages/home/info/InfoCtrl.js" type="text/javascript"></script>    <script src="/pages/home/info/InfoDirectiva.js" type="text/javascript"></script>    <script src="/pages/home/skill/SkillCtrl.js" type="text/javascript"></script>    <script src="/pages/home/skill/SkillDirectiva.js" type="text/javascript"></script>    <script src="/pages/home/project/ProjectCtrl.js" type="text/javascript"></script>    <script src="/pages/home/project/ProjectDirectiva.js" type="text/javascript"></script>    <script src="/pages/search/SearchCtrl.js" type="text/javascript"></script>    <script src="/pages/login/LoginCtrl.js" type="text/javascript"></script>    <script src="/pages/register/RegisterCtrl.js" type="text/javascript"></script>

21

Page 22: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

    <script src="/pages/error/ErrorCtrl.js" type="text/javascript"></script>    <script src="/pages/error/errorDirectives.js" type="text/javascript"></script>    <script src="/pages/navigation/NavCtrl.js" type="text/javascript"></script>    <script src="/pages/search/profile/ProfileCtrl.js" type="text/javascript"></script>

    <!-- ANGULAR CUSTOM SERVICE -->    <script src="/services/AuthService.js" type="text/javascript"></script>    <script src="/services/ErrorService.js" type="text/javascript"></script>    <script src="/services/UserService.js" type="text/javascript"></script>    <script src="/services/ValidationService.js" type="text/javascript"></script>    <script src="/services/SkillService.js" type="text/javascript"></script>    <script src="/services/ProjectService.js" type="text/javascript"></script>    <script src="/services/SearchService.js" type="text/javascript"></script>

</head><body>    <div class="container min-height">        <!-- NAVIGATION -->        <nav class="navbar navbar-inverse" ng-controller="NavigationController as navCtrl">            <div class="container-fluid">                <div class="navbar-header">                <span class="navbar-brand logo"><a href="#!/">CVCreator</a></span>            </div>                <ul class="nav navbar-nav" ng-if="!navCtrl.isLoggedIn()">                    <li><a href="#!/register">Register</a></li>                                 <li><a href="#!/login">Login</a></li>                 </ul>                <ul class="nav navbar-nav" ng-if="navCtrl.isLoggedIn()">                    <li><a href="#!/">Home</a></li>                    <li><a href="#!/search">Search People</a></li>

22

Page 23: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

                    <li><a href="/download/pdf" target="_blanko" >Download My CV</a></li>                 </ul>                <ul class="nav navbar-nav pull-right" ng-if="navCtrl.isLoggedIn()">                    <li><a href="" ng-click="navCtrl.logout()">Logout</a></li>                 </ul>            </div>        </nav>

        <!-- ERROR HANDELER -->        <div class="row errorContainer" ng-controller="ErrorController as errorCtrl" style="">

            <div class="alert alert-danger" style="margin-bottom: 0px" role="alert" ng-if="errorCtrl.errors.length > 0">                <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>                <span class="sr-only">Error:</span>                <strong> <span>{{errorCtrl.errors.length}}</span> ERRORS </strong>                <span class="glyphicon glyphicon-remove" ng-click="errorCtrl.removeAllErrors()" aria-hidden="true"></span>                <span class="glyphicon " ng-class="!errorCtrl.isAllErrorsShow ? 'glyphicon-menu-down' : 'glyphicon-menu-up'"                    style="float: right;" ng-click="!errorCtrl.isAllErrorsShow ? errorCtrl.showAllErrors() : errorCtrl.hideAllErrors()"                     aria-hidden="true"></span>            </div>

            <error-directive ng-repeat="error in errorCtrl.errors"                 error-message="{{error.message}}" error-id="{{error.id}}"                remove-error="errorCtrl.removeError(id)" ng-if="errorCtrl.isAllErrorsShow"></error-directive>        </div>                    <!-- ANGULAR DYNAMIC CONTENT -->        <div ng-view class="col-lg-12"></div>

    </div>    <footer class="footer">        <div class="container footer-text">

23

Page 24: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

            <h4>Created by: Marko Pavlovic 57/13</h4>            <div class="footer-social">                <p>                    Contacts                </p>                <a href="https://twitter.com/pavlovic265?lang=en" target="_blanko" class="btn btn-social-icon btn-twitter">                    <span class="fa fa-twitter"></span>                </a>                <a href="https://www.facebook.com/pavlovic265" target="_blanko" class="btn btn-social-icon btn-facebook">                    <span class="fa fa-facebook"></span>                </a>                <a href="https://www.linkedin.com/in/marko-pavlovic-43b734b0/" target="_blanko" class="btn btn-social-icon btn-linkedin">                    <span class="fa fa-linkedin"></span>                </a>                <a href="https://plus.google.com/u/0/114907740744879703737" target="_blanko" class="btn btn-social-icon btn-google">                    <span class="fa fa-google"></span>                </a>                </a>                <a href="https://bitbucket.org/pavlovic265/" target="_blanko" class="btn btn-social-icon btn-bitbucket">                    <span class="fa fa-bitbucket"></span>                </a>                <a href="https://github.com/pavlovic265" target="_blanko" class="btn btn-social-icon btn-github">                    <span class="fa fa-github"></span>                </a>            </div>

        </div> </footer>

</body></html>

4.1.1 app.js

const cvCreaterApp = angular.module('cvCreater', ['ngRoute',

24

Page 25: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

'ui.bootstrap', 'appRoutes', 'ngFileUpload', 'HomeCtrl', 'LoginCtrl', 'RegisterCtrl', 'ErrorCtrl', 'NavCtrl', 'ProfileCtrl', 'SearchCtrl'] );

4.1.2. appRoutes.js

angular.module('appRoutes', [])

.config(['$routeProvider', function($routeProvider) {

    $routeProvider

        .when('/login', {            templateUrl: '/pages/login/login.html',            controller: 'LoginController',            access: {restricted: false}        })

        .when('/register', {            templateUrl: '/pages/register/register.html',            controller: 'RegisterController',            access: {restricted: false}        })

        .when('/search', {            templateUrl: '/pages/search/search.html',            controller: 'SearchController',            access: {restricted: true}        })

        .when('/profile/:username', {            templateUrl: '/pages/search/profile/profile.html',            controller: 'ProfileController',            resolve: function(username) { return { username: usernames }; },            access: {restricted: true}

25

Page 26: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

        })

        .when('/', {            templateUrl: '/pages/home/home.html',            controller: 'HomeController',            access: {restricted: true}        })

        .otherwise({            redirectTo: '/login',            access: {restricted: false}        });}]).run(function ($rootScope, $location, $route, AuthService) {    if(!AuthService.isLoggedIn()) {        AuthService.checkStatus();    }    $rootScope.$on('$routeChangeStart', function (event, next, current) {              if (next.access && next.access.restricted && !AuthService.isLoggedIn()){            $location.path('/login');            $route.reload();        }    });});

4.2.0 AuthService.js

cvCreaterApp.service('AuthService', ['$http', '$location', 'ErrorService', function($http, $location, ErrorService){ var user = null, returnObj;

returnObj= { isLoggedIn: function() { return user ? true : false; },

getUser: function() { return user ? user : false; },

26

Page 27: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

login: function(username, password, errorCallback) { return $http.post('/user/login', {username: username, password: password}) .then(function(response){ user = response.data.user; $location.path('/'); }) .catch(function(reason) { ErrorService.addError("Error logging in!"); errorCallback(); }); },

logout: function() { $http.get('/user/logout') .then(function(reponse){ user = false; $location.path('/login'); }) .catch(function(reason) { ErrorService.addError("Error loging out!"); }); },

register: function(user) { return $http.post('/user/register', user) .then(function (response) { $location.path('/login'); }) .catch(function (reason) { ErrorService.addErrors(reason.data.errors); });; },

checkStatus: function() { return $http.get('/user') .then(function(response){ user = response.data.user; $location.path('/'); }); },

setImage: function(image){

27

Page 28: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

user.image = image; },

setInfo: function(newUserData){ user.firstname = newUserData.firstname; user.lastname = newUserData.lastname; user.birth = newUserData.birth; user.email = newUserData.email; if(!angular.isUndefined(newUserData.contact)) { user.contact = newUserData.contact; } }, setSkills: function(skills) { user.skills = skills; }, setProjects: function(projects) { user.projects = projects; }

} return returnObj; }]);

4.2.1 ErrorService.js

cvCreaterApp.service('ErrorService', ['$filter', function($filter){

var errors = [], id= 1;

return { getErrors: function(){ return errors; },

addError: function(error) { errors.push({id: id++, message: error}); },

28

Page 29: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

addErrors: function(errorsData) { errorsData.forEach(function(error){ errors.push({id: id++, message: error.msg}); }); }, //Returns array of left errors removeError: function(id) { errors = $filter('filter')(errors, {id: '!'+id}); return errors; }, //return empty array removeAllErrors: function() { errors.length = 0 return errors; },

getNumberOfErrors: function() { return errors.length; } };}]);4.2.2 ProjectService.js

cvCreaterApp.service('ProjectService', ['$http', '$filter', 'ErrorService', 'ValidationService', function($http, $filter, ErrorService, ValidationService){ var returnObj = {}, messages = ValidationService.messages; returnObj = { hasError: function(newProject){ var hasError = false;                        angular.forEach(newProject, function(value, key){                var keyHelp = '';                if(angular.isUndefined(value)) {                    hasError = true;                }else if('' === value.trim()) {                    hasError = true;                    keyHelp = 'Empty';                }                if(hasError) {                    ErrorService.addError(messages[key.concat(keyHelp)]);                                   }

29

Page 30: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

            });

return hasError; }, add: function(project, callbackSuccess, callbackError){ $http.post('/project', { title: project.title, link: project.link, description: project.description }) .then(function(response){ project._id = response.data.project._id; callbackSuccess(project) }) .catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); }, update: function(project, callbackSuccess, callbackError){ $http.put('/project', project) .then(function(response){ callbackSuccess(); }) .catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); }, delete: function(idProject, callbackSuccess, callbackError){ $http.delete('/project/'+idProject) .then(function(response){ callbackSuccess(); }) .catch(function(reason){ ErrorService.addError(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); }

30

Page 31: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

}; return returnObj;}])4.2.3 SearchService.js

cvCreaterApp.service('SearchService', [function(){

var returnObj = { username: '', currenPage: 1, setUsername: function(username) { this.username = username; return this; }, setCurrentPage: function(currenPage) { this.currenPage = currenPage; return this; }, getUsername: function(username) { return this.username; }, getCurrentPage: function(currenPage) { return this.currenPage; }, isEmpty: function() { return this.username === '' && this.currenPage === 1; }, reset: function() { this.username = ''; this.currenPage = 1; } }; return returnObj;}]);

4.2.4 SkillService.js

cvCreaterApp.service('SkillService', ['$http', '$filter', 'ErrorService', function($http, $filter, ErrorService){ var returnObj = {}, leftSkills = [], addedSkills = [];

31

Page 32: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

returnObj = { allSkills: function(callbackSuccess, callbackError) { if(leftSkills.length !== 0) { callbackSuccess(leftSkills) } $http.get('/skill') .then(function(response){ callbackSuccess(response.data.skills) }) .catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); }, getLeftSkills: function(){ return leftSkills; }, resetLeftSkills: function(){ if(addedSkills.length > 0) { angular.forEach(addedSkills, function(skill){ leftSkills.push(skill); }); } }, getSkill: function(skillName, userSkill){ var skill = $filter('filter')(userSkill || leftSkills, {name: skillName}, true); if(skill.length === 1) { return skill[0]; } return null; }, getUserSkill: function(skillName, userSkill) { return this.getSkill(skillName.name, userSkill); }, setNewSkills: function(newSkills) { leftSkills = newSkills; }, addSkill: function(skill){ leftSkills.push(skill); return this; }, addUserSkill: function(newSkill, userSkills){

32

Page 33: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

userSkills.push(newSkill); return this; }, removeSkill: function(newSkill) { var index = null; for(var i in leftSkills) { if(leftSkills[i]._id === newSkill._id) { index = i; break; } } if(null !== index) { addedSkills.push(newSkill); leftSkills.splice(index, 1); } return this; }, removeUserSkill: function(skill, userSkills) { var index = 0; for(var i in userSkills) { if(userSkills[i].name === skill.name) { index = i; break; } } addedSkills = $filter('filter')(addedSkills, {_id: '!'+skill._id}, true); userSkills.splice(index, 1); return this; }, saveSkills: function(userSkills, callbackSuccess, callbackError) { $http.put('/skill', userSkills) .then(function(response){ callbackSuccess(); }) .catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); } }; return returnObj;

33

Page 34: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

}])

4.2.5 UserService.js

cvCreaterApp.service('UserService', ['$http', '$filter', '$q', 'Upload', 'ErrorService', function($http, $filter, $q, Upload, ErrorService){ var returnObj = {}, canceler = $q.defer(), resolved = false, cancelHttp = function() { canceler.resolve("http call aborted"); };

returnObj = { uploadImage: function(newImage, newImageName, callbackSuccess, callbackError){ Upload.upload({ url: '/user/upload', data: {newImage: newImage, fileName: newImageName}, method: 'PUT' }) .then(function(response){ callbackSuccess(response); }) .catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); },

update: function(user, callbackSuccess, callbackError) { $http.put('/user', user) .then(function(response){ callbackSuccess(); }).catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } });

34

Page 35: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

},

isSame: function(oldData, newData){ return angular.equals(oldData, newData); },

checkFields: function(user, oldUser, value, key, messsages) { if(key === 'birth'){ value = $filter('date')(value, user.birthFormat.format); } if(messsages[key] && !(new RegExp('^' + user.regex[key] + '$')).test(value)) { user[key] = (key !== 'fullname') ? oldUser[key] : oldUser.firstname + ' ' + oldUser.lastname; ErrorService.addError(messsages[key]); return true; } return false; }, download: function(){ $http.get('/download/pdf') .then(function(response){ callbackSuccess(); }).catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); },

users: function(query, callbackSuccess, callbackError) { $http.get('/users/'+query) .then(function(response){ callbackSuccess(response.data); }).catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); },

find: function(query, callbackSuccess, callbackError) {

35

Page 36: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

if (resolved) { cancelHttp(); } canceler = $q.defer(); resolved = true;

$http({ method: "GET", url: "/users"+query, timeout: canceler.promise }).then(function(response) { resolved = false; callbackSuccess(response.data); }); },

profile: function(username, callbackSuccess, callbackError) { $http({ method: "GET", url: "/user/"+username }).then(function(response) { callbackSuccess(response.data); }).catch(function(reason){ ErrorService.addErrors(reason.data); if(!angular.isUndefined(callbackError)) { callbackError(); } }); } }; return returnObj;}]);

4.2.6 ValidationService.js

cvCreaterApp.service('ValidationService', [ function(){ var returnObj = { birthFormat: { format: 'dd/MM/yyyy', date: null, opened: false, open: function(){ this.opened = true;

36

Page 37: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

} }, regex: { fullname: '[A-Z][a-z]{1,}\\s[A-Z][a-z]{1,}', firstLastName: '[A-Z][a-z]{1,}', birth: '[0-9]{2}/[0-9]{2}/[0-9]{4}', username: '[\\d\\w\\-\\_]{3,}', password: '[a-zA-Z0-9\_\.\-]{8,}', email: '[\\da-z\_\.]{3,}\@[a-z]{3,10}\.[a-z]{2,4}(\.[a-z]{2,4})?', contact: '([\\d]{3}\/[\\d]{7})?', title: '[A-Za-z0-9\\s]{2,}', link: '((http(s)?\:\/\/)|([w]{3}\.))[A-Za-z]{1,}\.[a-z]{2,3}(\.[a-z]{2,3})?([\/a-zA-Z0-9\.\&\?\?\=\+\-]*)?', description: '[A-Za-z\\s0-9\!\?\-\_\;\'\:\,\\\/\.\=\+\*\n]*' }, messages: { fullname: 'Fullname is in wrong format!', email: 'Email wrong format!', birth: 'Birth wrong format!', contact: 'Contact wrong format!', skill: 'Skill is wrong format!', skillEmpty: 'Skill field is empty format!', title: 'Project title wrong format!', titleEmpty: 'Project title is empty!', description: 'Project description wrong format!', descriptionEmpty: 'Project description is empty!', link: 'Project URL wrong format!', linkEmpty: 'Project link is empty!' } }; return returnObj;}]);

4.3.0.1 ErrorCtrl.js

angular.module('ErrorCtrl', []).controller('ErrorController', ['$scope', 'ErrorService', function($scope, ErrorService){ $scope.errorCtrl = { errors: ErrorService.getErrors(),

37

Page 38: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

isErrorShow: ErrorService.getNumberOfErrors() > 0 ? true : false, isAllErrorsShow: false,

addError: function(id) { },

removeError: function(id) { this.errors = ErrorService.removeError(id); if(ErrorService.getNumberOfErrors() === 0) { this.isErrorShow = false; this.isAllErrorsShow = false; } },

removeAllErrors: function() { this.errors = ErrorService.removeAllErrors(); this.isErrorShow = false; this.isAllErrorsShow = false; },

showAllErrors: function() { this.isAllErrorsShow = true; },

hideAllErrors: function() { this.isAllErrorsShow = false; } }}]);

4.3.0.2 errorDirectives.js

cvCreaterApp.directive('errorDirective', function(){ return { restrict: 'E', templateUrl: '/pages/error/view/errorTemplate.html', replace: true, scope: { errorMessage: '@', errorId: '@',

38

Page 39: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

removeError: '&' } }});

4.3.0.3 errorTemplate.html

<div class="alert alert-danger" style="margin-bottom: 0px" role="alert"> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <span class="sr-only">Error:</span> <span ng-bind="::errorMessage"></span> <span class="glyphicon glyphicon-remove" style="float: right;" ng-click="removeError({id : errorId})" aria-hidden="true"></span></div>

4.3.1.0 HomeCtrl.js

var HomeController = angular.module('HomeCtrl', ['SkillCtrl', 'InfoCtrl', 'ProjectCtrl'])

.controller('HomeController', ['$scope', 'AuthService',function($scope, AuthService){    if(!AuthService.isLoggedIn()) {        AuthService.checkStatus();    }

    $scope.$on('hasError', function(event, section, action){        $scope.homeCtrl.section[section][action] = true;    });

    var user = AuthService.getUser();

    $scope.homeCtrl = {        userInfo: {                birth: user.birth,                contact: user.contact,                email: user.email,                firstname: user.firstname,                lastname: user.lastname,                fullname: user.fullname,                gender: user.gender,

39

Page 40: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

                image: user.image        },        userProjects: user.projects,        userSkills: user.skills,        section: {                info: {                    edit: false                },                skill: {                    edit: false                },                project: {                        add: false,                        edit: false,                }        },        addNew: function(action, section, eventName) {            this.section[section][action] = true;            $scope.$broadcast(eventName);        },        add: function(action, section, eventName) {            this.section[section][action] = true;            $scope.$broadcast(eventName);        },        edit: function(action, section, eventName) {            this.section[section][action] = true;                       $scope.$broadcast(eventName);        },        Cancel: function(action, section, eventName) {            this.section[section][action] = false;            $scope.$broadcast(eventName);        },        save: function(action, section, eventName) {            this.section[section][action] = false;            $scope.$broadcast(eventName);        }    }}])

4.3.1.1 home.html

40

Page 41: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<!--INFO--><div class="col-lg-12 row top-buffer"> <h4><strong>Info</strong></h4></div><div class="col-lg-12 row form-group"> <button type="button" class="btn btn-sm btn-info pull-right col-lg-1" ng-click="homeCtrl.edit('edit', 'info', 'infoEdit')" ng-show="!homeCtrl.section.info.edit">Edit</button> <button type="button" class="btn btn-sm btn-danger pull-right col-lg-1 btn-space" ng-click="homeCtrl.Cancel('edit', 'info', 'infoCancel')" ng-show="homeCtrl.section.info.edit">Cancel</button> <button type="button" class="btn btn-sm btn-success pull-right col-lg-1" ng-click="homeCtrl.save('edit', 'info', 'infoSave')" ng-show="homeCtrl.section.info.edit">Save</button></div><info-data-directive class="section-split" user="homeCtrl.userInfo"></info-data-directive>

<!--SKILLS--><div class="col-lg-12 row top-buffer"> <h4><strong>Skills</strong></h4></div><div class="col-lg-12 row form-group"> <button type="button" class="btn btn-sm btn-info pull-right col-lg-1" ng-click="homeCtrl.edit('edit', 'skill', 'skillEdit')" ng-show="!homeCtrl.section.skill.edit">Edit</button> <button type="button" class="btn btn-sm btn-danger pull-right col-lg-1 btn-space" ng-click="homeCtrl.Cancel('edit', 'skill', 'skillCancel')" ng-show="homeCtrl.section.skill.edit">Cancel</button> <button type="button" class="btn btn-sm btn-success pull-right col-lg-1" ng-click="homeCtrl.save('edit', 'skill', 'skillSave')" ng-show="homeCtrl.section.skill.edit">Save</button></div><skill-data-directive class="section-split" user-skills="homeCtrl.userSkills"></skill-data-directive>

<!--PROJECTS--><div class="col-lg-12 col-md-12 col-xs-12 row top-buffer"> <h4><strong>Projects</strong></h4></div>

41

Page 42: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<div class="col-lg-12 col-md-12 col-xs-12 row form-group"> <button type="button" class="btn btn-sm btn-info pull-right col-md-2" ng-click="homeCtrl.addNew('add', 'project', 'projectAddNew')" ng-show="!homeCtrl.section.project.add">Add New Project</button> <button type="button" class="btn btn-sm btn-danger pull-right col-md-1 btn-space" ng-click="homeCtrl.Cancel('add', 'project', 'projectAddCancel')" ng-show="homeCtrl.section.project.add">Cancel</button> <button type="button" class="btn btn-sm btn-success pull-right col-md-1" ng-click="homeCtrl.save('add', 'project', 'projectAdd')" ng-show="homeCtrl.section.project.add">Add</button></div>

<projects-directive user-projects="homeCtrl.userProjects"></projects-directive>

4.3.1.2 InfoCtrl.js

var InfoController = angular.module('InfoCtrl',[]).controller('InfoController', ['$scope', '$rootScope', 'ValidationService', 'UserService', 'ErrorService', 'AuthService',function($scope, $rootScope, ValidationService, UserService, ErrorService, AuthService){

    var user = $scope.user;

    $scope.$on('infoSave', function(){        $scope.infoCtrl.save();     });    $scope.$on('infoEdit', function(){        $scope.infoCtrl.edit();     });    $scope.$on('infoCancel', function(){        $scope.infoCtrl.Cancel();       });

    user.birthFormat = ValidationService.birthFormat;    user.regex = ValidationService.regex;

    user.birth = new Date(user.birth);

42

Page 43: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

    user.selectImage = function(file, errFiles) {        user.newImage = file;    }

    $scope.infoCtrl = {        user: user,        isEdit: false,        oldUser: {},        edit: function(){            this.isEdit = true;            this.oldUser = angular.extend({}, {                firstname: this.user.firstname,                lastname: this.user.lastname,                birth: this.user.birth,                email: this.user.email,                contact: this.user.contact            });        },        save: function(){            var hasError = false,                isSame = false,                messsages = ValidationService.messages,                submitUser,                _self = this;

            angular.forEach(this.user, function(value, key){                if(UserService.checkFields(this.user, this.oldUser, value, key, messsages)) {                    hasError = true;                }            }, this);

            if(hasError) {                $rootScope.$broadcast('hasError', 'info', 'edit');                return;            }

            this.user.firstname = this.user.fullname.split(' ')[0];            this.user.lastname = this.user.fullname.split(' ')[1];            submitUser = angular.copy({                            firstname: this.user.firstname,                            lastname: this.user.lastname,                            birth: this.user.birth,                            email: this.user.email

43

Page 44: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

                        });

            if(this.user.contact !== '') {                submitUser.contact = this.user.contact;            }

            isSame = UserService.isSame(this.oldUser, submitUser);

            if(this.user.newImage && '' !== this.user.newImage.name) {                UserService.uploadImage(this.user.newImage, this.user.newImage.name,                    function(response){                        _self.user.image = response.data.image;                        AuthService.setImage( response.data.image);                        delete _self.user.newImage;                    },                    function(){                        $rootScope.$broadcast('hasError', 'info', 'edit');                                          });                                }

            if(!isSame) {                UserService.update(submitUser,                    function(){                        _self.isEdit = false;                        AuthService.setInfo(submitUser);                    },                    function(){                        $rootScope.$broadcast('hasError', 'info', 'edit');                                  });            } else {                this.isEdit = false;            }        },        Cancel: function(){            this.isEdit = false;            this.user.firstname = this.oldUser.firstname;            this.user.lastname = this.oldUser.lastname;            this.user.birth = this.oldUser.birth;            this.user.email = this.oldUser.email;            this.user.contact = this.oldUser.contact;            this.user.newImage = '';

44

Page 45: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

        }    }}])4.3.1.3 InfoDirectiva.js

InfoController.directive('infoDataDirective', function(){ return { restrict: 'E', templateUrl: '/pages/home/info/view/infoData.html', replace: true, scope: { user: '=' }, controller: 'InfoController' }})

.directive('infoDataDirectiveShow', function(){ return { restrict: 'E', templateUrl: '/pages/home/info/view/infoDataShow.html', replace: true, scope: { user: '=' } }})

.directive('infoDataDirectiveEdit', function(){ return { restrict: 'E', templateUrl: '/pages/home/info/view/infoDataEdit.html', replace: true, scope: { user: '=' } }});

4.3.1.4 infoData.html

<div class="row">

45

Page 46: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<info-data-directive-show user="infoCtrl.user" ng-hide="infoCtrl.isEdit"></info-data-directive-show> <info-data-directive-edit user="infoCtrl.user" ng-show="infoCtrl.isEdit"></info-data-directive-edit></div>

4.3.1.5 infoDataEdit.html

<div class="row col-xs-offset-1"> <div class="col-lg-3 col-md-3 col-xs-3 form-group"> <img ng-src="{{user.image}}" width="150px" height="200px" ng-model="user.image"/> <div class="col-lg-12 col-md-12 col-xs-12 form-group"> <button type="file" ngf-select="user.selectImage($file, $invalidFiles)" class="btn btn-primary" ngf-accept="'image/*'">Change Picture</button> <div> {{user.newImage ? user.newImage.name : ''}} </div> </div> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group" > <div class="col-lg-4 col-md-4 col-xs-4"> <label>Full name: </label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> <input type="text" class="form-control" id="fullname" name="fullname" ng-init="user.fullname = user.firstname + ' ' + user.lastname" ng-model="user.fullname" ng-pattern="user.regex.fullname"/> </div> </div> <div class="col-lg-6 col-md-6 col-xs-6"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Date of birth</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> <div class="input-group form-group"> <input type="text" class="form-control" name="birth" uib-datepicker-popup="{{user.birthFormat.format}}" is-open="user.birthFormat.opened" placeholder="{{user.birthFormat.format}}" value="{{user.birthFormat.date}}"

46

Page 47: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

ng-model="user.birth" readonly /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="user.birthFormat.open()"> <i class="glyphicon glyphicon-calendar"></i> </button> </span> </div> </div> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Email</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> <input type="text" class="form-control" id="email" ng-model="user.email" ng-pattern="user.regex.email" /> </div> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Contact</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> <input type="text" class="form-control" id="contact" ng-model="user.contact" ng-pattern="user.regex.contact" /> </div> </div></div>

4.3.1.6 infoDataShow.html

<div class="row col-xs-offset-1"> <div class="col-lg-3 col-md-3 col-xs-3"> <img ng-src="{{user.image}}" width="150px" height="200px"/> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Full name</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> {{(user.firstname + ' ' + user.lastname)}}

47

Page 48: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

</div> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Date of birth</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> {{user.birth | date : 'dd/MM/yyyy'}} </div> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Email</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> {{user.email}} </div> </div> <div class="col-lg-6 col-md-6 col-xs-6 form-group"> <div class="col-lg-4 col-md-4 col-xs-4"> <label>Contact</label> </div> <div class="col-lg-6 col-md-6 col-xs-6"> {{user.contact}} </div> </div></div>

4.3.1.7 ProjectCtrl.js

var ProjectController = angular.module('ProjectCtrl',[])

.controller('ProjectController', ['$scope', '$rootScope', 'ProjectService', 'ValidationService', 'AuthService',function($scope, $rootScope, ProjectService, ValidationService, AuthService){    $scope.$on('projectAddNew', function(){        $scope.projectCtrl.isAdd = true;    });    $scope.$on('projectAdd', function(){        $scope.projectCtrl.isAdd = false;        $scope.projectCtrl.add();    });    $scope.$on('projectAddCancel', function(){

48

Page 49: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

        $scope.projectCtrl.isAdd = false;    });

    var extendProject = {                    isEdit: false,                    oldProject: undefined,                    Cancel: function(){                        this.isEdit = false;                        if(angular.isUndefined(this.oldProject)) {                            return;                        }

                        this.title = this.oldProject.title;                        this.link = this.oldProject.link;                        this.description = this.oldProject.description;                    },                    edit: function(project){                        this.isEdit = !this.isEdit;                        this.oldProject = angular.copy({                                                    title: project.title,                                                    link: project.link,                                                    description: project.description                                                });                    },                    save: function(){                        this.isEdit = false;                        var _self = this,                            newProject = {title: this.title, link: this.link, description: this.description},                            projects = $scope.projectCtrl.projects;

                        if(angular.equals(this.oldProject, {title: this.title, link: this.link, description: this.description})) {                            return;                        }                        if(ProjectService.hasError(newProject)){                            return;                        }

                        ProjectService.update({                                _id: this._id,                                title: this.title,                                link: this.link,                                description: this.description

49

Page 50: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

                            },                            function(){                                AuthService.setProjects(projects);                            },                            function(){                                $rootScope.$broadcast('hasError', 'project', 'edit');                            });                    },                    delete: function(_id) {                        this.isEdit = false;                        var projects = $scope.projectCtrl.projects;                        for(var i = 0; i < projects.length; i++) {                            if(projects[i]._id == _id) {                                projects.splice(i, 1);                                break;                            }                           }

                        ProjectService.delete(_id,                            function(){                                AuthService.setProjects(projects);                            },                            function(){                                $rootScope.$broadcast('hasError', 'project', 'edit');                            });                    }                };    var extendedProjects = angular.copy($scope.userProjects)

    angular.forEach(extendedProjects, function(data){        angular.extend(data, extendProject);    });

    $scope.projectCtrl = {        newProject: {            title: '',            link: '',            description: ''        },        projects: extendedProjects,        isAdd: false,        regex: ValidationService.regex,        reset: function(){

50

Page 51: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

            this.newProject = {                        title: '',                        link: '',                        description: ''                    }        },        add: function() {            var _self = this;            if(ProjectService.hasError(this.newProject)) {                return;            }            angular.extend(this.newProject, extendProject);            ProjectService.add(this.newProject,                function(project){                    _self.projects.push(project);                    AuthService.setProjects(_self.projects)                },                function(){                    $rootScope.$broadcast('hasError', 'project', 'add');                });            this.reset();        }    }}]);

4.3.1.8 ProjectDirectiva.js

ProjectController

.directive('projectsDirective', function(){ return { restrict: 'E', templateUrl: '/pages/home/project/view/projectsData.html', replace: true, scope: { userProjects: '=userProjects' }, controller: 'ProjectController' }})

.directive('projectDirectiveAdd', function(){ return { restrict: 'E',

51

Page 52: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

templateUrl: '/pages/home/project/view/projectAdd.html', replace: true, scope: { project: '=', regex: '=' } }})

.directive('projectDataDirective', function(){ return { restrict: 'E', templateUrl: '/pages/home/project/view/projectData.html', replace: true, scope: { project: "=" } }})

.directive('projectDataDirectiveEdit', function(){ return { restrict: 'E', templateUrl: '/pages/home/project/view/projectEdit.html', replace: true, scope: { project: "=" } }})

.directive('projectDataDirectiveShow', function(){ return { restrict: 'E', templateUrl: '/pages/home/project/view/projectShow.html', replace: true, scope: { project: "=" } }});

52

Page 53: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

4.3.1.9 projectAdd.html

<div class="row"> <div class="col-lg-6 col-md-6 col-xs-6 top-buffer"> <label for="title">Title</label> <input type="text" id="title" name="title" class="form-control" ng-model="project.title" ng-pattern="regex.title" /> </div> <div class="col-lg-6 col-md-6 col-xs-6 top-buffer"> <label for="link">Link</label> <input type="text" id="link" name="link" class="form-control" ng-model="project.link" ng-pattern="regex.link" > </div> <div class="col-lg-12 col-md-12 col-xs-12 top-buffer"> <label for="description">Dsecription</label> <textarea id="description" name="description" rows="7" class="form-control project" ng-model="project.description" ng-pattern="regex.description" ></textarea> </div></div>

4.3.1.10 projectData.html

<div class="row top-buffer project-split"> <project-data-directive-edit ng-show="project.isEdit" project="project"> </project-data-directive-edit> <project-data-directive-show ng-show="!project.isEdit" project="project"> </project-data-directive-show> <div class="col-lg-6 col-md-6 col-xs-6 top-buffer pull-right form-group"> <button type="button" class="btn btn-sm btn-info pull-right col-lg-2" ng-click="project.edit(project)" ng-show="!project.isEdit">Edit</button> <button type="button" class="btn btn-sm btn-success pull-right col-lg-2 btn-space" ng-show="project.isEdit" ng-click="project.save()">Save</button> <button type="button" class="btn btn-sm btn-danger pull-right col-lg-2 btn-space" ng-show="project.isEdit" ng-click="project.Cancel()">Cancel</button>

53

Page 54: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<button type="button" class="btn btn-sm btn-warning pull-right col-lg-2" ng-show="project.isEdit" ng-click="project.delete(project._id)">Delete</button> </div></div>

4.3.1.11 projectEdit.html

<div class="col-lg-12 col-md-12 col-xs-12"> <div class="col-lg-6 col-md-6 col-xs-6 top-buffer"> <label for="title">Title</label> <input type="text" id="title" name="title" class="form-control" ng-model="project.title"> </div> <div class="col-lg-6 col-md-6 col-xs-6 top-buffer"> <label for="link">Link</label> <input type="text" id="link" name="link" class="form-control" ng-model="project.link"> </div> <div class="col-lg-12 col-md-12 col-xs-12 top-buffer"> <label for="descrption">Description</label> <textarea id="descrption" name="descrption" rows="7" class="form-control" ng-model="project.description"></textarea> </div></div>

4.3.1.12 projectsData.html

<div class="row col-lg-12 col-md-12 col-xs-12 top-buffer"> <project-directive-add class="col-lg-12 col-md-12 col-xs-12" ng-show="projectCtrl.isAdd" class="bottom-buffer" project="projectCtrl.newProject" regex="projectCtrl.regex"> </project-directive-add>

<project-data-directive class="col-lg-12 col-md-12 col-xs-12" ng-repeat="project in projectCtrl.projects" project="project"> </project-data-directive></div>

54

Page 55: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

4.3.1.13 projectShow.html

<div class="row"> <div class="col-lg-12 col-md-11 col-xs-12"><strong>Title:</strong> {{project.title}}</div> <div class="col-lg-12 col-md-12 col-xs-12 top-buffer"> <strong>Link:</strong> <a ng-href="{{project.link}}" target="_blank" ng-bind="project.link"></a> </div> <div class="col-lg-12 col-md-12 col-xs-12 top-buffer"> <strong>Description:</strong> </div> <div class="col-lg-12 col-md-12 col-xs-12" ng-bind="project.description"> </div></div>

4.3.1.14 SkillCtrl.js

var SkillController = angular.module('SkillCtrl', []).filter('newSkills', function(){    return function(array1, array2){            var newArray = [];            for(var i in array1) {                var exist = false;                for(var j in array2) {                    if(array1[i].name === array2[j].name) {                        exist = true;                        break;                    }                }                if(!exist) {                    newArray.push(array1[i]);                }            }            return newArray;    }}).controller('SkillController', ['$scope', '$rootScope', 'newSkillsFilter', 'SkillService', 'AuthService',function($scope, $rootScope, newSkillsFilter, SkillService, AuthService){    $scope.$on('skillSave', function(){        $scope.skillCtrl.save();

55

Page 56: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

    });

    $scope.$on('skillCancel', function(){        $scope.skillCtrl.Cancel();    });    $scope.$on('skillEdit', function(){        SkillService.allSkills(function(skills){                SkillService.setNewSkills(newSkillsFilter(skills, $scope.userSkills));                $scope.skillCtrl.edit();            },            function() {                $rootScope.$broadcast('hasError', 'skill', 'edit');            });    });

    $scope.skillCtrl = {        isTypeing: false,        isEdit: false,        newSkill: undefined,        userSkills: angular.copy($scope.userSkills),        reset: function(){                this.isTypeing = false;                this.newSkill = '';        },        typing: function() {            if(angular.isUndefined(this.newSkill) || !angular.isString(this.newSkill) || '' === this.newSkill.trim()) {                this.reset();                return;            }            this.isTypeing = true;        },        edit: function(){            this.isEdit = true;            this.leftSkills = SkillService.getLeftSkills();            this.userSkills = angular.copy($scope.userSkills);            this.reset();        },        add: function(skill) {            if(angular.isUndefined(this.newSkill) || !angular.isString(this.newSkill) || '' === this.newSkill.trim()) {                this.reset();                return;            }

56

Page 57: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

            if(!angular.isUndefined(skill) && angular.isObject(skill)) {                this.newSkill = skill;            } else {                this.newSkill = SkillService.getSkill(this.newSkill);            }            if(null === this.newSkill) {                this.reset();                return;            }            if(angular.isObject(this.newSkill)) {                SkillService.addUserSkill(this.newSkill, this.userSkills)                            .removeSkill(this.newSkill);            }                       this.reset();        },        remove: function(skill) {            skill = SkillService.getUserSkill(skill, this.userSkills);            SkillService.removeUserSkill(skill, this.userSkills)                        .addSkill(skill);        },        save: function() {            var _self = this;            SkillService.saveSkills(this.userSkills,            function(){                _self.isEdit = false;                $scope.userSkills = _self.userSkills;                AuthService.setSkills(_self.userSkills);            },            function(){                $rootScope.$broadcast('hasError', 'skill', 'edit');            });            this.reset();        },        Cancel: function(){            this.isEdit = false;            this.reset();            SkillService.resetLeftSkills();        }    }}]);4.3.1.15 SkillDirectiva.js

SkillController.directive('skillDataDirective', function(){ return {

57

Page 58: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

restrict: 'E', templateUrl: '/pages/home/skill/view/skillData.html', replace: true, scope: { userSkills: '=userSkills' },        controller: 'SkillController' }})

.directive('skillDataDirectiveShow', function(){ return { restrict: 'E', templateUrl: '/pages/home/skill/view/skillDataShow.html', replace: true, scope: { userSkills: '=userSkills' } }})

.directive('skillDataDirectiveEdit', function(){ return { restrict: 'E', templateUrl: '/pages/home/skill/view/skillDataEdit.html', replace: true, scope: { skillCtrl: '=skillCtrl' } }});

4.3.1.16 skillData.html

<div class="row"> <skill-data-directive-show user-skills="userSkills" ng-hide="skillCtrl.isEdit"></skill-data-directive-show> <skill-data-directive-edit skill-ctrl="skillCtrl" ng-show="skillCtrl.isEdit"></skill-data-directive-edit></div>

4.3.1.17 skillDataEdit.html

58

Page 59: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<div class="row top-buffer"> <div class="col-lg-6 col-md-6 col-xs-6"> <div class="input-group"> <input type="text" class="form-control" placeholder="Search for skill" ng-change="skillCtrl.typing()" ng-model="skillCtrl.newSkill"> <span class="input-group-btn"> <button class="btn btn-default" type="button" ng-click="skillCtrl.add()">Add</button> </span> </div> <div ng-if="skillCtrl.isTypeing"> <ul class="list-group skillSearch" > <li class="list-group-item item-skill" ng-repeat="(key, skill) in skillCtrl.leftSkills | filter:skillCtrl.newSkill as results" ng-click="skillCtrl.add(skill)" ng-bind="::skill.name"> </li> </ul> </div> </div> <div class="col-lg-12 col-md-12 col-xs-12 top-buffer"> <ul class="list-group"> <li class="list-group-item pull-left" ng-repeat="(key, skill) in skillCtrl.userSkills"> {{skill.name}} <span class="glyphicon glyphicon-remove js-remove-skill" aria-hidden="true" ng-click="skillCtrl.remove(skill)"></span> </li> </ul> </div></div>4.3.1.18 skillDataShow.html

<div class="col-lg-12 col-md-12 col-xs-12 top-buffer"> <ul class="list-group"> <li class="list-group-item pull-left" ng-repeat="(key, skill) in userSkills" ng-bind="::skill.name"> </li> </ul></div>

59

Page 60: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

4.3.2.0 login.html

<div class="row"> <div class="col-lg-3 col-md-3 col-xs-3 col-lg-offset-4 col-md-offset-4 col-xs-offset-4"> <form ng-submit="loginCtrl.login()" name="loginForm"> <div class="row"> <div class="col-lg-12 col-md-12 col-xs-12"> <label for="username">Username: </label> <input type="text" class="form-control" ng-model="loginCtrl.username" ng-change="loginCtrl.checkErrors()" name="username" ng-pattern="loginCtrl.regex.username" required /> </div> </div> <div class="row"> <div class="col-lg-12 col-md-12 col-xs-12"> <label for="password">Password: </label> <input type="password" class="form-control" ng-model="loginCtrl.password" ng-change="loginCtrl.checkErrors()" name="password" ng-pattern="loginCtrl.regex.password" required/> </div> </div> <div class="row top-buffer"> <div class="col-lg-12 col-md-12 col-xs-12"> <div class="alert alert-danger" ng-show="loginCtrl.error.loginError && loginCtrl.error.allErrors"> <div ng-repeat="field in loginForm"> <div ng-show="field.$error.required"><b>{{field.$name}} is required!</b></div> <div ng-show="field.$error.pattern"><b>{{field.$name}} is invalid!</b></div> </div> </div> <div class="alert alert-danger" ng-show="loginCtrl.error.baError && !(loginCtrl.error.loginError && loginCtrl.error.allErrors)"> <div><b>Username or password are not correct!</b></div> </div> </div> <div class="col-lg-6 col-md-6 col-xs-6">

60

Page 61: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<input type="submit" value="Login" ng-submit="loginCtrl.submitData()" class="btn btn-primary form-control" /> </div> <div class="col-lg-6 col-md-6 col-xs-6"> <input type="button" value="Reset" ng-click="loginCtrl.resetData()" class="btn btn-default form-control" /> </div> </div> <div class="row top-buffer"> <div class="col-lg-12 col-md-12 col-xs-12"> <input type="button" value="Register" ng-click="loginCtrl.registration()" class="btn btn-info form-control" /> </div> </div> </form> </div></div>

4.3.2.1 LoginCtrl.js

angular.module('LoginCtrl', [])

.controller('LoginController', ['$scope', '$location', 'ErrorService','UserService', 'AuthService', 'ValidationService',    function($scope, $location, ErrorService, UserService, AuthService, ValidationService){

        $scope.loginCtrl = {            username: '',            regex: ValidationService.regex,            password: '',            passwordMinLength: 8,            isDisabled: true,            error: {                loginError: false,                allErrors: false,                baError: false            },            change: false,

            //Reset form data

61

Page 62: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

            resetData: function(){                this.username = '';                this.password= '';                this.isDisabled = true;                this.error = false;            },

            //Submit username and password            login: function() {                if(this.checkErrors()) {                    this.error.loginError = true;                    return;                }                var _thisError = this.error;                _thisError.baError = false;                AuthService.login(this.username, this.password, function(){                    _thisError.baError = true;                });            },

            checkErrors: function() {                 this.error.allErrors = ($scope.loginForm.$invalid) ? true : false                return this.error.allErrors;            },            registration: function() {                $location.path('/register');            }        };    }   ]);

4.3.3.0 NavCtrl.js

angular.module('NavCtrl', [])

.controller('NavigationController', ['$scope', 'AuthService', 'UserService', function($scope, AuthService, UserService){ $scope.navCtrl = { isLoggedIn: AuthService.isLoggedIn,

62

Page 63: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

logout: function() {            AuthService.logout();        }, download: function(){ UserService.download(); } }}]);

4.3.4.0 register.html

<div class="col-md-4"> <h1>Register</h1> <form class="form" name="registerForm" ng-submit="regCtrl.register()"> <div class="form-group"> <label>First name</label> <input type="text" class="form-control" name="firstname" ng-model="regCtrl.firstname" required ng-pattern="regCtrl.regex.firstLastName" ng-change="regCtrl.checkErrors()"/> </div> <div class="form-group"> <label>Last Name</label> <input type="text" class="form-control" name="lastname" ng-model="regCtrl.lastname" required ng-pattern="regCtrl.regex.firstLastName" ng-change="regCtrl.checkErrors()"/> </div> <div class="form-group"> <label>Email</label> <input type="text" class="form-control" name="email" id="email" ng-model="regCtrl.email" required ng-pattern="regCtrl.regex.email" ng-change="regCtrl.checkErrors()"/> </div> <div class="form-group"> <label>Date of birth</label> <p class="input-group"> <input type="text" class="form-control" name="birthFormat" uib-datepicker-popup="{{regCtrl.birthFormat.format}}" datepicker-options="regCtrl.birthFormat.options" is-open="regCtrl.birthFormat.opened"

63

Page 64: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

ng-model="regCtrl.birthFormat.date" placeholder="{{regCtrl.birthFormat.format}}" readonly required ng-pattern="regCtrl.regex.birthFormat" ng-change="regCtrl.checkErrors()"/> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="regCtrl.birthFormat.open()"> <i class="glyphicon glyphicon-calendar"></i> </button> </span> </p> </div> <div class="form-group"> <label>Gender</label> <div class="form-group col-lg-12"> <label class="btn btn-default col-lg-5" name="gender" ng-model="regCtrl.gender" uib-btn-radio="'Male'">Male</label> <label class="btn btn-default col-lg-offset-2 col-lg-5" name="gender" ng-model="regCtrl.gender" uib-btn-radio="'Female'">Female</label> </div> </div> <div class="form-group"> <label>Username</label> <input type="text" class="form-control" name="username" ng-model="regCtrl.username" required ng-pattern="regCtrl.regex.username" ng-change="regCtrl.checkErrors()" /> </div> <div class="form-group"> <label>Password</label> <input type="password" class="form-control" name="password" ng-model="regCtrl.password" required ng-minlength="8" ng-pattern="regCtrl.regex.password"/> </div> <div class="form-group"> <label>Retype password</label> <input type="password" class="form-control" name="reTypePassword" ng-model="regCtrl.reTypePassword" required ng-minlength="8" password-check="{{regCtrl.password}}" ng-pattern="regCtrl.regex.password" /> </div> <div>

64

Page 65: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<button type="submit" class="btn btn-primary" ng-disabled="disabled">Register</button> <button type="reset" class="btn btn-default" ng-disabled="disabled">Reset</button> </div> </form></div><div class="col-md-3" ng-show="regCtrl.error.errorRegister && regCtrl.error.allErrors"> <h1>Errors</h1> <div class="alert alert-danger"> <div ng-repeat="field in registerForm"> <div ng-show="field.$error.required"><b>{{field.$name}} is required!</b></div> <div ng-show="field.$error.passwordCheck"><b>passwords do not match!</b></div> <div ng-show="field.$error.minlength"><b>{{field.$name}} need to be at least 8 characters!</b></div> <div ng-show="field.$error.pattern"><b>{{field.$name}} is invalid!</b></div> <div ng-show="field.$error.date"><b>{{field.$name}} is wrong format!</b></div> </div> </div></div>

4.3.4.1 RegisterCtrl.js

angular.module('passwordCheck', []).directive('passwordCheck', function(){ return { restrict: 'A', require: 'ngModel', link: function(scope, element, attrs, ngModel) { if(!ngModel) return; // do nothing if no ng-model

// watch own value and re-validate on change scope.$watch(attrs.ngModel, function() { validate(); });

// observe the other value and re-validate on change attrs.$observe('passwordCheck', function (val) { validate();

65

Page 66: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

});

var validate = function() { // values var val1 = ngModel.$viewValue; var val2 = attrs.passwordCheck;

// set validity ngModel.$setValidity('passwordCheck', ! val1 || ! val2 || val1 === val2); }; } }});

angular.module('RegisterCtrl', ['passwordCheck'])

.controller('RegisterController', ['$scope', '$location', 'AuthService', 'ErrorService', 'ValidationService', function ($scope, $location, AuthService, ErrorService, ValidationService) {

$scope.regCtrl = { firstname: '', lastname: '', email: '', birthFormat: ValidationService.birthFormat, gender: 'Male', password: '', reTypePassword: '', regex: ValidationService.regex, error: { errorRegister: false, allErrors: false },

register: function () { if(this.checkErrors()) { this.error.errorRegister = true; return; } // this.error.errorRegister = false; // call register from service AuthService.register({

66

Page 67: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

firstname: this.firstname, lastname: this.lastname, email: this.email, username: this.username, birth: this.birthFormat.date, gender: this.gender, password: this.password, reTypePassword: this.reTypePassword });

},

checkErrors: function() { this.error.allErrors = ($scope.registerForm.$invalid) ? true : false return this.error.allErrors; } }

}]);

4.3.5.0 search.html

<div class="row"> <div class="col-lg-12 col-md-12 col-xs-12"> <input type="text" class="form-control search-spacing" placeholder="Search for user by username..." ng-model="searchCtrl.user.username" ng-change="searchCtrl.searching()" /> <input type="text" class="form-control search-spacing" placeholder="Search for user by firstname..." ng-model="searchCtrl.user.firstname" ng-change="searchCtrl.searching()" /> <input type="text" class="form-control search-spacing" placeholder="Search for user by lastname..." ng-model="searchCtrl.user.lastname" ng-change="searchCtrl.searching()" /> </div> <div class="row col-lg-12 col-md-12 col-xs-12 search" ng-repeat="user in searchCtrl.users | limitTo: searchCtrl.limit"> <div class="col-lg-3 col-md-3 col-xs-3"> <img ng-src="{{user.image}}" width="50px" height="60px"> </div>

67

Page 68: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<div class="col-lg-6 col-md-6 col-xs-6 search-top text-left"> {{user.firstname + ' ' + user.lastname + ' ( '+user.username+' ) '}} </div> <div class="col-lg-3 col-md-3 col-xs-3 search-top"> <a ng-href='#!/profile/{{user.username}}' class="btn btn-warning" ng-click="searchCtrl.rememberState()">See profile</a> </div> </div> <div class="col-lg-12 col-md-12 col-xs-12"> <ul uib-pagination total-items="searchCtrl.totalItems" ng-model="searchCtrl.currentPage" ng-change="searchCtrl.pageChanged()"></ul> </div></div>

4.3.5.1 SearchCtrl.js

angular.module('SearchCtrl', [])

.controller('SearchController', ['$scope', '$location', 'UserService', 'SearchService', 'ValidationService',    function($scope, $location, UserService, SearchService, ValidationService){        var query = 'current/' + SearchService.getCurrentPage();        if(!SearchService.isEmpty()) {            query += (SearchService.getUsername() !== '' ? '/username/' + SearchService.getUsername() : '');        }        UserService.users(query, function(data){            $scope.searchCtrl.users = data.users;            $scope.searchCtrl.totalItems = data.total;            if(!SearchService.isEmpty()) {                $scope.searchCtrl.currentPage = SearchService.getCurrentPage();                 $scope.searchCtrl.user.username = SearchService.getUsername();              }        });

        $scope.searchCtrl = {

68

Page 69: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

            searchCtrl: false,            limit: 10,            totalItems: 0,           currentPage: 1,            users: [],            user: {                username: '',                firstname: '',                lastname: ''            },            pageChanged: function() {                var query = 'current/'+ this.currentPage + (this.user.username !== '' ? '/username/' + this.user.username : '');

                UserService.users(query, function(data){                    $scope.searchCtrl.users.length= 0;                    angular.forEach(data.users, function(user){                        this.users.push(user);                                              }, $scope.searchCtrl);                    $scope.searchCtrl.totalItems = data.total;                });            },            setPage: function(num) {                this.currentPage = num;            },            searching: function() {

                    if(this.user.username === '' && this.user.firstname === '' && this.user.lastname === '') {                        this.setPage(1);                        this.pageChanged();                        return ;                    }                    _self = this;                    var query = '';                    if(this.user.username !== '') {                        query += '/username/'+this.user.username                    }                    if(this.user.firstname !== '') {                        query += '/firstname/'+this.user.firstname                    }                    if(this.user.lastname !== '') {                        query += '/lastname/'+this.user.lastname                    }

69

Page 70: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

                    if(query === '') {                        return;                    }

                    UserService.find(query, function(data){                        _self.users.length = 0;                        angular.forEach(data.users, function(user){                            this.users.push(user);                                                  }, _self);                        _self.totalItems = data.total;                        _self.setPage(1);                    });            },            rememberState: function() {                SearchService.setUsername(this.user.username)                            .setCurrentPage(this.currentPage);            }        };}]);

4.3.5.2 profile.html

<div class="row section-split"> <div class="col-lg-12 col-md-12 col-xs-12 row top-buffer"> <h4><strong>Info</strong></h4> </div> <a ng-href="/download/pdf/{{profileCtrl.user.username}}" target="_blanko" class="btn btn-success pull-right" >Download {{profileCtrl.user.username}} CV</a> <info-data-directive-show user="profileCtrl.user"></info-data-directive-show></div><div class="row section-split"> <div class="col-lg-12 col-md-12 col-xs-12 row top-buffer"> <h4><strong>Skills</strong></h4> </div> <skill-data-directive-show user-skills="profileCtrl.user.skills"></skill-data-directive-show></div>

70

Page 71: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<div class="row"> <div class="col-lg-12 col-md-12 col-xs-12 row top-buffer"> <h4><strong>Projects</strong></h4> </div> <div class="project-split col-lg-12 col-md-12 col-xs-12 project-spacing row" ng-repeat="project in profileCtrl.user.projects"> <project-data-directive-show project="project"></project-data-directive-show> </div></div>4.3.5.3 ProfileCtrl.js

angular.module('ProfileCtrl', [])

.controller('ProfileController', ['$scope', '$routeParams', 'UserService',    function($scope, $routeParams, UserService){

        UserService.profile($routeParams.username, function(user){            $scope.profileCtrl = user;          });

        $scope.profileCtrl = {            user: {}        };}]);

4.3.6 style.css

* { padding: 0px; margin: 0px; }body { padding: 0px; margin: 0px; }.top-buffer { margin-top:20px; }.bottom-buffer { margin-bottom: 20px; }.errorContainer{ width: 320px; position: fixed; z-index: 2; right: 0; top: 0; margin-top: 52px; }.btn-space { margin-left: 5px; }.skillSearch { position: absolute; z-index: 1; width: 100%; max-height: 206px; overflow: hidden; overflow-y: scroll; }.item-skill:hover { background: cornflowerblue;}.js-remove-skill:hover { color: darkred; }.project-split, .section-split { border-bottom: 1px solid lightslategray; padding-bottom: 10px; }

71

Page 72: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

.footer > div {bottom: 0; margin-top: 30px; height: 150px; background-color: #222; border-color: #080808; border-radius: 4px; }.search { margin: 10px; }.search-top { margin-top: 10px; }.project-spacing { padding-top: 40px; padding-bottom: 40px; }.project-description { overflow-x: hidden; }.logo a { color: #fff; text-decoration: none; }.search-spacing { margin-bottom: 15px; }.footer-text { text-align: center; color: #fff; padding-top: 10px; }.footer-text h4 { display: inline; font-weight: bold; }.footer-text > div { font-weight: bold; }.min-height { min-height: 600px; }.footer-social { padding-top: 20px; }4.4.0 validation.js

var moment = require('moment');

module.exports = { customValidators: { regexFirstLastName: function(value){ return /^[A-Z][a-z]{1,}$/.test(value); }, regexFirstLastNameSearch: function(value){ return /^[A-Za-z]{1,}$/.test(value); }, regexUsername: function(value, type) { return /^[\w\d\-\_]{3,}$/.test(value); }, regexPassword: function(value) { return /^[a-zA-Z0-9\_\.\-]{8,}$/.test(value); }, regexTitle: function(value){ return /^[A-Za-z0-9\s]{2,}$/.test(value); }, regexLink: function(value) { return /^((http(s)?\:\/\/)|([w]{3}\.))[A-Za-z]{1,}\.[a-z]{2,3}(\.[a-z]{2,3})?([\/a-zA-Z0-9\.\&\?\?\=\+\-]*)?$/.test(value); }, regexDescription: function(value){ return /^[A-Za-z\s0-9\!\?\-\_\;\'\:\\\/\,\.\=\+\*\n]*$/.test(value); }, isFileType: function(value, type) { return ('.jpg' === type || '.jpeg' === type || '.png' === type); },

72

Page 73: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

isDate: function(value){ value = moment(value).format('MM/DD/YYYY'); return moment(value, 'MM/DD/YYYY').isValid(); }, isContact: function(value){ return /^[\d]{3}\/[\d]{7}$/.test(value); } }};4.4.1.0 classes/project.js

// var ProjectModel = require('../models/project');var UserModel = require('../models/user');var mongoose = require('mongoose');

var Project = { addProject: function(id, project, callback){ UserModel.findOneAndUpdate({_id: id}, {$push: {projects: project}}, { new: true }, callback); }, updateProject: function(id, project, callback){ UserModel.update({'_id': id, 'projects._id': project._id}, { '$set': { 'projects.$.title': project.title, 'projects.$.link': project.link, 'projects.$.description': project.description } }, callback); }, deleteProject: function(id, idProject, callback){ UserModel.update({_id: id, 'projects._id': idProject}, {'$pull': {projects: {_id: idProject}}}, callback); }}

module.exports = Project;

4.4.1.1 classes/skill.js

var bcrypt = require('bcryptjs');

73

Page 74: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

var SkillModel = require('../models/skill');

var Skill = { allSkills: function(callback) { SkillModel.find({}, callback); }};

module.exports = Skill;

4.4.1.2 classes/user.js

var bcrypt = require('bcryptjs');var UserModel = require('../models/user');var ObjectId = require('mongodb').ObjectID;var mongoose = require('mongoose');

var User = { createUser: function(newUser, callback){

bcrypt.genSalt(10, function(err, salt) { if(err){ throw err; } bcrypt.hash(newUser.password, salt, function(err, hash) { if(err) { throw err; } newUser.password = hash; newUser.save(callback); }); }); },

getUserByUsername: function(username, callback){ UserModel.findOne({username: username}).populate('skills').exec(callback); },

getUserById: function(id, callback){ UserModel.findById(id).populate('skills').exec(callback); },

74

Page 75: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

comparePassword: function(typedPassword, hashPassword, callback){ bcrypt.compare(typedPassword, hashPassword, function(err, isMatch) { if(err) { throw err; } callback(null, isMatch); }); },

saveImagePath: function(id, imagePath, callback){ UserModel.findByIdAndUpdate(id, {$set: {image: imagePath}}, {new: true}, callback); },

checkAuthentication: function(req,res,next){ if(req.isAuthenticated()){ next(); } else{ res.redirect("/"); } },

updateUser: function(id, update, callback){ UserModel.update({ _id: id }, update, callback) },

setSkills: function(id, newSkills, callback){ UserModel.update({ _id: id }, { $set: { skills: newSkills } }, callback); },

numberOfUsers: function(){ return UserModel.count(); },

allUsers: function(startFrom, callback){ var countQuery = UserModel.count(), findQuery = UserModel.find().skip(startFrom).limit(10);

this.numberOfUsers() .exec(function (err, num) { if(err) { throw err; }

75

Page 76: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

findQuery.exec(function(err, users) { if(err) { throw err; } callback(users, num) }); }); },

allUsersWithQuery: function(startFrom, query, callback) { var countQuery = UserModel.find(query).count(), findQuery = UserModel.find(query).skip(startFrom).limit(10);

countQuery.exec(function (err, num) { if(err) { throw err; } findQuery.exec(function(err, users) { if(err) { throw err; } callback(users, num); }); }); },

userWithUsername: function(username, callback) { UserModel.find({username: new RegExp('^'+username, "i")}) .populate('skills') .exec(callback); }};module.exports = User;

4.4.2.0 template.ejs

<html> <head> <meta charset="utf8"> <title>SuitArt Business Card</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"

76

Page 77: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> * { padding: 0px; margin: 0px; } body { padding: 0px; margin: 0px; } .top-bottom-buffer { margin: 40px 0 40px 0; } .left-buffer { width: 300px; text-align: right; } .header { text-align: center; } .width100 { width: 590px;} .width50 { width: 250px } .width70 { width: 400px;} .width30 { width: 170px;} .height200 { height: 200px; } .description { word-wrap: break-word; } </style> </head> <body>    <div class="container width100"> <div class="width100 header" style="height: 25px;"> <h3><strong>Curriculum Vitae</strong></h3> </div> <div class="width100 top-bottom-buffer"> <h4><strong>Info</strong></h4> </div> <div class="width100"> <div class="width100" style="height: 200px;"> <div class="width30 pull-left" style="display: inline-block;margin-right: 30px;"> <img src="<%= imgSrc %>" width="150px" height="200px"/> </div> <div class="width30 pull-left" style="display: inline-block; vertical-align: middle;"> <div class="row form-group"> <label>Full name: </label> <span class="left-buffer"><%= user.firstname + ' ' + user.lastname %></span> </div> <div class="row form-group"> <label>Date of birth: </label> <span class="left-buffer"><%= moment(user.birth).format('MM/DD/YYYY') %></span> </div> <div class="row form-group"> <label>Email: </label> <span class="left-buffer"><%= user.email %></span>

77

Page 78: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

</div> <div class="row form-group"> <label>Contact: </label> <span class="left-buffer"><%= user.contact %></span> </div> </div> </div> <div class="width100 top-bottom-buffer"> <h4><strong>Skills</strong></h4> </div> <div class="width100" style="height: 42px;"> <ul class="list-group width100" style="float: left;"> <% user.skills.forEach(function(skill){ %> <li style="display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #fff; border: 1px solid #ddd; float: left;" > <%= skill.name %> </li> <% }); %> </ul> </div> <div class="width100 top-bottom-buffer"> <h4><strong>Projects</strong></h4> </div> <% user.projects.forEach(function(project){ %><% var length = 1; if (project.description.length >= 87) { length = parseInt(project.description.length / 87); } var heightProject = length*25+25+50+50;%> <div class="width100" style="<%= 'height:' + ( heightProject ) + 'px;' %> "> <div class="width100"> <label>Title</label> </div> <div class="width100"> <%= project.title %> </div> <div class="width100"> <label>Link</label> </div> <div class="width100">

78

Page 79: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

<a href="<%= project.link %>" target="_blank"><%= project.link %></a> </div> <div class="width100"> <label>Description</label> </div> <div class="width100 description"> <%= project.description %> </div> <hr/> </div> <% }); %>    </div> </body></html>

4.4.2.1 toPDF.js

var fs = require('fs');var path = require('path');var pdf = require('html-pdf');var ejs = require('ejs');var moment = require('moment');

var express = require('express');var router = express.Router();var UserClass = require('../classes/user');

router.get('/pdf', UserClass.checkAuthentication, function(req, res){ var source = 'app/download/template.ejs', html = fs.readFileSync(source, 'utf8'), pathCV = './app/download/CV.pdf', newDirname = (__dirname+'').replace('app\\download', 'public'), imgSrc = path.normalize('file://' + newDirname + req.user.image);

downloadFile(res, html, pathCV, imgSrc, req.user, source);});

router.get('/pdf/:username', UserClass.checkAuthentication,

79

Page 80: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

function(req, res){

var source = 'app/download/template.ejs', html = fs.readFileSync(source, 'utf8'), username = req.params.username;

UserClass.getUserByUsername(username, function(err, user){ if(err) { throw err; }

var pathCV = './app/download/CV.pdf', newDirname = (__dirname+'').replace('app\\download', 'public'), imgSrc = path.normalize('file://' + newDirname + user.image);

downloadFile(res, html, pathCV, imgSrc, user, source); });});

module.exports = router;

function downloadFile(res, html, pathCV, imgSrc, user, source) { ejs.renderFile(path.join(__dirname+'/template.ejs'), { user: user, imgSrc: imgSrc, moment: moment }, function(err, html){ if(err) { throw err; } var options = { base: 'file://' + path.resolve(source), format: 'A4', height: "842px", width: "595px", footer: { "height": "50px" },

80

Page 81: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

header: { "height": "50px" } }, fileNameForUser = 'CV_' + user.firstname + '_' + user.lastname + '.pdf'; pdf.create(html, options).toFile('./app/download/CV.pdf', function(err, file){ if(err) { throw err; } res.download(path.join(__dirname+'/CV.pdf'), fileNameForUser, function(err){ if(err){ res.status(500).json(err); }

if(fs.existsSync(pathCV)) { fs.unlink(pathCV); } }); }); });}4.4.3.0 models/ skill.js

var mongoose = require('mongoose');var Schema = mongoose.Schema;

var SkillSchema = new Schema({ name: { type: String, index: true }});

module.exports = mongoose.model('Skill', SkillSchema);

4.4.3.1 models/user.js

var mongoose = require('mongoose');var Schema = mongoose.Schema;

81

Page 82: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

var UserSchema = new Schema({ firstname: { type: String, index: true, required : true }, lastname: { type: String, required : true }, email: { type: String, required : true }, contact: { type: String, default: '' }, image: { type: String, default: '/images/default.png' }, username: { type: String, unique : true, required : true }, password: { type: String, required : true }, birth: { type: Date, required : true }, gender: { type: String, required : true }, skills: [ { type: Schema.ObjectId, ref: 'Skill', default: ''} ], projects: [ { title: { type: String, default: ''}, link: { type: String, default: ''}, description: { type: String, default: ''} } ]});

module.exports = mongoose.model('User', UserSchema);

4.4.2.2 routes/project.js

var express = require('express');var router = express.Router();var passport = require('passport');var path = require('path');

var ProjectClass = require('../classes/Project');var UserClass = require('../classes/user');

router.post('/', UserClass.checkAuthentication, function(req, res, next){

req.checkBody('title', 'Title is required!') .regexTitle().withMessage('Title is wrong format!') .notEmpty(); req.checkBody('link', 'Link is required!') .regexLink().withMessage('Link is wrong format!') .notEmpty(); req.checkBody('description', 'Description is required!')

82

Page 83: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

.regexDescription().withMessage('Description is wrong format!') .notEmpty();

var err = req.validationErrors(); if(err) { throw err; } ProjectClass.addProject(req.user.id, req.body, function(err, user){ if(err) { throw err; } res.status(200).json({ project: user.projects[user.projects.length-1] }); });});router.put('/', UserClass.checkAuthentication, function(req, res, next){ req.checkBody('title', 'Title is required!') .regexTitle().withMessage('Title is wrong format!') .notEmpty(); req.checkBody('link', 'Link is required!') .regexLink().withMessage('Link is wrong format!') .notEmpty(); req.checkBody('description', 'Description is required!') .regexDescription().withMessage('Description is wrong format!') .notEmpty();

var err = req.validationErrors(); if(err) { throw err; }

ProjectClass.updateProject(req.user.id, req.body, function(err){ if(err){ throw err; } res.status(200).json({ success: true }); });

83

Page 84: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

});router.delete('/:idProject', UserClass.checkAuthentication, function(req, res){

ProjectClass.deleteProject(req.user.id, req.params.idProject, function(err){ if(err){ throw err; } res.status(200).json({ success: true }); });

}); module.exports = router;

4.4.3.0 routes/skill.js

var express = require('express');var router = express.Router();var path = require('path');

var SkillClass = require('../classes/skill');var UserClass = require('../classes/user');

router.get('/', UserClass.checkAuthentication, function(req, res) { SkillClass.allSkills(function(err, skills){ if(err) { throw err; } res.status(200).json({ skills: skills }); });});

router.put('/', UserClass.checkAuthentication,

84

Page 85: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

function(req, res) { UserClass.setSkills(req.user.id, req.body, function(err){ if(err) { throw err; } res.status(200).json({ msg: 'Sucesss update!' }); });});

module.exports = router;

4.4.3.1 routes/user.js

var express = require('express');var router = express.Router();var passport = require('passport');var LocalStrategy = require('passport-local').Strategy;var multipart = require('connect-multiparty');var fs = require('fs');var path = require('path');var Random = require("random-js");var random = new Random(Random.engines.mt19937().autoSeed());

var UserModel = require('../models/user');var UserClass = require('../classes/user');

var multipartMiddleware = multipart();

// configure passportpassport.use(new LocalStrategy( function(username, password, done) { UserClass.getUserByUsername(username, function(err, user){ if(err) { throw err; } if(!user) { return done(null, false, { msg: 'Unknown User!' }); }

UserClass.comparePassword(password, user.password, function(err, isMatch){ if(err) {

85

Page 86: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

throw err; } if(isMatch) { return done(null, user); } else { return done(null, false, { msg : 'Invalid password!' }) } }); }); }));

passport.serializeUser(function(user, done) { done(null, user.username);});

passport.deserializeUser(function(username, done) { UserClass.getUserByUsername(username, function(err, user) { done(err, { id: user._id, firstname: user.firstname, lastname: user.lastname, username: user.username, email: user.email, birth: user.birth, skills: user.skills, projects: user.projects, image: user.image, contact: user.contact }); });});

router.get('/', UserClass.checkAuthentication, function(req, res, next){ UserClass.getUserByUsername(req.user.username, function(err, user){ if(err) { throw err; } res.status(200).json({ user: user }); });

86

Page 87: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

});

router.post('/register', function(req, res){ //Fields var firstname = req.body.firstname, lastname = req.body.lastname, username = req.body.username, email = req.body.email, password = req.body.password, password2 = req.body.reTypePassword, birth = req.body.birth, gender = req.body.gender;

//Validation req.checkBody('firstname', 'Firstname is required!') .regexFirstLastName().withMessage('Firstname is wrong format!') .notEmpty(); req.checkBody('lastname', 'Lastname is required!') .regexFirstLastName().withMessage('Lastname is wrong format!') .notEmpty(); req.checkBody('username', 'Username is required!') .regexUsername().withMessage('Username is wrong format!') .notEmpty(); req.checkBody('email', 'Email is required!') .isEmail().withMessage('Email is wrong format!') .notEmpty(); req.checkBody('password', 'Password is required!') .regexPassword().withMessage('Password is wrong format!') .notEmpty(); req.assert('password', 'Passwords do not match!').equals(password2); req.checkBody('birth', 'Birth is required!') .isDate().withMessage('Date is wrong format!') .notEmpty(); req.checkBody('gender', 'Gender is required!').notEmpty();

var errors = req.validationErrors();

if(errors) { throw errors } var newUser = new UserModel({ firstname: firstname, lastname: lastname, username: username, email: email,

87

Page 88: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

password: password, birth: birth, gender: gender }); UserClass.createUser(newUser, function(err, user){ if(err) { res.status(500).json({ errors: [{msg: 'Username already taken!'}] }); } else { res.status(200).json({ msg: 'You are registred and can now login!' }); } });});

router.get('/logout', UserClass.checkAuthentication, function(req, res){ req.logout(); req.session = null;

res.status(200).json({ status: 'Logout successful!', });});

router.post('/login', function localAuthentication(req, res, next) { var username = req.body.username, password = req.body.password;

req.checkBody('username', 'Username is required!') .regexUsername().withMessage('Username is wrong format!') .notEmpty(); req.checkBody('password', 'Password is required!') .regexPassword().withMessage('Password is wrong format!') .notEmpty();

var errors = req.validationErrors();

if(errors) { throw errors;

88

Page 89: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

} passport.authenticate('local', function(err, user, info){ if(err){ throw err; } if(!user) { return res.status(403).json({ message: "No user found" }); }

req.login(user, function(err){ if(err) { throw err; } return res.status(200).json({ message: 'user authenticated', user: req.user }); }); })(req, res, next); });

router.put('/upload', UserClass.checkAuthentication, multipartMiddleware, function (req, res) { var value = random.integer(1, 1000000), fileName = req.body.fileName.split('.')[0], type = path.extname(req.body.fileName), err, oldImagePath, dbImagePath, newIamgePath; req.checkBody('fileName', 'The file type is invalid.').isFileType(type); err = req.validationErrors();

oldImagePath = './public/' + req.user.image; dbImagePath = '/images/' + (fileName + '_' + value + '_' + Date.now() + type); newIamgePath = './public/images/' + (fileName + '_' + value + '_' + Date.now() + type); if(err) { throw err;

89

Page 90: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

}

fs.readFile(req.files.newImage.path, function (err, data) { if(err) { throw err; } fs.writeFile(newIamgePath, data, function (err) { if(err) { throw err; } UserClass.saveImagePath(req.user.id, dbImagePath, function(err, user) { if(err) { throw err; } if(-1 === oldImagePath.indexOf('default.png')) { fs.unlink(oldImagePath, function(err) { if(err) { throw err; } res.status(200).json({ image: user.image }); }); } else { res.status(200).json({ image: user.image }); } }); }); }); });

router.put('/', UserClass.checkAuthentication, function (req, res) {

req.checkBody('firstname', 'Firstname is required!') .regexFirstLastName().withMessage('Firstname is wrong format!') .notEmpty(); req.checkBody('lastname', 'Lastname is required!')

90

Page 91: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

.regexFirstLastName().withMessage('Lastname is wrong format!') .notEmpty(); req.checkBody('email', 'Username is required!') .isEmail().withMessage('Email is required!') .notEmpty(); req.checkBody('birth', 'Birth is required!') .isDate().withMessage('Date is wrong format!') .notEmpty(); req.checkBody('contact').optional() .isContact().withMessage('Contact is wrong format!');

var errors = req.validationErrors();

if(errors) { throw errors; }

UserClass.updateUser(req.user.id, req.body, function(err, numAffected){ if(err) { throw err; } res.status(200).json({ msg: "success" }); }); });

router.get('/:username', UserClass.checkAuthentication, function(req, res, next){

req.checkParams('username', 'Username is required!') .regexUsername().withMessage('Username is wrong format!') .notEmpty();

var errors = req.validationErrors();

if(errors) { throw errors }

var username = req.params.username;

91

Page 92: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

UserClass.getUserByUsername(username, function(err, user){ if(err) { throw err; } res.status(200).json({ user: user }); });});

module.exports = router;4.4.3.2 index.js

var express = require('express');var _ = require('lodash');var router = express.Router();var UserClass = require('../classes/user');

router.get('/users/current/:current', UserClass.checkAuthentication, function(req, res, next){ var current = req.params.current, startFrom = current*10-10;

UserClass.allUsers(startFrom , function(users, total){ res.status(200).json({ users: users, total: total }); });});

router.get('/users((/username/:username)?(/firstname/:firstname)?(/lastname/:lastname)?)', UserClass.checkAuthentication, function(req, res, next){

var query = {}, username = req.params.username, firstname = req.params.firstname, lastname = req.params.lastname;

if(!_.isUndefined(req.params.username)) { req.checkParams('username', 'Username is required!')

92

Page 93: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

.regexUsername().withMessage('Username is wrong format!') .notEmpty(); query.username = new RegExp('^'+username, "i"); } if(!_.isUndefined(req.params.firstname)) { req.checkParams('firstname', 'Firstname is required!') .regexFirstLastNameSearch().withMessage('Firstname is wrong format!') .notEmpty(); query.firstname = new RegExp('^'+firstname, "i"); } if(!_.isUndefined(req.params.lastname)) { req.checkParams('lastname', 'Lastname is required!') .regexFirstLastNameSearch().withMessage('Lastname is wrong format!') .notEmpty(); query.lastname = new RegExp('^'+lastname, "i"); }

var errors = req.validationErrors();

if(errors) { throw errors }

if(_.isEmpty(query)) { throw new Error('There was an error'); }

UserClass.allUsersWithQuery(0, query, function(users, total){ res.status(200).json({ users: users, total: total }); });});

router.get('/users/current/:current/username/:username', UserClass.checkAuthentication, function(req, res, next){

var query = {}, current = req.params.current, username = req.params.username,

93

Page 94: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

startFrom = current*10-10; if(!_.isUndefined(req.params.username)) { query.username = new RegExp('^'+username, "i"); }

if(_.isEmpty(query)) { throw new Error('There was an error'); }

UserClass.allUsersWithQuery(startFrom, query, function(users, total){ res.status(200).json({ users: users, total: total }); });});

module.exports = router;

94

Page 95: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

5. BAZA PODATAKA

5.1 Struktura baze podataka

Pošto je mnogo baza podataka bez šeme, a mongoose nam je omogćio da pravimo šeme nasih dokuemata.

User šema

var UserSchema = new Schema({ firstname: { type: String, index: true }, lastname: { type: String }, email: { type: String }, contact: { type: String, default: '' }, image: { type: String, default: '/images/default.png' }, username: { type: String }, password: { type: String }, birth: { type: Date }, gender: { type: String }, skills: [ { type: Schema.ObjectId, ref: 'Skill', default: ''} ], projects: [ { title: { type: String, default: ''}, link: { type: String, default: ''}, description: { type: String, default: ''} } ]});

Skill šema

var SkillSchema = new Schema({

95

Page 96: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

name: { type: String, index: true }});

6. ZAKLJUČAK

Napravili smo aplikaciju za pravljenje CV korisnika. Gde svaki korisnik može da se uloguje u slucaju da nema nalog na našem sajtu, ima opciju da se registruje. Takođe svaki korisnik može da ažurira, dodaje I briše stvari sa svog profila, a da I takođe ima opciju da preuzme svoj profil u pdf formatu. Pored CRUD operacija I download, ima mogućnost pretrage ostalih korisnika I uvid u njihove profile, kao I preuzimanje nihovih profila.

Ceo ovaj sajt je namenjen upoznavanju MEAN stack-a kao I dosta dodathin modula koje on zahteva da bih radio.

96

Page 97: Sadržaj: - Prva strana | Visoka ICT školawebdizajn.ict.edu.rs/sites/...2017_marko_pavlovic.docx  · Web viewOlakšana komunikacija u realnom vrmeneu, pored event-loop koji može

7. LITERATURA

1) AngularJS – https://docs.angularjs.org/api2) ExpressJS – https://expressjs.com/en/4x/api.html3) NodeJS - https://nodejs.org/en/docs/

4) MongoDB - https://docs.mongodb.com/v3.2/

5) AngularUI - https://angular-ui.github.io/

6) PassportJS - http://passportjs.org/docs

7) npmJS - https://www.npmjs.com/

8) ejs - http://ejs.co/

9) mongoosejs - http://mongoosejs.com/docs/

10) momentjs -https://momentjs.com/

11) lodash - https://lodash.com/

97