This commit is contained in:
Greg Taylor 2008-12-15 04:03:49 +00:00
parent 98b32580b5
commit f45aa57751
21 changed files with 0 additions and 0 deletions

View file

0
game/web/apps/news/__init__.py Executable file
View file

47
game/web/apps/news/models.py Executable file
View file

@ -0,0 +1,47 @@
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
class NewsTopic(models.Model):
"""
Represents a news topic.
"""
name = models.CharField(max_length=75, unique=True)
description = models.TextField(blank=True)
icon = models.ImageField(upload_to='newstopic_icons', default='newstopic_icons/default.png', blank=True, help_text="Image for the news topic.")
def __str__(self):
try:
return self.name
except:
return "Invalid"
class Meta:
ordering = ['name']
class Admin:
list_display = ('name', 'icon')
admin.site.register(NewsTopic)
class NewsEntry(models.Model):
"""
An individual news entry.
"""
author = models.ForeignKey(User, related_name='author')
title = models.CharField(max_length=255)
body = models.TextField()
topic = models.ForeignKey(NewsTopic, related_name='newstopic')
date_posted = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ('-date_posted',)
verbose_name_plural = "News entries"
class Admin:
list_display = ('title', 'author', 'topic', 'date_posted')
list_filter = ('topic',)
search_fields = ['title']
admin.site.register(NewsEntry)

8
game/web/apps/news/urls.py Executable file
View file

@ -0,0 +1,8 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('webapps.news.views',
(r'^show/(?P<entry_id>\d+)/$', 'show_news'),
(r'^archive/$', 'news_archive'),
(r'^search/$', 'search_form'),
(r'^search/results/$', 'search_results'),
)

126
game/web/apps/news/views.py Executable file
View file

@ -0,0 +1,126 @@
"""
This is a very simple news application, with most of the expected features
like:
* News categories/topics
* Searchable archives
"""
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
import django.views.generic.list_detail as gv_list_detail
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django import forms
from django.db.models import Q
from webapps.news.models import NewsTopic, NewsEntry
# The sidebar text to be included as a variable on each page. There's got to
# be a better, cleaner way to include this on every page.
sidebar = """
<p class='doNotDisplay doNotPrint'>This page&rsquo;s menu:</p>
<ul id='side-bar'>
<li><a href='/news/archive'>News Archive</a></li>
<li><a href='/news/search'>Search News</a></li>
</ul>
"""
class SearchForm(forms.Form):
"""
Class to represent a news search form under Django's newforms. This is used
to validate the input on the search_form view, as well as the search_results
view when we're picking the query out of GET. This makes searching safe
via the search form or by directly inputing values via GET key pairs.
"""
search_terms = forms.CharField(max_length=100, min_length=3, required=True)
def show_news(request, entry_id):
"""
Show an individual news entry. Display some basic information along with
the title and content.
"""
news_entry = get_object_or_404(NewsEntry, id=entry_id)
pagevars = {
"page_title": "News Entry",
"news_entry": news_entry,
"sidebar": sidebar
}
context_instance = RequestContext(request)
return render_to_response('news/show_entry.html', pagevars, context_instance)
def news_archive(request):
"""
Shows an archive of news entries.
TODO: Expand this a bit to allow filtering by month/year.
"""
news_entries = NewsEntry.objects.all().order_by('-date_posted')
# TODO: Move this to either settings.py or the SQL configuration.
entries_per_page = 15
pagevars = {
"page_title": "News Archive",
"browse_url": "/news/archive",
"sidebar": sidebar
}
return gv_list_detail.object_list(request, news_entries, template_name='news/archive.html', extra_context=pagevars, paginate_by=entries_per_page)
def search_form(request):
"""
Render the news search form. Don't handle much validation at all. If the
user enters a search term that meets the minimum, send them on their way
to the results page.
"""
if request.method == 'GET':
# A GET request was sent to the search page, load the value and
# validate it.
search_form = SearchForm(request.GET)
if search_form.is_valid():
# If the input is good, send them to the results page with the
# query attached in GET variables.
return HttpResponseRedirect('/news/search/results/?search_terms='+ search_form.cleaned_data['search_terms'])
else:
# Brand new search, nothing has been sent just yet.
search_form = SearchForm()
pagevars = {
"page_title": "Search News",
"search_form": search_form,
"debug": debug,
"sidebar": sidebar
}
context_instance = RequestContext(request)
return render_to_response('news/search_form.html', pagevars, context_instance)
def search_results(request):
"""
Shows an archive of news entries. Use the generic news browsing template.
"""
# TODO: Move this to either settings.py or the SQL configuration.
entries_per_page = 15
# Load the form values from GET to validate against.
search_form = SearchForm(request.GET)
# You have to call is_valid() or cleaned_data won't be populated.
valid_search = search_form.is_valid()
# This is the safe data that we can pass to queries without huge worry of
# badStuff(tm).
cleaned_get = search_form.cleaned_data
# Perform searches that match the title and contents.
# TODO: Allow the user to specify what to match against and in what
# topics/categories.
news_entries = NewsEntry.objects.filter(Q(title__contains=cleaned_get['search_terms']) | Q(body__contains=cleaned_get['search_terms']))
pagevars = {
"page_title": "Search Results",
"searchtext": cleaned_get['search_terms'],
"browse_url": "/news/search/results",
"sidebar": sidebar
}
return gv_list_detail.object_list(request, news_entries, template_name='news/archive.html', extra_context=pagevars, paginate_by=entries_per_page)

View file

View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View file

@ -0,0 +1,5 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('webapps.website.views',
(r'^$', 'page_index'),
)

View file

@ -0,0 +1,57 @@
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.contrib.auth.models import User
from apps.objects.models import Object
from webapps.news.models import NewsEntry
"""
This file contains the generic, assorted views that don't fall under one of
the other applications.
"""
def page_index(request):
"""
Main root page.
"""
# Some misc. configurable stuff.
# TODO: Move this to either SQL or settings.py based configuration.
fpage_player_limit = 4
fpage_news_entries = 2
# A QuerySet of recent news entries.
news_entries = NewsEntry.objects.all().order_by('-date_posted')[:fpage_news_entries]
# Dictionary containing database statistics.
objstats = Object.objects.object_totals()
# A QuerySet of the most recently connected players.
recent_players = Object.objects.get_recently_connected_users()[:fpage_player_limit]
pagevars = {
"page_title": "Front Page",
"news_entries": news_entries,
"players_connected_recent": recent_players,
"num_players_connected": Object.objects.get_connected_players().count(),
"num_players_registered": Object.objects.num_total_players(),
"num_players_connected_recent": Object.objects.get_recently_connected_users().count(),
"num_players_registered_recent": Object.objects.get_recently_created_users().count(),
"num_players": objstats["players"],
"num_rooms": objstats["rooms"],
"num_things": objstats["things"],
"num_exits": objstats["exits"],
}
context_instance = RequestContext(request)
return render_to_response('index.html', pagevars, context_instance)
def to_be_implemented(request):
"""
A notice letting the user know that this particular feature hasn't been
implemented yet.
"""
pagevars = {
"page_title": "To Be Implemented...",
}
context_instance = RequestContext(request)
return render_to_response('tbi.html', pagevars, context_instance)

View file

@ -0,0 +1,9 @@
from apps.config.models import ConfigValue
def general_context(request):
"""
Returns common Evennia-related context stuff.
"""
return {
'game_name': ConfigValue.objects.get_configvalue('site_name'),
}

View file

@ -0,0 +1,87 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-AU">
<head>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="author" content="haran" />
<meta name="generator" content="haran" />
{% if sidebar %}
<link rel="stylesheet" type="text/css" href="{{MEDIA_URL}}css/prosimii-screen-alt.css" media="screen" title="Prosimii (Sidebar)" />
{% else %}
<link rel="stylesheet" type="text/css" href="{{MEDIA_URL}}css/prosimii-screen.css" media="screen" title="Prosimii" />
{% endif %}
<link rel="stylesheet alternative" type="text/css" href="{{MEDIA_URL}}css/prosimii-print.css" media="screen" title="Print Preview" />
<link rel="stylesheet" type="text/css" href="{{MEDIA_URL}}css/prosimii-print.css" media="print" />
{% block header_ext %}
{% endblock %}
<title>{{game_name}} - {% if flatpage %}{{flatpage.title}}{% else %}{% block titleblock %}{{page_title}}{% endblock %}{% endif %}</title>
</head>
<body>
<!-- For non-visual user agents: -->
<div id="top"><a href="#main-copy" class="doNotDisplay doNotPrint">Skip to main content.</a></div>
<!-- ##### Header ##### -->
<div id="header">
<div class="superHeader">
<span>Related Sites:</span>
<a href="http://evennia.com" title="The Python-based MUD server">Evennia</a> |
<a href="http://www.oswd.org/designs/search/designer/id/3013/" title="Other designs by haran">haran&rsquo;s Designs</a>
</div>
<div class="midHeader">
<h1 class="headerTitle" lang="la">{{game_name}}</h1>
<div class="headerSubTitle" title="Slogan">
<!-- Insert a slogan here if you want -->
&nbsp;
</div>
<br class="doNotDisplay doNotPrint" />
<div class="headerLinks">
<span class="doNotDisplay">Tools:</span>
{% if user.is_authenticated %}
<a href="/accounts/logout/">Log Out &laquo;</a>
<span class="doNotDisplay">|</span>
Logged in as {{user.username}} &laquo;
{% else %}
<a href="/accounts/login/">Log In &laquo;</a>
<span class="doNotDisplay">|</span>
<a href="/tbi">Register &laquo;</a>
{% endif %}
</div>
</div>
<div class="subHeader">
<span class="doNotDisplay">Navigation:</span>
<a href="/">Home</a> |
<a href="/about/">About</a> |
<a href="/tbi">Documentation</a> |
<a href="/admin/">Admin Interface</a>
</div>
</div>
<!-- ##### Side Menu ##### -->
{% block sidebar %}{% endblock %}
<!-- ##### Main Copy ##### -->
<div id="main-copy">
{% block content %}
{% endblock %}
</div>
<!-- ##### Footer ##### -->
<div id="footer">
<span class="doNotPrint">
Powered by
<a href="http://evennia.com">Evennia</a><br />
</span>
</div>
</body>
</html>

View file

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block header_ext %}
{% endblock %}
{% block sidebar %}
{% endblock %}
{% block content %}
<h1 id="alt-layout">{{flatpage.title}}</h1>
{{flatpage.content}}
{% endblock %}

View file

@ -0,0 +1,77 @@
{% extends "base.html" %}
{% block sidebar %}
{% endblock %}
{% block header_ext %}
{% endblock %}
{% block content %}
<div class="rowOfBoxes">
<div class="twoThirds noBorderOnLeft">
<h1>Welcome!</h1>
<p>Welcome to your new installation of Evennia, your friendly
neighborhood next-generation MUD server. You'll want to customize
this file, webtemplates/prosimii/index.html, to have a more
valid welcome message. Should you have any questions, concerns,
ideas, or bug reports, head over to the
<a href="http://evennia.com">Evennia community</a> and
speak up!</p>
</div>
<div class="oneThird">
<h1>Latest News</h1>
{% for entry in news_entries %}
<a href="/news/show/{{entry.id}}" class="newsHeading">{{entry.topic.name}}: {{entry.title}}</a>
<p class="newsDate">By {{entry.author.username}} on {{entry.date_posted|time}}</p>
<p class="newsSummary">{{entry.body|truncatewords:20}}</p>
{% endfor %}
<div class="more"><a href="/news/archive">More News &raquo;</a></div>
<p class="filler"><!-- Filler para to extend left vertical line --></p>
</div>
</div>
<div class="rowOfBoxes dividingBorderAbove">
<div class="quarter noBorderOnLeft">
<h1>Players</h1>
<p>
There are currently <strong>{{num_players_connected}}</strong> connected,
and a total of <strong>{{num_players_registered}}</strong> registered. Of these, <strong>{{num_players_registered_recent}}</strong> were created this week, and <strong>{{num_players_connected_recent}}</strong> have connected within the last seven days.
</p>
</div>
<div class="quarter">
<h1>Recently Connected</h1>
<ul>
{% for player in players_connected_recent %}
<li>{{player.username}} -- <em>{{player.last_login|timesince}} ago</em></li>
{% endfor %}
</ul>
<p class="filler"><!-- Filler para to extend left vertical line --></p>
</div>
<div class="quarter">
<h1>Database Stats</h1>
<ul>
<li>{{num_players}} players</li>
<li>{{num_rooms}} rooms</li>
<li>{{num_things}} things</li>
<li>{{num_exits}} exits</li>
</ul>
</div>
<div class="quarter">
<h1>Evennia</h1>
<p><a href="http://evennia.com">Evennia</a> is MUD server built in
<a href="http://python.org">Python</a>, on top of the
<a href="http://twistedmatrix.com">Twisted</a> and
<a href="http://djangoproject.com">Django</a> frameworks. This
combination of technology allows for the quick and easy creation
of games, as simple as complex as one desires.</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,51 @@
{% extends "base.html" %}
{% block header_ext %}
{% endblock %}
{% block sidebar %}
{{sidebar}}
{% endblock %}
{% block content %}
<h1 id="alt-layout">{{page_title}}</h1>
<strong>Navigation:</strong> <a href="{{browse_url}}/?page=1&search_terms={{searchtext|urlencode}}">First</a> |
{% if has_previous %}
<a href="{{browse_url}}/?page={{previous}}&search_terms={{searchtext|urlencode}}">Prev</a>
{% else %}
Prev
{% endif %}
| <em>{{page}}</em> of <em>{{pages}}</em> pages |
{% if has_next %}
<a href="{{browse_url}}/?page={{next}}&search_terms={{searchtext|urlencode}}">Next</a>
{% else %}
Next
{% endif %}
| <a href="{{browse_url}}/?page={{pages}}&search_terms={{searchtext|urlencode}}">Last</a>
{% for entry in object_list %}
<a href="/news/show/{{entry.id}}" class="newsHeading">{{entry.topic.name}}: {{entry.title}}</a>
<p class="newsDate">By {{entry.author.username}} on {{entry.date_posted|time}}</p>
<p class="newsSummary">{{entry.body|truncatewords:80}}</p>
{% endfor %}
<strong>Navigation:</strong> <a href="{{browse_url}}/?page=1&search_terms={{searchtext|urlencode}}">First</a> |
{% if has_previous %}
<a href="{{browse_url}}/?page={{previous}}&search_terms={{searchtext|urlencode}}">Prev</a>
{% else %}
Prev
{% endif %}
| <em>{{page}}</em> of <em>{{pages}}</em> pages |
{% if has_next %}
<a href="{{browse_url}}/?page={{next}}&search_terms={{searchtext|urlencode}}">Next</a>
{% else %}
Next
{% endif %}
| <a href="{{browse_url}}/?page={{pages}}&search_terms={{searchtext|urlencode}}">Last</a>
{% endblock %}

View file

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block header_ext %}
{% endblock %}
{% block sidebar %}
{{sidebar}}
{% endblock %}
{% block content %}
<h1 id="alt-layout">Search News</h1>
<p>Enter a search term or phrase to search by. Matches will be made against
news titles and their contents. Searches must be at least three characters
long.</p>
<form method="GET">
{{search_form.search_terms}}
<button type="Submit">Search</button>
</form>
{% endblock %}

View file

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block header_ext %}
{% endblock %}
{% block sidebar %}
{{sidebar}}
{% endblock %}
{% block content %}
<h1 id="alt-layout">{{news_entry.topic.name}}: {{news_entry.title}}</h1>
<p class="newsDate">By {{news_entry.author.username}} on {{news_entry.date_posted|time}}</p>
<p class="newsSummary">{{news_entry.body}}</p>
{% endblock %}

View file

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block titleblock %}
Logged Out
{% endblock %}
{% block content %}
<h1 id="alt-layout">Logged Out</h1>
<p>You have been logged out.</p>
{% endblock %}

View file

@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block titleblock %}
Login
{% endblock %}
{% block content %}
<h1 id="alt-layout">Login</h1>
{% if user.is_authenticated %}
<p>You are already logged in!</p>
{% else %}
{% if form.has_errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action=".">
<table>
<tr>
<td><label for="id_username">Username:</label></td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td><label for="id_password">Password:</label></td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="Login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% block header_ext %}
{% endblock %}
{% block sidebar %}
{% endblock %}
{% block content %}
<h1 id="alt-layout">To Be Implemented...</h1>
<p>
This feature has yet to be implemented, but rest assured that it will be!
<br />
<br />
<br />
Eventually...
</p>
{% endblock %}

40
game/web/urls.py Executable file
View file

@ -0,0 +1,40 @@
#
# File that determines what each URL points to. This uses _Python_ regular
# expressions, not Perl's.
#
# See:
# http://diveintopython.org/regular_expressions/street_addresses.html#re.matching.2.3
#
from django.conf.urls.defaults import *
from django.conf import settings
from django.contrib import admin
urlpatterns = patterns('',
# User Authentication
url(r'^accounts/login', 'django.contrib.auth.views.login'),
url(r'^accounts/logout', 'django.contrib.auth.views.logout'),
# Front page
url(r'^', include('webapps.website.urls')),
# News stuff
url(r'^news/', include('webapps.news.urls')),
# Page place-holder for things that aren't implemented yet.
url(r'^tbi/', 'webapps.website.views.to_be_implemented'),
# Admin interface
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/(.*)', admin.site.root, name='admin'),
)
# If you'd like to serve media files via Django (strongly not recommended!),
# open up your settings.py file and set SERVE_MEDIA to True. This is
# appropriate on a developing site, or if you're running Django's built-in
# test server. Normally you want a webserver that is optimized for serving
# static content to handle media files (apache, lighttpd).
if settings.SERVE_MEDIA:
urlpatterns += patterns('',
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
)