first commit
This commit is contained in:
parent
7757b548e8
commit
1f4e175da5
|
@ -1,3 +1,3 @@
|
|||
-e svn+http://code.djangoproject.com/svn/django/branches/releases/1.1.X/#egg=django
|
||||
-e bzr+http://code.0xdb.org/python-oxdjango/#egg=python-oxdjango
|
||||
South
|
||||
# -e svn+http://code.djangoproject.com/svn/django/branches/releases/1.1.X/#egg=django
|
||||
-e bzr+http://code.0xdb.org/python-ox/#egg=python-ox
|
||||
# South
|
||||
|
|
6
urlweb/.gitignore
vendored
Normal file
6
urlweb/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
*.sqlite
|
||||
*.pyc
|
||||
.DS_Store
|
||||
.svn
|
||||
svn-commit.tmp
|
||||
svn-prop.tmp
|
22
urlweb/LICENSE
Normal file
22
urlweb/LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2009 Nilesh Kapadia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
34
urlweb/README
Normal file
34
urlweb/README
Normal file
|
@ -0,0 +1,34 @@
|
|||
url-shortener
|
||||
=============
|
||||
|
||||
This is URL shortening application using the Django framework
|
||||
|
||||
The shortened URLs use the base 62 value of ids of the model they are
|
||||
stored in. A count of how many times the URLs are used is kept. The
|
||||
main page shows the 10 most recent and 10 most popular URLs.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
Download Blueprint from: http://www.blueprintcss.org/
|
||||
Copy the "blueprint" folder into static/css/ (which you may need to create)
|
||||
|
||||
Note that in a production installation, you'll want to have your web
|
||||
server serve the "static" folder instead of letting Django serve it.
|
||||
|
||||
Settings
|
||||
========
|
||||
|
||||
The following values need to be set in settings.py:
|
||||
|
||||
SITE_NAME
|
||||
The name of the site (e.g. 'urlshorteningsite.com')
|
||||
|
||||
SITE_BASE_URL
|
||||
The base URL of the site. This can be based on the SITE_NAME:
|
||||
SITE_BASE_URL = 'http://' + SITE_NAME + '/'
|
||||
|
||||
REQUIRE_LOGIN
|
||||
Set REQUIRE_LOGIN to True if you want to require that a user be
|
||||
logged in to be able to submit a URL to be shortened. Set it
|
||||
to False if you do not want to require login to submit a URL.
|
0
urlweb/__init__.py
Normal file
0
urlweb/__init__.py
Normal file
BIN
urlweb/database.sqlite
Normal file
BIN
urlweb/database.sqlite
Normal file
Binary file not shown.
20
urlweb/deploy/deploy.wsgi
Normal file
20
urlweb/deploy/deploy.wsgi
Normal file
|
@ -0,0 +1,20 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
# redirect sys.stdout to sys.stderr for bad libraries like geopy that uses
|
||||
# print statements for optional import exceptions.
|
||||
sys.stdout = sys.stderr
|
||||
|
||||
from os.path import abspath, dirname, join
|
||||
from site import addsitedir
|
||||
|
||||
PROJECT_ROOT = abspath(join(dirname(__file__), "../"))
|
||||
|
||||
sys.path.insert(0, PROJECT_ROOT)
|
||||
sys.path.insert(0, abspath(join(dirname(__file__), "../../")))
|
||||
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
|
||||
os.environ["DJANGO_SETTINGS_MODULE"] = "urlweb.settings"
|
||||
|
||||
application = WSGIHandler()
|
11
urlweb/manage.py
Executable file
11
urlweb/manage.py
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env 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)
|
108
urlweb/settings.py
Normal file
108
urlweb/settings.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Django settings for urlweb project.
|
||||
import os, logging
|
||||
#from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS
|
||||
|
||||
logging.basicConfig(
|
||||
level = logging.DEBUG,
|
||||
format = '%(asctime)s %(levelname)s %(message)s',
|
||||
)
|
||||
|
||||
logging.debug("Reading settings...")
|
||||
|
||||
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@domain.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||
#DATABASE_NAME = '' # Or path to database file if using sqlite3.
|
||||
DATABASE_NAME = os.path.join(PROJECT_PATH, 'database.sqlite')
|
||||
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 = ''
|
||||
|
||||
# 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 = ''
|
||||
|
||||
# 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 = '/media/'
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = '#### CHANGE_ME ####'
|
||||
|
||||
# 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',
|
||||
# 'django.middleware.transaction.TransactionMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'urlweb.urls'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
os.path.join(PROJECT_PATH, 'templates')
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.admin',
|
||||
'urlweb.shortener',
|
||||
)
|
||||
|
||||
STATIC_DOC_ROOT = os.path.join(PROJECT_PATH, 'static')
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
#TEMPLATE_CONTEXT_PROCESSORS += (
|
||||
# 'django.core.context_processors.request',
|
||||
# )
|
||||
|
||||
SITE_NAME = 'localhost:8000'
|
||||
SITE_BASE_URL = 'http://' + SITE_NAME + '/'
|
||||
REQUIRE_LOGIN = True
|
||||
|
||||
try:
|
||||
from local_settings import *
|
||||
except:
|
||||
pass
|
0
urlweb/shortener/__init__.py
Normal file
0
urlweb/shortener/__init__.py
Normal file
9
urlweb/shortener/admin.py
Normal file
9
urlweb/shortener/admin.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from urlweb.shortener.models import Link
|
||||
|
||||
class LinkAdmin(admin.ModelAdmin):
|
||||
model = Link
|
||||
extra = 3
|
||||
|
||||
admin.site.register(Link, LinkAdmin)
|
58
urlweb/shortener/baseconv.py
Executable file
58
urlweb/shortener/baseconv.py
Executable file
|
@ -0,0 +1,58 @@
|
|||
"""
|
||||
Convert numbers from base 10 integers to base X strings and back again.
|
||||
|
||||
Original: http://www.djangosnippets.org/snippets/1431/
|
||||
|
||||
Sample usage:
|
||||
|
||||
>>> base20 = BaseConverter('0123456789abcdefghij')
|
||||
>>> base20.from_decimal(1234)
|
||||
'31e'
|
||||
>>> base20.to_decimal('31e')
|
||||
1234
|
||||
"""
|
||||
|
||||
class BaseConverter(object):
|
||||
decimal_digits = "0123456789"
|
||||
|
||||
def __init__(self, digits):
|
||||
self.digits = digits
|
||||
|
||||
def from_decimal(self, i):
|
||||
return self.convert(i, self.decimal_digits, self.digits)
|
||||
|
||||
def to_decimal(self, s):
|
||||
return int(self.convert(s, self.digits, self.decimal_digits))
|
||||
|
||||
def convert(number, fromdigits, todigits):
|
||||
# Based on http://code.activestate.com/recipes/111286/
|
||||
if str(number)[0] == '-':
|
||||
number = str(number)[1:]
|
||||
neg = 1
|
||||
else:
|
||||
neg = 0
|
||||
|
||||
# make an integer out of the number
|
||||
x = 0
|
||||
for digit in str(number):
|
||||
x = x * len(fromdigits) + fromdigits.index(digit)
|
||||
|
||||
# create the result in base 'len(todigits)'
|
||||
if x == 0:
|
||||
res = todigits[0]
|
||||
else:
|
||||
res = ""
|
||||
while x > 0:
|
||||
digit = x % len(todigits)
|
||||
res = todigits[digit] + res
|
||||
x = int(x / len(todigits))
|
||||
if neg:
|
||||
res = '-' + res
|
||||
return res
|
||||
convert = staticmethod(convert)
|
||||
|
||||
bin = BaseConverter('01')
|
||||
hexconv = BaseConverter('0123456789ABCDEF')
|
||||
base62 = BaseConverter(
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'
|
||||
)
|
60
urlweb/shortener/models.py
Normal file
60
urlweb/shortener/models.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
#from django.contrib.auth.models import User
|
||||
from django import forms
|
||||
|
||||
from urlweb.shortener.baseconv import base62
|
||||
|
||||
class Link(models.Model):
|
||||
"""
|
||||
Model that represents a shortened URL
|
||||
|
||||
# Initialize by deleting all Link objects
|
||||
>>> Link.objects.all().delete()
|
||||
|
||||
# Create some Link objects
|
||||
>>> link1 = Link.objects.create(url="http://www.google.com/")
|
||||
>>> link2 = Link.objects.create(url="http://www.nileshk.com/")
|
||||
|
||||
# Get base 62 representation of id
|
||||
>>> link1.to_base62()
|
||||
'B'
|
||||
>>> link2.to_base62()
|
||||
'C'
|
||||
|
||||
# Set SITE_BASE_URL to something specific
|
||||
>>> settings.SITE_BASE_URL = 'http://uu4.us/'
|
||||
|
||||
# Get short URL's
|
||||
>>> link1.short_url()
|
||||
'http://uu4.us/B'
|
||||
>>> link2.short_url()
|
||||
'http://uu4.us/C'
|
||||
|
||||
# Test usage_count
|
||||
>>> link1.usage_count
|
||||
0
|
||||
>>> link1.usage_count += 1
|
||||
>>> link1.usage_count
|
||||
1
|
||||
|
||||
"""
|
||||
url = models.TextField(unique=True, max_length=200000)
|
||||
date_submitted = models.DateTimeField(default=datetime.datetime.now())
|
||||
usage_count = models.IntegerField(default=0)
|
||||
|
||||
def to_base62(self):
|
||||
return base62.from_decimal(self.id)
|
||||
|
||||
def short_url(self):
|
||||
return settings.SITE_BASE_URL + self.to_base62()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.to_base62() + ' : ' + self.url
|
||||
|
||||
class LinkSubmitForm(forms.Form):
|
||||
u = forms.CharField(
|
||||
label='URL to be shortened:',
|
||||
)
|
89
urlweb/shortener/tests.py
Normal file
89
urlweb/shortener/tests.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
"""
|
||||
Tests for views
|
||||
"""
|
||||
|
||||
__test__ = {"doctest": """
|
||||
|
||||
# Initialize by deleting all Link objects
|
||||
>>> from models import Link
|
||||
>>> Link.objects.all().delete()
|
||||
|
||||
>>> from django.test import Client
|
||||
>>> client = Client()
|
||||
|
||||
# Index page
|
||||
>>> r = client.get('/')
|
||||
>>> r.status_code # /
|
||||
200
|
||||
>>> r.template[0].name
|
||||
'shortener/index.html'
|
||||
|
||||
# Turn off logged-in requirement and set base URL
|
||||
>>> from django.conf import settings
|
||||
>>> settings.REQUIRE_LOGIN = False
|
||||
>>> settings.SITE_BASE_URL = 'http://uu4.us/'
|
||||
|
||||
# Empty submission should forward to error page
|
||||
>>> r = client.get('/submit/')
|
||||
>>> r.status_code # /submit/
|
||||
200
|
||||
>>> r.template[0].name # /submit/
|
||||
'shortener/submit_failed.html'
|
||||
|
||||
# Submit a URL
|
||||
>>> url = 'http://www.google.com/'
|
||||
>>> r = client.get('/submit/', {'u': url})
|
||||
>>> r.status_code # /submit/u?=http%3A%2F%2Fwww.google.com%2F
|
||||
200
|
||||
>>> r.template[0].name
|
||||
'shortener/submit_success.html'
|
||||
>>> link = r.context[0]['link']
|
||||
>>> link.to_base62()
|
||||
'B'
|
||||
>>> link.short_url()
|
||||
'http://uu4.us/B'
|
||||
>>> link_from_db = Link.objects.get(url = url)
|
||||
>>> base62 = link_from_db.to_base62()
|
||||
>>> base62
|
||||
'B'
|
||||
>>> link_from_db.usage_count
|
||||
0
|
||||
|
||||
# Short URL for previously submitted URL
|
||||
>>> r = client.get('/' + base62)
|
||||
>>> r.status_code # '/' + base62
|
||||
301
|
||||
>>> r['Location']
|
||||
'http://www.google.com/'
|
||||
|
||||
# Invalid URL should get a 404
|
||||
>>> r = client.get('/INVALID')
|
||||
>>> r.status_code # /INVALID
|
||||
404
|
||||
|
||||
# Index now shows link in recent_links / most_popular_links
|
||||
>>> r = client.get('/')
|
||||
>>> r.status_code # /
|
||||
200
|
||||
>>> r.template[0].name
|
||||
'shortener/index.html'
|
||||
>>> context = r.context[0]
|
||||
>>> len(context['recent_links'])
|
||||
1
|
||||
>>> len(context['most_popular_links'])
|
||||
1
|
||||
|
||||
# Get info on Link
|
||||
>>> r = client.get('/info/' + base62)
|
||||
>>> r.status_code # info
|
||||
200
|
||||
>>> r.template[0].name
|
||||
'shortener/link_info.html'
|
||||
>>> link = r.context[0]['link']
|
||||
>>> link.url
|
||||
u'http://www.google.com/'
|
||||
>>> link.usage_count # Usage count should be 1 now
|
||||
1
|
||||
|
||||
"""}
|
||||
|
109
urlweb/shortener/views.py
Normal file
109
urlweb/shortener/views.py
Normal file
|
@ -0,0 +1,109 @@
|
|||
import logging
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth import authenticate
|
||||
from django.views.generic import list_detail
|
||||
from django.shortcuts import get_object_or_404, get_list_or_404, render_to_response
|
||||
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponsePermanentRedirect
|
||||
from django.utils import simplejson
|
||||
from django.template import RequestContext
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.db import transaction
|
||||
from django.conf import settings
|
||||
|
||||
from urlweb.shortener.baseconv import base62
|
||||
from urlweb.shortener.models import Link, LinkSubmitForm
|
||||
|
||||
def follow(request, base62_id):
|
||||
"""
|
||||
View which gets the link for the given base62_id value
|
||||
and redirects to it.
|
||||
"""
|
||||
key = base62.to_decimal(base62_id)
|
||||
link = get_object_or_404(Link, pk = key)
|
||||
link.usage_count += 1
|
||||
link.save()
|
||||
return HttpResponse("<script>window.location = '%s';</script>" % (link.url,));
|
||||
|
||||
def default_values(request, link_form=None):
|
||||
"""
|
||||
Return a new object with the default values that are typically
|
||||
returned in a request.
|
||||
"""
|
||||
if not link_form:
|
||||
link_form = LinkSubmitForm()
|
||||
allowed_to_submit = is_allowed_to_submit(request)
|
||||
return { 'show_bookmarklet': allowed_to_submit,
|
||||
'show_url_form': allowed_to_submit,
|
||||
'site_name': settings.SITE_NAME,
|
||||
'site_base_url': settings.SITE_BASE_URL,
|
||||
'link_form': link_form,
|
||||
}
|
||||
|
||||
def info(request, base62_id):
|
||||
"""
|
||||
View which shows information on a particular link
|
||||
"""
|
||||
key = base62.to_decimal(base62_id)
|
||||
link = get_object_or_404(Link, pk = key)
|
||||
values = default_values(request)
|
||||
values['link'] = link
|
||||
return render_to_response(
|
||||
'shortener/link_info.html',
|
||||
values,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def submit(request):
|
||||
"""
|
||||
View for submitting a URL
|
||||
"""
|
||||
if settings.REQUIRE_LOGIN and not request.user.is_authenticated():
|
||||
# TODO redirect to an error page
|
||||
foo = 'foo'
|
||||
# raise Http404
|
||||
url = None
|
||||
link_form = None
|
||||
if request.GET:
|
||||
link_form = LinkSubmitForm(request.GET)
|
||||
elif request.POST:
|
||||
link_form = LinkSubmitForm(request.POST)
|
||||
if link_form and link_form.is_valid():
|
||||
url = link_form.cleaned_data['u']
|
||||
link = None
|
||||
try:
|
||||
link = Link.objects.get(url = url)
|
||||
except Link.DoesNotExist:
|
||||
pass
|
||||
if link == None:
|
||||
new_link = Link(url = url)
|
||||
new_link.save()
|
||||
link = new_link
|
||||
values = default_values(request)
|
||||
values['link'] = link
|
||||
return render_to_response(
|
||||
'shortener/submit_success.html',
|
||||
values,
|
||||
context_instance=RequestContext(request))
|
||||
values = default_values(request, link_form=link_form)
|
||||
return render_to_response(
|
||||
'shortener/submit_failed.html',
|
||||
values,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def index(request):
|
||||
"""
|
||||
View for main page (lists recent and popular links)
|
||||
"""
|
||||
values = default_values(request)
|
||||
values['recent_links'] = Link.objects.all().order_by('-date_submitted')[0:10]
|
||||
values['most_popular_links'] = Link.objects.all().order_by('-usage_count')[0:10]
|
||||
return render_to_response(
|
||||
'shortener/index.html',
|
||||
values,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def is_allowed_to_submit(request):
|
||||
"""
|
||||
Return true if user is allowed to submit URLs
|
||||
"""
|
||||
return not settings.REQUIRE_LOGIN or request.user.is_authenticated()
|
12
urlweb/templates/404.html
Normal file
12
urlweb/templates/404.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>404</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>404 Not Found</h1>
|
||||
<a href="/">Home</a>
|
||||
</body>
|
||||
</html>
|
65
urlweb/templates/base.html
Normal file
65
urlweb/templates/base.html
Normal file
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>{% block title %}URLs{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="/static/css/blueprint/screen.css"
|
||||
type="text/css" media="screen, projection"/>
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="static/css/blueprint/print.css" type="text/css" media="print"/>
|
||||
|
||||
<!--[if lt IE 8]>
|
||||
<link rel="stylesheet"
|
||||
href="/static/css/blueprint/ie.css"
|
||||
type="text/css" media="screen, projection"/>
|
||||
<![endif]-->
|
||||
|
||||
<link rel="stylesheet"
|
||||
href="/static/css/blueprint/plugins/fancy-type/screen.css"
|
||||
type="text/css" media="screen, projection"/>
|
||||
|
||||
<script type="text/javascript"
|
||||
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
|
||||
</script>
|
||||
|
||||
{% block extra_head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="span-24 last">
|
||||
<h1><a href="{{ site_base_url }}">{{ site_name }}</a></h1>
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="span-24 last">
|
||||
{% block content %}{% endblock %}
|
||||
<hr/>
|
||||
</div>
|
||||
|
||||
<div class="span-19 colborder">
|
||||
{% if show_url_form %}
|
||||
{% block url_form %}
|
||||
<form name="url_form" action="/submit/" method="get">
|
||||
{{ link_form.as_p }}
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="span-4 last">
|
||||
{% if show_bookmarklet %}
|
||||
{% block bookmarklet %}
|
||||
<br/>
|
||||
Bookmarklet (drag this to your bookmarks bar):<br/>
|
||||
<a href="javascript:(function(){var a=window,b=document,c=encodeURIComponent;d=a.open('{{ site_base_url }}submit/?u='+c(b.location));})();">Shorten with {{ site_name }}</a>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% block extra_body %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
27
urlweb/templates/shortener/index.html
Normal file
27
urlweb/templates/shortener/index.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>Recent Links</h2>
|
||||
<ul>
|
||||
{% for link in recent_links %}
|
||||
<li><a href="{{ link.short_url }}">{{ link.url }}</a> (Score: {{ link.usage_count }})
|
||||
(<a href="{% url shortener.views.info link.to_base62 %}">Info</a>)</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h2>Most Popular Links</h2>
|
||||
<ul>
|
||||
{% for link in most_popular_links %}
|
||||
<li><a href="{{ link.short_url }}">{{ link.url }}</a> (Score: {{ link.usage_count }})
|
||||
(<a href="{% url shortener.views.info link.to_base62 %}">Info</a>)</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_body %}
|
||||
<script>
|
||||
$(document).ready(
|
||||
function() {
|
||||
$("#id_u").focus().select();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
12
urlweb/templates/shortener/link_info.html
Normal file
12
urlweb/templates/shortener/link_info.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Link info{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
<a href="{{ link.short_url }}">{{ link.url }}</a> (Score: {{ link.usage_count }})
|
||||
<br/>
|
||||
<br/>
|
||||
Submitted on: {{ link.date_submitted|date:"M d, Y" }}
|
||||
</p>
|
||||
{% endblock %}
|
16
urlweb/templates/shortener/submit_failed.html
Normal file
16
urlweb/templates/shortener/submit_failed.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}URL submission failed{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>URL submission failed</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_body %}
|
||||
<script>
|
||||
$(document).ready(
|
||||
function() {
|
||||
$("#id_u").focus().select();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
29
urlweb/templates/shortener/submit_success.html
Normal file
29
urlweb/templates/shortener/submit_success.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}URL shortened{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
The following URL:<br/>
|
||||
<pre>
|
||||
{{ link.url }}
|
||||
</pre>
|
||||
<br/>
|
||||
Was shortened to:<br/>
|
||||
<input id="link" type="text" size="30"
|
||||
value="{{ link.short_url }}"/>
|
||||
<br/>
|
||||
<br/>
|
||||
Score: {{ link.usage_count }}
|
||||
(<a href="{% url shortener.views.info link.to_base62 %}">Info</a>)
|
||||
<br/>
|
||||
<br/>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_body %}
|
||||
<script>
|
||||
$(document).ready(
|
||||
function() {
|
||||
$("#link").focus().select();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
17
urlweb/urls.py
Normal file
17
urlweb/urls.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
(r'^$', 'shortener.views.index'),
|
||||
(r'^admin/(.*)', admin.site.root),
|
||||
(r'^submit/$', 'shortener.views.submit'),
|
||||
(r'^(?P<base62_id>\w+)$', 'shortener.views.follow'),
|
||||
(r'^info/(?P<base62_id>\w+)$', 'shortener.views.info'),
|
||||
|
||||
(r'^static/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': settings.STATIC_DOC_ROOT}),
|
||||
)
|
Loading…
Reference in New Issue
Block a user