Sanj
13 years ago
22 changed files with 707 additions and 3 deletions
@ -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 |
|||
|
@ -0,0 +1,6 @@ |
|||
*.sqlite |
|||
*.pyc |
|||
.DS_Store |
|||
.svn |
|||
svn-commit.tmp |
|||
svn-prop.tmp |
@ -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. |
@ -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. |
Binary file not shown.
@ -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() |
@ -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) |
@ -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,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) |
@ -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' |
|||
) |
@ -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:', |
|||
) |
@ -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 |
|||
|
|||
"""} |
|||
|
@ -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() |
@ -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> |
@ -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> |
@ -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 %} |
@ -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 %} |
@ -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 %} |
@ -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 %} |
@ -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