diff --git a/project/__init__.py b/project/__init__.py index 1fe76c5a3c811955b94e4886a7105d0e19044fe4..ae31105a1ac099007c8887dfc21dbb6242a928c3 100644 --- a/project/__init__.py +++ b/project/__init__.py @@ -1,13 +1,17 @@ # -*- coding: utf-8 -*- __version__ = '0.1' from functools import wraps -from flask import Flask, session, redirect, url_for, request +from flask import Flask, session, redirect, url_for, request, render_template from flask_debugtoolbar import DebugToolbarExtension from flask.ext.mongoengine import MongoEngine import sys sys.path.append('../pytmdb3/') +from tmdb3 import set_key + +set_key('542606a6ccff81a0337dc370a0cbfc37') + app = Flask('project') app.config['SECRET_KEY'] = 'random' app.config['MONGODB_SETTINGS'] = {'DB': 'my_movie_library'} @@ -49,6 +53,5 @@ def security(role=None): 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 bc436e84445ba290263d35a1fad002adce263fa9..38299a6a4ea291b24fde9513aea1f2262f224af6 100644 --- a/project/controllers/home.py +++ b/project/controllers/home.py @@ -35,5 +35,4 @@ def model(): 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 index 350403f62247853ae3d06cc8889e8109b8a80fee..c8d57f3aee6010d4fec22fdcbf8e9c64d430d16c 100644 --- a/project/controllers/library.py +++ b/project/controllers/library.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from project import app, security -from flask import render_template, request, session +from flask import render_template, request, session, redirect, url_for,jsonify from flask.ext.wtf import Form, TextField, validators from project.model.Library import Library from project.model.User import User @@ -13,14 +13,104 @@ def libraries(user = None): libraries = Library.objects(user=user,unit='Movie') return render_template('library/master.html', libraries=libraries,user=user) +@app.route('/libraries/add', methods=['POST']) +@security('user') +def addLibrary(user = None): + name = request.form['name'] + library = Library.objects(user=user,unit='Movie',name=name).first() + if library: + return jsonify(response='error',message='Library with name %s already exists' % library.name),404 + library = Library(user=user,unit='Movie',name=name).save() + return jsonify(response='success',type='redirect',path=url_for(endpoint='libraries',_external=True)) + +@app.route('/libraries/remove', methods=['POST']) +@security('user') +def removeLibrary(user = None): + name = request.form['name'] + library = Library.objects(user=user,unit='Movie',name=name).first() + if not library: + return jsonify(response='error',message='Library requested does not exists'),404 + if library.name == 'Master' or library.name == 'Borrowed': + return jsonify(response='error',message='Library %s cannot be deleted' % library.name),404 + library.delete() + return jsonify(response='success',type='redirect',path=url_for(endpoint='libraries',_external=True)) + @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) + if not library: + return render_template('404.html',message='Unable to find given Library',user=user),404 + return render_template('library/library.html',library=library,user=user) @app.route('/libraries/<name>/<int:index>') @security('user') def libraryItem(name, index,user=None): - return render_template('library/libraryItem.html') + from project.model.Movie import Movie + library = Library.objects(user=user,name=name,unit='Movie').first() + if not library: + return render_template('404.html',message='Unable to find given Library',user=user),404 + movie = library.hydrateUnit(index-1) + if not movie: + return render_template('404.html',message='Unable to find given Movie',user=user),404 + return render_template('library/libraryItem.html',item=movie,user=user) + +@app.route('/libraries/<name>/remove', methods=['POST']) +@security('user') +def removelibraryItem(name,user=None): + from project.model.Movie import Movie + library = Library.objects(user=user,name=name,unit='Movie').first() + if not library: + return jsonify(response='error',message='Unable to find the given Library'),404 + index = int(request.form['id']) + if not index: + return jsonify(response='error',message='Invalid parameters'),404 + movie = library.hydrateUnit(index-1) + if not movie: + return jsonify(response='error',message='Unable to find the given Movie in Library %s' % library.name),404 + + if library.name == 'Master': + libraries = Library.objects(user=user,unit='Movie') + for library in libraries: + library.removeUnit(movie) + else: + library.removeUnit(movie) + + return jsonify(response='success',type='redirect',path=url_for(endpoint='library',name=name,_external=True)) + +@app.route('/libraries/<name>/add', methods=['POST']) +@security('user') +def addlibraryItem(name,user=None): + from project.model.Movie import Movie + + library = Library.objects(user=user,name=name,unit='Movie').first() + if not library: + return jsonify(response='error',message='Unable to find the given Library'),404 + + movie_id = request.form['id'] + if not movie_id: + return jsonify(response='error',message='Invalid Movie given'),404 + + from project.model.Movie import Movie + movie = Movie.objects(tmdb_id=movie_id).first() + if movie: + if library.name != 'Master': + master = Library.objects(user=user,name="Master",unit='Movie').first() + master.addUnit(movie) + library.addUnit(movie) + return jsonify(response='success',type='redirect',path=url_for(endpoint='library',name=name,_external=True)) + + from tmdb3 import Movie as tmdbMovie + movie = tmdbMovie(movie_id) + if not movie: + return jsonify(response='error',message='Invalid Movie given'),404 + + from project.model.Movie import Movie + movie = Movie.convertMovie(movie) + library.addUnit(movie) + if library.name != 'Master': + master = Library(user=user,name="Master",unit='Movie').first() + master.addUnit(movie) + + return jsonify(response='success',type='redirect',path=url_for(endpoint='library',name=name,_external=True)) \ No newline at end of file diff --git a/project/controllers/login.py b/project/controllers/login.py index 94102c871f001e34d24a19fe40cab3848865c380..9af30f6fbfd61dd79a2573a60ddcc94175153470 100644 --- a/project/controllers/login.py +++ b/project/controllers/login.py @@ -1,14 +1,11 @@ # -*- coding: utf-8 -*- from project import app -from flask import render_template, request, session, redirect, url_for +from flask import render_template, request, session, redirect, url_for, jsonify from flask.ext.wtf import Form, TextField, validators from project.model.User import User -@app.route('/login', methods=['GET', 'POST']) +@app.route('/login-ajax', methods=['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'] @@ -16,40 +13,32 @@ def login(): if user is not None: session['user'] = user.toJSON() - return redirect(url_for('libraries')) + return jsonify(response='success',type='redirect',path=url_for(endpoint='libraries',_external=True)) 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 jsonify(response='error',message=error),401 - return render_template('login/master.html') - -@app.route('/signup', methods=['GET', 'POST']) +@app.route('/signup-ajax', methods=['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: + if password != passwordConfirm: 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') + return jsonify(response='error',message=error),400 + + if len(User.objects(email=email)) > 0: + error = "The email provided is already in use with another account." + return jsonify(response='error',message=error),400 + + from project.model.Library import Library + 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 jsonify(response='success',type='redirect',path=url_for(endpoint='libraries',_external=True)) @app.route('/logout', methods=['GET', 'POST']) def logout(): diff --git a/project/controllers/movies.py b/project/controllers/movies.py index 0c071d34c6db059f850b6bb38fcccb4748c99097..07162632c13e4472ae7affb3960d1ac3aa8228e1 100644 --- a/project/controllers/movies.py +++ b/project/controllers/movies.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from project import app -from flask import render_template, request +from project import app, security +from flask import render_template, request, jsonify from flask.ext.wtf import Form, TextField, validators @@ -11,3 +11,32 @@ def movies(): @app.route('/movies/<movieId>') def movie_item(movieId): return render_template('movies/movie.html') + +@app.route('/search-movies', methods=['POST']) +@security('user') +def searchMovie(user=None): + term = request.form['term']; + if not term: + return jsonify(response='error',message='Invalid search term'),404 + from tmdb3 import searchMovie + movies = searchMovie(term) + if len(movies) == 0: + return jsonify(response='error',message='No results given'),404 + results = [] + limit = 10 + for index in range(len(movies)): + movie = movies[index] + if limit <= 0: + break + result = {} + poster = movie.poster + if poster: + sizes = poster.sizes() + if len(sizes) > 0: + result['poster'] = poster.geturl(sizes[0]) + result['title'] = movie.title + result['id'] = movie.id + results.append(result) + limit -= 1; + + return jsonify(response='success',movies=results) \ No newline at end of file diff --git a/project/model/Library.py b/project/model/Library.py index 70c924013904e888d27ab69687d9912302520d09..07ee137a9f896e12b16023e882b9ad589b55bf31 100644 --- a/project/model/Library.py +++ b/project/model/Library.py @@ -11,9 +11,10 @@ class Library(db.Document): def addUnit(self,unit): if self.unit == type(unit).__name__: - value = unit[self.lookup_attribute] + value = str(unit[self.lookup_attribute]) if value is not None and value not in self.collection: - self.collection.append("%s" % value) + self.collection.append(value) + self.save() else: return self else: @@ -22,9 +23,10 @@ class Library(db.Document): def removeUnit(self,unit): if self.unit == type(unit).__name__: - value = unit[self.lookup_attribute] + value = str(unit[self.lookup_attribute]) if value is not None and value in self.collection: - self.collection.remove("%s" % value) + self.collection.remove(value) + self.save() else: return self else: @@ -34,7 +36,7 @@ class Library(db.Document): # @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) + return None attr = {} attr[self.lookup_attribute] = self.collection[index] model = getattr(sys.modules["project.model.%s" % self.unit], self.unit) @@ -42,7 +44,6 @@ class Library(db.Document): 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 = {} diff --git a/project/model/Loan.py b/project/model/Loan.py new file mode 100644 index 0000000000000000000000000000000000000000..7247cad0409fb2b0d7a8029aa9850011b756f4a6 --- /dev/null +++ b/project/model/Loan.py @@ -0,0 +1,28 @@ +from project import db +import datetime +from User import User +from Movie import Movie + +class Loan(db.Document): + user = db.ReferenceField(User) + movie = db.ReferenceField(Movie, unique_with=['user']) + lent_date = db.DateTimeField(default=datetime.datetime.now, required=True) + expected_return_date = db.DateTimeField(default=datetime.datetime.now, required=True) + borrower_email = db.StringField() + + @staticmethod + def create(user,movie,email,expected_return_date=None): + info = Loan(user=user,movie=movie,email=email) + if expected_return_date: + info.expected_return_date = expected_return_date + else: + info.expected_return_date = info.expected_return_date + datetime.timedelta(days=7) + info.save() + return info + + + def __str__(self): + return "%s due %s" % (self.movie.title, self.expected_return_date.isoformat()) + + def __repr__(self): + return self.__str__() \ No newline at end of file diff --git a/project/model/Movie.py b/project/model/Movie.py index c6e0a1aff0e28fc256dce3173ef8e03992415140..0998b4141e117cccb37062f2b12d60146456f9c8 100644 --- a/project/model/Movie.py +++ b/project/model/Movie.py @@ -2,27 +2,53 @@ 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)) + 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)) + tmdb_id = db.IntField() + runtime = db.IntField() + poster = db.StringField() + popularity = db.FloatField() - def addTag(self,tag): - if tag not in self.tags: - self.tags.append(tag) - return self + 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 removeTag(self,tag): + if tag in self.tags: + self.tags.remove(tag) + return self - def __str__(self): - return self.title + def __str__(self): + return self.title - def __repr__(self): - return str(self.toJSON()) + def __repr__(self): + return self.__str__() - 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 + 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)}) + + @staticmethod + def convertMovie(movie): + result = Movie() + result.tmdb_id = int(movie.id) + result.title = str(movie.title) + result.summary = str(movie.overview.encode('utf-8')) + if movie.poster: + sizes = movie.poster.sizes() + if len(sizes) > 0: + medium = int(len(sizes)/2) + result.poster = str(movie.poster.geturl(sizes[medium])) + result.popularity = float(movie.popularity) + result.runtime = int(movie.runtime) + tags = movie.keywords + for tag in tags: + result.addTag(str(tag)) + genres = movie.genres + for genre in genres: + result.addTag(str(genre)) + result.save() + return result diff --git a/project/static/css/style.css b/project/static/css/style.css index dcbbde0d5ebb604aeccc10df8bc2141259d8766c..c803093cfdb9ece5c9c2c0a0ab36899bdebedf59 100644 --- a/project/static/css/style.css +++ b/project/static/css/style.css @@ -1,71 +1,51 @@ -body { - font-family: sans-serif; - background: #eee; +html,body { + height: 100%; } -a,h1,h2 { - color: #377BA8; +#wrap { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -60px; } -h1,h2 { - font-family: 'Georgia', serif; - margin: 0; +#push, +#footer { + height: 60px; } -h1 { - border-bottom: 2px solid #eee; +@media (max-width: 767px) { + #footer { + margin-left: -20px; + margin-right: -20px; + padding-left: 20px; + padding-right: 20px; + } } -h2 { - font-size: 1.2em; +.adjust-left { + padding-right: 5%; } -.page { - margin: 2em auto; - width: 35em; - border: 5px solid #ccc; - padding: 0.8em; - background: white; +.movie-select { + padding: 10px 15px 0px 20px; + border-radius: 15px; + background-color: darkslategray; + margin: 5px; + width: 30%; + height: 200px; + overflow: hidden; } -.entries { - list-style: none; - margin: 0; - padding: 0; +.movie-select.selected { + background-color: slategray; } -.entries li { - margin: 0.8em 1.2em; +.clearfix { + float: none !important; } -.entries li h2 { - margin-left: -1em; -} - -.add-entry { - font-size: 0.9em; - border-bottom: 1px solid #ccc; -} - -.add-entry dl { - font-weight: bold; -} - -.metanav { - text-align: right; - font-size: 0.8em; - padding: 0.3em; - margin-bottom: 1em; - background: #fafafa; -} - -.flash { - background: #CEE5F5; - padding: 0.5em; - border: 1px solid #AACBE2; -} - -.error { - background: #F0D6D6; - padding: 0.5em; +#results { + max-height: 500px; + overflow-y: scroll; } \ No newline at end of file diff --git a/project/templates/404.html b/project/templates/404.html new file mode 100644 index 0000000000000000000000000000000000000000..d9366f72ef9384b50cd1f376f1d65f47b99c4288 --- /dev/null +++ b/project/templates/404.html @@ -0,0 +1,14 @@ +{% extends "usernav.html" if user else 'index.html'%} +{% block title %}Page Not Found{% endblock %} +{% block content %} + <div style="width: 400px;margin: 0 auto;"> + <h1>Page Not Found</h1> + <p> + {% if message %} + {{ message }} + {% else %} + What you were looking for is just not there. + {% endif %} + </p> +</div> +{% endblock %} \ No newline at end of file diff --git a/project/templates/home/home.html b/project/templates/home/home.html index 534d0d9e5fc2ef1e50b0c0f4651cffda3d52ce29..b0442808195c905202477212fb0afef7af4ec419 100644 --- a/project/templates/home/home.html +++ b/project/templates/home/home.html @@ -27,3 +27,8 @@ </div> </div> {% endblock %} + +{% block modals %} + {% include "home/login_modal.html" %} + {% include "home/signup_modal.html" %} +{% endblock %} diff --git a/project/templates/home/login_modal.html b/project/templates/home/login_modal.html new file mode 100644 index 0000000000000000000000000000000000000000..383645142825318938ff9cab3cfdc4114f3180b2 --- /dev/null +++ b/project/templates/home/login_modal.html @@ -0,0 +1,32 @@ +{% extends "modal_base.html" %} +{% block modal_title %} Login {% endblock %} +{% block modal_content %} + <form class="form-horizontal" id="login" 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> + </form> +{% endblock %} +{% block accept_button_text %}Submit{% endblock %} +{% block ajax_url %}'{{ url_for('login') }}'{% endblock %} +{% block form_id %}login{% endblock %} +{% block validation_rules %} +rules: { + // simple rule, converted to {required:true} + password: "required", + // compound rule + email: { + required: true, + email: true + } +} +{% endblock %} \ No newline at end of file diff --git a/project/templates/home/signup_modal.html b/project/templates/home/signup_modal.html new file mode 100644 index 0000000000000000000000000000000000000000..2d354027363608e6209b5363636751873e82684b --- /dev/null +++ b/project/templates/home/signup_modal.html @@ -0,0 +1,41 @@ +{% extends "modal_base.html" %} +{% block modal_title %} Sign Up {% endblock %} +{% block modal_content %} + <form class="form-horizontal" id="signup" 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> + </form> +{% endblock %} +{% block accept_button_text %}Submit{% endblock %} +{% block ajax_url %}'{{ url_for('signup') }}'{% endblock %} +{% block form_id %}signup{% endblock %} +{% block validation_rules %} +rules: { + password: "required", + passwordConfirm: { + required: true, + minlength: 5, + equalTo: "#userPassword" + }, + email: { + required: true, + email: true + } +} +{% endblock %} \ No newline at end of file diff --git a/project/templates/index.html b/project/templates/index.html index 285b5a0bffdf0b8171d2e184cda96efa1e37da9c..d28cb64e2edb6c9fe05f3efe83b611f1e48976d9 100644 --- a/project/templates/index.html +++ b/project/templates/index.html @@ -5,16 +5,17 @@ <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>My Movie Library</title> + <title>{% block title %}My Movie Library{% endblock %}</title> <link rel="shortcut icon" href="{{ url_for('static', filename='images/favicon.ico') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-slate.min.css') }}"> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <!-- holderJS --> <script src="{{ url_for('static', filename='lib/holder.js') }}"></script> </head> <body> - <div class="page"> + <div id="wrap" class="page"> <div class="container"> <!-- Begin the navbar --> <nav class="navbar navbar-default" role="navigation"> @@ -38,8 +39,8 @@ {% 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> + <li><a data-target="#login-form" data-toggle="modal" >Login</a></li> + <li><a data-target="#signup-form" data-toggle="modal" >Sign-up</a></li> </ul> {% endblock %} </div><!-- /.navbar-collapse --> @@ -50,21 +51,28 @@ {% block content %}{% endblock %} </div> + <div id="push"></div> + </div> + </div> + <div id="footer"> + <div class="content"> <div class="panel panel-default"> - <div class="panel-body"> - Open Source by Brian Wood & Nathan Bills, 2014 - </div> + <div class="panel-body"> + Open Source by Brian Wood & Nathan Bills, 2014 </div> + </div> </div> + </div> + <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> + <script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.12.0/jquery.validate.min.js"></script> + <!-- Include all compiled plugins (below), or include individual files as needed --> + <script src="{{ url_for('static', filename='lib/bootstrap.min.js') }}"></script> + {% block javascript %} + {% endblock %} - <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> - <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> - <!-- Include all compiled plugins (below), or include individual files as needed --> - <script src="{{ url_for('static', filename='lib/bootstrap.min.js') }}"></script> - - {% block javascript %} - {% endblock %} - </div> + {% block modals %} + {% endblock %} </body> </html> diff --git a/project/templates/library/addlibrary_modal.html b/project/templates/library/addlibrary_modal.html new file mode 100644 index 0000000000000000000000000000000000000000..dc62d12ac7098254916c4ca7c0d80f6efab4b420 --- /dev/null +++ b/project/templates/library/addlibrary_modal.html @@ -0,0 +1,20 @@ +{% extends "modal_base.html" %} +{% block modal_title %} Add Library {% endblock %} +{% block modal_content %} + <form class="form-horizontal" id="addLibrary" role="form" method="post" action="{{ url_for('addLibrary') }}"> + <div class="form-group"> + <label for="name" class="col-sm-3 control-label">Name</label> + <div class="col-xs-9 col-md-6"> + <input type="text" class="form-control" name="name" placeholder="New Library Name"> + </div> + </div> + </form> +{% endblock %} +{% block accept_button_text %}Submit{% endblock %} +{% block ajax_url %}'{{ url_for('addLibrary') }}'{% endblock %} +{% block form_id %}addLibrary{% endblock %} +{% block validation_rules %} +rules: { + name: "required", +} +{% endblock %} \ No newline at end of file diff --git a/project/templates/library/addmovie_modal.html b/project/templates/library/addmovie_modal.html new file mode 100644 index 0000000000000000000000000000000000000000..191c7a380c52f6bf2617ada10e82d3f91f387a73 --- /dev/null +++ b/project/templates/library/addmovie_modal.html @@ -0,0 +1,83 @@ +{% extends "modal_base.html" %} +{% block modal_title %} Add Movie {% endblock %} +{% block modal_content %} + <div class="row"> + <div class="pull-right adjust-left"> + <form id="movieSearch "class="navbar-form" role="search" method="post" action="{{ url_for('searchMovie') }}"> + <div class="form-group"> + <input type="text" name="term" class="form-control" placeholder="Title, keyword, etc."> + </div> + <button type="submit" id="search-accept" class="btn btn-default">Search</button> + </form> + </div> + </div> + <div id="results" class="clearfix well col-md-10 col-md-offset-1"> + Search for your movie! + </div> + <div class="hidden"> + <form class="hidden" id="addMovie" method="post" action="{{ url_for('addlibraryItem',name=library.name) }}"> + <div class="form-group"> + <div class="col-xs-9 col-md-6"> + <input type="hidden" name="id"> + </div> + </div> + </form> + </div> +{% endblock %} +{% block accept_button_text %}Submit{% endblock %} +{% block ajax_url %}'{{ url_for('addlibraryItem',name=library.name) }}'{% endblock %} +{% block form_id %}addMovie{% endblock %} +{% block additional_javascripts %} + <script type="text/javascript"> + $(function(){ + var $addForm = $(document.getElementById("addMovie")); + var $acceptButton = $(document.getElementById("search-accept")); + var $form = $acceptButton.parent(); + var $results = $(document.getElementById("results")); + $acceptButton.on('click',function(event){ + event.preventDefault(); + $.ajax({ + url: "{{ url_for('searchMovie') }}", + data: $form.serialize(), + method: 'POST', + success: function(data,status,jqXHR){ + movies = data['movies']; + listMovies($results,movies); + $results.on('click','.movie-select',function(){ + var id = $(this).data('id'); + $('.selected').removeClass('selected'); + $(this).addClass('selected'); + $addForm.find('input').val(id); + }); + }, + error: function(jqXHR, textStatus, errorThrown){ + $results.html('There was an error with your search please try again') + } + }); + }); + $("#search input").keyup(function(event){ + if(event.keyCode == 13){ + $acceptButton.click(); + } + }); + }); + function listMovies(list,movies){ + var html = ""; + for (var i = movies.length - 1; i >= 0; i--) { + movie = movies[i]; + html += "<div title='"+movie['title']+"' class='col-md-4 movie-select' data-id='"+movie['id']+"'>"; + html += "<img class='img-thumbnail' src='"+movie['poster']+"'/>"; + html += "<h6>"+movie['title']+"</h6>"; + html += "</div>"; + }; + list.html(html); + }; + </script> +{% endblock %} +{% block validation_rules %} +rules: { + id: "required", + errorLabelContainer: "#addMovie-error" +} +{% endblock %} + \ No newline at end of file diff --git a/project/templates/library/library.html b/project/templates/library/library.html index c77414423a0c6a7a449f9863cee7ab4f1203d1c7..f8ccba06e3d93b9db262c66a04ca2efc7aa75a5c 100644 --- a/project/templates/library/library.html +++ b/project/templates/library/library.html @@ -5,7 +5,7 @@ <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> + <button type="button" class="btn btn-default add-movie" data-toggle="modal" data-target="#addMovie-form">Add Movie</button> </div> <table class="table table-striped table-hover"> <thead> @@ -23,9 +23,9 @@ </tr> {% else %} {% for movie in library.hydrateList() %} - <tr> + <tr class='clickable' data-id='{{ loop.index }}'> <td> - <img src="{{ movie.image|default('') }}" style="max-width: 200px;" alt="{{ movie.title }}"> + <img src="{{ movie.poster|default('') }}" style="max-width: 200px;" alt="{{ movie.title }}"> <p>{{ movie.title }}</p> </td> <td> @@ -36,9 +36,9 @@ </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> + <button type="button" class="btn btn-default btn-small lend-movie" data-id='{{ loop.index }}'>Lend</button> + <button type="button" class="btn btn-default btn-small borrow-movie" data-id='{{ loop.index }}'>Edit</button> + <button type="button" class="btn btn-default btn-small remove-movie" data-id='{{ loop.index }}'>Remove</button> </div> </td> </tr> @@ -48,4 +48,55 @@ </tbody> </table> </div> +{% endblock %} + +{% block javascript %} + <script> + $(function(){ + var libraryPath = '{{ url_for("libraryItem", name=library.name, index="999") }}'; + $('.clickable').on('click',function(){ + var movie_id = $(this).data('id'); + window.document.location = libraryPath.replace('999',movie_id); + }); + + var removePath = '{{ url_for("removelibraryItem", name=library.name) }}'; + $('.remove-movie').on('click',function(event){ + var movie_id = $(this).data('id'); + {% if library.name == 'Master' %} + var message = "Removing a movie from your Master list will remove it from all other lists. Are you sure you want remove this movie? "; + {% else %} + var message = "Are you sure you want remove this movie? "; + {% endif %} + if(confirm(message)){ + event.stopPropagation(); + event.preventDefault(); + $.ajax({ + url: removePath, + data: {id:movie_id}, + method: 'POST', + success: function(data,status,jqXHR){ + if(data['response'] == 'success'){ + if(data['type'] == 'redirect'){ + window.document.location = data['path']; + } else if (data['type'] == 'reload') { + window.document.location.reload(); + } + } + }, + error: function(jqXHR, textStatus, errorThrown){ + response = jqXHR.responseJSON; + if('message' in response){ + alert(response['message']); + } + } + }); + + } + }); + }); + </script> +{% endblock %} + +{% block modals %} + {% include 'library/addmovie_modal.html' %} {% endblock %} \ No newline at end of file diff --git a/project/templates/library/master.html b/project/templates/library/master.html index 883ff8306d02ead92d8e3319eb7304f5c6231405..79d925a4bf681c5baae8bb3ff2f7e2e349fc187f 100644 --- a/project/templates/library/master.html +++ b/project/templates/library/master.html @@ -1,7 +1,12 @@ {% extends "usernav.html" %} {% block content %} <div class="col-sm-12"> - <h3>My Libraries</h3> + <div class="col-md-4"> + <h3>My Libraries</h3> + </div> + <div class="btn-group pull-right"> + <button type="button" class="btn btn-default add-movie" data-toggle="modal" data-target="#addLibrary-form">Add Library</button> + </div> <table class="table table-striped table-hover"> <thead> <tr> @@ -24,9 +29,9 @@ {{ library.collection|length }} </td> <td> - <button class="btn btn-default btn-sm"> - <span class="glyphicon glyphicon-pencil"></span> Edit - </button> + {% if library.name != 'Master' and library.name != 'Borrowed' %} + <button type="button" class="btn btn-default btn-small remove-library" data-name='{{ library.name }}'>Delete</button> + {% endif %} </td> </tr> {% endfor %} @@ -35,6 +40,10 @@ </div> {% endblock %} +{% block modals %} + {% include 'library/addlibrary_modal.html' %} +{% endblock %} + {% block javascript %} <script> $(function(){ @@ -42,7 +51,42 @@ $('.clickable').on('click',function(){ var libname = $(this).data('name'); window.document.location = libraryPath.replace('__lib__',libname); - }) - })(jQuery); + }); + + var removePath = '{{ url_for("removeLibrary") }}'; + $('.remove-library').on('click',function(event){ + event.stopPropagation(); + event.preventDefault(); + var library_name = $(this).data('name'); + var message = "Are you sure you want to delete this Library?"; + if(library_name == "Master" || library_name == "Borrowed"){ + alert("You cannot delete the "+library_name+" library."); + return false; + } + if(confirm(message)){ + $.ajax({ + url: removePath, + data: {name:library_name}, + method: 'POST', + success: function(data,status,jqXHR){ + if(data['response'] == 'success'){ + if(data['type'] == 'redirect'){ + window.document.location = data['path']; + } else if (data['type'] == 'reload') { + window.document.location.reload(); + } + } + }, + error: function(jqXHR, textStatus, errorThrown){ + response = jqXHR.responseJSON; + if('message' in response){ + alert(response['message']); + } + } + }); + + } + }); + }); </script> {% endblock %} \ No newline at end of file diff --git a/project/templates/loan/master.html b/project/templates/loan/master.html index d0318605d0cd1a586f59619749941840ea21fc44..18204e2d0cfbfbd5d1114a775aa17840db55a2f3 100644 --- a/project/templates/loan/master.html +++ b/project/templates/loan/master.html @@ -18,10 +18,11 @@ <p>The Bourne Identity</p> </td> <td> - <p>Brian Wood</p> + <!-- Maybe this can link to a modal that shows more information about the borrower??? --> + <p><a href="#">Brian Wood</a></p> </td> <td> - <p>Thur, April 17, 2014/p> + <p>Thur, April 17, 2014</p> </td> <td> <p>Thur, May 1, 2014</p> @@ -32,6 +33,26 @@ </button> </td> </tr> + <tr> + <td> + <p>Frozen</p> + </td> + <td> + <!-- Maybe this can link to a modal that shows more information about the borrower??? --> + <p><a href="#">Grandma Bills</a></p> + </td> + <td> + <p>Sun, April 20, 2014</p> + </td> + <td> + <p>Sun, May 4, 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> diff --git a/project/templates/login/master.html b/project/templates/login/master.html deleted file mode 100644 index 0e2db7ccc65f381e5dbbcd94fbc881f0fba80ef5..0000000000000000000000000000000000000000 --- a/project/templates/login/master.html +++ /dev/null @@ -1,36 +0,0 @@ -{% 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 deleted file mode 100644 index 9ac427f280fa14e9d891a684ccdc93c84fe1d8e3..0000000000000000000000000000000000000000 --- a/project/templates/login/signup.html +++ /dev/null @@ -1,42 +0,0 @@ -{% 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/modal_base.html b/project/templates/modal_base.html new file mode 100644 index 0000000000000000000000000000000000000000..777f7b6e7bd5693bd44a4b873d2feb5cb70e6ae3 --- /dev/null +++ b/project/templates/modal_base.html @@ -0,0 +1,82 @@ +{% set modal_body_class = modal_body_class|default ~ ' modal-body' %} +<div class="modal fade" id="{% block form_id %}{% endblock %}-form"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3 class="blue bigger">{% block modal_title %}{% endblock %}</h3> + </div> + <div class="{{ modal_body_class }}"> + <div class="alert alert-danger hide {{ self.form_id()}}-error"> + </div> + {% block modal_content %}{% endblock %} + </div> + <div class="modal-footer"> + {% block footer %} + {% block cancel_button %} + <button class="btn btn-default" data-dismiss="modal" type="button"> + <i class="icon-remove"></i> + {% block cancel_button_text %}Cancel{% endblock %} + </button> + {% endblock %} + {% block accept_button %} + <button class="btn btn-small btn-primary" id="{{ self.form_id() }}-accept" type="submit"> + <i class="icon-ok"></i> + {% block accept_button_text %}Save Changes{% endblock %} + </button> + {% endblock %} + {% endblock %} + </div> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> +</div><!-- /.modal --> +{% block javascripts %} + <script type="text/javascript"> + $(function(){ + var $form = $(document.getElementById("{{ self.form_id() }}")); + var $acceptButton = $(document.getElementById("{{ self.form_id() }}-accept")); + $acceptButton.on('click',function(){ + $.ajax({ + url: {% block ajax_url %}{% endblock %}, + data: $form.serialize(), + method: 'POST', + beforeSend: function(){ + $form.validate({ + {% block validation_rules %} + + {% endblock %} + }); + + return $form.valid(); + }, + success: function(data,status,jqXHR){ + {% block ajax_success %} + if(data['response'] == 'success'){ + if(data['type'] == 'redirect'){ + window.document.location = data['path']; + } else if (data['type'] == 'reload') { + window.document.location.reload(); + } + } + {% endblock %} + }, + error: function(jqXHR, textStatus, errorThrown){ + {% block ajax_fail %} + response = jqXHR.responseJSON; + $(".{{ self.form_id() }}-error").html(response['message']).removeClass('hide'); + {% endblock %} + } + }); + }); + $("#{{ self.form_id() }} input").keydown(function(event){ + if(event.keyCode == 13){ + event.preventDefault(); + event.stopPropagation(); + $acceptButton.click(); + } + }); + }); + </script> +{% endblock %} +{% block additional_javascripts %} +{% endblock %}