diff --git a/README.md b/README.md index d29b743fd89eafb1c9832db0e9338f69aafc642e..dba931b2751d164cc8d844b43dce1aad74f394ff 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,19 @@ The idea is to create a responsive front-end that consumes a RESTful web service * Rate Movies (0-5 star scale) * Public lists (allow your whole neighborhood to see and request to borrow your movies) * Movie Reviews for a movie -* Google Analytics \ No newline at end of file +* Google Analytics + +#Dependencies + +* MongoDB +* https://github.com/wagnerrp/pytmdb3/ + +#Setup + +- Clone this directory +- in the same parent folder you cloned this directory clone the following dependency https://github.com/wagnerrp/pytmdb3/ +- run the command `pip install -r requirements.txt` +- update the `__init__.py` in the project folder to point to your mongodb server +- start your mongodb server if it isn't running already +- run the `python runserver.py` in the MyMovieLibrary directory +- the application should be running on localhost:8080 \ No newline at end of file diff --git a/project/__init__.py b/project/__init__.py index 4c1162e4e1db92ee70b7db64a35ce75be52ef0d2..1fe76c5a3c811955b94e4886a7105d0e19044fe4 100644 --- a/project/__init__.py +++ b/project/__init__.py @@ -1,15 +1,54 @@ # -*- coding: utf-8 -*- __version__ = '0.1' -from flask import Flask +from functools import wraps +from flask import Flask, session, redirect, url_for, request from flask_debugtoolbar import DebugToolbarExtension from flask.ext.mongoengine import MongoEngine -#from mongoengine import * -#connect('my_movie_library') +import sys + +sys.path.append('../pytmdb3/') app = Flask('project') app.config['SECRET_KEY'] = 'random' app.config['MONGODB_SETTINGS'] = {'DB': 'my_movie_library'} -db = MongoEngine(app) +# app.config["MONGODB_SETTINGS"] = {'DB': "my_movie_library", + # 'host': '192.168.1.89'} app.debug = True -toolbar = DebugToolbarExtension(app) +# toolbar = DebugToolbarExtension(app) +db = MongoEngine(app) +# app.debug = True +# toolbar = DebugToolbarExtension(app) +def security(role=None): + def wrapper(func): + @wraps(func) + def security_check(*args,**kwargs): + import json + user = None + if 'user' in session: + from project.model.User import User + user = json.loads(session['user']) + user = User.objects(email=user['email']).first() + kwargs['user'] = user + + if role == None: + return func(*args,**kwargs); + if isinstance(role, list): + if len(role) == 0: + return func(*args,**kwargs); + elif user == None: + return redirect(url_for('login', next=request.url)) + + for r in role: + if r in user.roles: + return func(*args,**kwargs); + return redirect(url_for('login', next=request.url)) + + if user == None or role not in user.roles: + return redirect(url_for('login', next=request.url)) + + return func(*args,**kwargs); + return security_check + return wrapper + + from project.controllers import * diff --git a/project/controllers/home.py b/project/controllers/home.py index b0b565970ef508b62fb6e8ca2bd9c60482933173..bc436e84445ba290263d35a1fad002adce263fa9 100644 --- a/project/controllers/home.py +++ b/project/controllers/home.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from project import app +from project import app, security from flask import render_template, request from flask.ext.wtf import Form, TextField, validators @@ -9,16 +9,31 @@ class CreateForm(Form): @app.route('/') -def start(): - return render_template('home/home.html') - +@security() +def start(user=None): + return render_template('home/home.html',user=user) @app.route('/print', methods=['GET', 'POST']) def printer(): form = CreateForm(request.form) - if request.method == 'POST' and form.validate(): - from app.models.Printer import Printer - printer = Printer() - printer.show_string(form.text.data) - return render_template('home/index.html') - return render_template('home/print.html', form=form) + return render_template('printer/index.html') + # return render_template('printer/print.html', form=form) + +@app.route('/test', methods=['GET', 'POST']) +def model(): + from project.models.User import User + from project.models.Library import Library + from project.models.Movie import Movie + User.objects().delete() + Library.objects().delete() + Movie.objects().delete() + + user = User(email='test@test.com', password='password').save() + testCollection = Library( user=user, unit='Movie', name='Action').save() + movie = Movie(title="SpiderMan",summary="SpiderMan gets the girl and saves the day").save() + movie.addTag('action').save() + user.addRole('kangaroo').save() + testCollection.addUnit(movie).save() + m = testCollection.getUnit(0) + print m.title + return render_template('printer/index.html') diff --git a/project/controllers/library.py b/project/controllers/library.py new file mode 100644 index 0000000000000000000000000000000000000000..350403f62247853ae3d06cc8889e8109b8a80fee --- /dev/null +++ b/project/controllers/library.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from project import app, security +from flask import render_template, request, session +from flask.ext.wtf import Form, TextField, validators +from project.model.Library import Library +from project.model.User import User +import json + + +@app.route('/libraries') +@security('user') +def libraries(user = None): + libraries = Library.objects(user=user,unit='Movie') + return render_template('library/master.html', libraries=libraries,user=user) + +@app.route('/libraries/<name>') +@security('user') +def library(name,user=None): + from project.model.Movie import Movie + library = Library.objects(user=user,name=name,unit='Movie').first() + return render_template('library/library.html',library=library) + +@app.route('/libraries/<name>/<int:index>') +@security('user') +def libraryItem(name, index,user=None): + return render_template('library/libraryItem.html') diff --git a/project/controllers/loan.py b/project/controllers/loan.py new file mode 100644 index 0000000000000000000000000000000000000000..52ab879febe58a114386fbddcc955cd7390784a0 --- /dev/null +++ b/project/controllers/loan.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +from project import app, security +from flask import render_template, request +from flask.ext.wtf import Form, TextField, validators + +@app.route('/loaned') +@security('user') +def loaned(user=None): + return render_template('loan/master.html', user=user) \ No newline at end of file diff --git a/project/controllers/login.py b/project/controllers/login.py new file mode 100644 index 0000000000000000000000000000000000000000..94102c871f001e34d24a19fe40cab3848865c380 --- /dev/null +++ b/project/controllers/login.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +from project import app +from flask import render_template, request, session, redirect, url_for +from flask.ext.wtf import Form, TextField, validators +from project.model.User import User + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'GET': + return render_template('login/master.html') + if request.method == 'POST': + email = request.form['email'] + password = request.form['password'] + + user = User.validateUser(email, password) + + if user is not None: + session['user'] = user.toJSON() + return redirect(url_for('libraries')) + else: + error = "That email or password is not valid. Please check your credentials and try again." + return render_template('login/master.html', error=error) + + return render_template('login/master.html') + +@app.route('/signup', methods=['GET', 'POST']) +def signup(): + if request.method == 'GET': + return render_template('login/signup.html') + if request.method == 'POST': + email = request.form['email'] + password = request.form['password'] + passwordConfirm = request.form['passwordConfirm'] + + if password == passwordConfirm: + #check unique user + if len(User.objects(email=email)) == 0: + from project.model.Library import Library + #if unique, create user + user = User.createUser(email, password) + user.addRole('user').save() + session['user'] = user.toJSON() + Library(user=user, unit='Movie', name='Master').save() + Library(user=user, unit='Movie', name='Borrowed').save() + return redirect(url_for('libraries')) + else: + error = "The email provided is already in use with another account." + return render_template('login/signup.html', error=error, email=email) + else: + error = "The passwords you entered did not match. Please try again." + return render_template('login/signup.html', error=error, email=email) + return render_template('login/signup.html') + +@app.route('/logout', methods=['GET', 'POST']) +def logout(): + if 'user' in session: + session.pop('user') + return redirect(url_for('start')) diff --git a/project/controllers/movies.py b/project/controllers/movies.py new file mode 100644 index 0000000000000000000000000000000000000000..0c071d34c6db059f850b6bb38fcccb4748c99097 --- /dev/null +++ b/project/controllers/movies.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +from project import app +from flask import render_template, request +from flask.ext.wtf import Form, TextField, validators + + +@app.route('/movies') +def movies(): + return render_template('movies/master.html') + +@app.route('/movies/<movieId>') +def movie_item(movieId): + return render_template('movies/movie.html') diff --git a/project/controllers/test.py b/project/controllers/test.py index 2273b7cfe318187b7066718c37badec38d2183c9..5c3b8bb8a804742e1aa8733ca66cf4ffd62e780d 100644 --- a/project/controllers/test.py +++ b/project/controllers/test.py @@ -22,9 +22,8 @@ def test(): @app.route('/test-user') def test_user(): - from project.models.User import User - test_user = User(created_at=datetime.datetime.now(), email='test@user.org', password='password', role='admin').save() - print "date: %s" % test_user.created_at + from project.model.User import User + test_user = User.createUser(email='ndbills@gmail.com', password='password') #check if test_user exists #if yes, return a success render_template #else add the user and return some other template diff --git a/project/model/Group.py b/project/model/Group.py new file mode 100644 index 0000000000000000000000000000000000000000..8eb605c0012b12bcdb156861e55b769ba597a324 --- /dev/null +++ b/project/model/Group.py @@ -0,0 +1,21 @@ +from project import db + +class Group(db.Document): + category = db.StringField(max_length=50, required=True) + tags = db.ListField(db.StringField(max_length=50)) + + def addTag(self,tag): + if tag not in self.tags: + self.tags.append(tag) + return self + + def removeTag(self,tag): + if tag in self.tags: + self.tags.remove(tag) + return self + + def __str__(self): + return "Category: %s - %s" % (self.category, self.tags) + + def __repr__(self): + return "{%s}" % (self.category) \ No newline at end of file diff --git a/project/model/Library.py b/project/model/Library.py new file mode 100644 index 0000000000000000000000000000000000000000..70c924013904e888d27ab69687d9912302520d09 --- /dev/null +++ b/project/model/Library.py @@ -0,0 +1,131 @@ +from project import db +import User, sys, copy, Group + +class Library(db.Document): + user = db.ReferenceField(User.User) + unit = db.StringField(max_length=50) #what Document the Library's collection relates to + name = db.StringField(max_length=100, unique_with=['user','unit']) #name of the Library + lookup_attribute = db.StringField(default='id') + collection = db.ListField(db.StringField()) + summary = db.StringField() + + def addUnit(self,unit): + if self.unit == type(unit).__name__: + value = unit[self.lookup_attribute] + if value is not None and value not in self.collection: + self.collection.append("%s" % value) + else: + return self + else: + raise Exception("Cannot add %s to Library of %s" % (type(unit).__name__,self.unit)) + return self + + def removeUnit(self,unit): + if self.unit == type(unit).__name__: + value = unit[self.lookup_attribute] + if value is not None and value in self.collection: + self.collection.remove("%s" % value) + else: + return self + else: + raise Exception("Cannot remove %s from Library of %s" % (type(unit).__name__,self.unit)) + return self + + # @param index --represents the index in the Library collection of the object + def hydrateUnit(self, index): + if index < 0 or index > self.collection.count: + raise Exception("Invalid index for Library %s" % self.name) + attr = {} + attr[self.lookup_attribute] = self.collection[index] + model = getattr(sys.modules["project.model.%s" % self.unit], self.unit) + return model.objects(**attr).first() + + def hydrateList(self): + hydratedCollection = [] + print sys.modules.keys() + model = getattr(sys.modules["project.model.%s" % self.unit], self.unit) + for index, hash_value in enumerate(self.collection): + attr = {} + attr[self.lookup_attribute] = self.collection[index] + unit = model.objects(**attr).first() + hydratedCollection.append(unit) + return hydratedCollection + + def searchCollection(self,keyword,exclude =[] ,heirarchy = []): + """ + Searches the collection of this library for matches to the keyword + + keyword String: keyword to search for + exclude List: list of attributes to exclude from the search + heirarchy List: order that matches should be returned + (first matches returned are always matches of multiple categories) + + Return List of unique matches ordered by heirarchy if provided else by attribute order of object + """ + import datetime + hydratedCollection = self.hydrateList() + model = getattr(sys.modules["project.model.%s"%self.unit], self.unit) + model_attributes = model.__dict__['_db_field_map'].keys() + model_attributes = self.diff(model_attributes,exclude) + if len(heirarchy) > 0: + model_attributes = self.ordered_union(heirarchy,model_attributes) + + result = [[]] + for attr in model_attributes: + result.append([]); + for unit in hydratedCollection: + for index,attr in enumerate(model_attributes): + value = unit[attr] + if isinstance(value, datetime.datetime): + value = value.isoformat() + if isinstance(value,list) and keyword.lower() in (val.lower() for val in value): + result[index].append(unit) + elif isinstance(value,basestring) and keyword.lower() in value.lower(): + result[index].append(unit) + groups = Group.Group.objects(tags=keyword) + for group in groups: + if self.similar(group.tags,value): + result[-1].append(unit) + break + + finish_result = [] + for index,r in enumerate(result[:-1]): + for val in r: + for s in result[index:]: + if val in s: + if val not in finish_result: + finish_result.append(val) + + for r in result: + additions = self.diff(r,finish_result) + finish_result.extend(additions) + + return finish_result + + @staticmethod + def diff(a, b): + b = set(b) + return [aa for aa in a if aa not in b] + + @staticmethod + def ordered_intersect(a, b): + b = set(b) + return [aa for aa in a if aa in b] + + @staticmethod + def ordered_union(a, b): + a.extend(Library.diff(b,a)) + return a + + @staticmethod + def similar(a,b): + for val in a: + if val in b: + return True + return False + + def __str__(self): + return "Library {%s-%s}" % (self.name, self.unit) + + def __repr__(self): + return self.__str__() diff --git a/project/model/Movie.py b/project/model/Movie.py new file mode 100644 index 0000000000000000000000000000000000000000..c6e0a1aff0e28fc256dce3173ef8e03992415140 --- /dev/null +++ b/project/model/Movie.py @@ -0,0 +1,28 @@ +from project import db +import datetime + +class Movie(db.Document): + created = db.DateTimeField(default=datetime.datetime.now, required=True) + title = db.StringField(max_length=255, required=True) + summary = db.StringField(max_length=10000, required=True) + tags = db.ListField(db.StringField(max_length=50)) + + def addTag(self,tag): + if tag not in self.tags: + self.tags.append(tag) + return self + + def removeTag(self,tag): + if tag in self.tags: + self.tags.remove(tag) + return self + + def __str__(self): + return self.title + + def __repr__(self): + return str(self.toJSON()) + + def toJSON(self): + import json + return json.dumps({'created': self.created.isoformat(), 'title': self.title, 'summary': self.summary, 'tags': str(self.tags), 'id':str(self.id)}) \ No newline at end of file diff --git a/project/model/User.py b/project/model/User.py new file mode 100644 index 0000000000000000000000000000000000000000..16e03da6a165ae2ff3222ee71028ffcd9d0e37ce --- /dev/null +++ b/project/model/User.py @@ -0,0 +1,62 @@ +from project import db +import datetime, hashlib, random + +class User(db.Document): + created = db.DateTimeField(default=datetime.datetime.now(), required=True) + email = db.StringField(max_length=255, required=True, unique=True) + salt = db.StringField(max_length=255, required=True) + password = db.StringField(max_length=255, required=True) + roles = db.ListField(db.StringField(max_length=50)) + + def hasRole(self,role): + return role in self.roles + + def addRole(self,role): + if role not in self.roles: + self.roles.append(role) + return self + + def removeRole(self,role): + if role in self.roles: + self.roles.remove(role) + return self + + def changePassword(self,password): + self.password = hashlib.sha224(self.salt.join(password)).hexdigest() + self.save() + return self + + def toJSON(self): + import json + return json.dumps({'created': self.created.isoformat(), 'email': self.email, 'roles': self.roles, 'id':str(self.id)}) + + @staticmethod + def createUser(email, password): + user = User(email=email) + user.salt = str(random.getrandbits(128)) + user.password = hashlib.sha224(user.salt.join(password)).hexdigest() + user.save() + return user + + @staticmethod + def validateUser(email, password): + user = User.objects(email=email).first() + if user == None: + return None + password = hashlib.sha224(user.salt.join(password)).hexdigest() + if user.password == password: + return user + else: + return None + + def __str__(self): + return self.email + + def __repr__(self): + return str(self.toJSON()) + + meta = { + 'allow_inheritance': True, + 'indexes': ['-created', 'email'], + 'ordering': ['-created'] + } \ No newline at end of file diff --git a/project/models/__init__.py b/project/model/__init__.py similarity index 100% rename from project/models/__init__.py rename to project/model/__init__.py diff --git a/project/models/Home.py b/project/models/Home.py deleted file mode 100644 index 5aae59746363b13bd25e4b158474a9f26eeccd22..0000000000000000000000000000000000000000 --- a/project/models/Home.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -from flask import flash - - -class Home(object): - - def show_string(self, text): - if text == '': - flash("You didn't enter any text to flash") - else: - flash(text + "!!!") diff --git a/project/models/Test.py b/project/models/Test.py deleted file mode 100644 index 4cd556ec3e8fcbe671cb954a2edd9138546cae64..0000000000000000000000000000000000000000 --- a/project/models/Test.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -from flask import flash - - -class Test(object): - - def show_string(self, text): - if text == '': - flash("You didn't enter any text to flash") - else: - flash(text + "!!!") \ No newline at end of file diff --git a/project/static/js/Widgets/Login.js b/project/static/js/Widgets/Login.js new file mode 100644 index 0000000000000000000000000000000000000000..9dc734b61194071cd663cb6555780b28eefe49f3 --- /dev/null +++ b/project/static/js/Widgets/Login.js @@ -0,0 +1,36 @@ +// Set up the namespace +window.mml = window.mml || {}; +mml.Widgets = mml.Widgets || {}; + +mml.Widgets.Login = function (options) { + /// <summary> + /// 1: Login(options) - This function accepts a JavaScript object containing the options to create a new login widget for a single user. + /// </summary> + /// <param name="options" type="object"> + /// 1: url - The url of the service to retrieve data from. + /// 2: selector - The jQuery selector for the container the data will be put in. + /// </param> + /// <returns type="mml.Widgets.Login" /> + if (!(this instanceof mml.Widgets.Login)) { + return new mml.Widgets.Login(options); + } + this.init(options); //This will setup the new instance +}; + +mml.Widgets.Login.prototype = (function () { + var init; + + init = function(options){ + var template; + + template = ''; + + $(options.selectedElement).append(template); + + }; + + /* PUBLIC */ + return { + init: init + }; +}()); \ No newline at end of file diff --git a/project/templates/home/home.html b/project/templates/home/home.html index ae77ccd23682abf3b974185db722eb5fa49c9666..534d0d9e5fc2ef1e50b0c0f4651cffda3d52ce29 100644 --- a/project/templates/home/home.html +++ b/project/templates/home/home.html @@ -1,25 +1,27 @@ -{% extends "index.html" %} +{% extends "usernav.html" if user else 'index.html'%} {% block content %} - <div class="jumbotron"> - <h1>It's working!!!</h1> - <p>Here, all of your wildest dreams can come true.</p> - <p><a class="btn btn-primary btn-lg" role="button">The Demo</a></p> - </div> + <div class="col-sm-12"> + <div class="jumbotron"> + <h1>It's working!!!</h1> + <p>Here, all of your wildest dreams can come true.</p> + <p><a class="btn btn-primary btn-lg" role="button">The Demo</a></p> + </div> - <div class="row"> - <div class="col-xs-12 col-sm-6"> - <div class="thumbnail"> - <img data-src="holder.js/100%x200/sky" alt="..."> - <div class="caption"> - <h3>Awesome Sauce!</h3> + <div class="row"> + <div class="col-xs-12 col-sm-6"> + <div class="thumbnail"> + <img data-src="holder.js/100%x200/sky" alt="..."> + <div class="caption"> + <h3>Awesome Sauce!</h3> + </div> </div> </div> - </div> - <div class="col-xs-12 col-sm-6"> - <div class="thumbnail"> - <img data-src="holder.js/100%x200/vine" alt="..."> - <div class="caption"> - <h3>It is so cool!</h3> + <div class="col-xs-12 col-sm-6"> + <div class="thumbnail"> + <img data-src="holder.js/100%x200/vine" alt="..."> + <div class="caption"> + <h3>It is so cool!</h3> + </div> </div> </div> </div> diff --git a/project/templates/index.html b/project/templates/index.html index c5f5364453ce1cec394375a3223db75b90da924d..285b5a0bffdf0b8171d2e184cda96efa1e37da9c 100644 --- a/project/templates/index.html +++ b/project/templates/index.html @@ -35,21 +35,26 @@ <ul class="nav navbar-nav"> <li><a href="https://github.com/ndbills/MyMovieLibrary/" target="_blank">GitHub</a></li> </ul> - <ul class="nav navbar-nav navbar-right"> - <li><a href="#">Login</a></li> - <li><a href="#">Sign-up</a></li> - </ul> + + {% block usernav %} + <ul class="nav navbar-nav navbar-right"> + <li><a href="{{ url_for('login') }}">Login</a></li> + <li><a href="{{ url_for('signup') }}">Sign-up</a></li> + </ul> + {% endblock %} </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav><!-- end navbar --> - {% block content %}{% endblock %} + <div class="row"> + {% block content %}{% endblock %} + </div> <div class="panel panel-default"> - <div class="panel-body"> - Open Source by Brian Wood & Nathan Bills, 2014 + <div class="panel-body"> + Open Source by Brian Wood & Nathan Bills, 2014 + </div> </div> - </div> </div> @@ -58,17 +63,8 @@ <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="{{ url_for('static', filename='lib/bootstrap.min.js') }}"></script> - - <!--<h1>Flask MVC Boilerplate</h1> - {#{% for message in get_flashed_messages() %} - <div class="flash">{{ message }}</div> - {% endfor %} - {% macro render_error(field) %} - {% if field.errors %} - {% for error in field.errors %}<span class='error'>{{ error }}</span>{% endfor %} - {% endif %} - {% endmacro %} - {% block body %}{% endblock %}#}--> + {% block javascript %} + {% endblock %} </div> </body> </html> diff --git a/project/templates/library/library.html b/project/templates/library/library.html new file mode 100644 index 0000000000000000000000000000000000000000..c77414423a0c6a7a449f9863cee7ab4f1203d1c7 --- /dev/null +++ b/project/templates/library/library.html @@ -0,0 +1,51 @@ +{% extends "usernav.html" %} +{% block content %} + <div class="col-sm-12"> + <div class="col-md-4"> + <h3>{{ library.name ~ ' ' ~ library.unit }} Library </h3> + </div> + <div class="btn-group pull-right"> + <button type="button" class="btn btn-default add-movie" data-id='{{ library.name }}'>Add Movie</button> + </div> + <table class="table table-striped table-hover"> + <thead> + <tr> + <td>Movie</td> + <td>Summary</td> + <td>Rating</td> + <td>Options</td> + </tr> + </thead> + <tbody> + {% if library.collection|length <= 0 %} + <tr> + <td>No Movies are in this collection </td> + </tr> + {% else %} + {% for movie in library.hydrateList() %} + <tr> + <td> + <img src="{{ movie.image|default('') }}" style="max-width: 200px;" alt="{{ movie.title }}"> + <p>{{ movie.title }}</p> + </td> + <td> + {{ movie.summary }} + </td> + <td> + {{ movie.rating }} + </td> + <td> + <div class="btn-group-vertical"> + <button type="button" class="btn btn-default btn-small lend-movie" data-id='{{ movie.id }}'>Lend</button> + <button type="button" class="btn btn-default btn-small borrow-movie" data-id='{{ movie.id }}'>Edit</button> + <button type="button" class="btn btn-default btn-small remove-movie" data-id='{{ movie.id }}'>Remove</button> + </div> + </td> + </tr> + {% endfor %} + + {% endif %} + </tbody> + </table> + </div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/library/libraryItem.html b/project/templates/library/libraryItem.html new file mode 100644 index 0000000000000000000000000000000000000000..256b9569cec8e094608d254e5e507705565960ee --- /dev/null +++ b/project/templates/library/libraryItem.html @@ -0,0 +1,38 @@ +{% extends "usernav.html" %} +{% block content %} + <div class="col-sm-12"> + <div class="row"> + <div class="col-xs-12 col-sm-3 col-md-2"> + <img data-src="holder.js/100%x200/sky" alt="movie title"> + <button type="button" class="btn btn-default btn-sm"> + <span class="glyphicon glyphicon-pencil"></span> Edit + </button> + <button type="button" class="btn btn-warning btn-sm"> + <span class="glyphicon glyphicon-remove"></span> Remove + </button> + </div> + + <div class="col-xs-12 col-sm-3 col-md-2"> + <ul class="list-unstyled"> + <li>Movie Title</li> + <li>Director(s)</li> + <li>Actors (truncated)</li> + <li>Producers</li> + <li>Composer</li> + </ul> + </div> + + <div class="col-xs-12 col-sm-6 col-md-8"> + <h2>Summary</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eget metus vitae mi interdum tristique. Integer malesuada quam tincidunt, gravida nisi vehicula, auctor mi. Morbi fermentum, neque quis adipiscing placerat, odio nunc consequat ligula, vel sagittis nisl orci id risus. Aliquam hendrerit, elit a molestie suscipit, eros tortor scelerisque odio, sit amet cursus ligula lorem et turpis. Pellentesque ante metus, suscipit id blandit eget, vehicula quis tortor. Aliquam a pharetra enim. Morbi in purus sit amet nulla egestas consectetur. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla diam nulla, iaculis lobortis varius at, auctor malesuada enim. Donec a nisl eget ligula rhoncus fermentum.</p> + </div> + </div> + + <div class="row"> + <div class="col-xs-12 col-sm-6 col-md-12"> + <h3>Movie Reviews</h3> + <p>Coming Soon!</p> + </div> + </div> + </div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/library/master.html b/project/templates/library/master.html new file mode 100644 index 0000000000000000000000000000000000000000..883ff8306d02ead92d8e3319eb7304f5c6231405 --- /dev/null +++ b/project/templates/library/master.html @@ -0,0 +1,48 @@ +{% extends "usernav.html" %} +{% block content %} + <div class="col-sm-12"> + <h3>My Libraries</h3> + <table class="table table-striped table-hover"> + <thead> + <tr> + <td>Library</td> + <td>Summary</td> + <td>Movie Count</td> + <td>Options</td> + </tr> + </thead> + <tbody> + {% for library in libraries %} + <tr class='clickable' data-name='{{ library.name }}'> + <td> + <a href="#">{{ library.name }}</a> + </td> + <td> + {{ library.summary }} + </td> + <td> + {{ library.collection|length }} + </td> + <td> + <button class="btn btn-default btn-sm"> + <span class="glyphicon glyphicon-pencil"></span> Edit + </button> + </td> + </tr> + {% endfor %} + </tbody> + </table> + </div> +{% endblock %} + +{% block javascript %} + <script> + $(function(){ + var libraryPath = '{{ url_for('library', name='__lib__') }}'; + $('.clickable').on('click',function(){ + var libname = $(this).data('name'); + window.document.location = libraryPath.replace('__lib__',libname); + }) + })(jQuery); + </script> +{% endblock %} \ No newline at end of file diff --git a/project/templates/loan/master.html b/project/templates/loan/master.html new file mode 100644 index 0000000000000000000000000000000000000000..d0318605d0cd1a586f59619749941840ea21fc44 --- /dev/null +++ b/project/templates/loan/master.html @@ -0,0 +1,38 @@ +{% extends "usernav.html" %} +{% block content %} + <div class="col-sm-12"> + <h3>Movies Loaned Out</h3> + <table class="table table-striped table-hover"> + <thead> + <tr> + <td>Movie</td> + <td>Borrower</td> + <td>Date Borrowed</td> + <td>Due Date</td> + <td>Options</td> + </tr> + </thead> + <tbody> + <tr> + <td> + <p>The Bourne Identity</p> + </td> + <td> + <p>Brian Wood</p> + </td> + <td> + <p>Thur, April 17, 2014/p> + </td> + <td> + <p>Thur, May 1, 2014</p> + </td> + <td> + <button class="btn btn-default btn-sm"> + <span class="glyphicon glyphicon-hand-down"></span> Return + </button> + </td> + </tr> + </tbody> + </table> + </div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/login/master.html b/project/templates/login/master.html new file mode 100644 index 0000000000000000000000000000000000000000..0e2db7ccc65f381e5dbbcd94fbc881f0fba80ef5 --- /dev/null +++ b/project/templates/login/master.html @@ -0,0 +1,36 @@ +{% extends "index.html" %} +{% block content %} + <!-- Login --> + <div class="col-sm-12"> + <div class="col-xs-6 col-md-6"> + {% if error %} + <div class="alert alert-danger"> + {{ error }} + </div> + {% endif %} + + <div class="col-sm-offset-3 col-sm-12"> + <h3>Login</h3> + </div> + <form class="form-horizontal" role="form" method="post" action="{{ url_for('login') }}"> + <div class="form-group"> + <label for="userEmail" class="col-sm-3 control-label">Email</label> + <div class="col-xs-9 col-md-6"> + <input type="email" class="form-control" id="userEmail" name="email" placeholder="Email address"> + </div> + </div> + <div class="form-group"> + <label for="userPassword" class="col-sm-3 control-label">Password</label> + <div class="col-xs-9 col-md-6"> + <input type="password" class="form-control" id="userPassword" name="password" placeholder="Password"> + </div> + </div> + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-10"> + <button type="submit" class="btn btn-default">Login</button> + </div> + </div> + </form> + </div> + </div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/login/signup.html b/project/templates/login/signup.html new file mode 100644 index 0000000000000000000000000000000000000000..9ac427f280fa14e9d891a684ccdc93c84fe1d8e3 --- /dev/null +++ b/project/templates/login/signup.html @@ -0,0 +1,42 @@ +{% extends "index.html" %} +{% block content %} + <!-- Sign-up --> + <div class="col-sm-12"> + <div class="col-xs-6 col-md-6"> + {% if error %} + <div class="alert alert-danger"> + {{ error }} + </div> + {% endif %} + + <div class="col-sm-offset-3 col-sm-12"> + <h3>Sign-up</h3> + </div> + <form class="form-horizontal" role="form" method="post" action="{{ url_for('signup') }}"> + <div class="form-group"> + <label for="userEmail" class="col-sm-3 control-label">Email</label> + <div class="col-xs-9 col-md-6"> + <input type="email" class="form-control" id="userEmail" name="email" placeholder="Email address"> + </div> + </div> + <div class="form-group"> + <label for="userPassword" class="col-sm-3 control-label">Password</label> + <div class="col-xs-9 col-md-6"> + <input type="password" class="form-control" id="userPassword" name="password" placeholder="Password"> + </div> + </div> + <div class="form-group"> + <label for="userPasswordConfirm" class="col-sm-3 control-label">Confirm Password</label> + <div class="col-xs-9 col-md-6"> + <input type="password" class="form-control" id="userPasswordConfirm" name="passwordConfirm" placeholder="Password"> + </div> + </div> + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-9"> + <button type="submit" class="btn btn-default">Sign up</button> + </div> + </div> + </form> + </div> + </div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/movies/master.html b/project/templates/movies/master.html new file mode 100644 index 0000000000000000000000000000000000000000..ae1f94864c45629bfab97d164695de75112b7a4b --- /dev/null +++ b/project/templates/movies/master.html @@ -0,0 +1,60 @@ +{% extends "usernav.html" %} +{% block content %} + <div class="col-sm-12"> + <h3>My Movies</h3> + <table class="table table-striped table-hover"> + <thead> + <tr> + <td>Movie</td> + <td>Summary</td> + <td>Rating</td> + <td>Options</td> + </tr> + </thead> + <tbody> + <tr> + <td> + <!--<img data-src="holder.js/165x200/sky" alt="movie title">--> + <img src="http://1.bp.blogspot.com/__cfySUj2yhk/TKE8n5fJo4I/AAAAAAAAAnY/Vwf39Hilx2Y/s1600/Bourne_Identity.jpg" style="max-width: 200px;" alt="The Bourne Identity"> + <p>The Bourne Identity</p> + </td> + <td> + <p>A man is picked up by a fishing boat, bullet-riddled and without memory, then races to elude assassins and recover from amnesia.</p> + </td> + <td> + <p>7.9/10</p> + </td> + <td> + <button class="btn btn-info btn-sm"> + <span class="glyphicon glyphicon-hand-right"></span> Lend + </button> + <button class="btn btn-default btn-sm"> + <span class="glyphicon glyphicon-pencil"></span> Edit + </button> + </td> + </tr> + <tr> + <td> + <img src="http://www.eeriedigest.com/wordpress/wp-content/uploads/2011/08/bourne-supremacy.jpg" style="max-width: 200px;" alt="The Bourne Supremacy"> + <p>The Bourne Supremacy</p> + </td> + <td> + <p>When Jason Bourne is framed for a CIA operation gone awry, he is forced to resume his former life as a trained assassin to survive.</p> + </td> + <td> + <p>7.8/10</p> + </td> + <td> + <button class="btn btn-info btn-sm"> + <span class="glyphicon glyphicon-hand-right"></span> Lend + </button> + <button class="btn btn-default btn-sm"> + <span class="glyphicon glyphicon-pencil"></span> Edit + </button> + </td> + </tr> + </tbody> + </table> + </div> + +{% endblock %} \ No newline at end of file diff --git a/project/templates/movies/movie.html b/project/templates/movies/movie.html new file mode 100644 index 0000000000000000000000000000000000000000..256b9569cec8e094608d254e5e507705565960ee --- /dev/null +++ b/project/templates/movies/movie.html @@ -0,0 +1,38 @@ +{% extends "usernav.html" %} +{% block content %} + <div class="col-sm-12"> + <div class="row"> + <div class="col-xs-12 col-sm-3 col-md-2"> + <img data-src="holder.js/100%x200/sky" alt="movie title"> + <button type="button" class="btn btn-default btn-sm"> + <span class="glyphicon glyphicon-pencil"></span> Edit + </button> + <button type="button" class="btn btn-warning btn-sm"> + <span class="glyphicon glyphicon-remove"></span> Remove + </button> + </div> + + <div class="col-xs-12 col-sm-3 col-md-2"> + <ul class="list-unstyled"> + <li>Movie Title</li> + <li>Director(s)</li> + <li>Actors (truncated)</li> + <li>Producers</li> + <li>Composer</li> + </ul> + </div> + + <div class="col-xs-12 col-sm-6 col-md-8"> + <h2>Summary</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eget metus vitae mi interdum tristique. Integer malesuada quam tincidunt, gravida nisi vehicula, auctor mi. Morbi fermentum, neque quis adipiscing placerat, odio nunc consequat ligula, vel sagittis nisl orci id risus. Aliquam hendrerit, elit a molestie suscipit, eros tortor scelerisque odio, sit amet cursus ligula lorem et turpis. Pellentesque ante metus, suscipit id blandit eget, vehicula quis tortor. Aliquam a pharetra enim. Morbi in purus sit amet nulla egestas consectetur. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla diam nulla, iaculis lobortis varius at, auctor malesuada enim. Donec a nisl eget ligula rhoncus fermentum.</p> + </div> + </div> + + <div class="row"> + <div class="col-xs-12 col-sm-6 col-md-12"> + <h3>Movie Reviews</h3> + <p>Coming Soon!</p> + </div> + </div> + </div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/usernav.html b/project/templates/usernav.html new file mode 100644 index 0000000000000000000000000000000000000000..b875e665e3537b8b35ca4c01e21d8492313ce3ba --- /dev/null +++ b/project/templates/usernav.html @@ -0,0 +1,11 @@ +{% extends "index.html" %} + +{% block usernav %} + <p class="navbar-text">Hello, {{ user.email }}</p> + <ul class="nav navbar-nav navbar-right"> + <li><a href="{{ url_for('libraries') }}">Libraries</a></li> + <li><a href="{{ url_for('movies') }}">Movies</a></li> + <li><a href="{{ url_for('loaned') }}">Loaned Out</a></li> + <li><a href="{{ url_for('logout') }}">Logout</a></li> + </ul> +{% endblock %} \ No newline at end of file