Added Melange front page edit view where you can change title, content, feed url. Created SiteSettings and Document models and some logic for them. Added isFeedURLValid function in soc/logic/feed.py. Created some functions for handling datastore updates of different kinds of Models (soc/logic/model.py). Fixed some typos and too long lines of code.
Patch by: Pawel Solyga
Review by: to-be-reviewed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/document.py Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,85 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Document (Model) query functions.
+"""
+
+__authors__ = [
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
+ ]
+
+from google.appengine.ext import db
+
+import soc.models.document
+import soc.logic.model
+
+def getDocumentFromPath(path):
+ """Returns Document entity for a given path, or None if not found.
+
+ Args:
+ id: a Google Account (users.User) object
+ """
+ # lookup by Doc:path key name
+ key_name = getDocumentKeyNameForPath(path)
+
+ if key_name:
+ document = soc.models.document.Document.get_by_key_name(key_name)
+ else:
+ document = None
+
+ return document
+
+def getDocumentKeyNameForPath(path):
+ """Return a Datastore key_name for a Document from the path.
+
+ Args:
+ path: a request path of the Document that uniquely identifies it
+ """
+ if not path:
+ return None
+
+ return 'Doc:%s' % path
+
+
+def updateOrCreateDocumentFromPath(path, **document_properties):
+ """Update existing Document entity, or create new one with supplied properties.
+
+ Args:
+ path: a request path of the Document that uniquely identifies it
+ **document_properties: keyword arguments that correspond to Document entity
+ properties and their values
+
+ Returns:
+ the Document entity corresponding to the path, with any supplied
+ properties changed, or a new Document entity now associated with the
+ supplied path and properties.
+ """
+ # attempt to retrieve the existing Document
+ document = getDocumentFromPath(path)
+
+ if not document:
+ # document did not exist, so create one in a transaction
+ key_name = getDocumentKeyNameForPath(path)
+ document = soc.models.document.Document.get_or_insert(
+ key_name, **document_properties)
+
+ # there is no way to be sure if get_or_insert() returned a new Document or
+ # got an existing one due to a race, so update with document_properties anyway,
+ # in a transaction
+ return soc.logic.model.updateModelProperties(document, **document_properties)
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/feed.py Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,39 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Feeds helpers functions.
+"""
+
+__authors__ = [
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
+ ]
+
+from google.appengine.api import urlfetch
+from soc.utils import feedparser
+
+def isFeedURLValid(feed_url=None):
+ """Returns True if provided url is valid ATOM or RSS.
+
+ Args:
+ feed_url: ATOM or RSS feed url
+ """
+ if feed_url:
+ result = urlfetch.fetch(feed_url)
+ if result.status_code == 200:
+ parsed_feed = feedparser.parse(result.content)
+ if parsed_feed.version and (parsed_feed.version != ''):
+ return True
+ return False
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/model.py Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,56 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helpers functions for updating different kinds of models in datastore.
+"""
+
+__authors__ = [
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
+ ]
+
+from google.appengine.ext import db
+
+def updateModelProperties(model, **model_properties):
+ """Update existing model entity using supplied model properties.
+
+ Args:
+ model: a model entity
+ **model_properties: keyword arguments that correspond to model entity
+ properties and their values
+
+ Returns:
+ the original model entity with any supplied properties changed
+ """
+ def update():
+ return _unsafeUpdateModelProperties(model, **model_properties)
+
+ return db.run_in_transaction(update)
+
+
+def _unsafeUpdateModelProperties(model, **model_properties):
+ """(see updateModelProperties)
+
+ Like updateModelProperties(), but not run within a transaction.
+ """
+ properties = model.properties()
+
+ for prop in properties.values():
+ if prop.name in model_properties:
+ value = model_properties[prop.name]
+ prop.__set__(model, value)
+
+ model.put()
+ return model
\ No newline at end of file
--- a/app/soc/logic/site/id_user.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/logic/site/id_user.py Sat Sep 13 22:00:51 2008 +0000
@@ -134,7 +134,27 @@
return True
else:
return False
+
+def isIdUser(id=None):
+ """Returns True if Google Account has it's soc.models.user.User entity in datastore.
+ Args:
+ id: a Google Account (users.User) object; if id is not supplied,
+ the current logged-in user is checked
+ """
+ id = getIdIfMissing(id)
+
+ if not id:
+ # no Google Account was supplied or is logged in
+ return False
+
+ user = getUserFromId(id)
+
+ if not user:
+ # no User entity for this Google Account
+ return False
+
+ return True
def isIdDeveloper(id=None):
"""Returns True if Google Account is a Developer with special privileges.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/site/settings.py Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,82 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""SiteSettings (Model) query functions.
+"""
+
+__authors__ = [
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
+ ]
+
+from google.appengine.ext import db
+
+import soc.models.site_settings
+import soc.logic.model
+
+def getSiteSettingsFromPath(path):
+ """Returns SiteSettings entity for a given path, or None if not found.
+
+ Args:
+ path: a request path of the SiteSettings that uniquely identifies it
+ """
+ # lookup by Settings:path key name
+ key_name = getSiteSettingsKeyNameForPath(path)
+
+ if key_name:
+ site_settings = soc.models.site_settings.SiteSettings.get_by_key_name(key_name)
+ else:
+ site_settings = None
+
+ return site_settings
+
+def getSiteSettingsKeyNameForPath(path):
+ """Return a Datastore key_name for a SiteSettings from the path.
+
+ Args:
+ path: a request path of the SiteSettings that uniquely identifies it
+ """
+ if not path:
+ return None
+
+ return 'Settings:%s' % path
+
+
+def updateOrCreateSiteSettingsFromPath(path, **site_settings_properties):
+ """Update existing SiteSettings entity, or create new one with supplied properties.
+
+ Args:
+ path: a request path of the SiteSettings that uniquely identifies it
+ **site_settings_properties: keyword arguments that correspond to Document entity
+ properties and their values
+
+ Returns:
+ the SiteSettings entity corresponding to the path, with any supplied
+ properties changed, or a new SiteSettings entity now associated with the
+ supplied path and properties.
+ """
+ # attempt to retrieve the existing Site Settings
+ site_settings = getSiteSettingsFromPath(path)
+
+ if not site_settings:
+ # site settings did not exist, so create one in a transaction
+ key_name = getSiteSettingsKeyNameForPath(path)
+ site_settings = soc.models.site_settings.SiteSettings.get_or_insert(
+ key_name, **site_settings_properties)
+
+ # there is no way to be sure if get_or_insert() returned a new SiteSettings or
+ # got an existing one due to a race, so update with site_settings_properties anyway,
+ # in a transaction
+ return soc.logic.model.updateModelProperties(site_settings, **site_settings_properties)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/models/document.py Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,54 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""This module contains the Document Model."""
+
+__authors__ = [
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
+]
+
+from google.appengine.ext import db
+from django.utils.translation import ugettext_lazy
+import soc.models.user
+
+class Document(db.Model):
+ """Model of a Document.
+
+ Document is used for things like FAQs, front page text etc.
+ """
+
+ title = db.StringProperty(required=True,
+ verbose_name=ugettext_lazy('Title'))
+ title.help_text = ugettext_lazy('Document title displayed on the top of the page')
+
+ link_name = db.StringProperty(required=True,
+ verbose_name=ugettext_lazy('Link name'))
+ link_name.help_text = ugettext_lazy('Document link name used in URLs')
+
+ short_name = db.StringProperty(required=True,
+ verbose_name=ugettext_lazy('Short name'))
+ short_name.help_text = ugettext_lazy('Document short name used for sidebar menu')
+
+ content = db.TextProperty(
+ verbose_name=ugettext_lazy('Content'))
+
+ created = db.DateTimeProperty(auto_now_add=True)
+ modified = db.DateTimeProperty(auto_now=True)
+ user = db.ReferenceProperty(reference_class=soc.models.user.User,
+ required=True, collection_name='documents')
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/models/site_settings.py Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""This module contains the SiteSettings Model."""
+
+__authors__ = [
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
+]
+
+from google.appengine.ext import db
+from django.utils.translation import ugettext_lazy
+
+class SiteSettings(db.Model):
+ """Model of a SiteSettings, which stores per site configuration."""
+
+ #: Valid ATOM or RSS feed url or None if unused. Feed entries are shown
+ #: on the site page using Google's JavaScript blog widget
+ feed_url = db.LinkProperty(
+ verbose_name=ugettext_lazy('Feed URL'))
+ feed_url.help_text = ugettext_lazy(
+ 'The URL should be a valid ATOM or RSS feed. '
+ 'Feed entries are shown on the site page.')
+
+
--- a/app/soc/templates/soc/base.html Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/templates/soc/base.html Sat Sep 13 22:00:51 2008 +0000
@@ -65,7 +65,11 @@
{% block sidebar_menu %}
<ul>
<li>
-<a class="selected" href="/">Google Open Source Programs</a>
+<a class="selected" href="/">
+ {% block sidebar_menu_title %}
+ Google Open Source Programs
+ {% endblock %}
+ </a>
<ul>
<li>
<a href="/user/profile">User (Sign In)</a>
@@ -75,6 +79,20 @@
</li>
</ul>
+{% if is_admin %}
+ <ul>
+ <li>Developer
+ <ul>
+ <li>
+ <a href="/site/home/edit">Site Settings</a>
+ </li>
+ <li>
+ <a href="/site/user/lookup">Look Up User</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+{% endif %}
<ul>
<li>
<a href="/program/gsoc2009/home">Google Summer of Code</a>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/site/home/edit.html Sat Sep 13 22:00:51 2008 +0000
@@ -0,0 +1,54 @@
+{% extends "soc/base.html" %}
+{% comment %}
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+{% block scripts %}
+ <script type="text/javascript" src="/soc/content/js/tiny_mce/tiny_mce_src.js"></script>
+{% endblock %}
+
+{% block page_title %}Site Settings{% endblock %}
+{% block header_title %}
+Site Settings
+{% endblock %}
+
+{% block body %}
+<p>
+<p>
+{% block instructions %}
+Please use this form to set basic site settings.
+{% endblock %}
+</p>
+<form method="POST">
+ <table>
+ {{ document_form.as_table }}
+ {{ settings_form.as_table }}
+ <tr>
+ <td colspan="4"> </td>
+ </tr>
+ <tr>
+ <td>
+ <input style="font-weight: bold" type="submit" value="Save Changes"/></span>
+ </td>
+ <td>
+ <input type="button" onclick="location.href='/'" value="Cancel"/>
+ </td>
+ <td> </td>
+ <td>
+ {% if submit_message %}<b><i>{{ submit_message }}</i></b>{% endif %}
+ </td>
+ </tr>
+ </table>
+</form>
+</p>
+{% endblock %}
\ No newline at end of file
--- a/app/soc/templates/soc/site/home/public.html Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/templates/soc/site/home/public.html Sat Sep 13 22:00:51 2008 +0000
@@ -14,6 +14,7 @@
{% endcomment %}
{% block scripts %}
+{% if site_settings.feed_url %}
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript" src="/soc/content/js/blog.js"></script>
<script type="text/javascript">
@@ -22,14 +23,37 @@
function initialize() {
var blog = new BlogPreview(document.getElementById("blog"));
- blog.show("http://google-opensource.blogspot.com/feeds/posts/default");
+ blog.show("{{ site_settings.feed_url }}");
}
google.setOnLoadCallback(initialize);
</script>
+{% endif %}
+{% endblock %}
+
+{% block page_title %}
+{% if site_document %}
+{{ site_document.title }}
+{% else %}
+Google Open Source Programs
+{% endif %}
+{% endblock %}
+
+{% block header_title %}
+{% if site_document %}
+{{ site_document.title }}
+{% else %}
+Google Open Source Programs
+{% endif %}
{% endblock %}
{% block body %}
+ {% if site_document %}
+ {{ site_document.content|safe }}
+ {% else %}
{{ block.super }}
+ {% endif %}
+ {% if site_settings.feed_url %}
<div id="blog"></div>
+ {% endif %}
{% endblock %}
\ No newline at end of file
--- a/app/soc/views/helpers/custom_widgets.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/views/helpers/custom_widgets.py Sat Sep 13 22:00:51 2008 +0000
@@ -21,14 +21,14 @@
'"Pawel Solyga" <pawel.solyga@gmail.com>',
]
-import django.newforms as forms
+from django import newforms as forms
from django.newforms.widgets import flatatt
from django.newforms.util import smart_unicode
from django.utils.html import escape
from django.utils import simplejson
from django.utils.safestring import mark_safe
-class TinyMCE(forms.Textarea):
+class TinyMCE(forms.widgets.Textarea):
"""
TinyMCE widget. requires you include tiny_mce_src.js in your template
you can customize the mce_settings by overwriting instance mce_settings,
--- a/app/soc/views/helpers/template_helpers.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/views/helpers/template_helpers.py Sat Sep 13 22:00:51 2008 +0000
@@ -19,6 +19,7 @@
__authors__ = [
'"Todd Larsen" <tlarsen@google.com>',
+ '"Pawel Solyga" <pawel.solyga@gmail.com>'
]
@@ -55,3 +56,13 @@
'%s/%s' % (t.rsplit('/', 1)[0], new_template_file) for t in templates]
return sibling_templates + default_template
+
+def unescape(html):
+ "Returns the given HTML with ampersands, quotes and carets decoded"
+
+ if not isinstance(html, basestring):
+ html = str(html)
+
+ html.replace('&', '&').replace('<', '<')
+ html.replace('>', '>').replace('"', '"').replace(''',"'")
+ return html
--- a/app/soc/views/simple.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/views/simple.py Sat Sep 13 22:00:51 2008 +0000
@@ -15,14 +15,11 @@
# limitations under the License.
"""Simple views that depend entirely on the template and context.
-
-simpleWithLinkName: a simple template view for URLs with a linkname
-
-errorResponse: renders an out_of_band.ErrorResponse page
"""
__authors__ = [
'"Todd Larsen" <tlarsen@google.com>',
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
]
@@ -174,12 +171,44 @@
return requestLogin(request, template, context,
login_message_fmt=login_message_fmt)
+DEF_NO_USER_LOGIN_MSG_FMT = ugettext_lazy(
+ 'Please create <a href="/user/profile">User Profile</a>'
+ ' in order to view this page.')
+
+def getAltResponseIfNotUser(request, context=None,
+ template=DEF_LOGIN_TMPL, id=None,
+ login_message_fmt=DEF_LOGIN_MSG_FMT):
+ """Returns an alternate HTTP response if Google Account has no User entity.
+
+ Args:
+ request: the standard django request object
+ context: the context supplied to the template (implements dict)
+ template: the "sibling" template (or a search list of such templates)
+ from which to construct the public.html template name (or names)
+ id: a Google Account (users.User) object, or None, in which case
+ the current logged-in user is used
+
+ Returns:
+ None if User exists for id, or a subclass of django.http.HttpResponse
+ which contains the alternate response that should be returned by the
+ calling view.
+ """
+ user_exist = id_user.isIdUser(id)
+
+ if user_exist:
+ return None
+
+ # if missing, create default template context for use with any templates
+ context = response_helpers.getUniversalContext(request, context=context)
+
+ return requestLogin(request, template, context,
+ login_message_fmt=DEF_NO_USER_LOGIN_MSG_FMT)
DEF_DEV_LOGIN_MSG_FMT = ugettext_lazy(
'Please <a href="%(sign_in)s">sign in</a>'
' as a site developer to view this page.')
-DEF_DEF_LOGOUT_LOGIN_MSG_FMT = ugettext_lazy(
+DEF_DEV_LOGOUT_LOGIN_MSG_FMT = ugettext_lazy(
'Please <a href="%(sign_out)s">sign out</a>'
' and <a href="%(sign_in)s">sign in</a>'
' again as a site developer to view this page.')
@@ -212,7 +241,7 @@
if not id_user.isIdDeveloper(id=id):
return requestLogin(request, template, context,
- login_message_fmt=DEF_DEF_LOGOUT_LOGIN_MSG_FMT)
+ login_message_fmt=DEF_DEV_LOGOUT_LOGIN_MSG_FMT)
# Google Account is logged in and is a Developer, so no need for sign in
return None
--- a/app/soc/views/site/home.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/views/site/home.py Sat Sep 13 22:00:51 2008 +0000
@@ -20,7 +20,7 @@
site
There may (eventually) be different views of the site home page for
-logged-in Users (such as a debug() view for logged-in Developers).
+logged-in Users (such as a edit() view for logged-in Developers).
"""
__authors__ = [
@@ -29,10 +29,64 @@
from google.appengine.api import users
+
+from django import http
+from django import shortcuts
+from django import newforms as forms
+
+from soc.logic import out_of_band
+from soc.logic import feed
+from soc.logic.site import id_user
+from soc.views import simple
+from soc.views.helpers import forms_helpers
from soc.views.helpers import response_helpers
+from soc.views.helpers import template_helpers
+from soc.views.helpers import widgets
+
+import soc.models.site_settings
+import soc.models.document
+import soc.logic.document
+import soc.logic.site.settings
-def public(request, template='soc/site/home/public.html'):
+class DocumentForm(forms_helpers.DbModelForm):
+ content = forms.fields.CharField(widget=widgets.TinyMCE())
+ #link_name = forms.CharField(widget=forms.TextInput(
+ # attrs={'readonly':'true'}))
+ class Meta:
+ """Inner Meta class that defines some behavior for the form.
+ """
+ #: db.Model subclass for which the form will gather information
+ model = soc.models.document.Document
+
+ #: list of model fields which will *not* be gathered by the form
+ exclude = ['user','modified','created','link_name']
+
+class SiteSettingsForm(forms_helpers.DbModelForm):
+ """Django form displayed when creating or editing Site Settings.
+ """
+ class Meta:
+ """Inner Meta class that defines some behavior for the form.
+ """
+ #: db.Model subclass for which the form will gather information
+ model = soc.models.site_settings.SiteSettings
+
+ def clean_feed_url(self):
+ feed_url = self.cleaned_data.get('feed_url')
+
+ if feed_url == '':
+ # feed url not supplied (which is OK), so do not try to validate it
+ return None
+
+ if not feed.isFeedURLValid(feed_url):
+ raise forms.ValidationError('This URL is not a valid ATOM or RSS feed.')
+
+ return feed_url
+
+DEF_SITE_HOME_PATH = 'site/home'
+DEF_SITE_HOME_PUBLIC_TMPL = 'soc/site/home/public.html'
+
+def public(request, template=DEF_SITE_HOME_PUBLIC_TMPL):
"""How the "general public" sees the Melange site home page.
Args:
@@ -42,5 +96,109 @@
Returns:
A subclass of django.http.HttpResponse with generated template.
"""
- return response_helpers.respond(request,
- template, {'template': template})
+ # create default template context for use with any templates
+ context = response_helpers.getUniversalContext(request)
+
+ document = soc.logic.document.getDocumentFromPath(DEF_SITE_HOME_PATH)
+ site_settings = soc.logic.site.settings.getSiteSettingsFromPath(DEF_SITE_HOME_PATH)
+
+ if document:
+ document.content = template_helpers.unescape(document.content)
+ context.update({'site_document': document})
+
+ if site_settings:
+ context.update({'site_settings': site_settings})
+
+ return response_helpers.respond(request, template, context)
+
+
+DEF_SITE_HOME_EDIT_TMPL = 'soc/site/home/edit.html'
+
+def edit(request, template=DEF_SITE_HOME_EDIT_TMPL):
+ """View for Developer to edit content of Melange site home page.
+
+ Args:
+ request: the standard django request object.
+ template: the template path to use for rendering the template.
+
+ Returns:
+ A subclass of django.http.HttpResponse with generated template.
+ """
+ # create default template context for use with any templates
+ context = response_helpers.getUniversalContext(request)
+
+ logged_in_id = users.get_current_user()
+
+ alt_response = simple.getAltResponseIfNotDeveloper(request, context,
+ id = logged_in_id)
+ if alt_response:
+ # not a developer
+ return alt_response
+
+ alt_response = simple.getAltResponseIfNotLoggedIn(request, context,
+ id = logged_in_id)
+ if alt_response:
+ # not logged in
+ return alt_response
+
+ alt_response = simple.getAltResponseIfNotUser(request, context,
+ id = logged_in_id)
+ if alt_response:
+ # no existing User entity for logged in Google Account. User entity is
+ # required for creating Documents
+ return alt_response
+
+ settings_form = None
+ document_form = None
+
+ if request.method == 'POST':
+ document_form = DocumentForm(request.POST)
+ settings_form = SiteSettingsForm(request.POST)
+
+ if document_form.is_valid() and settings_form.is_valid():
+ title = document_form.cleaned_data.get('title')
+ link_name = DEF_SITE_HOME_PATH
+ short_name = document_form.cleaned_data.get('short_name')
+ content = document_form.cleaned_data.get('content')
+
+ feed_url = settings_form.cleaned_data.get('feed_url')
+
+ document = soc.logic.document.updateOrCreateDocumentFromPath(
+ DEF_SITE_HOME_PATH,
+ link_name = link_name,
+ title = title,
+ short_name = short_name,
+ content = content,
+ user = id_user.getUserFromId(logged_in_id))
+
+ site_settings = soc.logic.site.settings.updateOrCreateSiteSettingsFromPath(
+ DEF_SITE_HOME_PATH,
+ feed_url = feed_url)
+
+ context.update({'submit_message': 'Site Settings saved.'})
+ else: # request.method == 'GET'
+ # try to fetch Document entity by unique key_name
+ document = soc.logic.document.getDocumentFromPath(DEF_SITE_HOME_PATH)
+
+ if document:
+ # populate form with the existing Document entity
+ document_form = DocumentForm(instance=document)
+ else:
+ # no Document entity exists for this key_name, so show a blank form
+ document_form = DocumentForm()
+
+ # try to fetch SiteSettings entity by unique key_name
+ site_settings = soc.logic.site.settings.getSiteSettingsFromPath(
+ DEF_SITE_HOME_PATH)
+
+ if site_settings:
+ # populate form with the existing SiteSettings entity
+ settings_form = SiteSettingsForm(instance=site_settings)
+ else:
+ # no SiteSettings entity exists for this key_name, so show a blank form
+ settings_form = SiteSettingsForm()
+
+ context.update({'document_form': document_form,
+ 'settings_form': settings_form })
+
+ return response_helpers.respond(request, template, context)
--- a/app/soc/views/site/user/profile.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/soc/views/site/user/profile.py Sat Sep 13 22:00:51 2008 +0000
@@ -92,16 +92,17 @@
logged_in_id = users.get_current_user()
- if not logged_in_id:
- return simple.requestLogin(request, template, context,
- login_message_fmt='Please <a href="%(sign_in)s">sign in</a>'
- ' as a site developer to view this page.')
-
- if not id_user.isIdDeveloper(id=logged_in_id):
- return simple.requestLogin(request, template, context,
- login_message_fmt='Please <a href="%(sign_out)s">sign out</a>'
- ' and <a href="%(sign_in)s">sign in</a>'
- ' again as a site developer to view this page.')
+ alt_response = simple.getAltResponseIfNotDeveloper(request, context,
+ id = logged_in_id)
+ if alt_response:
+ # not a developer
+ return alt_response
+
+ alt_response = simple.getAltResponseIfNotLoggedIn(request, context,
+ id = logged_in_id)
+ if alt_response:
+ # not logged in
+ return alt_response
user = None # assume that no User entity will be found
form = None # assume blank form needs to be displayed
--- a/app/urls.py Sat Sep 13 21:27:17 2008 +0000
+++ b/app/urls.py Sat Sep 13 22:00:51 2008 +0000
@@ -24,6 +24,8 @@
urlpatterns = patterns(
'',
(r'^$', 'soc.views.site.home.public'),
+ (r'^site/home$', 'soc.views.site.home.public'),
+ (r'^site/home/edit$', 'soc.views.site.home.edit'),
# TODO(tlarsen): uncomment these when the view functions are committed
# attempt to send User to their dashboard
@@ -35,7 +37,9 @@
# 'soc.views.user.roles.dashboard'),
(r'^site/user/lookup$', 'soc.views.site.user.profile.lookup'),
-
+ (r'^site/user/profile/(?P<linkname>[_0-9a-z]+)$',
+ 'soc.views.site.user.profile.edit'),
+
# TODO(tlarsen): uncomment these when the view functions are committed
# (r'^site/user/profile$', 'soc.views.site.user.profile.create'),
# (r'^site/user/profile/(?P<linkname>[_0-9a-z]+)$',