Commits

Sergio Berlotto  committed d92b063

Muitos ajustes para o bom funcionamento

  • Participants
  • Parent commits 93dcf1e

Comments (0)

Files changed (63)

File asciiblog.py

 from flask import Flask, render_template, request, jsonify, redirect, url_for
 from flask.ext.sqlalchemy import SQLAlchemy
 from werkzeug.contrib.cache import MemcachedCache, SimpleCache
+from general.models import Link, Like
 from jinja2 import evalcontextfilter, Markup, escape
+from flask.ext.login import login_required, current_user
 import markdown
 import util
+import traceback
 import os
 
 #========================================= CONFIGURATION
 try:
 	app.config.from_pyfile('asciiblog.cfg')
 except:
-	print('-> Config from env vars')
-	# Generate with os.urandom(24)
-	SECRET_KEY=os.environ.get('SECRET_KEY','O\xde\xbe\xe5\x18\xa3\x18\xcdFos\xd1\x03(\xba\xd59+\x97&\xa2D\x9cb')
-	#Folder where files wil be uploaded
-	UPLOADED_POSTFILE_DEST = os.environ.get('UPLOADED_POSTFILE_DEST', './static/uploads/')
-	#Url for access the uploaded files
-	UPLOADED_FILE_URL=os.environ.get('UPLOADED_FILE_URL','/static/uploads/')
-	SESSION_COOKIE_NAME=os.environ.get('SESSION_COOKIE_NAME','asciiblog')
-	SESSION_COOKIE_SECURE=False
-	POSTS_PER_PAGE=os.environ.get('POSTS_PER_PAGE',5)
-	SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI','postgresql://asciiblog:asciiblog@localhost:5432/asciiblog')
-	TWITTER_CONSUMER_KEY=os.environ.get('TWITTER_CONSUMER_KEY',"")
-	TWITTER_CONSUMER_SECRET=os.environ.get('TWITTER_CONSUMER_SECRET',"")
-	TWITTER_ACCESS_TOKEN=os.environ.get('TWITTER_ACCESS_TOKEN',"")
-	TWITTER_ACCESS_TOKEN_SECRET=os.environ.get('TWITTER_ACCESS_TOKEN_SECRET',"")
-	pass
-
+	print('-> Config file not found!')
 
 db = SQLAlchemy(app)
 
 cache = SimpleCache()
 #cache = MemcachedCache(['127.0.0.1:11211'])
 
+#+======================================== CONTEXT PROCESSORS
+@app.context_processor
+def inject_user():
+	#Adiciona o usuario corrent no contexto dos templates
+    return dict(user=current_user)
+
+@app.context_processor
+def inject_verify_login():
+	def is_authenticated():
+		if current_user and current_user.is_active() :
+			return True
+		else:
+			return False
+	return dict(is_authenticated=is_authenticated)
+
+@app.context_processor
+def inject_current_url():
+	def current_url():
+		return request.path
+	return dict(current_url=current_url)
+
 #========================================= CUSTOM FILTERS FOR JINJA
 @app.template_filter()
 @evalcontextfilter
 @evalcontextfilter
 def gravatar(env, email):
 	import urllib, hashlib
-	default = 'http://localhost:8000'+url_for('static', filename='img/default_gravatar.png')
+	default = 'http://'+app.config['SERVER_NAME']+url_for('static', filename='img/default_gravatar.png')
 	print default
 	size = 40
 
 from blog.models import Post, Comment
 from media import media
 from pages import pages
+from auth import auth, init_auth
 
 #========================================= UPLOADS
 from flaskext.uploads import UploadSet, configure_uploads, IMAGES, UploadNotAllowed
 
-uploaded_files = UploadSet('postfile')
-configure_uploads(app, uploaded_files)
+uploaded_files = UploadSet('postfile', IMAGES, default_dest=lambda app: 'post-images')
+uploaded_media = UploadSet('media', default_dest=lambda app: 'uploaded-media')
+configure_uploads(app, (uploaded_files, uploaded_media))
 
 #========================================= BLUEPRINTS APPS
+app.register_blueprint(auth, url_prefix='/user')
 app.register_blueprint(blog, url_prefix='/blog')
 blog.register_uploader(uploaded_files)
 app.register_blueprint(media, url_prefix='/media')
 app.register_blueprint(pages, url_prefix='/pages')
 
+init_auth(app)
+
+#========================================= ERROR TEMPLATES
+
+@app.errorhandler(404)
+def page_not_found(e):
+    return render_template('404.html'), 404
+
+@app.errorhandler(500)
+def page_not_found(e):
+    return render_template('500.html'), 500
+
+@app.errorhandler(401)
+def page_not_found(e):
+    return render_template('401.html'), 401
+
+@app.errorhandler(400)
+def page_not_found(e):
+    return render_template('400.html'), 400
 
 #========================================= MAIN ROUTES
 @app.route('/')
 def index():
 	twitts, posts, comments, photo, random_posts = util.last_contents()
-	return render_template('home.html', posts=posts, comments=comments, twitts=twitts, 
+	# fp = cache.get('featured_posts')
+	# if not fp:
+	featured_posts = Post.query.filter(Post.featured=='Y')
+		#cache.set('featured_posts', featured_posts)
+	return render_template('home.html', posts=posts, featured_posts=featured_posts, 
+		                                comments=comments, twitts=twitts, 
 		                                flickr_photo=photo, random_posts=random_posts)
 
 @app.route('/slugfy/')
 	'''
 	return redirect( url_for('pages.view', slug=slug))
 
+#========================================= ROUTE FOR LINKS
+@app.route('/links/')
+@login_required
+def links():
+	try:
+		page = "1"
+		if 'page' in request.args:
+			page = request.args['page']
+		links = Link.query.order_by('id').paginate(page=int(page), per_page=app.config['POSTS_PER_PAGE']).items
+		# links = []
+	except:
+		traceback.print_exc()
+	return render_template('links.html', links=links)
+
+@app.route('/link/edit/<int:link_id>')
+@login_required
+def edit_link(link_id):
+	pass
+
+@app.route('/link/delete/<int:link_id>')
+@login_required
+def delete_link(link_id):
+	pass
+
+#========================================= ROUTE FOR FEEDS
+@app.route('/feed/')
+@login_required
+def feed():
+	pass
 
 #========================================= MAIN APP
 if __name__ == '__main__':
-    port = int(os.environ.get('PORT', 5000))
-    app.run(host='0.0.0.0', port=port)
+	app.run(host='0.0.0.0', debug=True)

File auth/__init__.py

+#! -*- encoding: utf8 -*-
+from flask import Blueprint, current_app, render_template, abort, flash, request, url_for, redirect, session, make_response, jsonify
+from flask.ext.login import LoginManager, login_user, current_user, logout_user
+from flask.ext.sqlalchemy import SQLAlchemy
+from sqlalchemy.orm.exc import NoResultFound
+from models import User
+import hashlib
+
+auth = Blueprint('auth', __name__, template_folder='templates', static_folder='static')
+
+db = SQLAlchemy()
+
+login_manager = LoginManager()
+
+def init_auth(app):
+	login_manager.setup_app(app)
+
+@login_manager.user_loader
+def load_user(userid):
+	return User.query.filter(User.id==userid).one()
+
+@auth.route('/logout/')
+def logout():
+	logout_user()
+	next = request.args.get('next') or request.referrer or url_for("index")
+	return redirect(next)
+
+@auth.route("/login/", methods=["GET", "POST"])
+def login():
+	next = request.args.get('next') or request.referrer or url_for("index")
+	print "VAI PARA:", next
+	if request.method == 'POST':
+		print "Login POST"
+		usuario, senha = request.form['inputLogin'], request.form['inputPassword']
+		if usuario and senha:
+			# login and validate the user...
+			try:
+				user = User.query.filter(User.login==usuario).one()
+				if user:
+					hash_senha = hashlib.md5( senha ).hexdigest()
+					if hash_senha == user.password:
+						login_user(user)
+						flash("Login efetuado com sucesso.")
+						return redirect(next)
+					else:
+						flash("Usuario ou senha incorretos.")
+				else:
+					flash("Usuario ou senha incorretos")
+			except NoResultFound:
+				flash("Usuario ou senha incorretos")
+	return render_template("login.html",next=next)

File auth/models.py

+from flask import current_app
+# from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
+from flask.ext.sqlalchemy import SQLAlchemy
+# from database import Base
+
+db = SQLAlchemy()
+
+class User(db.Model):
+	__tablename__ = 'user'
+	id = db.Column(db.Integer, db.Sequence('user_id'), primary_key=True)
+	name = db.Column(db.String(150))
+	login = db.Column(db.String(500), unique=True)
+	password = db.Column(db.String(1000))
+	email = db.Column(db.String(1000)) #may be 'S' or ('N','')
+
+	def is_active(self):
+		return True
+
+	def is_authenticated(self):
+		return True
+
+	def is_anonymous(self):
+		return False
+
+	def get_id(self):
+		return self.id
+
+	def __repr__(self):
+		return '<User %r>' % (self.login)

File auth/templates/login.html

+{% extends "base.html" %}
+
+{% block head %}
+<link rel="stylesheet" href="{{ url_for('auth.static', filename='css/auth.css') }}"/>
+<script src="{{ url_for('auth.static', filename='js/auth.js') }}"></script>
+{% endblock %}
+
+{% block content %}
+
+	<section class="span12">
+
+{% with messages = get_flashed_messages() %}
+  {% if messages %}
+    {% for message in messages %}
+      <div class="alert alert-error">{{ message }}</div>
+    {% endfor %}
+  {% endif %}
+{% endwith %}
+
+
+		<form class="form-horizontal offset3" method="POST" action="/user/login/{%if next%}?next={{next}}{%endif%}">
+			<div class="control-group">
+				<label class="control-label" for="inputLogin">Usuário</label>
+				<div class="controls">
+					<input type="text" name="inputLogin" id="inputLogin" placeholder="Usuario">
+				</div>
+			</div>
+			<div class="control-group">
+				<label class="control-label" for="inputPassword">Senha</label>
+				<div class="controls">
+					<input type="password" name="inputPassword" id="inputPassword" placeholder="Senha">
+				</div>
+			</div>
+			<div class="control-group">
+				<div class="controls">
+					<button type="submit" class="btn">Entrar</button>
+				</div>
+			</div>
+		</form>
+
+	</section>
+
+	
+
+{% endblock %}
+
+
+{% block bottompage %}
+
+
+{% endblock %}

File blog/__init__.py

 # -*- encoding: utf-8 -*-
 from flask import Blueprint, current_app, render_template, abort, flash, request, url_for, redirect, session, make_response, jsonify
-from models import Post, Comment, Like, Link
+from models import Post, Comment
+from general.models import Link, Like
 # from database import db_session
 from datetime import datetime
 from werkzeug.contrib.cache import MemcachedCache, SimpleCache
 from sqlalchemy.exc import IntegrityError
 from sqlalchemy.orm.exc import NoResultFound
 from flaskext.uploads import UploadNotAllowed
+from flask.ext.login import login_required, current_user
 import sys
 import util
+import traceback
+
 
 #Deve ser configuravel atraves do asciiblog.cfg
 cache = SimpleCache()
 	return render_template('list-posts.html', posts=posts)
 
 @blog.route('/new-post')
+@login_required
 def new_post():
 	return render_template('new-post.html')
 
 @blog.route('/edit-post/<int:post_id>')
+@login_required
 def edit_post(post_id):
 	post = Post.query.filter(Post.id==post_id).one()
 	if post:
 		if not post:
 			post = Post.query.filter(Post.slug==slug).one()
 			cache.set('view-post-%s' % slug, post)
-		return render_template('one-post.html',post=post, sidebar=util.last_contents())
+		comentarios = Comment.query.filter(Comment.post_id==post.id,Comment.display==True)
+		return render_template('one-post.html',post=post, comentarios=comentarios, sidebar=util.last_contents())
 	except NoResultFound as nrf:
 		return abort(404)
 
-@blog.route('/save-comment',methods = ['POST',])
+@blog.route('/savecomment',methods = ['POST',])
 def save_comment():
-	return "Ok"
+	msg = "Comment not saved."
+	try:
+		nome = request.form['name']
+		email = request.form['email']
+		texto = request.form['content']
+		post_id = request.form['post_id']
+		comentario = Comment()
+		comentario.name = nome
+		comentario.email = email
+		comentario.content = texto
+		comentario.display = False
+		comentario.date_created = datetime.today()
+		comentario.post_id = post_id
+		db.session.add(comentario)
+		db.session.commit()
+		msg = "Coment&aacute;rio enviado! Obrigado!"
+	except:
+		db.session.rollback()
+		traceback.print_exc()
+		#raise
+	data = {}
+	data['message'] = msg
+	return jsonify(data)
+
 
 @blog.route('/save-post',methods = ['POST',])
+@login_required
 def save_post():
 	try:
 		# print "RECEBIDO", request.form
 		content = request.form['content']
 		featured = 'N'
 		if "featured" in request.form:
-			featured = request.form['featured']
+			featured = 'Y'
+			#Limpa o cache para atualizar na proxima chamada da capa.
+			#cache.delete("featured_posts")
+			print "FEATURED POST"
 		resume = request.form['resume']
 		slug = request.form['slug']
 		tags = request.form['tags']
 
-		add = False;
+		add = False
 		if 'id' in request.form:
 			post = Post.query.get(request.form['id'])
 			cache.delete('view-post-%s' % slug) #Remove this post from cache for update in next view
 			add =  True
 			post = Post()
 			post.date_created = datetime.today()
+			post.short_url = util.encurtar(url_for('blog.view_post',  slug=slug))
 
 		post.title = title
 		post.content = content
 		post.resume = resume
-		post.featured = featured or 'N'
+		post.featured = featured
 		post.slug = slug
 		post.date_updated = datetime.today()
-		post.picture = ''
+		#post.picture = '' #don't clear this field if edit
 		post.tags = tags
 
 		try:
 			global uploaded_files
 			photo = request.files.get('postfile')
 			if photo:
+				print "SALVANDO A IMAGEM DO POST"
 				filename = uploaded_files.save(photo)
-				post.picture = filename
+				post.picture = uploaded_files.path(filename)
+				post.picture_url = uploaded_files.url(filename)
+				#print 'UPLOADED:', uploaded_files.path(filename)
+
+				#Gerar um thumbnail para mostrar dentro do post
+				#200x150 - Dentro do post
+				#220x100 - Lista de posts
+			else:
+				print "POST SEM IMAGEM"
 
 		except UploadNotAllowed:
 			flash("The upload was not allowed")
 
 		if add:
 			db.session.add(post)
+		else:
+			db.session.merge(post)
 		db.session.commit()
 		# flash('Post salvo com sucesso')
 		# return redirect(url_for('blog.view_post',  slug=slug))

File blog/models.py

 
 class Post(db.Model):
 	__tablename__ = 'post'
-	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+	id = db.Column(db.Integer, db.Sequence('post_id'), primary_key=True)
 	title = db.Column(db.String(150))
 	slug = db.Column(db.String(500), unique=True)
 	resume = db.Column(db.String(1000))
 	content = db.Column(db.Text)
 	date_created = db.Column(db.DateTime)
 	date_updated = db.Column(db.DateTime)
-	picture	= db.Column(db.Integer, db.ForeignKey("file.id")) #id if File that is used for banner
+	picture	= db.Column(db.String(2000))  #Complete path of picture file
+	picture_url	= db.Column(db.String(2000))  #The static url of picture file
+	short_url	= db.Column(db.String(1000))  #The short url if exists
 	featured = db.Column(db.String(1)) #may be 'S' or ('N','')
 	tags = db.Column(db.String(4000))
 
 		return '<Post %r>' % (self.slug)
 
 
-class File(db.Model):
-	"""This model represents the media files uploaded from user"""
-	__tablename__ = "file"
-	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
-	title = db.Column(db.String(150))
-	name = db.Column(db.String(1000))
-
 class Comment(db.Model):
 	__tablename__ = 'comment'
-	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+	id = db.Column(db.Integer,  db.Sequence('comment_id'), primary_key=True)
+	name = db.Column(db.String(150))
+	email = db.Column(db.String(500))
 	content = db.Column(db.String(2000))
 	date_created = db.Column(db.DateTime)
+	display = db.Column(db.Boolean)
+	post_id = db.Column(db.Integer, db.ForeignKey("post.id"))
 	reply_for = db.Column(db.Integer, db.ForeignKey("comment.id")) # for specify if this comment is a reply for other comment
 
-class Link(db.Model):
-	__tablename__ = 'link'
-	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
-	name = db.Column(db.String(100))
-	url = db.Column(db.String(1000))
-
-class Like(db.Model):
-	__tablename__ = 'like'
-	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
-	comment = db.Column(db.Integer, db.ForeignKey('comment.id'))
-	post = db.Column(db.Integer, db.ForeignKey('post.id'))
-	link = db.Column(db.Integer, db.ForeignKey('link.id'))
+
 

File blog/static/js/index.js

 $(function(){ // wait for document to load
 
   //Install de infinite scroll ( tks for http://andersonferminiano.com/jqueryscrollpagination/ )
-  $('#artigos').scrollPagination({
-      'contentPage': '/blog/more/', // the url you are fetching the results
-      'contentData': {}, // these are the variables you can pass to the request, for example: children().size() to know which page you are
-      'scrollTarget': $(window), // who gonna scroll? in this example, the full window
-      'heightOffset': 5, // it gonna request when scroll is 10 pixels before the page ends
-      'beforeLoad': function(){ // before load function, you can display a preloader div
-          $('#loading').fadeIn();
-      },
-      'afterLoad': function(elementsLoaded){ // after loading content, you can use this function to animate your new elements
-           $('#loading').fadeOut();
-           var i = 0;
-           $(elementsLoaded).fadeInWithDelay();
-           if ( $('#nomoreresults').length ){ // if no more posts, then #nomereresults will be returned
-              $('#nomoreresults').fadeIn();
-              $('#artigos').stopScrollPagination();
-           }
-      }
-  });
+  if(!cancel_loading){
+    $('#artigos').scrollPagination({
+        'contentPage': '/blog/more/', // the url you are fetching the results
+        'contentData': {}, // these are the variables you can pass to the request, for example: children().size() to know which page you are
+        'scrollTarget': $(window), // who gonna scroll? in this example, the full window
+        'heightOffset': 5, // it gonna request when scroll is 10 pixels before the page ends
+        'beforeLoad': function(){ // before load function, you can display a preloader div
+            $('#loading').fadeIn();
+        },
+        'afterLoad': function(elementsLoaded){ // after loading content, you can use this function to animate your new elements
+             $('#loading').fadeOut();
+             var i = 0;
+             $(elementsLoaded).fadeInWithDelay();
+             if ( $('#nomoreresults').length ){ // if no more posts, then #nomereresults will be returned
+                $('#nomoreresults').fadeIn();
+                $('#artigos').stopScrollPagination();
+             }
+        }
+    });
+  }
 
   // code for fade in element by element
   $.fn.fadeInWithDelay = function(){
   $("#toplink").hide();
 
   // fade in #back-top
-  $(function () {
+  if(!cancel_loading){
     $(window).scroll(function () {
       $('#readmore').fadeOut()
       if ($(this).scrollTop() > 100) {
       }, 800);
       return false;
     });
-  });
+  }
 
 
 });

File blog/templates/index.html

 			</div>
 			</article>
 		<hr>
+		{%else%}
+			<img src="{{url_for('static', filename='img/finish_flag.png')}}">
+			<div class="offset3">Infelizmente não há posts para serem lidos!</div>
+			<script type="text/javascript">cancel_loading = true;</script>
 		{%endfor%}
-
 	</section>
 
 	{% include 'sidebar.html' %}

File blog/templates/list-posts.html

 					<a href="{{url_for('blog.view_post', slug=post.slug)}}"><i class="icon-eye-open"></i></a>
 				</td>
 				<td class="center">
-				{%if post.featured == 'N'%}
-				<input type="checkbox">
-				{%else%}
+				{%if post.featured == 'Y'%}
 				<input type="checkbox" checked="checked">
+				{%else%}
+				<input type="checkbox">
 				{%endif%}
 				</td>
 				<td>{{post.id}}</td>

File blog/templates/new-post.html

 }
 </style>
 
-<script src="{{url_for('static', filename="file-upload/jquery.MultiFile.pack.js")}}"></script>
+<!-- <script src="{{url_for('static', filename="file-upload/jquery.MultiFile.pack.js")}}"></script> -->
 {% endblock %}
 
 {% block content %}
 		<label id="id">Post id:{{post.id}}</label>
 		<input type="hidden" name="id" value="{{post.id}}">
 		<label for="title">Título</label><input class="span7 required" id="title" name="title" type="text" length="150" value="{{post.title}}"><br>&nbsp;
-		<label class="checkbox" for="featured"><input type="checkbox" value="featured" id="featured" name="featured" {%if post.featured%}checked="checked"{%endif%}>Post destacado</label>
+		<label class="checkbox" for="featured"><input type="checkbox" value="featured" id="featured" name="featured" {%if post.featured=='Y'%}checked="checked"{%endif%}>Post destacado</label>
 		<pre id="editor">{{post.content}}</pre>
 		<input type="hidden" id="content" name="content" value="{{post.content}}">
-		<label for="resume">Resumo</label><textarea class="span12 required" id="resume" name="resume">{{post.resume}}</textarea>
+		<label for="resume">Resumo</label><textarea class="span12" id="resume" name="resume">{{post.resume}}</textarea>
 		<label for="slug">Slug</label><input class="span7 required" id="slug" name="slug" type="text" length="150" value="{{post.slug}}">
 		<label for="tags">Tags</label><input class="span8" id="tags" name="tags" type="text" lenght="500" value="{{post.tags}}">
 		<label for="postfile">Banner (use este campo para alterar o banner)</label><input type="file" id="postfile" name="postfile">
 		<div id="postfile-list"></div>
-		<img src="{{post.picture}}" class="img-featured">
+		<img src="{{post.picture_url}}" class="img-featured">
 	{% else %}
 		<h1 id="acao">Novo post</h1>
 		<label for="title">Título</label><input class="span7 required" id="title" name="title" type="text" length="150"><br>&nbsp;
 		<label>Conteúdo:</label>
 		<pre id="editor"></pre>
 		<input type="hidden" id="content" name="content" value="">
-		<label for="resume">Resumo</label><textarea class="span12 required" id="resume" name="resume"></textarea>
+		<label for="resume">Resumo</label><textarea class="span12" id="resume" name="resume"></textarea>
 		<div id="help"></div>
 		<label for="slug">Slug</label><input class="span7 required" id="slug" name="slug" type="text" length="150">
 		<label for="tags">Tags</label><input class="span8" id="tags" name="tags" type="text" lenght="500" value="">

File blog/templates/one-post.html

 </script>
 <style type="text/css">
 
-.fechado {
-	border-width:1px;
-	border-style:solid;
-	border-color: #C9C9C9;
-	background:#eee url(/static/img/arrow-down.gif) no-repeat 98% 50%;
-	padding: 3px;
-	border-radius:5px;
-}
-
-.aberto {
-	border-radius:5px;
-	border-width:1px;
-	border-color: #C9C9C9;
-	border-style:solid;
-	background:#eee url(/static/img/arrow-up.gif) no-repeat 98% 50%;
-	padding: 3px;
-}
-.comment{
-	/*display: none;*/
-}
 </style>
 {% endblock %}
 
 				<button class="btn comentarios"><i class="icon-comment"></i>10</button>
 				<button class="btn curtir"><i class="icon-thumbs-up"></i>12</button>
 				<button class="btn descurtir"><i class="icon-thumbs-down"></i>2</button>
+				{%if is_authenticated()%}
 				<button class="btn" onclick="edit('{{url_for('blog.edit_post', post_id=post.id)}}');"><i class="icon-edit"></i></button>
+				{%endif%}
 				<button class="btn source"><i class="icon-download-alt"></i></button>
 			</div>
 			<h1>{{post.title}}</h1>
-			<h4>Data: {{post.date_created.strftime('%d/%B/%Y %H:%M:%S')}}</h4>
-			<h4>Url Curta: <a href="http://berlotto.net/u/123">http://berlotto.net/u/123</a></h4>
+			<p>Data: {{post.date_created.strftime('%d de %B de %Y')}}</p>
+			{%if post.short_url%}
+			<p>Url Curta: <a href="http://berlotto.net/u/123">{{post.short_url}}</a></p>
+			{%endif%}
 
 		</header>
 		{% if post.picture %}
 	</article>
 	<br><br>
 
-	<h4 id="title-comments" class="fechado">Comentários:</h4>
-	<div class="comments">
-		<div class="comment">
-			<div class="comment-header span6">
-				<span>Autor: Usuário 1</span>
-				<span>Data: 01/01/2012</span>
-			</div>
-			<div class="span1">
-				<i class="icon-thumbs-up"></i>
-				<i class="icon-thumbs-down"></i>
-			</div>
-			<div class="comment-content">
-				<img src="{{'emailqnaoexiste@teste.com.br'|gravatar}}" alt="" class="img-polaroid">
-				<p class="offset1 span6">{{lipsum(n=1, html=True, min=5, max=30)}}</p>
-			</div>
-		</div>
-		<div class="comment">
-			<div class="comment-header">
-				<div class="span1 offset6">
-					<i class="icon-thumbs-up"></i>
-					<i class="icon-thumbs-down"></i>
-				</div>
-				<span>Autor: Usuário 2</span>
-				<span>Data: 01/01/2012</span>
-			</div>
-			<div class="comment-content">
-				<img src="{{'sergio.berlotto@gmail.com'|gravatar}}" alt="" class="img-polaroid">
-				<p>{{lipsum(n=1, html=True, min=5, max=30)}}</p>
-			</div>
-		</div>
-	</div>
-<br>
 	<div id="div-comment">
 
 		<form id="form-comentario" method="POST" action="{{url_for('blog.save_comment')}}" class="well">
+			<input type="hidden" name="post_id" value="{{post.id}}">
 			<h4>Enviar comentário:</h4>
 			<div class="controls controls-row">
-				<label for="nome" class="span1">Nome:</label><input type="text" id="name" name="name" class="span7" placeholder="Seu nome">
-				<label for="email" class="span1">Email:</label><input type="text" id="email" name="email" class="span7" placeholder="Seu email">
+				<label for="nome" class="span1">Nome:</label><input type="text" id="name" name="name" maxlength="150" class="span3" placeholder="Seu nome">
+				<label for="email" class="span1">Email:</label><input type="text" id="email" name="email" maxlength="500" class="span3" placeholder="Seu email">
 			</div>
 			<div class="controls controls-row">
 				<label class="span1" for="content">Comentário:</label>
-				<textarea id="content" name="content" class="span8" placeholder="Comente aqui..."></textarea>
+				<textarea id="content" name="content" class="span8" maxlength="2000" placeholder="Comente aqui..."></textarea>
 			</div>
 			    <button type="submit" class="offset7 btn btn-primary">Enviar</button>
+			<div id="comentario-msg"></div>
 		</form>
 	</div>
 
+	<h4 id="title-comments" class="fechado">Comentários:</h4>
+	<div class="comments">
+		{%for com in comentarios%}
+		<dl>
+			<dt>
+				<img src="{{com.email|gravatar}}" alt="" title="{{com.name}}" class="img-rounded">
+				Autor: {{com.name}}, em {{com.date_created.strftime('%d/%B/%Y %H:%M:%S')}}
+				<a href="/curtir/comment/{{com.id}}" title="Gostei"><i class="icon-thumbs-up"></i></a>
+				<a href="/ncurtir/comment/{{com.id}}" title="Não Gostei"><i class="icon-thumbs-down"></i></a>
+			<dt>
+			<dd>
+				<!-- {{lipsum(n=1, html=True, min=5, max=30)}} -->
+				{{com.content}}
+			</dd>
+		<dl>
+		{%else%}
+		<p>Ainda sem nenhum comentário neste post. Seja o primeiro a enviar sua opinião!</p>
+		{%endfor%}
+	</div>
 	<hr>
 
 </section>
     // wait for the DOM to be loaded
     $(document).ready(function() {
         // bind 'form-comentario' and provide a simple callback function
-        $('#form-comentario').ajaxForm(function() {
-        	alert("Thank you for your comment!");
-        });
-        $('#title-comments').click(function(){
-        	$('.comments').animate({
-        		height: 'toggle',
-        	}, 500, function(){
-        		$('#title-comments').toggleClass('aberto','fechado');
-        	})
-        });
-
-        $('.comments').animate({
-    		height: 'hide',
-    	})
+        showResponse = function(data, statusText, xhr, $form) {
+        	//alert(data.message);
+        	$('#comentario-msg').html(data.message);
+        };
+        var options = { 
+	        //target:        '#output1',   // target element(s) to be updated with server response 
+	        //beforeSubmit:  showRequest,  // pre-submit callback 
+	        success:       showResponse,  // post-submit callback 
+	 
+	        // other available options: 
+	        //url:       url         // override for form's 'action' attribute 
+	        //type:      'post',        // 'get' or 'post', override for form's 'method' attribute 
+	        dataType:  'json',        // 'xml', 'script', or 'json' (expected server response type) 
+	        clearForm: true,        // clear all form fields after successful submit 
+	        resetForm: true        // reset the form after successful submit 
+	 
+	        // $.ajax options can be used here too, for example: 
+	        //timeout:   3000 
+	    }; 
+        $('#form-comentario').ajaxForm(options);
+
+        // $('#title-comments').click(function(){
+        // 	$('.comments').animate({
+        // 		height: 'toggle',
+        // 	}, 500, function(){
+        // 		$('#title-comments').toggleClass('aberto','fechado');
+        // 	})
+        // });
+
+     //    $('.comments').animate({
+    	// 	height: 'hide',
+    	// })
     });
 
 </script>

File general/__init__.py

Empty file added.

File general/models.py

+from flask import current_app
+# from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
+from flask.ext.sqlalchemy import SQLAlchemy
+# from database import Base
+
+db = SQLAlchemy()
+
+class Link(db.Model):
+	__tablename__ = 'link'
+	id = db.Column(db.Integer, db.Sequence('link_id'), primary_key=True)
+	title = db.Column(db.String(150))
+	url = db.Column(db.String(2000), unique=True)
+	short_url = db.Column(db.String(1000))
+	sidebar = db.Column(db.Boolean) #Show or Not this link in sidebar
+
+	def __repr__(self):
+		return '<Link %r>' % (self.shorturl)
+
+class Like(db.Model):
+	__tablename__ = 'like'
+	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+	comment = db.Column(db.Integer, db.ForeignKey('comment.id'))
+	post = db.Column(db.Integer, db.ForeignKey('post.id'))
+	link = db.Column(db.Integer, db.ForeignKey('link.id'))

File media/models.py

+from flask import current_app
+# from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
+from flask.ext.sqlalchemy import SQLAlchemy
+# from database import Base
+
+db = SQLAlchemy()
+
+class File(db.Model):
+	"""This model represents the media files uploaded from user"""
+	__tablename__ = "file"
+	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+	title = db.Column(db.String(150))
+	name = db.Column(db.String(1000))
+

File media/templates/media_index.html

 {% extends "base.html" %}
 
 {% block head %}
-
+<link href="{{ url_for('static', filename='filedrag/filedrag.css')}}" media="screen" rel="stylesheet" type="text/css" />
 {% endblock %}
 
 {% block content %}
-<div class="row">
-    <div class="span12">
-        <h1>Arquivos de mídia</h1><br>&nbsp;
-    </div>
-</div>
-<div class="row">
     <div class="span12">
     <!-- The file upload form used as target for the file upload widget -->
-    <form id="fileupload" action="{{url_for('media.upload')}}" method="POST" enctype="multipart/form-data">
         <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
         <div class="row fileupload-buttonbar">
-            <div class="span7">
-                <!-- The fileinput-button span is used to style the file input field as button -->
-                <input type="file" name="files[]" multiple>
-                <button type="submit" class="btn btn-primary start">
-                    <i class="icon-upload icon-white"></i>
-                    <span>Start upload</span>
-                </button>
+            <div class="span12">
+
+                <form id="upload" class="well" action="{{url_for('media.upload')}}" method="POST" enctype="multipart/form-data">  
+                    <fieldset>  
+                        <legend>Arquivos de Mídia</legend>  
+                        <input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" />  
+                        <div>  
+                            <label for="fileselect">Files to upload:</label>  
+                            <input type="file" id="fileselect" name="fileselect[]" multiple="multiple" />  
+                            <div id="filedrag" class="filearea">ou arraste e solte os arquivos aqui</div>  
+                        </div>  
+                        <!-- The loading indicator is shown during file processing -->
+                        <div id="messages">
+                        &nbsp;
+                        </div>
+                        <div id="progress"></div>
+                        <button type="submit" id="submitbutton" class="btn btn-large btn-block">Upload Files</button>
+                    </fieldset>  
+                </form>  
+
             </div>
         </div>
-        <!-- The loading indicator is shown during file processing -->
-        <div class="fileupload-loading"></div>
+
         <br>
         <!-- The table listing the files available for upload/download -->
         <table role="presentation" class="table table-striped">
                 </tr>
             </tbody>
         </table>
-    </form>
     <br>
 
 </div></div>
             <i class="icon-arrow-right icon-white"></i>
         </a>
     </div>
-</div>
 {%endblock%}
 
 {%block bottompage%}
-
+<script src="{{url_for('static', filename="filedrag/filedrag.js")}}"></script>
 {%endblock%}

File others/filedrag3.zip

Binary file added.

File others/filedrag3/filedrag.js

+/*
+filedrag.js - HTML5 File Drag & Drop demonstration
+Featured on SitePoint.com
+Developed by Craig Buckler (@craigbuckler) of OptimalWorks.net
+*/
+(function() {
+
+	// getElementById
+	function $id(id) {
+		return document.getElementById(id);
+	}
+
+
+	// output information
+	function Output(msg) {
+		var m = $id("messages");
+		m.innerHTML = msg + m.innerHTML;
+	}
+
+
+	// file drag hover
+	function FileDragHover(e) {
+		e.stopPropagation();
+		e.preventDefault();
+		e.target.className = (e.type == "dragover" ? "hover" : "");
+	}
+
+
+	// file selection
+	function FileSelectHandler(e) {
+
+		// cancel event and hover styling
+		FileDragHover(e);
+
+		// fetch FileList object
+		var files = e.target.files || e.dataTransfer.files;
+
+		// process all File objects
+		for (var i = 0, f; f = files[i]; i++) {
+			ParseFile(f);
+			UploadFile(f);
+		}
+
+	}
+
+
+	// output file information
+	function ParseFile(file) {
+
+		Output(
+			"<p>Arquivo: <strong>" + file.name +
+			"</strong> Tipo: <strong>" + file.type +
+			"</strong> Tamanho: <strong>" + file.size +
+			"</strong> bytes</p>"
+		);
+
+		// display an image
+		if (file.type.indexOf("image") == 0) {
+			var reader = new FileReader();
+			reader.onload = function(e) {
+				Output(
+					"<p><strong>" + file.name + ":</strong><br />" +
+					'<img class="img-polaroid" src="' + e.target.result + '" /></p>'
+				);
+			}
+			reader.readAsDataURL(file);
+		}
+
+		// display text
+		if (file.type.indexOf("text") == 0) {
+			var reader = new FileReader();
+			reader.onload = function(e) {
+				Output(
+					"<p><strong>" + file.name + ":</strong></p><pre>" +
+					e.target.result.replace(/</g, "&lt;").replace(/>/g, "&gt;") +
+					"</pre>"
+				);
+			}
+			reader.readAsText(file);
+		}
+
+	}
+
+
+	// upload JPEG files
+	function UploadFile(file) {
+
+		// following line is not necessary: prevents running on SitePoint servers
+		if (location.host.indexOf("sitepointstatic") >= 0) return
+
+		var xhr = new XMLHttpRequest();
+		if (xhr.upload && file.type == "image/jpeg" && file.size <= $id("MAX_FILE_SIZE").value) {
+
+			// create progress bar
+			var o = $id("progress");
+			var progress = o.appendChild(document.createElement("p"));
+			progress.appendChild(document.createTextNode("upload " + file.name));
+
+
+			// progress bar
+			xhr.upload.addEventListener("progress", function(e) {
+				var pc = parseInt(100 - (e.loaded / e.total * 100));
+				progress.style.backgroundPosition = pc + "% 0";
+			}, false);
+
+			// file received/failed
+			xhr.onreadystatechange = function(e) {
+				if (xhr.readyState == 4) {
+					progress.className = (xhr.status == 200 ? "success" : "failure");
+				}
+			};
+
+			// start upload
+			xhr.open("POST", $id("upload").action, true);
+			xhr.setRequestHeader("X_FILENAME", file.name);
+			xhr.send(file);
+
+		}
+
+	}
+
+
+	// initialize
+	function Init() {
+
+		var fileselect = $id("fileselect"),
+			filedrag = $id("filedrag"),
+			submitbutton = $id("submitbutton");
+
+		// file select
+		fileselect.addEventListener("change", FileSelectHandler, false);
+
+		// is XHR2 available?
+		var xhr = new XMLHttpRequest();
+		if (xhr.upload) {
+
+			// file drop
+			filedrag.addEventListener("dragover", FileDragHover, false);
+			filedrag.addEventListener("dragleave", FileDragHover, false);
+			filedrag.addEventListener("drop", FileSelectHandler, false);
+			filedrag.style.display = "block";
+
+			// remove submit button
+			submitbutton.style.display = "none";
+		}
+
+	}
+
+	// call initialization file
+	if (window.File && window.FileList && window.FileReader) {
+		Init();
+	}
+
+
+})();

File others/filedrag3/index.html

+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>HTML5 File Drag &amp; Drop API</title>
+<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
+</head>
+<body>
+
+<h1>HTML5 File Drag &amp; Drop API</h1>
+
+<p>This is a demonstration of the HTML5 file drag &amp; drop API with asynchronous Ajax file uploads, graphical progress bars and progressive enhancement.</p>
+
+<p>For more information, please refer to:</p>
+<ul>
+<li><a href="http://www.sitepoint.com/html5-file-drag-and-drop/">How to Use HTML5 File Drag &amp Drop</a></li>
+<li><a href="http://www.sitepoint.com/html5-javascript-open-dropped-files">How to Open Dropped Files Using HTML5 and JavaScript</a></li>
+<li><a href="http://www.sitepoint.com/html5-ajax-file-upload">How to Asynchronously Upload Files Using HTML5 and Ajax</a></li>
+<li><a href="http://www.sitepoint.com/html5-javascript-file-upload-progress-bar">How to Create File Upload Progress Bars in HTML5 and JavaScript</a></li>
+</ul>
+
+
+<form id="upload" action="upload.php" method="POST" enctype="multipart/form-data">
+
+<fieldset>
+<legend>HTML File Upload</legend>
+
+<input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" />
+
+<div>
+	<label for="fileselect">Files to upload:</label>
+	<input type="file" id="fileselect" name="fileselect[]" multiple="multiple" />
+	<div id="filedrag">or drop files here</div>
+</div>
+
+<div id="submitbutton">
+	<button type="submit">Upload Files</button>
+</div>
+
+</fieldset>
+
+</form>
+
+<div id="progress"></div>
+
+<div id="messages">
+<p>Status Messages</p>
+</div>
+
+
+<h2>Disclaimer</h2>
+<p>The code was developed by <a href="http://twitter.com/craigbuckler">Craig Buckler</a> of <a href="http://optimalworks.net/">OptimalWorks.net</a> for <a href="http://sitepoint.com/">SitePoint.com</a>.</p>
+
+<p>This code can be <a href="http://blogs.sitepointstatic.com/examples/tech/filedrag/3/filedrag3.zip">downloaded and used</a> without any restrictions but please don't expect support! A link back to SitePoint.com is appreciated.</p>
+
+<script src="filedrag.js"></script>
+</body>
+</html>

File others/filedrag3/progress.png

Added
New image

File others/filedrag3/styles.css

+/*
+Styles for HTML5 File Drag & Drop demonstration
+Featured on SitePoint.com
+Developed by Craig Buckler (@craigbuckler) of OptimalWorks.net
+*/
+body
+{
+	font-family: "Segoe UI", Tahoma, Helvetica, freesans, sans-serif;
+	font-size: 90%;
+	margin: 10px;
+	color: #333;
+	background-color: #fff;
+}
+
+h1, h2
+{
+	font-size: 1.5em;
+	font-weight: normal;
+}
+
+h2
+{
+	font-size: 1.3em;
+}
+
+legend
+{
+	font-weight: bold;
+	color: #333;
+}
+
+#filedrag
+{
+	display: none;
+	font-weight: bold;
+	text-align: center;
+	padding: 1em 0;
+	margin: 1em 0;
+	color: #555;
+	border: 2px dashed #555;
+	border-radius: 7px;
+	cursor: default;
+}
+
+#filedrag.hover
+{
+	color: #f00;
+	border-color: #f00;
+	border-style: solid;
+	box-shadow: inset 0 3px 4px #888;
+}
+
+img
+{
+	max-width: 100%;
+}
+
+pre
+{
+	width: 95%;
+	height: 8em;
+	font-family: monospace;
+	font-size: 0.9em;
+	padding: 1px 2px;
+	margin: 0 0 1em auto;
+	border: 1px inset #666;
+	background-color: #eee;
+	overflow: auto;
+}
+
+#messages
+{
+	padding: 0 10px;
+	margin: 1em 0;
+	border: 1px solid #999;
+}
+
+#progress p
+{
+	display: block;
+	width: 240px;
+	padding: 2px 5px;
+	margin: 2px 0;
+	border: 1px inset #446;
+	border-radius: 5px;
+	background: #eee url("progress.png") 100% 0 repeat-y;
+}
+
+#progress p.success
+{
+	background: #0c0 none 0 0 no-repeat;
+}
+
+#progress p.failed
+{
+	background: #c00 none 0 0 no-repeat;
+}

File others/filedrag3/upload.php

+<?php
+/*
+Server-side PHP file upload code for HTML5 File Drag & Drop demonstration
+Featured on SitePoint.com
+Developed by Craig Buckler (@craigbuckler) of OptimalWorks.net
+*/
+$fn = (isset($_SERVER['HTTP_X_FILENAME']) ? $_SERVER['HTTP_X_FILENAME'] : false);
+
+if ($fn) {
+
+	// AJAX call
+	file_put_contents(
+		'uploads/' . $fn,
+		file_get_contents('php://input')
+	);
+	echo "$fn uploaded";
+	exit();
+
+}
+else {
+
+	// form submit
+	$files = $_FILES['fileselect'];
+
+	foreach ($files['error'] as $id => $err) {
+		if ($err == UPLOAD_ERR_OK) {
+			$fn = $files['name'][$id];
+			move_uploaded_file(
+				$files['tmp_name'][$id],
+				'uploads/' . $fn
+			);
+			echo "<p>File $fn uploaded.</p>";
+		}
+	}
+
+}

File others/filedrag3/uploads/uploads.txt

+Uploaded files will be stored in this folder.

File others/logotype.png

Old
Old image
New
New image

File others/logotype.svg

Old
Old image
New
New image

File others/markdown_cheatsheet.pdf

Binary file added.

File others/qrcode.png

Added
New image

File pages/__init__.py

 from model import Page
 from werkzeug.contrib.cache import MemcachedCache, SimpleCache
 from flask.ext.sqlalchemy import SQLAlchemy
+from flask.ext.login import login_required
 from sqlalchemy.exc import IntegrityError
 from sqlalchemy.orm.exc import NoResultFound
 from flaskext.uploads import UploadNotAllowed
 		return abort(404)
 
 @pages.route('/edit/<slug>/')
+@login_required
 def edit(slug):
 	try:
 		pagina = Page.query.filter(Page.slug==slug).one()
 
 
 @pages.route('/new')
+@login_required
 def new():
 	return render_template('pages_new.html')
 
 
 @pages.route('/save/',methods = ['POST',])
+@login_required
 def save():
 	try:
 		add = False
 
 
 @pages.route('/delete/',methods = ['POST',])
+@login_required
 def delete():
 	pass

File pages/model.py

 
 class Page(db.Model):
 	__tablename__ = 'page'
-	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+	id = db.Column(db.Integer,  db.Sequence('page_id'), primary_key=True)
 	title = db.Column(db.String(150))
 	slug = db.Column(db.String(500), unique=True)
 	content = db.Column(db.Text)

File pages/templates/pages_view.html

 		    	<div class="btn-group pull-right">
 				    <button class="btn curtir"><i class="icon-thumbs-up"></i>12</button>
 				    <button class="btn descurtir"><i class="icon-thumbs-down"></i>2</button>
+				    {%if is_authenticated()%}
 				    <button class="btn" onclick="edit('{{url_for('pages.edit', slug=page.slug)}}');"><i class="icon-edit"></i></button>
+				    {%endif%}
 				    <button class="btn source"><i class="icon-download-alt"></i></button>
 			    </div>
 				<h1>{{page.title}}</h1>
-				<h4>Data: {{page.date_created.strftime('%d/%B/%Y %H:%M:%S')}}</h4>
-				<h4>Url Curta: <a href="http://berlotto.net/u/123">http://berlotto.net/u/123</a></h4>
+				<p>Url Curta: <a href="http://berlotto.net/u/123">http://berlotto.net/u/123</a></p>
 
 			</header>
 			<div class="content">{{page.content|translate_markdown|safe}}</div>
+gunicorn asciiblog:app -b 0.0.0.0:5000 -w 3

File static/css/asciiblog.css

 
 div#facebox{
 	height: 300px !important;
+}
+
+
+/*MEUS ICONES*/
+.ilogin {
+	background-image: url("../img/icones/glyphicons_203_lock.png");
+}
+
+.icone{
+    background-repeat: no-repeat;
+    background-size: 12px 14px;
+    display: inline-block;
+    height: 14px;
+    line-height: 16px;
+    margin-top: 1px;
+    vertical-align: text-top;
+    width: 14px;
+}
+
+/*Effects to logotype img \/ */
+.logotype{
+	padding: 10px;
+	/*border: 5px solid #eee;*/
+	/*-webkit-box-shadow: 4px 4px 4px rgba(0,0,0,0.2);
+	-moz-box-shadow: 4px 4px 4px rgba(0,0,0,0.2);
+	box-shadow: 4px 4px 4px rgba(0,0,0,0.2);*/
+	-webkit-transition: all 0.5s ease-out;
+	-moz-transition: all 0.5s ease;
+	-o-transition: all 0.5s ease;
+}
+.logotype:hover{
+	-webkit-transform: rotate(-7deg);
+	-moz-transform: rotate(-7deg);
+	-o-transform: rotate(-7deg);
+}
+
+/*Effects to logotype img /\*/
+
+.footer-icone{
+	margin-bottom: 3px;
 }

File static/filedrag/filedrag.css

+/*
+Styles for HTML5 File Drag & Drop demonstration
+Featured on SitePoint.com
+Developed by Craig Buckler (@craigbuckler) of OptimalWorks.net
+*/
+
+#filedrag
+{
+	display: none;
+	font-weight: bold;
+	text-align: center;
+	padding: 1em 0;
+	margin: 1em 0;
+	color: #555;
+	border: 2px dashed #555;
+	border-radius: 7px;
+	cursor: default;
+}
+
+#filedrag.hover
+{
+	color: #f00;
+	border-color: #f00;
+	border-style: solid;
+	box-shadow: inset 0 3px 4px #888;
+}
+
+.img-upload
+{
+	max-width: 150px;
+}
+
+.pre-text-upload
+{
+	width: 95%;
+	height: 8em;
+	font-family: monospace;
+	font-size: 0.9em;
+	padding: 1px 2px;
+	margin: 0 0 1em auto;
+	border: 1px inset #666;
+	background-color: #eee;
+	overflow: auto;
+}
+
+#messages
+{
+	padding: 0 10px;
+	margin: 1em 0;
+	border: 0px solid #999;
+}
+
+#progress p
+{
+	display: block;
+	width: 240px;
+	padding: 2px 5px;
+	margin: 2px 0;
+	border: 1px inset #446;
+	border-radius: 5px;
+	background: #eee url("progress.png") 100% 0 repeat-y;
+}
+
+#progress p.success
+{
+	background: #0c0 none 0 0 no-repeat;
+}
+
+#progress p.failed
+{
+	background: #c00 none 0 0 no-repeat;
+}

File static/filedrag/filedrag.js

+/*
+filedrag.js - HTML5 File Drag & Drop demonstration
+Featured on SitePoint.com
+Developed by Craig Buckler (@craigbuckler) of OptimalWorks.net
+*/
+(function() {
+
+	// getElementById
+	function $id(id) {
+		return document.getElementById(id);
+	}
+
+
+	// output information
+	function Output(msg) {
+		var m = $id("messages");
+		m.innerHTML = msg + m.innerHTML;
+	}
+
+
+	// file drag hover
+	function FileDragHover(e) {
+		e.stopPropagation();
+		e.preventDefault();
+		e.target.className = (e.type == "dragover" ? "hover" : "");
+	}
+
+
+	// file selection
+	function FileSelectHandler(e) {
+
+		// cancel event and hover styling
+		FileDragHover(e);
+
+		// fetch FileList object
+		var files = e.target.files || e.dataTransfer.files;
+
+		console.log("FileSelectHandler:"+files.length);
+
+		// process all File objects
+		for (var i = 0, f; f = files[i]; i++) {
+			console.log("Processing file " + i);
+			ParseFile(f);
+			UploadFile(f);
+		}
+
+	}
+
+
+	// output file information
+	function ParseFile(file) {
+
+		Output(
+			"<p>Arquivo: <strong>" + file.name +
+			"</strong> Tipo: <strong>" + file.type +
+			"</strong> Tamanho: <strong>" + file.size +
+			"</strong> bytes</p>"
+		);
+
+		// display an image
+		if (file.type.indexOf("image") == 0) {
+			var reader = new FileReader();
+			reader.onload = function(e) {
+				Output(
+					"<p><strong>" + file.name + ":</strong><br />" +
+					'<img class="img-upload img-polaroid" src="' + e.target.result + '" /></p>'
+				);
+			}
+			reader.readAsDataURL(file);
+		}
+
+		// display text
+		if (file.type.indexOf("text") == 0) {
+			var reader = new FileReader();
+			reader.onload = function(e) {
+				Output(
+					"<p><strong>" + file.name + ":</strong></p><pre class=\"pre-text-upload\">" +
+					e.target.result.replace(/</g, "&lt;").replace(/>/g, "&gt;") +
+					"</pre>"
+				);
+			}
+			reader.readAsText(file);
+		}
+
+	}
+
+
+	// upload JPEG files
+	function UploadFile(file) {
+
+		// following line is not necessary: prevents running on SitePoint servers
+		if (location.host.indexOf("sitepointstatic") >= 0) return
+
+		var xhr = new XMLHttpRequest();
+
+		console.log(file.size <= $id("MAX_FILE_SIZE").value);
+
+		// if (xhr.upload && file.type == "image/jpeg" && file.size <= $id("MAX_FILE_SIZE").value) {
+		if (xhr.upload && file.size <= $id("MAX_FILE_SIZE").value) {
+
+console.log("1");
+
+			// create progress bar
+			var o = $id("progress");
+			var progress = o.appendChild(document.createElement("p"));
+			progress.appendChild(document.createTextNode("upload " + file.name));
+
+console.log("3");
+
+			// progress bar
+			xhr.upload.addEventListener("progress", function(e) {
+				var pc = parseInt(100 - (e.loaded / e.total * 100));
+				progress.style.backgroundPosition = pc + "% 0";
+			}, false);
+
+console.log("3");
+
+			// file received/failed
+			xhr.onreadystatechange = function(e) {
+				if (xhr.readyState == 4) {
+					progress.className = (xhr.status == 200 ? "success" : "failure");
+				}
+			};
+
+console.log("4");
+
+			// start upload
+			console.log("Pushing file to:"+$id("upload").action);
+			xhr.open("POST", $id("upload").action, true);
+			xhr.setRequestHeader("X_FILENAME", file.name);
+			xhr.send(file);
+
+		}
+
+	}
+
+
+	// initialize
+	function Init() {
+
+		var fileselect = $id("fileselect"),
+			filedrag = $id("filedrag"),
+			submitbutton = $id("submitbutton");
+
+		// file select
+		fileselect.addEventListener("change", FileSelectHandler, false);
+
+		// is XHR2 available?
+		var xhr = new XMLHttpRequest();
+		if (xhr.upload) {
+
+			// file drop
+			filedrag.addEventListener("dragover", FileDragHover, false);
+			filedrag.addEventListener("dragleave", FileDragHover, false);
+			filedrag.addEventListener("drop", FileSelectHandler, false);
+			filedrag.style.display = "block";
+
+			// remove submit button
+			submitbutton.style.display = "none";
+		}
+
+	}
+
+	// call initialization file
+	if (window.File && window.FileList && window.FileReader) {
+		Init();
+	}
+
+
+})();

File static/filedrag/progress.png

Added
New image

File static/img/icones/glyphicons_072_bookmark.png

Added
New image

File static/img/icones/glyphicons_203_lock.png

Added
New image

File static/img/icones/glyphicons_362_google+_alt.png

Added
New image

File static/img/icones/glyphicons_377_linked_in.png

Added
New image

File static/img/icones/glyphicons_382_youtube.png

Added
New image

File static/img/icones/glyphicons_390_facebook.png

Added
New image

File static/img/icones/glyphicons_392_twitter.png

Added
New image

File static/img/icones/glyphicons_397_rss.png

Added
New image

File static/img/icones/glyphicons_398_skype.png

Added
New image

File static/img/icones/glyphicons_399_e-mail.png

Added
New image

File static/img/login.png

Added
New image

File static/img/logotipo-b.png

Old
Old image
New
New image

File static/uploads/banner_1.jpg

Added
New image

File static/uploads/banner_1_1.jpg

Added
New image

File static/uploads/banner_1_2.jpg

Added
New image

File static/uploads/banner_1_3.jpg

Added
New image

File static/uploads/banner_1_4.jpg

Added
New image

File static/uploads/banner_1_5.jpg

Added
New image

File static/uploads/banner_1_6.jpg

Added
New image

File static/uploads/banner_2.png

Added
New image

File static/uploads/banner_3.png

Added
New image

File templates/400.html

+{% extends "base.html" %}
+
+{% block head %}
+
+  <script src="{{url_for('static', filename="js/scrollpagination.js")}}"></script>
+
+{% endblock %}
+
+{% block content %}
+
+	<section id="artigos" class="offset5 span7">
+
+		<h1>Oops !</h1>
+
+	</section>
+	<section id="artigos" class="offset1 span10">
+
+		<p>Infelizmente ocorreu um erro na página que quer acessar, mas você 
+			pode ir para a página inicial e tentar por outro caminho, ou utilizar o meu acima.</p>
+
+	</section>
+
+{% endblock %}
+
+{% block bottompage %}
+  <script src="{{url_for('blog.static', filename="js/index.js")}}"></script>
+{% endblock %}

File templates/401.html

+{% extends "base.html" %}
+
+{% block head %}
+
+  <script src="{{url_for('static', filename="js/scrollpagination.js")}}"></script>
+
+{% endblock %}
+
+{% block content %}
+
+	<section id="artigos" class="offset4 span8">
+
+		<h1>Não autorizado</h1>
+
+	</section>
+	<section id="artigos" class="offset1 span10">
+
+		<p>Infelizmente esta página que você está acessando não pode ser exibida por falta de permissões
+			, mas você pode ir para a página inicial e navegar no resto do site, ou utilizar o meu acima.</p>
+
+	</section>
+
+{% endblock %}
+
+{% block bottompage %}
+  <script src="{{url_for('blog.static', filename="js/index.js")}}"></script>
+{% endblock %}

File templates/404.html

+{% extends "base.html" %}
+
+{% block head %}
+
+  <script src="{{url_for('static', filename="js/scrollpagination.js")}}"></script>
+
+{% endblock %}
+
+{% block content %}
+
+	<section id="artigos" class="offset4 span8">
+
+		<h1>Página não encontrada</h1>
+
+	</section>
+	<section id="artigos" class="offset1 span10">
+
+		<p>Infelizmente esta página que você está procurando não foi encontrada, mas de repente você 
+			pode ir para a página inicial e tentar por outro caminho, ou utilizar o meu acima.</p>
+
+	</section>
+
+{% endblock %}
+
+{% block bottompage %}
+  <script src="{{url_for('blog.static', filename="js/index.js")}}"></script>
+{% endblock %}

File templates/500.html

+{% extends "base.html" %}
+
+{% block head %}
+
+  <script src="{{url_for('static', filename="js/scrollpagination.js")}}"></script>
+
+{% endblock %}
+
+{% block content %}
+
+	<section id="artigos" class="offset4 span8">
+
+		<h1>Algum problema ocorreu</h1>
+
+	</section>
+	<section id="artigos" class="offset1 span10">
+
+		<p>Infelizmente esta página que você está acessando não pode ser exibida, mas de repente você 
+			pode ir para a página inicial e tentar por outro caminho, ou utilizar o meu acima.</p>
+
+	</section>
+
+{% endblock %}
+
+{% block bottompage %}
+  <script src="{{url_for('blog.static', filename="js/index.js")}}"></script>
+{% endblock %}

File templates/base.html

 	<!-- For call functions after stop typing on inputs -->
 	<script src="{{url_for('static', filename="js/jquery.typing-0.2.0.min.js")}}"></script>
 
+	<!-- flip effect on logo -->
+	<script src="{{url_for('static', filename="js/jquery.flip.min.js")}}"></script>
+
   <!-- http://blueimp.github.com/jQuery-File-Upload/ for media gallery -->
 
 	{% block head %}
 	<div class="container">
 		<div class="row page-header">
 
-			<header class="span3">
-				<a href="/"><img src="{{url_for('static', filename='img/logotipo-b.png')}}"></a>
+			<header class="span3 logotype-container">
+				<a href="/"><img class="logotype" src="{{url_for('static', filename='img/logotipo-b.png')}}"></a>
 			</header>
 
-			<div class="navbar span8 offset1">
-				<div  id="menu" class="navbar-inner">
+			{%if is_authenticated()%}
+			<div class="span7 offset2">
+			{%else%}
+			<div class="span6 offset3">
+			{%endif%}
 					<!-- <ul class="nav nav-pills pull-right"> -->
-					<ul class="nav nav-pills pull-right">
-						<li class="active"><a href="/"><i class="icon-home"></i>&nbsp;Home</a></li>
-						<li><a href="/blog">Blog</a></li>
-						<li><a href="/livro">Livro</a></li>
-						<li><a href="#">Encurtador</a></li>
-						<li><a href="#">Fotos</a></li>
-						<li><a href="/contatos">Contato</a></li>
-						<li><a href="/projetos">Projetos</a></li>
+					<ul class="nav nav-pills">
+						<li class="{%if current_url() == '/'%}active{%endif%}"><a href="/"><i class="icon-home"></i>&nbsp;Home</a></li>
+						<li class="{%if current_url() == '/blog/'%}active{%endif%}"><a href="/blog"><i class="icon-list"></i>&nbsp;Blog</a></li>
+						<li class="{%if current_url() == '/pages/livro/'%}active{%endif%}"><a href="/livro"><i class="icon-book"></i>&nbsp;Livro</a></li>
+						<li><a href="#"><i class="icon-camera"></i>&nbsp;Fotos</a></li>
+						<li class="{%if current_url() == '/pages/contatos/'%}active{%endif%}"><a href="/contatos"><i class="icon-envelope"></i>&nbsp;Contato</a></li>
+						<li class="{%if current_url() == '/pages/projetos/'%}active{%endif%}"><a href="/projetos"><i class="icon-hdd"></i>&nbsp;Projetos</a></li>
+						{%if is_authenticated()%}
 						<li class="dropdown">
-							<a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="icon-cog"></i>&nbsp;Administração <b class="caret"></b></a>
+							<a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="icon-cog"></i>&nbsp;Admin <b class="caret"></b></a>
 							<ul class="dropdown-menu">
-								<li class="nav-header">Blog</li>
+								<li class="nav-header">- Blog -</li>
 								<li><a href="{{url_for('blog.list_posts')}}">Posts</a></li>
 								<li><a href="{{url_for('blog.new_post')}}">New post</a></li>
 								<li><a href="{{url_for('media.index')}}">Media</a></li>
 								<li><a href="#">Comentários</a></li>
-								<li class="nav-header">Encurtador</li>
-								<li><a id='link-links' href="#">Links</a></li>
-								<li><a href="#">Encurtar</a></li>
-								<li class="nav-header">Páginas</li>
+								<li class="nav-header">- Encurtador -</li>
+								<li><a id='link-links' href="/links" title="Ver links encurtados">Links</a></li>
+								<li class="nav-header">- Páginas -</li>
 								<li><a href="{{url_for('pages.index')}}">Páginas</a></li>
 								<li><a href="{{url_for('pages.new')}}">Nova Pagina</a></li>
-								<li class="nav-header">Usuário</li>
-								<li><a href="#">Usuários</a></li>
-								<li><a href="#">Logout</a></li>
+								<li class="nav-header">- Usuário [{{user.login}}] -</li>
+								<!-- <li><a href="#">Usuários</a></li> -->
+								<li><a href="/user/logout/">Logout</a></li>
 								<li class="divider"></li>
-								<li><a href="/configuration">Configuration</a></li>
+								<!-- <li><a href="/configuration">Configuration</a></li> -->
 							</ul>