Skip to content
Browse files

Run 0.4-add-feed-meta.py with this checkin.

Added Webpage model. Adding ability to add static chunks of text or
HTML before and after the stream and profile widget. Setting
filename for logging. Consolidated tidy_up and limiting entries
to PATCHOULI_TLDR during display.
  • Loading branch information...
1 parent 502967e commit 2d3ba1e7d2e8789838a45ebef1fe7ca4ffb5163c @ozten committed Jun 26, 2010
View
4 cron/feeder.py
@@ -181,10 +181,8 @@ def cron_fetch_feeds():
log.exception(e)
finally:
lock.close()
- else:
- log.debug("Without caching")
log.info("Finished run in %f seconds for %d new entries" % ((time.time() - start), new_entry_count))
return 'Finished importing %d items' % new_entry_count
if __name__ == '__main__':
- cron_fetch_feeds()
+ cron_fetch_feeds()
View
24 docs/database/migrations/0.4-add-feed-meta.py
@@ -0,0 +1,24 @@
+from django.core.management import setup_environ
+import settings
+setup_environ(settings)
+
+import logging
+logging.basicConfig(level = logging.DEBUG,
+ format = '%(asctime)s %(levelname)s %(process)d %(message)s', )
+log = logging.getLogger()
+
+from lifestream.models import Stream
+from lifestream.models import Webpage
+import lifestream.models
+
+import patchouli_auth.preferences
+
+streams = Stream.objects.all()
+for stream in streams:
+ log.info("Checking page exists for %s owned by %s" % (stream.name, stream.user.username))
+ try:
+ Webpage.objects.get(name=stream.name, user=stream.user)
+ except Webpage.DoesNotExist:
+ webpage = Webpage(name=stream.name, user=stream.user, config='{}')
+ patchouli_auth.preferences.savePageOrStreamProperties(
+ webpage, patchouli_auth.preferences.getPageProperties(webpage))
View
27 lifestream/generic/hooks.py
@@ -3,10 +3,11 @@
import lxml.etree
import lxml.html.soupparser
+from django.conf import settings
+
from bleach import Bleach
bleach = Bleach()
-
def tidy_up(entry, log):
# TODO Security, mostly using bleach to linkify and cleanup (tidy style)
html_tags = ['a', 'abbr', 'b', 'blockquote', 'br',
@@ -37,16 +38,15 @@ def tidy_up(entry, log):
'span': basic_attrs,
'p': basic_attrs,
- }
+ }
try:
+ # Bugfix wrap content in <div> and then pop it out, othewise 'foo <span>bar</span>' will fail
+ htmlElement = lxml.html.soupparser.fromstring("<div>%s</div>" % entry[0:settings.PATCHOULI_TLDR])
+ elements = ''.join([lxml.html.tostring(el) for el in htmlElement.getchildren()])
- htmlElement = lxml.html.soupparser.fromstring(entry)
- if htmlElement.getchildren():
- elements = ''.join([lxml.html.tostring(el) for el in htmlElement.getchildren()])
- else:
- elements = entry
+ # <div> - 5 </div> - 6 characters
return bleach.linkify(
- bleach.clean(elements, tags=html_tags, attributes=attrs))
+ bleach.clean(elements[5:-6], tags=html_tags, attributes=attrs))
except Exception, x:
log.error("Ouch, unable to linkify or clean _%s_\nError: %s" % (entry, x))
log.exception(x)
@@ -59,15 +59,22 @@ def prepare_entry(entryJSON, log):
elif 'description' in entryJSON:
content = entryJSON['description']
else:
- #log.debug('unreadable... ' + str(entryJSON))
+ log.debug('unreadable... ' + str(entryJSON))
pass
title = tidy_up(entryJSON['title'], log)
content = tidy_up(content, log)
+
+ # Generic image in feed?
+ image = None
+ if 'links' in entryJSON:
+ for link in entryJSON['links']:
+ if link['rel'] == 'image':
+ image = link['href']
tags = []
if 'tags' in entryJSON:
for tag in entryJSON['tags']:
if 'term' in tag:
tags.append({'tag': tag['term'], 'name': tag['term']})
- return {'entry': content, 'tags': tags, 'title': title, 'permalink': entryJSON['link'], 'raw': entryJSON}
+ return {'entry': content, 'tags': tags, 'title': title, 'permalink': entryJSON['link'], 'raw': entryJSON, 'image': image}
View
49 lifestream/gowalla/hooks.py
@@ -1,46 +1,4 @@
-from bleach import Bleach
-bleach = Bleach()
-
-import re
-
-def tidy_up(entry, log):
- # TODO Security, mostly using bleach to linkify and cleanup (tidy style)
- html_tags = ['a', 'abbr', 'b', 'blockquote', 'br',
- 'cite', 'code', 'dd', 'dl', 'div', 'dt',
- 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
- 'i', 'img', 'hr',
- 'math', 'mi', 'mo', 'mn', 'mfrac', 'mrow', 'msqrt', 'msup',
- 'pre', 'span', 'strong',
- 'svg', 'path', 'line', 'circle',
- 'strike', 'strong', 'sub'
- 'table', 'caption', 'thead', 'tfoot', 'tbody', 'tr', 'td', 'th', 'colgroup', 'col',
- 'tt', 'var',
- 'ul', 'li', 'ol', 'p', 'q']
-
- a_attrs = ['href', 'rel', 'title']
- img_attrs = ['align', 'alt', 'border', 'height','src', 'width']
- basic_attrs = ['class', 'dir', 'lang', 'title']
-
- [x.extend(basic_attrs) for x in (a_attrs, img_attrs)]
-
- attrs = {
- 'a': a_attrs,
- 'img': img_attrs,
-
- 'abbr': basic_attrs,
- 'acronym': basic_attrs,
- 'div': basic_attrs,
- 'span': basic_attrs,
- 'p': basic_attrs,
-
- }
- try:
- return bleach.linkify(
- bleach.clean(entry, tags=html_tags, attributes=attrs))
- except Exception, x:
- log.error("Ouch, unable to linkify or clean _%s_" % entry)
- log.exception(x)
- return entry
+from lifestream.generic.hooks import tidy_up
def prepare_entry(entryJSON, log):
content = ''
@@ -54,13 +12,8 @@ def prepare_entry(entryJSON, log):
image = None
if 'links' in entryJSON:
for link in entryJSON['links']:
- log.info(link)
if link['rel'] == 'image':
image = link['href']
- else:
- log.info("No photo")
- else:
- log.info("No links")
title = tidy_up(entryJSON['title'], log)
content = tidy_up(content, log)
View
5 lifestream/identica/hooks.py
@@ -1,7 +1,6 @@
import re
-from bleach import Bleach
-bleach = Bleach()
+from lifestream.generic.hooks import tidy_up
def prepare_entry(entryJSON, log):
""" Given entryJSON, output a dictionary of variables for use in
@@ -56,7 +55,7 @@ def prepare_entry(entryJSON, log):
status = re.sub('@(\w+)', r'<a href="http://identi.ca/\1">@\1</a>', status)
try:
- status = bleach.linkify(status.replace('\n', '<br />'))
+ status = tidy_up(status.replace('\n', '<br />'), log)
except Exception, x:
log.error("Ouch, unable to linkify _%s_" % status)
log.exception(x)
View
17 lifestream/models.py
@@ -4,8 +4,22 @@
import django.forms.models
from django.contrib.auth.models import User
+class Webpage(models.Model):
+ """ A webpage is composed of Streams, page widgets,
+ and other funage.
+ """
+ user = models.ForeignKey(User)
+ name = models.CharField(max_length=140)
+ title = models.CharField(max_length=140)
+ # PageConfig
+ config = models.TextField()
+ created_date = models.DateTimeField(auto_now_add=True,
+ verbose_name='Created On')
+ updated_date = models.DateTimeField(auto_now=True,
+ verbose_name='Last Modified')
+
class Stream(models.Model):
- """ A Stream is like a 'page'. A stream is combosed of Feeds. """
+ """ A stream is composed of Feeds. """
user = models.ForeignKey(User)
name = models.CharField(max_length=140)
# StreamConfig
@@ -19,7 +33,6 @@ class Stream(models.Model):
# ALTER TABLE lifestream_stream ADD COLUMN `config` longtext NOT NULL;
# ALTER TABLE lifestream_stream ADD COLUMN `edit_list` longtext NOT NULL;
-
def __unicode__(self):
return self.name
View
5 lifestream/reddit/hooks.py
@@ -1,5 +1,4 @@
-from bleach import Bleach
-bleach = Bleach()
+from lifestream.generic.hooks import tidy_up
def prepare_entry(entryJSON, log):
content = ''
@@ -12,7 +11,7 @@ def prepare_entry(entryJSON, log):
else:
content = 'unreadable... ' + str(entryJSON)
try:
- content = bleach.linkify(content)
+ content = tidy_up(content, log)
except Exception, x:
log.error("Ouch, unable to linkify _%s_" % content)
log.exception(x)
View
8 lifestream/twitter_com/hooks.py
@@ -1,8 +1,4 @@
-import re
-
-from bleach import Bleach
-bleach = Bleach()
-
+from lifestream.generic.hooks import tidy_up
def prepare_entry(entryJSON, log):
""" Given entryJSON, output a dictionary of variables for use in
templates/twitter_com/entry.html
@@ -75,7 +71,7 @@ def prepare_entry(entryJSON, log):
tweet = re.sub('#(\w+)', r'<a href="http://twitter.com/search?q=%23\1">#\1</a>', tweet)
tweet = re.sub('@(\w+)', r'<a href="http://twitter.com/\1">@\1</a>', tweet)
try:
- tweet = bleach.linkify(tweet.replace('\n', '<br />'))
+ tweet = tidy_up(tweet.replace('\n', '<br />'), log)
except Exception, x:
log.error("Ouch, unable to linkify _%s_ caught" % tweet)
log.exception(x)
View
29 lifestream/views.py
@@ -5,6 +5,7 @@
import jsonpickle
import simplejson as json
+from django.conf import settings
import django.utils.encoding
import django.template
import django.template.loaders
@@ -19,7 +20,7 @@
from patchouli.plugins.hostname_css_class import HostnameCssPlugin
from patchouli.plugins.social_identities import SocialIdentityFromTagsPlugin
-logging.basicConfig( level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
+logging.basicConfig(filename=settings.LOG_FILENAME, level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
log = logging.getLogger()
def profile(request, username):
@@ -41,30 +42,35 @@ def js_embed_stream(request, username, streamname):
def stream(request, username, streamname):
ctx = django.template.RequestContext(request)
ctx.autoescape=False #TODO Need more thinking around best way to handle this...
-
pageVars = common_stream(request, username, streamname)
-
+
return render_to_response('lifestream/profile.html',
pageVars,
context_instance=ctx,
)
def common_stream(request, username, streamname):
username = username.lower()
- user = User.objects.get(username=username)
+ user = User.objects.get(username=username)
+
rawEntries = (lifestream.models.Entry.objects.order_by('-last_published_date')
.filter(feed__user=user,
feed__streams__name__exact = streamname,
- visible=True))[:50]
+ visible=True))[:150]
+
entries = []
plugins = []
if username == 'ozten':
plugins = [SocialIdentityFromTagsPlugin()]
plugins.append(HostnameCssPlugin(log))
+
renderedEntries = render_entries(request, rawEntries, plugins)
- profile = renderProfile(request, user, plugins)
-
+ webpage = lifestream.models.Webpage.objects.get(name=streamname, user=user)
+ webpage_properties = patchouli_auth.preferences.getPageProperties(webpage)
+
+ profile = renderProfile(request, user, plugins, webpage_properties)
+
preferences = patchouli_auth.preferences.getPreferences(user)
if 'default' == preferences['javascript_url']:
@@ -76,15 +82,15 @@ def common_stream(request, username, streamname):
css_url = '/static/css/stylo.css'
else:
css_url = preferences['css_url']
-
return {'entries': renderedEntries,
'profile': profile,
'css_url': css_url,
'javascript_url': js_url,
'processing_js': preferences['processing_js'],
'stream_name': streamname,
'user': user,
- 'username': username}
+ 'username': username,
+ 'page_props': webpage_properties,}
def render_entries(request, rawEntries, plugins=[]):
""" plugins - list of functions to be run once for each entry's variables """
@@ -107,7 +113,7 @@ def render_entries(request, rawEntries, plugins=[]):
[p.post_observe_stream_entries() for p in plugins]
return renderedEntries
-def renderProfile(request, user, plugins):
+def renderProfile(request, user, plugins, webpage_properties):
sourcesResults = lifestream.models.Feed.objects.order_by('url').filter(user=user)
sources = [{'title': s.title, 'url':s.url} for s in sourcesResults]
@@ -124,7 +130,8 @@ def renderProfile(request, user, plugins):
'show_fn': show_fn,
'username': user.username,
'preferences': json.loads(user.get_profile().properties),
- 'sources': sources}
+ 'sources': sources,
+ 'page_props': webpage_properties,}
[data.update(plugin.template_variables()) for plugin in plugins]
t = django.template.loader.select_template(('foo', 'lifestream/profile_blurb.html'))
View
32 patchouli_auth/preferences.py
@@ -1,3 +1,5 @@
+# coding=utf-8
+
import simplejson
import patchouli_auth.models
@@ -30,4 +32,32 @@ def savePreferences(user, properties):
profile.save()
except patchouli_auth.models.UserProfile.DoesNotExist, e:
# Violates a pre-condition that getPreferences will be called atleast once before savePreferences is called..
- pass
+ pass
+
+def getPageProperties(page):
+ """ Loads the properties for a Page.
+ This includes migrating any settings
+ for new code """
+ after_profile_html_area = """
+<!-- Give props to Robert Podgórski -->
+<h5>Firefoxzilla protects the city</h5>
+<div>Background imagery By <a href="http://creative.mozilla.org/people/blackmoondev">Blackmoondev</a></div>
+ """
+ pageProps = {
+ 'before_stream_html_area': 'before stream',
+ 'after_stream_html_area': 'after stream',
+ 'show_profile_blurb': True,
+ 'show_follow_me_links': True,
+ 'before_profile_html_area': 'before profile',
+ 'after_profile_html_area': after_profile_html_area,
+ }
+
+ existingProps = simplejson.loads(page.config)
+ pageProps.update(existingProps)
+ return pageProps
+
+def savePageOrStreamProperties(model, properties):
+ """ Given a Page or Stream model, persists the
+ properties """
+ model.config = simplejson.dumps(properties)
+ model.save()
View
9 patchouli_auth/views.py
@@ -2,19 +2,19 @@
import hashlib
import simplejson
+from django.conf import settings
import django.http
from django.shortcuts import render_to_response
import django.utils.encoding
-
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
import lifestream.models
import patchouli_auth.models
import patchouli_auth.preferences
-logging.basicConfig( level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
+logging.basicConfig(filename=settings.LOG_FILENAME, level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
log = logging.getLogger()
#@login_required
@@ -26,12 +26,15 @@ def account_checkauth(request):
encoded_url = django.utils.encoding.iri_to_uri(manageUrl)
resp = django.http.HttpResponseRedirect(encoded_url)
except lifestream.models.Stream.DoesNotExist:
- log.debug("Account didn't exist")
+ log.debug("Account didn't exist")
stream = lifestream.models.Stream()
stream.user_id = request.user.id
stream.name = 'home'
log.info("Saving %s %s" % (request.user.id, 'home'))
stream.save()
+ webpage = lifestream.models.Webpage(name=stream.name, user=request.user, config='{}')
+ patchouli_auth.preferences.savePageOrStreamProperties(
+ webpage, patchouli_auth.preferences.getPageProperties(webpage))
resp = django.http.HttpResponseRedirect('/auth/confirm_profile')
except Exception, exception:
log.exception("Unable to load user... trying to save %s", exception)
View
9 requirements.txt
@@ -1,17 +1,26 @@
BeautifulSoup==3.1.0.1
Django==1.1.1
MySQL-python==1.2.3c1
+amqplib==0.6.1
+anyjson==0.2.4
+billiard==0.3.1
bleach==0.3.1
+carrot==0.10.5
+celery==1.0.5
+django-picklefield==0.1.6
feedparser==4.1
html5lib==0.90
+importlib==1.0.2
ipython==0.10
jsonpickle==0.3.1
logilab-astng==0.20.1
logilab-common==0.50.1
lxml==2.2.6
+multiprocessing==2.6.2.1
pyflakes==0.4.0
pylint==0.21.0
pyquery==0.5
+python-dateutil==1.5
python-openid==2.2.4
simplejson==2.1.0
wsgiref==0.1.2
View
4 settings-dev.py
@@ -112,3 +112,7 @@
# dev
# CACHE_BACKEND = 'dummy://'
AUTH_PROFILE_MODULE = 'patchouli_auth.UserProfile'
+
+# Cut off extermely log entries
+PATCHOULI_TLDR = 8192
+LOG_FILENAME = '/tmp/django.log'
View
14 static/css/editor-stylo.css
@@ -73,6 +73,12 @@ div.logo code {
float: right;
}
#design-stream-panel .save { float: right; margin: 1em 1em 0em 0em }/* text-align: right; */
+
+#page-widgets-panel label { font-weight: bold; font-size: 115%; }
+#page-widgets-panel textarea { width: 600px; height: 300px; margin:10px}
+#page-widgets-panel strong { display: block; }
+.checkbox-label {font-weight: bold;}
+#page-widgets-panel label.checkbox {font-weight: normal; font-size: 100%;}
br { clear: both; }
.display_entry {
@@ -94,4 +100,12 @@ feed-entries-visible-default.shown {
div.entry-hidden {
background-color: #CCC;
color: #333;
+}
+fieldset, .field {
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border: 1px solid #000;
+ margin: 10px auto 10px auto;
+ padding: 10px;
+ width: 650px;
}
View
4 static/css/general-site.css
@@ -177,3 +177,7 @@ div > label{
clear: both;
padding-left: 1em;
}
+
+.error-message {
+ color: red;
+}
View
11 static/js/behavior.js
@@ -31,16 +31,7 @@ $(document).ready(function(){
});
// Fixup Twitter html
$('.feed-twitter').find('a:last').addClass('permalink');
-
- //Give props to Robert Podgórski
- $('.powered_by').prepend('<h5>Firefoxzilla protects the city</h5><div>Background imagery By <a href="http://creative.mozilla.org/people/blackmoondev">Blackmoondev</a></div>');
-
- // Shift the background by the height of the profile
- /*var pos = $('#viewport').css('background-position'),
- parts = pos.split(' '),
- blurb = $('.profile_blurb'),
- profileHeight = blurb.outerHeight() + blurb.offset().top + 10;
- $('#viewport').css('background-position', parts[0] + ' ' + profileHeight + 'px');*/
+
});
View
24 static/js/stream_editor.js
@@ -42,14 +42,17 @@ $('.tab a').click(function(){
if (other != newActive) {
$('#' + other).removeClass('active-tab');
- $('#' + other + '-panel').hide();
+ $('.tab').each(function(i, li) {
+ if ($(li).attr('id') != newActive) {
+ $('#' + $(li).attr('id') + '-panel').hide();
+ }
+ });
$('#' + newActive).addClass('active-tab');
$('#' + newActive + '-panel').show();
}
return false;
});
$('.tab a:first').trigger('click');
-
/* auth confirm page */
$('#profile-image button').click(function(event){
event.preventDefault();
@@ -86,9 +89,24 @@ $(document).ready(function(){
return false;
});
$('a.display_entry.entry-hidden').parent().addClass('entry-hidden');
+ $('.optional-widget').each(function(i, div) {
+ var linkActive = $('.add-widget', $(div)).hasClass('active');
+ if (linkActive) {
+ $('.field', $(div)).hide();
+ } else {
+ $('.add-widget', $(div)).hide();
+ }
+ $('.add-widget', $(div)).click(function() {
+ window.art = $(this);
+ $(this).removeClass('active').hide('slow');
+ $(this).parent().find('.field').show('slow');
+ return false;
+ });
+ });
});
$('#username').bind('focus, blur', function(){
$(this).val($(this).val().toLowerCase());
});
-$('#username').trigger('change');
+$('#username').trigger('change');
+
View
10 streamManager/stream_config.py
@@ -5,7 +5,10 @@
import simplejson
-logging.basicConfig( level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
+
+from django.conf import settings
+
+logging.basicConfig(filename=settings.LOG_FILENAME, level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
log = logging.getLogger()
class StreamConfig(object):
@@ -54,10 +57,11 @@ def ensureFeedsConfig(self, feeds):
self.ensureFeedConfig(feed)
for feed_config in self.config['feeds'][:]:
if feed_config['url_hash'] not in active_feeds:
- log.debug("Removing %s", feed_config)
+ #log.debug("Removing %s", feed_config)
self.config['feeds'].remove(feed_config)
else:
- log.debug("Keeping %s", feed_config)
+ #log.debug("Keeping %s", feed_config)
+ pass
def ensureFeedConfig(self, feed):
""" Make sure this feed is in the feedsConfig """
View
68 streamManager/templates/stream_editor.html
@@ -14,9 +14,11 @@
<div id="stream-editor-panel">
<ul class="tabs">
<li id="edit-stream" class="tab editor"><a href="#">Edit </a><strong>{{stream.name}}</strong> Stream</li>
- <li id="design-stream" class="tab design active-tab"><a href="#">Design</a></li>
+ <li id="design-stream" class="tab design xactive-tab"><a href="#">Design</a></li>
+ <li id="page-widgets" class="tab pagewidget active-tab"><a href="#">Page Widgets</a></li>
<li><a id="preview" href="/u/{{username}}/s/{{stream.name}}" target="_new" title="View your currenly published Stream in a new window">View</a></li>
</ul>
+<!--------------------- Edit Stream Panel ------------------------->
<div id="edit-stream-panel" class="panel">
<h2 class="new-feed">Add A Source</h2>
<p>Paste in the Url to a website or any Atom or RSS feed <img src="/static/img/feed-icon-14x14.png" width="14" height="14" alt="Feed Icon" />.</p>
@@ -67,6 +69,70 @@ <h2 class="new-feed">Add A Source</h2>
{% endfor %}
</div><!-- /stream-preview -->
</div><!-- /edit-stream-panel -->
+<!--------------------- Page Widgets Panel ------------------------->
+ <div id="page-widgets-panel" class="panel">
+ <h2>Page Widgets</h2>
+ <p>In addition to a stream, this profile page can have other pieces content.</p>
+
+ <form id="manage-page-widgets" action="/manage/page/{{page_name}}" method="post">
+ <input id="page_name" type="hidden" value="{{page_name}}" />
+
+ <div class="optional-widget">
+ <a class="add-widget optional-text-widget {% if not page_props.before_profile_html_area %}active{% endif %}" href="#">Add Custom HTML</a>
+ <div class="field optional-text-widget {% if page_props.before_profile_html_area %}active{% endif %}">
+ <label for="before_profile_html_area">Before Profile Custom HTML</label>
+ <p class="help">Custom HTML or text that should show up before the profile. Most tags supported.</p>
+ <textarea id="before_profile_html_area" name="before_profile_html_area">{{page_props.before_profile_html_area|safe}}</textarea>
+ </div><!-- /field -->
+ </div><!-- /optional-widget -->
+
+ <fieldset><legend>Profile</legend>
+ <div class="profile">
+ <img src="{{gravatar}}" />
+ </div>
+ <span class="checkbox-label">Display:</span>
+ <label for="show_profile_blurb" class="checkbox">Profile Blurb</label>
+ <input id="show_profile_blurb" name="show_profile_blurb" type="checkbox" checked=checked />
+ <label for="show_follow_me_links" class="checkbox">Follow Me Links</label>
+ <input id="show_follow_me_links" name="show_follow_me_links" type="checkbox" checked=checked />
+ </fieldset>
+
+ <div class="optional-widget">
+ <a class="add-widget optional-text-widget {% if not page_props.after_profile_html_area %}active{% endif %}" href="#">Add Custom HTML</a>
+ <div class="field optional-text-widget {% if page_props.after_profile_html_area %}active{% endif %}">
+ <label for="after_profile_html_area">After Profile Custom HTML</label>
+ <p class="help">Custom HTML or text that should show up after the profile. Most tags supported.</p>
+ <textarea id="after_profile_html_area" name="after_profile_html_area">{{page_props.after_profile_html_area|safe}}</textarea>
+ </div><!-- /field -->
+ </div><!-- /optional-widget -->
+
+ <div class="optional-widget">
+ <a class="add-widget optional-text-widget {% if not page_props.before_stream_html_area %}active{% endif %}" href="#">Add Custom HTML</a>
+ <div class="field optional-text-widget {% if page_props.before_stream_html_area %}active{% endif %}">
+ <label for="before_stream_html_area">Before Stream Custom HTML</label>
+ <p class="help">Custom HTML or text that should show up before your stream.</p>
+ <textarea id="before_stream_html_area" name="before_stream_html_area">{{page_props.before_stream_html_area|safe}}</textarea>
+ </div><!-- /field -->
+ </div><!-- /optional-widget -->
+
+ <div class="stream-icon">
+ <h3>Your {{stream.name}} Stream</h3>
+ <img src="/static/img/StreamDiagram_25perc.jpg" width="76" height="95" />
+ </div>
+
+ <div class="optional-widget">
+ <a class="add-widget optional-text-widget {% if not page_props.after_stream_html_area %}active{% endif %}" href="#">Add Custom HTML</a>
+ <div class="field optional-text-widget {% if page_props.after_stream_html_area %}active{% endif %}">
+ <label for="after_stream_html_area">After Streamm Custom HTML</label>
+ <p class="help">Custom HTML or text that should show up after the stream.</p>
+ <textarea id="after_stream_html_area" name="after_stream_html_area">{{page_props.after_stream_html_area}}</textarea>
+ </div><!-- /field -->
+ </div><!-- /optional-widget -->
+
+ <input type="submit" id="page-widget-save" name="page-widget-save" value="save" />
+ </form>
+ </div>
+<!--------------------- Design Stream Panel ------------------------->
<div id="design-stream-panel" class="panel">
<form id="design-form" action="/manage/stream/design" method="post">
<div class="save"><input type="submit" id="design-submit1" value="Save" /></div>
View
2 streamManager/urls.py
@@ -5,6 +5,8 @@
(r'^stream/design$', 'views.manage_stream_design'),
(r'^stream/(?P<username>\w+)/s/(?P<streamname>\w+)$', 'views.manage_stream'),
+ (r'^page/(?P<page_name>\w+)$', 'views.manage_page_widgets'),
+
(r'^urls/(?P<username>\w+)$', 'views.urls'),
(r'^url/(?P<username>\w+)/(?P<feed_url_hash>\w+)$', 'views.url'),
View
50 streamManager/views.py
@@ -1,10 +1,12 @@
import logging
import datetime
+import hashlib
import feedparser
from pyquery import PyQuery
import simplejson as json
+from django.conf import settings
import django.http
import django.template
import django.template.loaders
@@ -19,9 +21,10 @@
import patchouli_auth.preferences
from patchouli.plugins.stream_editor import StreamEditorPlugin
from lifestream.views import render_entries
+from lifestream.generic.hooks import tidy_up
from streamManager.stream_config import StreamConfig
-logging.basicConfig( level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
+logging.basicConfig(filename=settings.LOG_FILENAME, level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', )
log = logging.getLogger()
HACKING_MESSAGE = 'Hack much? Your request has been logged. Authorities have been dispatched.'
@@ -43,7 +46,8 @@ def manage(request, username):
@login_required
def manage_stream(request, username, streamname):
if request.user.username == username:
-
+ webpage = get_object_or_404(lifestream.models.Webpage, user=request.user, name=streamname)
+ webpage_properties = patchouli_auth.preferences.getPageProperties(webpage)
stream = get_object_or_404(lifestream.models.Stream, user=request.user, name=streamname)
stream_config = StreamConfig(stream.config)
feed_rows = stream.feed_set.all()
@@ -63,22 +67,29 @@ def manage_stream(request, username, streamname):
raw_entries = (lifestream.models.Entry.objects.order_by('-last_published_date')
.filter(feed__user=request.user,
- feed__streams__name__exact = streamname))[:150]
+ feed__streams__name__exact = streamname))[:150] #24 - 50
plugins = [StreamEditorPlugin(log)]
entry_pair = zip(raw_entries, render_entries(request, raw_entries, plugins))
feed_model = lifestream.models.FeedForm()
stream.url = "/u/%s/s/%s" % (username, stream.name)
+
+ gravatarHash = hashlib.md5(
+ django.utils.encoding.smart_str(request.user.email)).hexdigest()
+ gravatar = "http://www.gravatar.com/avatar/%s.jpg?d=monsterid&s=80" % gravatarHash
preferences = patchouli_auth.preferences.getPreferences(request.user)
template_data = { 'feeds': feeds,
'entry_pair': entry_pair,
'unused_feeds': [],
'form': feed_model,
+ 'gravatar': gravatar,
+ 'page_name': stream.name,
'request': request,
'stream': stream,
'stream_config': stream_config,
'username': request.user.username,
+ 'page_props': webpage_properties,
'preferences': preferences}
[template_data.update(plugin.template_variables()) for plugin in plugins]
return render_to_response('stream_editor.html',
@@ -122,8 +133,7 @@ def is_possible_feed(url):
if 1 == possible_feed.bozo and len(possible_feed.entries) == 0:
log.info("%s triggered feedparser's bozo detection" % url)
return False
- else:
- log.info("%s is a valid feed according to feedparser" % url)
+ else:
# The url is a feed
feed_title = 'Unknown'
if 'feed' in possible_feed and 'title' in possible_feed['feed']:
@@ -231,9 +241,9 @@ def url(request, username, feed_url_hash):
@login_required
def manage_stream_design(request):
if 'POST' == request.method:
- preferences = patchouli_auth.preferences.getPreferences(request.user)
+ preferences = patchouli_auth.preferences.getPreferences(request.user)
params = request.POST.copy()
- log.info(str(preferences))
+ #log.debug(str(preferences))
if 'default' == params['css_type']:
preferences['css_url'] = 'default'
@@ -247,11 +257,29 @@ def manage_stream_design(request):
preferences['processing_js'] = params['processing']
patchouli_auth.preferences.savePreferences(request.user, preferences)
- return django.http.HttpResponseRedirect('/auth') #TODO django.core.urlresolvers reverse('confirm_profile')
+ return django.http.HttpResponseRedirect("/auth") #TODO django.core.urlresolvers reverse('confirm_profile')
+
+@login_required
+def manage_page_widgets(request, page_name):
+ if 'POST' == request.method:
+ params = request.POST.copy()
+ # KISS stream and page have the same name
+ page = get_object_or_404(lifestream.models.Webpage, name=page_name, user=request.user)
+ preferences = patchouli_auth.preferences.getPageProperties(page)
+
+ preferences['before_profile_html_area'] = tidy_up(params['before_profile_html_area'], log)
+ preferences['show_profile_blurb'] = tidy_up(params['show_profile_blurb'], log)
+ preferences['show_follow_me_links'] = tidy_up(params['show_follow_me_links'], log)
+
+ preferences['after_profile_html_area'] = tidy_up(params['after_profile_html_area'], log)
+ preferences['before_stream_html_area'] = tidy_up(params['before_stream_html_area'], log)
+ preferences['after_stream_html_area'] = tidy_up(params['after_stream_html_area'], log)
+
+
+ patchouli_auth.preferences.savePageOrStreamProperties(page, preferences)
+ manageUrl = "/manage/account/%s" % request.user.username.encode('utf-8')
+ return django.http.HttpResponseRedirect(manageUrl)
-import django.http
-from django.shortcuts import render_to_response
-
def entry(request, entry_id):
"""
change-visibility set to true - show entry
View
2 templates/basic_template.html
@@ -18,6 +18,6 @@
var pageTracker = _gat._getTracker("UA-16311800-1");
pageTracker._trackPageview();
} catch(err) {}</script>
-<div id="release-footer"><a href="http://sudosocial.me"><strong><code>sudo</code>Social</strong></a> Version 0.2 &quot;Early, Often&quot; release</div>
+<div id="release-footer"><a href="http://sudosocial.me"><strong><code>sudo</code>Social</strong></a> Version 0.3 &quot;Early, Often&quot; release</div>
</body>
</html>
View
1 templates/generic/entry.html
@@ -14,6 +14,7 @@
{% endif %}
<h3>{%if permalink %}<a href="{{permalink}}">{% endif %}{{title|safe}}{%if permalink %}</a>{% endif %}</h3>
+ {% if image %} <img src="{{image}}" class="feed-linked-image" /> {% endif %}
{{ entry|safe }}
<div class="tag-area">
<h6 class="tag-heading">Tags:</h6>
View
2 templates/lifestream/profile.html
@@ -5,7 +5,9 @@
<canvas id="processing-canvas"></canvas>
<div id="viewport">
<div id="content">
+ {{page_props.before_stream_html_area|safe}}
{% include "lifestream/display_feed_entries.html" %}
+ {{page_props.after_stream_html_area|safe}}
</div><!-- /#content -->
</div><!-- /#viewport -->
View
12 templates/lifestream/profile_blurb.html
@@ -1,5 +1,7 @@
{% load common_tags %}
<div class="profile_blurb">
+ {{page_props.before_profile_html_area|safe}}
+ {% if page_props.show_profile_blurb %}
<div id="hcard-{{a_user.first_name}}-{{a_user.last_name}}" class="vcard profile_image">
<img id="profile_image" src="{{ avatar_src }}" alt="Profile Photo of {{ username }}" class="photo"/>
{% if show_fn %}
@@ -9,11 +11,9 @@
{% endif %}
{% if preferences.publish_email %}
<a class="email" href="mailto:{{a_user.email}}">{{a_user.email}}</a>
- {% endif %}
-
-
-
+ {% endif %}
</div><!-- /hcard-->
+ {% endif %}
{% if show_social_identities %}
<div class="social_identities">
<h5>Who am I lately?</h5>
@@ -25,6 +25,7 @@
</ul>
</div><!-- /social_identities-->
{% endif %}
+ {% if page_props.show_follow_me_links %}
<div class="followme">
<h5>Follow {{a_user.first_name|title}} on the Interwebs:</h5>
<ul>
@@ -33,8 +34,11 @@
{% endfor %}
</ul>
</div><!-- /followme -->
+ {% endif %}
+ {{page_props.after_profile_html_area|safe}}
<div class="powered_by">
<h5 class="logo"><strong><code>sudo</code>Social</strong></h5>
<a href="{% secure %}{% url confirm_profile %}{% endsecure %}">Take control of your stream</a>
</div>
+
</div><!-- /profile_blurb -->

0 comments on commit 2d3ba1e

Please sign in to comment.
Something went wrong with that request. Please try again.