first commit - fetches all data from pad.ma
This commit is contained in:
commit
2c52062b6d
0
__init__.py
Normal file
0
__init__.py
Normal file
BIN
__init__.pyc
Normal file
BIN
__init__.pyc
Normal file
Binary file not shown.
0
lists/__init__.py
Normal file
0
lists/__init__.py
Normal file
3
lists/models.py
Normal file
3
lists/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
31
lists/views.py
Normal file
31
lists/views.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import urllib
|
||||||
|
from django.utils import simplejson
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
|
||||||
|
def getHtmlFromUrl(url):
|
||||||
|
try:
|
||||||
|
u = urllib.urlopen(url)
|
||||||
|
t = u.read()
|
||||||
|
u.close
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return t
|
||||||
|
|
||||||
|
def fetchJson(request):
|
||||||
|
if request.GET['url']:
|
||||||
|
u = request.GET['url']
|
||||||
|
json = getHtmlFromUrl(u)
|
||||||
|
return HttpResponse(json, mimetype="application/javascript")
|
||||||
|
else:
|
||||||
|
return HttpResponse("{'error': True}", mimetype="application/javascript")
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
return render_to_response("index.html")
|
||||||
|
|
||||||
|
def listDetail(request):
|
||||||
|
if request.GET:
|
||||||
|
rDict = {'listId': request.GET['id'] }
|
||||||
|
return render_to_response("list.html", rDict)
|
||||||
|
else:
|
||||||
|
return HttpResponse("Please pass me a list id:(")
|
11
manage.py
Executable file
11
manage.py
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
from django.core.management import execute_manager
|
||||||
|
try:
|
||||||
|
import settings # Assumed to be in the same directory.
|
||||||
|
except ImportError:
|
||||||
|
import sys
|
||||||
|
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_manager(settings)
|
101
settings.py
Normal file
101
settings.py
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# Django settings for theatre project.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
LOCAL_DEVELOPMENT = True
|
||||||
|
LOGGING_INTERCEPT_REDIRECTS = True
|
||||||
|
LOGGING_LOG_SQL = True
|
||||||
|
LOGGING_SHOW_METRICS = True
|
||||||
|
LOGGING_OUTPUT_ENABLED = True
|
||||||
|
|
||||||
|
PROJECT_PATH = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
ADMINS = (
|
||||||
|
# ('Your Name', 'your_email@domain.com'),
|
||||||
|
)
|
||||||
|
|
||||||
|
INTERNAL_IPS = ('127.0.0.1',)
|
||||||
|
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||||
|
DATABASE_NAME = '' # Or path to database file if using sqlite3.
|
||||||
|
DATABASE_USER = '' # Not used with sqlite3.
|
||||||
|
DATABASE_PASSWORD = '' # Not used with sqlite3.
|
||||||
|
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
||||||
|
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||||
|
|
||||||
|
# Local time zone for this installation. Choices can be found here:
|
||||||
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||||
|
# although not all choices may be available on all operating systems.
|
||||||
|
# If running in a Windows environment this must be set to the same as your
|
||||||
|
# system time zone.
|
||||||
|
TIME_ZONE = 'America/Chicago'
|
||||||
|
|
||||||
|
# Language code for this installation. All choices can be found here:
|
||||||
|
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
# If you set this to False, Django will make some optimizations so as not
|
||||||
|
# to load the internationalization machinery.
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
# Absolute path to the directory that holds media.
|
||||||
|
# Example: "/home/media/media.lawrence.com/"
|
||||||
|
MEDIA_ROOT = join(PROJECT_PATH, 'static')
|
||||||
|
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
"django.core.context_processors.auth",
|
||||||
|
"django.core.context_processors.debug",
|
||||||
|
"django.core.context_processors.i18n",
|
||||||
|
"django.core.context_processors.media",
|
||||||
|
)
|
||||||
|
|
||||||
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
|
# trailing slash if there is a path component (optional in other cases).
|
||||||
|
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
||||||
|
MEDIA_URL = '/static/'
|
||||||
|
|
||||||
|
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
||||||
|
# trailing slash.
|
||||||
|
# Examples: "http://foo.com/media/", "/media/".
|
||||||
|
ADMIN_MEDIA_PREFIX = '/admin/media/'
|
||||||
|
|
||||||
|
# Make this unique, and don't share it with anybody.
|
||||||
|
SECRET_KEY = 'fognneiw#s_o-#^tiny@^f-$x#1&29$3a37w5=kll57i!^uo@r'
|
||||||
|
|
||||||
|
# List of callables that know how to import templates from various sources.
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.load_template_source',
|
||||||
|
'django.template.loaders.app_directories.load_template_source',
|
||||||
|
'django.template.loaders.eggs.load_template_source',
|
||||||
|
)
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'multilingual.middleware.DefaultLanguageMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'urls'
|
||||||
|
|
||||||
|
TEMPLATE_DIRS = (
|
||||||
|
join(PROJECT_PATH, 'templates'),
|
||||||
|
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||||
|
# Always use forward slashes, even on Windows.
|
||||||
|
# Don't forget to use absolute paths, not relative paths.
|
||||||
|
)
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.sites',
|
||||||
|
)
|
BIN
settings.pyc
Normal file
BIN
settings.pyc
Normal file
Binary file not shown.
68
static/css/padmaApi.css
Normal file
68
static/css/padmaApi.css
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
body {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lists {
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
margin-right: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapper {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 5px;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listDetail {
|
||||||
|
padding: 20px;
|
||||||
|
width: 80%;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
background: #ccc;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listImage {
|
||||||
|
float: left;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listTitle {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listTitle a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listDescription {
|
||||||
|
padding-left: 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
26
static/css/padmaList.css
Normal file
26
static/css/padmaList.css
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoWrapper {
|
||||||
|
border: 1px solid #000;
|
||||||
|
width: 80%;
|
||||||
|
min-height: 180px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoMeta {
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoPoster {
|
||||||
|
margin: 5px;
|
||||||
|
float: left;
|
||||||
|
}
|
19
static/js/jquery.js
vendored
Normal file
19
static/js/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
static/js/padmaApi.js
Normal file
8
static/js/padmaApi.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
var local_json_url = "/jPadma?url=http://pad.ma";
|
||||||
|
|
||||||
|
function callPadma(api, callback, extra_params) {
|
||||||
|
var url = local_json_url + api;
|
||||||
|
$.getJSON(url, function(json) {
|
||||||
|
callback(json, extra_params);
|
||||||
|
});
|
||||||
|
}
|
70
static/js/padmaList.js
Normal file
70
static/js/padmaList.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
$(document).ready(function() {
|
||||||
|
var apiUrl = "/list/videos?listId=" + listId;
|
||||||
|
callPadma(apiUrl, initList);
|
||||||
|
});
|
||||||
|
|
||||||
|
var padmaVideo = function(json, index) {
|
||||||
|
this.id = json.id;
|
||||||
|
this.index = index;
|
||||||
|
this.title = json.title;
|
||||||
|
this.poster = "http://pad.ma/" + this.id + "/poster.jpg";
|
||||||
|
this.wrapperHtml = this.getWrapperHtml()
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaVideo.prototype.getWrapperHtml = function() {
|
||||||
|
var that = this;
|
||||||
|
var videoElem = $('<div />');
|
||||||
|
videoElem.attr('id', that.id);
|
||||||
|
videoElem.addClass("videoWrapper");
|
||||||
|
var html = '';
|
||||||
|
html += "<div class='videoTitle'>";
|
||||||
|
html += this.title;
|
||||||
|
html += "</div>";
|
||||||
|
html += "<div class='videoPoster'>";
|
||||||
|
html += "<img src='" + this.poster + "' />";
|
||||||
|
html += "</div>";
|
||||||
|
html += "<div class='videoMeta'>";
|
||||||
|
html += "Loading...";
|
||||||
|
html += "</div>";
|
||||||
|
videoElem.html(html);
|
||||||
|
return videoElem;
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaVideo.prototype.init = function() {
|
||||||
|
var that = this;
|
||||||
|
$('#videos').append(that.wrapperHtml);
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaVideo.prototype.metaLoaded = function() {
|
||||||
|
var that = this;
|
||||||
|
$('#' + that.id + ' .videoMeta').html(that.meta.description_html);
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaVideo.prototype.layersLoaded = function() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaVideo.prototype.loadData = function() {
|
||||||
|
var that = this;
|
||||||
|
var metaUrl = "http://pad.ma/" + that.id + "/video.js";
|
||||||
|
$.getScript(metaUrl, function() {
|
||||||
|
that.meta = video;
|
||||||
|
that.metaLoaded();
|
||||||
|
var layersUrl = "http://pad.ma/" + that.id + "/layers.js";
|
||||||
|
$.getScript(layersUrl, function() {
|
||||||
|
that.layers = layers;
|
||||||
|
that.layersLoaded();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var padmaVideos = [];
|
||||||
|
function initList(json) {
|
||||||
|
var videos = json.videos;
|
||||||
|
for (v in videos) {
|
||||||
|
var thisVideo = new padmaVideo(videos[v], v);
|
||||||
|
padmaVideos.push(thisVideo);
|
||||||
|
}
|
||||||
|
}
|
64
static/js/padmaLists.js
Normal file
64
static/js/padmaLists.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
var padmaLists = []
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
callPadma("/list/get", initLists);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var padmaList = function(json) {
|
||||||
|
this.listId = json.listId;
|
||||||
|
this.description = json.description;
|
||||||
|
this.iconId = json.iconId;
|
||||||
|
this.iconPath = "http://pad.ma/" + this.iconId + "/poster.jpg";
|
||||||
|
this.title = json.title;
|
||||||
|
this.html = this.getHtml();
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaList.prototype.init = function() {
|
||||||
|
var that = this;
|
||||||
|
$('#lists').append(that.html);
|
||||||
|
}
|
||||||
|
|
||||||
|
padmaList.prototype.getHtml = function() {
|
||||||
|
var that = this;
|
||||||
|
var wrapperElem = $("<div />");
|
||||||
|
wrapperElem.addClass("listWrapper");
|
||||||
|
var titleElem = $('<div />');
|
||||||
|
titleElem.attr("id", this.listId);
|
||||||
|
titleElem.addClass("listTitle");
|
||||||
|
var titleHtml = "<a href='/list?id=" + this.listId + "'>" + this.title + "</a>";
|
||||||
|
console.log(titleHtml);
|
||||||
|
titleElem.html(titleHtml);
|
||||||
|
titleElem.hover(function() {
|
||||||
|
$(this).next().show();
|
||||||
|
}, function() {
|
||||||
|
$(this).next().hide();
|
||||||
|
});
|
||||||
|
wrapperElem.append(titleElem);
|
||||||
|
var innerElem = $('<div />');
|
||||||
|
innerElem.addClass("listDetail");
|
||||||
|
innerElem.hide();
|
||||||
|
var innerHtml = '';
|
||||||
|
innerHtml += "<div class='listImage'>";
|
||||||
|
innerHtml += "<img src='" + this.iconPath + "' />"
|
||||||
|
innerHtml += "</div>";
|
||||||
|
innerHtml += "<div class='listDescription'>";
|
||||||
|
innerHtml += this.description;
|
||||||
|
innerHtml += "</div>";
|
||||||
|
innerElem.html(innerHtml);
|
||||||
|
innerElem.css("min-height", "150px");
|
||||||
|
wrapperElem.append(innerElem);
|
||||||
|
return wrapperElem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initLists(json) {
|
||||||
|
var allLists = json['lists']
|
||||||
|
for (l in allLists) {
|
||||||
|
console.log(allLists[l]);
|
||||||
|
var list = new padmaList(allLists[l]);
|
||||||
|
// padmaLists.push(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
17
templates/base.html
Normal file
17
templates/base.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="/static/js/jquery.js"></script>
|
||||||
|
{% block head %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
20
templates/index.html
Normal file
20
templates/index.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/css/padmaApi.css" />
|
||||||
|
<script type="text/javascript" src="/static/js/padmaApi.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/padmaLists.js"></script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="header">
|
||||||
|
Playing with the Pad.ma API ...
|
||||||
|
</div>
|
||||||
|
<div id="lists">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
20
templates/list.html
Normal file
20
templates/list.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block head %}
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/css/padmaList.css" />
|
||||||
|
<script type="text/javascript" src="/static/js/padmaApi.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/padmaList.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var listId = "{{ listId|escapejs }}"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="videos">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
29
urls.py
Normal file
29
urls.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
import settings
|
||||||
|
|
||||||
|
# Uncomment the next two lines to enable the admin:
|
||||||
|
# from django.contrib import admin
|
||||||
|
# admin.autodiscover()
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
# Example:
|
||||||
|
# (r'^padmaApi/', include('padmaApi.foo.urls')),
|
||||||
|
(r'jPadma/', 'lists.views.fetchJson'),
|
||||||
|
(r'list/', 'lists.views.listDetail'),
|
||||||
|
(r'^$', 'lists.views.index'),
|
||||||
|
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
|
||||||
|
# to INSTALLED_APPS to enable admin documentation:
|
||||||
|
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
|
||||||
|
# Uncomment the next line to enable the admin:
|
||||||
|
# (r'^admin/(.*)', admin.site.root),
|
||||||
|
)
|
||||||
|
|
||||||
|
if settings.LOCAL_DEVELOPMENT:
|
||||||
|
urlpatterns += patterns("django.views",
|
||||||
|
url(r"%s(?P<path>.*)/$" % settings.MEDIA_URL[1:], "static.serve", {
|
||||||
|
"document_root": settings.MEDIA_ROOT,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
28
urls.py~
Normal file
28
urls.py~
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
import settings
|
||||||
|
|
||||||
|
# Uncomment the next two lines to enable the admin:
|
||||||
|
# from django.contrib import admin
|
||||||
|
# admin.autodiscover()
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
# Example:
|
||||||
|
# (r'^padmaApi/', include('padmaApi.foo.urls')),
|
||||||
|
(r'jPadma/', 'lists.views.fetchJson'),
|
||||||
|
(r'^$', 'lists.views.index'),
|
||||||
|
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
|
||||||
|
# to INSTALLED_APPS to enable admin documentation:
|
||||||
|
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
|
||||||
|
# Uncomment the next line to enable the admin:
|
||||||
|
# (r'^admin/(.*)', admin.site.root),
|
||||||
|
)
|
||||||
|
|
||||||
|
if settings.LOCAL_DEVELOPMENT:
|
||||||
|
urlpatterns += patterns("django.views",
|
||||||
|
url(r"%s(?P<path>.*)/$" % settings.MEDIA_URL[1:], "static.serve", {
|
||||||
|
"document_root": settings.MEDIA_ROOT,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user