diff --git a/.gitignore b/.gitignore index dff2e19..8896fea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store +.idea +*.bak *.pyc *.pyo .bzr diff --git a/admin.py b/admin.py index 643cf10..91eee71 100644 --- a/admin.py +++ b/admin.py @@ -1,28 +1,14 @@ # -*- coding: utf-8 -*- -import cgi, os,sys,traceback,logging -import wsgiref.handlers -##os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' -##from django.conf import settings -##settings._target = None +import os os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' -from django.utils.translation import check_for_language, activate, to_locale, get_language +from google.appengine.dist import use_library +use_library('django', '1.2') +import wsgiref.handlers from django.conf import settings settings._target = None - -from google.appengine.ext.webapp import template, \ - WSGIApplication -from google.appengine.api import users -#import app.webapp as webapp2 -from google.appengine.ext import db -from google.appengine.ext import zipserve -from google.appengine.api import urlfetch -from google.appengine.api import memcache -from google.appengine.api.labs import taskqueue -from datetime import datetime ,timedelta -import base64,random,math,zipfile from django.utils import simplejson -import pickle -from base import * +from django.utils.translation import ugettext as _ +import base from model import * from app.pingback import autoPingback @@ -30,32 +16,29 @@ import xmlrpclib from xmlrpclib import Fault - -class Error404(BaseRequestHandler): - #@printinfo - def get(self,slug=None): - self.render2('views/admin/404.html') - -class setlanguage(BaseRequestHandler): - def get(self): - lang_code = self.param('language') - next = self.param('next') - if (not next) and os.environ.has_key('HTTP_REFERER'): - next = os.environ['HTTP_REFERER'] - if not next: - next = '/' - os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - from django.utils.translation import check_for_language, activate, to_locale, get_language - from django.conf import settings - settings._target = None - - if lang_code and check_for_language(lang_code): - g_blog.language=lang_code - activate(g_blog.language) - g_blog.save() - self.redirect(next) - - +class Error404(base.BaseRequestHandler): + #@printinfo + def get(self,slug=None): + self.render2('views/admin/404.html') + +class setlanguage(base.BaseRequestHandler): + def get(self): + lang_code = self.param('language') + next = self.param('next') + if (not next) and os.environ.has_key('HTTP_REFERER'): + next = os.environ['HTTP_REFERER'] + if not next: + next = '/' + os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + from django.utils.translation import check_for_language, activate + from django.conf import settings + settings._target = None + + if lang_code and check_for_language(lang_code): + g_blog.language=lang_code + activate(g_blog.language) + g_blog.save() + self.redirect(next) ## if hasattr(request, 'session'): ## request.session['django_language'] = lang_code @@ -67,245 +50,248 @@ def get(self): ## ) ## self.write(cookiestr) - #self.response.headers.add_header('Set-Cookie', cookiestr) +## self.response.headers.add_header('Set-Cookie', cookiestr) def fetch_result(target_uri): - for RETRY in range(5): - try: - response = urlfetch.fetch(target_uri) - return response - except urlfetch.DownloadError: - logging.info('Download Error, Retry %s times'%RETRY) - continue - except: - raise PingbackError(16) - else: - logging.info('Times Over') - raise PingbackError(16) - - -class admin_do_action(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - try: - func=getattr(self,'action_'+slug) - if func and callable(func): - func() - else: - self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) - except: - self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) - - - def post(self,slug=None): - try: - func=getattr(self,'action_'+slug) - if func and callable(func): - func() - else: - self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) - except: - self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) - - @requires_admin - def action_test(self): - self.write(os.environ) - - @requires_admin - def action_cacheclear(self): - memcache.flush_all() - self.write(_('"Cache cleared successful"')) - - @requires_admin - def action_updatecomments(self): - for entry in Entry.all(): - cnt=entry.comments().count() - if cnt<>entry.commentcount: - entry.commentcount=cnt - entry.put() - self.write(_('"All comments updated"')) - - @requires_admin - def action_updatecommentno(self): - for entry in Entry.all(): - entry.update_commentno() - self.write(_('"All comments number Updates."')) - - @requires_admin - def action_updatelink(self): - link_format=self.param('linkfmt') - - if link_format: - link_format=link_format.strip() - g_blog.link_format=link_format - g_blog.save() - for entry in Entry.all(): - vals={'year':entry.date.year,'month':str(entry.date.month).zfill(2),'day':entry.date.day, - 'postname':entry.slug,'post_id':entry.post_id} - - if entry.slug: - newlink=link_format%vals - else: - newlink=g_blog.default_link_format%vals - - if entry.link<>newlink: - entry.link=newlink - entry.put() - self.write(_('"Link formated succeed"')) - else: - self.write(_('"Please input url format."')) - - @requires_admin - def action_init_blog(self,slug=None): - - for com in Comment.all(): - com.delete() - - for entry in Entry.all(): - entry.delete() - - g_blog.entrycount=0 - self.write(_('"Init has succeed."')) - - @requires_admin - def action_update_tags(self,slug=None): - for tag in Tag.all(): - tag.delete() - for entry in Entry.all().filter('entrytype =','post'): - if entry.tags: - for t in entry.tags: - try: - Tag.add(t) - except: - traceback.print_exc() - - self.write(_('"All tags for entry have been updated."')) - - @requires_admin - def action_update_archives(self,slug=None): - for archive in Archive.all(): - archive.delete() - entries=Entry.all().filter('entrytype =','post') - - archives={} - - - for entry in entries: - my = entry.date.strftime('%B %Y') # September-2008 - sy = entry.date.strftime('%Y') #2008 - sm = entry.date.strftime('%m') #09 - if archives.has_key(my): - archive=archives[my] - archive.entrycount+=1 - else: - archive = Archive(monthyear=my,year=sy,month=sm,entrycount=1) - archives[my]=archive - - for ar in archives.values(): - ar.put() - - self.write(_('"All entries have been updated."')) - - - def action_trackback_ping(self): - tbUrl=self.param('tbUrl') - title=self.param('title') - excerpt=self.param('excerpt') - url=self.param('url') - blog_name=self.param('blog_name') - tb=TrackBack(tbUrl,title,excerpt,url,blog_name) - tb.ping() - - def action_pingback_ping(self): - """Try to notify the server behind `target_uri` that `source_uri` - points to `target_uri`. If that fails an `PingbackError` is raised. - """ - source_uri=self.param('source') - target_uri=self.param('target') - try: - #response =urlfetch.fetch(target_uri) - response=fetch_result(target_uri) #retry up to 5 times - except: - raise PingbackError(32) - - try: - pingback_uri = response.headers['X-Pingback'] - except KeyError: - _pingback_re = re.compile(r'(?i)') - match = _pingback_re.search(response.content) - if match is None: - raise PingbackError(33) - pingback_uri =urldecode(match.group(1)) - - rpc = xmlrpclib.ServerProxy(pingback_uri) - try: - return rpc.pingback.ping(source_uri, target_uri) - except Fault, e: - raise PingbackError(e.faultCode) - except: - raise PingbackError(32) - - - - -class admin_tools(BaseRequestHandler): - def __init__(self): - self.current="config" - - @requires_admin - def get(self,slug=None): - self.render2('views/admin/tools.html') - - -class admin_sitemap(BaseRequestHandler): - def __init__(self): - self.current="config" - - @requires_admin - def get(self,slug=None): - self.render2('views/admin/sitemap.html') - - - @requires_admin - def post(self): - str_options= self.param('str_options').split(',') - for name in str_options: - value=self.param(name) - setattr(g_blog,name,value) - - bool_options= self.param('bool_options').split(',') - for name in bool_options: - value=self.param(name)=='on' - setattr(g_blog,name,value) - - int_options= self.param('int_options').split(',') - for name in int_options: - try: - value=int( self.param(name)) - setattr(g_blog,name,value) - except: - pass - float_options= self.param('float_options').split(',') - for name in float_options: - try: - value=float( self.param(name)) - setattr(g_blog,name,value) - except: - pass - - - g_blog.save() - self.render2('views/admin/sitemap.html',{}) - -class admin_import(BaseRequestHandler): - def __init__(self): - self.current='config' - - @requires_admin - def get(self,slug=None): - gblog_init() - self.render2('views/admin/import.html',{'importitems': - self.blog.plugins.filter('is_import_plugin',True)}) + for RETRY in range(5): + try: + response = urlfetch.fetch(target_uri) + return response + except urlfetch.DownloadError: + logging.info('Download Error, Retry %s times'%RETRY) + continue + except: + raise base.PingbackError(16) + else: + logging.info('Times Over') + raise base.PingbackError(16) + + +class admin_do_action(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug=None): + try: + func=getattr(self,'action_'+slug) + if func and callable(func): + func() + else: + self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) + except: + self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) + + + def post(self,slug=None): + try: + func=getattr(self,'action_'+slug) + if func and callable(func): + func() + else: + self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) + except: + self.render2('views/admin/error.html',{'message':_('This operate has not defined!')}) + + @base.requires_admin + def action_test(self): + self.write(os.environ) + + @base.requires_admin + def action_cacheclear(self): + memcache.flush_all() + self.write(_('"Cache cleared successful"')) + + @base.requires_admin + def action_updatecomments(self): + for entry in Entry.all(): + cnt=entry.comments().count() + if cnt<>entry.commentcount: + entry.commentcount=cnt + entry.put() + self.write(_('"All comments updated"')) + + @base.requires_admin + def action_updatecommentno(self): + for entry in Entry.all(): + entry.update_commentno() + self.write(_('"All comments number Updates."')) + + @base.requires_admin + def action_updatelink(self): + link_format=self.param('linkfmt') + + if link_format: + link_format=link_format.strip() + g_blog.link_format=link_format + g_blog.save() + for entry in Entry.all(): + vals={'year':entry.date.year,'month':str(entry.date.month).zfill(2),'day':entry.date.day, + 'postname':entry.slug,'post_id':entry.post_id} + + if entry.slug: + newlink=link_format%vals + else: + newlink=g_blog.default_link_format%vals + + if entry.link<>newlink: + entry.link=newlink + entry.put() + self.write(_('"Link formated succeed"')) + else: + self.write(_('"Please input url format."')) + + @base.requires_admin + def action_init_blog(self,slug=None): + + for com in Comment.all(): + com.delete() + + for entry in Entry.all(): + entry.delete() + + g_blog.entrycount=0 + self.write(_('"Init has succeed."')) + + @base.requires_admin + def action_update_tags(self,slug=None): + for tag in Tag.all(): + tag.delete() + for entry in Entry.all().filter('entrytype =','post'): + if entry.tags: + for t in entry.tags: + try: + Tag.add(t) + except: + base.traceback.print_exc() + + self.write(_('"All tags for entry have been updated."')) + + @base.requires_admin + def action_update_archives(self,slug=None): + for archive in Archive.all(): + archive.delete() + entries=Entry.all().filter('entrytype =','post') + + archives={} + + + for entry in entries: + my = entry.date.strftime('%B %Y') # September-2008 + sy = entry.date.strftime('%Y') #2008 + sm = entry.date.strftime('%m') #09 + if archives.has_key(my): + archive=archives[my] + archive.entrycount+=1 + else: + archive = Archive(monthyear=my,year=sy,month=sm,entrycount=1) + archives[my]=archive + + for ar in archives.values(): + ar.put() + + self.write(_('"All entries have been updated."')) + + + def action_trackback_ping(self): + tbUrl=self.param('tbUrl') + title=self.param('title') + excerpt=self.param('excerpt') + url=self.param('url') + blog_name=self.param('blog_name') + tb=TrackBack(tbUrl,title,excerpt,url,blog_name) + tb.ping() + + def action_pingback_ping(self): + """Try to notify the server behind `target_uri` that `source_uri` + points to `target_uri`. If that fails an `PingbackError` is raised. + """ + source_uri=self.param('source') + target_uri=self.param('target') + try: + #response =urlfetch.fetch(target_uri) + response=fetch_result(target_uri) #retry up to 5 times + except: + raise base.PingbackError(32) + + try: + pingback_uri = response.headers['X-Pingback'] + except KeyError: + _pingback_re = re.compile(r'(?i)') + match = _pingback_re.search(response.content) + if match is None: + raise base.PingbackError(33) + pingback_uri =base.urldecode(match.group(1)) + + rpc = xmlrpclib.ServerProxy(pingback_uri) + try: + return rpc.pingback.ping(source_uri, target_uri) + except Fault, e: + raise base.PingbackError(e.faultCode) + except: + raise base.PingbackError(32) + + + + +class admin_tools(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current="config" + + @base.requires_admin + def get(self,slug=None): + self.render2('views/admin/tools.html') + + +class admin_sitemap(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current="config" + + @base.requires_admin + def get(self,slug=None): + self.render2('views/admin/sitemap.html') + + + @base.requires_admin + def post(self): + str_options= self.param('str_options').split(',') + for name in str_options: + value=self.param(name) + setattr(g_blog,name,value) + + bool_options= self.param('bool_options').split(',') + for name in bool_options: + value=self.param(name)=='on' + setattr(g_blog,name,value) + + int_options= self.param('int_options').split(',') + for name in int_options: + try: + value=int( self.param(name)) + setattr(g_blog,name,value) + except: + pass + float_options= self.param('float_options').split(',') + for name in float_options: + try: + value=float( self.param(name)) + setattr(g_blog,name,value) + except: + pass + + + g_blog.save() + self.render2('views/admin/sitemap.html',{}) + +class admin_import(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current='config' + + @base.requires_admin + def get(self,slug=None): + gblog_init() + self.render2('views/admin/import.html',{'importitems': + self.blog.plugins.filter('is_import_plugin',True)}) ## def post(self): ## try: @@ -343,788 +329,791 @@ def get(self,slug=None): ## except: ## self.render2('views/admin/import.html',{'error':'import faiure.'}) -class admin_setup(BaseRequestHandler): - def __init__(self): - self.current='config' - - @requires_admin - def get(self,slug=None): - vals={'themes':ThemeIterator()} - self.render2('views/admin/setup.html',vals) - - @requires_admin - def post(self): - old_theme=g_blog.theme_name - str_options= self.param('str_options').split(',') - for name in str_options: - value=self.param(name) - setattr(g_blog,name,value) - - bool_options= self.param('bool_options').split(',') - for name in bool_options: - value=self.param(name)=='on' - setattr(g_blog,name,value) - - int_options= self.param('int_options').split(',') - for name in int_options: - try: - value=int( self.param(name)) - setattr(g_blog,name,value) - except: - pass - float_options= self.param('float_options').split(',') - for name in float_options: - try: - value=float( self.param(name)) - setattr(g_blog,name,value) - except: - pass - - - if old_theme !=g_blog.theme_name: - g_blog.get_theme() - - - g_blog.owner=self.login_user - g_blog.author=g_blog.owner.nickname() - g_blog.save() - gblog_init() - vals={'themes':ThemeIterator()} - memcache.flush_all() - self.render2('views/admin/setup.html',vals) - -class admin_entry(BaseRequestHandler): - def __init__(self): - self.current='write' - - @requires_admin - def get(self,slug='post'): - action=self.param("action") - entry=None - cats=Category.all() - alltags=Tag.all() - if action and action=='edit': - try: - key=self.param('key') - entry=Entry.get(key) - - except: - pass - else: - action='add' - - def mapit(cat): - return {'name':cat.name,'slug':cat.slug,'select':entry and cat.key() in entry.categorie_keys} - - vals={'action':action,'entry':entry,'entrytype':slug,'cats':map(mapit,cats),'alltags':alltags} - self.render2('views/admin/entry.html',vals) - - @requires_admin - def post(self,slug='post'): - action=self.param("action") - title=self.param("post_title") - content=self.param('content') - tags=self.param("tags") - cats=self.request.get_all('cats') - key=self.param('key') - if self.param('publish')!='': - published=True - elif self.param('unpublish')!='': - published=False - else: - published=self.param('published')=='True' - - allow_comment=self.parambool('allow_comment') - allow_trackback=self.parambool('allow_trackback') - entry_slug=self.param('slug') - entry_parent=self.paramint('entry_parent') - menu_order=self.paramint('menu_order') - entry_excerpt=self.param('excerpt').replace('\n','
') - password=self.param('password') - sticky=self.parambool('sticky') - - is_external_page=self.parambool('is_external_page') - target=self.param('target') - external_page_address=self.param('external_page_address') - - def mapit(cat): - return {'name':cat.name,'slug':cat.slug,'select':cat.slug in cats} - - vals={'action':action,'postback':True,'cats':Category.all(),'entrytype':slug, - 'cats':map(mapit,Category.all()), - 'entry':{ 'title':title,'content':content,'strtags':tags,'key':key,'published':published, - 'allow_comment':allow_comment, - 'allow_trackback':allow_trackback, - 'slug':entry_slug, - 'entry_parent':entry_parent, - 'excerpt':entry_excerpt, - 'menu_order':menu_order, - 'is_external_page':is_external_page, - 'target':target, - 'external_page_address':external_page_address, - 'password':password, - 'sticky':sticky} - } - - if not (title and (content or (is_external_page and external_page_address))): - vals.update({'result':False, 'msg':_('Please input title and content.')}) - self.render2('views/admin/entry.html',vals) - else: - if action=='add': - entry= Entry(title=title,content=content) - entry.settags(tags) - entry.entrytype=slug - entry.slug=entry_slug.replace(" ","-") - entry.entry_parent=entry_parent - entry.menu_order=menu_order - entry.excerpt=entry_excerpt - entry.is_external_page=is_external_page - entry.target=target - entry.external_page_address=external_page_address - newcates=[] - entry.allow_comment=allow_comment - entry.allow_trackback=allow_trackback - entry.author=self.author.user - entry.author_name=self.author.dispname - entry.password=password - entry.sticky=sticky - if cats: - - for cate in cats: - c=Category.all().filter('slug =',cate) - if c: - newcates.append(c[0].key()) - entry.categorie_keys=newcates; - - entry.save(published) - if published: - smsg=_('Saved ok. View it now!') - else: - smsg=_('Saved ok.') - - vals.update({'action':'edit','result':True,'msg':smsg%{'link':str(entry.link)},'entry':entry}) - self.render2('views/admin/entry.html',vals) - if published and entry.allow_trackback and g_blog.allow_pingback: - try: - autoPingback(entry.fullurl,HTML=content) - except: - pass - elif action=='edit': - try: - entry=Entry.get(key) - entry.title=title - entry.content=content - entry.slug=entry_slug.replace(' ','-') - entry.entry_parent=entry_parent - entry.menu_order=menu_order - entry.excerpt=entry_excerpt - entry.is_external_page=is_external_page - entry.target=target - entry.external_page_address=external_page_address - entry.settags(tags) - entry.author=self.author.user - entry.author_name=self.author.dispname - entry.password=password - entry.sticky=sticky - newcates=[] - - if cats: - - for cate in cats: - c=Category.all().filter('slug =',cate) - if c: - newcates.append(c[0].key()) - entry.categorie_keys=newcates; - entry.allow_comment=allow_comment - entry.allow_trackback=allow_trackback - - entry.save(published) - - if published: - smsg=_('Saved ok. View it now!') - else: - smsg=_('Saved ok.') - vals.update({'result':True,'msg':smsg%{'link':str(urlencode( entry.link))},'entry':entry}) - - self.render2('views/admin/entry.html',vals) - if published and entry.allow_trackback and g_blog.allow_pingback: - try: - autoPingback(entry.fullurl,HTML=content) - except: - pass - - except: - vals.update({'result':False,'msg':_('Error:Entry can''t been saved.')}) - self.render2('views/admin/entry.html',vals) - - -class admin_entries(BaseRequestHandler): - @requires_admin - def get(self,slug='post'): - try: - page_index=int(self.param('page')) - except: - page_index=1 - - - - - entries=Entry.all().filter('entrytype =',slug).order('-date') - entries,links=Pager(query=entries,items_per_page=15).fetch(page_index) - - self.render2('views/admin/'+slug+'s.html', - { - 'current':slug+'s', - 'entries':entries, - 'pager':links - } - ) - - @requires_admin - def post(self,slug='post'): - try: - linkcheck= self.request.get_all('checks') - for id in linkcheck: - kid=int(id) - - entry=Entry.get_by_id(kid) - - #delete it's comments - #entry.delete_comments() - - entry.delete() - g_blog.entrycount-=1 - finally: - - self.redirect('/admin/entries/'+slug) - - -class admin_categories(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - try: - page_index=int(self.param('page')) - except: - page_index=1 - - - - - cats=Category.allTops() - entries,pager=Pager(query=cats,items_per_page=15).fetch(page_index) - - self.render2('views/admin/categories.html', - { - 'current':'categories', - 'cats':cats, - 'pager':pager - } - ) - - @requires_admin - def post(self,slug=None): - try: - linkcheck= self.request.get_all('checks') - for key in linkcheck: - - cat=Category.get(key) - if cat: - cat.delete() - finally: - self.redirect('/admin/categories') - -class admin_comments(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - try: - page_index=int(self.param('page')) - except: - page_index=1 - - - - cq=self.param('cq') - cv=self.param('cv') - if cq and cv: - query=Comment.all().filter(cq+' =',cv).order('-date') - else: - query=Comment.all().order('-date') - comments,pager=Pager(query=query,items_per_page=15).fetch(page_index) - - self.render2('views/admin/comments.html', - { - 'current':'comments', - 'comments':comments, - 'pager':pager, - 'cq':cq, - 'cv':cv - } - ) - - @requires_admin - def post(self,slug=None): - try: - linkcheck= self.request.get_all('checks') - entrykeys=[] - for key in linkcheck: - - comment=Comment.get(key) - comment.delit() - entrykeys.append(comment.entry.key()) - entrykeys=set(entrykeys) - for key in entrykeys: - e=Entry.get(key) - e.update_commentno() - e.removecache() - memcache.delete("/feed/comments") - finally: - self.redirect(self.request.uri) - -class admin_links(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - self.render2('views/admin/links.html', - { - 'current':'links', - 'links':Link.all().filter('linktype =','blogroll')#.order('-createdate') - } - ) - @requires_admin - def post(self): - linkcheck= self.request.get_all('linkcheck') - for link_id in linkcheck: - kid=int(link_id) - link=Link.get_by_id(kid) - link.delete() - self.redirect('/admin/links') - -class admin_link(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - action=self.param("action") - vals={'current':'links'} - if action and action=='edit': - try: - action_id=int(self.param('id')) - link=Link.get_by_id(action_id) - vals.update({'link':link}) - except: - pass - else: - action='add' - vals.update({'action':action}) - - self.render2('views/admin/link.html',vals) - - @requires_admin - def post(self): - action=self.param("action") - name=self.param("link_name") - url=self.param("link_url") - comment = self.param("link_comment") - - vals={'action':action,'postback':True,'current':'links'} - if not (name and url): - vals.update({'result':False,'msg':_('Please input name and url.')}) - self.render2('views/admin/link.html',vals) - else: - if action=='add': - link= Link(linktext=name,href=url,linkcomment=comment) - link.put() - vals.update({'result':True,'msg':'Saved ok'}) - self.render2('views/admin/link.html',vals) - elif action=='edit': - try: - action_id=int(self.param('id')) - link=Link.get_by_id(action_id) - link.linktext=name - link.href=url - link.linkcomment = comment - link.put() - #goto link manage page - self.redirect('/admin/links') - - except: - vals.update({'result':False,'msg':_('Error:Link can''t been saved.')}) - self.render2('views/admin/link.html',vals) - -class admin_category(BaseRequestHandler): - def __init__(self): - self.current='categories' - - @requires_admin - def get(self,slug=None): - action=self.param("action") - key=self.param('key') - category=None - if action and action=='edit': - try: - - category=Category.get(key) - - except: - pass - else: - action='add' - vals={'action':action,'category':category,'key':key,'categories':[c for c in Category.all() if not category or c.key()!=category.key()]} - self.render2('views/admin/category.html',vals) - - @requires_admin - def post(self): - def check(cate): - parent=cate.parent_cat - skey=cate.key() - while parent: - if parent.key()==skey: - return False - parent=parent.parent_cat - return True - - action=self.param("action") - name=self.param("name") - slug=self.param("slug") - parentkey=self.param('parentkey') - key=self.param('key') - - - vals={'action':action,'postback':True} - - try: - - if action=='add': - cat= Category(name=name,slug=slug) - if not (name and slug): - raise Exception(_('Please input name and slug.')) - if parentkey: - cat.parent_cat=Category.get(parentkey) - - cat.put() - self.redirect('/admin/categories') - - #vals.update({'result':True,'msg':_('Saved ok')}) - #self.render2('views/admin/category.html',vals) - elif action=='edit': - - cat=Category.get(key) - cat.name=name - cat.slug=slug - if not (name and slug): - raise Exception(_('Please input name and slug.')) - if parentkey: - cat.parent_cat=Category.get(parentkey) - if not check(cat): - raise Exception(_('A circle declaration found.')) - else: - cat.parent_cat=None - cat.put() - self.redirect('/admin/categories') - - except Exception ,e : - if cat.is_saved(): - cates=[c for c in Category.all() if c.key()!=cat.key()] - else: - cates= Category.all() - - vals.update({'result':False,'msg':e.message,'category':cat,'key':key,'categories':cates}) - self.render2('views/admin/category.html',vals) - -class admin_status(BaseRequestHandler): - @requires_admin - def get(self): - self.render2('views/admin/status.html',{'cache':memcache.get_stats(),'current':'status','environ':os.environ}) -class admin_authors(BaseRequestHandler): - @requires_admin - def get(self): - try: - page_index=int(self.param('page')) - except: - page_index=1 - - - - - authors=User.all().filter('isAuthor =',True) - entries,pager=Pager(query=authors,items_per_page=15).fetch(page_index) - - self.render2('views/admin/authors.html', - { - 'current':'authors', - 'authors':authors, - 'pager':pager - } - ) - - - @requires_admin - def post(self,slug=None): - try: - linkcheck= self.request.get_all('checks') - for key in linkcheck: - - author=User.get(key) - author.delete() - finally: - self.redirect('/admin/authors') -class admin_author(BaseRequestHandler): - def __init__(self): - self.current='authors' - - @requires_admin - def get(self,slug=None): - action=self.param("action") - author=None - if action and action=='edit': - try: - key=self.param('key') - author=User.get(key) - - except: - pass - else: - action='add' - vals={'action':action,'author':author} - self.render2('views/admin/author.html',vals) - - @requires_admin - def post(self): - action=self.param("action") - name=self.param("name") - slug=self.param("email") - - vals={'action':action,'postback':True} - if not (name and slug): - vals.update({'result':False,'msg':_('Please input dispname and email.')}) - self.render2('views/admin/author.html',vals) - else: - if action=='add': - author= User(dispname=name,email=slug ) - author.user=db.users.User(slug) - author.put() - vals.update({'result':True,'msg':'Saved ok'}) - self.render2('views/admin/author.html',vals) - elif action=='edit': - try: - key=self.param('key') - author=User.get(key) - author.dispname=name - author.email=slug - author.user=db.users.User(slug) - author.put() - if author.isadmin: - g_blog.author=name - self.redirect('/admin/authors') - - except: - vals.update({'result':False,'msg':_('Error:Author can''t been saved.')}) - self.render2('views/admin/author.html',vals) -class admin_plugins(BaseRequestHandler): - def __init__(self): - self.current='plugins' - - @requires_admin - def get(self,slug=None): - vals={'plugins':self.blog.plugins} - self.render2('views/admin/plugins.html',vals) - - @requires_admin - def post(self): - action=self.param("action") - name=self.param("plugin") - ret=self.param("return") - self.blog.plugins.activate(name,action=="activate") - if ret: - self.redirect(ret) - else: - vals={'plugins':self.blog.plugins} - self.render2('views/admin/plugins.html',vals) - -class admin_plugins_action(BaseRequestHandler): - def __init__(self): - self.current='plugins' - - @requires_admin - def get(self,slug=None): - plugin=self.blog.plugins.getPluginByName(slug) - if not plugin : - self.error(404) - return - plugins=self.blog.plugins.filter('active',True) - if not plugin.active: - pcontent=_('''
Plugin '%(name)s' havn't actived!

''')%{'name':plugin.name,'iname':plugin.iname} - plugins.insert(0,plugin) - else: - pcontent=plugin.get(self) - - - vals={'plugins':plugins, - 'plugin':plugin, - 'pcontent':pcontent} - - self.render2('views/admin/plugin_action.html',vals) - - @requires_admin - def post(self,slug=None): - - plugin=self.blog.plugins.getPluginByName(slug) - if not plugin : - self.error(404) - return - plugins=self.blog.plugins.filter('active',True) - if not plugin.active: - pcontent=_('''
Plugin '%(name)s' havn't actived!

''')%{'name':plugin.name,'iname':plugin.iname} - plugins.insert(0,plugin) - else: - pcontent=plugin.post(self) - - - vals={'plugins':plugins, - 'plugin':plugin, - 'pcontent':pcontent} - - self.render2('views/admin/plugin_action.html',vals) - -class WpHandler(BaseRequestHandler): - @requires_admin - def get(self,tags=None): - try: - all=self.param('all') - except: - all=False - - if(all): - entries = Entry.all().order('-date') - filename='micolog.%s.xml'%datetime.now().strftime('%Y-%m-%d') - else: - str_date_begin=self.param('date_begin') - str_date_end=self.param('date_end') - try: - date_begin=datetime.strptime(str_date_begin,"%Y-%m-%d") - date_end=datetime.strptime(str_date_end,"%Y-%m-%d") - entries = Entry.all().filter('date >=',date_begin).filter('date <',date_end).order('-date') - filename='micolog.%s.%s.xml'%(str(str_date_begin),str(str_date_end)) - except: - self.render2('views/admin/404.html') - return - - cates=Category.all() - tags=Tag.all() - - self.response.headers['Content-Type'] = 'binary/octet-stream'#'application/atom+xml' - self.response.headers['Content-Disposition'] = 'attachment; filename=%s'%filename - self.render2('views/wordpress.xml',{'entries':entries,'cates':cates,'tags':tags}) - -class Upload(BaseRequestHandler): - - @requires_admin - def post(self): - name = self.param('filename') - mtype = self.param('fileext') - bits = self.param('upfile') - Media(name = name, mtype = mtype, bits = bits).put() - - self.redirect('/admin/filemanager') - -class UploadEx(BaseRequestHandler): - @requires_admin - def get(self): - extstr=self.param('ext') - ext=extstr.split('|') - files=Media.all() - if extstr!='*': - files=files.filter('mtype IN',ext) - self.render2('views/admin/upload.html',{'ext':extstr,'files':files}) - - @requires_admin - def post(self): - ufile=self.request.params['userfile'] - #if ufile: - name=ufile.filename - mtype =os.path.splitext(name)[1][1:] - bits = self.param('userfile') - media=Media(name = name, mtype = mtype, bits = bits) - media.put() - self.write(simplejson.dumps({'name':media.name,'size':media.size,'id':str(media.key())})) - -class FileManager(BaseRequestHandler): - - def __init__(self): - self.current='files' - - @requires_admin - def get(self): - try: - page_index=int(self.param('page')) - except: - page_index=1 - files = Media.all().order('-date') - files,links=Pager(query=files,items_per_page=15).fetch(page_index) - self.render2('views/admin/filemanager.html',{'files' : files,'pager':links}) - - @requires_admin - def post(self): # delete files - delids = self.request.POST.getall('del') - if delids: - for id in delids: - file = Media.get_by_id(int(id)) - file.delete() - - self.redirect('/admin/filemanager') - -class admin_main(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - if self.is_admin: - self.redirect('/admin/setup') - else: - self.redirect('/admin/entries/post') - -class admin_ThemeEdit(BaseRequestHandler): - @requires_admin - def get(self,slug): - zfile=zipfile.ZipFile(os.path.join(rootpath,"themes",slug+".zip")) - newfile=zipfile.ZipFile('') - for item in zfile.infolist(): - self.write(item.filename+"
") +class admin_setup(base.BaseRequestHandler): + def __init__(self): + self.current='config' + + @base.requires_admin + def get(self,slug=None): + vals={'themes':ThemeIterator()} + self.render2('views/admin/setup.html',vals) + + @base.requires_admin + def post(self): + old_theme=g_blog.theme_name + str_options= self.param('str_options').split(',') + for name in str_options: + value=self.param(name) + setattr(g_blog,name,value) + + bool_options= self.param('bool_options').split(',') + for name in bool_options: + value=self.param(name)=='on' + setattr(g_blog,name,value) + + int_options= self.param('int_options').split(',') + for name in int_options: + try: + value=int( self.param(name)) + setattr(g_blog,name,value) + except: + pass + float_options= self.param('float_options').split(',') + for name in float_options: + try: + value=float( self.param(name)) + setattr(g_blog,name,value) + except: + pass + + + if old_theme !=g_blog.theme_name: + g_blog.get_theme() + + + g_blog.owner=self.login_user + g_blog.author=g_blog.owner.nickname() + g_blog.save() + gblog_init() + vals={'themes':ThemeIterator()} + memcache.flush_all() + self.render2('views/admin/setup.html',vals) + +class admin_entry(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current='write' + + @base.requires_admin + def get(self,slug='post'): + action=self.param("action") + entry=None + cats=Category.all() + alltags=Tag.all() + if action and action=='edit': + try: + key=self.param('key') + entry=Entry.get(key) + + except: + pass + else: + action='add' + + def mapit(cat): + return {'name':cat.name,'slug':cat.slug,'select':entry and cat.key() in entry.categorie_keys} + + vals={'action':action,'entry':entry,'entrytype':slug,'cats':map(mapit,cats),'alltags':alltags} + self.render2('views/admin/entry.html',vals) + + @base.requires_admin + def post(self,slug='post'): + action=self.param("action") + title=self.param("post_title") + content=self.param('content') + tags=self.param("tags") + cats=self.request.get_all('cats') + key=self.param('key') + if self.param('publish')!='': + published=True + elif self.param('unpublish')!='': + published=False + else: + published=self.param('published')=='True' + + allow_comment=self.parambool('allow_comment') + allow_trackback=self.parambool('allow_trackback') + entry_slug=self.param('slug') + entry_parent=self.paramint('entry_parent') + menu_order=self.paramint('menu_order') + entry_excerpt=self.param('excerpt').replace('\n','
') + password=self.param('password') + sticky=self.parambool('sticky') + + is_external_page=self.parambool('is_external_page') + target=self.param('target') + external_page_address=self.param('external_page_address') + + def mapit(cat): + return {'name':cat.name,'slug':cat.slug,'select':cat.slug in cats} + + vals={'action':action,'postback':True,'cats':Category.all(),'entrytype':slug, + 'cats':map(mapit,Category.all()), + 'entry':{ 'title':title,'content':content,'strtags':tags,'key':key,'published':published, + 'allow_comment':allow_comment, + 'allow_trackback':allow_trackback, + 'slug':entry_slug, + 'entry_parent':entry_parent, + 'excerpt':entry_excerpt, + 'menu_order':menu_order, + 'is_external_page':is_external_page, + 'target':target, + 'external_page_address':external_page_address, + 'password':password, + 'sticky':sticky} + } + + if not (title and (content or (is_external_page and external_page_address))): + vals.update({'result':False, 'msg':_('Please input title and content.')}) + self.render2('views/admin/entry.html',vals) + else: + if action=='add': + entry= Entry(title=title,content=content) + entry.settags(tags) + entry.entrytype=slug + entry.slug=entry_slug.replace(" ","_") + entry.entry_parent=entry_parent + entry.menu_order=menu_order + entry.excerpt=entry_excerpt + entry.is_external_page=is_external_page + entry.target=target + entry.external_page_address=external_page_address + newcates=[] + entry.allow_comment=allow_comment + entry.allow_trackback=allow_trackback + entry.author=self.author.user + entry.author_name=self.author.dispname + entry.password=password + entry.sticky=sticky + if cats: + + for cate in cats: + c=Category.all().filter('slug =',cate) + if c: + newcates.append(c[0].key()) + entry.categorie_keys=newcates; + + entry.save(published) + if published: + smsg=_('Saved ok. View it now!') + else: + smsg=_('Saved ok.') + + vals.update({'action':'edit','result':True,'msg':smsg%{'link':str(entry.link)},'entry':entry}) + self.render2('views/admin/entry.html',vals) + if published and entry.allow_trackback and g_blog.allow_pingback: + try: + autoPingback(str(entry.fullurl),HTML=content) + except: + pass + elif action=='edit': + try: + entry=Entry.get(key) + entry.title=title + entry.content=content + entry.slug=entry_slug.replace(' ','-') + entry.entry_parent=entry_parent + entry.menu_order=menu_order + entry.excerpt=entry_excerpt + entry.is_external_page=is_external_page + entry.target=target + entry.external_page_address=external_page_address + entry.settags(tags) + entry.author=self.author.user + entry.author_name=self.author.dispname + entry.password=password + entry.sticky=sticky + newcates=[] + + if cats: + + for cate in cats: + c=Category.all().filter('slug =',cate) + if c: + newcates.append(c[0].key()) + entry.categorie_keys=newcates; + entry.allow_comment=allow_comment + entry.allow_trackback=allow_trackback + + entry.save(published) + + if published: + smsg=_('Saved ok. View it now!') + else: + smsg=_('Saved ok.') + vals.update({'result':True,'msg':smsg%{'link':str(base.urlencode( entry.link))},'entry':entry}) + + self.render2('views/admin/entry.html',vals) + if published and entry.allow_trackback and g_blog.allow_pingback: + try: + autoPingback(entry.fullurl,HTML=content) + except: + pass + + except: + vals.update({'result':False,'msg':_('Error:Entry can''t been saved.')}) + self.render2('views/admin/entry.html',vals) + + +class admin_entries(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug='post'): + try: + page_index=int(self.param('page')) + except: + page_index=1 + + + + + entries=Entry.all().filter('entrytype =',slug).order('-date') + entries,links=base.Pager(query=entries,items_per_page=15).fetch(page_index) + + self.render2('views/admin/'+slug+'s.html', + { + 'current':slug+'s', + 'entries':entries, + 'pager':links + } + ) + + @base.requires_admin + def post(self,slug='post'): + try: + linkcheck= self.request.get_all('checks') + for id in linkcheck: + kid=int(id) + + entry=Entry.get_by_id(kid) + + #delete it's comments + #entry.delete_comments() + + entry.delete() + g_blog.entrycount-=1 + finally: + + self.redirect('/admin/entries/'+slug) + + +class admin_categories(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug=None): + try: + page_index=int(self.param('page')) + except: + page_index=1 + + + + + cats=Category.allTops() + entries,pager=base.Pager(query=cats,items_per_page=15).fetch(page_index) + + self.render2('views/admin/categories.html', + { + 'current':'categories', + 'cats':cats, + 'pager':pager + } + ) + + @base.requires_admin + def post(self,slug=None): + try: + linkcheck= self.request.get_all('checks') + for key in linkcheck: + + cat=Category.get(key) + if cat: + cat.delete() + finally: + self.redirect('/admin/categories') + +class admin_comments(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug=None): + try: + page_index=int(self.param('page')) + except: + page_index=1 + + + + cq=self.param('cq') + cv=self.param('cv') + if cq and cv: + query=Comment.all().filter(cq+' =',cv).order('-date') + else: + query=Comment.all().order('-date') + comments,pager=base.Pager(query=query,items_per_page=15).fetch(page_index) + + self.render2('views/admin/comments.html', + { + 'current':'comments', + 'comments':comments, + 'pager':pager, + 'cq':cq, + 'cv':cv + } + ) + + @base.requires_admin + def post(self,slug=None): + try: + linkcheck= self.request.get_all('checks') + entrykeys=[] + for key in linkcheck: + + comment=Comment.get(key) + comment.delit() + entrykeys.append(comment.entry.key()) + entrykeys=set(entrykeys) + for key in entrykeys: + e=Entry.get(key) + e.update_commentno() + e.removecache() + memcache.delete("/feed/comments") + finally: + self.redirect(self.request.uri) + +class admin_links(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug=None): + self.render2('views/admin/links.html', + { + 'current':'links', + 'links':Link.all().filter('linktype =','blogroll')#.order('-createdate') + } + ) + @base.requires_admin + def post(self): + linkcheck= self.request.get_all('linkcheck') + for link_id in linkcheck: + kid=int(link_id) + link=Link.get_by_id(kid) + link.delete() + self.redirect('/admin/links') + +class admin_link(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug=None): + action=self.param("action") + vals={'current':'links'} + if action and action=='edit': + try: + action_id=int(self.param('id')) + link=Link.get_by_id(action_id) + vals.update({'link':link}) + except: + pass + else: + action='add' + vals.update({'action':action}) + + self.render2('views/admin/link.html',vals) + + @base.requires_admin + def post(self): + action=self.param("action") + name=self.param("link_name") + url=self.param("link_url") + comment = self.param("link_comment") + + vals={'action':action,'postback':True,'current':'links'} + if not (name and url): + vals.update({'result':False,'msg':_('Please input name and url.')}) + self.render2('views/admin/link.html',vals) + else: + if action=='add': + link= Link(linktext=name,href=url,linkcomment=comment) + link.put() + vals.update({'result':True,'msg':'Saved ok'}) + self.render2('views/admin/link.html',vals) + elif action=='edit': + try: + action_id=int(self.param('id')) + link=Link.get_by_id(action_id) + link.linktext=name + link.href=url + link.linkcomment = comment + link.put() + #goto link manage page + self.redirect('/admin/links') + + except: + vals.update({'result':False,'msg':_('Error:Link can''t been saved.')}) + self.render2('views/admin/link.html',vals) + +class admin_category(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current='categories' + + @base.requires_admin + def get(self,slug=None): + action=self.param("action") + key=self.param('key') + category=None + if action and action=='edit': + try: + + category=Category.get(key) + + except: + pass + else: + action='add' + vals={'action':action,'category':category,'key':key,'categories':[c for c in Category.all() if not category or c.key()!=category.key()]} + self.render2('views/admin/category.html',vals) + + @base.requires_admin + def post(self): + def check(cate): + parent=cate.parent_cat + skey=cate.key() + while parent: + if parent.key()==skey: + return False + parent=parent.parent_cat + return True + + action=self.param("action") + name=self.param("name") + slug=self.param("slug") + parentkey=self.param('parentkey') + key=self.param('key') + + + vals={'action':action,'postback':True} + + try: + + if action=='add': + cat= Category(name=name,slug=slug) + if not (name and slug): + raise Exception(_('Please input name and slug.')) + if parentkey: + cat.parent_cat=Category.get(parentkey) + + cat.put() + self.redirect('/admin/categories') + + #vals.update({'result':True,'msg':_('Saved ok')}) + #self.render2('views/admin/category.html',vals) + elif action=='edit': + + cat=Category.get(key) + cat.name=name + cat.slug=slug + if not (name and slug): + raise Exception(_('Please input name and slug.')) + if parentkey: + cat.parent_cat=Category.get(parentkey) + if not check(cat): + raise Exception(_('A circle declaration found.')) + else: + cat.parent_cat=None + cat.put() + self.redirect('/admin/categories') + + except Exception ,e : + if cat.is_saved(): + cates=[c for c in Category.all() if c.key()!=cat.key()] + else: + cates= Category.all() + + vals.update({'result':False,'msg':e.message,'category':cat,'key':key,'categories':cates}) + self.render2('views/admin/category.html',vals) + +class admin_status(base.BaseRequestHandler): + @base.requires_admin + def get(self): + self.render2('views/admin/status.html',{'cache':memcache.get_stats(),'current':'status','environ':os.environ}) +class admin_authors(base.BaseRequestHandler): + @base.requires_admin + def get(self): + try: + page_index=int(self.param('page')) + except: + page_index=1 + + + + + authors=User.all().filter('isAuthor =',True) + entries,pager=base.Pager(query=authors,items_per_page=15).fetch(page_index) + + self.render2('views/admin/authors.html', + { + 'current':'authors', + 'authors':authors, + 'pager':pager + } + ) + + + @base.requires_admin + def post(self,slug=None): + try: + linkcheck= self.request.get_all('checks') + for key in linkcheck: + + author=User.get(key) + author.delete() + finally: + self.redirect('/admin/authors') + +class admin_author(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current='authors' + + @base.requires_admin + def get(self,slug=None): + action=self.param("action") + author=None + if action and action=='edit': + try: + key=self.param('key') + author=User.get(key) + + except: + pass + else: + action='add' + vals={'action':action,'author':author} + self.render2('views/admin/author.html',vals) + + @base.requires_admin + def post(self): + action=self.param("action") + name=self.param("name") + slug=self.param("email") + + vals={'action':action,'postback':True} + if not (name and slug): + vals.update({'result':False,'msg':_('Please input dispname and email.')}) + self.render2('views/admin/author.html',vals) + else: + if action=='add': + author= User(dispname=name,email=slug ) + author.user=db.users.User(slug) + author.put() + vals.update({'result':True,'msg':'Saved ok'}) + self.render2('views/admin/author.html',vals) + elif action=='edit': + try: + key=self.param('key') + author=User.get(key) + author.dispname=name + author.email=slug + author.user=db.users.User(slug) + author.put() + if author.isadmin: + g_blog.author=name + self.redirect('/admin/authors') + + except: + vals.update({'result':False,'msg':_('Error:Author can''t been saved.')}) + self.render2('views/admin/author.html',vals) + +class admin_plugins(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current='plugins' + + @base.requires_admin + def get(self,slug=None): + vals={'plugins':self.blog.plugins} + self.render2('views/admin/plugins.html',vals) + + @base.requires_admin + def post(self): + action=self.param("action") + name=self.param("plugin") + ret=self.param("return") + self.blog.plugins.activate(name,action=="activate") + if ret: + self.redirect(ret) + else: + vals={'plugins':self.blog.plugins} + self.render2('views/admin/plugins.html',vals) + +class admin_plugins_action(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current='plugins' + + @base.requires_admin + def get(self,slug=None): + plugin=self.blog.plugins.getPluginByName(slug) + if not plugin : + self.error(404) + return + plugins=self.blog.plugins.filter('active',True) + if not plugin.active: + pcontent=_('''
Plugin '%(name)s' havn't actived!

''')%{'name':plugin.name,'iname':plugin.iname} + plugins.insert(0,plugin) + else: + pcontent=plugin.get(self) + + + vals={'plugins':plugins, + 'plugin':plugin, + 'pcontent':pcontent} + + self.render2('views/admin/plugin_action.html',vals) + + @base.requires_admin + def post(self,slug=None): + + plugin=self.blog.plugins.getPluginByName(slug) + if not plugin : + self.error(404) + return + plugins=self.blog.plugins.filter('active',True) + if not plugin.active: + pcontent=_('''
Plugin '%(name)s' havn't actived!

''')%{'name':plugin.name,'iname':plugin.iname} + plugins.insert(0,plugin) + else: + pcontent=plugin.post(self) + + + vals={'plugins':plugins, + 'plugin':plugin, + 'pcontent':pcontent} + + self.render2('views/admin/plugin_action.html',vals) + +class WpHandler(base.BaseRequestHandler): + @base.requires_admin + def get(self,tags=None): + try: + all=self.param('all') + except: + all=False + + if(all): + entries = Entry.all().order('-date') + filename='micolog.%s.xml'%datetime.now().strftime('%Y-%m-%d') + else: + str_date_begin=self.param('date_begin') + str_date_end=self.param('date_end') + try: + date_begin=datetime.strptime(str_date_begin,"%Y-%m-%d") + date_end=datetime.strptime(str_date_end,"%Y-%m-%d") + entries = Entry.all().filter('date >=',date_begin).filter('date <',date_end).order('-date') + filename='micolog.%s.%s.xml'%(str(str_date_begin),str(str_date_end)) + except: + self.render2('views/admin/404.html') + return + + cates=Category.all() + tags=Tag.all() + + self.response.headers['Content-Type'] = 'binary/octet-stream'#'application/atom+xml' + self.response.headers['Content-Disposition'] = 'attachment; filename=%s'%filename + self.render2('views/wordpress.xml',{'entries':entries,'cates':cates,'tags':tags}) + +class Upload(base.BaseRequestHandler): + @base.requires_admin + def post(self): + name = self.param('filename') + mtype = self.param('fileext') + bits = self.param('upfile') + Media(name = name, mtype = mtype, bits = bits).put() + + self.redirect('/admin/filemanager') + +class UploadEx(base.BaseRequestHandler): + @base.requires_admin + def get(self): + extstr=self.param('ext') + ext=extstr.split('|') + files=Media.all() + if extstr!='*': + files=files.filter('mtype IN',ext) + self.render2('views/admin/upload.html',{'ext':extstr,'files':files}) + + @base.requires_admin + def post(self): + ufile=self.request.params['userfile'] + #if ufile: + name=ufile.filename + mtype =os.path.splitext(name)[1][1:] + bits = self.param('userfile') + media=Media(name = name, mtype = mtype, bits = bits) + media.put() + self.write(simplejson.dumps({'name':media.name,'size':media.size,'id':str(media.key())})) + +class FileManager(base.BaseRequestHandler): + def __init__(self): + base.BaseRequestHandler.__init__(self) + self.current = 'files' + + @base.requires_admin + def get(self): + try: + page_index=int(self.param('page')) + except: + page_index=1 + files = Media.all().order('-date') + files,links=base.Pager(query=files,items_per_page=15).fetch(page_index) + self.render2('views/admin/filemanager.html',{'files' : files,'pager':links}) + + @base.requires_admin + def post(self): # delete files + delids = self.request.POST.getall('del') + if delids: + for id in delids: + file = Media.get_by_id(int(id)) + file.delete() + + self.redirect('/admin/filemanager') + +class admin_main(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug=None): + if self.is_admin: + self.redirect('/admin/setup') + else: + self.redirect('/admin/entries/post') + +class admin_ThemeEdit(base.BaseRequestHandler): + @base.requires_admin + def get(self,slug): + zfile=zipfile.ZipFile(os.path.join(rootpath,"themes",slug+".zip")) + newfile=zipfile.ZipFile('') + for item in zfile.infolist(): + self.write(item.filename+"
") def main(): - webapp.template.register_template_library('filter') - webapp.template.register_template_library('app.recurse') - - application = webapp.WSGIApplication( - [ - ('/admin/{0,1}',admin_main), - ('/admin/setup',admin_setup), - ('/admin/entries/(post|page)',admin_entries), - ('/admin/links',admin_links), - ('/admin/categories',admin_categories), - ('/admin/comments',admin_comments), - ('/admin/link',admin_link), - ('/admin/category',admin_category), - ('/admin/(post|page)',admin_entry), - ('/admin/status',admin_status), - ('/admin/authors',admin_authors), - ('/admin/author',admin_author), - ('/admin/import',admin_import), - ('/admin/tools',admin_tools), - ('/admin/plugins',admin_plugins), - ('/admin/plugins/(\w+)',admin_plugins_action), - ('/admin/sitemap',admin_sitemap), - ('/admin/export/micolog.xml',WpHandler), - ('/admin/do/(\w+)',admin_do_action), - ('/admin/lang',setlanguage), - ('/admin/theme/edit/(\w+)',admin_ThemeEdit), - - ('/admin/upload', Upload), - ('/admin/filemanager', FileManager), - - ('/admin/uploadex', UploadEx), - - ('.*',Error404), - ],debug=True) - g_blog.application=application - g_blog.plugins.register_handlerlist(application) - wsgiref.handlers.CGIHandler().run(application) + base.webapp.template.register_template_library('app.filter') + base.webapp.template.register_template_library('app.recurse') + + application = base.webapp.WSGIApplication( + [ + ('/admin/{0,1}',admin_main), + ('/admin/setup',admin_setup), + ('/admin/entries/(post|page)',admin_entries), + ('/admin/links',admin_links), + ('/admin/categories',admin_categories), + ('/admin/comments',admin_comments), + ('/admin/link',admin_link), + ('/admin/category',admin_category), + ('/admin/(post|page)',admin_entry), + ('/admin/status',admin_status), + ('/admin/authors',admin_authors), + ('/admin/author',admin_author), + ('/admin/import',admin_import), + ('/admin/tools',admin_tools), + ('/admin/plugins',admin_plugins), + ('/admin/plugins/(\w+)',admin_plugins_action), + ('/admin/sitemap',admin_sitemap), + ('/admin/export/micolog.xml',WpHandler), + ('/admin/do/(\w+)',admin_do_action), + ('/admin/lang',setlanguage), + ('/admin/theme/edit/(\w+)',admin_ThemeEdit), + ('/admin/upload', Upload), + ('/admin/filemanager', FileManager), + ('/admin/uploadex', UploadEx), + ('.*',Error404), + ],debug=True) + g_blog.application=application + g_blog.plugins.register_handlerlist(application) + wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": - main() \ No newline at end of file + main() \ No newline at end of file diff --git a/api_rpc.py b/api_rpc.py index f8f0e1a..61c22ef 100644 --- a/api_rpc.py +++ b/api_rpc.py @@ -1,140 +1,140 @@ # -*- coding: utf-8 -*- +import cgi,os,sys +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') import wsgiref.handlers import xmlrpclib from xmlrpclib import Fault -import sys -import cgi -import base64 +from datetime import timedelta #from datetime import datetime -import app.mktimefix as datetime from SimpleXMLRPCServer import SimpleXMLRPCDispatcher -from functools import wraps from django.utils.html import strip_tags sys.path.append('modules') from base import * from model import * from micolog_plugin import * -from urlparse import urlparse + MAX_NUM=100 def checkauth(pos=1): - def _decorate(method): - def _wrapper(*args, **kwargs): + def _decorate(method): + def _wrapper(*args, **kwargs): - username = args[pos+0] - password = args[pos+1] + username = args[pos+0] + password = args[pos+1] - if not (username and password and g_blog.rpcuser and g_blog.rpcpassword - and (g_blog.rpcuser==username) - and (g_blog.rpcpassword==password)): - raise ValueError("Authentication Failure") - args = args[0:pos]+args[pos+2:] - return method(*args, **kwargs) + if not (username and password and g_blog.rpcuser and g_blog.rpcpassword + and (g_blog.rpcuser==username) + and (g_blog.rpcpassword==password)): + raise ValueError("Authentication Failure") + args = args[0:pos]+args[pos+2:] + return method(*args, **kwargs) - return _wrapper - return _decorate + return _wrapper + return _decorate def format_date(d): - if not d: return None - #return xmlrpclib.DateTime(d.isoformat()) - return xmlrpclib.DateTime(d) + if not d: return None + #return xmlrpclib.DateTime(d.isoformat()) + return xmlrpclib.DateTime(d) def dateformat(creatdate): - try: - dt=datetime.strptime(creatdate, "%Y%m%dT%H:%M:%S") - except: - dt=datetime.strptime(creatdate, "%Y%m%dT%H:%M:%SZ") - return dt + try: + dt=datetime.strptime(creatdate, "%Y%m%dT%H:%M:%S") + except: + dt=datetime.strptime(creatdate, "%Y%m%dT%H:%M:%SZ") + return dt def post_struct(entry): - if not entry: - raise Fault(404, "Post does not exist") - categories=[] - if entry.categorie_keys: - categories =[cate.name for cate in entry.categories] - - - struct = { - 'postid': entry.key().id(), - 'title': entry.title, - 'link': entry.fullurl, - 'permaLink': entry.fullurl, - 'description': unicode(entry.content), - 'categories': categories, - 'userid': '1', - 'mt_keywords':','.join(entry.tags), - 'mt_excerpt': '', - 'mt_text_more': '', - 'mt_allow_comments': entry.allow_comment and 1 or 0, - 'mt_allow_pings': entry.allow_trackback and 1 or 0, - 'custom_fields':[], - 'post_status':entry.post_status, - 'sticky':entry.sticky, - 'wp_author_display_name': entry.get_author_user().dispname, - 'wp_author_id': str(entry.get_author_user().key().id()), - 'wp_password': entry.password, - 'wp_slug':entry.slug - } - if entry.date: - t=timedelta(seconds=3600*g_blog.timedelta) - struct['dateCreated'] = format_date(entry.date+t) - struct['date_created_gmt'] = format_date(entry.date) - - return struct + if not entry: + raise Fault(404, "Post does not exist") + categories=[] + if entry.categorie_keys: + categories =[cate.name for cate in entry.categories] + + + struct = { + 'postid': str(entry.key().id()), + 'title': entry.title, + 'link': entry.fullurl, + 'permaLink': entry.fullurl, + 'description': unicode(entry.content), + 'categories': categories, + 'userid': '1', + 'mt_keywords':','.join(entry.tags), + 'mt_excerpt': '', + 'mt_text_more': '', + 'mt_allow_comments': entry.allow_comment and 1 or 0, + 'mt_allow_pings': entry.allow_trackback and 1 or 0, + 'custom_fields':[], + 'post_status':entry.post_status, + 'sticky':entry.sticky, + 'wp_author_display_name': entry.get_author_user().dispname, + 'wp_author_id': str(entry.get_author_user().key().id()), + 'wp_password': entry.password, + 'wp_slug':entry.slug + } + if entry.date: + t=timedelta(seconds=3600*g_blog.timedelta) + struct['dateCreated'] = format_date(entry.date+t) + struct['date_created_gmt'] = format_date(entry.date) + + return struct def page_struct(entry): - if not entry: - raise Fault(404, "Post does not exist") - categories=[] - if entry.categorie_keys: - categories =[cate.name for cate in entry.categories] - - - struct = { - 'page_id': entry.key().id(), - 'title': entry.title, - 'link': entry.fullurl, - 'permaLink': entry.fullurl, - 'description': unicode(entry.content), - 'categories': categories, - 'userid': '1', - 'mt_allow_comments': entry.allow_comment and 1 or 0, - 'mt_allow_pings': entry.allow_trackback and 1 or 0, - 'custom_fields':[], - 'page_status':entry.post_status, - 'sticky':entry.sticky, - 'wp_author_display_name': entry.get_author_user().dispname, - 'wp_author_id': str(entry.get_author_user().key().id()), - 'wp_password': entry.password, - 'wp_slug':entry.slug, - 'text_more': '', - 'wp_author': 'admin', - 'wp_page_order': entry.menu_order, - 'wp_page_parent_id': 0, - 'wp_page_parent_title': '', - 'wp_page_template': 'default', - } - if entry.date: - struct['dateCreated'] = format_date(entry.date) - struct['date_created_gmt'] = format_date(entry.date) - - return struct + if not entry: + raise Fault(404, "Post does not exist") + categories=[] + if entry.categorie_keys: + categories =[cate.name for cate in entry.categories] + + + struct = { + 'page_id': str(entry.key().id()), + 'title': entry.title, + 'link': entry.fullurl, + 'permaLink': entry.fullurl, + 'description': unicode(entry.content), + 'categories': categories, + 'userid': '1', + 'mt_allow_comments': entry.allow_comment and 1 or 0, + 'mt_allow_pings': entry.allow_trackback and 1 or 0, + 'custom_fields':[], + 'page_status':entry.post_status, + 'sticky':entry.sticky, + 'wp_author_display_name': entry.get_author_user().dispname, + 'wp_author_id': str(entry.get_author_user().key().id()), + 'wp_password': entry.password, + 'wp_slug':entry.slug, + 'text_more': '', + 'wp_author': 'admin', + 'wp_page_order': entry.menu_order, + 'wp_page_parent_id': 0, + 'wp_page_parent_title': '', + 'wp_page_template': 'default', + } + if entry.date: + struct['dateCreated'] = format_date(entry.date) + struct['date_created_gmt'] = format_date(entry.date) + + return struct def entry_title_struct(entry): - if not entry: - raise Fault(404, "Post does not exist") - struct = { - 'postid': str(entry.key().id()), - 'title': entry.title, - 'userid': '1', - } - if entry.date: - struct['dateCreated'] = format_date(entry.date) - return struct + if not entry: + raise Fault(404, "Post does not exist") + struct = { + 'postid': str(entry.key().id()), + 'title': entry.title, + 'userid': '1', + } + if entry.date: + struct['dateCreated'] = format_date(entry.date) + return struct class Logger(db.Model): - request = db.TextProperty() - response = db.TextProperty() - date = db.DateTimeProperty(auto_now_add=True) + request = db.TextProperty() + response = db.TextProperty() + date = db.DateTimeProperty(auto_now_add=True) #------------------------------------------------------------------------------- @@ -143,27 +143,21 @@ class Logger(db.Model): @checkauth() def blogger_getUsersBlogs(discard): - return [{'url' : g_blog.baseurl, 'blogid' : '1','isAdmin':True, 'blogName' : g_blog.title,'xmlrpc':g_blog.baseurl+"/rpc"}] + return [{'url' : g_blog.baseurl, 'blogid' : '1','isAdmin':True, 'blogName' : g_blog.title,'xmlrpc':g_blog.baseurl+"/rpc"}] @checkauth(pos=2) def blogger_deletePost(appkey, postid, publish=False): - post=Entry.get_by_id(int(postid)) - post.delete() - return True + post=Entry.get_by_id(int(postid)) + post.delete() + return True @checkauth() def blogger_getUserInfo(appkey): - for user in User.all(): - if user.isadmin: - return {'email':user.email,'firstname':'','nickname':user.dispname,'userid':str(user.key().id()), - 'url':'','lastname':''} - return None - -#------------------------------------------------------------------------------- -# Test XMLRPC API by saying, "Hello!" to client. -#------------------------------------------------------------------------------- -def demo_sayHello(): - return 'Hello!' + for user in User.all(): + if user.isadmin: + return {'email':user.email,'firstname':'','nickname':user.dispname,'userid':str(user.key().id()), + 'url':'','lastname':''} + return None #------------------------------------------------------------------------------- # metaWeblog @@ -171,175 +165,175 @@ def demo_sayHello(): @checkauth() def metaWeblog_newPost(blogid, struct, publish): - if struct.has_key('categories'): - cates = struct['categories'] - else: - cates = [] - - newcates=[] - for cate in cates: - c=Category.all().filter('name =',cate) - if c: - newcates.append(c[0].key()) - entry=Entry(title = struct['title'], - content = struct['description'], - categorie_keys=newcates - ) - - if struct.has_key('mt_text_more'): - content=struct['mt_text_more'] - if content: - entry.content=entry.content+""+struct['mt_text_more'] - if struct.has_key('mt_keywords'): - entry.settags(struct['mt_keywords']) - - if struct.has_key('wp_slug'): - entry.slug=struct['wp_slug'] - - if struct.has_key('mt_excerpt'): - entry.excerpt=struct['mt_excerpt'] - - try: - if struct.has_key('date_created_gmt'): #如果有日期属性 - dt=str(struct['date_created_gmt']) - entry.date=dateformat(dt) - elif struct.has_key('dateCreated'): #如果有日期属性 - dt=str(struct['dateCreated']) - entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) - except: - pass - - if struct.has_key('wp_password'): - entry.password=struct['wp_password'] - - if struct.has_key('sticky'): - entry.sticky=struct['sticky'] - - - if struct.has_key('wp_author_id'): - author=User.get_by_id(int(struct['wp_author_id'])) - entry.author=author.user - entry.author_name=author.dispname - else: - entry.author=g_blog.owner - entry.author_name=g_blog.author - - if publish: - entry.save(True) - - if struct.has_key('mt_tb_ping_urls'): - links=struct['mt_tb_ping_urls'].split(' ') - for url in links: - util.do_trackback(url,entry.title,entry.get_content_excerpt(more='')[:60],entry.fullurl,g_blog.title) - g_blog.tigger_action("xmlrpc_publish_post",entry) - else: - entry.save() - postid =entry.key().id() - return str(postid) + if struct.has_key('categories'): + cates = struct['categories'] + else: + cates = [] + + newcates=[] + for cate in cates: + c=Category.all().filter('name =',cate) + if c: + newcates.append(c[0].key()) + entry=Entry(title = struct['title'], + content = struct['description'], + categorie_keys=newcates + ) + + if struct.has_key('mt_text_more'): + content=struct['mt_text_more'] + if content: + entry.content=entry.content+""+struct['mt_text_more'] + if struct.has_key('mt_keywords'): + entry.settags(struct['mt_keywords']) + + if struct.has_key('wp_slug'): + entry.slug=struct['wp_slug'] + + if struct.has_key('mt_excerpt'): + entry.excerpt=struct['mt_excerpt'] + + try: + if struct.has_key('date_created_gmt'): #如果有日期属性 + dt=str(struct['date_created_gmt']) + entry.date=dateformat(dt) + elif struct.has_key('dateCreated'): #如果有日期属性 + dt=str(struct['dateCreated']) + entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) + except: + pass + + if struct.has_key('wp_password'): + entry.password=struct['wp_password'] + + if struct.has_key('sticky'): + entry.sticky=struct['sticky'] + + + if struct.has_key('wp_author_id'): + author=User.get_by_id(int(struct['wp_author_id'])) + entry.author=author.user + entry.author_name=author.dispname + else: + entry.author=g_blog.owner + entry.author_name=g_blog.author + + if publish: + entry.save(True) + + if struct.has_key('mt_tb_ping_urls'): + links=struct['mt_tb_ping_urls'].split(' ') + for url in links: + util.do_trackback(url,entry.title,entry.get_content_excerpt(more='')[:60],entry.fullurl,g_blog.title) + g_blog.tigger_action("xmlrpc_publish_post",entry) + else: + entry.save() + postid =entry.key().id() + return str(postid) @checkauth() def metaWeblog_newMediaObject(blogid,struct): - name=struct['name'] + name=struct['name'] - if struct.has_key('type'): - mtype=struct['type'] - else: - st=name.split('.') - if len(st)>1: - mtype=st[-1] - else: - mtype=None - bits=db.Blob(str(struct['bits'])) - media=Media(name=name,mtype=mtype,bits=bits) - media.put() + if struct.has_key('type'): + mtype=struct['type'] + else: + st=name.split('.') + if len(st)>1: + mtype=st[-1] + else: + mtype=None + bits=db.Blob(str(struct['bits'])) + media=Media(name=name,mtype=mtype,bits=bits) + media.put() - return {'url':g_blog.baseurl+'/media/'+str(media.key())} + return {'url':g_blog.baseurl+'/media/'+str(media.key())} @checkauth() def metaWeblog_editPost(postid, struct, publish): - if struct.has_key('categories'): - cates = struct['categories'] - else: - cates = [] - newcates=[] - for cate in cates: - c=Category.all().filter('name =',cate).fetch(1) - if c: - newcates.append(c[0].key()) - entry=Entry.get_by_id(int(postid)) - - - if struct.has_key('mt_keywords'): - entry.settags(struct['mt_keywords']) - - if struct.has_key('wp_slug'): - entry.slug=struct['wp_slug'] - if struct.has_key('mt_excerpt'): - entry.excerpt=struct['mt_excerpt'] - - try: - if struct.has_key('date_created_gmt'): #如果有日期属性 - dt=str(struct['date_created_gmt']) - entry.date=dateformat(dt) - elif struct.has_key('dateCreated'): #如果有日期属性 - dt=str(struct['dateCreated']) - entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) - except: - pass - - if struct.has_key('wp_password'): - entry.password=struct['wp_password'] - - if struct.has_key('sticky'): - entry.sticky=struct['sticky'] - - if struct.has_key('wp_author_id'): - author=User.get_by_id(int(struct['wp_author_id'])) - entry.author=author.user - entry.author_name=author.dispname - else: - entry.author=g_blog.owner - entry.author_name=g_blog.author - - entry.title = struct['title'] - entry.content = struct['description'] - if struct.has_key('mt_text_more'): - content=struct['mt_text_more'] - if content: - entry.content=entry.content+""+struct['mt_text_more'] - entry.categorie_keys=newcates - if publish: - entry.save(True) - else: - entry.save() - - return True + if struct.has_key('categories'): + cates = struct['categories'] + else: + cates = [] + newcates=[] + for cate in cates: + c=Category.all().filter('name =',cate).fetch(1) + if c: + newcates.append(c[0].key()) + entry=Entry.get_by_id(int(postid)) + + + if struct.has_key('mt_keywords'): + entry.settags(struct['mt_keywords']) + + if struct.has_key('wp_slug'): + entry.slug=struct['wp_slug'] + if struct.has_key('mt_excerpt'): + entry.excerpt=struct['mt_excerpt'] + + try: + if struct.has_key('date_created_gmt'): #如果有日期属性 + dt=str(struct['date_created_gmt']) + entry.date=dateformat(dt) + elif struct.has_key('dateCreated'): #如果有日期属性 + dt=str(struct['dateCreated']) + entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) + except: + pass + + if struct.has_key('wp_password'): + entry.password=struct['wp_password'] + + if struct.has_key('sticky'): + entry.sticky=struct['sticky'] + + if struct.has_key('wp_author_id'): + author=User.get_by_id(int(struct['wp_author_id'])) + entry.author=author.user + entry.author_name=author.dispname + else: + entry.author=g_blog.owner + entry.author_name=g_blog.author + + entry.title = struct['title'] + entry.content = struct['description'] + if struct.has_key('mt_text_more'): + content=struct['mt_text_more'] + if content: + entry.content=entry.content+""+struct['mt_text_more'] + entry.categorie_keys=newcates + if publish: + entry.save(True) + else: + entry.save() + + return True @checkauth() def metaWeblog_getCategories(blogid): - categories =Category.all() - cates=[] - for cate in categories: - cates.append({ 'categoryDescription':'', - 'categoryId' : str(cate.ID()), - 'parentId':'0', - 'description':cate.name, - 'categoryName':cate.name, - 'htmlUrl':'', - 'rssUrl':'' - }) - return cates + categories =Category.all() + cates=[] + for cate in categories: + cates.append({ 'categoryDescription':'', + 'categoryId' : str(cate.ID()), + 'parentId':'0', + 'description':cate.name, + 'categoryName':cate.name, + 'htmlUrl':'', + 'rssUrl':'' + }) + return cates @checkauth() def metaWeblog_getPost(postid): - entry = Entry.get_by_id(int(postid)) - return post_struct(entry) + entry = Entry.get_by_id(int(postid)) + return post_struct(entry) @checkauth() def metaWeblog_getRecentPosts(blogid, num=20): - entries = Entry.all().filter('entrytype =','post').order('-date').fetch(min(num, MAX_NUM)) - return [post_struct(entry) for entry in entries] + entries = Entry.all().filter('entrytype =','post').order('-date').fetch(min(num, MAX_NUM)) + return [post_struct(entry) for entry in entries] @@ -348,650 +342,656 @@ def metaWeblog_getRecentPosts(blogid, num=20): #------------------------------------------------------------------------------- @checkauth(pos=0) def wp_getUsersBlogs(): - #return [{'url' : g_blog.baseurl, 'blog_id' : 1,'is_admin':True, 'blog_name' : g_blog.title,'xmlrpc_url':g_blog.baseurl+"/xmlrpc.php"}] + #return [{'url' : g_blog.baseurl, 'blog_id' : 1,'is_admin':True, 'blog_name' : g_blog.title,'xmlrpc_url':g_blog.baseurl+"/xmlrpc.php"}] return [{'url' : g_blog.baseurl, 'blogid' : '1','isAdmin':True, 'blogName' : g_blog.title,'xmlrpc':g_blog.baseurl+"/rpc"}] @checkauth() def wp_getTags(blog_id): - def func(blog_id): - for tag in Tag.all(): - yield {'tag_ID':'0','name':tag.tag,'count':str(tag.tagcount),'slug':tag.tag,'html_url':'','rss_url':''} - return list(func(blog_id)) + def func(blog_id): + for tag in Tag.all(): + yield {'tag_ID':'0','name':tag.tag,'count':str(tag.tagcount),'slug':tag.tag,'html_url':'','rss_url':''} + return list(func(blog_id)) @checkauth() def wp_getCommentCount(blog_id,postid): - entry = Entry.get_by_id(postid) - if entry: - return {'approved':entry.commentcount,'awaiting_moderation':0,'spam':0,'total_comments':entry.commentcount} + entry = Entry.get_by_id(postid) + if entry: + return {'approved':entry.commentcount,'awaiting_moderation':0,'spam':0,'total_comments':entry.commentcount} @checkauth() def wp_getPostStatusList(blogid): - return {'draft': 'Draft', - 'pending': 'Pending Review', - 'private': 'Private', - 'publish': 'Published'} + return {'draft': 'Draft', + 'pending': 'Pending Review', + 'private': 'Private', + 'publish': 'Published'} @checkauth() def wp_getPageStatusList(blogid): - return {'draft': 'Draft', 'private': 'Private', 'publish': 'Published'} + return {'draft': 'Draft', 'private': 'Private', 'publish': 'Published'} @checkauth() def wp_getPageTemplates(blogid): - return {} + return {} @checkauth() def wp_setOptions(blogid,options): - for name,value in options,options.values(): - if hasattr(g_blog,name): - setattr(g_blog,name,value) - return options + for name,value in options,options.values(): + if hasattr(g_blog,name): + setattr(g_blog,name,value) + return options @checkauth() def wp_getOptions(blogid,options): - #todo:Options is None ,return all attrbutes - mdict={} - if options: - for option in options: - if hasattr(g_blog,option): - mdict[option]={'desc':option, - 'readonly:':False, - 'value':getattr(g_blog,option)} - return mdict + #todo:Options is None ,return all attrbutes + mdict={} + if options: + for option in options: + if hasattr(g_blog,option): + mdict[option]={'desc':option, + 'readonly:':False, + 'value':getattr(g_blog,option)} + return mdict @checkauth() def wp_newCategory(blogid,struct): - name=struct['name'] + name=struct['name'] - category=Category.all().filter('name =',name).fetch(1) - if category and len(category): - return category[0].ID() - else: - #category=Category(key_name=urlencode(name), name=name,slug=urlencode(name)) - category=Category(name=name,slug=name) - category.put() - return category.ID() + category=Category.all().filter('name =',name).fetch(1) + if category and len(category): + return category[0].ID() + else: + #category=Category(key_name=urlencode(name), name=name,slug=urlencode(name)) + category=Category(name=name,slug=name) + category.put() + return category.ID() @checkauth() def wp_newPage(blogid,struct,publish): - entry=Entry(title = struct['title'], - content = struct['description'], - ) - if struct.has_key('mt_text_more'): - entry.content=entry.content+""+struct['mt_text_more'] - - try: - if struct.has_key('date_created_gmt'): #如果有日期属性 - dt=str(struct['date_created_gmt']) - entry.date=dateformat(dt) - elif struct.has_key('dateCreated'): #如果有日期属性 - dt=str(struct['dateCreated']) - entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) - except: - pass - - if struct.has_key('wp_slug'): - entry.slug=struct['wp_slug'] - if struct.has_key('wp_page_order'): - entry.menu_order=int(struct['wp_page_order']) - if struct.has_key('wp_password'): - entry.password=struct['wp_password'] - - if struct.has_key('wp_author_id'): - author=User.get_by_id(int(struct['wp_author_id'])) - entry.author=author.user - entry.author_name=author.dispname - else: - entry.author=g_blog.owner - entry.author_name=g_blog.author - - entry.entrytype='page' - if publish: - entry.save(True) - else: - entry.save() - - postid =entry.key().id() - return str(postid) + entry=Entry(title = struct['title'], + content = struct['description'], + ) + if struct.has_key('mt_text_more'): + entry.content=entry.content+""+struct['mt_text_more'] + + try: + if struct.has_key('date_created_gmt'): #如果有日期属性 + dt=str(struct['date_created_gmt']) + entry.date=dateformat(dt) + elif struct.has_key('dateCreated'): #如果有日期属性 + dt=str(struct['dateCreated']) + entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) + except: + pass + + if struct.has_key('wp_slug'): + entry.slug=struct['wp_slug'] + if struct.has_key('wp_page_order'): + entry.menu_order=int(struct['wp_page_order']) + if struct.has_key('wp_password'): + entry.password=struct['wp_password'] + + if struct.has_key('wp_author_id'): + author=User.get_by_id(int(struct['wp_author_id'])) + entry.author=author.user + entry.author_name=author.dispname + else: + entry.author=g_blog.owner + entry.author_name=g_blog.author + + entry.entrytype='page' + if publish: + entry.save(True) + else: + entry.save() + + postid =entry.key().id() + return str(postid) @checkauth(2) def wp_getPage(blogid,pageid): - entry = Entry.get_by_id(int(pageid)) - return page_struct(entry) + entry = Entry.get_by_id(int(pageid)) + return page_struct(entry) @checkauth() def wp_getPages(blogid,num=20): - entries = Entry.all().filter('entrytype =','page').order('-date').fetch(min(num, MAX_NUM)) - return [page_struct(entry) for entry in entries] + entries = Entry.all().filter('entrytype =','page').order('-date').fetch(min(num, MAX_NUM)) + return [page_struct(entry) for entry in entries] @checkauth(2) def wp_editPage(blogid,pageid,struct,publish): - entry=Entry.get_by_id(int(pageid)) - - ## if struct.has_key('mt_keywords'): - ## entry.tags=struct['mt_keywords'].split(',') - - if struct.has_key('wp_slug'): - entry.slug=struct['wp_slug'] - - if struct.has_key('wp_page_order'): - entry.menu_order=int(struct['wp_page_order']) - try: - if struct.has_key('date_created_gmt'): #如果有日期属性 - dt=str(struct['date_created_gmt']) - entry.date=dateformat(dt) - elif struct.has_key('dateCreated'): #如果有日期属性 - dt=str(struct['dateCreated']) - entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) - except: - pass - - if struct.has_key('wp_password'): - entry.password=struct['wp_password'] - if struct.has_key('wp_author_id'): - author=User.get_by_id(int(struct['wp_author_id'])) - entry.author=author.user - entry.author_name=author.dispname - else: - entry.author=g_blog.owner - entry.author_name=g_blog.author - entry.title = struct['title'] - entry.content = struct['description'] - if struct.has_key('mt_text_more'): - entry.content=entry.content+""+struct['mt_text_more'] - entry.save(True) - - return True + entry=Entry.get_by_id(int(pageid)) + + ## if struct.has_key('mt_keywords'): + ## entry.tags=struct['mt_keywords'].split(',') + + if struct.has_key('wp_slug'): + entry.slug=struct['wp_slug'] + + if struct.has_key('wp_page_order'): + entry.menu_order=int(struct['wp_page_order']) + try: + if struct.has_key('date_created_gmt'): #如果有日期属性 + dt=str(struct['date_created_gmt']) + entry.date=dateformat(dt) + elif struct.has_key('dateCreated'): #如果有日期属性 + dt=str(struct['dateCreated']) + entry.date=dateformat(dt)-timedelta(seconds=3600*g_blog.timedelta) + except: + pass + + if struct.has_key('wp_password'): + entry.password=struct['wp_password'] + if struct.has_key('wp_author_id'): + author=User.get_by_id(int(struct['wp_author_id'])) + entry.author=author.user + entry.author_name=author.dispname + else: + entry.author=g_blog.owner + entry.author_name=g_blog.author + entry.title = struct['title'] + entry.content = struct['description'] + if struct.has_key('mt_text_more'): + entry.content=entry.content+""+struct['mt_text_more'] + entry.save(True) + + return True @checkauth() def wp_deletePage(blogid,pageid): - post=Entry.get_by_id(int(pageid)) - post.delete() - return True + post=Entry.get_by_id(int(pageid)) + post.delete() + return True @checkauth() def wp_getAuthors(blogid): - ulist=[] - i=1 - for user in User.all(): - ulist.append({'user_id':str(user.key().id()),'user_login':'admin','display_name':user.dispname}) - i=i+1 - return ulist + ulist=[] + i=1 + for user in User.all(): + ulist.append({'user_id':str(user.key().id()),'user_login':'admin','display_name':user.dispname}) + i += 1 + return ulist @checkauth() def wp_deleteComment(blogid,commentid): - try: - comment=Comment.get_by_id(int(commentid)) - if comment: - comment.delit() - return True + try: + comment=Comment.get_by_id(int(commentid)) + if comment: + comment.delit() + return True + + except: + return False - except: - return False @checkauth() def wp_editComment(blogid,commentid,struct): - try: - comment=Comment.get_by_id(int(commentid)) - if comment: - url=struct['author_url'] - if url: - try: - comment.weburl=url - except: - comment.weburl=None - #comment.date= format_date(datetime.now()) - comment.author=struct['author'] - #comment.weburl=struct['author_url'] - comment.email=struct['author_email'] - comment.content=struct['content'] - #comment.status=struct['status'] - comment.save() - return True - except: - raise - return False + try: + comment=Comment.get_by_id(int(commentid)) + if comment: + url=struct['author_url'] + if url: + try: + comment.weburl=url + except: + comment.weburl=None + #comment.date= format_date(datetime.now()) + comment.author=struct['author'] + #comment.weburl=struct['author_url'] + comment.email=struct['author_email'] + comment.content=struct['content'] + #comment.status=struct['status'] + comment.save() + return True + except: + raise + return False @checkauth() def wp_newComment(blogid,postid,struct): - post=Entry.get_by_id(postid) - if not post: - raise Fault(404, "Post does not exist") - comment=Comment(entry=post,content=struct['content'], - author=struct['author'], - email=struct['author_email']) - url=struct['author_url'] - if url: - try: - comment.weburl=url - except: - comment.weburl=None - - comment.save() - return comment.key().id() + post=Entry.get_by_id(postid) + if not post: + raise Fault(404, "Post does not exist") + comment=Comment(entry=post,content=struct['content'], + author=struct['author'], + email=struct['author_email']) + url=struct['author_url'] + if url: + try: + comment.weburl=url + except: + comment.weburl=None + + comment.save() + return comment.key().id() @checkauth() def wp_getCommentStatusList(blogid): - return {'hold':0,'approve':Comment.all().count(),'spam':0} + return {'hold':0,'approve':Comment.all().count(),'spam':0} @checkauth() def wp_getPageList(blogid,num=20): - def func(blogid): - entries = Entry.all().filter('entrytype =','page').order('-date').fetch(min(num, MAX_NUM)) - for entry in entries: - yield {'page_id':str(entry.key().id()),'page_title':entry.title,'page_parent_id':0,'dateCreated': format_date(entry.date),'date_created_gmt': format_date(entry.date)} - return list(func(blogid)) + def func(blogid): + entries = Entry.all().filter('entrytype =','page').order('-date').fetch(min(num, MAX_NUM)) + for entry in entries: + yield {'page_id':str(entry.key().id()),'page_title':entry.title,'page_parent_id':0,'dateCreated': format_date(entry.date),'date_created_gmt': format_date(entry.date)} + return list(func(blogid)) @checkauth() def wp_deleteCategory(blogid,cateid): - try: - cate=Category.get_from_id(int(cateid)) - cate.delete() - return True - except: - return False + try: + cate=Category.get_from_id(int(cateid)) + cate.delete() + return True + except: + return False @checkauth() def wp_suggestCategories(blogid,category,max_result): - categories=Category.all() - cates=[] - for cate in categories: - cates.append({ 'categoryId' : str(cate.ID()), - 'categoryName':cate.name - }) - return cates[:max_result] + categories=Category.all() + cates=[] + for cate in categories: + cates.append({ 'categoryId' : str(cate.ID()), + 'categoryName':cate.name + }) + return cates[:max_result] @checkauth() def wp_getComment(blogid,commentid): - comment=Comment.get_by_id(int(commentid)) - return { - 'dateCreated':format_date(comment.date), - 'date_created_gmt':format_date(comment.date), - 'user_id':'0', - 'comment_id':str(comment.key().id()), - 'parent':'', - 'status':'approve', - 'content':unicode(comment.content), - 'link':comment.entry.link+"#comment-"+str(comment.key().id()), - 'post_id':str(comment.entry.key().id()), - 'post_title':comment.entry.title, - 'author':comment.author, - 'author_url':str(comment.weburl), - 'author_email':str(comment.email), - 'author_ip':comment.ip, - 'type':'' - } + comment=Comment.get_by_id(int(commentid)) + return { + 'dateCreated':format_date(comment.date), + 'date_created_gmt':format_date(comment.date), + 'user_id':'0', + 'comment_id':str(comment.key().id()), + 'parent':'', + 'status':'approve', + 'content':unicode(comment.content), + 'link':comment.entry.link+"#comment-"+str(comment.key().id()), + 'post_id':str(comment.entry.key().id()), + 'post_title':comment.entry.title, + 'author':comment.author, + 'author_url':str(comment.weburl), + 'author_email':str(comment.email), + 'author_ip':comment.ip, + 'type':'' + } @checkauth() def wp_getComments(blogid,data): - def func(blogid,data): - number=int(data['number']) - try: - offset=int(data['offset']) - except: - offset=0 - - comments=[] - - if data['post_id']: - postid=int(data['post_id']) - post=Entry.get_by_id(postid) - if post: - comments=post.comments() - else: - comments=Comment.all() - - for comment in comments.fetch(number,offset): - yield { - 'dateCreated':format_date(comment.date), - 'date_created_gmt':format_date(comment.date), - 'user_id':'0', - 'comment_id':str(comment.key().id()), - 'parent':'', - 'status':'approve', - 'content':unicode(comment.content), - 'link':comment.entry.link+"#comment-"+str(comment.key().id()), - 'post_id':str(comment.entry.key().id()), - 'post_title':comment.entry.title, - 'author':comment.author, - 'author_url':str(comment.weburl), - 'author_email':str(comment.email), - 'author_ip':comment.ip, - 'type':'' - } - return list(func(blogid,data)) + def func(blogid,data): + number=int(data['number']) + try: + offset=int(data['offset']) + except: + offset=0 + + comments=[] + + if data['post_id']: + postid=int(data['post_id']) + post=Entry.get_by_id(postid) + if post: + comments=post.comments() + else: + comments=Comment.all() + + for comment in comments.fetch(number,offset): + yield { + 'dateCreated':format_date(comment.date), + 'date_created_gmt':format_date(comment.date), + 'user_id':'0', + 'comment_id':str(comment.key().id()), + 'parent':'', + 'status':'approve', + 'content':unicode(comment.content), + 'link':comment.entry.link+"#comment-"+str(comment.key().id()), + 'post_id':str(comment.entry.key().id()), + 'post_title':comment.entry.title, + 'author':comment.author, + 'author_url':str(comment.weburl), + 'author_email':str(comment.email), + 'author_ip':comment.ip, + 'type':'' + } + return list(func(blogid,data)) @checkauth() def mt_getPostCategories(postid): - post=Entry.get_by_id(int(postid)) - categories=post.categories - cates=[] - for cate in categories: - #cate=Category(key) - cates.append({'categoryId' : str(cate.ID()), - 'categoryName':cate.name, - 'isPrimary':True - }) - return cates + post=Entry.get_by_id(int(postid)) + categories=post.categories + cates=[] + for cate in categories: + #cate=Category(key) + cates.append({'categoryId' : str(cate.ID()), + 'categoryName':cate.name, + 'isPrimary':True + }) + return cates @checkauth() def mt_getCategoryList(blogid): - categories=Category.all() - cates=[] - for cate in categories: - cates.append({ 'categoryId' : str(cate.ID()), - 'categoryName':cate.name - }) - return cates + categories=Category.all() + cates=[] + for cate in categories: + cates.append({ 'categoryId' : str(cate.ID()), + 'categoryName':cate.name + }) + return cates @checkauth() def mt_setPostCategories(postid,cates): - try: - entry=Entry.get_by_id(int(postid)) - newcates=[] - - for cate in cates: - if cate.has_key('categoryId'): - id=int(cate['categoryId']) - c=Category.get_from_id(int(cate['categoryId'])) - if c: - newcates.append(c.key()) - entry.categorie_keys=newcates - entry.put() - return True - except: - return False + try: + entry=Entry.get_by_id(int(postid)) + newcates=[] + + for cate in cates: + if cate.has_key('categoryId'): + id=int(cate['categoryId']) + c=Category.get_from_id(int(cate['categoryId'])) + if c: + newcates.append(c.key()) + entry.categorie_keys=newcates + entry.put() + return True + except: + return False @checkauth() def mt_getTrackbackPings(self,postid): - try: - entry=Entry.get_by_id(int(postid)) - Tracks=[] - list=Comment.all().filter('entry =',entry).filter('ctype =',1) + try: + entry=Entry.get_by_id(int(postid)) + Tracks=[] + list=Comment.all().filter('entry =',entry).filter('ctype =',1) - for track in list: - Tracks.append({'pingIP':track.ip,'pingURL':track.weburl,'pingTitle':track.author}) - return Tracks - except: - return False + for track in list: + Tracks.append({'pingIP':track.ip,'pingURL':track.weburl,'pingTitle':track.author}) + return Tracks + except: + return False @checkauth() def mt_publishPost(postid): - try: - entry=Entry.get_by_id(int(postid)) - entry.save(True) - return entry.key().id() - except: - return 0 + try: + entry=Entry.get_by_id(int(postid)) + entry.save(True) + return entry.key().id() + except: + return 0 @checkauth() def mt_getRecentPostTitles(blogid,num): - entries = Entry.all().filter('entrytype =','post').order('-date').fetch(min(num, MAX_NUM)) - return [entry_title_struct(entry) for entry in entries] + entries = Entry.all().filter('entrytype =','post').order('-date').fetch(min(num, MAX_NUM)) + return [entry_title_struct(entry) for entry in entries] #------------------------------------------------------------------------------ #pingback #------------------------------------------------------------------------------ def pingback_extensions_getPingbacks(self,url): - from urlparse import urlparse - param=urlparse(url) - slug=param[2] - slug=urldecode(slug) - try: - entry = Entry.all().filter("published =", True).filter('link =', slug).fetch(1) - pings=[] - list=Comment.all().filter('entry =',entry).filter('ctype =',2) - - for ping in list: - pings.append(ping.weburl) - return pings - except: - return False + from urlparse import urlparse + param=urlparse(url) + slug=param[2] + slug=urldecode(slug) + try: + entry = Entry.all().filter("published =", True).filter('link =', slug).fetch(1) + pings=[] + list=Comment.all().filter('entry =',entry).filter('ctype =',2) + + for ping in list: + pings.append(ping.weburl) + return pings + except: + return False _title_re = re.compile(r'(.*?)(?i)') _pingback_re = re.compile(r'(?i)') _chunk_re = re.compile(r'\n\n|<(?:p|div|h\d)[^>]*>') def fetch_result(source_uri): - for RETRY in range(5): - rpc = urlfetch.create_rpc() - urlfetch.make_fetch_call(rpc, source_uri) - try: - response = rpc.get_result() - return response - except urlfetch.DownloadError: - logging.info('Download Error, Retry %s times'%RETRY) - continue - except: - raise Fault(16, 'The source URL does not exist.%s'%source_uri) - else: - logging.info('Times Over') - raise Fault(16, 'The source URL does not exist.%s'%source_uri) + for RETRY in range(5): + rpc = urlfetch.create_rpc() + urlfetch.make_fetch_call(rpc, source_uri) + try: + response = rpc.get_result() + return response + except urlfetch.DownloadError: + logging.info('Download Error, Retry %s times'%RETRY) + continue + except: + raise Fault(16, 'The source URL does not exist.%s'%source_uri) + else: + logging.info('Times Over') + raise Fault(16, 'The source URL does not exist.%s'%source_uri) + def pingback_ping(source_uri, target_uri): - # next we check if the source URL does indeed exist - if not g_blog.allow_pingback: - raise Fault(49,"Access denied.") - try: - - g_blog.tigger_action("pre_ping",source_uri,target_uri) - response = fetch_result(source_uri) - logging.info('source_uri: '+source_uri+' target_uri:'+target_uri) - except Exception ,e : - #logging.info(e.message) - logging.info('The source URL does not exist.%s'%source_uri) - raise Fault(16, 'The source URL does not exist.%s'%source_uri) - # we only accept pingbacks for links below our blog URL - blog_url = g_blog.baseurl - if not blog_url.endswith('/'): - blog_url += '/' - if not target_uri.startswith(blog_url): - raise Fault(32, 'The specified target URL does not exist.') - path_info = target_uri[len(blog_url):] - - pingback_post(response,source_uri,target_uri,path_info) - try: - logging.info('Micolog pingback succeed!') - return "Micolog pingback succeed!" - except: - raise Fault(49,"Access denied.") + # next we check if the source URL does indeed exist + if not g_blog.allow_pingback: + raise Fault(49,"Access denied.") + try: + + g_blog.tigger_action("pre_ping",source_uri,target_uri) + response = fetch_result(source_uri) + logging.info('source_uri: '+source_uri+' target_uri:'+target_uri) + except Exception ,e : + #logging.info(e.message) + logging.info('The source URL does not exist.%s'%source_uri) + raise Fault(16, 'The source URL does not exist.%s'%source_uri) + # we only accept pingbacks for links below our blog URL + blog_url = g_blog.baseurl + if not blog_url.endswith('/'): + blog_url += '/' + if not target_uri.startswith(blog_url): + raise Fault(32, 'The specified target URL does not exist.') + path_info = target_uri[len(blog_url):] + if path_info.startswith('?'): + try: postid = path_info.split('&')[0].split('=')[1] + except: postid=None + pingback_post(response,source_uri,target_uri,postid=postid) + else: + path = path_info.split('?')[0] + pingback_post(response,source_uri,target_uri,slug=path) + + #pingback_post(response,source_uri,target_uri,path_info) + try: + logging.info('Micolog pingback succeed!') + return "Micolog pingback succeed!" + except: + raise Fault(49,"Access denied.") def get_excerpt(response, url_hint, body_limit=1024 * 512): - """Get an excerpt from the given `response`. `url_hint` is the URL - which will be used as anchor for the excerpt. The return value is a - tuple in the form ``(title, body)``. If one of the two items could - not be calculated it will be `None`. - """ - contents = response.content[:body_limit] - if 'charset=gb2312' in contents[:200].lower(): - contents = contents.decode('gb2312').encode('UTF-8') - elif 'charset=gbk"' in contents[:200].lower(): - contents = contents.decode('GBK').encode('UTF-8') - try: - contents=contents.decode('utf-8') - except: - pass - - title_match = _title_re.search(contents) - title = title_match and strip_tags(title_match.group(1)) or None - - link_re = re.compile(r']+?"\s*%s\s*"[^>]*>(.*?)(?is)' % - re.escape(url_hint)) - for chunk in _chunk_re.split(contents): - match = link_re.search(chunk) - if not match: - continue - before = chunk[:match.start()] - after = chunk[match.end():] - raw_body = '%s\0%s' % (strip_tags(before).replace('\0', ''), - strip_tags(after).replace('\0', '')) - raw_body=raw_body.replace('\n', '').replace('\t', '') - body_match = re.compile(r'(?:^|\b)(.{0,120})\0(.{0,120})(?:\b|$)') \ - .search(raw_body) - if body_match: - break - else: - return title, None - - - before, after = body_match.groups() - link_text = strip_tags(match.group(1)) - if len(link_text) > 60: - link_text = link_text[:60] + u' …' - - bits = before.split() - bits.append(link_text) - bits.extend(after.split()) - return title, u'[…] %s […]' % u' '.join(bits) - -def pingback_post(response,source_uri, target_uri, slug): - """This is the pingback handler for posts.""" - entry = Entry.all().filter("published =", True).filter('link =', slug).get() - #use allow_trackback as allow_pingback - if entry is None or not entry.allow_trackback: - raise Fault(33, 'no such post') - title, excerpt = get_excerpt(response, target_uri) - if not title: - raise Fault(17, 'no title provided') - elif not excerpt: - raise Fault(17, 'no useable link to target') - - comment = Comment.all().filter("entry =", entry).filter("weburl =", source_uri).get() - if comment: - raise Fault(48, 'pingback has already been registered') - return - - comment=Comment(author=title[:30], - content=""+title[:250]+"...
" + - excerpt[:250] + '...', - weburl=source_uri, - entry=entry) - comment.ctype=COMMENT_PINGBACK - try: - comment.save() - g_blog.tigger_action("pingback_post",comment) - logging.info("PingBack Successfully Added ! From %s"%source_uri) - memcache.delete("/"+entry.link) - return True - except: - raise Fault(49,"Access denied.") - return + """Get an excerpt from the given `response`. `url_hint` is the URL + which will be used as anchor for the excerpt. The return value is a + tuple in the form ``(title, body)``. If one of the two items could + not be calculated it will be `None`. + """ + contents = response.content[:body_limit] + if 'charset=gb2312' in contents[:400].lower(): + contents = contents.decode('gb2312').encode('UTF-8') + elif 'charset=gbk"' in contents[:400].lower(): + contents = contents.decode('GBK').encode('UTF-8') + elif 'charset=big5' in contents[:400].lower(): + contents = contents.decode('big5') + try: + contents=contents.decode('utf-8') + except: + pass + + title_match = _title_re.search(contents) + title = title_match and strip_tags(title_match.group(1)) or None + + link_re = re.compile(r']+?"\s*%s\s*"[^>]*>(.*?)(?is)' % + re.escape(url_hint)) + for chunk in _chunk_re.split(contents): + match = link_re.search(chunk) + if not match: + continue + before = chunk[:match.start()] + after = chunk[match.end():] + raw_body = '%s\0%s' % (strip_tags(before).replace('\0', ''), + strip_tags(after).replace('\0', '')) + raw_body=raw_body.replace('\n', '').replace('\t', '') + body_match = re.compile(r'(?:^|\b)(.{0,120})\0(.{0,120})(?:\b|$)') \ + .search(raw_body) + if body_match: + break + else: + return title, None + + + before, after = body_match.groups() + link_text = strip_tags(match.group(1)) + if len(link_text) > 60: + link_text = link_text[:60] + u' …' + + bits = before.split() + bits.append(link_text) + bits.extend(after.split()) + return title, u'[…] %s […]' % u' '.join(bits) + +def pingback_post(response,source_uri, target_uri, slug=None, postid=None): + """This is the pingback handler for posts.""" + if slug: + entry = Entry.all().filter("published =", True).filter('link =', slug).get() + else: + entry = Entry.all().filter("published =", True).filter('post_id =', postid).get() + #use allow_trackback as allow_pingback + if entry is None or not entry.allow_trackback: + raise Fault(33, 'no such post') + title, excerpt = get_excerpt(response, target_uri) + if not title: + raise Fault(17, 'no title provided') + elif not excerpt: + raise Fault(17, 'no useable link to target') + + comment = Comment.all().filter("entry =", entry).filter("weburl =", source_uri).get() + if comment: + raise Fault(48, 'pingback has already been registered') + return + + comment=Comment(author=title[:30], + content=""+title[:250]+"...
" + + excerpt[:250] + '...', + weburl=source_uri, + entry=entry) + comment.ctype=COMMENT_PINGBACK + try: + comment.save() + g_blog.tigger_action("pingback_post",comment) + logging.info("PingBack Successfully Added ! From %s"%source_uri) + memcache.delete("/"+entry.link) + return True + except: + raise Fault(49,"Access denied.") + return ##------------------------------------------------------------------------------ class PlogXMLRPCDispatcher(SimpleXMLRPCDispatcher): - def __init__(self, funcs): - SimpleXMLRPCDispatcher.__init__(self, True, 'utf-8') - self.funcs = funcs - self.register_introspection_functions() + def __init__(self, funcs): + SimpleXMLRPCDispatcher.__init__(self, True, 'utf-8') + self.funcs = funcs + self.register_introspection_functions() dispatcher = PlogXMLRPCDispatcher({ - 'demo.sayHello':demo_sayHello, - 'blogger.getUsersBlogs' : blogger_getUsersBlogs, - 'blogger.deletePost' : blogger_deletePost, - 'blogger.getUserInfo': blogger_getUserInfo, - - 'metaWeblog.newPost' : metaWeblog_newPost, - 'metaWeblog.editPost' : metaWeblog_editPost, - 'metaWeblog.getCategories' : metaWeblog_getCategories, - 'metaWeblog.getPost' : metaWeblog_getPost, - 'metaWeblog.getRecentPosts' : metaWeblog_getRecentPosts, - 'metaWeblog.newMediaObject':metaWeblog_newMediaObject, - - 'wp.getUsersBlogs':wp_getUsersBlogs, - 'wp.getTags':wp_getTags, - 'wp.getCommentCount':wp_getCommentCount, - 'wp.getPostStatusList':wp_getPostStatusList, - 'wp.getPageStatusList':wp_getPageStatusList, - 'wp.getPageTemplates':wp_getPageTemplates, - 'wp.getOptions':wp_getOptions, - 'wp.setOptions':wp_setOptions, - 'wp.getCategories':metaWeblog_getCategories, - 'wp.newCategory':wp_newCategory, - 'wp.newPage':wp_newPage, - 'wp.getPage':wp_getPage, - 'wp.getPages':wp_getPages, - 'wp.editPage':wp_editPage, - 'wp.getPageList':wp_getPageList, - 'wp.deletePage':wp_deletePage, - 'wp.getAuthors':wp_getAuthors, - 'wp.deleteComment':wp_deleteComment, - 'wp.editComment':wp_editComment, - 'wp.newComment':wp_newComment, - 'wp.getCommentStatusList':wp_getCommentStatusList, - 'wp.deleteCategory':wp_deleteCategory, - 'wp.suggestCategories':wp_suggestCategories, - 'wp.getComment':wp_getComment, - 'wp.getComments':wp_getComments, - 'wp.uploadFile':metaWeblog_newMediaObject, - - 'mt.setPostCategories':mt_setPostCategories, - 'mt.getPostCategories':mt_getPostCategories, - 'mt.getCategoryList':mt_getCategoryList, - 'mt.publishPost':mt_publishPost, - 'mt.getRecentPostTitles':mt_getRecentPostTitles, - 'mt.getTrackbackPings':mt_getTrackbackPings, - - ##pingback - 'pingback.ping':pingback_ping, - 'pingback.extensions.getPingbacks':pingback_extensions_getPingbacks, - - - - }) + 'blogger.getUsersBlogs' : blogger_getUsersBlogs, + 'blogger.deletePost' : blogger_deletePost, + 'blogger.getUserInfo': blogger_getUserInfo, + + 'metaWeblog.newPost' : metaWeblog_newPost, + 'metaWeblog.editPost' : metaWeblog_editPost, + 'metaWeblog.getCategories' : metaWeblog_getCategories, + 'metaWeblog.getPost' : metaWeblog_getPost, + 'metaWeblog.getRecentPosts' : metaWeblog_getRecentPosts, + 'metaWeblog.newMediaObject':metaWeblog_newMediaObject, + + 'wp.getUsersBlogs':wp_getUsersBlogs, + 'wp.getTags':wp_getTags, + 'wp.getCommentCount':wp_getCommentCount, + 'wp.getPostStatusList':wp_getPostStatusList, + 'wp.getPageStatusList':wp_getPageStatusList, + 'wp.getPageTemplates':wp_getPageTemplates, + 'wp.getOptions':wp_getOptions, + 'wp.setOptions':wp_setOptions, + 'wp.getCategories':metaWeblog_getCategories, + 'wp.newCategory':wp_newCategory, + 'wp.newPage':wp_newPage, + 'wp.getPage':wp_getPage, + 'wp.getPages':wp_getPages, + 'wp.editPage':wp_editPage, + 'wp.getPageList':wp_getPageList, + 'wp.deletePage':wp_deletePage, + 'wp.getAuthors':wp_getAuthors, + 'wp.deleteComment':wp_deleteComment, + 'wp.editComment':wp_editComment, + 'wp.newComment':wp_newComment, + 'wp.getCommentStatusList':wp_getCommentStatusList, + 'wp.deleteCategory':wp_deleteCategory, + 'wp.suggestCategories':wp_suggestCategories, + 'wp.getComment':wp_getComment, + 'wp.getComments':wp_getComments, + 'wp.uploadFile':metaWeblog_newMediaObject, + + 'mt.setPostCategories':mt_setPostCategories, + 'mt.getPostCategories':mt_getPostCategories, + 'mt.getCategoryList':mt_getCategoryList, + 'mt.publishPost':mt_publishPost, + 'mt.getRecentPostTitles':mt_getRecentPostTitles, + 'mt.getTrackbackPings':mt_getTrackbackPings, + + ##pingback + 'pingback.ping':pingback_ping, + 'pingback.extensions.getPingbacks':pingback_extensions_getPingbacks, + }) # {{{ Handlers class CallApi(BaseRequestHandler): - def get(self): - Logger(request = self.request.uri, response = self.request.remote_addr+'----------------------------------').put() - self.write('

please use POST

') + def get(self): + Logger(request = self.request.uri, response = self.request.remote_addr+'----------------------------------').put() + self.write('

please use POST

') - def post(self): - #self.response.headers['Content-Type'] = 'application/xml; charset=utf-8' - request = self.request.body - response = dispatcher._marshaled_dispatch(request) - Logger(request = unicode(request, 'utf-8'), response = unicode(response, 'utf-8')).put() - self.write(response) + def post(self): + #self.response.headers['Content-Type'] = 'application/xml; charset=utf-8' + request = self.request.body + response = dispatcher._marshaled_dispatch(request) + Logger(request = unicode(request, 'utf-8'), response = unicode(response, 'utf-8')).put() + self.write(response) class View(BaseRequestHandler): - @requires_admin - def get(self): - self.write('

Logger

') - for log in Logger.all().order('-date').fetch(5,0): - self.write("

date: %s

" % log.date) - self.write("

Request

") - self.write('
%s
' % cgi.escape(log.request)) - self.write("

Reponse

") - self.write('
%s
' % cgi.escape(log.response)) - self.write("
") - self.write('') + @requires_admin + def get(self): + self.write('

Logger

') + for log in Logger.all().order('-date').fetch(5,0): + self.write("

date: %s

" % log.date) + self.write("

Request

") + self.write('
%s
' % cgi.escape(log.request)) + self.write("

Reponse

") + self.write('
%s
' % cgi.escape(log.response)) + self.write("
") + self.write('') class DeleteLog(BaseRequestHandler): - def get(self): - if self.chk_admin(): - for log in Logger.all(): - log.delete() - - - self.redirect('/rpc/view') + def get(self): + if self.chk_admin(): + for log in Logger.all(): + log.delete() + self.redirect('/rpc/view') #}}} - def main(): - #webapp.template.register_template_library("filter") - application = webapp.WSGIApplication( - [ - ('/rpc', CallApi), - ('/xmlrpc\.php',CallApi), - ('/rpc/view', View), - ('/rpc/dellog', DeleteLog), - - ], - debug=True) - wsgiref.handlers.CGIHandler().run(application) + #webapp.template.register_template_library("filter") + application = webapp.WSGIApplication( + [ + ('/rpc', CallApi), + ('/xmlrpc\.php',CallApi), + ('/rpc/view', View), + ('/rpc/dellog', DeleteLog), + ], + debug=True) + wsgiref.handlers.CGIHandler().run(application) if __name__ == '__main__': - main() + main() diff --git a/filter.py b/app/filter.py similarity index 86% rename from filter.py rename to app/filter.py index 58759ac..7a217c1 100644 --- a/filter.py +++ b/app/filter.py @@ -1,100 +1,104 @@ -# -*- coding: utf-8 -*- -import logging -from django import template -from model import * -import django.template.defaultfilters as defaultfilters -import urllib -register = template.Library() -from datetime import * - -@register.filter -def datetz(date,format): #datetime with timedelta - t=timedelta(seconds=3600*g_blog.timedelta) - return defaultfilters.date(date+t,format) - -@register.filter -def TimestampISO8601(t): - """Seconds since epoch (1970-01-01) --> ISO 8601 time string.""" - return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(t)) - -@register.filter -def urlencode(value): - return urllib.quote(value.encode('utf8')) - -@register.filter -def check_current(v1,v2): - if v1==v2: - return "current" - else: - return "" - -@register.filter -def excerpt_more(entry,value='..more'): - return entry.get_content_excerpt(value.decode('utf8')) - -@register.filter -def dict_value(v1,v2): - return v1[v2] - - -from app.html_filter import html_filter - -plog_filter = html_filter() -plog_filter.allowed = { - 'a': ('href', 'target', 'name'), - 'b': (), - 'blockquote': (), - 'pre': (), - 'em': (), - 'i': (), - 'img': ('src', 'width', 'height', 'alt', 'title'), - 'strong': (), - 'u': (), - 'font': ('color', 'size'), - 'p': (), - 'h1': (), - 'h2': (), - 'h3': (), - 'h4': (), - 'h5': (), - 'h6': (), - 'table': (), - 'tr': (), - 'th': (), - 'td': (), - 'ul': (), - 'ol': (), - 'li': (), - 'br': (), - 'hr': (), - } - -plog_filter.no_close += ('br',) -plog_filter.allowed_entities += ('nbsp','ldquo', 'rdquo', 'hellip',) -plog_filter.make_clickable_urls = False # enable this will get a bug about a and img - -@register.filter -def do_filter(data): - return plog_filter.go(data) - -''' -tag like {%mf header%}xxx xxx{%endmf%} -''' -@register.tag("mf") -def do_mf(parser, token): - nodelist = parser.parse(('endmf',)) - parser.delete_first_token() - return MfNode(nodelist,token) - -class MfNode(template.Node): - def __init__(self, nodelist,token): - self.nodelist = nodelist - self.token=token - - def render(self, context): - tokens= self.token.split_contents() - if len(tokens)<2: - raise TemplateSyntaxError, "'mf' tag takes one argument: the filter name is needed" - fname=tokens[1] - output = self.nodelist.render(context) +# -*- coding: utf-8 -*- +import os +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') +from django import template +from model import * +import django.template.defaultfilters as defaultfilters +import urllib +register = template.Library() +from datetime import * + +@register.filter +def datetz(date,format): #datetime with timedelta + t=timedelta(seconds=3600*g_blog.timedelta) + return defaultfilters.date(date+t,format) + +@register.filter +def TimestampISO8601(t): + """Seconds since epoch (1970-01-01) --> ISO 8601 time string.""" + return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(t)) + +@register.filter +def urlencode(value): + return urllib.quote(value.encode('utf8')) + +@register.filter +def check_current(v1,v2): + if v1==v2: + return "current" + else: + return "" + +@register.filter +def excerpt_more(entry,value='..more'): + return entry.get_content_excerpt(value.decode('utf8')) + +@register.filter +def dict_value(v1,v2): + return v1[v2] + + +import app.html_filter + +plog_filter = app.html_filter.html_filter() +plog_filter.allowed = { + 'a': ('href', 'target', 'name', 'rel'), + 'b': (), + 'blockquote': (), + 'pre': (), + 'em': (), + 'i': (), + 'img': ('src', 'width', 'height', 'alt', 'title'), + 'strong': (), + 'u': (), + 'font': ('color', 'size'), + 'p': (), + 'h1': (), + 'h2': (), + 'h3': (), + 'h4': (), + 'h5': (), + 'h6': (), + 'table': (), + 'tr': (), + 'th': (), + 'td': (), + 'ul': (), + 'ol': (), + 'li': (), + 'br': (), + 'hr': (), + 'code':(), + } + +plog_filter.no_close += ('br',) +plog_filter.allowed_entities += ('nbsp','ldquo', 'rdquo', 'hellip',) +plog_filter.make_clickable_urls = False # enable this will get a bug about a and img + +@register.filter +def do_filter(data): + return plog_filter.go(data) + +''' +tag like {%mf header%}xxx xxx{%endmf%} +''' +@register.tag("mf") +def do_mf(parser, token): + nodelist = parser.parse(('endmf',)) + parser.delete_first_token() + return MfNode(nodelist,token) + +class MfNode(template.Node): + def __init__(self, nodelist,token): + self.nodelist = nodelist + self.token=token + + def render(self, context): + tokens= self.token.split_contents() + if len(tokens)<2: + raise TemplateSyntaxError, "'mf' tag takes one argument: the filter name is needed" + fname=tokens[1] + output = self.nodelist.render(context) return g_blog.tigger_filter(fname,output) \ No newline at end of file diff --git a/app/gbtools.py b/app/gbtools.py index 72a7951..1c59cba 100644 --- a/app/gbtools.py +++ b/app/gbtools.py @@ -72,14 +72,14 @@ def string2List(ustring): utmp=[] for uchar in ustring: if is_other(uchar): - if len(utmp)==0: + if not len(utmp): continue else: retList.append("".join(utmp)) utmp=[] else: utmp.append(uchar) - if len(utmp)!=0: + if len(utmp): retList.append("".join(utmp)) return retList diff --git a/app/gmemsess.py b/app/gmemsess.py index 54d01cd..29fc1a2 100644 --- a/app/gmemsess.py +++ b/app/gmemsess.py @@ -46,7 +46,7 @@ def __init__(self,rh,name=_defaultCookieName,timeout=_defaultTimeout): if name in rh.request.str_cookies: self._sid=rh.request.str_cookies[name] data=memcache.get(self._sid) - if data!=None: + if data is not None: self.update(data) # memcache timeout is absolute, so we need to reset it on each access memcache.set(self._sid,data,self._timeout) @@ -82,7 +82,7 @@ def get_id(self): def invalidate(self): """Delete session data and cookie.""" self.rh.response.headers.add_header('Set-Cookie', - '%s=; expires=Sat, 1-Jan-2000 00:00:00 GMT;'%(self._name)) + '%s=; expires=Sat, 1-Jan-2000 00:00:00 GMT;'%self._name) memcache.delete(self._sid) self.clear() self._invalid=True diff --git a/app/pingback.py b/app/pingback.py index bf0b027..c2f8e48 100644 --- a/app/pingback.py +++ b/app/pingback.py @@ -122,10 +122,9 @@ def autoPingback(sourceURI, reST = None, HTML = None): The following specification details how this code should work: http://www.hixie.ch/specs/pingback/pingback""" - assert reST != None or HTML != None + assert reST is not None or HTML is not None - - if reST != None: + if reST is not None: links = reSTLinks(reST) else: links = htmlLinks(HTML) diff --git a/app/recurse.py b/app/recurse.py index 4995f0f..2e62809 100755 --- a/app/recurse.py +++ b/app/recurse.py @@ -1,6 +1,10 @@ +import os +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') from django.template import Library -from django.template import Node, NodeList, Template, Context -from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END +from django.template import Node, NodeList +from django.template import TemplateSyntaxError, VariableDoesNotExist register = Library() diff --git a/app/safecode.py b/app/safecode.py index ffaa01b..c282b65 100644 --- a/app/safecode.py +++ b/app/safecode.py @@ -10,7 +10,7 @@ # Description: 负责验证码生成。 # Modify Date: 2008-08-06 -import md5 +import hashlib import random from pngcanvas import PNGCanvas diff --git a/app/trackback.py b/app/trackback.py index 1b1c893..8caa3a5 100644 --- a/app/trackback.py +++ b/app/trackback.py @@ -13,14 +13,16 @@ 0.0.2: 1/21/03 - First working version. 0.0.1: 1/21/03 - Initial version. Thanks to Mark Pilgrim for helping me figure some module basics out. """ -import httplib, urllib, urlparse, re +import urllib, re from google.appengine.api import urlfetch import logging -"""Everything I needed to know about trackback I learned from the trackback tech specs page -http://www.movabletype.org/docs/mttrackback.html. All arguments are optional. This allows us to create an empty TrackBack object, -then use autodiscovery to populate its attributes. -""" + class TrackBack: + """ + Everything I needed to know about trackback I learned from the trackback tech specs page + http://www.movabletype.org/docs/mttrackback.html. All arguments are optional. This allows us to create an empty TrackBack object, + then use autodiscovery to populate its attributes. + """ def __init__(self, tbUrl=None, title=None, excerpt=None, url=None, blog_name=None): self.tbUrl = tbUrl diff --git a/appengine_console.py b/appengine_console.py deleted file mode 100644 index 45c4c4f..0000000 --- a/appengine_console.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python - -# Code copied from http://code.google.com/appengine/articles/remote_api.html -# with minor modifications. - -import code -import getpass -import os -import sys - -DIR_PATH = "/root/dev/google_appengine" -sys.path.append(os.path.join(os.path.dirname(__file__), '..\\')) - -SCRIPT_DIR = os.path.join(DIR_PATH, 'google', 'appengine', 'tools') - -EXTRA_PATHS = [ - DIR_PATH, - os.path.join(DIR_PATH, 'lib', 'antlr3'), - os.path.join(DIR_PATH, 'lib', 'django'), - os.path.join(DIR_PATH, 'lib', 'webob'), - os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'), -] -sys.path = EXTRA_PATHS + sys.path -from google.appengine.ext.remote_api import remote_api_stub -from google.appengine.ext import db - -def auth_func(): - return raw_input('Username:'), getpass.getpass('Password:') - -if len(sys.argv) < 2: - print "Usage: %s app_id [host]" % (sys.argv[0],) -app_id = sys.argv[1] -if len(sys.argv) > 2: - host = sys.argv[2] -else: - host = '%s.appspot.com' % app_id -os.environ['APPLICATION_ID']=app_id - -from google.appengine.api import apiproxy_stub_map -from google.appengine.api import urlfetch_stub -apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() -apiproxy_stub_map.apiproxy.RegisterStub('urlfetch',urlfetch_stub.URLFetchServiceStub()) - -from google.appengine.api import datastore_file_stub -from google.appengine.api import mail_stub -#from google3.apphosting.api import user_service_stub - -#apiproxy_stub_map.apiproxy.RegisterStub('user',user_service_stub.UserServiceStub()) -apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', datastore_file_stub.DatastoreFileStub(app_id, '/tmp/dev_appserver.datastore', '/dev/null')) -apiproxy_stub_map.apiproxy.RegisterStub('mail',mail_stub.MailServiceStub()) - - -#remote_api_stub.ConfigureRemoteDatastore(app_id, '/remote_api', auth_func, host) -from model import * -code.interact('App Engine interactive console for %s' % (app_id,), None, locals()) diff --git a/base.py b/base.py index 6d3607d..ecc0fad 100644 --- a/base.py +++ b/base.py @@ -1,25 +1,22 @@ # -*- coding: utf-8 -*- import os,logging -import re -from functools import wraps +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') +import functools from google.appengine.api import users from google.appengine.ext import webapp -from google.appengine.ext import db from google.appengine.ext.webapp import template from google.appengine.api import memcache -from google.appengine.api import urlfetch ##import app.webapp as webapp2 -os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' -from django.utils.translation import activate from django.template import TemplateDoesNotExist from django.conf import settings settings._target = None #from model import g_blog,User #activate(g_blog.language) -from google.appengine.api.labs import taskqueue -import wsgiref.handlers +from google.appengine.api import taskqueue from mimetypes import types_map -from datetime import datetime, timedelta +from datetime import datetime import urllib import traceback import micolog_template @@ -27,164 +24,164 @@ logging.info('module base reloaded') def urldecode(value): - return urllib.unquote(urllib.unquote(value)).decode('utf8') + return urllib.unquote(urllib.unquote(value)).decode('utf8') #return urllib.unquote(value).decode('utf8') def urlencode(value): - return urllib.quote(value.encode('utf8')) + return urllib.quote(value.encode('utf8')) def sid(): - now=datetime.datetime.now() - return now.strftime('%y%m%d%H%M%S')+str(now.microsecond) + now=datetime.datetime.now() + return now.strftime('%y%m%d%H%M%S')+str(now.microsecond) def requires_admin(method): - @wraps(method) - def wrapper(self, *args, **kwargs): - if not self.is_login: - self.redirect(users.create_login_url(self.request.uri)) - return - elif not (self.is_admin - or self.author): - return self.error(403) - else: - return method(self, *args, **kwargs) - return wrapper + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + if not self.is_login: + self.redirect(users.create_login_url(self.request.uri)) + return + elif not (self.is_admin + or self.author): + return self.error(403) + else: + return method(self, *args, **kwargs) + return wrapper def printinfo(method): - @wraps(method) - def wrapper(self, *args, **kwargs): - print self #.__name__ - print dir(self) - for x in self.__dict__: - print x - return method(self, *args, **kwargs) - return wrapper + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + print self #.__name__ + print dir(self) + for x in self.__dict__: + print x + return method(self, *args, **kwargs) + return wrapper #only ajax methed allowed def ajaxonly(method): - @wraps(method) - def wrapper(self, *args, **kwargs): - if not self.request.headers["X-Requested-With"]=="XMLHttpRequest": - self.error(404) - else: - return method(self, *args, **kwargs) - return wrapper + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + if not self.request.headers["X-Requested-With"]=="XMLHttpRequest": + self.error(404) + else: + return method(self, *args, **kwargs) + return wrapper #only request from same host can passed def hostonly(method): - @wraps(method) - def wrapper(self, *args, **kwargs): - if self.request.headers['Referer'].startswith(os.environ['HTTP_HOST'],7): - return method(self, *args, **kwargs) - else: - self.error(404) - return wrapper + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + if self.request.headers['Referer'].startswith(os.environ['HTTP_HOST'],7): + return method(self, *args, **kwargs) + else: + self.error(404) + return wrapper def format_date(dt): - return dt.strftime('%a, %d %b %Y %H:%M:%S GMT') + return dt.strftime('%a, %d %b %Y %H:%M:%S GMT') def cache(key="",time=3600): - def _decorate(method): - def _wrapper(*args, **kwargs): - from model import g_blog - if not g_blog.enable_memcache: - method(*args, **kwargs) - return - - request=args[0].request - response=args[0].response - skey=key+ request.path_qs - #logging.info('skey:'+skey) - html= memcache.get(skey) - #arg[0] is BaseRequestHandler object - - if html: - logging.info('cache:'+skey) - response.last_modified =html[1] - ilen=len(html) - if ilen>=3: - response.set_status(html[2]) - if ilen>=4: - for skey,value in html[3].items(): - response.headers[skey]=value - response.out.write(html[0]) - else: - if 'last-modified' not in response.headers: - response.last_modified = format_date(datetime.utcnow()) - - method(*args, **kwargs) - result=response.out.getvalue() - status_code = response._Response__status[0] - logging.debug("Cache:%s"%status_code) - memcache.set(skey,(result,response.last_modified,status_code,response.headers),time) - - return _wrapper - return _decorate + def _decorate(method): + def _wrapper(*args, **kwargs): + from model import g_blog + if not g_blog.enable_memcache: + method(*args, **kwargs) + return + + request=args[0].request + response=args[0].response + skey=key+ request.path_qs + #logging.info('skey:'+skey) + html= memcache.get(skey) + #arg[0] is BaseRequestHandler object + + if html: + logging.info('cache:'+skey) + response.last_modified =html[1] + ilen=len(html) + if ilen>=3: + response.set_status(html[2]) + if ilen>=4: + for skey,value in html[3].items(): + response.headers[skey]=value + response.out.write(html[0]) + else: + if 'last-modified' not in response.headers: + response.last_modified = format_date(datetime.utcnow()) + + method(*args, **kwargs) + result=response.out.getvalue() + status_code = response._Response__status[0] + logging.debug("Cache:%s"%status_code) + memcache.set(skey,(result,response.last_modified,status_code,response.headers),time) + + return _wrapper + return _decorate #------------------------------------------------------------------------------- class PingbackError(Exception): - """Raised if the remote server caused an exception while pingbacking. - This is not raised if the pingback function is unable to locate a - remote server. - """ - - _ = lambda x: x - default_messages = { - 16: _(u'source URL does not exist'), - 17: _(u'The source URL does not contain a link to the target URL'), - 32: _(u'The specified target URL does not exist'), - 33: _(u'The specified target URL cannot be used as a target'), - 48: _(u'The pingback has already been registered'), - 49: _(u'Access Denied') - } - del _ - - def __init__(self, fault_code, internal_message=None): - Exception.__init__(self) - self.fault_code = fault_code - self._internal_message = internal_message - - def as_fault(self): - """Return the pingback errors XMLRPC fault.""" - return Fault(self.fault_code, self.internal_message or - 'unknown server error') - - @property - def ignore_silently(self): - """If the error can be ignored silently.""" - return self.fault_code in (17, 33, 48, 49) - - @property - def means_missing(self): - """If the error means that the resource is missing or not - accepting pingbacks. - """ - return self.fault_code in (32, 33) - - @property - def internal_message(self): - if self._internal_message is not None: - return self._internal_message - return self.default_messages.get(self.fault_code) or 'server error' - - @property - def message(self): - msg = self.default_messages.get(self.fault_code) - if msg is not None: - return _(msg) - return _(u'An unknown server error (%s) occurred') % self.fault_code + """Raised if the remote server caused an exception while pingbacking. + This is not raised if the pingback function is unable to locate a + remote server. + """ + + _ = lambda x: x + default_messages = { + 16: _(u'source URL does not exist'), + 17: _(u'The source URL does not contain a link to the target URL'), + 32: _(u'The specified target URL does not exist'), + 33: _(u'The specified target URL cannot be used as a target'), + 48: _(u'The pingback has already been registered'), + 49: _(u'Access Denied') + } + del _ + + def __init__(self, fault_code, internal_message=None): + Exception.__init__(self) + self.fault_code = fault_code + self._internal_message = internal_message + + def as_fault(self): + """Return the pingback errors XMLRPC fault.""" + return Fault(self.fault_code, self.internal_message or + 'unknown server error') + + @property + def ignore_silently(self): + """If the error can be ignored silently.""" + return self.fault_code in (17, 33, 48, 49) + + @property + def means_missing(self): + """If the error means that the resource is missing or not + accepting pingbacks. + """ + return self.fault_code in (32, 33) + + @property + def internal_message(self): + if self._internal_message is not None: + return self._internal_message + return self.default_messages.get(self.fault_code) or 'server error' + + @property + def message(self): + msg = self.default_messages.get(self.fault_code) + if msg is not None: + return _(msg) + return _(u'An unknown server error (%s) occurred') % self.fault_code class util: - @classmethod - def do_trackback(cls, tbUrl=None, title=None, excerpt=None, url=None, blog_name=None): - taskqueue.add(url='/admin/do/trackback_ping', - params={'tbUrl': tbUrl,'title':title,'excerpt':excerpt,'url':url,'blog_name':blog_name}) + @classmethod + def do_trackback(cls, tbUrl=None, title=None, excerpt=None, url=None, blog_name=None): + taskqueue.add(url='/admin/do/trackback_ping', + params={'tbUrl': tbUrl,'title':title,'excerpt':excerpt,'url':url,'blog_name':blog_name}) - #pingback ping - @classmethod - def do_pingback(cls,source_uri, target_uri): - taskqueue.add(url='/admin/do/pingback_ping', - params={'source': source_uri,'target':target_uri}) + #pingback ping + @classmethod + def do_pingback(cls,source_uri, target_uri): + taskqueue.add(url='/admin/do/pingback_ping', + params={'source': source_uri,'target':target_uri}) @@ -192,196 +189,179 @@ def do_pingback(cls,source_uri, target_uri): class Pager(object): - def __init__(self, model=None,query=None, items_per_page=10): - if model: - self.query = model.all() - else: - self.query=query + def __init__(self, model=None,query=None, items_per_page=10): + if model: + self.query = model.all() + else: + self.query=query - self.items_per_page = items_per_page + self.items_per_page = items_per_page - def fetch(self, p): - if hasattr(self.query,'__len__'): - max_offset=len(self.query) - else: - max_offset = self.query.count() - n = max_offset / self.items_per_page - if max_offset % self.items_per_page != 0: - n += 1 + def fetch(self, p): + if hasattr(self.query,'__len__'): + max_offset=len(self.query) + else: + max_offset = self.query.count() + n = max_offset / self.items_per_page + if max_offset % self.items_per_page: + n += 1 - if p < 0 or p > n: - p = 1 - offset = (p - 1) * self.items_per_page - if hasattr(self.query,'fetch'): - results = self.query.fetch(self.items_per_page, offset) - else: - results = self.query[offset:offset+self.items_per_page] + if p < 0 or p > n: + p = 1 + offset = (p - 1) * self.items_per_page + if hasattr(self.query,'fetch'): + results = self.query.fetch(self.items_per_page, offset) + else: + results = self.query[offset:offset+self.items_per_page] - links = {'count':max_offset,'page_index':p,'prev': p - 1, 'next': p + 1, 'last': n} - if links['next'] > n: - links['next'] = 0 + links = {'count':max_offset,'page_index':p,'prev': p - 1, 'next': p + 1, 'last': n} + if links['next'] > n: + links['next'] = 0 - return (results, links) + return (results, links) class BaseRequestHandler(webapp.RequestHandler): - def __init__(self): - self.current='home' + def __init__(self): + self.current='home' ## def head(self, *args): ## return self.get(*args) - def initialize(self, request, response): - webapp.RequestHandler.initialize(self, request, response) - os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - from model import g_blog,User - self.blog = g_blog - self.login_user = users.get_current_user() - self.is_login = (self.login_user != None) - self.loginurl=users.create_login_url(self.request.uri) - self.logouturl=users.create_logout_url(self.request.uri) - self.is_admin = users.is_current_user_admin() - - if self.is_admin: - self.auth = 'admin' - self.author=User.all().filter('email =',self.login_user.email()).get() - if not self.author: - self.author=User(dispname=self.login_user.nickname(),email=self.login_user.email()) - self.author.isadmin=True - self.author.user=self.login_user - self.author.put() - elif self.is_login: - self.author=User.all().filter('email =',self.login_user.email()).get() - if self.author: - self.auth='author' - else: - self.auth = 'login' - else: - self.auth = 'guest' - - try: - self.referer = self.request.headers['referer'] - except: - self.referer = None - - - - self.template_vals = {'self':self,'blog':self.blog,'current':self.current} - - def __before__(self,*args): - pass - - def __after__(self,*args): - pass - - def error(self,errorcode,message='an error occured'): - if errorcode == 404: - message = 'Sorry, we were not able to find the requested page. We have logged this error and will look into it.' - elif errorcode == 403: - message = 'Sorry, that page is reserved for administrators. ' - elif errorcode == 500: - message = "Sorry, the server encountered an error. We have logged this error and will look into it." - - message+="

"+traceback.format_exc()+"

" - self.template_vals.update( {'errorcode':errorcode,'message':message}) - - - - - - - if errorcode>0: - self.response.set_status(errorcode) - - - #errorfile=getattr(self.blog.theme,'error'+str(errorcode)) - #logging.debug(errorfile) + def initialize(self, request, response): + webapp.RequestHandler.initialize(self, request, response) + os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + from model import g_blog,User + self.blog = g_blog + self.login_user = users.get_current_user() + self.is_login = (self.login_user != None) + self.loginurl=users.create_login_url(self.request.uri) + self.logouturl=users.create_logout_url(self.request.uri) + self.is_admin = users.is_current_user_admin() + + if self.is_admin: + self.auth = 'admin' + self.author=User.all().filter('email =',self.login_user.email()).get() + if not self.author: + self.author=User(dispname=self.login_user.nickname(),email=self.login_user.email()) + self.author.isadmin=True + self.author.user=self.login_user + self.author.put() + elif self.is_login: + self.author=User.all().filter('email =',self.login_user.email()).get() + if self.author: + self.auth='author' + else: + self.auth = 'login' + else: + self.auth = 'guest' + + try: + self.referer = self.request.headers['referer'] + except: + self.referer = None + + self.template_vals = {'self':self,'blog':self.blog,'current':self.current} + + def __before__(self,*args): + pass + + def __after__(self,*args): + pass + + def error(self,errorcode,message='an error occured'): + if errorcode == 404: + message = 'Sorry, we were not able to find the requested page. We have logged this error and will look into it.' + elif errorcode == 403: + message = 'Sorry, that page is reserved for administrators. ' + elif errorcode == 500: + message = "Sorry, the server encountered an error. We have logged this error and will look into it." + + message+="

"+traceback.format_exc()+"

" + self.template_vals.update( {'errorcode':errorcode,'message':message}) + + if errorcode>0: + self.response.set_status(errorcode) + + + #errorfile=getattr(self.blog.theme,'error'+str(errorcode)) + #logging.debug(errorfile) ## if not errorfile: ## errorfile=self.blog.theme.error - errorfile='error'+str(errorcode)+".html" - try: - content=micolog_template.render(self.blog.theme,errorfile, self.template_vals) - except TemplateDoesNotExist: - try: - content=micolog_template.render(self.blog.theme,"error.html", self.template_vals) - except TemplateDoesNotExist: - content=micolog_template.render(self.blog.default_theme,"error.html", self.template_vals) - except: - content=message - except: - content=message - self.response.out.write(content) - - def get_render(self,template_file,values): - template_file=template_file+".html" - self.template_vals.update(values) - - try: - #sfile=getattr(self.blog.theme, template_file) - logging.debug("get_render:"+template_file) - html = micolog_template.render(self.blog.theme, template_file, self.template_vals) - except TemplateDoesNotExist: - #sfile=getattr(self.blog.default_theme, template_file) - html = micolog_template.render(self.blog.default_theme, template_file, self.template_vals) - - return html - - def render(self,template_file,values): - """ - Helper method to render the appropriate template - """ - - html=self.get_render(template_file,values) - self.response.out.write(html) - - def message(self,msg,returl=None,title='Infomation'): - self.render('msg',{'message':msg,'title':title,'returl':returl}) - - def render2(self,template_file,template_vals={}): - """ - Helper method to render the appropriate template - """ - - self.template_vals.update(template_vals) - path = os.path.join(self.blog.rootdir, template_file) - self.response.out.write(template.render(path, self.template_vals)) - - - def param(self, name, **kw): - return self.request.get(name, **kw) - - def paramint(self, name, default=0): - try: - return int(self.request.get(name)) - except: - return default - - def parambool(self, name, default=False): - try: - return self.request.get(name)=='on' - except: - return default - - - def write(self, s): - self.response.out.write(s) - - - - def chk_login(self, redirect_url='/'): - if self.is_login: - return True - else: - self.redirect(redirect_url) - return False - - def chk_admin(self, redirect_url='/'): - if self.is_admin: - return True - else: - self.redirect(redirect_url) - return False - - + errorfile='error'+str(errorcode)+".html" + try: + content=micolog_template.render(self.blog.theme,errorfile, self.template_vals) + except TemplateDoesNotExist: + try: + content=micolog_template.render(self.blog.theme,"error.html", self.template_vals) + except TemplateDoesNotExist: + content=micolog_template.render(self.blog.default_theme,"error.html", self.template_vals) + except: + content=message + self.response.out.write(content) + + def get_render(self,template_file,values): + template_file=template_file+".html" + self.template_vals.update(values) + + try: + #sfile=getattr(self.blog.theme, template_file) + logging.debug("get_render:"+template_file) + html = micolog_template.render(self.blog.theme, template_file, self.template_vals) + except TemplateDoesNotExist: + #sfile=getattr(self.blog.default_theme, template_file) + html = micolog_template.render(self.blog.default_theme, template_file, self.template_vals) + + return html + + def render(self,template_file,values): + """ + Helper method to render the appropriate template + """ + html=self.get_render(template_file,values) + self.response.out.write(html) + + def message(self,msg,returl=None,title='Infomation'): + self.render('msg',{'message':msg,'title':title,'returl':returl}) + + def render2(self,template_file,template_vals={}): + """ + Helper method to render the appropriate template + """ + self.template_vals.update(template_vals) + path = os.path.join(os.path.dirname(__file__), template_file) + self.response.out.write(template.render(path, self.template_vals)) + + def param(self, name, **kw): + return self.request.get(name, **kw) + + def paramint(self, name, default=0): + try: + return int(self.request.get(name)) + except: + return default + + def parambool(self, name, default=False): + try: + return self.request.get(name)=='on' + except: + return default + + def write(self, s): + self.response.out.write(s) + + def chk_login(self, redirect_url='/'): + if self.is_login: + return True + else: + self.redirect(redirect_url) + return False + + def chk_admin(self, redirect_url='/'): + if self.is_admin: + return True + else: + self.redirect(redirect_url) + return False diff --git a/blog.py b/blog.py index b7be144..c85e839 100644 --- a/blog.py +++ b/blog.py @@ -1,32 +1,22 @@ # -*- coding: utf-8 -*- -import cgi, os,sys,math +import cgi, os +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') import wsgiref.handlers -import google.appengine.api # Google App Engine imports. -from google.appengine.ext.webapp import util -from google.appengine.ext.webapp import template, \ - WSGIApplication -from google.appengine.api import users ##import app.webapp as webapp2 -from google.appengine.ext import db -# Force Django to reload its settings. - -from datetime import datetime ,timedelta -import base64,random +from datetime import timedelta +import random from django.utils import simplejson -import filter as myfilter -from django.template.loader import * -##settings.configure(LANGUAGE_CODE = 'zh-cn') -# Must set this env var before importing any part of Django - +import app.filter as myfilter from app.safecode import Image from app.gmemsess import Session - from base import * from model import * - +from django.utils.translation import ugettext as _ ##os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' ##from django.utils.translation import activate @@ -35,290 +25,266 @@ ##activate(g_blog.language) from google.appengine.ext import zipserve - def doRequestHandle(old_handler,new_handler,**args): - new_handler.initialize(old_handler.request,old_handler.response) - return new_handler.get(**args) + new_handler.initialize(old_handler.request,old_handler.response) + return new_handler.get(**args) def doRequestPostHandle(old_handler,new_handler,**args): - new_handler.initialize(old_handler.request,old_handler.response) - return new_handler.post(**args) + new_handler.initialize(old_handler.request,old_handler.response) + return new_handler.post(**args) class BasePublicPage(BaseRequestHandler): - def initialize(self, request, response): - BaseRequestHandler.initialize(self,request, response) - m_pages=Entry.all().filter('entrytype =','page')\ - .filter('published =',True)\ - .filter('entry_parent =',0)\ - .order('menu_order') - blogroll=Link.all().filter('linktype =','blogroll') - archives=Archive.all().order('-year').order('-month').fetch(12) - alltags=Tag.all() - self.template_vals.update({ - 'menu_pages':m_pages, - 'categories':Category.all(), - 'blogroll':blogroll, - 'archives':archives, - 'alltags':alltags, - 'recent_comments':Comment.all().order('-date').fetch(5) - }) - - def m_list_pages(self): - menu_pages=None - entry=None - if self.template_vals.has_key('menu_pages'): - menu_pages= self.template_vals['menu_pages'] - if self.template_vals.has_key('entry'): - entry=self.template_vals['entry'] - ret='' - current='' - for page in menu_pages: - if entry and entry.entrytype=='page' and entry.key()==page.key(): - current= 'current_page_item' - else: - current= 'page_item' - #page is external page ,and page.slug is none. - if page.is_external_page and not page.slug: - ret+='
  • %s
  • '%( current,page.link,page.target, page.title) - else: - ret+='
  • %s
  • '%( current,page.link, page.target,page.title) - return ret - - def sticky_entrys(self): - return Entry.all().filter('entrytype =','post')\ - .filter('published =',True)\ - .filter('sticky =',True)\ - .order('-date') + def initialize(self, request, response): + BaseRequestHandler.initialize(self,request, response) + m_pages=Entry.all().filter('entrytype =','page')\ + .filter('published =',True)\ + .filter('entry_parent =',0)\ + .order('menu_order') + blogroll=Link.all().filter('linktype =','blogroll') + archives=Archive.all().order('-year').order('-month').fetch(12) + alltags=Tag.all() + self.template_vals.update( + dict(menu_pages=m_pages, categories=Category.all(), blogroll=blogroll, archives=archives, alltags=alltags, + recent_comments=Comment.all().order('-date').fetch(5))) + + def m_list_pages(self): + menu_pages=None + entry=None + if self.template_vals.has_key('menu_pages'): + menu_pages= self.template_vals['menu_pages'] + if self.template_vals.has_key('entry'): + entry=self.template_vals['entry'] + ret='' + current='' + for page in menu_pages: + if entry and entry.entrytype=='page' and entry.key()==page.key(): + current= 'current_page_item' + else: + current= 'page_item' + #page is external page ,and page.slug is none. + if page.is_external_page and not page.slug: + ret+='
  • %s
  • '%( current,page.link,page.target, page.title) + else: + ret+='
  • %s
  • '%( current,page.link, page.target,page.title) + return ret + + def sticky_entrys(self): + return Entry.all().filter('entrytype =','post')\ + .filter('published =',True)\ + .filter('sticky =',True)\ + .order('-date') class MainPage(BasePublicPage): - def get(self,page=1): - postid=self.param('p') - if postid: - try: - postid=int(postid) - return doRequestHandle(self,SinglePost(),postid=postid) #singlepost.get(postid=postid) - except: - return self.error(404) - self.doget(page) - - def post(self): - postid=self.param('p') - if postid: - try: - postid=int(postid) - return doRequestPostHandle(self,SinglePost(),postid=postid) #singlepost.get(postid=postid) - except: - return self.error(404) - - - @cache() - def doget(self,page): - page=int(page) - entrycount=g_blog.postscount() - max_page = entrycount / g_blog.posts_per_page + ( entrycount % g_blog.posts_per_page and 1 or 0 ) - - if page < 1 or page > max_page: - return self.error(404) - - entries = Entry.all().filter('entrytype =','post').\ - filter("published =", True).order('-date').\ - fetch(self.blog.posts_per_page, offset = (page-1) * self.blog.posts_per_page) - - - show_prev =entries and (not (page == 1)) - show_next =entries and (not (page == max_page)) - #print page,max_page,g_blog.entrycount,self.blog.posts_per_page - - - return self.render('index',{'entries':entries, - 'show_prev' : show_prev, - 'show_next' : show_next, - 'pageindex':page, - 'ishome':True, - 'pagecount':max_page, - 'postscounts':entrycount - }) - + def head(self,page=1): + if g_blog.allow_pingback : + self.response.headers['X-Pingback']="%s/rpc"%str(g_blog.baseurl) + + def get(self,page=1): + postid=self.param('p') + if postid: + try: + postid=int(postid) + return doRequestHandle(self,SinglePost(),postid=postid) #singlepost.get(postid=postid) + except: + return self.error(404) + if g_blog.allow_pingback : + self.response.headers['X-Pingback']="%s/rpc"%str(g_blog.baseurl) + self.doget(page) + + def post(self): + postid=self.param('p') + if postid: + try: + postid=int(postid) + return doRequestPostHandle(self,SinglePost(),postid=postid) #singlepost.get(postid=postid) + except: + return self.error(404) + + + @cache() + def doget(self,page): + page=int(page) + entrycount=g_blog.postscount() + max_page = entrycount / g_blog.posts_per_page + ( entrycount % g_blog.posts_per_page and 1 or 0 ) + + if page < 1 or page > max_page: + return self.error(404) + + entries = Entry.all().filter('entrytype =','post').\ + filter("published =", True).order('-sticky').order('-date').\ + fetch(self.blog.posts_per_page, offset = (page-1) * self.blog.posts_per_page) + + + show_prev =entries and (not (page == 1)) + show_next =entries and (not (page == max_page)) + #print page,max_page,g_blog.entrycount,self.blog.posts_per_page + + return self.render('index', + dict(entries=entries, show_prev=show_prev, show_next=show_next, pageindex=page, ishome=True, + pagecount=max_page, postscounts=entrycount)) class entriesByCategory(BasePublicPage): - @cache() - def get(self,slug=None): - if not slug: - self.error(404) - return - try: - page_index=int(self.param('page')) - except: - page_index=1 - slug=urldecode(slug) - - cats=Category.all().filter('slug =',slug).fetch(1) - if cats: - entries=Entry.all().filter("published =", True).filter('categorie_keys =',cats[0].key()).order("-date") - entries,links=Pager(query=entries,items_per_page=20).fetch(page_index) - self.render('category',{'entries':entries,'category':cats[0],'pager':links}) - else: - self.error(414,slug) + @cache() + def get(self,slug=None): + if not slug: + self.error(404) + return + + try: + page_index=int(self.param('page')) + except: + page_index=1 + + slug=urldecode(slug) + + cats=Category.all().filter('slug =',slug).fetch(1) + if cats: + entries=Entry.all().filter("published =", True).filter('categorie_keys =',cats[0].key()).order("-date") + entries,links=Pager(query=entries,items_per_page=20).fetch(page_index) + self.render('category', dict(entries=entries, category=cats[0], pager=links)) + else: + self.error(404,slug) class archive_by_month(BasePublicPage): - @cache() - def get(self,year,month): - try: - page_index=int (self.param('page')) - except: - page_index=1 - - firstday=datetime(int(year),int(month),1) - if int(month)!=12: - lastday=datetime(int(year),int(month)+1,1) - else: - lastday=datetime(int(year)+1,1,1) - entries=db.GqlQuery("SELECT * FROM Entry WHERE date > :1 AND date <:2 AND entrytype =:3 AND published = True ORDER BY date DESC",firstday,lastday,'post') - entries,links=Pager(query=entries).fetch(page_index) - - self.render('month',{'entries':entries,'year':year,'month':month,'pager':links}) + @cache() + def get(self,year,month): + try: + page_index=int (self.param('page')) + except: + page_index=1 + + firstday=datetime(int(year),int(month),1) + if int(month)!=12: + lastday=datetime(int(year),int(month)+1,1) + else: + lastday=datetime(int(year)+1,1,1) + entries=db.GqlQuery("SELECT * FROM Entry WHERE date > :1 AND date <:2 AND entrytype =:3 AND published = True ORDER BY date DESC",firstday,lastday,'post') + entries,links=Pager(query=entries).fetch(page_index) + + self.render('month', dict(entries=entries, year=year, month=month, pager=links)) class entriesByTag(BasePublicPage): - @cache() - def get(self,slug=None): - if not slug: - self.error(404) - return - try: - page_index=int (self.param('page')) - except: - page_index=1 - import urllib - slug=urldecode(slug) - - entries=Entry.all().filter("published =", True).filter('tags =',slug).order("-date") - entries,links=Pager(query=entries,items_per_page=20).fetch(page_index) - self.render('tag',{'entries':entries,'tag':slug,'pager':links}) - - + @cache() + def get(self,slug=None): + if not slug: + self.error(404) + return + try: + page_index=int (self.param('page')) + except: + page_index=1 + slug=urldecode(slug) + + entries=Entry.all().filter("published =", True).filter('tags =',slug).order("-date") + entries,links=Pager(query=entries,items_per_page=20).fetch(page_index) + self.render('tag',{'entries':entries,'tag':slug,'pager':links}) class SinglePost(BasePublicPage): - def head(self,slug=None,postid=None): - if g_blog.allow_pingback : - self.response.headers['X-Pingback']="%s/rpc"%str(g_blog.baseurl) - - @cache() - def get(self,slug=None,postid=None): - if postid: - entries = Entry.all().filter("published =", True).filter('post_id =', postid).fetch(1) - else: - slug=urldecode(slug) - entries = Entry.all().filter("published =", True).filter('link =', slug).fetch(1) - if not entries or len(entries) == 0: - return self.error(404) - - mp=self.paramint("mp",1) - - entry=entries[0] - if entry.is_external_page: - return self.redirect(entry.external_page_address,True) - if g_blog.allow_pingback and entry.allow_trackback: - self.response.headers['X-Pingback']="%s/rpc"%str(g_blog.baseurl) - entry.readtimes += 1 - entry.put() - self.entry=entry - - - comments=entry.get_comments_by_page(mp,self.blog.comments_per_page) - + def head(self,slug=None,postid=None): + if g_blog.allow_pingback : + self.response.headers['X-Pingback']="%s/rpc"%str(g_blog.baseurl) + + @cache() + def get(self,slug=None,postid=None): + if postid: + entries = Entry.all().filter("published =", True).filter('post_id =', postid).fetch(1) + else: + slug=urldecode(slug) + entries = Entry.all().filter("published =", True).filter('link =', slug).fetch(1) + if not entries or len(entries) == 0: + return self.error(404) + + mp=self.paramint("mp",1) + + entry=entries[0] + if entry.is_external_page: + return self.redirect(entry.external_page_address,True) + if g_blog.allow_pingback and entry.allow_trackback: + self.response.headers['X-Pingback']="%s/rpc"%str(g_blog.baseurl) + entry.readtimes += 1 + entry.put() + self.entry=entry + + + comments=entry.get_comments_by_page(mp,self.blog.comments_per_page) ## commentuser=self.request.cookies.get('comment_user', '') ## if commentuser: ## commentuser=commentuser.split('#@#') ## else: - commentuser=['','',''] - - comments_nav=self.get_comments_nav(mp,entry.purecomments().count()) - - if entry.entrytype=='post': - self.render('single', - { - 'entry':entry, - 'relateposts':entry.relateposts, - 'comments':comments, - 'user_name':commentuser[0], - 'user_email':commentuser[1], - 'user_url':commentuser[2], - 'checknum1':random.randint(1,10), - 'checknum2':random.randint(1,10), - 'comments_nav':comments_nav, - }) - - else: - self.render('page', - {'entry':entry, - 'relateposts':entry.relateposts, - 'comments':comments, - 'user_name':commentuser[0], - 'user_email':commentuser[1], - 'user_url':commentuser[2], - 'checknum1':random.randint(1,10), - 'checknum2':random.randint(1,10), - 'comments_nav':comments_nav, - }) - - def post(self,slug=None,postid=None): - '''handle trackback''' - error = ''' + commentuser=['','',''] + + comments_nav=self.get_comments_nav(mp,entry.purecomments().count()) + + if entry.entrytype=='post': + self.render('single', + dict(entry=entry, relateposts=entry.relateposts, comments=comments, user_name=commentuser[0], + user_email=commentuser[1], user_url=commentuser[2], checknum1=random.randint(1, 10), + checknum2=random.randint(1, 10), comments_nav=comments_nav)) + + else: + self.render('page', + dict(entry=entry, relateposts=entry.relateposts, comments=comments, user_name=commentuser[0], + user_email=commentuser[1], user_url=commentuser[2], checknum1=random.randint(1, 10), + checknum2=random.randint(1, 10), comments_nav=comments_nav)) + + def post(self,slug=None,postid=None): + '''handle trackback''' + error = ''' 1 %s ''' - success = ''' + success = ''' 0 ''' - if not g_blog.allow_trackback: - self.response.out.write(self.error % "Trackback denied.") - return - self.response.headers['Content-Type'] = "text/xml" - if postid: - entries = Entry.all().filter("published =", True).filter('post_id =', postid).fetch(1) - else: - slug=urldecode(slug) - entries = Entry.all().filter("published =", True).filter('link =', slug).fetch(1) - - if not entries or len(entries) == 0 :#or (postid and not entries[0].link.endswith(g_blog.default_link_format%{'post_id':postid})): - self.response.out.write(error % "empty slug/postid") - return - #check code ,rejest spam - entry=entries[0] - logging.info(self.request.remote_addr+self.request.path+" "+entry.trackbackurl) - #key=self.param("code") - #if (self.request.uri!=entry.trackbackurl) or entry.is_external_page or not entry.allow_trackback: - #import cgi - from urlparse import urlparse - param=urlparse(self.request.uri) - code=param[4] - param=cgi.parse_qs(code) - if param.has_key('code'): - code=param['code'][0] - - if (not str(entry.key())==code) or entry.is_external_page or not entry.allow_trackback: - self.response.out.write(error % "Invalid trackback url.") - return - - - coming_url = self.param('url') - blog_name = myfilter.do_filter(self.param('blog_name')) - excerpt = myfilter.do_filter(self.param('excerpt')) - title = myfilter.do_filter(self.param('title')) - - if not coming_url or not blog_name or not excerpt or not title: - self.response.out.write(error % "not enough post info") - return - - import time - #wait for half second in case otherside hasn't been published - time.sleep(0.5) + if not g_blog.allow_trackback: + self.response.out.write(error % "Trackback denied.") + return + self.response.headers['Content-Type'] = "text/xml" + if postid: + entries = Entry.all().filter("published =", True).filter('post_id =', postid).fetch(1) + else: + slug=urldecode(slug) + entries = Entry.all().filter("published =", True).filter('link =', slug).fetch(1) + + if not entries or len(entries) == 0 :#or (postid and not entries[0].link.endswith(g_blog.default_link_format%{'post_id':postid})): + self.response.out.write(error % "empty slug/postid") + return + #check code ,rejest spam + entry=entries[0] + logging.info(self.request.remote_addr+self.request.path+" "+entry.trackbackurl) + #key=self.param("code") + #if (self.request.uri!=entry.trackbackurl) or entry.is_external_page or not entry.allow_trackback: + #import cgi + from urlparse import urlparse + param=urlparse(self.request.uri) + code=param[4] + param=cgi.parse_qs(code) + if param.has_key('code'): + code=param['code'][0] + + if (not str(entry.key())==code) or entry.is_external_page or not entry.allow_trackback: + self.response.out.write(error % "Invalid trackback url.") + return + + + coming_url = self.param('url') + blog_name = myfilter.do_filter(self.param('blog_name')) + excerpt = myfilter.do_filter(self.param('excerpt')) + title = myfilter.do_filter(self.param('title')) + + if not coming_url or not blog_name or not excerpt or not title: + self.response.out.write(error % "not enough post info") + return + + import time + #wait for half second in case otherside hasn't been published + time.sleep(0.5) ## #also checking the coming url is valid and contains our link ## #this is not standard trackback behavior @@ -334,397 +300,387 @@ def post(self,slug=None,postid=None): ## self.response.out.write(error % "urlfetch error") ## return - comment = Comment.all().filter("entry =", entry).filter("weburl =", coming_url).get() - if comment: - self.response.out.write(error % "has pinged before") - return - - comment=Comment(author=blog_name, - content=""+title[:250]+"...
    " + - excerpt[:250] + '...', - weburl=coming_url, - entry=entry) - - comment.ip=self.request.remote_addr - comment.ctype=COMMENT_TRACKBACK - try: - comment.save() - - memcache.delete("/"+entry.link) - self.write(success) - g_blog.tigger_action("pingback_post",comment) - except: - self.response.out.write(error % "unknow error") - - def get_comments_nav(self,pindex,count): - - maxpage=count / g_blog.comments_per_page + ( count % g_blog.comments_per_page and 1 or 0 ) - if maxpage==1: - return "" - - result="" - - if pindex>1: - result="«" - - minr=max(pindex-3,1) - maxr=min(pindex+3,maxpage) - if minr>2: - result+="1" - result+="..." - - for n in range(minr,maxr+1): - if n==pindex: - result+=""+str(n)+"" - else: - result+=""+str(n)+"" - if maxr"+str(maxpage)+"" - - if pindex»" - - return {'nav':result,'current':pindex,'maxpage':maxpage} - - def get_comments_pagenum_link(self,pindex): - url=str(self.entry.link) - if url.find('?')>=0: - return "/"+url+"&mp="+str(pindex)+"#comments" - else: - return "/"+url+"?mp="+str(pindex)+"#comments" + comment = Comment.all().filter("entry =", entry).filter("weburl =", coming_url).get() + if comment: + self.response.out.write(error % "has pinged before") + return + + comment=Comment(author=blog_name, + content="..."+title[:250]+" " + + excerpt[:250] + '...', + weburl=coming_url, + entry=entry) + + comment.ip=self.request.remote_addr + comment.ctype=COMMENT_TRACKBACK + try: + comment.save() + + memcache.delete("/"+entry.link) + self.write(success) + g_blog.tigger_action("pingback_post",comment) + except: + self.response.out.write(error % "unknow error") + + def get_comments_nav(self,pindex,count): + maxpage=count / g_blog.comments_per_page + ( count % g_blog.comments_per_page and 1 or 0 ) + if maxpage==1: + return {'nav':"",'current':pindex} + + result="" + + if pindex>1: + result="«" + + minr=max(pindex-3,1) + maxr=min(pindex+3,maxpage) + if minr>2: + result+="1" + result+="..." + + for n in range(minr,maxr+1): + if n==pindex: + result+=""+str(n)+"" + else: + result+=""+str(n)+"" + if maxr"+str(maxpage)+"" + + if pindex»" + + return {'nav':result,'current':pindex,'maxpage':maxpage} + + def get_comments_pagenum_link(self,pindex): + url=str(self.entry.link) + if url.find('?')>=0: + return "/"+url+"&mp="+str(pindex)+"#comments" + else: + return "/"+url+"?mp="+str(pindex)+"#comments" class FeedHandler(BaseRequestHandler): - @cache(time=600) - def get(self,tags=None): - entries = Entry.all().filter('entrytype =','post').filter('published =',True).order('-date').fetch(10) - if entries and entries[0]: - last_updated = entries[0].date - last_updated = last_updated.strftime("%a, %d %b %Y %H:%M:%S +0000") - for e in entries: - e.formatted_date = e.date.strftime("%a, %d %b %Y %H:%M:%S +0000") - self.response.headers['Content-Type'] = 'application/rss+xml' - self.render2('views/rss.xml',{'entries':entries,'last_updated':last_updated}) + @cache(time=600) + def get(self,tags=None): + entries = Entry.all().filter('entrytype =','post').filter('published =',True).order('-date').fetch(10) + if entries and entries[0]: + last_updated = entries[0].date + last_updated = last_updated.strftime("%a, %d %b %Y %H:%M:%S +0000") + for e in entries: + e.formatted_date = e.date.strftime("%a, %d %b %Y %H:%M:%S +0000") + self.response.headers['Content-Type'] = 'application/rss+xml; charset=utf-8' + self.render2('views/rss.xml',{'entries':entries,'last_updated':last_updated}) class CommentsFeedHandler(BaseRequestHandler): - @cache(time=600) - def get(self,tags=None): - comments = Comment.all().order('-date').filter('ctype =',0).fetch(10) - if comments and comments[0]: - last_updated = comments[0].date - last_updated = last_updated.strftime("%a, %d %b %Y %H:%M:%S +0000") - for e in comments: - e.formatted_date = e.date.strftime("%a, %d %b %Y %H:%M:%S +0000") - self.response.headers['Content-Type'] = 'application/rss+xml' - self.render2('views/comments.xml',{'comments':comments,'last_updated':last_updated}) + @cache(time=600) + def get(self,tags=None): + comments = Comment.all().order('-date').filter('ctype =',0).fetch(10) + if comments and comments[0]: + last_updated = comments[0].date + last_updated = last_updated.strftime("%a, %d %b %Y %H:%M:%S +0000") + for e in comments: + e.formatted_date = e.date.strftime("%a, %d %b %Y %H:%M:%S +0000") + self.response.headers['Content-Type'] = 'application/rss+xml; charset=UTF-8' + self.render2('views/comments.xml',{'comments':comments,'last_updated':last_updated}) class SitemapHandler(BaseRequestHandler): - @cache(time=36000) - def get(self,tags=None): - urls = [] - def addurl(loc,lastmod=None,changefreq=None,priority=None): - url_info = { - 'location': loc, - 'lastmod': lastmod, - 'changefreq': changefreq, - 'priority': priority - } - urls.append(url_info) - - addurl(g_blog.baseurl,changefreq='daily',priority=0.9 ) - - entries = Entry.all().filter('published =',True).order('-date').fetch(g_blog.sitemap_entries) - - for item in entries: - loc = "%s/%s" % (g_blog.baseurl, item.link) - addurl(loc,item.mod_date or item.date,'never',0.6) - - if g_blog.sitemap_include_category: - cats=Category.all() - for cat in cats: - loc="%s/category/%s"%(g_blog.baseurl,cat.slug) - addurl(loc,None,'weekly',0.5) - - if g_blog.sitemap_include_tag: - tags=Tag.all() - for tag in tags: - loc="%s/tag/%s"%(g_blog.baseurl, urlencode(tag.tag)) - addurl(loc,None,'weekly',0.5) + @cache(time=36000) + def get(self,tags=None): + urls = [] + def addurl(loc,lastmod=None,changefreq=None,priority=None): + url_info = { + 'location': loc, + 'lastmod': lastmod, + 'changefreq': changefreq, + 'priority': priority + } + urls.append(url_info) + + addurl(g_blog.baseurl,changefreq='daily',priority=0.9 ) + + entries = Entry.all().filter('published =',True).order('-date').fetch(g_blog.sitemap_entries) + + for item in entries: + loc = "%s/%s" % (g_blog.baseurl, item.link) + addurl(loc,item.mod_date or item.date,'never',0.6) + + if g_blog.sitemap_include_category: + cats=Category.all() + for cat in cats: + loc="%s/category/%s"%(g_blog.baseurl,cat.slug) + addurl(loc,None,'weekly',0.5) + + if g_blog.sitemap_include_tag: + tags=Tag.all() + for tag in tags: + loc="%s/tag/%s"%(g_blog.baseurl, urlencode(tag.tag)) + addurl(loc,None,'weekly',0.5) ## self.response.headers['Content-Type'] = 'application/atom+xml' - self.render2('views/sitemap.xml',{'urlset':urls}) + self.render2('views/sitemap.xml',{'urlset':urls}) class Error404(BaseRequestHandler): - @cache(time=36000) - def get(self,slug=None): - self.error(404) + @cache(time=36000) + def get(self,slug=None): + self.error(404) class Post_comment(BaseRequestHandler): - #@printinfo - def post(self,slug=None): - useajax=self.param('useajax')=='1' - - name=self.param('author') - email=self.param('email') - url=self.param('url') - - key=self.param('key') - content=self.param('comment') - parent_id=self.paramint('parentid',0) - reply_notify_mail=self.parambool('reply_notify_mail') - - sess=Session(self,timeout=180) - if not self.is_login: - #if not (self.request.cookies.get('comment_user', '')): - try: - check_ret=True - if g_blog.comment_check_type in (1,2) : - checkret=self.param('checkret') - check_ret=(int(checkret) == sess['code']) - elif g_blog.comment_check_type ==3: - import app.gbtools as gb - checknum=self.param('checknum') - checkret=self.param('checkret') - check_ret=eval(checknum)==int(gb.stringQ2B( checkret)) - - if not check_ret: - if useajax: - self.write(simplejson.dumps((False,-102,_('Your check code is invalid .')),ensure_ascii = False)) - else: - self.error(-102,_('Your check code is invalid .')) - return - except: - if useajax: - self.write(simplejson.dumps((False,-102,_('Your check code is invalid .')),ensure_ascii = False)) - else: - self.error(-102,_('Your check code is invalid .')) - return - - sess.invalidate() - content=content.replace('\n','
    ') - content=myfilter.do_filter(content) - name=cgi.escape(name)[:20] - url=cgi.escape(url)[:100] - - if not (name and email and content): - if useajax: - self.write(simplejson.dumps((False,-101,_('Please input name, email and comment .')))) - else: - self.error(-101,_('Please input name, email and comment .')) - else: - comment=Comment(author=name, - content=content, - email=email, - reply_notify_mail=reply_notify_mail, - entry=Entry.get(key)) - if url: - try: - if not url.startswith(('http://','https://')): - url = 'http://' + url - comment.weburl=url - except: - comment.weburl=None - - #name=name.decode('utf8').encode('gb2312') - - - info_str='#@#'.join([urlencode(name),urlencode(email),urlencode(url)]) - - #info_str='#@#'.join([name,email,url.encode('utf8')]) - cookiestr='comment_user=%s;expires=%s;domain=%s;path=/'%( info_str, - (datetime.now()+timedelta(days=100)).strftime("%a, %d-%b-%Y %H:%M:%S GMT"), - '' - ) - comment.ip=self.request.remote_addr - - if parent_id: - comment.parent=Comment.get_by_id(parent_id) - - comment.no=comment.entry.commentcount+1 - try: - comment.save() - memcache.delete("/"+comment.entry.link) - - self.response.headers.add_header( 'Set-Cookie', cookiestr) - if useajax: - comment_c=self.get_render('comment',{'comment':comment}) - self.write(simplejson.dumps((True,comment_c.decode('utf8')),ensure_ascii = False)) - else: - self.redirect(self.referer+"#comment-"+str(comment.key().id())) - - comment.entry.removecache() - memcache.delete("/feed/comments") - except: - if useajax: - self.write(simplejson.dumps((False,-102,_('Comment not allowed.')))) - else: - self.error(-102,_('Comment not allowed .')) - + #@printinfo + def post(self,slug=None): + useajax=self.param('useajax')=='1' + + name=self.param('author') + email=self.param('email') + url=self.param('url') + + key=self.param('key') + content=self.param('comment') + parent_id=self.paramint('parentid',0) + reply_notify_mail=self.parambool('reply_notify_mail') + + sess=Session(self,timeout=180) + if not self.is_login: + #if not (self.request.cookies.get('comment_user', '')): + try: + check_ret=True + if g_blog.comment_check_type in (1,2) : + checkret=self.param('checkret') + check_ret=(int(checkret) == sess['code']) + elif g_blog.comment_check_type ==3: + import app.gbtools as gb + checknum=self.param('checknum') + checkret=self.param('checkret') + check_ret=eval(checknum)==int(gb.stringQ2B( checkret)) + + if not check_ret: + if useajax: + self.write(simplejson.dumps((False,-102,_('Your check code is invalid .')),ensure_ascii = False)) + else: + self.error(-102,_('Your check code is invalid .')) + return + except: + if useajax: + self.write(simplejson.dumps((False,-102,_('Your check code is invalid .')),ensure_ascii = False)) + else: + self.error(-102,_('Your check code is invalid .')) + return + + sess.invalidate() + content=content.replace('\n','
    ') + content=myfilter.do_filter(content) + name=cgi.escape(name)[:20] + url=cgi.escape(url)[:100] + + if not (name and email and content): + if useajax: + self.write(simplejson.dumps((False,-101,_('Please input name, email and comment .')))) + else: + self.error(-101,_('Please input name, email and comment .')) + else: + comment=Comment(author=name, + content=content, + email=email, + reply_notify_mail=reply_notify_mail, + entry=Entry.get(key)) + if url: + try: + if not url.lower().startswith(('http://','https://')): + url = 'http://' + url + comment.weburl=url + except: + comment.weburl=None + + #name=name.decode('utf8').encode('gb2312') + + info_str='#@#'.join([urlencode(name),urlencode(email),urlencode(url)]) + + #info_str='#@#'.join([name,email,url.encode('utf8')]) + cookiestr='comment_user=%s;expires=%s;path=/;'%( info_str, + (datetime.now()+timedelta(days=100)).strftime("%a, %d-%b-%Y %H:%M:%S GMT") + ) + comment.ip=self.request.remote_addr + + if parent_id: + comment.parent=Comment.get_by_id(parent_id) + + comment.no=comment.entry.commentcount+1 + try: + comment.save() + memcache.delete("/"+comment.entry.link) + + self.response.headers.add_header( 'Set-Cookie', cookiestr) + if useajax: + comment_c=self.get_render('comment',{'comment':comment}) + self.write(simplejson.dumps((True,comment_c.decode('utf8')),ensure_ascii = False)) + else: + self.redirect(self.referer+"#comment-"+str(comment.key().id())) + + comment.entry.removecache() + memcache.delete("/feed/comments") + except: + if useajax: + self.write(simplejson.dumps((False,-102,_('Comment not allowed.')))) + else: + self.error(-102,_('Comment not allowed .')) class ChangeTheme(BaseRequestHandler): - @requires_admin - def get(self,slug=None): - theme=self.param('t') - g_blog.theme_name=theme - g_blog.get_theme() - self.redirect('/') + @requires_admin + def get(self,slug=None): + theme=self.param('t') + g_blog.theme_name=theme + g_blog.get_theme() + self.redirect('/') class do_action(BaseRequestHandler): - def get(self,slug=None): - - try: - func=getattr(self,'action_'+slug) - if func and callable(func): - func() - else: - self.error(404) - except BaseException,e: - self.error(404) - - def post(self,slug=None): - try: - func=getattr(self,'action_'+slug) - if func and callable(func): - func() - else: - self.error(404) - except: - self.error(404) - - @ajaxonly - def action_info_login(self): - if self.login_user: - self.write(simplejson.dumps({'islogin':True, - 'isadmin':self.is_admin, - 'name': self.login_user.nickname()})) - else: - self.write(simplejson.dumps({'islogin':False})) - - #@hostonly - @cache() - def action_proxy(self): - result=urlfetch.fetch(self.param("url"), headers=self.request.headers) - if result.status_code == 200: - self.response.headers['Expires'] = 'Thu, 15 Apr 3010 20:00:00 GMT' - self.response.headers['Cache-Control'] = 'max-age=3600,public' - self.response.headers['Content-Type'] = result.headers['Content-Type'] - self.response.out.write(result.content) - return - - def action_getcomments(self): - key=self.param('key') - entry=Entry.get(key) - comments=Comment.all().filter("entry =",key) - - commentuser=self.request.cookies.get('comment_user', '') - if commentuser: - commentuser=commentuser.split('#@#') - else: - commentuser=['','',''] - - - vals={ - 'entry':entry, - 'comments':comments, - 'user_name':commentuser[0], - 'user_email':commentuser[1], - 'user_url':commentuser[2], - 'checknum1':random.randint(1,10), - 'checknum2':random.randint(1,10), - } - html=self.get_render('comments',vals) - - self.write(simplejson.dumps(html.decode('utf8'))) - - def action_test(self): - self.write(settings.LANGUAGE_CODE) - self.write(_("this is a test")) + def get(self,slug=None): + + try: + func=getattr(self,'action_'+slug) + if func and callable(func): + func() + else: + self.error(404) + except BaseException,e: + self.error(404) + + def post(self,slug=None): + try: + func=getattr(self,'action_'+slug) + if func and callable(func): + func() + else: + self.error(404) + except: + self.error(404) + + @ajaxonly + def action_info_login(self): + if self.login_user: + self.write(simplejson.dumps({'islogin':True, + 'isadmin':self.is_admin, + 'name': self.login_user.nickname()})) + else: + self.write(simplejson.dumps({'islogin':False})) + + #@hostonly + @cache() + def action_proxy(self): + result=urlfetch.fetch(self.param("url"), headers=self.request.headers) + if result.status_code == 200: + self.response.headers['Expires'] = 'Thu, 15 Apr 3010 20:00:00 GMT' + self.response.headers['Cache-Control'] = 'max-age=3600,public' + self.response.headers['Content-Type'] = result.headers['Content-Type'] + self.response.out.write(result.content) + return + + def action_getcomments(self): + key=self.param('key') + entry=Entry.get(key) + comments=Comment.all().filter("entry =",key) + + commentuser=self.request.cookies.get('comment_user', '') + if commentuser: + commentuser=commentuser.split('#@#') + else: + commentuser=['','',''] + + + vals= dict(entry=entry, comments=comments, user_name=commentuser[0], user_email=commentuser[1], + user_url=commentuser[2], checknum1=random.randint(1, 10), checknum2=random.randint(1, 10)) + html=self.get_render('comments',vals) + + self.write(simplejson.dumps(html.decode('utf8'))) + + def action_test(self): + self.write(settings.LANGUAGE_CODE) + self.write(_("this is a test")) class getMedia(webapp.RequestHandler): - def get(self,slug): - media=Media.get(slug) - if media: - self.response.headers['Expires'] = 'Thu, 15 Apr 3010 20:00:00 GMT' - self.response.headers['Cache-Control'] = 'max-age=3600,public' - self.response.headers['Content-Type'] = str(media.mtype) - self.response.out.write(media.bits) - a=self.request.get('a') - if a and a.lower()=='download': - media.download+=1 - media.put() + def get(self,slug): + media=Media.get(slug) + if media: + self.response.headers['Expires'] = 'Thu, 15 Apr 3010 20:00:00 GMT' + self.response.headers['Cache-Control'] = 'max-age=3600,public' + self.response.headers['Content-Type'] = str(media.mtype) + self.response.out.write(media.bits) + a=self.request.get('a') + if a and a.lower()=='download': + media.download+=1 + media.put() class CheckImg(BaseRequestHandler): - def get(self): - img = Image() - imgdata = img.create() - sess=Session(self,timeout=900) - if not sess.is_new(): - sess.invalidate() - sess=Session(self,timeout=900) - sess['code']=img.text - sess.save() - self.response.headers['Content-Type'] = "image/png" - self.response.out.write(imgdata) + def get(self): + img = Image() + imgdata = img.create() + sess=Session(self,timeout=900) + if not sess.is_new(): + sess.invalidate() + sess=Session(self,timeout=900) + sess['code']=img.text + sess.save() + self.response.headers['Content-Type'] = "image/png" + self.response.out.write(imgdata) class CheckCode(BaseRequestHandler): - def get(self): - sess=Session(self,timeout=900) - num1=random.randint(1,10) - num2=random.randint(1,10) - code="%d + %d ="%(num1,num2) - sess['code']=num1+num2 - sess.save() - #self.response.headers['Content-Type'] = "text/html" - self.response.out.write(code) + def get(self): + sess=Session(self,timeout=900) + num1=random.randint(30,50) + num2=random.randint(1,10) + code="%d - %d ="%(num1,num2) + sess['code']=num1-num2 + sess.save() + #self.response.headers['Content-Type'] = "text/html" + self.response.out.write(code) class Other(BaseRequestHandler): - def get(self,slug=None): - if not g_blog.tigger_urlmap(slug,page=self): - self.error(404) + def get(self,slug=None): + if not g_blog.tigger_urlmap(slug,page=self): + self.error(404) - def post(self,slug=None): - content=g_blog.tigger_urlmap(slug,page=self) - if content: - self.write(content) - else: - self.error(404) + def post(self,slug=None): + content=g_blog.tigger_urlmap(slug,page=self) + if content: + self.write(content) + else: + self.error(404) def getZipHandler(**args): - return ('/xheditor/(.*)',zipserve.make_zip_handler('''D:\\work\\micolog\\plugins\\xheditor\\xheditor.zip''')) + return ('/xheditor/(.*)',zipserve.make_zip_handler('''D:\\Projects\\eric-guo\\plugins\\xheditor\\xheditor.zip''')) def main(): - webapp.template.register_template_library('filter') - webapp.template.register_template_library('app.recurse') - urls= [('/media/([^/]*)/{0,1}.*',getMedia), - ('/checkimg/', CheckImg), - ('/checkcode/', CheckCode), - ('/skin',ChangeTheme), - ('/feed', FeedHandler), - ('/feed/comments',CommentsFeedHandler), - ('/sitemap', SitemapHandler), - ('/post_comment',Post_comment), - ('/page/(?P\d+)', MainPage), - ('/category/(.*)',entriesByCategory), - ('/(\d{4})/(\d{2})',archive_by_month), - ('/tag/(.*)',entriesByTag), - #('/\?p=(?P\d+)',SinglePost), - ('/', MainPage), - ('/do/(\w+)', do_action), - ('/e/(.*)',Other), - ('/([\\w\\-\\./%]+)', SinglePost), - ('.*',Error404), - ] - application = webapp.WSGIApplication(urls,debug=False) - g_blog.application=application - g_blog.plugins.register_handlerlist(application) - wsgiref.handlers.CGIHandler().run(application) + webapp.template.register_template_library('app.filter') + webapp.template.register_template_library('app.recurse') + urls= [('/media/([^/]*)/{0,1}.*',getMedia), + ('/checkimg/', CheckImg), + ('/checkcode/', CheckCode), + ('/skin',ChangeTheme), + ('/feed', FeedHandler), + ('/feed/comments',CommentsFeedHandler), + ('/sitemap', SitemapHandler), + ('/sitemap\.xml', SitemapHandler), + ('/post_comment',Post_comment), + ('/page/(?P\d+)', MainPage), + ('/category/(.*)',entriesByCategory), + ('/(\d{4})/(\d{1,2})',archive_by_month), + ('/tag/(.*)',entriesByTag), + #('/\?p=(?P\d+)',SinglePost), + ('/', MainPage), + ('/do/(\w+)', do_action), + ('/e/(.*)',Other), + ('/([\\w\\-\\./%]+)', SinglePost), + ('.*',Error404), + ] + application = webapp.WSGIApplication(urls) + g_blog.application=application + g_blog.plugins.register_handlerlist(application) + wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": - main() \ No newline at end of file + main() \ No newline at end of file diff --git a/complie_text.bat b/complie_text.bat index 5f62a20..c52f9db 100644 --- a/complie_text.bat +++ b/complie_text.bat @@ -1,2 +1,2 @@ -set path=E:\Program Files\Google\google_appengine\gettext;%path% +set path=C:\Program Files\Google\google_appengine\gettext;%path% tools\compile-messages.py -l zh_CN \ No newline at end of file diff --git a/index.yaml b/index.yaml index 9b12f4c..3fce5b3 100644 --- a/index.yaml +++ b/index.yaml @@ -1,167 +1,183 @@ -indexes: - -# AUTOGENERATED - -# This index.yaml is automatically updated whenever the dev_appserver -# detects that a new type of query is run. If you want to manage the -# index.yaml file manually, remove the above marker line (the line -# saying "# AUTOGENERATED"). If you want to manage some indexes -# manually, move them above the marker line. The index.yaml file is -# automatically uploaded to the admin console when you next deploy -# your application using appcfg.py. - -- kind: Archive - properties: - - name: year - direction: desc - - name: month - direction: desc - -- kind: Comment - properties: - - name: author - - name: date - direction: desc - -- kind: Comment - properties: - - name: ctype - - name: date - direction: desc - -- kind: Comment - properties: - - name: ctype - - name: entry - - name: date - -- kind: Comment - properties: - - name: ctype - - name: entry - - name: date - direction: desc - -- kind: Comment - properties: - - name: email - - name: date - direction: desc - -- kind: Comment - properties: - - name: entry - - name: date - -- kind: Comment - properties: - - name: entry - - name: date - direction: desc - -- kind: Comment - properties: - - name: ip - - name: date - direction: desc - -- kind: Entry - properties: - - name: categorie_keys - - name: date - direction: desc - -- kind: Entry - properties: - - name: categorie_keys - - name: published - - name: date - direction: desc - -- kind: Entry - properties: - - name: entry_parent - - name: entrytype - - name: published - - name: menu_order - -- kind: Entry - properties: - - name: entrytype - - name: date - direction: desc - -- kind: Entry - properties: - - name: entrytype - - name: post_id - -- kind: Entry - properties: - - name: entrytype - - name: post_id - direction: desc - -- kind: Entry - properties: - - name: entrytype - - name: published - - name: date - -- kind: Entry - properties: - - name: entrytype - - name: published - - name: date - direction: desc - -- kind: Entry - properties: - - name: entrytype - - name: published - - name: readtimes - direction: desc - -- kind: Entry - properties: - - name: entrytype - - name: published - - name: slug - - name: date - -- kind: Entry - properties: - - name: published - - name: date - direction: desc - -- kind: Entry - properties: - - name: published - - name: tags - - name: date - direction: desc - -- kind: Entry - properties: - - name: published - - name: tags - - name: post_id - direction: desc - -- kind: Entry - properties: - - name: tags - - name: date - direction: desc - -- kind: Entry - properties: - - name: tags - - name: post_id - direction: desc - -- kind: Media - properties: - - name: mtype - - name: date +indexes: + +# AUTOGENERATED + +# This index.yaml is automatically updated whenever the dev_appserver +# detects that a new type of query is run. If you want to manage the +# index.yaml file manually, remove the above marker line (the line +# saying "# AUTOGENERATED"). If you want to manage some indexes +# manually, move them above the marker line. The index.yaml file is +# automatically uploaded to the admin console when you next deploy +# your application using appcfg.py. + +- kind: Archive + properties: + - name: year + direction: desc + - name: month + direction: desc + +- kind: Comment + properties: + - name: author + - name: date + direction: desc + +- kind: Comment + properties: + - name: ctype + - name: date + direction: desc + +- kind: Comment + properties: + - name: ctype + - name: entry + - name: date + +- kind: Comment + properties: + - name: ctype + - name: entry + - name: date + direction: desc + +- kind: Comment + properties: + - name: email + - name: date + +- kind: Comment + properties: + - name: email + - name: date + direction: desc + +- kind: Comment + properties: + - name: entry + - name: date + +- kind: Comment + properties: + - name: entry + - name: date + direction: desc + +- kind: Comment + properties: + - name: ip + - name: date + direction: desc + +- kind: Entry + properties: + - name: categorie_keys + - name: date + direction: desc + +- kind: Entry + properties: + - name: categorie_keys + - name: published + - name: date + direction: desc + +- kind: Entry + properties: + - name: entry_parent + - name: entrytype + - name: published + - name: menu_order + +- kind: Entry + properties: + - name: entrytype + - name: date + direction: desc + +- kind: Entry + properties: + - name: entrytype + - name: post_id + +- kind: Entry + properties: + - name: entrytype + - name: post_id + direction: desc + +- kind: Entry + properties: + - name: entrytype + - name: published + - name: date + direction: desc + +- kind: Entry + properties: + - name: entrytype + - name: published + - name: readtimes + direction: desc + +- kind: Entry + properties: + - name: entrytype + - name: published + - name: slug + - name: date + +- kind: Entry + properties: + - name: entrytype + - name: published + - name: sticky + direction: desc + - name: date + +- kind: Entry + properties: + - name: entrytype + - name: published + - name: sticky + direction: desc + - name: date + direction: desc + +- kind: Entry + properties: + - name: published + - name: date + direction: desc + +- kind: Entry + properties: + - name: published + - name: tags + - name: date + direction: desc + +- kind: Entry + properties: + - name: published + - name: tags + - name: post_id + direction: desc + +- kind: Entry + properties: + - name: tags + - name: date + direction: desc + +- kind: Entry + properties: + - name: tags + - name: post_id + direction: desc + +- kind: Media + properties: + - name: mtype + - name: date diff --git a/locale/en_US/LC_MESSAGES/django.mo b/locale/en_US/LC_MESSAGES/django.mo index 31d426d..162dc11 100644 Binary files a/locale/en_US/LC_MESSAGES/django.mo and b/locale/en_US/LC_MESSAGES/django.mo differ diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index 456797b..ca06f21 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -1,973 +1,1032 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-10-10 17:01+0800\n" -"PO-Revision-Date: 2010-02-01 11:12+0800\n" -"Last-Translator: xuming \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: .\admin.py:95 .\admin.py:97 .\admin.py:106 .\admin.py:108 -msgid "This operate has not defined!" -msgstr "" - -#: .\admin.py:117 -msgid "\"Cache cleared successful\"" -msgstr "" - -#: .\admin.py:126 -msgid "\"All comments updated\"" -msgstr "" - -#: .\admin.py:132 -msgid "\"All comments number Updates.\"" -msgstr "" - -#: .\admin.py:154 -msgid "\"Link formated succeed\"" -msgstr "" - -#: .\admin.py:156 -msgid "\"Please input url format.\"" -msgstr "" - -#: .\admin.py:168 -msgid "\"Init has succeed.\"" -msgstr "" - -#: .\admin.py:182 -msgid "\"All tags for entry have been updated.\"" -msgstr "" - -#: .\admin.py:207 -msgid "\"All entries have been updated.\"" -msgstr "" - -#: .\admin.py:470 -msgid "Please input title and content." -msgstr "" - -#: .\admin.py:501 .\admin.py:544 -#, python-format -msgid "Saved ok. View it now!" -msgstr "" - -#: .\admin.py:503 .\admin.py:546 -msgid "Saved ok." -msgstr "" - -#: .\admin.py:552 -msgid "Error:Entry cant been saved." -msgstr "" - -#: .\admin.py:723 -msgid "Please input name and url." -msgstr "" - -#: .\admin.py:743 -msgid "Error:Link cant been saved." -msgstr "" - -#: .\admin.py:792 .\admin.py:807 -msgid "Please input name and slug." -msgstr "" - -#: .\admin.py:811 -msgid "A circle declaration found." -msgstr "" - -#: .\admin.py:891 -msgid "Please input dispname and email." -msgstr "" - -#: .\admin.py:913 -msgid "Error:Author cant been saved." -msgstr "" - -#: .\admin.py:948 .\admin.py:969 -#, python-format -msgid "" -"
    Plugin '%(name)s' havn't actived!

    " -msgstr "" - -#: .\base.py:133 .\tools\pingback.py:55 -msgid "source URL does not exist" -msgstr "" - -#: .\base.py:134 .\tools\pingback.py:56 -msgid "The source URL does not contain a link to the target URL" -msgstr "" - -#: .\base.py:135 .\tools\pingback.py:57 -msgid "The specified target URL does not exist" -msgstr "" - -#: .\base.py:136 .\tools\pingback.py:58 -msgid "The specified target URL cannot be used as a target" -msgstr "" - -#: .\base.py:137 .\tools\pingback.py:59 -msgid "The pingback has already been registered" -msgstr "" - -#: .\base.py:138 .\tools\pingback.py:60 -msgid "Access Denied" -msgstr "" - -#: .\base.py:175 .\tools\pingback.py:97 -msgid "An unknown server error (%s) occurred" -msgstr "" - -#: .\blog.py:493 .\blog.py:495 .\blog.py:499 .\blog.py:501 -msgid "Your check code is invalid ." -msgstr "" - -#: .\blog.py:512 .\blog.py:514 -msgid "Please input name, email and comment ." -msgstr "" - -#: .\blog.py:560 -msgid "Comment not allowed." -msgstr "" - -#: .\blog.py:562 -msgid "Comment not allowed ." -msgstr "" - -#: .\blog.py:643 -msgid "this is a test" -msgstr "" - -#: .\model.py:434 -msgid "..more" -msgstr "" - -#: .\model.py:901 -msgid "Hello world!" -msgstr "" - -#: .\model.py:902 -msgid "" -"

    Welcome to micolog. This is your first post. Edit or delete it, then " -"start blogging!

    " -msgstr "" - -#: .\model.py:904 -msgid "Xuming's blog" -msgstr "" - -#: .\plugins\sys_plugin\setup.html.py:4 .\plugins\sys_plugin\setup2.html.py:31 -#: .\views\admin\setup.html.py:122 -msgid "Enable comment notify by mail:" -msgstr "" - -#: .\plugins\sys_plugin\setup.html.py:7 .\plugins\sys_plugin\setup2.html.py:34 -#: -msgid "Message to me:" -msgstr "" - -#: .\plugins\sys_plugin\setup.html.py:11 -#: .\plugins\sys_plugin\setup2.html.py:38 -msgid "Message to commenter:" -msgstr "" - -#: .\plugins\sys_plugin\setup2.html.py:8 .\views\admin\import.html.py:32 -#: .\views\admin\setup.html.py:4 .\views\admin\setup_base.html.py:8 -#: .\views\admin\sitemap.html.py:4 .\views\admin\tools.html.py:76 -msgid "Basic" -msgstr "" - -#: .\plugins\sys_plugin\setup2.html.py:9 .\views\admin\import.html.py:33 -#: .\views\admin\setup.html.py:5 .\views\admin\setup_base.html.py:9 -#: .\views\admin\sitemap.html.py:5 .\views\admin\tools.html.py:77 -msgid "Import/Export" -msgstr "" - -#: .\plugins\sys_plugin\setup2.html.py:10 .\views\admin\import.html.py:34 -#: .\views\admin\setup.html.py:6 .\views\admin\setup_base.html.py:10 -#: .\views\admin\sitemap.html.py:6 .\views\admin\tools.html.py:78 -msgid "Sitemap" -msgstr "" - -#: .\plugins\sys_plugin\setup2.html.py:11 .\views\admin\import.html.py:35 -#: .\views\admin\setup.html.py:7 .\views\admin\setup_base.html.py:11 -#: .\views\admin\sitemap.html.py:7 .\views\admin\tools.html.py:79 -#: .\views\admin\tools.html.py:86 -msgid "Tools" -msgstr "" - -#: .\plugins\sys_plugin\sys_plugin.py:70 -msgid "Notify" -msgstr "" - -#: .\plugins\wordpress\wpimport.html.py:56 -msgid "Please select a file which exported from wordpress to import!" -msgstr "" - -#: .\plugins\wordpress\wpimport.html.py:62 -msgid "Wordpress export file (WXR file):" -msgstr "" - -#: .\plugins\wordpress\wpimport.html.py:71 .\views\admin\import.html.py:59 -msgid "Import" -msgstr "" - -#: .\views\admin\author.html.py:11 -msgid "Edit Author" -msgstr "" - -#: .\views\admin\author.html.py:11 -msgid "Add Author" -msgstr "" - -#: .\views\admin\author.html.py:28 .\views\admin\category.html.py:28 -#: .\views\admin\entry.html.py:188 .\views\admin\link.html.py:28 -msgid "Related" -msgstr "" - -#: .\views\admin\author.html.py:31 -msgid "Manage All Authors" -msgstr "" - -#: .\views\admin\author.html.py:40 -msgid "Display Name" -msgstr "" - -#: .\views\admin\author.html.py:43 -msgid "The name is used to display." -msgstr "" - -#: .\views\admin\authors.html.py:7 -msgid "Manage Authors" -msgstr "" - -#: .\views\admin\authors.html.py:7 .\views\admin\categories.html.py:7 -#: .\views\admin\links.html.py:7 .\views\admin\pages.html.py:7 -#: .\views\admin\posts.html.py:7 -msgid "add new" -msgstr "" - -#: .\views\admin\authors.html.py:8 -msgid "All Authors" -msgstr "" - -#: .\views\admin\authors.html.py:14 .\views\admin\categories.html.py:14 -#: .\views\admin\comments.html.py:26 .\views\admin\filemanager.html.py:53 -#: .\views\admin\links.html.py:12 .\views\admin\pages.html.py:14 -#: .\views\admin\posts.html.py:15 -msgid "Delete" -msgstr "" - -#: .\views\admin\authors.html.py:18 .\views\admin\categories.html.py:18 -#: .\views\admin\comments.html.py:30 .\views\admin\filemanager.html.py:57 -#: .\views\admin\pages.html.py:18 .\views\admin\posts.html.py:20 -msgid "« prev" -msgstr "" - -#: .\views\admin\authors.html.py:22 .\views\admin\categories.html.py:22 -#: .\views\admin\comments.html.py:34 .\views\admin\filemanager.html.py:61 -#: .\views\admin\pages.html.py:22 .\views\admin\posts.html.py:24 -msgid "next »" -msgstr "" - -#: .\views\admin\authors.html.py:23 .\views\admin\categories.html.py:24 -#: .\views\admin\comments.html.py:35 .\views\admin\filemanager.html.py:62 -#: .\views\admin\pages.html.py:23 .\views\admin\posts.html.py:26 -msgid " Page %(pindex)s" -msgstr "" - -#: .\views\admin\authors.html.py:34 .\views\admin\categories.html.py:37 -#: .\views\admin\category.html.py:40 .\views\admin\link.html.py:40 -#: .\views\admin\links.html.py:22 -msgid "Name" -msgstr "" - -#: .\views\admin\authors.html.py:34 -msgid "IsAdmin" -msgstr "" - -#: .\views\admin\base.html.py:31 -msgid "Micolog Dashboard" -msgstr "" - -#: .\views\admin\base.html.py:31 -msgid "Logout" -msgstr "" - -#: .\views\admin\base.html.py:60 -msgid "Write" -msgstr "" - -#: .\views\admin\base.html.py:61 -msgid "Posts" -msgstr "" - -#: .\views\admin\base.html.py:62 -msgid "Pages" -msgstr "" - -#: .\views\admin\base.html.py:63 -msgid "Links" -msgstr "" - -#: .\views\admin\base.html.py:64 -msgid "Categories" -msgstr "" - -#: .\views\admin\base.html.py:65 -msgid "Comments" -msgstr "" - -#: .\views\admin\base.html.py:66 .\views\admin\entry.html.py:108 -#: .\views\admin\upload.html.py:5 -msgid "Files" -msgstr "" - -#: .\views\admin\base.html.py:69 -msgid "Configuration" -msgstr "" - -#: .\views\admin\base.html.py:70 -msgid "Plugins" -msgstr "" - -#: .\views\admin\base.html.py:72 -msgid "Cache" -msgstr "" - -#: .\views\admin\base.html.py:73 -msgid "Authors" -msgstr "" - -#: .\views\admin\base.html.py:78 -msgid "help" -msgstr "" - -#: .\views\admin\categories.html.py:7 -msgid "Manage Categories" -msgstr "" - -#: .\views\admin\categories.html.py:8 -msgid "All Categories" -msgstr "" - -#: .\views\admin\categories.html.py:37 .\views\admin\category.html.py:47 -#: .\views\admin\entry.html.py:267 -msgid "Slug" -msgstr "" - -#: .\views\admin\categories.html.py:37 -msgid "Parent" -msgstr "" - -#: .\views\admin\category.html.py:11 -msgid "Edit category" -msgstr "" - -#: .\views\admin\category.html.py:11 -msgid "Add category" -msgstr "" - -#: .\views\admin\category.html.py:24 .\views\admin\entry.html.py:169 -#: .\views\admin\link.html.py:24 -msgid "Save" -msgstr "" - -#: .\views\admin\category.html.py:31 -msgid "Manage All Categories" -msgstr "" - -#: .\views\admin\category.html.py:43 -msgid "" -"The name is used to identify the category almost everywhere, for example " -"under the post or in the category widget." -msgstr "" - -#: .\views\admin\category.html.py:50 -msgid "" -"The 'slug' is the URL-friendly version of the name. It is usually all " -"lowercase and contains only letters, numbers, and hyphens." -msgstr "" - -#: .\views\admin\category.html.py:54 -msgid "Parent Category" -msgstr "" - -#: .\views\admin\category.html.py:57 -msgid "None" -msgstr "" - -#: .\views\admin\comments.html.py:7 -msgid "Manage Comments" -msgstr "" - -#: .\views\admin\comments.html.py:8 -msgid "All Comments" -msgstr "" - -#: .\views\admin\comments.html.py:11 -msgid "Filter:" -msgstr "" - -#: .\views\admin\comments.html.py:13 .\views\admin\comments.html.py:46 -#: .\views\admin\plugins.html.py:10 .\views\admin\posts.html.py:37 -msgid "Author" -msgstr "" - -#: .\views\admin\comments.html.py:18 -msgid "Do" -msgstr "Do it" - -#: .\views\admin\comments.html.py:46 .\views\admin\pages.html.py:34 -#: .\views\admin\posts.html.py:37 -msgid "Date" -msgstr "" - -#: .\views\admin\comments.html.py:46 -msgid "Content" -msgstr "" - -#: .\views\admin\comments.html.py:46 -msgid "Email" -msgstr "" - -#: .\views\admin\comments.html.py:46 -msgid "IP" -msgstr "" - -#: .\views\admin\comments.html.py:46 -msgid "Entry" -msgstr "" - -#: .\views\admin\entry.html.py:131 -msgid "New Post" -msgstr "" - -#: .\views\admin\entry.html.py:131 -msgid "New Page" -msgstr "" - -#: .\views\admin\entry.html.py:143 -msgid "Write Post" -msgstr "" - -#: .\views\admin\entry.html.py:145 -msgid "Write Page" -msgstr "" - -#: .\views\admin\entry.html.py:155 -msgid "Category" -msgstr "" - -#: .\views\admin\entry.html.py:171 -msgid "UnPublish" -msgstr "" - -#: .\views\admin\entry.html.py:173 -msgid "Publish" -msgstr "" - -#: .\views\admin\entry.html.py:180 -msgid "Sticky it." -msgstr "" - -#: .\views\admin\entry.html.py:182 .\views\admin\posts.html.py:37 -msgid "allow comment" -msgstr "" - -#: .\views\admin\entry.html.py:184 -msgid "allow trackback" -msgstr "" - -#: .\views\admin\entry.html.py:191 -msgid "Manage All Posts" -msgstr "" - -#: .\views\admin\entry.html.py:192 -msgid "Manage All Pages" -msgstr "" - -#: .\views\admin\entry.html.py:203 -msgid "Use as external page" -msgstr "" - -#: .\views\admin\entry.html.py:207 .\views\admin\pages.html.py:34 -#: .\views\admin\posts.html.py:37 -msgid "Title" -msgstr "" - -#: .\views\admin\entry.html.py:220 -msgid "External page address" -msgstr "" - -#: .\views\admin\entry.html.py:224 -msgid "Target" -msgstr "" - -#: .\views\admin\entry.html.py:237 -msgid "Post" -msgstr "" - -#: .\views\admin\entry.html.py:239 -msgid "Page" -msgstr "" - -#: .\views\admin\entry.html.py:275 -msgid "Tag" -msgstr "" - -#: .\views\admin\entry.html.py:285 -msgid "Password" -msgstr "" - -#: .\views\admin\entry.html.py:295 -msgid "Options" -msgstr "" - -#: .\views\admin\entry.html.py:298 -msgid "Page level:" -msgstr "" - -#: .\views\admin\entry.html.py:300 -msgid "Page order:" -msgstr "" - -#: .\views\admin\entry.html.py:310 -msgid "Excerpt" -msgstr "" - -#: .\views\admin\filemanager.html.py:31 -msgid "Upload a file" -msgstr "" - -#: .\views\admin\filemanager.html.py:37 -msgid "Upload" -msgstr "" - -#: .\views\admin\filemanager.html.py:46 -msgid "Manage Files" -msgstr "" - -#: .\views\admin\filemanager.html.py:47 -msgid "All Files" -msgstr "" - -#: .\views\admin\filemanager.html.py:72 -msgid "File Name" -msgstr "" - -#: .\views\admin\filemanager.html.py:73 .\views\admin\upload.html.py:98 -msgid "Size" -msgstr "" - -#: .\views\admin\filemanager.html.py:74 -msgid "Upload Date" -msgstr "" - -#: .\views\admin\filemanager.html.py:76 -msgid "Url" -msgstr "" - -#: .\views\admin\filemanager.html.py:77 -msgid "Download" -msgstr "" - -#: .\views\admin\import.html.py:41 -msgid "Export/Import for wordpress" -msgstr "" - -#: .\views\admin\import.html.py:42 .\views\admin\import.html.py:57 -msgid "Export" -msgstr "" - -#: .\views\admin\import.html.py:43 -msgid "Export as WordPress eXtended RSS file" -msgstr "" - -#: .\views\admin\import.html.py:45 -msgid "Export from Date:" -msgstr "" - -#: .\views\admin\import.html.py:46 -msgid "to Date:" -msgstr "" - -#: .\views\admin\import.html.py:46 -msgid "'s posts" -msgstr "" - -#: .\views\admin\import.html.py:56 -msgid "Export all posts" -msgstr "" - -#: .\views\admin\link.html.py:11 -msgid "Edit link" -msgstr "" - -#: .\views\admin\link.html.py:11 -msgid "Add Link" -msgstr "" - -#: .\views\admin\link.html.py:31 -msgid "Manage All Links" -msgstr "" - -#: .\views\admin\link.html.py:43 -msgid "Example: Nifty blogging software" -msgstr "" - -#: .\views\admin\link.html.py:47 -msgid "Web Address" -msgstr "" - -#: .\views\admin\link.html.py:50 -msgid "" -"Example: http://xuming.net - don't forget the http://" -msgstr "" - -#: .\views\admin\link.html.py:54 .\views\admin\links.html.py:22 -msgid "LinkComment" -msgstr "" - -#: .\views\admin\link.html.py:57 -msgid "Example: Xuming's Blog" -msgstr "" - -#: .\views\admin\links.html.py:7 -msgid "Manage Links" -msgstr "" - -#: .\views\admin\links.html.py:22 -msgid "URL" -msgstr "" - -#: .\views\admin\pages.html.py:7 -msgid "Manage Pages" -msgstr "" - -#: .\views\admin\pages.html.py:8 -msgid "All Pages" -msgstr "" - -#: .\views\admin\pages.html.py:34 .\views\admin\posts.html.py:37 -msgid "Status" -msgstr "" - -#: .\views\admin\pages.html.py:40 .\views\admin\posts.html.py:43 -msgid "edit" -msgstr "" - -#: .\views\admin\pages.html.py:42 .\views\admin\posts.html.py:46 -msgid "Published" -msgstr "" - -#: .\views\admin\pages.html.py:42 .\views\admin\posts.html.py:46 -msgid "Unpublished" -msgstr "" - -#: .\views\admin\plugins.html.py:5 -msgid "Manage Plugins" -msgstr "" - -#: .\views\admin\plugins.html.py:10 -msgid "Plugin" -msgstr "" - -#: .\views\admin\plugins.html.py:10 -msgid "Version" -msgstr "" - -#: .\views\admin\plugins.html.py:10 -msgid "Description" -msgstr "" - -#: .\views\admin\plugins.html.py:10 -msgid "Active Status" -msgstr "" - -#: .\views\admin\plugins.html.py:20 -msgid "Deactivate" -msgstr "" - -#: .\views\admin\plugins.html.py:23 -msgid "Activate" -msgstr "" - -#: .\views\admin\posts.html.py:7 -msgid "Manage Posts" -msgstr "" - -#: .\views\admin\posts.html.py:9 -msgid "All Posts" -msgstr "" - -#: .\views\admin\setup.html.py:14 -msgid "Setup and Configuraiton for this Blog" -msgstr "" - -#: .\views\admin\setup.html.py:18 -msgid "Blog Title:" -msgstr "" - -#: .\views\admin\setup.html.py:24 -msgid "Sub Title:" -msgstr "" - -#: .\views\admin\setup.html.py:27 -msgid "In a few words, explain what this blog is about." -msgstr "" - -#: .\views\admin\setup.html.py:31 -msgid "Blog notice:" -msgstr "" - -#: .\views\admin\setup.html.py:37 -msgid "Default Link Structure:" -msgstr "" - -#: .\views\admin\setup.html.py:40 .\views\admin\setup.html.py:47 -msgid "Allowed variants: " -msgstr "" - -#: .\views\admin\setup.html.py:44 -msgid "Permalink Structure:" -msgstr "" - -#: .\views\admin\setup.html.py:51 -msgid "Domain:" -msgstr "" - -#: .\views\admin\setup.html.py:57 -msgid "Micolog Address(URL):" -msgstr "" - -#: .\views\admin\setup.html.py:63 -msgid "RSS Feed URL:" -msgstr "" - -#: .\views\admin\setup.html.py:68 -msgid "Time delta:(Hours)" -msgstr "" - -#: .\views\admin\setup.html.py:75 -msgid "Theme:" -msgstr "" - -#: .\views\admin\setup.html.py:83 -msgid "Get more themes" -msgstr "" - -#: .\views\admin\setup.html.py:87 -msgid "Posts per page:" -msgstr "" - -#: .\views\admin\setup.html.py:92 -msgid "Comments order:" -msgstr "" - -#: .\views\admin\setup.html.py:94 -msgid "ASC" -msgstr "" - -#: .\views\admin\setup.html.py:95 -msgid "DESC" -msgstr "" - -#: .\views\admin\setup.html.py:99 -msgid "Comment Check Type:" -msgstr "" - -#: .\views\admin\setup.html.py:102 -msgid "Arithmetic" -msgstr "" - -#: .\views\admin\setup.html.py:103 -msgid "Graph" -msgstr "" - -#: .\views\admin\setup.html.py:104 -msgid "Client Arithmetic" -msgstr "" - -#: .\views\admin\setup.html.py:108 -msgid "Comments per page:" -msgstr "" - -#: .\views\admin\setup.html.py:114 -msgid "Avatar icon style:" -msgstr "" - -#: .\views\admin\setup.html.py:116 -msgid "Default" -msgstr "" - -#: .\views\admin\setup.html.py:117 -msgid "Identicon" -msgstr "" - -#: .\views\admin\setup.html.py:127 -msgid "Enable memcache:" -msgstr "" - -#: .\views\admin\setup.html.py:132 -msgid "Allow Trackback:" -msgstr "" - -#: .\views\admin\setup.html.py:138 -msgid "Allow Pingback:" -msgstr "" - -#: .\views\admin\setup.html.py:143 -msgid "Xmlrpc setting" -msgstr "" - -#: .\views\admin\setup.html.py:144 -msgid "" -"Setup the username and password for RPC call. These will be used by client " -"tools (e.g. Windows live writer). " -msgstr "" - -#: .\views\admin\setup.html.py:147 -msgid "User name:" -msgstr "" - -#: .\views\admin\setup.html.py:153 -msgid "Password:" -msgstr "" - -#: .\views\admin\setup.html.py:160 .\views\admin\sitemap.html.py:49 -msgid "Save Changes" -msgstr "" - -#: .\views\admin\sitemap.html.py:16 -msgid "Sitemap Setup" -msgstr "" - -#: .\views\admin\sitemap.html.py:20 -msgid "Entries in sitemap:" -msgstr "" - -#: .\views\admin\sitemap.html.py:28 -msgid "Include category:" -msgstr "" - -#: .\views\admin\sitemap.html.py:34 -msgid "Include tag:" -msgstr "" - -#: .\views\admin\sitemap.html.py:39 -msgid "Pinging Search Engine (Google):" -msgstr "" - -#: .\views\admin\status.html.py:23 -msgid "Cache status" -msgstr "" - -#: .\views\admin\status.html.py:25 .\views\admin\status.html.py:28 -msgid "Clear cache" -msgstr "" - -#: .\views\admin\status.html.py:28 -msgid "Clear cache data." -msgstr "" - -#: .\views\admin\status.html.py:31 -msgid "Environ" -msgstr "" - -#: .\views\admin\tools.html.py:88 -msgid "Careless use of it will be dangerous!" -msgstr "" - -#: .\views\admin\tools.html.py:91 .\views\admin\tools.html.py:97 -#: .\views\admin\tools.html.py:104 .\views\admin\tools.html.py:111 -#: .\views\admin\tools.html.py:118 .\views\admin\tools.html.py:129 -msgid "Run" -msgstr "" - -#: .\views\admin\tools.html.py:92 -msgid "Update tags" -msgstr "" - -#: .\views\admin\tools.html.py:93 -msgid "It will refresh all tags for each entry." -msgstr "" - -#: .\views\admin\tools.html.py:99 -msgid "Init datas" -msgstr "" - -#: .\views\admin\tools.html.py:100 -msgid "It will delete all posts, pages and comments." -msgstr "" - -#: .\views\admin\tools.html.py:106 -msgid "Update comments" -msgstr "" - -#: .\views\admin\tools.html.py:107 -msgid "It will refresh the number of comments for each entry." -msgstr "" - -#: .\views\admin\tools.html.py:113 -msgid "Update comment number" -msgstr "" - -#: .\views\admin\tools.html.py:114 -msgid "It will refresh the number for each comment." -msgstr "" - -#: .\views\admin\tools.html.py:120 -msgid "Rebuild URL" -msgstr "" - -#: .\views\admin\tools.html.py:121 -msgid "The URL of all entries (post or page) will be rebuild." -msgstr "" - -#: .\views\admin\tools.html.py:123 -msgid "Permalink" -msgstr "" - -#: .\views\admin\tools.html.py:125 -msgid "Available parameters:" -msgstr "" - -#: .\views\admin\tools.html.py:132 -msgid "Update archives" -msgstr "" - -#: .\views\admin\tools.html.py:133 -msgid "It will update the monthyear of entries." -msgstr "" - -#: .\views\admin\upload.html.py:29 -msgid "Error: invalid file extension" -msgstr "" - -#: .\views\admin\upload.html.py:53 .\views\admin\upload.html.py:101 -msgid "Add" -msgstr "" - -#: .\views\admin\upload.html.py:92 -msgid "New file" -msgstr "" - -#: .\views\admin\upload.html.py:98 -msgid "Filename" -msgstr "" - -#: .\views\admin\upload.html.py:98 -msgid "Operater" -msgstr "" +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-10-10 17:01+0800\n" +"PO-Revision-Date: 2011-10-30 19:20+0800\n" +"Last-Translator: Eric Guo \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .\admin.py:95 +#: .\admin.py:97 +#: .\admin.py:106 +#: .\admin.py:108 +msgid "This operate has not defined!" +msgstr "" + +#: .\admin.py:117 +msgid "\"Cache cleared successful\"" +msgstr "" + +#: .\admin.py:126 +msgid "\"All comments updated\"" +msgstr "" + +#: .\admin.py:132 +msgid "\"All comments number Updates.\"" +msgstr "" + +#: .\admin.py:154 +msgid "\"Link formated succeed\"" +msgstr "" + +#: .\admin.py:156 +msgid "\"Please input url format.\"" +msgstr "" + +#: .\admin.py:168 +msgid "\"Init has succeed.\"" +msgstr "" + +#: .\admin.py:182 +msgid "\"All tags for entry have been updated.\"" +msgstr "" + +#: .\admin.py:207 +msgid "\"All entries have been updated.\"" +msgstr "" + +#: .\admin.py:470 +msgid "Please input title and content." +msgstr "" + +#: .\admin.py:501 +#: .\admin.py:544 +#, python-format +msgid "Saved ok. View it now!" +msgstr "" + +#: .\admin.py:503 +#: .\admin.py:546 +msgid "Saved ok." +msgstr "" + +#: .\admin.py:552 +msgid "Error:Entry cant been saved." +msgstr "" + +#: .\admin.py:723 +msgid "Please input name and url." +msgstr "" + +#: .\admin.py:743 +msgid "Error:Link cant been saved." +msgstr "" + +#: .\admin.py:792 +#: .\admin.py:807 +msgid "Please input name and slug." +msgstr "" + +#: .\admin.py:811 +msgid "A circle declaration found." +msgstr "" + +#: .\admin.py:891 +msgid "Please input dispname and email." +msgstr "" + +#: .\admin.py:913 +msgid "Error:Author cant been saved." +msgstr "" + +#: .\admin.py:948 +#: .\admin.py:969 +#, python-format +msgid "
    Plugin '%(name)s' havn't actived!

    " +msgstr "" + +#: .\base.py:133 +#: .\tools\pingback.py:55 +msgid "source URL does not exist" +msgstr "" + +#: .\base.py:134 +#: .\tools\pingback.py:56 +msgid "The source URL does not contain a link to the target URL" +msgstr "" + +#: .\base.py:135 +#: .\tools\pingback.py:57 +msgid "The specified target URL does not exist" +msgstr "" + +#: .\base.py:136 +#: .\tools\pingback.py:58 +msgid "The specified target URL cannot be used as a target" +msgstr "" + +#: .\base.py:137 +#: .\tools\pingback.py:59 +msgid "The pingback has already been registered" +msgstr "" + +#: .\base.py:138 +#: .\tools\pingback.py:60 +msgid "Access Denied" +msgstr "" + +#: .\base.py:175 +#: .\tools\pingback.py:97 +msgid "An unknown server error (%s) occurred" +msgstr "" + +#: .\blog.py:493 +#: .\blog.py:495 +#: .\blog.py:499 +#: .\blog.py:501 +msgid "Your check code is invalid ." +msgstr "" + +#: .\blog.py:512 +#: .\blog.py:514 +msgid "Please input name, email and comment ." +msgstr "" + +#: .\blog.py:560 +msgid "Comment not allowed." +msgstr "" + +#: .\blog.py:562 +msgid "Comment not allowed ." +msgstr "" + +#: .\blog.py:643 +msgid "this is a test" +msgstr "" + +#: .\model.py:434 +msgid "..more" +msgstr "" + +#: .\model.py:901 +msgid "Hello world!" +msgstr "" + +#: .\model.py:902 +msgid "

    Welcome to micolog. This is your first post. Edit or delete it, then start blogging!

    " +msgstr "" + +#: .\model.py:904 +msgid "Xuming's blog" +msgstr "" + +#: .\plugins\sys_plugin\setup.html.py:4 +#: .\plugins\sys_plugin\setup2.html.py:31 +msgid "Enable comment notify by mail:" +msgstr "" + +#: .\plugins\sys_plugin\setup.html.py:7 +#: .\plugins\sys_plugin\setup2.html.py:34 +msgid "Message to me:" +msgstr "" + +#: .\plugins\sys_plugin\setup.html.py:11 +#: .\plugins\sys_plugin\setup2.html.py:38 +msgid "Message to commenter:" +msgstr "" + +#: .\plugins\sys_plugin\setup2.html.py:8 +#: .\views\admin\import.html.py:32 +#: .\views\admin\setup.html.py:4 +#: .\views\admin\setup_base.html.py:8 +#: .\views\admin\sitemap.html.py:4 +#: .\views\admin\tools.html.py:76 +msgid "Basic" +msgstr "" + +#: .\plugins\sys_plugin\setup2.html.py:9 +#: .\views\admin\import.html.py:33 +#: .\views\admin\setup.html.py:5 +#: .\views\admin\setup_base.html.py:9 +#: .\views\admin\sitemap.html.py:5 +#: .\views\admin\tools.html.py:77 +msgid "Import/Export" +msgstr "" + +#: .\plugins\sys_plugin\setup2.html.py:10 +#: .\views\admin\import.html.py:34 +#: .\views\admin\setup.html.py:6 +#: .\views\admin\setup_base.html.py:10 +#: .\views\admin\sitemap.html.py:6 +#: .\views\admin\tools.html.py:78 +msgid "Sitemap" +msgstr "" + +#: .\plugins\sys_plugin\setup2.html.py:11 +#: .\views\admin\import.html.py:35 +#: .\views\admin\setup.html.py:7 +#: .\views\admin\setup_base.html.py:11 +#: .\views\admin\sitemap.html.py:7 +#: .\views\admin\tools.html.py:79 +#: .\views\admin\tools.html.py:86 +msgid "Tools" +msgstr "" + +#: .\plugins\sys_plugin\sys_plugin.py:70 +msgid "Notify" +msgstr "" + +#: .\plugins\wordpress\wpimport.html.py:56 +msgid "Please select a file which exported from wordpress to import!" +msgstr "" + +#: .\plugins\wordpress\wpimport.html.py:62 +msgid "Wordpress export file (WXR file):" +msgstr "" + +#: .\plugins\wordpress\wpimport.html.py:71 +#: .\views\admin\import.html.py:59 +msgid "Import" +msgstr "" + +#: .\views\admin\author.html.py:11 +msgid "Edit Author" +msgstr "" + +#: .\views\admin\author.html.py:11 +msgid "Add Author" +msgstr "" + +#: .\views\admin\author.html.py:28 +#: .\views\admin\category.html.py:28 +#: .\views\admin\entry.html.py:188 +#: .\views\admin\link.html.py:28 +msgid "Related" +msgstr "" + +#: .\views\admin\author.html.py:31 +msgid "Manage All Authors" +msgstr "" + +#: .\views\admin\author.html.py:40 +msgid "Display Name" +msgstr "" + +#: .\views\admin\author.html.py:43 +msgid "The name is used to display." +msgstr "" + +#: .\views\admin\authors.html.py:7 +msgid "Manage Authors" +msgstr "" + +#: .\views\admin\authors.html.py:7 +#: .\views\admin\categories.html.py:7 +#: .\views\admin\links.html.py:7 +#: .\views\admin\pages.html.py:7 +#: .\views\admin\posts.html.py:7 +msgid "add new" +msgstr "" + +#: .\views\admin\authors.html.py:8 +msgid "All Authors" +msgstr "" + +#: .\views\admin\authors.html.py:14 +#: .\views\admin\categories.html.py:14 +#: .\views\admin\comments.html.py:26 +#: .\views\admin\filemanager.html.py:53 +#: .\views\admin\links.html.py:12 +#: .\views\admin\pages.html.py:14 +#: .\views\admin\posts.html.py:15 +msgid "Delete" +msgstr "" + +#: .\views\admin\authors.html.py:18 +#: .\views\admin\categories.html.py:18 +#: .\views\admin\comments.html.py:30 +#: .\views\admin\filemanager.html.py:57 +#: .\views\admin\pages.html.py:18 +#: .\views\admin\posts.html.py:20 +msgid "« prev" +msgstr "" + +#: .\views\admin\authors.html.py:22 +#: .\views\admin\categories.html.py:22 +#: .\views\admin\comments.html.py:34 +#: .\views\admin\filemanager.html.py:61 +#: .\views\admin\pages.html.py:22 +#: .\views\admin\posts.html.py:24 +msgid "next »" +msgstr "" + +#: .\views\admin\authors.html.py:23 +#: .\views\admin\categories.html.py:24 +#: .\views\admin\comments.html.py:35 +#: .\views\admin\filemanager.html.py:62 +#: .\views\admin\pages.html.py:23 +#: .\views\admin\posts.html.py:26 +msgid " Page %(pindex)s" +msgstr "" + +#: .\views\admin\authors.html.py:34 +#: .\views\admin\categories.html.py:37 +#: .\views\admin\category.html.py:40 +#: .\views\admin\link.html.py:40 +#: .\views\admin\links.html.py:22 +msgid "Name" +msgstr "" + +#: .\views\admin\authors.html.py:34 +msgid "IsAdmin" +msgstr "" + +#: .\views\admin\base.html.py:31 +msgid "Micolog Dashboard" +msgstr "" + +#: .\views\admin\base.html.py:31 +msgid "Logout" +msgstr "" + +#: .\views\admin\base.html.py:60 +msgid "Write" +msgstr "" + +#: .\views\admin\base.html.py:61 +msgid "Posts" +msgstr "" + +#: .\views\admin\base.html.py:62 +msgid "Pages" +msgstr "" + +#: .\views\admin\base.html.py:63 +msgid "Links" +msgstr "" + +#: .\views\admin\base.html.py:64 +msgid "Categories" +msgstr "" + +#: .\views\admin\base.html.py:65 +msgid "Comments" +msgstr "" + +#: .\views\admin\base.html.py:66 +#: .\views\admin\entry.html.py:108 +#: .\views\admin\upload.html.py:5 +msgid "Files" +msgstr "" + +#: .\views\admin\base.html.py:69 +msgid "Configuration" +msgstr "" + +#: .\views\admin\base.html.py:70 +msgid "Plugins" +msgstr "" + +#: .\views\admin\base.html.py:72 +msgid "Cache" +msgstr "" + +#: .\views\admin\base.html.py:73 +msgid "Authors" +msgstr "" + +#: .\views\admin\base.html.py:78 +msgid "help" +msgstr "" + +#: .\views\admin\categories.html.py:7 +msgid "Manage Categories" +msgstr "" + +#: .\views\admin\categories.html.py:8 +msgid "All Categories" +msgstr "" + +#: .\views\admin\categories.html.py:37 +#: .\views\admin\category.html.py:47 +#: .\views\admin\entry.html.py:267 +msgid "Slug" +msgstr "" + +#: .\views\admin\categories.html.py:37 +msgid "Parent" +msgstr "" + +#: .\views\admin\category.html.py:11 +msgid "Edit category" +msgstr "" + +#: .\views\admin\category.html.py:11 +msgid "Add category" +msgstr "" + +#: .\views\admin\category.html.py:24 +#: .\views\admin\entry.html.py:169 +#: .\views\admin\link.html.py:24 +msgid "Save" +msgstr "" + +#: .\views\admin\category.html.py:31 +msgid "Manage All Categories" +msgstr "" + +#: .\views\admin\category.html.py:43 +msgid "The name is used to identify the category almost everywhere, for example under the post or in the category widget." +msgstr "" + +#: .\views\admin\category.html.py:50 +msgid "The 'slug' is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens." +msgstr "" + +#: .\views\admin\category.html.py:54 +msgid "Parent Category" +msgstr "" + +#: .\views\admin\category.html.py:57 +msgid "None" +msgstr "" + +#: .\views\admin\comments.html.py:7 +msgid "Manage Comments" +msgstr "" + +#: .\views\admin\comments.html.py:8 +msgid "All Comments" +msgstr "" + +#: .\views\admin\comments.html.py:11 +msgid "Filter:" +msgstr "" + +#: .\views\admin\comments.html.py:13 +#: .\views\admin\comments.html.py:46 +#: .\views\admin\plugins.html.py:10 +#: .\views\admin\posts.html.py:37 +msgid "Author" +msgstr "" + +#: .\views\admin\comments.html.py:18 +msgid "Do" +msgstr "" + +#: .\views\admin\comments.html.py:46 +#: .\views\admin\pages.html.py:34 +#: .\views\admin\posts.html.py:37 +msgid "Date" +msgstr "" + +#: .\views\admin\comments.html.py:46 +msgid "Content" +msgstr "" + +#: .\views\admin\comments.html.py:46 +msgid "Email" +msgstr "" + +#: .\views\admin\comments.html.py:46 +msgid "IP" +msgstr "" + +#: .\views\admin\comments.html.py:46 +msgid "Entry" +msgstr "" + +#: .\views\admin\entry.html.py:131 +msgid "New Post" +msgstr "" + +#: .\views\admin\entry.html.py:131 +msgid "New Page" +msgstr "" + +#: .\views\admin\entry.html.py:143 +msgid "Write Post" +msgstr "" + +#: .\views\admin\entry.html.py:145 +msgid "Write Page" +msgstr "" + +#: .\views\admin\entry.html.py:155 +msgid "Category" +msgstr "" + +#: .\views\admin\entry.html.py:171 +msgid "UnPublish" +msgstr "" + +#: .\views\admin\entry.html.py:173 +msgid "Publish" +msgstr "" + +#: .\views\admin\entry.html.py:180 +msgid "Sticky it." +msgstr "" + +#: .\views\admin\entry.html.py:182 +#: .\views\admin\posts.html.py:37 +msgid "allow comment" +msgstr "" + +#: .\views\admin\entry.html.py:184 +msgid "allow trackback" +msgstr "" + +#: .\views\admin\entry.html.py:191 +msgid "Manage All Posts" +msgstr "" + +#: .\views\admin\entry.html.py:192 +msgid "Manage All Pages" +msgstr "" + +#: .\views\admin\entry.html.py:203 +msgid "Use as external page" +msgstr "" + +#: .\views\admin\entry.html.py:207 +#: .\views\admin\pages.html.py:34 +#: .\views\admin\posts.html.py:37 +msgid "Title" +msgstr "" + +#: .\views\admin\entry.html.py:220 +msgid "External page address" +msgstr "" + +#: .\views\admin\entry.html.py:224 +msgid "Target" +msgstr "" + +#: .\views\admin\entry.html.py:237 +msgid "Post" +msgstr "" + +#: .\views\admin\entry.html.py:239 +msgid "Page" +msgstr "" + +#: .\views\admin\entry.html.py:275 +msgid "Tag" +msgstr "" + +#: .\views\admin\entry.html.py:285 +msgid "Password" +msgstr "" + +#: .\views\admin\entry.html.py:295 +msgid "Options" +msgstr "" + +#: .\views\admin\entry.html.py:298 +msgid "Page level:" +msgstr "" + +#: .\views\admin\entry.html.py:300 +msgid "Page order:" +msgstr "" + +#: .\views\admin\entry.html.py:310 +msgid "Excerpt" +msgstr "" + +#: .\views\admin\filemanager.html.py:31 +msgid "Upload a file" +msgstr "" + +#: .\views\admin\filemanager.html.py:37 +msgid "Upload" +msgstr "" + +#: .\views\admin\filemanager.html.py:46 +msgid "Manage Files" +msgstr "" + +#: .\views\admin\filemanager.html.py:47 +msgid "All Files" +msgstr "" + +#: .\views\admin\filemanager.html.py:72 +msgid "File Name" +msgstr "" + +#: .\views\admin\filemanager.html.py:73 +#: .\views\admin\upload.html.py:98 +msgid "Size" +msgstr "" + +#: .\views\admin\filemanager.html.py:74 +msgid "Upload Date" +msgstr "" + +#: .\views\admin\filemanager.html.py:76 +msgid "Url" +msgstr "" + +#: .\views\admin\filemanager.html.py:77 +msgid "Download" +msgstr "" + +#: .\views\admin\import.html.py:41 +msgid "Export/Import for wordpress" +msgstr "" + +#: .\views\admin\import.html.py:42 +#: .\views\admin\import.html.py:57 +msgid "Export" +msgstr "" + +#: .\views\admin\import.html.py:43 +msgid "Export as WordPress eXtended RSS file" +msgstr "" + +#: .\views\admin\import.html.py:45 +msgid "Export from Date:" +msgstr "" + +#: .\views\admin\import.html.py:46 +msgid "to Date:" +msgstr "" + +#: .\views\admin\import.html.py:46 +msgid "'s posts" +msgstr "" + +#: .\views\admin\import.html.py:56 +msgid "Export all posts" +msgstr "" + +#: .\views\admin\link.html.py:11 +msgid "Edit link" +msgstr "" + +#: .\views\admin\link.html.py:11 +msgid "Add Link" +msgstr "" + +#: .\views\admin\link.html.py:31 +msgid "Manage All Links" +msgstr "" + +#: .\views\admin\link.html.py:43 +msgid "Example: Nifty blogging software" +msgstr "" + +#: .\views\admin\link.html.py:47 +msgid "Web Address" +msgstr "" + +#: .\views\admin\link.html.py:50 +msgid "Example: http://xuming.net - don't forget the http://" +msgstr "" + +#: .\views\admin\link.html.py:54 +#: .\views\admin\links.html.py:22 +msgid "LinkComment" +msgstr "" + +#: .\views\admin\link.html.py:57 +msgid "Example: Xuming's Blog" +msgstr "" + +#: .\views\admin\links.html.py:7 +msgid "Manage Links" +msgstr "" + +#: .\views\admin\links.html.py:22 +msgid "URL" +msgstr "" + +#: .\views\admin\pages.html.py:7 +msgid "Manage Pages" +msgstr "" + +#: .\views\admin\pages.html.py:8 +msgid "All Pages" +msgstr "" + +#: .\views\admin\pages.html.py:34 +#: .\views\admin\posts.html.py:37 +msgid "Status" +msgstr "" + +#: .\views\admin\pages.html.py:40 +#: .\views\admin\posts.html.py:43 +msgid "edit" +msgstr "" + +#: .\views\admin\pages.html.py:42 +#: .\views\admin\posts.html.py:46 +msgid "Published" +msgstr "" + +#: .\views\admin\pages.html.py:42 +#: .\views\admin\posts.html.py:46 +msgid "Unpublished" +msgstr "" + +#: .\views\admin\plugins.html.py:5 +msgid "Manage Plugins" +msgstr "" + +#: .\views\admin\plugins.html.py:10 +msgid "Plugin" +msgstr "" + +#: .\views\admin\plugins.html.py:10 +msgid "Version" +msgstr "" + +#: .\views\admin\plugins.html.py:10 +msgid "Description" +msgstr "" + +#: .\views\admin\plugins.html.py:10 +msgid "Active Status" +msgstr "" + +#: .\views\admin\plugins.html.py:20 +msgid "Deactivate" +msgstr "" + +#: .\views\admin\plugins.html.py:23 +msgid "Activate" +msgstr "" + +#: .\views\admin\posts.html.py:7 +msgid "Manage Posts" +msgstr "" + +#: .\views\admin\posts.html.py:9 +msgid "All Posts" +msgstr "" + +#: .\views\admin\setup.html.py:14 +msgid "Setup and Configuraiton for this Blog" +msgstr "" + +#: .\views\admin\setup.html.py:18 +msgid "Blog Title:" +msgstr "" + +#: .\views\admin\setup.html.py:24 +msgid "Sub Title:" +msgstr "" + +#: .\views\admin\setup.html.py:27 +msgid "In a few words, explain what this blog is about." +msgstr "" + +#: .\views\admin\setup.html.py:31 +msgid "Blog notice:" +msgstr "" + +#: .\views\admin\setup.html.py:37 +msgid "Default Link Structure:" +msgstr "" + +#: .\views\admin\setup.html.py:40 +#: .\views\admin\setup.html.py:47 +msgid "Allowed variants: " +msgstr "" + +#: .\views\admin\setup.html.py:44 +msgid "Permalink Structure:" +msgstr "" + +#: .\views\admin\setup.html.py:51 +msgid "Domain:" +msgstr "" + +#: .\views\admin\setup.html.py:57 +msgid "Micolog Address(URL):" +msgstr "" + +#: .\views\admin\setup.html.py:63 +msgid "RSS Feed URL:" +msgstr "" + +#: .\views\admin\setup.html.py:68 +msgid "Time delta:(Hours)" +msgstr "" + +#: .\views\admin\setup.html.py:75 +msgid "Theme:" +msgstr "" + +#: .\views\admin\setup.html.py:83 +msgid "Get more themes" +msgstr "" + +#: .\views\admin\setup.html.py:87 +msgid "Posts per page:" +msgstr "" + +#: .\views\admin\setup.html.py:92 +msgid "Comments order:" +msgstr "" + +#: .\views\admin\setup.html.py:94 +msgid "ASC" +msgstr "" + +#: .\views\admin\setup.html.py:95 +msgid "DESC" +msgstr "" + +#: .\views\admin\setup.html.py:99 +msgid "Comment Check Type:" +msgstr "" + +#: .\views\admin\setup.html.py:102 +msgid "Arithmetic" +msgstr "" + +#: .\views\admin\setup.html.py:103 +msgid "Graph" +msgstr "" + +#: .\views\admin\setup.html.py:104 +msgid "Client Arithmetic" +msgstr "" + +#: .\views\admin\setup.html.py:108 +msgid "Comments per page:" +msgstr "" + +#: .\views\admin\setup.html.py:114 +msgid "Avatar icon style:" +msgstr "" + +#: .\views\admin\setup.html.py:116 +msgid "Default" +msgstr "" + +#: .\views\admin\setup.html.py:117 +msgid "Identicon" +msgstr "" + +#: .\views\admin\setup.html.py:127 +msgid "Enable memcache:" +msgstr "" + +#: .\views\admin\setup.html.py:132 +msgid "Allow Trackback:" +msgstr "" + +#: .\views\admin\setup.html.py:138 +msgid "Allow Pingback:" +msgstr "" + +#: .\views\admin\setup.html.py:143 +msgid "Xmlrpc setting" +msgstr "" + +#: .\views\admin\setup.html.py:144 +msgid "Setup the username and password for RPC call. These will be used by client tools (e.g. Windows live writer). " +msgstr "" + +#: .\views\admin\setup.html.py:147 +msgid "User name:" +msgstr "" + +#: .\views\admin\setup.html.py:153 +msgid "Password:" +msgstr "" + +#: .\views\admin\setup.html.py:160 +#: .\views\admin\sitemap.html.py:49 +msgid "Save Changes" +msgstr "" + +#: .\views\admin\sitemap.html.py:16 +msgid "Sitemap Setup" +msgstr "" + +#: .\views\admin\sitemap.html.py:20 +msgid "Entries in sitemap:" +msgstr "" + +#: .\views\admin\sitemap.html.py:28 +msgid "Include category:" +msgstr "" + +#: .\views\admin\sitemap.html.py:34 +msgid "Include tag:" +msgstr "" + +#: .\views\admin\sitemap.html.py:39 +msgid "Pinging Search Engine (Google):" +msgstr "" + +#: .\views\admin\status.html.py:23 +msgid "Cache status" +msgstr "" + +#: .\views\admin\status.html.py:25 +#: .\views\admin\status.html.py:28 +msgid "Clear cache" +msgstr "" + +#: .\views\admin\status.html.py:28 +msgid "Clear cache data." +msgstr "" + +#: .\views\admin\status.html.py:31 +msgid "Environ" +msgstr "" + +#: .\views\admin\tools.html.py:88 +msgid "Careless use of it will be dangerous!" +msgstr "" + +#: .\views\admin\tools.html.py:91 +#: .\views\admin\tools.html.py:97 +#: .\views\admin\tools.html.py:104 +#: .\views\admin\tools.html.py:111 +#: .\views\admin\tools.html.py:118 +#: .\views\admin\tools.html.py:129 +msgid "Run" +msgstr "" + +#: .\views\admin\tools.html.py:92 +msgid "Update tags" +msgstr "" + +#: .\views\admin\tools.html.py:93 +msgid "It will refresh all tags for each entry." +msgstr "" + +#: .\views\admin\tools.html.py:99 +msgid "Init datas" +msgstr "" + +#: .\views\admin\tools.html.py:100 +msgid "It will delete all posts, pages and comments." +msgstr "" + +#: .\views\admin\tools.html.py:106 +msgid "Update comments" +msgstr "" + +#: .\views\admin\tools.html.py:107 +msgid "It will refresh the number of comments for each entry." +msgstr "" + +#: .\views\admin\tools.html.py:113 +msgid "Update comment number" +msgstr "" + +#: .\views\admin\tools.html.py:114 +msgid "It will refresh the number for each comment." +msgstr "" + +#: .\views\admin\tools.html.py:120 +msgid "Rebuild URL" +msgstr "" + +#: .\views\admin\tools.html.py:121 +msgid "The URL of all entries (post or page) will be rebuild." +msgstr "" + +#: .\views\admin\tools.html.py:123 +msgid "Permalink" +msgstr "" + +#: .\views\admin\tools.html.py:125 +msgid "Available parameters:" +msgstr "" + +#: .\views\admin\tools.html.py:132 +msgid "Update archives" +msgstr "" + +#: .\views\admin\tools.html.py:133 +msgid "It will update the monthyear of entries." +msgstr "" + +#: .\views\admin\upload.html.py:29 +msgid "Error: invalid file extension" +msgstr "" + +#: .\views\admin\upload.html.py:53 +#: .\views\admin\upload.html.py:101 +msgid "Add" +msgstr "" + +#: .\views\admin\upload.html.py:92 +msgid "New file" +msgstr "" + +#: .\views\admin\upload.html.py:98 +msgid "Filename" +msgstr "" + +#: .\views\admin\upload.html.py:98 +msgid "Operater" +msgstr "" + diff --git a/locale/es_AR/LC_MESSAGES/django.mo b/locale/es_AR/LC_MESSAGES/django.mo index 5e01aa4..8f641ba 100644 Binary files a/locale/es_AR/LC_MESSAGES/django.mo and b/locale/es_AR/LC_MESSAGES/django.mo differ diff --git a/locale/es_AR/LC_MESSAGES/django.po b/locale/es_AR/LC_MESSAGES/django.po index 9d9f80a..17c9320 100644 --- a/locale/es_AR/LC_MESSAGES/django.po +++ b/locale/es_AR/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-01 11:10+0800\n" -"PO-Revision-Date: 2010-02-19 02:27-0300\n" -"Last-Translator: \n" +"PO-Revision-Date: 2011-08-25 23:19+0800\n" +"Last-Translator: Eric Guo \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" diff --git a/locale/it_IT/LC_MESSAGES/django.mo b/locale/it_IT/LC_MESSAGES/django.mo index 9d9383f..8ec4a14 100644 Binary files a/locale/it_IT/LC_MESSAGES/django.mo and b/locale/it_IT/LC_MESSAGES/django.mo differ diff --git a/locale/it_IT/LC_MESSAGES/django.po b/locale/it_IT/LC_MESSAGES/django.po index 9d82204..d7586e4 100644 --- a/locale/it_IT/LC_MESSAGES/django.po +++ b/locale/it_IT/LC_MESSAGES/django.po @@ -6,8 +6,8 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-02-01 11:10+0800\n" -"PO-Revision-Date: 2010-05-04 16:42+0100\n" -"Last-Translator: Rafael Driutti \n" +"PO-Revision-Date: 2011-08-25 23:19+0800\n" +"Last-Translator: Eric Guo \n" "Language-Team: Spanish <>\n" "Language: es\n" "MIME-Version: 1.0\n" diff --git a/locale/ja/LC_MESSAGES/django.mo b/locale/ja/LC_MESSAGES/django.mo index 79cfc0a..400314a 100644 Binary files a/locale/ja/LC_MESSAGES/django.mo and b/locale/ja/LC_MESSAGES/django.mo differ diff --git a/locale/ja/LC_MESSAGES/django.po b/locale/ja/LC_MESSAGES/django.po index 2ae5ad9..619147a 100644 --- a/locale/ja/LC_MESSAGES/django.po +++ b/locale/ja/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: micolog 0.7\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-05-04 11:06+0800\n" -"PO-Revision-Date: 2010-09-25 11:26+0800\n" -"Last-Translator: simon hsu \n" +"PO-Revision-Date: 2011-08-25 23:19+0800\n" +"Last-Translator: Eric Guo \n" "Language-Team: micolog \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" @@ -1011,8 +1011,10 @@ msgstr "操作" #~ msgid "Please input title and content or external_page_address." #~ msgstr "请输入标题和内容或扩展页面地址" + #~ msgid "Saved ok" #~ msgstr "保存成功" + #~ msgid "" #~ "New comment on your post \"%s\"\n" #~ "Author : %s\n" @@ -1031,6 +1033,7 @@ msgstr "操作" #~ "%s\n" #~ "查看所有评论:\n" #~ "%s\n" + #~ msgid "" #~ "Hi~ New reference on your comment for post \"%s\"\n" #~ "Author : %s\n" @@ -1047,12 +1050,13 @@ msgstr "操作" #~ "%s\n" #~ "You can see all comments on this post here:\n" #~ "%s\n" + #~ msgid "Insert" #~ msgstr "插入" #, fuzzy #~ msgid "Web Comment" #~ msgstr "评论" + #~ msgid "Advance" #~ msgstr "高级" - diff --git a/locale/zh_CN/LC_MESSAGES/django.mo b/locale/zh_CN/LC_MESSAGES/django.mo index 0390eb1..1902db7 100644 Binary files a/locale/zh_CN/LC_MESSAGES/django.mo and b/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/locale/zh_CN/LC_MESSAGES/django.po b/locale/zh_CN/LC_MESSAGES/django.po index a57b95e..5ac1296 100644 --- a/locale/zh_CN/LC_MESSAGES/django.po +++ b/locale/zh_CN/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: micolog 0.7\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-10-10 16:56+0800\n" -"PO-Revision-Date: 2010-10-10 21:36+0800\n" -"Last-Translator: SkyCloud \n" +"PO-Revision-Date: 2011-10-30 19:19+0800\n" +"Last-Translator: Eric Guo \n" "Language-Team: micolog \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" @@ -170,7 +170,7 @@ msgstr "这是一个测试" #: .\model.py:434 msgid "..more" -msgstr "(..更多内容)" +msgstr "..更多" #: .\model.py:901 msgid "Hello world!" @@ -494,7 +494,7 @@ msgstr "邮件" #: .\views\admin\comments.html.py:46 msgid "IP" -msgstr "" +msgstr "IP" #: .\views\admin\comments.html.py:46 msgid "Entry" @@ -630,7 +630,7 @@ msgstr "上传日期" #: .\views\admin\filemanager.html.py:76 msgid "Url" -msgstr "" +msgstr "URL" #: .\views\admin\filemanager.html.py:77 msgid "Download" @@ -696,7 +696,7 @@ msgstr "链接描述" #: .\views\admin\link.html.py:57 msgid "Example: Xuming's Blog" -msgstr "" +msgstr "例子:徐明的博客" #: .\views\admin\links.html.py:7 msgid "Manage Links" diff --git a/micolog_plugin.py b/micolog_plugin.py index 4ad4872..a13b777 100644 --- a/micolog_plugin.py +++ b/micolog_plugin.py @@ -3,6 +3,7 @@ from google.appengine.ext.webapp import template from google.appengine.ext import zipserve RE_FIND_GROUPS = re.compile('\(.*?\)') + class PluginIterator: def __init__(self, plugins_path='plugins'): self.iterating = False @@ -26,10 +27,10 @@ def next(self): value = self.list[self.cursor] self.cursor += 1 if os.path.isdir(os.path.join(self.plugins_path, value)): - return (value,'%s.%s.%s'%(self.plugins_path,value,value)) + return value,'%s.%s.%s'%(self.plugins_path,value,value) elif value.endswith('.py') and not value=='__init__.py': value=value[:-3] - return (value,'%s.%s'%(self.plugins_path,value)) + return value,'%s.%s'%(self.plugins_path,value) else: return self.next() @@ -113,7 +114,7 @@ def activate(self,iname,active): if active: plugin=self.getPluginByName(iname) if plugin: - if (iname not in self.active_list): + if iname not in self.active_list: self.active_list.append(iname) OptionSet.setValue("PluginActive",self.active_list) plugin.active=active @@ -133,7 +134,7 @@ def activate(self,iname,active): else: plugin=self.getPluginByName(iname) if plugin: - if (iname in self.active_list): + if iname in self.active_list: self.active_list.remove(iname) OptionSet.setValue("PluginActive",self.active_list) plugin.active=active @@ -220,8 +221,6 @@ def get_handlerlist(self,url): else: return {} - - def tigger_filter(self,name,content,*arg1,**arg2): for func in self.get_filter_plugins(name): content=func(content,*arg1,**arg2) @@ -273,7 +272,6 @@ def error(self,msg=""): def register_filter(self,name,func): self._filter[name]=func - def register_action(self,name,func): self._action[name]=func diff --git a/micolog_template.py b/micolog_template.py index b6cac5f..45b63f2 100644 --- a/micolog_template.py +++ b/micolog_template.py @@ -42,23 +42,20 @@ -import md5 +import hashlib import os,logging - -try: - from django import v0_96 -except ImportError: - pass +from google.appengine.dist import use_library +use_library('django', '1.2') import django import django.conf try: django.conf.settings.configure( - DEBUG=False, - TEMPLATE_DEBUG=False, - TEMPLATE_LOADERS=( - 'django.template.loaders.filesystem.load_template_source', - ), + DEBUG=False, + TEMPLATE_DEBUG=False, + TEMPLATE_LOADERS=( + 'django.template.loaders.filesystem.load_template_source', + ), ) except (EnvironmentError, RuntimeError): pass @@ -67,22 +64,22 @@ from google.appengine.ext import webapp - def render(theme,template_file, template_dict, debug=False): """Renders the template at the given path with the given dict of values. Example usage: - render("templates/index.html", {"name": "Bret", "values": [1, 2, 3]}) + render("templates/index.html", {"name": "Bret", "values": [1, 2, 3]}) Args: - template_path: path to a Django template - template_dict: dictionary of values to apply to the template + template_path: path to a Django template + template_dict: dictionary of values to apply to the template """ t = load(theme,template_file, debug) return t.render(Context(template_dict)) template_cache = {} + def load(theme,template_file, debug=False): """Loads the Django template from the given path. @@ -92,46 +89,46 @@ class below because Django requires you to load the template with a method """ #template_file=os.path.join("templates",template_file) if theme.isZip: - theme_path=theme.server_dir + theme_path=theme.server_dir else: - theme_path=os.path.join( theme.server_dir,"templates") + theme_path=os.path.join( theme.server_dir,"templates") abspath =os.path.join( theme_path,template_file) logging.debug("theme_path:%s,abspath:%s"%(theme_path,abspath)) if not debug: - template = template_cache.get(abspath, None) + template = template_cache.get(abspath) else: - template = None + template = None if not template: - #file_name = os.path.split(abspath) - new_settings = { - 'TEMPLATE_DIRS': (theme_path,), - 'TEMPLATE_DEBUG': debug, - 'DEBUG': debug, - } - old_settings = _swap_settings(new_settings) - try: - template = django.template.loader.get_template(template_file) - finally: - _swap_settings(old_settings) - - if not debug: - template_cache[abspath] = template - - def wrap_render(context, orig_render=template.render): - URLNode = django.template.defaulttags.URLNode - save_urlnode_render = URLNode.render - old_settings = _swap_settings(new_settings) - try: - URLNode.render = _urlnode_render_replacement - return orig_render(context) - finally: - _swap_settings(old_settings) - URLNode.render = save_urlnode_render - - template.render = wrap_render + #file_name = os.path.split(abspath) + new_settings = { + 'TEMPLATE_DIRS': (theme_path,), + 'TEMPLATE_DEBUG': debug, + 'DEBUG': debug, + } + old_settings = _swap_settings(new_settings) + try: + template = django.template.loader.get_template(template_file) + finally: + _swap_settings(old_settings) + + if not debug: + template_cache[abspath] = template + + def wrap_render(context, orig_render=template.render): + URLNode = django.template.defaulttags.URLNode + save_urlnode_render = URLNode.render + old_settings = _swap_settings(new_settings) + try: + URLNode.render = _urlnode_render_replacement + return orig_render(context) + finally: + _swap_settings(old_settings) + URLNode.render = save_urlnode_render + + template.render = wrap_render return template @@ -140,30 +137,30 @@ def _swap_settings(new): """Swap in selected Django settings, returning old settings. Example: - save = _swap_settings({'X': 1, 'Y': 2}) - try: - ...new settings for X and Y are in effect here... - finally: - _swap_settings(save) + save = _swap_settings({'X': 1, 'Y': 2}) + try: + ...new settings for X and Y are in effect here... + finally: + _swap_settings(save) Args: - new: A dict containing settings to change; the keys should - be setting names and the values settings values. + new: A dict containing settings to change; the keys should + be setting names and the values settings values. Returns: - Another dict structured the same was as the argument containing - the original settings. Original settings that were not set at all - are returned as None, and will be restored as None by the - 'finally' clause in the example above. This shouldn't matter; we - can't delete settings that are given as None, since None is also a - legitimate value for some settings. Creating a separate flag value - for 'unset' settings seems overkill as there is no known use case. + Another dict structured the same was as the argument containing + the original settings. Original settings that were not set at all + are returned as None, and will be restored as None by the + 'finally' clause in the example above. This shouldn't matter; we + can't delete settings that are given as None, since None is also a + legitimate value for some settings. Creating a separate flag value + for 'unset' settings seems overkill as there is no known use case. """ settings = django.conf.settings old = {} for key, value in new.iteritems(): - old[key] = getattr(settings, key, None) - setattr(settings, key, value) + old[key] = getattr(settings, key, None) + setattr(settings, key, value) return old @@ -174,22 +171,22 @@ def create_template_register(): module, and create a module-level variable named "register", and register all custom filters to it as described at http://www.djangoproject.com/documentation/templates_python/ - #extending-the-template-system: + #extending-the-template-system: - templatefilters.py - ================== - register = webapp.template.create_template_register() + templatefilters.py + ================== + register = webapp.template.create_template_register() - def cut(value, arg): - return value.replace(arg, '') - register.filter(cut) + def cut(value, arg): + return value.replace(arg, '') + register.filter(cut) Then, register the custom template module with the register_template_library function below in your application module: - myapp.py - ======== - webapp.template.register_template_library('templatefilters') + myapp.py + ======== + webapp.template.register_template_library('templatefilters') """ return django.template.Library() @@ -198,8 +195,8 @@ def register_template_library(package_name): """Registers a template extension module to make it usable in templates. See the documentation for create_template_register for more information.""" - if not django.template.libraries.get(package_name, None): - django.template.add_to_builtins(package_name) + if not django.template.libraries.get(package_name): + django.template.add_to_builtins(package_name) Template = django.template.Template @@ -220,8 +217,8 @@ def _urlnode_render_replacement(self, context): """ args = [arg.resolve(context) for arg in self.args] try: - app = webapp.WSGIApplication.active_instance - handler = app.get_registered_handler_by_name(self.view_name) - return handler.get_url(implicit_args=True, *args) + app = webapp.WSGIApplication.active_instance + handler = app.get_registered_handler_by_name(self.view_name) + return handler.get_url(implicit_args=True, *args) except webapp.NoUrlFoundError: - return '' + return '' diff --git a/model.py b/model.py index 076bf64..1048320 100644 --- a/model.py +++ b/model.py @@ -1,850 +1,817 @@ # -*- coding: utf-8 -*- import os,logging -from google.appengine.api import users +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') from google.appengine.ext import db from google.appengine.ext.db import Model as DBModel from google.appengine.api import memcache -from google.appengine.api import mail from google.appengine.api import urlfetch from google.appengine.api import datastore from datetime import datetime import urllib, hashlib,urlparse -import zipfile,re,pickle,uuid -#from base import * +import re,pickle logging.info('module base reloaded') - +from django.utils.translation import ugettext as _ rootpath=os.path.dirname(__file__) def vcache(key="",time=3600): - def _decorate(method): - def _wrapper(*args, **kwargs): - if not g_blog.enable_memcache: - return method(*args, **kwargs) - - result=method(*args, **kwargs) - memcache.set(key,result,time) - return result - - return _wrapper - return _decorate + def _decorate(method): + def _wrapper(*args, **kwargs): + if not g_blog.enable_memcache: + return method(*args, **kwargs) + result=method(*args, **kwargs) + memcache.set(key,result,time) + return result + return _wrapper + return _decorate class Theme: - def __init__(self, name='default'): - self.name = name - self.mapping_cache = {} - self.dir = '/themes/%s' % name - self.viewdir=os.path.join(rootpath, 'view') - self.server_dir = os.path.join(rootpath, 'themes',self.name) - if os.path.exists(self.server_dir): - self.isZip=False - else: - self.isZip=True - self.server_dir =self.server_dir+".zip" - #self.server_dir=os.path.join(self.server_dir,"templates") - logging.debug('server_dir:%s'%self.server_dir) - - def __getattr__(self, name): - if self.mapping_cache.has_key(name): - return self.mapping_cache[name] - else: - - path ="/".join((self.name,'templates', name + '.html')) - logging.debug('path:%s'%path) + def __init__(self, name='default'): + self.name = name + self.mapping_cache = {} + self.dir = '/themes/%s' % name + self.viewdir=os.path.join(rootpath, 'view') + self.server_dir = os.path.join(rootpath, 'themes',self.name) + if os.path.exists(self.server_dir): + self.isZip=False + else: + self.isZip=True + self.server_dir += ".zip" + #self.server_dir=os.path.join(self.server_dir,"templates") + logging.debug('server_dir:%s'%self.server_dir) + + def __getattr__(self, name): + if self.mapping_cache.has_key(name): + return self.mapping_cache[name] + else: + path ="/".join((self.name,'templates', name + '.html')) + logging.debug('path:%s'%path) ## if not os.path.exists(path): ## path = os.path.join(rootpath, 'themes', 'default', 'templates', name + '.html') ## if not os.path.exists(path): ## path = None - self.mapping_cache[name]=path - return path - + self.mapping_cache[name]=path + return path class ThemeIterator: - def __init__(self, theme_path='themes'): - self.iterating = False - self.theme_path = theme_path - self.list = [] - - def __iter__(self): - return self - - def next(self): - if not self.iterating: - self.iterating = True - self.list = os.listdir(self.theme_path) - self.cursor = 0 - - if self.cursor >= len(self.list): - self.iterating = False - raise StopIteration - else: - value = self.list[self.cursor] - self.cursor += 1 - if value.endswith('.zip'): - value=value[:-4] - return value - #return (str(value), unicode(value)) + def __init__(self, theme_path='themes'): + self.iterating = False + self.theme_path = theme_path + self.list = [] + + def __iter__(self): + return self + + def next(self): + if not self.iterating: + self.iterating = True + self.list = os.listdir(self.theme_path) + self.cursor = 0 + + if self.cursor >= len(self.list): + self.iterating = False + raise StopIteration + else: + value = self.list[self.cursor] + self.cursor += 1 + if value.endswith('.zip'): + value=value[:-4] + return value + #return (str(value), unicode(value)) class LangIterator: - def __init__(self,path='locale'): - self.iterating = False - self.path = path - self.list = [] - for value in os.listdir(self.path): - if os.path.isdir(os.path.join(self.path,value)): - if os.path.exists(os.path.join(self.path,value,'LC_MESSAGES')): - try: - lang=open(os.path.join(self.path,value,'language')).readline() - self.list.append({'code':value,'lang':lang}) - except: - self.list.append( {'code':value,'lang':value}) - - def __iter__(self): - return self - - def next(self): - if not self.iterating: - self.iterating = True - self.cursor = 0 - - if self.cursor >= len(self.list): - self.iterating = False - raise StopIteration - else: - value = self.list[self.cursor] - self.cursor += 1 - return value - - def getlang(self,language): - from django.utils.translation import to_locale - for item in self.list: - if item['code']==language or item['code']==to_locale(language): - return item - return {'code':'en_US','lang':'English'} + def __init__(self,path='locale'): + self.iterating = False + self.path = path + self.list = [] + for value in os.listdir(self.path): + if os.path.isdir(os.path.join(self.path,value)): + if os.path.exists(os.path.join(self.path,value,'LC_MESSAGES')): + try: + lang=open(os.path.join(self.path,value,'language')).readline() + self.list.append({'code':value,'lang':lang}) + except: + self.list.append({'code':value,'lang':value}) + + def __iter__(self): + return self + + def next(self): + if not self.iterating: + self.iterating = True + self.cursor = 0 + + if self.cursor >= len(self.list): + self.iterating = False + raise StopIteration + else: + value = self.list[self.cursor] + self.cursor += 1 + return value + + def getlang(self,language): + from django.utils.translation import to_locale + for item in self.list: + if item['code']==language or item['code']==to_locale(language): + return item + return {'code':'en_US','lang':'English'} class BaseModel(db.Model): - def __init__(self, parent=None, key_name=None, _app=None, **kwds): - self.__isdirty = False - DBModel.__init__(self, parent=None, key_name=None, _app=None, **kwds) - - def __setattr__(self,attrname,value): - """ - DataStore api stores all prop values say "email" is stored in "_email" so - we intercept the set attribute, see if it has changed, then check for an - onchanged method for that property to call - """ - if (attrname.find('_') != 0): - if hasattr(self,'_' + attrname): - curval = getattr(self,'_' + attrname) - if curval != value: - self.__isdirty = True - if hasattr(self,attrname + '_onchange'): - getattr(self,attrname + '_onchange')(curval,value) - - DBModel.__setattr__(self,attrname,value) + def __init__(self, parent=None, key_name=None, _app=None, **kwds): + self.__isdirty = False + DBModel.__init__(self, parent=None, key_name=None, _app=None, **kwds) + + def __setattr__(self,attrname,value): + """ + DataStore api stores all prop values say "email" is stored in "_email" so + we intercept the set attribute, see if it has changed, then check for an + onchanged method for that property to call + """ + if attrname.find('_') != 0: + if hasattr(self,'_' + attrname): + curval = getattr(self,'_' + attrname) + if curval != value: + self.__isdirty = True + if hasattr(self,attrname + '_onchange'): + getattr(self,attrname + '_onchange')(curval,value) + DBModel.__setattr__(self,attrname,value) class Cache(db.Model): - cachekey = db.StringProperty(multiline=False) - content = db.TextProperty() + cachekey = db.StringProperty() + content = db.TextProperty() class Blog(db.Model): - owner = db.UserProperty() - author=db.StringProperty(default='admin') - rpcuser=db.StringProperty(default='admin') - rpcpassword=db.StringProperty(default='') - description = db.TextProperty() - baseurl = db.StringProperty(multiline=False,default=None) - urlpath = db.StringProperty(multiline=False) - title = db.StringProperty(multiline=False,default='Micolog') - subtitle = db.StringProperty(multiline=False,default='This is a micro blog.') - entrycount = db.IntegerProperty(default=0) - posts_per_page= db.IntegerProperty(default=10) - feedurl = db.StringProperty(multiline=False,default='/feed') - blogversion = db.StringProperty(multiline=False,default='0.30') - theme_name = db.StringProperty(multiline=False,default='default') - enable_memcache = db.BooleanProperty(default = False) - link_format=db.StringProperty(multiline=False,default='%(year)s/%(month)s/%(day)s/%(postname)s.html') - comment_notify_mail=db.BooleanProperty(default=True) - #评论顺序 - comments_order=db.IntegerProperty(default=0) - #每页评论数 - comments_per_page=db.IntegerProperty(default=20) - #comment check type 0-No 1-算术 2-验证码 3-客户端计算 - comment_check_type=db.IntegerProperty(default=1) - #0 default 1 identicon - avatar_style=db.IntegerProperty(default=0) - - blognotice=db.TextProperty(default='') - - domain=db.StringProperty() - show_excerpt=db.BooleanProperty(default=True) - version=0.736 - timedelta=db.FloatProperty(default=8.0)# hours - language=db.StringProperty(default="en-us") - - sitemap_entries=db.IntegerProperty(default=30) - sitemap_include_category=db.BooleanProperty(default=False) - sitemap_include_tag=db.BooleanProperty(default=False) - sitemap_ping=db.BooleanProperty(default=False) - default_link_format=db.StringProperty(multiline=False,default='?p=%(post_id)s') - default_theme=Theme("default") - - allow_pingback=db.BooleanProperty(default=False) - allow_trackback=db.BooleanProperty(default=False) - - theme=None - langs=None - application=None - - - - - def __init__(self, - parent=None, - key_name=None, - _app=None, - _from_entity=False, - **kwds): - from micolog_plugin import Plugins - self.plugins=Plugins(self) - db.Model.__init__(self,parent,key_name,_app,_from_entity,**kwds) - - def tigger_filter(self,name,content,*arg1,**arg2): - return self.plugins.tigger_filter(name,content,blog=self,*arg1,**arg2) - - def tigger_action(self,name,*arg1,**arg2): - return self.plugins.tigger_action(name,blog=self,*arg1,**arg2) - - def tigger_urlmap(self,url,*arg1,**arg2): - return self.plugins.tigger_urlmap(url,blog=self,*arg1,**arg2) - - def get_ziplist(self): - return self.plugins.get_ziplist(); - - def save(self): - self.put() - - def initialsetup(self): - self.title = 'Your Blog Title' - self.subtitle = 'Your Blog Subtitle' - - def get_theme(self): - self.theme= Theme(self.theme_name); - return self.theme - - def get_langs(self): - self.langs=LangIterator() - return self.langs - - def cur_language(self): - return self.get_langs().getlang(self.language) - - def rootpath(self): - return rootpath - - @vcache("blog.hotposts") - def hotposts(self): - return Entry.all().filter('entrytype =','post').filter("published =", True).order('-readtimes').fetch(8) - - @vcache("blog.recentposts") - def recentposts(self): - return Entry.all().filter('entrytype =','post').filter("published =", True).order('-date').fetch(8) - - @vcache("blog.postscount") - def postscount(self): - return Entry.all().filter('entrytype =','post').filter("published =", True).order('-date').count() - + owner = db.UserProperty() + author=db.StringProperty(default='admin') + rpcuser=db.StringProperty(default='admin') + rpcpassword=db.StringProperty(default='') + description = db.TextProperty() + baseurl = db.StringProperty(default=None) + urlpath = db.StringProperty() + title = db.StringProperty(default='Micolog') + subtitle = db.StringProperty(default='This is a micro blog.') + entrycount = db.IntegerProperty(default=0) + posts_per_page= db.IntegerProperty(default=10) + feedurl = db.StringProperty(default='/feed') + blogversion = db.StringProperty(default='0.30') + theme_name = db.StringProperty(default='default') + enable_memcache = db.BooleanProperty(default = False) + link_format=db.StringProperty(default='%(year)s/%(month)s/%(day)s/%(postname)s.html') + comment_notify_mail=db.BooleanProperty(default=True) + #评论顺序 + comments_order=db.IntegerProperty(default=0) + #每页评论数 + comments_per_page=db.IntegerProperty(default=20) + #comment check type 0-No 1-算术 2-验证码 3-客户端计算 + comment_check_type=db.IntegerProperty(default=1) + #0 default 1 identicon + avatar_style=db.IntegerProperty(default=0) + blognotice=db.TextProperty(default='') + domain=db.StringProperty() + show_excerpt=db.BooleanProperty(default=True) + version=0.743 + timedelta=db.FloatProperty(default=8.0)# hours + language=db.StringProperty(default="en-us") + sitemap_entries=db.IntegerProperty(default=30) + sitemap_include_category=db.BooleanProperty(default=False) + sitemap_include_tag=db.BooleanProperty(default=False) + sitemap_ping=db.BooleanProperty(default=False) + default_link_format=db.StringProperty(default='?p=%(post_id)s') + default_theme=Theme() + allow_pingback=db.BooleanProperty(default=False) + allow_trackback=db.BooleanProperty(default=False) + admin_essential=db.BooleanProperty(default=False) + theme=None + langs=None + application=None + + def __init__(self, + parent=None, + key_name=None, + _app=None, + _from_entity=False, + **kwds): + from micolog_plugin import Plugins + self.plugins=Plugins(self) + db.Model.__init__(self,parent,key_name,_app,_from_entity,**kwds) + + def tigger_filter(self,name,content,*arg1,**arg2): + return self.plugins.tigger_filter(name,content,blog=self,*arg1,**arg2) + + def tigger_action(self,name,*arg1,**arg2): + return self.plugins.tigger_action(name,blog=self,*arg1,**arg2) + + def tigger_urlmap(self,url,*arg1,**arg2): + return self.plugins.tigger_urlmap(url,blog=self,*arg1,**arg2) + + def get_ziplist(self): + return self.plugins.get_ziplist(); + + def save(self): + self.put() + + def get_theme(self): + self.theme= Theme(self.theme_name); + return self.theme + + def get_langs(self): + self.langs=LangIterator() + return self.langs + + def cur_language(self): + return self.get_langs().getlang(self.language) + + def rootpath(self): + return rootpath + + @vcache("blog.hotposts") + def hotposts(self): + return Entry.all().filter('entrytype =','post').filter("published =", True).order('-readtimes').fetch(7) + + @vcache("blog.recentposts") + def recentposts(self): + return Entry.all().filter('entrytype =','post').filter("published =", True).order('-date').fetch(7) + + @vcache("blog.postscount") + def postscount(self): + return Entry.all().filter('entrytype =','post').filter("published =", True).order('-date').count() class Category(db.Model): - uid=db.IntegerProperty() - name=db.StringProperty(multiline=False) - slug=db.StringProperty(multiline=False) - parent_cat=db.SelfReferenceProperty() - @property - def posts(self): - return Entry.all().filter('entrytype =','post').filter("published =", True).filter('categorie_keys =',self) - - @property - def count(self): - return self.posts.count() - - def put(self): - db.Model.put(self) - g_blog.tigger_action("save_category",self) - - def delete(self): - for entry in Entry.all().filter('categorie_keys =',self): - entry.categorie_keys.remove(self.key()) - entry.put() - for cat in Category.all().filter('parent_cat =',self): - cat.delete() - db.Model.delete(self) - g_blog.tigger_action("delete_category",self) - - def ID(self): - try: - id=self.key().id() - if id: - return id - except: - pass - - if self.uid : - return self.uid - else: - #旧版本Category没有ID,为了与wordpress兼容 - from random import randint - uid=randint(0,99999999) - cate=Category.all().filter('uid =',uid).get() - while cate: - uid=randint(0,99999999) - cate=Category.all().filter('uid =',uid).get() - self.uid=uid - print uid - self.put() - return uid - - @classmethod - def get_from_id(cls,id): - cate=Category.get_by_id(id) - if cate: - return cate - else: - cate=Category.all().filter('uid =',id).get() - return cate - - @property - def children(self): - key=self.key() - return [c for c in Category.all().filter('parent_cat =',self)] - - - @classmethod - def allTops(self): - return [c for c in Category.all() if not c.parent_cat] + uid=db.IntegerProperty() + name=db.StringProperty() + slug=db.StringProperty() + parent_cat=db.SelfReferenceProperty() + @property + def posts(self): + return Entry.all().filter('entrytype =','post').filter("published =", True).filter('categorie_keys =',self) + + @property + def count(self): + return self.posts.count() + + def put(self): + db.Model.put(self) + g_blog.tigger_action("save_category",self) + + def delete(self): + for entry in Entry.all().filter('categorie_keys =',self): + entry.categorie_keys.remove(self.key()) + entry.put() + for cat in Category.all().filter('parent_cat =',self): + cat.delete() + db.Model.delete(self) + g_blog.tigger_action("delete_category",self) + + def ID(self): + try: + id=self.key().id() + if id: + return id + except: + pass + + if self.uid : + return self.uid + else: + #旧版本Category没有ID,为了与wordpress兼容 + from random import randint + uid=randint(0,99999999) + cate=Category.all().filter('uid =',uid).get() + while cate: + uid=randint(0,99999999) + cate=Category.all().filter('uid =',uid).get() + self.uid=uid + print uid + self.put() + return uid + + @classmethod + def get_from_id(cls,id): + cate=Category.get_by_id(id) + if cate: + return cate + else: + cate=Category.all().filter('uid =',id).get() + return cate + + @property + def children(self): + key=self.key() + return [c for c in Category.all().filter('parent_cat =',self)] + + + @classmethod + def allTops(self): + return [c for c in Category.all() if not c.parent_cat] class Archive(db.Model): - monthyear = db.StringProperty(multiline=False) - year = db.StringProperty(multiline=False) - month = db.StringProperty(multiline=False) - entrycount = db.IntegerProperty(default=0) - date = db.DateTimeProperty(auto_now_add=True) + monthyear = db.StringProperty() + year = db.StringProperty() + month = db.StringProperty() + entrycount = db.IntegerProperty(default=0) + date = db.DateTimeProperty(auto_now_add=True) class Tag(db.Model): - tag = db.StringProperty(multiline=False) - tagcount = db.IntegerProperty(default=0) - @property - def posts(self): - return Entry.all('entrytype =','post').filter("published =", True).filter('tags =',self) - - @classmethod - def add(cls,value): - if value: - tag= Tag.get_by_key_name(value) - if not tag: - tag=Tag(key_name=value) - tag.tag=value - - tag.tagcount+=1 - tag.put() - return tag - else: - return None - - @classmethod - def remove(cls,value): - if value: - tag= Tag.get_by_key_name(value) - if tag: - if tag.tagcount>1: - tag.tagcount-=1 - tag.put() - else: - tag.delete() - - + tag = db.StringProperty() + tagcount = db.IntegerProperty(default=0) + @property + def posts(self): + return Entry.all('entrytype =','post').filter("published =", True).filter('tags =',self) + + @classmethod + def add(cls,value): + if value: + tag= Tag.get_by_key_name(value) + if not tag: + tag=Tag(key_name=value) + tag.tag=value + + tag.tagcount+=1 + tag.put() + return tag + else: + return None + + @classmethod + def remove(cls,value): + if value: + tag= Tag.get_by_key_name(value) + if tag: + if tag.tagcount>1: + tag.tagcount-=1 + tag.put() + else: + tag.delete() class Link(db.Model): - href = db.StringProperty(multiline=False,default='') - linktype = db.StringProperty(multiline=False,default='blogroll') - linktext = db.StringProperty(multiline=False,default='') - linkcomment = db.StringProperty(multiline=False,default='') - createdate=db.DateTimeProperty(auto_now=True) + href = db.StringProperty(default='') + linktype = db.StringProperty(default='blogroll') + linktext = db.StringProperty(default='') + linkcomment = db.StringProperty(default='') + createdate=db.DateTimeProperty(auto_now=True) - @property - def get_icon_url(self): - "get ico url of the wetsite" - ico_path = '/favicon.ico' - ix = self.href.find('/',len('http://') ) - return (ix>0 and self.href[:ix] or self.href ) + ico_path + @property + def get_icon_url(self): + """get ico url of the wetsite""" + ico_path = '/favicon.ico' + ix = self.href.find('/',len('http://') ) + return (ix>0 and self.href[:ix] or self.href ) + ico_path - def put(self): - db.Model.put(self) - g_blog.tigger_action("save_link",self) + def put(self): + db.Model.put(self) + g_blog.tigger_action("save_link",self) - def delete(self): - db.Model.delete(self) - g_blog.tigger_action("delete_link",self) + def delete(self): + db.Model.delete(self) + g_blog.tigger_action("delete_link",self) class Entry(BaseModel): - author = db.UserProperty() - author_name = db.StringProperty() - published = db.BooleanProperty(default=False) - content = db.TextProperty(default='') - readtimes = db.IntegerProperty(default=0) - title = db.StringProperty(multiline=False,default='') - date = db.DateTimeProperty(auto_now_add=True) - mod_date = db.DateTimeProperty(auto_now_add=True) - tags = db.StringListProperty() - categorie_keys=db.ListProperty(db.Key) - slug = db.StringProperty(multiline=False,default='') - link= db.StringProperty(multiline=False,default='') - monthyear = db.StringProperty(multiline=False) - entrytype = db.StringProperty(multiline=False,default='post',choices=[ - 'post','page']) - entry_parent=db.IntegerProperty(default=0)#When level=0 show on main menu. - menu_order=db.IntegerProperty(default=0) - commentcount = db.IntegerProperty(default=0) - trackbackcount = db.IntegerProperty(default=0) - - allow_comment = db.BooleanProperty(default=True) #allow comment - #allow_pingback=db.BooleanProperty(default=False) - allow_trackback=db.BooleanProperty(default=True) - password=db.StringProperty() - - #compatible with wordpress - is_wp=db.BooleanProperty(default=False) - post_id= db.IntegerProperty() - excerpt=db.StringProperty(multiline=True) - - #external page - is_external_page=db.BooleanProperty(default=False) - target=db.StringProperty(default="_self") - external_page_address=db.StringProperty() - - #keep in top - sticky=db.BooleanProperty(default=False) - - - postname='' - _relatepost=None - - @property - def content_excerpt(self): - return self.get_content_excerpt(_('..more').decode('utf8')) - - - def get_author_user(self): - if not self.author: - self.author=g_blog.owner - return User.all().filter('email =',self.author.email()).get() - - def get_content_excerpt(self,more='..more'): - if g_blog.show_excerpt: - if self.excerpt: - return self.excerpt+' %s'%(self.link,more) - else: - sc=self.content.split('') - if len(sc)>1: - return sc[0]+u' %s'%(self.link,more) - else: - return sc[0] - else: - return self.content - - def slug_onchange(self,curval,newval): - if not (curval==newval): - self.setpostname(newval) - - def setpostname(self,newval): - #check and fix double slug - if newval: - slugcount=Entry.all()\ - .filter('entrytype',self.entrytype)\ - .filter('date <',self.date)\ - .filter('slug =',newval)\ - .filter('published',True)\ - .count() - if slugcount>0: - self.postname=newval+str(slugcount) - else: - self.postname=newval - else: - self.postname="" - - - - - @property - def fullurl(self): - return g_blog.baseurl+'/'+self.link; - - @property - def categories(self): - try: - return db.get(self.categorie_keys) - except: - return [] - - @property - def post_status(self): - return self.published and 'publish' or 'draft' - - def settags(self,values): - if not values:tags=[] - if type(values)==type([]): - tags=values - else: - tags=values.split(',') - - - - if not self.tags: - removelist=[] - addlist=tags - else: - #search different tags - removelist=[n for n in self.tags if n not in tags] - addlist=[n for n in tags if n not in self.tags] - for v in removelist: - Tag.remove(v) - for v in addlist: - Tag.add(v) - self.tags=tags - - def get_comments_by_page(self,index,psize): - return self.comments().fetch(psize,offset = (index-1) * psize) - - @property - def strtags(self): - return ','.join(self.tags) - - @property - def edit_url(self): - return '/admin/%s?key=%s&action=edit'%(self.entrytype,self.key()) - - def comments(self): - if g_blog.comments_order: - return Comment.all().filter('entry =',self).order('-date') - else: - return Comment.all().filter('entry =',self).order('date') - - def purecomments(self): - if g_blog.comments_order: - return Comment.all().filter('entry =',self).filter('ctype =',0).order('-date') - else: - return Comment.all().filter('entry =',self).filter('ctype =',0).order('date') - - def trackcomments(self): - if g_blog.comments_order: - return Comment.all().filter('entry =',self).filter('ctype IN',[1,2]).order('-date') - else: - return Comment.all().filter('entry =',self).filter('ctype IN',[1,2]).order('date') - def commentsTops(self): - return [c for c in self.purecomments() if c.parent_key()==None] - - def delete_comments(self): - cmts = Comment.all().filter('entry =',self) - for comment in cmts: - comment.delete() - self.commentcount = 0 - self.trackbackcount = 0 - def update_commentno(self): - cmts = Comment.all().filter('entry =',self).order('date') - i=1 - for comment in cmts: - comment.no=i - i+=1 - comment.store() - - def update_archive(self,cnt=1): - """Checks to see if there is a month-year entry for the - month of current blog, if not creates it and increments count""" - my = self.date.strftime('%B %Y') # September-2008 - sy = self.date.strftime('%Y') #2008 - sm = self.date.strftime('%m') #09 - - - archive = Archive.all().filter('monthyear',my).get() - if self.entrytype == 'post': - if not archive: - archive = Archive(monthyear=my,year=sy,month=sm,entrycount=1) - self.monthyear = my - archive.put() - else: - # ratchet up the count - archive.entrycount += cnt - archive.put() - g_blog.entrycount+=cnt - g_blog.put() - - - def save(self,is_publish=False): - """ - Use this instead of self.put(), as we do some other work here - @is_pub:Check if need publish id - """ - g_blog.tigger_action("pre_save_post",self,is_publish) - my = self.date.strftime('%B %Y') # September 2008 - self.monthyear = my - old_publish=self.published - self.mod_date=datetime.now() - - if is_publish: - if not self.is_wp: - self.put() - self.post_id=self.key().id() - - #fix for old version - if not self.postname: - self.setpostname(self.slug) - - - vals={'year':self.date.year,'month':str(self.date.month).zfill(2),'day':self.date.day, - 'postname':self.postname,'post_id':self.post_id} - - - if self.entrytype=='page': - if self.slug: - self.link=self.postname - else: - #use external page address as link - if self.is_external_page: - self.link=self.external_page_address - else: - self.link=g_blog.default_link_format%vals - else: - if g_blog.link_format and self.postname: - self.link=g_blog.link_format.strip()%vals - else: - self.link=g_blog.default_link_format%vals - - self.published=is_publish - self.put() - - if is_publish: - if g_blog.sitemap_ping: - Sitemap_NotifySearch() - - if old_publish and not is_publish: - self.update_archive(-1) - if not old_publish and is_publish: - self.update_archive(1) - - self.removecache() - - self.put() - g_blog.tigger_action("save_post",self,is_publish) - - - - - def removecache(self): - memcache.delete('/') - memcache.delete('/'+self.link) - memcache.delete('/sitemap') - memcache.delete('blog.postcount') - g_blog.tigger_action("clean_post_cache",self) - - @property - def next(self): - return Entry.all().filter('entrytype =','post').filter("published =", True).order('date').filter('date >',self.date).fetch(1) - - - @property - def prev(self): - return Entry.all().filter('entrytype =','post').filter("published =", True).order('-date').filter('date <',self.date).fetch(1) - - @property - def relateposts(self): - if self._relatepost: - return self._relatepost - else: - if self.tags: - self._relatepost= Entry.gql("WHERE published=True and tags IN :1 and post_id!=:2 order by post_id desc ",self.tags,self.post_id).fetch(5) - else: - self._relatepost= [] - return self._relatepost - - @property - def trackbackurl(self): - if self.link.find("?")>-1: - return g_blog.baseurl+"/"+self.link+"&code="+str(self.key()) - else: - return g_blog.baseurl+"/"+self.link+"?code="+str(self.key()) - - def getbylink(self): - pass - - def delete(self): - g_blog.tigger_action("pre_delete_post",self) - if self.published: - self.update_archive(-1) - self.delete_comments() - db.Model.delete(self) - g_blog.tigger_action("delete_post",self) + author = db.UserProperty() + author_name = db.StringProperty() + published = db.BooleanProperty(default=False) + content = db.TextProperty(default='') + readtimes = db.IntegerProperty(default=0) + title = db.StringProperty(default='') + date = db.DateTimeProperty(auto_now_add=True) + mod_date = db.DateTimeProperty(auto_now_add=True) + tags = db.StringListProperty() + categorie_keys=db.ListProperty(db.Key) + slug = db.StringProperty(default='') + link= db.StringProperty(default='') + monthyear = db.StringProperty() + entrytype = db.StringProperty(default='post',choices=[ + 'post','page']) + entry_parent=db.IntegerProperty(default=0) #When level=0 show on main menu. + menu_order=db.IntegerProperty(default=0) + commentcount = db.IntegerProperty(default=0) + trackbackcount = db.IntegerProperty(default=0) + + allow_comment = db.BooleanProperty(default=True) #allow comment + #allow_pingback=db.BooleanProperty(default=False) + allow_trackback=db.BooleanProperty(default=True) + password=db.StringProperty() + + #compatible with wordpress + is_wp=db.BooleanProperty(default=False) + post_id= db.IntegerProperty() + excerpt=db.StringProperty(multiline=True) + + #external page + is_external_page=db.BooleanProperty(default=False) + target=db.StringProperty(default="_self") + external_page_address=db.StringProperty() + + #keep in top + sticky=db.BooleanProperty(default=False) + + postname='' + _relatepost=None + + @property + def content_excerpt(self): + return self.get_content_excerpt(_('..more')) + + def get_author_user(self): + if not self.author: + self.author=g_blog.owner + return User.all().filter('email =',self.author.email()).get() + + def get_content_excerpt(self,more='..more'): + if g_blog.show_excerpt: + if self.excerpt: + return self.excerpt+' %s'%(self.link,more) + else: + sc=self.content.split('') + if len(sc)>1: + return sc[0]+u' %s'%(self.link,more) + else: + return sc[0] + else: + return self.content + + def slug_onchange(self,curval,newval): + if not (curval==newval): + self.setpostname(newval) + + def setpostname(self,newval): + #check and fix double slug + if newval: + slugcount=Entry.all()\ + .filter('entrytype',self.entrytype)\ + .filter('date <',self.date)\ + .filter('slug =',newval)\ + .filter('published',True)\ + .count() + if slugcount>0: + self.postname=newval+str(slugcount) + else: + self.postname=newval + else: + self.postname="" + + @property + def fullurl(self): + return g_blog.baseurl+'/'+self.link; + + @property + def categories(self): + try: + return db.get(self.categorie_keys) + except: + return [] + + @property + def post_status(self): + return self.published and 'publish' or 'draft' + + def settags(self,values): + if not values:tags=[] + if type(values)==type([]): + tags=values + else: + tags=values.split(',') + + if not self.tags: + removelist=[] + addlist=tags + else: + #search different tags + removelist=[n for n in self.tags if n not in tags] + addlist=[n for n in tags if n not in self.tags] + for v in removelist: + Tag.remove(v) + for v in addlist: + Tag.add(v) + self.tags=tags + + def get_comments_by_page(self,index,psize): + return self.comments().fetch(psize,offset = (index-1) * psize) + + @property + def strtags(self): + return ','.join(self.tags) + + @property + def edit_url(self): + return '/admin/%s?key=%s&action=edit'%(self.entrytype,self.key()) + + def comments(self): + if g_blog.comments_order: + return Comment.all().filter('entry =',self).order('-date') + else: + return Comment.all().filter('entry =',self).order('date') + + def purecomments(self): + if g_blog.comments_order: + return Comment.all().filter('entry =',self).filter('ctype =',0).order('-date') + else: + return Comment.all().filter('entry =',self).filter('ctype =',0).order('date') + + def trackcomments(self): + if g_blog.comments_order: + return Comment.all().filter('entry =',self).filter('ctype IN',[1,2]).order('-date') + else: + return Comment.all().filter('entry =',self).filter('ctype IN',[1,2]).order('date') + def commentsTops(self): + return [c for c in self.purecomments() if c.parent_key()==None] + + def delete_comments(self): + cmts = Comment.all().filter('entry =',self) + for comment in cmts: + comment.delete() + self.commentcount = 0 + self.trackbackcount = 0 + def update_commentno(self): + cmts = Comment.all().filter('entry =',self).order('date') + i=1 + for comment in cmts: + if comment.no != i: + comment.no = i + comment.store() + i+=1 + + def update_archive(self,cnt=1): + """Checks to see if there is a month-year entry for the + month of current blog, if not creates it and increments count""" + my = self.date.strftime('%B %Y') # September-2008 + sy = self.date.strftime('%Y') #2008 + sm = self.date.strftime('%m') #09 + + + archive = Archive.all().filter('monthyear',my).get() + if self.entrytype == 'post': + if not archive: + archive = Archive(monthyear=my,year=sy,month=sm,entrycount=1) + self.monthyear = my + archive.put() + else: + # ratchet up the count + archive.entrycount += cnt + archive.put() + g_blog.entrycount+=cnt + g_blog.put() + + + def save(self,is_publish=False): + """ + Use this instead of self.put(), as we do some other work here + @is_pub:Check if need publish id + """ + g_blog.tigger_action("pre_save_post",self,is_publish) + my = self.date.strftime('%B %Y') # September 2008 + self.monthyear = my + old_publish=self.published + self.mod_date=datetime.now() + + if is_publish: + if not self.is_wp: + self.put() + self.post_id=self.key().id() + + #fix for old version + if not self.postname: + self.setpostname(self.slug) + + + vals={'year':self.date.year,'month':str(self.date.month).zfill(2),'day':self.date.day, + 'postname':self.postname,'post_id':self.post_id} + + + if self.entrytype=='page': + if self.slug: + self.link=self.postname + else: + #use external page address as link + if self.is_external_page: + self.link=self.external_page_address + else: + self.link=g_blog.default_link_format%vals + else: + if g_blog.link_format and self.postname: + self.link=g_blog.link_format.strip()%vals + else: + self.link=g_blog.default_link_format%vals + + self.published=is_publish + self.put() + + if is_publish: + if g_blog.sitemap_ping: + Sitemap_NotifySearch() + + if old_publish and not is_publish: + self.update_archive(-1) + if not old_publish and is_publish: + self.update_archive(1) + + self.removecache() + + self.put() + g_blog.tigger_action("save_post",self,is_publish) + + def removecache(self): + memcache.delete('/') + memcache.delete('/'+self.link) + memcache.delete('/sitemap') + memcache.delete('blog.postcount') + g_blog.tigger_action("clean_post_cache",self) + + @property + def next(self): + return Entry.all().filter('entrytype =','post').filter("published =", True).order('date').filter('date >',self.date).fetch(1) + + + @property + def prev(self): + return Entry.all().filter('entrytype =','post').filter("published =", True).order('-date').filter('date <',self.date).fetch(1) + + @property + def relateposts(self): + if self._relatepost: + return self._relatepost + else: + if self.tags: + try: self._relatepost= Entry.gql("WHERE published=True and tags IN :1 and post_id!=:2 order by post_id desc ",self.tags,self.post_id).fetch(5) + except: self._relatepost= [] + else: + self._relatepost= [] + return self._relatepost + + @property + def trackbackurl(self): + if self.link.find("?")>-1: + return g_blog.baseurl+"/"+self.link+"&code="+str(self.key()) + else: + return g_blog.baseurl+"/"+self.link+"?code="+str(self.key()) + + def getbylink(self): + pass + + def delete(self): + g_blog.tigger_action("pre_delete_post",self) + if self.published: + self.update_archive(-1) + self.delete_comments() + db.Model.delete(self) + g_blog.tigger_action("delete_post",self) class User(db.Model): - user = db.UserProperty(required = False) - dispname = db.StringProperty() - email=db.StringProperty() - website = db.LinkProperty() - isadmin=db.BooleanProperty(default=False) - isAuthor=db.BooleanProperty(default=True) - #rpcpwd=db.StringProperty() - - def __unicode__(self): - #if self.dispname: - return self.dispname - #else: - # return self.user.nickname() - - def __str__(self): - return self.__unicode__().encode('utf-8') + user = db.UserProperty(required = False) + dispname = db.StringProperty() + email=db.StringProperty() + website = db.LinkProperty() + isadmin=db.BooleanProperty(default=False) + isAuthor=db.BooleanProperty(default=True) + #rpcpwd=db.StringProperty() + + def __unicode__(self): + #if self.dispname: + return self.dispname + #else: + # return self.user.nickname() + + def __str__(self): + return self.__unicode__().encode('utf-8') COMMENT_NORMAL=0 COMMENT_TRACKBACK=1 COMMENT_PINGBACK=2 class Comment(db.Model): - entry = db.ReferenceProperty(Entry) - date = db.DateTimeProperty(auto_now_add=True) - content = db.TextProperty(required=True) - author=db.StringProperty() - email=db.EmailProperty() - weburl=db.URLProperty() - status=db.IntegerProperty(default=0) - reply_notify_mail=db.BooleanProperty(default=False) - ip=db.StringProperty() - ctype=db.IntegerProperty(default=COMMENT_NORMAL) - no=db.IntegerProperty(default=0) - comment_order=db.IntegerProperty(default=1) - - @property - def mpindex(self): - count=self.entry.commentcount - no=self.no - if g_blog.comments_order: - no=count-no+1 - index=no / g_blog.comments_per_page - if no % g_blog.comments_per_page or no==0: - index+=1 - return index - - @property - def shortcontent(self,len=20): - scontent=self.content - scontent=re.sub(r'',' ',scontent) - scontent=re.sub(r'<[^>]+>','',scontent) - scontent=re.sub(r'(@[\S]+)-\d{2,7}',r'\1:',scontent) - return scontent[:len].replace('<','<').replace('>','>') - - - def gravatar_url(self): - - # Set your variables here - if g_blog.avatar_style==0: - default = g_blog.baseurl+'/static/images/homsar.jpeg' - else: - default='identicon' - - if not self.email: - return default - - size = 50 - - try: - # construct the url - imgurl = "http://www.gravatar.com/avatar/" - imgurl +=hashlib.md5(self.email.lower()).hexdigest()+"?"+ urllib.urlencode({ - 'd':default, 's':str(size),'r':'G'}) - - return imgurl - except: - return default - - def save(self): - - - self.put() - self.entry.commentcount+=1 - self.comment_order=self.entry.commentcount - if (self.ctype == COMMENT_TRACKBACK) or (self.ctype == COMMENT_PINGBACK): - self.entry.trackbackcount+=1 - self.entry.put() - memcache.delete("/"+self.entry.link) - return True - - def delit(self): - self.entry.commentcount-=1 - if self.entry.commentcount<0: - self.entry.commentcount = 0 - if (self.ctype == COMMENT_TRACKBACK) or (self.ctype == COMMENT_PINGBACK): - self.entry.trackbackcount-=1 - if self.entry.trackbackcount<0: - self.entry.trackbackcount = 0 - self.entry.put() - self.delete() - - def put(self): - g_blog.tigger_action("pre_comment",self) - db.Model.put(self) - g_blog.tigger_action("save_comment",self) - - def delete(self): - db.Model.delete(self) - g_blog.tigger_action("delete_comment",self) - - @property - def children(self): - key=self.key() - comments=Comment.all().ancestor(self) - return [c for c in comments if c.parent_key()==key] - - def store(self, **kwargs): - rpc = datastore.GetRpcFromKwargs(kwargs) - self._populate_internal_entity() - return datastore.Put(self._entity, rpc=rpc) + entry = db.ReferenceProperty(Entry) + date = db.DateTimeProperty(auto_now_add=True) + content = db.TextProperty(required=True) + author=db.StringProperty() + email=db.EmailProperty() + weburl=db.URLProperty() + status=db.IntegerProperty(default=0) + reply_notify_mail=db.BooleanProperty(default=False) + ip=db.StringProperty() + ctype=db.IntegerProperty(default=COMMENT_NORMAL) + no=db.IntegerProperty(default=0) + comment_order=db.IntegerProperty(default=1) + + @property + def mpindex(self): + count=self.entry.commentcount + no=self.no + if g_blog.comments_order: + no=count-no+1 + index=no / g_blog.comments_per_page + if no % g_blog.comments_per_page or no==0: + index+=1 + return index + + @property + def shortcontent(self,len=20): + scontent=self.content + scontent=re.sub(r'',' ',scontent) + scontent=re.sub(r'<[^>]+>','',scontent) + scontent=re.sub(r'(@[\S]+)-\d{2,7}',r'\1:',scontent) + return scontent[:len].replace('<','<').replace('>','>') + + + def gravatar_url(self): + # Set your variables here + if g_blog.avatar_style==0: + default = g_blog.baseurl+'/static/images/homsar.jpeg' + else: + default='identicon' + + if not self.email: + return default + + size = 50 + + try: + # construct the url + imgurl = "http://www.gravatar.com/avatar/" + imgurl +=hashlib.md5(self.email.lower()).hexdigest()+"?"+ urllib.urlencode({ + 'd':default, 's':str(size),'r':'G'}) + return imgurl + except: + return default + + def save(self): + self.put() + self.comment_order=self.entry.commentcount + self.entry.commentcount+=1 + if (self.ctype == COMMENT_TRACKBACK) or (self.ctype == COMMENT_PINGBACK): + self.entry.trackbackcount+=1 + self.entry.put() + memcache.delete("/"+self.entry.link) + return True + + def delit(self): + self.entry.commentcount-=1 + if self.entry.commentcount<0: + self.entry.commentcount = 0 + if (self.ctype == COMMENT_TRACKBACK) or (self.ctype == COMMENT_PINGBACK): + self.entry.trackbackcount-=1 + if self.entry.trackbackcount<0: + self.entry.trackbackcount = 0 + self.entry.put() + self.delete() + + def put(self): + g_blog.tigger_action("pre_comment",self) + db.Model.put(self) + g_blog.tigger_action("save_comment",self) + + def delete(self): + db.Model.delete(self) + g_blog.tigger_action("delete_comment",self) + + @property + def children(self): + key=self.key() + comments=Comment.all().ancestor(self) + return [c for c in comments if c.parent_key()==key] + + def store(self, **kwargs): + rpc = datastore.GetRpcFromKwargs(kwargs) + self._populate_internal_entity() + return datastore.Put(self._entity, rpc=rpc) class Media(db.Model): - name =db.StringProperty() - mtype=db.StringProperty() - bits=db.BlobProperty() - date=db.DateTimeProperty(auto_now_add=True) - download=db.IntegerProperty(default=0) - - @property - def size(self): - return len(self.bits) - + name =db.StringProperty() + mtype=db.StringProperty() + bits=db.BlobProperty() + date=db.DateTimeProperty(auto_now_add=True) + download=db.IntegerProperty(default=0) + @property + def size(self): + return len(self.bits) class OptionSet(db.Model): - name=db.StringProperty() - value=db.TextProperty() - #blobValue=db.BlobProperty() - #isBlob=db.BooleanProperty() - - @classmethod - def getValue(cls,name,default=None): - try: - opt=OptionSet.get_by_key_name(name) - return pickle.loads(str(opt.value)) - except: - return default - - @classmethod - def setValue(cls,name,value): - opt=OptionSet.get_or_insert(name) - opt.name=name - opt.value=pickle.dumps(value) - opt.put() - - @classmethod - def remove(cls,name): - opt= OptionSet.get_by_key_name(name) - if opt: - opt.delete() + name=db.StringProperty() + value=db.TextProperty() + #blobValue=db.BlobProperty() + #isBlob=db.BooleanProperty() + + @classmethod + def getValue(cls,name,default=None): + try: + opt=OptionSet.get_by_key_name(name) + return pickle.loads(str(opt.value)) + except: + return default + + @classmethod + def setValue(cls,name,value): + opt=OptionSet.get_or_insert(name) + opt.name=name + opt.value=pickle.dumps(value) + opt.put() + + @classmethod + def remove(cls,name): + opt= OptionSet.get_by_key_name(name) + if opt: + opt.delete() NOTIFICATION_SITES = [ ('http', 'www.google.com', 'webmasters/sitemaps/ping', {}, '', 'sitemap') @@ -852,85 +819,87 @@ def remove(cls,name): def Sitemap_NotifySearch(): - """ Send notification of the new Sitemap(s) to the search engines. """ - - - url=g_blog.baseurl+"/sitemap" - - # Cycle through notifications - # To understand this, see the comment near the NOTIFICATION_SITES comment - for ping in NOTIFICATION_SITES: - query_map = ping[3] - query_attr = ping[5] - query_map[query_attr] = url - query = urllib.urlencode(query_map) - notify = urlparse.urlunsplit((ping[0], ping[1], ping[2], query, ping[4])) - # Send the notification - logging.info('Notifying search engines. %s'%ping[1]) - logging.info('url: %s'%notify) - try: - result = urlfetch.fetch(notify) - if result.status_code == 200: - logging.info('Notify Result: %s' % result.content) - if result.status_code == 404: - logging.info('HTTP error 404: Not Found') - logging.warning('Cannot contact: %s' % ping[1]) - - except : - logging.error('Cannot contact: %s' % ping[1]) + """ Send notification of the new Sitemap(s) to the search engines. """ + + + url=g_blog.baseurl+"/sitemap" + + # Cycle through notifications + # To understand this, see the comment near the NOTIFICATION_SITES comment + for ping in NOTIFICATION_SITES: + query_map = ping[3] + query_attr = ping[5] + query_map[query_attr] = url + query = urllib.urlencode(query_map) + notify = urlparse.urlunsplit((ping[0], ping[1], ping[2], query, ping[4])) + # Send the notification + logging.info('Notifying search engines. %s'%ping[1]) + logging.info('url: %s'%notify) + try: + result = urlfetch.fetch(notify) + if result.status_code == 200: + logging.info('Notify Result: %s' % result.content) + if result.status_code == 404: + logging.info('HTTP error 404: Not Found') + logging.warning('Cannot contact: %s' % ping[1]) + + except : + logging.error('Cannot contact: %s' % ping[1]) def InitBlogData(): - global g_blog - OptionSet.setValue('PluginActive',[u'googleAnalytics', u'wordpress', u'sys_plugin']) - - g_blog = Blog(key_name = 'default') - g_blog.domain=os.environ['HTTP_HOST'] - g_blog.baseurl="http://"+g_blog.domain - g_blog.feedurl=g_blog.baseurl+"/feed" - os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - lang="zh-cn" - if os.environ.has_key('HTTP_ACCEPT_LANGUAGE'): - lang=os.environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0] - from django.utils.translation import activate,to_locale - g_blog.language=to_locale(lang) - from django.conf import settings - settings._target = None - activate(g_blog.language) - g_blog.save() - - entry=Entry(title=_("Hello world!").decode('utf8')) - entry.content=_('

    Welcome to micolog. This is your first post. Edit or delete it, then start blogging!

    ').decode('utf8') - entry.save(True) - link=Link(href='http://xuming.net',linktext=_("Xuming's blog").decode('utf8')) - link.put() - return g_blog + global g_blog + OptionSet.setValue('PluginActive',[u'googleAnalytics', u'wordpress', u'sys_plugin']) + + g_blog = Blog(key_name = 'default') + g_blog.domain=os.environ['HTTP_HOST'] + g_blog.baseurl="http://"+g_blog.domain + g_blog.feedurl=g_blog.baseurl+"/feed" + os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + g_blog.admin_essential = False + if os.environ.has_key('HTTP_ACCEPT_LANGUAGE'): + lang=os.environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0] + from django.utils.translation import activate,to_locale + g_blog.language=to_locale(lang) + g_blog.admin_essential=False + from django.conf import settings + settings._target = None + activate(g_blog.language) + g_blog.save() + + entry=Entry(title="Hello world!".decode('utf8')) + entry.content='

    Welcome to micolog %s. This is your first post. Edit or delete it, then start blogging!

    '%g_blog.version + entry.save(True) + link=Link(href='http://xuming.net',linktext="Xuming's blog".decode('utf8')) + link.put() + link=Link(href='http://eric.cloud-mes.com/',linktext="Eric Guo's blog".decode('utf8')) + link.put() + return g_blog def gblog_init(): - global g_blog - try: - if g_blog : - return g_blog - except: - pass - g_blog = Blog.get_by_key_name('default') - if not g_blog: - g_blog=InitBlogData() - - - g_blog.get_theme() - g_blog.rootdir=os.path.dirname(__file__) - return g_blog + global g_blog + try: + if g_blog : + return g_blog + except: + pass + g_blog = Blog.get_by_key_name('default') + if not g_blog: + g_blog=InitBlogData() + + g_blog.get_theme() + g_blog.rootdir=os.path.dirname(__file__) + return g_blog try: - g_blog=gblog_init() + g_blog=gblog_init() - os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - from django.utils.translation import activate - from django.conf import settings - settings._target = None - activate(g_blog.language) + os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + from django.utils.translation import activate + from django.conf import settings + settings._target = None + activate(g_blog.language) except: - pass + pass diff --git a/no data run.bat b/no data run.bat index 3434249..9b2db67 100644 --- a/no data run.bat +++ b/no data run.bat @@ -1 +1 @@ -dev_appserver.py -c ..\micolog \ No newline at end of file +dev_appserver.py --skip_sdk_update_check -c --debug . \ No newline at end of file diff --git a/plugins/live_import/BeautifulSoup.py b/plugins/live_import/BeautifulSoup.py new file mode 100644 index 0000000..4b17b85 --- /dev/null +++ b/plugins/live_import/BeautifulSoup.py @@ -0,0 +1,2014 @@ +"""Beautiful Soup +Elixir and Tonic +"The Screen-Scraper's Friend" +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup parses a (possibly invalid) XML or HTML document into a +tree representation. It provides methods and Pythonic idioms that make +it easy to navigate, search, and modify the tree. + +A well-formed XML/HTML document yields a well-formed data +structure. An ill-formed XML/HTML document yields a correspondingly +ill-formed data structure. If your document is only locally +well-formed, you can use this library to find and process the +well-formed part of it. + +Beautiful Soup works with Python 2.2 and up. It has no external +dependencies, but you'll have more success at converting data to UTF-8 +if you also install these three packages: + +* chardet, for auto-detecting character encodings + http://chardet.feedparser.org/ +* cjkcodecs and iconv_codec, which add more encodings to the ones supported + by stock Python. + http://cjkpython.i18n.org/ + +Beautiful Soup defines classes for two main parsing strategies: + + * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific + language that kind of looks like XML. + + * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid + or invalid. This class has web browser-like heuristics for + obtaining a sensible parse tree in the face of common HTML errors. + +Beautiful Soup also defines a class (UnicodeDammit) for autodetecting +the encoding of an HTML or XML document, and converting it to +Unicode. Much of this code is taken from Mark Pilgrim's Universal Feed Parser. + +For more than you ever wanted to know about Beautiful Soup, see the +documentation: +http://www.crummy.com/software/BeautifulSoup/documentation.html + +Here, have some legalese: + +Copyright (c) 2004-2010, Leonard Richardson + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the the Beautiful Soup Consortium and All + Night Kosher Bakery nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE, DAMMIT. + +""" +from __future__ import generators + +__author__ = "Leonard Richardson (leonardr@segfault.org)" +__version__ = "3.2.0" +__copyright__ = "Copyright (c) 2004-2010 Leonard Richardson" +__license__ = "New-style BSD" + +from sgmllib import SGMLParser, SGMLParseError +import codecs +import markupbase +import types +import re +import sgmllib +try: + from htmlentitydefs import name2codepoint +except ImportError: + name2codepoint = {} +try: + set +except NameError: + from sets import Set as set + +#These hacks make Beautiful Soup able to parse XML with namespaces +sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') +markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match + +DEFAULT_OUTPUT_ENCODING = "utf-8" + +def _match_css_class(str): + """Build a RE to match the given CSS class.""" + return re.compile(r"(^|.*\s)%s($|\s)" % str) + +# First, the classes that represent markup elements. + +class PageElement(object): + """Contains the navigational information for some part of the page + (either a tag or a piece of text)""" + + def setup(self, parent=None, previous=None): + """Sets up the initial relations between this element and + other elements.""" + self.parent = parent + self.previous = previous + self.next = None + self.previousSibling = None + self.nextSibling = None + if self.parent and self.parent.contents: + self.previousSibling = self.parent.contents[-1] + self.previousSibling.nextSibling = self + + def replaceWith(self, replaceWith): + oldParent = self.parent + myIndex = self.parent.index(self) + if hasattr(replaceWith, "parent")\ + and replaceWith.parent is self.parent: + # We're replacing this element with one of its siblings. + index = replaceWith.parent.index(replaceWith) + if index and index < myIndex: + # Furthermore, it comes before this element. That + # means that when we extract it, the index of this + # element will change. + myIndex = myIndex - 1 + self.extract() + oldParent.insert(myIndex, replaceWith) + + def replaceWithChildren(self): + myParent = self.parent + myIndex = self.parent.index(self) + self.extract() + reversedChildren = list(self.contents) + reversedChildren.reverse() + for child in reversedChildren: + myParent.insert(myIndex, child) + + def extract(self): + """Destructively rips this element out of the tree.""" + if self.parent: + try: + del self.parent.contents[self.parent.index(self)] + except ValueError: + pass + + #Find the two elements that would be next to each other if + #this element (and any children) hadn't been parsed. Connect + #the two. + lastChild = self._lastRecursiveChild() + nextElement = lastChild.next + + if self.previous: + self.previous.next = nextElement + if nextElement: + nextElement.previous = self.previous + self.previous = None + lastChild.next = None + + self.parent = None + if self.previousSibling: + self.previousSibling.nextSibling = self.nextSibling + if self.nextSibling: + self.nextSibling.previousSibling = self.previousSibling + self.previousSibling = self.nextSibling = None + return self + + def _lastRecursiveChild(self): + "Finds the last element beneath this object to be parsed." + lastChild = self + while hasattr(lastChild, 'contents') and lastChild.contents: + lastChild = lastChild.contents[-1] + return lastChild + + def insert(self, position, newChild): + if isinstance(newChild, basestring) \ + and not isinstance(newChild, NavigableString): + newChild = NavigableString(newChild) + + position = min(position, len(self.contents)) + if hasattr(newChild, 'parent') and newChild.parent is not None: + # We're 'inserting' an element that's already one + # of this object's children. + if newChild.parent is self: + index = self.index(newChild) + if index > position: + # Furthermore we're moving it further down the + # list of this object's children. That means that + # when we extract this element, our target index + # will jump down one. + position = position - 1 + newChild.extract() + + newChild.parent = self + previousChild = None + if position == 0: + newChild.previousSibling = None + newChild.previous = self + else: + previousChild = self.contents[position-1] + newChild.previousSibling = previousChild + newChild.previousSibling.nextSibling = newChild + newChild.previous = previousChild._lastRecursiveChild() + if newChild.previous: + newChild.previous.next = newChild + + newChildsLastElement = newChild._lastRecursiveChild() + + if position >= len(self.contents): + newChild.nextSibling = None + + parent = self + parentsNextSibling = None + while not parentsNextSibling: + parentsNextSibling = parent.nextSibling + parent = parent.parent + if not parent: # This is the last element in the document. + break + if parentsNextSibling: + newChildsLastElement.next = parentsNextSibling + else: + newChildsLastElement.next = None + else: + nextChild = self.contents[position] + newChild.nextSibling = nextChild + if newChild.nextSibling: + newChild.nextSibling.previousSibling = newChild + newChildsLastElement.next = nextChild + + if newChildsLastElement.next: + newChildsLastElement.next.previous = newChildsLastElement + self.contents.insert(position, newChild) + + def append(self, tag): + """Appends the given tag to the contents of this tag.""" + self.insert(len(self.contents), tag) + + def findNext(self, name=None, attrs={}, text=None, **kwargs): + """Returns the first item that matches the given criteria and + appears after this Tag in the document.""" + return self._findOne(self.findAllNext, name, attrs, text, **kwargs) + + def findAllNext(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Returns all items that match the given criteria and appear + after this Tag in the document.""" + return self._findAll(name, attrs, text, limit, self.nextGenerator, + **kwargs) + + def findNextSibling(self, name=None, attrs={}, text=None, **kwargs): + """Returns the closest sibling to this Tag that matches the + given criteria and appears after this Tag in the document.""" + return self._findOne(self.findNextSiblings, name, attrs, text, + **kwargs) + + def findNextSiblings(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Returns the siblings of this Tag that match the given + criteria and appear after this Tag in the document.""" + return self._findAll(name, attrs, text, limit, + self.nextSiblingGenerator, **kwargs) + fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x + + def findPrevious(self, name=None, attrs={}, text=None, **kwargs): + """Returns the first item that matches the given criteria and + appears before this Tag in the document.""" + return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs) + + def findAllPrevious(self, name=None, attrs={}, text=None, limit=None, + **kwargs): + """Returns all items that match the given criteria and appear + before this Tag in the document.""" + return self._findAll(name, attrs, text, limit, self.previousGenerator, + **kwargs) + fetchPrevious = findAllPrevious # Compatibility with pre-3.x + + def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs): + """Returns the closest sibling to this Tag that matches the + given criteria and appears before this Tag in the document.""" + return self._findOne(self.findPreviousSiblings, name, attrs, text, + **kwargs) + + def findPreviousSiblings(self, name=None, attrs={}, text=None, + limit=None, **kwargs): + """Returns the siblings of this Tag that match the given + criteria and appear before this Tag in the document.""" + return self._findAll(name, attrs, text, limit, + self.previousSiblingGenerator, **kwargs) + fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x + + def findParent(self, name=None, attrs={}, **kwargs): + """Returns the closest parent of this Tag that matches the given + criteria.""" + # NOTE: We can't use _findOne because findParents takes a different + # set of arguments. + r = None + l = self.findParents(name, attrs, 1) + if l: + r = l[0] + return r + + def findParents(self, name=None, attrs={}, limit=None, **kwargs): + """Returns the parents of this Tag that match the given + criteria.""" + + return self._findAll(name, attrs, None, limit, self.parentGenerator, + **kwargs) + fetchParents = findParents # Compatibility with pre-3.x + + #These methods do the real heavy lifting. + + def _findOne(self, method, name, attrs, text, **kwargs): + r = None + l = method(name, attrs, text, 1, **kwargs) + if l: + r = l[0] + return r + + def _findAll(self, name, attrs, text, limit, generator, **kwargs): + "Iterates over a generator looking for things that match." + + if isinstance(name, SoupStrainer): + strainer = name + # (Possibly) special case some findAll*(...) searches + elif text is None and not limit and not attrs and not kwargs: + # findAll*(True) + if name is True: + return [element for element in generator() + if isinstance(element, Tag)] + # findAll*('tag-name') + elif isinstance(name, basestring): + return [element for element in generator() + if isinstance(element, Tag) and + element.name == name] + else: + strainer = SoupStrainer(name, attrs, text, **kwargs) + # Build a SoupStrainer + else: + strainer = SoupStrainer(name, attrs, text, **kwargs) + results = ResultSet(strainer) + g = generator() + while True: + try: + i = g.next() + except StopIteration: + break + if i: + found = strainer.search(i) + if found: + results.append(found) + if limit and len(results) >= limit: + break + return results + + #These Generators can be used to navigate starting from both + #NavigableStrings and Tags. + def nextGenerator(self): + i = self + while i is not None: + i = i.next + yield i + + def nextSiblingGenerator(self): + i = self + while i is not None: + i = i.nextSibling + yield i + + def previousGenerator(self): + i = self + while i is not None: + i = i.previous + yield i + + def previousSiblingGenerator(self): + i = self + while i is not None: + i = i.previousSibling + yield i + + def parentGenerator(self): + i = self + while i is not None: + i = i.parent + yield i + + # Utility methods + def substituteEncoding(self, str, encoding=None): + encoding = encoding or "utf-8" + return str.replace("%SOUP-ENCODING%", encoding) + + def toEncoding(self, s, encoding=None): + """Encodes an object to a string in some encoding, or to Unicode. + .""" + if isinstance(s, unicode): + if encoding: + s = s.encode(encoding) + elif isinstance(s, str): + if encoding: + s = s.encode(encoding) + else: + s = unicode(s) + else: + if encoding: + s = self.toEncoding(str(s), encoding) + else: + s = unicode(s) + return s + +class NavigableString(unicode, PageElement): + + def __new__(cls, value): + """Create a new NavigableString. + + When unpickling a NavigableString, this method is called with + the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be + passed in to the superclass's __new__ or the superclass won't know + how to handle non-ASCII characters. + """ + if isinstance(value, unicode): + return unicode.__new__(cls, value) + return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING) + + def __getnewargs__(self): + return (NavigableString.__str__(self),) + + def __getattr__(self, attr): + """text.string gives you text. This is for backwards + compatibility for Navigable*String, but for CData* it lets you + get the string without the CData wrapper.""" + if attr == 'string': + return self + else: + raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + + def __unicode__(self): + return str(self).decode(DEFAULT_OUTPUT_ENCODING) + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + if encoding: + return self.encode(encoding) + else: + return self + +class CData(NavigableString): + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class ProcessingInstruction(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + output = self + if "%SOUP-ENCODING%" in output: + output = self.substituteEncoding(output, encoding) + return "" % self.toEncoding(output, encoding) + +class Comment(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class Declaration(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class Tag(PageElement): + + """Represents a found HTML tag with its attributes and contents.""" + + def _invert(h): + "Cheap function to invert a hash." + i = {} + for k,v in h.items(): + i[v] = k + return i + + XML_ENTITIES_TO_SPECIAL_CHARS = { "apos" : "'", + "quot" : '"', + "amp" : "&", + "lt" : "<", + "gt" : ">" } + + XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS) + + def _convertEntities(self, match): + """Used in a call to re.sub to replace HTML, XML, and numeric + entities with the appropriate Unicode characters. If HTML + entities are being converted, any unrecognized entities are + escaped.""" + x = match.group(1) + if self.convertHTMLEntities and x in name2codepoint: + return unichr(name2codepoint[x]) + elif x in self.XML_ENTITIES_TO_SPECIAL_CHARS: + if self.convertXMLEntities: + return self.XML_ENTITIES_TO_SPECIAL_CHARS[x] + else: + return u'&%s;' % x + elif len(x) > 0 and x[0] == '#': + # Handle numeric entities + if len(x) > 1 and x[1] == 'x': + return unichr(int(x[2:], 16)) + else: + return unichr(int(x[1:])) + + elif self.escapeUnrecognizedEntities: + return u'&%s;' % x + else: + return u'&%s;' % x + + def __init__(self, parser, name, attrs=None, parent=None, + previous=None): + "Basic constructor." + + # We don't actually store the parser object: that lets extracted + # chunks be garbage-collected + self.parserClass = parser.__class__ + self.isSelfClosing = parser.isSelfClosingTag(name) + self.name = name + if attrs is None: + attrs = [] + elif isinstance(attrs, dict): + attrs = attrs.items() + self.attrs = attrs + self.contents = [] + self.setup(parent, previous) + self.hidden = False + self.containsSubstitutions = False + self.convertHTMLEntities = parser.convertHTMLEntities + self.convertXMLEntities = parser.convertXMLEntities + self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities + + # Convert any HTML, XML, or numeric entities in the attribute values. + convert = lambda(k, val): (k, + re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);", + self._convertEntities, + val)) + self.attrs = map(convert, self.attrs) + + def getString(self): + if (len(self.contents) == 1 + and isinstance(self.contents[0], NavigableString)): + return self.contents[0] + + def setString(self, string): + """Replace the contents of the tag with a string""" + self.clear() + self.append(string) + + string = property(getString, setString) + + def getText(self, separator=u""): + if not len(self.contents): + return u"" + stopNode = self._lastRecursiveChild().next + strings = [] + current = self.contents[0] + while current is not stopNode: + if isinstance(current, NavigableString): + strings.append(current.strip()) + current = current.next + return separator.join(strings) + + text = property(getText) + + def get(self, key, default=None): + """Returns the value of the 'key' attribute for the tag, or + the value given for 'default' if it doesn't have that + attribute.""" + return self._getAttrMap().get(key, default) + + def clear(self): + """Extract all children.""" + for child in self.contents[:]: + child.extract() + + def index(self, element): + for i, child in enumerate(self.contents): + if child is element: + return i + raise ValueError("Tag.index: element not in tag") + + def has_key(self, key): + return self._getAttrMap().has_key(key) + + def __getitem__(self, key): + """tag[key] returns the value of the 'key' attribute for the tag, + and throws an exception if it's not there.""" + return self._getAttrMap()[key] + + def __iter__(self): + "Iterating over a tag iterates over its contents." + return iter(self.contents) + + def __len__(self): + "The length of a tag is the length of its list of contents." + return len(self.contents) + + def __contains__(self, x): + return x in self.contents + + def __nonzero__(self): + "A tag is non-None even if it has no contents." + return True + + def __setitem__(self, key, value): + """Setting tag[key] sets the value of the 'key' attribute for the + tag.""" + self._getAttrMap() + self.attrMap[key] = value + found = False + for i in range(0, len(self.attrs)): + if self.attrs[i][0] == key: + self.attrs[i] = (key, value) + found = True + if not found: + self.attrs.append((key, value)) + self._getAttrMap()[key] = value + + def __delitem__(self, key): + "Deleting tag[key] deletes all 'key' attributes for the tag." + for item in self.attrs: + if item[0] == key: + self.attrs.remove(item) + #We don't break because bad HTML can define the same + #attribute multiple times. + self._getAttrMap() + if self.attrMap.has_key(key): + del self.attrMap[key] + + def __call__(self, *args, **kwargs): + """Calling a tag like a function is the same as calling its + findAll() method. Eg. tag('a') returns a list of all the A tags + found within this tag.""" + return apply(self.findAll, args, kwargs) + + def __getattr__(self, tag): + #print "Getattr %s.%s" % (self.__class__, tag) + if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3: + return self.find(tag[:-3]) + elif tag.find('__') != 0: + return self.find(tag) + raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag) + + def __eq__(self, other): + """Returns true iff this tag has the same name, the same attributes, + and the same contents (recursively) as the given tag. + + NOTE: right now this will return false if two tags have the + same attributes in a different order. Should this be fixed?""" + if other is self: + return True + if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other): + return False + for i in range(0, len(self.contents)): + if self.contents[i] != other.contents[i]: + return False + return True + + def __ne__(self, other): + """Returns true iff this tag is not identical to the other tag, + as defined in __eq__.""" + return not self == other + + def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING): + """Renders this tag as a string.""" + return self.__str__(encoding) + + def __unicode__(self): + return self.__str__(None) + + BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|" + + "&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)" + + ")") + + def _sub_entity(self, x): + """Used with a regular expression to substitute the + appropriate XML entity for an XML special character.""" + return "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";" + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING, + prettyPrint=False, indentLevel=0): + """Returns a string or Unicode representation of this tag and + its contents. To get Unicode, pass None for encoding. + + NOTE: since Python's HTML parser consumes whitespace, this + method is not certain to reproduce the whitespace present in + the original string.""" + + encodedName = self.toEncoding(self.name, encoding) + + attrs = [] + if self.attrs: + for key, val in self.attrs: + fmt = '%s="%s"' + if isinstance(val, basestring): + if self.containsSubstitutions and '%SOUP-ENCODING%' in val: + val = self.substituteEncoding(val, encoding) + + # The attribute value either: + # + # * Contains no embedded double quotes or single quotes. + # No problem: we enclose it in double quotes. + # * Contains embedded single quotes. No problem: + # double quotes work here too. + # * Contains embedded double quotes. No problem: + # we enclose it in single quotes. + # * Embeds both single _and_ double quotes. This + # can't happen naturally, but it can happen if + # you modify an attribute value after parsing + # the document. Now we have a bit of a + # problem. We solve it by enclosing the + # attribute in single quotes, and escaping any + # embedded single quotes to XML entities. + if '"' in val: + fmt = "%s='%s'" + if "'" in val: + # TODO: replace with apos when + # appropriate. + val = val.replace("'", "&squot;") + + # Now we're okay w/r/t quotes. But the attribute + # value might also contain angle brackets, or + # ampersands that aren't part of entities. We need + # to escape those to XML entities too. + val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val) + + attrs.append(fmt % (self.toEncoding(key, encoding), + self.toEncoding(val, encoding))) + close = '' + closeTag = '' + if self.isSelfClosing: + close = ' /' + else: + closeTag = '' % encodedName + + indentTag, indentContents = 0, 0 + if prettyPrint: + indentTag = indentLevel + space = (' ' * (indentTag-1)) + indentContents = indentTag + 1 + contents = self.renderContents(encoding, prettyPrint, indentContents) + if self.hidden: + s = contents + else: + s = [] + attributeString = '' + if attrs: + attributeString = ' ' + ' '.join(attrs) + if prettyPrint: + s.append(space) + s.append('<%s%s%s>' % (encodedName, attributeString, close)) + if prettyPrint: + s.append("\n") + s.append(contents) + if prettyPrint and contents and contents[-1] != "\n": + s.append("\n") + if prettyPrint and closeTag: + s.append(space) + s.append(closeTag) + if prettyPrint and closeTag and self.nextSibling: + s.append("\n") + s = ''.join(s) + return s + + def decompose(self): + """Recursively destroys the contents of this tree.""" + self.extract() + if len(self.contents) == 0: + return + current = self.contents[0] + while current is not None: + next = current.next + if isinstance(current, Tag): + del current.contents[:] + current.parent = None + current.previous = None + current.previousSibling = None + current.next = None + current.nextSibling = None + current = next + + def prettify(self, encoding=DEFAULT_OUTPUT_ENCODING): + return self.__str__(encoding, True) + + def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING, + prettyPrint=False, indentLevel=0): + """Renders the contents of this tag as a string in the given + encoding. If encoding is None, returns a Unicode string..""" + s=[] + for c in self: + text = None + if isinstance(c, NavigableString): + text = c.__str__(encoding) + elif isinstance(c, Tag): + s.append(c.__str__(encoding, prettyPrint, indentLevel)) + if text and prettyPrint: + text = text.strip() + if text: + if prettyPrint: + s.append(" " * (indentLevel-1)) + s.append(text) + if prettyPrint: + s.append("\n") + return ''.join(s) + + #Soup methods + + def find(self, name=None, attrs={}, recursive=True, text=None, + **kwargs): + """Return only the first child of this Tag matching the given + criteria.""" + r = None + l = self.findAll(name, attrs, recursive, text, 1, **kwargs) + if l: + r = l[0] + return r + findChild = find + + def findAll(self, name=None, attrs={}, recursive=True, text=None, + limit=None, **kwargs): + """Extracts a list of Tag objects that match the given + criteria. You can specify the name of the Tag and any + attributes you want the Tag to have. + + The value of a key-value pair in the 'attrs' map can be a + string, a list of strings, a regular expression object, or a + callable that takes a string and returns whether or not the + string matches for some custom definition of 'matches'. The + same is true of the tag name.""" + generator = self.recursiveChildGenerator + if not recursive: + generator = self.childGenerator + return self._findAll(name, attrs, text, limit, generator, **kwargs) + findChildren = findAll + + # Pre-3.x compatibility methods + first = find + fetch = findAll + + def fetchText(self, text=None, recursive=True, limit=None): + return self.findAll(text=text, recursive=recursive, limit=limit) + + def firstText(self, text=None, recursive=True): + return self.find(text=text, recursive=recursive) + + #Private methods + + def _getAttrMap(self): + """Initializes a map representation of this tag's attributes, + if not already initialized.""" + if not getattr(self, 'attrMap'): + self.attrMap = {} + for (key, value) in self.attrs: + self.attrMap[key] = value + return self.attrMap + + #Generator methods + def childGenerator(self): + # Just use the iterator from the contents + return iter(self.contents) + + def recursiveChildGenerator(self): + if not len(self.contents): + raise StopIteration + stopNode = self._lastRecursiveChild().next + current = self.contents[0] + while current is not stopNode: + yield current + current = current.next + + +# Next, a couple classes to represent queries and their results. +class SoupStrainer: + """Encapsulates a number of ways of matching a markup element (tag or + text).""" + + def __init__(self, name=None, attrs={}, text=None, **kwargs): + self.name = name + if isinstance(attrs, basestring): + kwargs['class'] = _match_css_class(attrs) + attrs = None + if kwargs: + if attrs: + attrs = attrs.copy() + attrs.update(kwargs) + else: + attrs = kwargs + self.attrs = attrs + self.text = text + + def __str__(self): + if self.text: + return self.text + else: + return "%s|%s" % (self.name, self.attrs) + + def searchTag(self, markupName=None, markupAttrs={}): + found = None + markup = None + if isinstance(markupName, Tag): + markup = markupName + markupAttrs = markup + callFunctionWithTagData = callable(self.name) \ + and not isinstance(markupName, Tag) + + if (not self.name) \ + or callFunctionWithTagData \ + or (markup and self._matches(markup, self.name)) \ + or (not markup and self._matches(markupName, self.name)): + if callFunctionWithTagData: + match = self.name(markupName, markupAttrs) + else: + match = True + markupAttrMap = None + for attr, matchAgainst in self.attrs.items(): + if not markupAttrMap: + if hasattr(markupAttrs, 'get'): + markupAttrMap = markupAttrs + else: + markupAttrMap = {} + for k,v in markupAttrs: + markupAttrMap[k] = v + attrValue = markupAttrMap.get(attr) + if not self._matches(attrValue, matchAgainst): + match = False + break + if match: + if markup: + found = markup + else: + found = markupName + return found + + def search(self, markup): + #print 'looking for %s in %s' % (self, markup) + found = None + # If given a list of items, scan it for a text element that + # matches. + if hasattr(markup, "__iter__") \ + and not isinstance(markup, Tag): + for element in markup: + if isinstance(element, NavigableString) \ + and self.search(element): + found = element + break + # If it's a Tag, make sure its name or attributes match. + # Don't bother with Tags if we're searching for text. + elif isinstance(markup, Tag): + if not self.text: + found = self.searchTag(markup) + # If it's text, make sure the text matches. + elif isinstance(markup, NavigableString) or \ + isinstance(markup, basestring): + if self._matches(markup, self.text): + found = markup + else: + raise Exception, "I don't know how to match against a %s" \ + % markup.__class__ + return found + + def _matches(self, markup, matchAgainst): + #print "Matching %s against %s" % (markup, matchAgainst) + result = False + if matchAgainst is True: + result = markup is not None + elif callable(matchAgainst): + result = matchAgainst(markup) + else: + #Custom match methods take the tag as an argument, but all + #other ways of matching match the tag name as a string. + if isinstance(markup, Tag): + markup = markup.name + if markup and not isinstance(markup, basestring): + markup = unicode(markup) + #Now we know that chunk is either a string, or None. + if hasattr(matchAgainst, 'match'): + # It's a regexp object. + result = markup and matchAgainst.search(markup) + elif hasattr(matchAgainst, '__iter__'): # list-like + result = markup in matchAgainst + elif hasattr(matchAgainst, 'items'): + result = markup.has_key(matchAgainst) + elif matchAgainst and isinstance(markup, basestring): + if isinstance(markup, unicode): + matchAgainst = unicode(matchAgainst) + else: + matchAgainst = str(matchAgainst) + + if not result: + result = matchAgainst == markup + return result + +class ResultSet(list): + """A ResultSet is just a list that keeps track of the SoupStrainer + that created it.""" + def __init__(self, source): + list.__init__([]) + self.source = source + +# Now, some helper functions. + +def buildTagMap(default, *args): + """Turns a list of maps, lists, or scalars into a single map. + Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and + NESTING_RESET_TAGS maps out of lists and partial maps.""" + built = {} + for portion in args: + if hasattr(portion, 'items'): + #It's a map. Merge it. + for k,v in portion.items(): + built[k] = v + elif hasattr(portion, '__iter__'): # is a list + #It's a list. Map each item to the default. + for k in portion: + built[k] = default + else: + #It's a scalar. Map it to the default. + built[portion] = default + return built + +# Now, the parser classes. + +class BeautifulStoneSoup(Tag, SGMLParser): + + """This class contains the basic parser and search code. It defines + a parser that knows nothing about tag behavior except for the + following: + + You can't close a tag without closing all the tags it encloses. + That is, "" actually means + "". + + [Another possible explanation is "", but since + this class defines no SELF_CLOSING_TAGS, it will never use that + explanation.] + + This class is useful for parsing XML or made-up markup languages, + or when BeautifulSoup makes an assumption counter to what you were + expecting.""" + + SELF_CLOSING_TAGS = {} + NESTABLE_TAGS = {} + RESET_NESTING_TAGS = {} + QUOTE_TAGS = {} + PRESERVE_WHITESPACE_TAGS = [] + + MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'), + lambda x: x.group(1) + ' />'), + (re.compile(']*)>'), + lambda x: '') + ] + + ROOT_TAG_NAME = u'[document]' + + HTML_ENTITIES = "html" + XML_ENTITIES = "xml" + XHTML_ENTITIES = "xhtml" + # TODO: This only exists for backwards-compatibility + ALL_ENTITIES = XHTML_ENTITIES + + # Used when determining whether a text node is all whitespace and + # can be replaced with a single space. A text node that contains + # fancy Unicode spaces (usually non-breaking) should be left + # alone. + STRIP_ASCII_SPACES = { 9: None, 10: None, 12: None, 13: None, 32: None, } + + def __init__(self, markup="", parseOnlyThese=None, fromEncoding=None, + markupMassage=True, smartQuotesTo=XML_ENTITIES, + convertEntities=None, selfClosingTags=None, isHTML=False): + """The Soup object is initialized as the 'root tag', and the + provided markup (which can be a string or a file-like object) + is fed into the underlying parser. + + sgmllib will process most bad HTML, and the BeautifulSoup + class has some tricks for dealing with some HTML that kills + sgmllib, but Beautiful Soup can nonetheless choke or lose data + if your data uses self-closing tags or declarations + incorrectly. + + By default, Beautiful Soup uses regexes to sanitize input, + avoiding the vast majority of these problems. If the problems + don't apply to you, pass in False for markupMassage, and + you'll get better performance. + + The default parser massage techniques fix the two most common + instances of invalid HTML that choke sgmllib: + +
    (No space between name of closing tag and tag close) + (Extraneous whitespace in declaration) + + You can pass in a custom list of (RE object, replace method) + tuples to get Beautiful Soup to scrub your input the way you + want.""" + + self.parseOnlyThese = parseOnlyThese + self.fromEncoding = fromEncoding + self.smartQuotesTo = smartQuotesTo + self.convertEntities = convertEntities + # Set the rules for how we'll deal with the entities we + # encounter + if self.convertEntities: + # It doesn't make sense to convert encoded characters to + # entities even while you're converting entities to Unicode. + # Just convert it all to Unicode. + self.smartQuotesTo = None + if convertEntities == self.HTML_ENTITIES: + self.convertXMLEntities = False + self.convertHTMLEntities = True + self.escapeUnrecognizedEntities = True + elif convertEntities == self.XHTML_ENTITIES: + self.convertXMLEntities = True + self.convertHTMLEntities = True + self.escapeUnrecognizedEntities = False + elif convertEntities == self.XML_ENTITIES: + self.convertXMLEntities = True + self.convertHTMLEntities = False + self.escapeUnrecognizedEntities = False + else: + self.convertXMLEntities = False + self.convertHTMLEntities = False + self.escapeUnrecognizedEntities = False + + self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags) + SGMLParser.__init__(self) + + if hasattr(markup, 'read'): # It's a file-type object. + markup = markup.read() + self.markup = markup + self.markupMassage = markupMassage + try: + self._feed(isHTML=isHTML) + except StopParsing: + pass + self.markup = None # The markup can now be GCed + + def convert_charref(self, name): + """This method fixes a bug in Python's SGMLParser.""" + try: + n = int(name) + except ValueError: + return + if not 0 <= n <= 127 : # ASCII ends at 127, not 255 + return + return self.convert_codepoint(n) + + def _feed(self, inDocumentEncoding=None, isHTML=False): + # Convert the document to Unicode. + markup = self.markup + if isinstance(markup, unicode): + if not hasattr(self, 'originalEncoding'): + self.originalEncoding = None + else: + dammit = UnicodeDammit\ + (markup, [self.fromEncoding, inDocumentEncoding], + smartQuotesTo=self.smartQuotesTo, isHTML=isHTML) + markup = dammit.unicode + self.originalEncoding = dammit.originalEncoding + self.declaredHTMLEncoding = dammit.declaredHTMLEncoding + if markup: + if self.markupMassage: + if not hasattr(self.markupMassage, "__iter__"): + self.markupMassage = self.MARKUP_MASSAGE + for fix, m in self.markupMassage: + markup = fix.sub(m, markup) + # TODO: We get rid of markupMassage so that the + # soup object can be deepcopied later on. Some + # Python installations can't copy regexes. If anyone + # was relying on the existence of markupMassage, this + # might cause problems. + del(self.markupMassage) + self.reset() + + SGMLParser.feed(self, markup) + # Close out any unfinished strings and close all the open tags. + self.endData() + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() + + def __getattr__(self, methodName): + """This method routes method call requests to either the SGMLParser + superclass or the Tag superclass, depending on the method name.""" + #print "__getattr__ called on %s.%s" % (self.__class__, methodName) + + if methodName.startswith('start_') or methodName.startswith('end_') \ + or methodName.startswith('do_'): + return SGMLParser.__getattr__(self, methodName) + elif not methodName.startswith('__'): + return Tag.__getattr__(self, methodName) + else: + raise AttributeError + + def isSelfClosingTag(self, name): + """Returns true iff the given string is the name of a + self-closing tag according to this parser.""" + return self.SELF_CLOSING_TAGS.has_key(name) \ + or self.instanceSelfClosingTags.has_key(name) + + def reset(self): + Tag.__init__(self, self, self.ROOT_TAG_NAME) + self.hidden = 1 + SGMLParser.reset(self) + self.currentData = [] + self.currentTag = None + self.tagStack = [] + self.quoteStack = [] + self.pushTag(self) + + def popTag(self): + tag = self.tagStack.pop() + + #print "Pop", tag.name + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag): + #print "Push", tag.name + if self.currentTag: + self.currentTag.contents.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + + def endData(self, containerClass=NavigableString): + if self.currentData: + currentData = u''.join(self.currentData) + if (currentData.translate(self.STRIP_ASCII_SPACES) == '' and + not set([tag.name for tag in self.tagStack]).intersection( + self.PRESERVE_WHITESPACE_TAGS)): + if '\n' in currentData: + currentData = '\n' + else: + currentData = ' ' + self.currentData = [] + if self.parseOnlyThese and len(self.tagStack) <= 1 and \ + (not self.parseOnlyThese.text or \ + not self.parseOnlyThese.search(currentData)): + return + o = containerClass(currentData) + o.setup(self.currentTag, self.previous) + if self.previous: + self.previous.next = o + self.previous = o + self.currentTag.contents.append(o) + + + def _popToTag(self, name, inclusivePop=True): + """Pops the tag stack up to and including the most recent + instance of the given tag. If inclusivePop is false, pops the tag + stack up to but *not* including the most recent instqance of + the given tag.""" + #print "Popping to %s" % name + if name == self.ROOT_TAG_NAME: + return + + numPops = 0 + mostRecentTag = None + for i in range(len(self.tagStack)-1, 0, -1): + if name == self.tagStack[i].name: + numPops = len(self.tagStack)-i + break + if not inclusivePop: + numPops = numPops - 1 + + for i in range(0, numPops): + mostRecentTag = self.popTag() + return mostRecentTag + + def _smartPop(self, name): + + """We need to pop up to the previous tag of this type, unless + one of this tag's nesting reset triggers comes between this + tag and the previous tag of this type, OR unless this tag is a + generic nesting trigger and another generic nesting trigger + comes between this tag and the previous tag of this type. + + Examples: +

    FooBar *

    * should pop to 'p', not 'b'. +

    FooBar *

    * should pop to 'table', not 'p'. +

    Foo

    Bar *

    * should pop to 'tr', not 'p'. + +

    • *
    • * should pop to 'ul', not the first 'li'. +
  • ** should pop to 'table', not the first 'tr' + tag should + implicitly close the previous tag within the same
    ** should pop to 'tr', not the first 'td' + """ + + nestingResetTriggers = self.NESTABLE_TAGS.get(name) + isNestable = nestingResetTriggers != None + isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + popTo = None + inclusive = True + for i in range(len(self.tagStack)-1, 0, -1): + p = self.tagStack[i] + if (not p or p.name == name) and not isNestable: + #Non-nestable tags get popped to the top or to their + #last occurance. + popTo = name + break + if (nestingResetTriggers is not None + and p.name in nestingResetTriggers) \ + or (nestingResetTriggers is None and isResetNesting + and self.RESET_NESTING_TAGS.has_key(p.name)): + + #If we encounter one of the nesting reset triggers + #peculiar to this tag, or we encounter another tag + #that causes nesting to reset, pop up to but not + #including that tag. + popTo = p.name + inclusive = False + break + p = p.parent + if popTo: + self._popToTag(popTo, inclusive) + + def unknown_starttag(self, name, attrs, selfClosing=0): + #print "Start tag %s: %s" % (name, attrs) + if self.quoteStack: + #This is not a real tag. + #print "<%s> is not real!" % name + attrs = ''.join([' %s="%s"' % (x, y) for x, y in attrs]) + self.handle_data('<%s%s>' % (name, attrs)) + return + self.endData() + + if not self.isSelfClosingTag(name) and not selfClosing: + self._smartPop(name) + + if self.parseOnlyThese and len(self.tagStack) <= 1 \ + and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)): + return + + tag = Tag(self, name, attrs, self.currentTag, self.previous) + if self.previous: + self.previous.next = tag + self.previous = tag + self.pushTag(tag) + if selfClosing or self.isSelfClosingTag(name): + self.popTag() + if name in self.QUOTE_TAGS: + #print "Beginning quote (%s)" % name + self.quoteStack.append(name) + self.literal = 1 + return tag + + def unknown_endtag(self, name): + #print "End tag %s" % name + if self.quoteStack and self.quoteStack[-1] != name: + #This is not a real end tag. + #print " is not real!" % name + self.handle_data('' % name) + return + self.endData() + self._popToTag(name) + if self.quoteStack and self.quoteStack[-1] == name: + self.quoteStack.pop() + self.literal = (len(self.quoteStack) > 0) + + def handle_data(self, data): + self.currentData.append(data) + + def _toStringSubclass(self, text, subclass): + """Adds a certain piece of text to the tree as a NavigableString + subclass.""" + self.endData() + self.handle_data(text) + self.endData(subclass) + + def handle_pi(self, text): + """Handle a processing instruction as a ProcessingInstruction + object, possibly one with a %SOUP-ENCODING% slot into which an + encoding will be plugged later.""" + if text[:3] == "xml": + text = u"xml version='1.0' encoding='%SOUP-ENCODING%'" + self._toStringSubclass(text, ProcessingInstruction) + + def handle_comment(self, text): + "Handle comments as Comment objects." + self._toStringSubclass(text, Comment) + + def handle_charref(self, ref): + "Handle character references as data." + if self.convertEntities: + data = unichr(int(ref)) + else: + data = '&#%s;' % ref + self.handle_data(data) + + def handle_entityref(self, ref): + """Handle entity references as data, possibly converting known + HTML and/or XML entity references to the corresponding Unicode + characters.""" + data = None + if self.convertHTMLEntities: + try: + data = unichr(name2codepoint[ref]) + except KeyError: + pass + + if not data and self.convertXMLEntities: + data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref) + + if not data and self.convertHTMLEntities and \ + not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref): + # TODO: We've got a problem here. We're told this is + # an entity reference, but it's not an XML entity + # reference or an HTML entity reference. Nonetheless, + # the logical thing to do is to pass it through as an + # unrecognized entity reference. + # + # Except: when the input is "&carol;" this function + # will be called with input "carol". When the input is + # "AT&T", this function will be called with input + # "T". We have no way of knowing whether a semicolon + # was present originally, so we don't know whether + # this is an unknown entity or just a misplaced + # ampersand. + # + # The more common case is a misplaced ampersand, so I + # escape the ampersand and omit the trailing semicolon. + data = "&%s" % ref + if not data: + # This case is different from the one above, because we + # haven't already gone through a supposedly comprehensive + # mapping of entities to Unicode characters. We might not + # have gone through any mapping at all. So the chances are + # very high that this is a real entity, and not a + # misplaced ampersand. + data = "&%s;" % ref + self.handle_data(data) + + def handle_decl(self, data): + "Handle DOCTYPEs and the like as Declaration objects." + self._toStringSubclass(data, Declaration) + + def parse_declaration(self, i): + """Treat a bogus SGML declaration as raw data. Treat a CDATA + declaration as a CData object.""" + j = None + if self.rawdata[i:i+9] == '', i) + if k == -1: + k = len(self.rawdata) + data = self.rawdata[i+9:k] + j = k+3 + self._toStringSubclass(data, CData) + else: + try: + j = SGMLParser.parse_declaration(self, i) + except SGMLParseError: + toHandle = self.rawdata[i:] + self.handle_data(toHandle) + j = i + len(toHandle) + return j + +class BeautifulSoup(BeautifulStoneSoup): + + """This parser knows the following facts about HTML: + + * Some tags have no closing tag and should be interpreted as being + closed as soon as they are encountered. + + * The text inside some tags (ie. 'script') may contain tags which + are not really part of the document and which should be parsed + as text, not tags. If you want to parse the text as tags, you can + always fetch it and parse it explicitly. + + * Tag nesting rules: + + Most tags can't be nested at all. For instance, the occurance of + a

    tag should implicitly close the previous

    tag. + +

    Para1

    Para2 + should be transformed into: +

    Para1

    Para2 + + Some tags can be nested arbitrarily. For instance, the occurance + of a

    tag should _not_ implicitly close the previous +
    tag. + + Alice said:
    Bob said:
    Blah + should NOT be transformed into: + Alice said:
    Bob said:
    Blah + + Some tags can be nested, but the nesting is reset by the + interposition of other tags. For instance, a
    , + but not close a tag in another table. + +
    BlahBlah + should be transformed into: +
    BlahBlah + but, + Blah
    Blah + should NOT be transformed into + Blah
    Blah + + Differing assumptions about tag nesting rules are a major source + of problems with the BeautifulSoup class. If BeautifulSoup is not + treating as nestable a tag your page author treats as nestable, + try ICantBelieveItsBeautifulSoup, MinimalSoup, or + BeautifulStoneSoup before writing your own subclass.""" + + def __init__(self, *args, **kwargs): + if not kwargs.has_key('smartQuotesTo'): + kwargs['smartQuotesTo'] = self.HTML_ENTITIES + kwargs['isHTML'] = True + BeautifulStoneSoup.__init__(self, *args, **kwargs) + + SELF_CLOSING_TAGS = buildTagMap(None, + ('br' , 'hr', 'input', 'img', 'meta', + 'spacer', 'link', 'frame', 'base', 'col')) + + PRESERVE_WHITESPACE_TAGS = set(['pre', 'textarea']) + + QUOTE_TAGS = {'script' : None, 'textarea' : None} + + #According to the HTML standard, each of these inline tags can + #contain another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_INLINE_TAGS = ('span', 'font', 'q', 'object', 'bdo', 'sub', 'sup', + 'center') + + #According to the HTML standard, these block tags can contain + #another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_BLOCK_TAGS = ('blockquote', 'div', 'fieldset', 'ins', 'del') + + #Lists can contain other lists, but there are restrictions. + NESTABLE_LIST_TAGS = { 'ol' : [], + 'ul' : [], + 'li' : ['ul', 'ol'], + 'dl' : [], + 'dd' : ['dl'], + 'dt' : ['dl'] } + + #Tables can contain other tables, but there are restrictions. + NESTABLE_TABLE_TAGS = {'table' : [], + 'tr' : ['table', 'tbody', 'tfoot', 'thead'], + 'td' : ['tr'], + 'th' : ['tr'], + 'thead' : ['table'], + 'tbody' : ['table'], + 'tfoot' : ['table'], + } + + NON_NESTABLE_BLOCK_TAGS = ('address', 'form', 'p', 'pre') + + #If one of these tags is encountered, all tags up to the next tag of + #this type are popped. + RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', + NON_NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, + NESTABLE_TABLE_TAGS) + + NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) + + # Used to detect the charset in a META tag; see start_meta + CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)", re.M) + + def start_meta(self, attrs): + """Beautiful Soup can detect a charset included in a META tag, + try to convert the document to that charset, and re-parse the + document from the beginning.""" + httpEquiv = None + contentType = None + contentTypeIndex = None + tagNeedsEncodingSubstitution = False + + for i in range(0, len(attrs)): + key, value = attrs[i] + key = key.lower() + if key == 'http-equiv': + httpEquiv = value + elif key == 'content': + contentType = value + contentTypeIndex = i + + if httpEquiv and contentType: # It's an interesting meta tag. + match = self.CHARSET_RE.search(contentType) + if match: + if (self.declaredHTMLEncoding is not None or + self.originalEncoding == self.fromEncoding): + # An HTML encoding was sniffed while converting + # the document to Unicode, or an HTML encoding was + # sniffed during a previous pass through the + # document, or an encoding was specified + # explicitly and it worked. Rewrite the meta tag. + def rewrite(match): + return match.group(1) + "%SOUP-ENCODING%" + newAttr = self.CHARSET_RE.sub(rewrite, contentType) + attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], + newAttr) + tagNeedsEncodingSubstitution = True + else: + # This is our first pass through the document. + # Go through it again with the encoding information. + newCharset = match.group(3) + if newCharset and newCharset != self.originalEncoding: + self.declaredHTMLEncoding = newCharset + self._feed(self.declaredHTMLEncoding) + raise StopParsing + pass + tag = self.unknown_starttag("meta", attrs) + if tag and tagNeedsEncodingSubstitution: + tag.containsSubstitutions = True + +class StopParsing(Exception): + pass + +class ICantBelieveItsBeautifulSoup(BeautifulSoup): + + """The BeautifulSoup class is oriented towards skipping over + common HTML errors like unclosed tags. However, sometimes it makes + errors of its own. For instance, consider this fragment: + + FooBar + + This is perfectly valid (if bizarre) HTML. However, the + BeautifulSoup class will implicitly close the first b tag when it + encounters the second 'b'. It will think the author wrote + "FooBar", and didn't close the first 'b' tag, because + there's no real-world reason to bold something that's already + bold. When it encounters '' it will close two more 'b' + tags, for a grand total of three tags closed instead of two. This + can throw off the rest of your document structure. The same is + true of a number of other tags, listed below. + + It's much more common for someone to forget to close a 'b' tag + than to actually use nested 'b' tags, and the BeautifulSoup class + handles the common case. This class handles the not-co-common + case: where you can't believe someone wrote what they did, but + it's valid HTML and BeautifulSoup screwed up by assuming it + wouldn't be.""" + + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ + ('em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', + 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b', + 'big') + + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ('noscript',) + + NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) + +class MinimalSoup(BeautifulSoup): + """The MinimalSoup class is for parsing HTML that contains + pathologically bad markup. It makes no assumptions about tag + nesting, but it does know which tags are self-closing, that + + +

    LiveSpace 导入插件

    +

    目前支持导入文章,评论和图片

    +使用方法: +
    +
      +
    1. http://spaces.live.com 将您的日志下载到 PC
    2. +
    3. 上传space.zip并导入
    4. +
    +

    +

    选项: + +
    +

    +
    + +
    +
    + +{%if error%} {{error}}{%endif%} +
    + + + + diff --git a/plugins/live_import/live_import.py b/plugins/live_import/live_import.py new file mode 100644 index 0000000..e6fcdf0 --- /dev/null +++ b/plugins/live_import/live_import.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- +from micolog_plugin import * +from BeautifulSoup import * +from datetime import datetime +from model import Entry,Comment,Media +import logging,math +import re +from base import BaseRequestHandler,urldecode + + +class Importhandler(BaseRequestHandler): + def post(self): + + if not self.is_login: + self.redirect(users.create_login_url(self.request.uri)) + filename=self.param('filename') + do_comment=self.paramint('c',0) + if filename[:4]=='img/':#处理图片 + new_filename=filename.split('/')[1] + mtype =new_filename.split('.')[1] + bits = self.request.body + media=Media.all().filter('name =',new_filename) + if media.count()>0: + media=media[0] + else: + media=Media() + media.name=new_filename + media.mtype=mtype + media.bits=bits + media.put() + bid='_'.join(new_filename.split('_')[:-1]) + entries=Entry.all().filter('slug =',bid) + if entries.count()>0: + entry=entries[0] + entry.content=entry.content.replace(filename,'/media/'+str(media.key())) + entry.put() + return + + if filename=="index.html" or filename[-5:]!='.html': + return + #处理html页面 + bid=filename[:-5] + try: + + soup=BeautifulSoup(self.request.body) + bp=soup.find(id='bp') + title=self.getChineseStr( soup.title.text) + logging.info(bid) + pubdate=self.getdate( bp.find(id='bp-'+bid+'-publish').text) + body=bp.find('div','blogpost') + + entries=Entry.all().filter('title = ',title) + if entries.count()<1: + entry=Entry() + else: + entry=entries[0] +## entry=Entry.get_by_key_name(bid) +## if not entry: +## entry=Entry(key_name=bid) + entry.slug=bid + entry.title=title + entry.author_name=self.login_user.nickname() + entry.date=pubdate + entry.settags("") + entry.content=unicode(body) + entry.author=self.login_user + + entry.save(True) + if do_comment>0: + comments=soup.find('div','comments','div') + if comments: + for comment in comments.contents: + name,date=comment.h5.text.split(' - ') + # modify by lastmind4 + name_date_pair = comment.h5.text + if name_date_pair.index('- ') == 0: + name_date_pair = 'Anonymous ' + name_date_pair + name,date=name_date_pair.split(' - ') + + key_id=comment.h5['id'] + date=self.getdate(date) + content=comment.contents[1].text + comment=Comment.get_or_insert(key_id,content=content) + comment.entry=entry + comment.date=date + comment.author=name + comment.save() + + except Exception,e : + logging.info("import error: %s"%e.message) + + def getdate(self,d): + try: + ret=datetime.strptime(d,"%Y/%m/%d %H:%M:%S") + except: + try: + ret=datetime.strptime(d,"%m/%d/%Y %H:%M:%S %p") + except: + ret=datetime.now() + return ret + + def getChineseStr(self,s): + return re.sub(r'&#(\d+);',lambda x:unichr(int(x.group(1))) ,s) + +class live_import(Plugin_importbase): + def __init__(self): + Plugin_importbase.__init__(self,__file__,"spaces.live.com","Plugin for import entries from space.zip.") + self.author="xuming" + self.authoruri="http://xuming.net" + self.uri="http://xuming.net" + self.description='''Plugin for import entries from space.zip.
    + 将Spaces.Live.com博客导入到Micolog.''' + self.name="LiveSapce Import" + self.version="0.12" + self.register_urlzip('/admin/live_import/swfupload/(.*)','swfupload.zip') + self.register_urlhandler('/admin/live_import/import',Importhandler) + + + + def get(self,page): + return self.render_content("import.html",{'name':self.name}) + diff --git a/plugins/live_import/swfupload.js b/plugins/live_import/swfupload.js new file mode 100644 index 0000000..df2bc07 --- /dev/null +++ b/plugins/live_import/swfupload.js @@ -0,0 +1,988 @@ +/** + * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com + * + * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/ + * + * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzn and Mammon Media and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + */ + + +/* ******************* */ +/* Constructor & Init */ +/* ******************* */ +var SWFUpload; + +if (SWFUpload == undefined) { + SWFUpload = function (settings) { + this.initSWFUpload(settings); + }; +} + +SWFUpload.prototype.initSWFUpload = function (settings) { + try { + this.customSettings = {}; // A container where developers can place their own settings associated with this instance. + this.settings = settings; + this.eventQueue = []; + this.movieName = "SWFUpload_" + SWFUpload.movieCount++; + this.movieElement = null; + + + // Setup global control tracking + SWFUpload.instances[this.movieName] = this; + + // Load the settings. Load the Flash movie. + this.initSettings(); + this.loadFlash(); + this.displayDebugInfo(); + } catch (ex) { + delete SWFUpload.instances[this.movieName]; + throw ex; + } +}; + +/* *************** */ +/* Static Members */ +/* *************** */ +SWFUpload.instances = {}; +SWFUpload.movieCount = 0; +SWFUpload.version = "2.2.0 2009-03-25"; +SWFUpload.QUEUE_ERROR = { + QUEUE_LIMIT_EXCEEDED : -100, + FILE_EXCEEDS_SIZE_LIMIT : -110, + ZERO_BYTE_FILE : -120, + INVALID_FILETYPE : -130 +}; +SWFUpload.UPLOAD_ERROR = { + HTTP_ERROR : -200, + MISSING_UPLOAD_URL : -210, + IO_ERROR : -220, + SECURITY_ERROR : -230, + UPLOAD_LIMIT_EXCEEDED : -240, + UPLOAD_FAILED : -250, + SPECIFIED_FILE_ID_NOT_FOUND : -260, + FILE_VALIDATION_FAILED : -270, + FILE_CANCELLED : -280, + UPLOAD_STOPPED : -290 +}; +SWFUpload.FILE_STATUS = { + QUEUED : -1, + IN_PROGRESS : -2, + ERROR : -3, + COMPLETE : -4, + CANCELLED : -5 +}; +SWFUpload.BUTTON_ACTION = { + SELECT_FILE : -100, + SELECT_FILES : -110, + START_UPLOAD : -120 +}; +SWFUpload.CURSOR = { + ARROW : -1, + HAND : -2 +}; +SWFUpload.WINDOW_MODE = { + WINDOW : "window", + TRANSPARENT : "transparent", + OPAQUE : "opaque" +}; + +// Private: takes a URL, determines if it is relative and converts to an absolute URL +// using the current site. Only processes the URL if it can, otherwise returns the URL untouched +SWFUpload.completeURL = function(url) { + if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) { + return url; + } + + var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : ""); + + var indexSlash = window.location.pathname.lastIndexOf("/"); + if (indexSlash <= 0) { + path = "/"; + } else { + path = window.location.pathname.substr(0, indexSlash) + "/"; + } + + return /*currentURL +*/ path + url; + +}; + + +/* ******************** */ +/* Instance Members */ +/* ******************** */ + +// Private: initSettings ensures that all the +// settings are set, getting a default value if one was not assigned. +SWFUpload.prototype.initSettings = function () { + this.ensureDefault = function (settingName, defaultValue) { + this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName]; + }; + + // Upload backend settings + this.ensureDefault("upload_url", ""); + this.ensureDefault("preserve_relative_urls", false); + this.ensureDefault("file_post_name", "Filedata"); + this.ensureDefault("post_params", {}); + this.ensureDefault("use_query_string", false); + this.ensureDefault("requeue_on_error", false); + this.ensureDefault("http_success", []); + this.ensureDefault("assume_success_timeout", 0); + + // File Settings + this.ensureDefault("autoZip", false); + this.ensureDefault("allowZerofile", false); + this.ensureDefault("zipFormat", "utf-8"); + + this.ensureDefault("file_types", "*.*"); + this.ensureDefault("file_types_description", "All Files"); + this.ensureDefault("file_size_limit", 0); // Default zero means "unlimited" + this.ensureDefault("file_upload_limit", 0); + this.ensureDefault("file_queue_limit", 0); + + // Flash Settings + this.ensureDefault("flash_url", "swfupload.swf"); + this.ensureDefault("prevent_swf_caching", true); + + // Button Settings + this.ensureDefault("button_image_url", ""); + this.ensureDefault("button_width", 1); + this.ensureDefault("button_height", 1); + this.ensureDefault("button_text", ""); + this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;"); + this.ensureDefault("button_text_top_padding", 0); + this.ensureDefault("button_text_left_padding", 0); + this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES); + this.ensureDefault("button_disabled", false); + this.ensureDefault("button_placeholder_id", ""); + this.ensureDefault("button_placeholder", null); + this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW); + this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW); + + // Debug Settings + this.ensureDefault("debug", false); + this.settings.debug_enabled = this.settings.debug; // Here to maintain v2 API + + // Event Handlers + this.settings.return_upload_start_handler = this.returnUploadStart; + this.ensureDefault("swfupload_loaded_handler", null); + this.ensureDefault("file_dialog_start_handler", null); + this.ensureDefault("file_queued_handler", null); + this.ensureDefault("file_queue_error_handler", null); + this.ensureDefault("file_dialog_complete_handler", null); + + this.ensureDefault("upload_start_handler", null); + this.ensureDefault("upload_progress_handler", null); + this.ensureDefault("upload_error_handler", null); + this.ensureDefault("upload_success_handler", null); + this.ensureDefault("upload_complete_handler", null); + + this.ensureDefault("debug_handler", this.debugMessage); + + this.ensureDefault("custom_settings", {}); + + // Other settings + this.customSettings = this.settings.custom_settings; + + // Update the flash url if needed + if (!!this.settings.prevent_swf_caching) { + this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime(); + } + + if (!this.settings.preserve_relative_urls) { + //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url); // Don't need to do this one since flash doesn't look at it + this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url); + this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url); + } + + delete this.ensureDefault; +}; + +// Private: loadFlash replaces the button_placeholder element with the flash movie. +SWFUpload.prototype.loadFlash = function () { + var targetElement, tempParent; + + // Make sure an element with the ID we are going to use doesn't already exist + if (document.getElementById(this.movieName) !== null) { + throw "ID " + this.movieName + " is already in use. The Flash Object could not be added"; + } + + // Get the element where we will be placing the flash movie + targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder; + + if (targetElement == undefined) { + throw "Could not find the placeholder element: " + this.settings.button_placeholder_id; + } + + // Append the container and load the flash + tempParent = document.createElement("div"); + tempParent.innerHTML = this.getFlashHTML(); // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers) + targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement); + + // Fix IE Flash/Form bug + if (window[this.movieName] == undefined) { + window[this.movieName] = this.getMovieElement(); + } + +}; + +// Private: getFlashHTML generates the object tag needed to embed the flash in to the document +SWFUpload.prototype.getFlashHTML = function () { + // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay + return ['', + '', + '', + '', + '', + '', + '', + ''].join(""); +}; + +// Private: getFlashVars builds the parameter string that will be passed +// to flash in the flashvars param. +SWFUpload.prototype.getFlashVars = function () { + // Build a string from the post param object + var paramString = this.buildParamString(); + var httpSuccessString = this.settings.http_success.join(","); + + // Build the parameter string + return ["movieName=", encodeURIComponent(this.movieName), + "&autoZip=", encodeURIComponent(this.settings.autoZip), + "&allowZerofile=", encodeURIComponent(this.settings.allowZerofile), + "&zipFormat=", encodeURIComponent(this.settings.zipFormat), + "&uploadURL=", encodeURIComponent(this.settings.upload_url), + "&useQueryString=", encodeURIComponent(this.settings.use_query_string), + "&requeueOnError=", encodeURIComponent(this.settings.requeue_on_error), + "&httpSuccess=", encodeURIComponent(httpSuccessString), + "&assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout), + "&params=", encodeURIComponent(paramString), + "&filePostName=", encodeURIComponent(this.settings.file_post_name), + "&fileTypes=", encodeURIComponent(this.settings.file_types), + "&fileTypesDescription=", encodeURIComponent(this.settings.file_types_description), + "&fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit), + "&fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit), + "&fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit), + "&debugEnabled=", encodeURIComponent(this.settings.debug_enabled), + "&buttonImageURL=", encodeURIComponent(this.settings.button_image_url), + "&buttonWidth=", encodeURIComponent(this.settings.button_width), + "&buttonHeight=", encodeURIComponent(this.settings.button_height), + "&buttonText=", encodeURIComponent(this.settings.button_text), + "&buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding), + "&buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding), + "&buttonTextStyle=", encodeURIComponent(this.settings.button_text_style), + "&buttonAction=", encodeURIComponent(this.settings.button_action), + "&buttonDisabled=", encodeURIComponent(this.settings.button_disabled), + "&buttonCursor=", encodeURIComponent(this.settings.button_cursor) + ].join(""); +}; + +// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload +// The element is cached after the first lookup +SWFUpload.prototype.getMovieElement = function () { + if (this.movieElement == undefined) { + this.movieElement = document.getElementById(this.movieName); + } + + if (this.movieElement === null) { + throw "Could not find Flash element"; + } + + return this.movieElement; +}; + +// Private: buildParamString takes the name/value pairs in the post_params setting object +// and joins them up in to a string formatted "name=value&name=value" +SWFUpload.prototype.buildParamString = function () { + var postParams = this.settings.post_params; + var paramStringPairs = []; + + if (typeof(postParams) === "object") { + for (var name in postParams) { + if (postParams.hasOwnProperty(name)) { + paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString())); + } + } + } + + return paramStringPairs.join("&"); +}; + +// Public: Used to remove a SWFUpload instance from the page. This method strives to remove +// all references to the SWF, and other objects so memory is properly freed. +// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state. +// Credits: Major improvements provided by steffen +SWFUpload.prototype.destroy = function () { + try { + // Make sure Flash is done before we try to remove it + this.cancelUpload(null, false); + + + // Remove the SWFUpload DOM nodes + var movieElement = null; + movieElement = this.getMovieElement(); + + if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE + // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround) + for (var i in movieElement) { + try { + if (typeof(movieElement[i]) === "function") { + movieElement[i] = null; + } + } catch (ex1) {} + } + + // Remove the Movie Element from the page + try { + movieElement.parentNode.removeChild(movieElement); + } catch (ex) {} + } + + // Remove IE form fix reference + window[this.movieName] = null; + + // Destroy other references + SWFUpload.instances[this.movieName] = null; + delete SWFUpload.instances[this.movieName]; + + this.movieElement = null; + this.settings = null; + this.customSettings = null; + this.eventQueue = null; + this.movieName = null; + + + return true; + } catch (ex2) { + return false; + } +}; + + +// Public: displayDebugInfo prints out settings and configuration +// information about this SWFUpload instance. +// This function (and any references to it) can be deleted when placing +// SWFUpload in production. +SWFUpload.prototype.displayDebugInfo = function () { + this.debug( + [ + "---SWFUpload Instance Info---\n", + "Version: ", SWFUpload.version, "\n", + "Movie Name: ", this.movieName, "\n", + "Settings:\n", + "\t", "auto_zip: ", this.settings.autoZip, "\n", + "\t", "upload_url: ", this.settings.upload_url, "\n", + "\t", "flash_url: ", this.settings.flash_url, "\n", + "\t", "use_query_string: ", this.settings.use_query_string.toString(), "\n", + "\t", "requeue_on_error: ", this.settings.requeue_on_error.toString(), "\n", + "\t", "http_success: ", this.settings.http_success.join(", "), "\n", + "\t", "assume_success_timeout: ", this.settings.assume_success_timeout, "\n", + "\t", "file_post_name: ", this.settings.file_post_name, "\n", + "\t", "post_params: ", this.settings.post_params.toString(), "\n", + "\t", "file_types: ", this.settings.file_types, "\n", + "\t", "file_types_description: ", this.settings.file_types_description, "\n", + "\t", "file_size_limit: ", this.settings.file_size_limit, "\n", + "\t", "file_upload_limit: ", this.settings.file_upload_limit, "\n", + "\t", "file_queue_limit: ", this.settings.file_queue_limit, "\n", + "\t", "debug: ", this.settings.debug.toString(), "\n", + + "\t", "prevent_swf_caching: ", this.settings.prevent_swf_caching.toString(), "\n", + + "\t", "button_placeholder_id: ", this.settings.button_placeholder_id.toString(), "\n", + "\t", "button_placeholder: ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n", + "\t", "button_image_url: ", this.settings.button_image_url.toString(), "\n", + "\t", "button_width: ", this.settings.button_width.toString(), "\n", + "\t", "button_height: ", this.settings.button_height.toString(), "\n", + "\t", "button_text: ", this.settings.button_text.toString(), "\n", + "\t", "button_text_style: ", this.settings.button_text_style.toString(), "\n", + "\t", "button_text_top_padding: ", this.settings.button_text_top_padding.toString(), "\n", + "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n", + "\t", "button_action: ", this.settings.button_action.toString(), "\n", + "\t", "button_disabled: ", this.settings.button_disabled.toString(), "\n", + + "\t", "custom_settings: ", this.settings.custom_settings.toString(), "\n", + "Event Handlers:\n", + "\t", "swfupload_loaded_handler assigned: ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n", + "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n", + "\t", "file_queued_handler assigned: ", (typeof this.settings.file_queued_handler === "function").toString(), "\n", + "\t", "file_queue_error_handler assigned: ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n", + "\t", "upload_start_handler assigned: ", (typeof this.settings.upload_start_handler === "function").toString(), "\n", + "\t", "upload_progress_handler assigned: ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n", + "\t", "upload_error_handler assigned: ", (typeof this.settings.upload_error_handler === "function").toString(), "\n", + "\t", "upload_success_handler assigned: ", (typeof this.settings.upload_success_handler === "function").toString(), "\n", + "\t", "upload_complete_handler assigned: ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n", + "\t", "debug_handler assigned: ", (typeof this.settings.debug_handler === "function").toString(), "\n" + ].join("") + ); +}; + +/* Note: addSetting and getSetting are no longer used by SWFUpload but are included + the maintain v2 API compatibility +*/ +// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used. +SWFUpload.prototype.addSetting = function (name, value, default_value) { + if (value == undefined) { + return (this.settings[name] = default_value); + } else { + return (this.settings[name] = value); + } +}; + +// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found. +SWFUpload.prototype.getSetting = function (name) { + if (this.settings[name] != undefined) { + return this.settings[name]; + } + + return ""; +}; + + + +// Private: callFlash handles function calls made to the Flash element. +// Calls are made with a setTimeout for some functions to work around +// bugs in the ExternalInterface library. +SWFUpload.prototype.callFlash = function (functionName, argumentArray) { + argumentArray = argumentArray || []; + + var movieElement = this.getMovieElement(); + var returnValue, returnString; + + // Flash's method if calling ExternalInterface methods (code adapted from MooTools). + try { + returnString = movieElement.CallFunction('' + __flash__argumentsToXML(argumentArray, 0) + ''); + returnValue = eval(returnString); + } catch (ex) { + throw "Call to " + functionName + " failed"; + } + + // Unescape file post param values + if (returnValue != undefined && typeof returnValue.post === "object") { + returnValue = this.unescapeFilePostParams(returnValue); + } + + return returnValue; +}; + +/* ***************************** + -- Flash control methods -- + Your UI should use these + to operate SWFUpload + ***************************** */ + +// WARNING: this function does not work in Flash Player 10 +// Public: selectFile causes a File Selection Dialog window to appear. This +// dialog only allows 1 file to be selected. +SWFUpload.prototype.selectFile = function () { + this.callFlash("SelectFile"); +}; + +// WARNING: this function does not work in Flash Player 10 +// Public: selectFiles causes a File Selection Dialog window to appear/ This +// dialog allows the user to select any number of files +// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names. +// If the selection name length is too long the dialog will fail in an unpredictable manner. There is no work-around +// for this bug. +SWFUpload.prototype.selectFiles = function () { + this.callFlash("SelectFiles"); +}; + + +// Public: startUpload starts uploading the first file in the queue unless +// the optional parameter 'fileID' specifies the ID +SWFUpload.prototype.startUpload = function (fileID) { + this.callFlash("StartUpload", [fileID]); +}; + +// Public: cancelUpload cancels any queued file. The fileID parameter may be the file ID or index. +// If you do not specify a fileID the current uploading file or first file in the queue is cancelled. +// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter. +SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) { + if (triggerErrorEvent !== false) { + triggerErrorEvent = true; + } + this.callFlash("CancelUpload", [fileID, triggerErrorEvent]); +}; + +// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue. +// If nothing is currently uploading then nothing happens. +SWFUpload.prototype.stopUpload = function () { + this.callFlash("StopUpload"); +}; + +/* ************************ + * Settings methods + * These methods change the SWFUpload settings. + * SWFUpload settings should not be changed directly on the settings object + * since many of the settings need to be passed to Flash in order to take + * effect. + * *********************** */ + +// Public: getStats gets the file statistics object. +SWFUpload.prototype.getStats = function () { + return this.callFlash("GetStats"); +}; + +// Public: setStats changes the SWFUpload statistics. You shouldn't need to +// change the statistics but you can. Changing the statistics does not +// affect SWFUpload accept for the successful_uploads count which is used +// by the upload_limit setting to determine how many files the user may upload. +SWFUpload.prototype.setStats = function (statsObject) { + this.callFlash("SetStats", [statsObject]); +}; + +// Public: getFile retrieves a File object by ID or Index. If the file is +// not found then 'null' is returned. +SWFUpload.prototype.getFile = function (fileID) { + if (typeof(fileID) === "number") { + return this.callFlash("GetFileByIndex", [fileID]); + } else { + return this.callFlash("GetFile", [fileID]); + } +}; + +// Public: addFileParam sets a name/value pair that will be posted with the +// file specified by the Files ID. If the name already exists then the +// exiting value will be overwritten. +SWFUpload.prototype.addFileParam = function (fileID, name, value) { + return this.callFlash("AddFileParam", [fileID, name, value]); +}; + +// Public: removeFileParam removes a previously set (by addFileParam) name/value +// pair from the specified file. +SWFUpload.prototype.removeFileParam = function (fileID, name) { + this.callFlash("RemoveFileParam", [fileID, name]); +}; + +// Public: setUploadUrl changes the upload_url setting. +SWFUpload.prototype.setUploadURL = function (url) { + this.settings.upload_url = url.toString(); + this.callFlash("SetUploadURL", [url]); +}; + +// Public: setPostParams changes the post_params setting +SWFUpload.prototype.setPostParams = function (paramsObject) { + this.settings.post_params = paramsObject; + this.callFlash("SetPostParams", [paramsObject]); +}; + +// Public: addPostParam adds post name/value pair. Each name can have only one value. +SWFUpload.prototype.addPostParam = function (name, value) { + this.settings.post_params[name] = value; + this.callFlash("SetPostParams", [this.settings.post_params]); +}; + +// Public: removePostParam deletes post name/value pair. +SWFUpload.prototype.removePostParam = function (name) { + delete this.settings.post_params[name]; + this.callFlash("SetPostParams", [this.settings.post_params]); +}; + +// Public: setFileTypes changes the file_types setting and the file_types_description setting +SWFUpload.prototype.setFileTypes = function (types, description) { + this.settings.file_types = types; + this.settings.file_types_description = description; + this.callFlash("SetFileTypes", [types, description]); +}; + +// Public: setFileSizeLimit changes the file_size_limit setting +SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) { + this.settings.file_size_limit = fileSizeLimit; + this.callFlash("SetFileSizeLimit", [fileSizeLimit]); +}; + +// Public: setFileUploadLimit changes the file_upload_limit setting +SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) { + this.settings.file_upload_limit = fileUploadLimit; + this.callFlash("SetFileUploadLimit", [fileUploadLimit]); +}; + +// Public: setFileQueueLimit changes the file_queue_limit setting +SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) { + this.settings.file_queue_limit = fileQueueLimit; + this.callFlash("SetFileQueueLimit", [fileQueueLimit]); +}; + +// Public: setFilePostName changes the file_post_name setting +SWFUpload.prototype.setFilePostName = function (filePostName) { + this.settings.file_post_name = filePostName; + this.callFlash("SetFilePostName", [filePostName]); +}; + +// Public: setUseQueryString changes the use_query_string setting +SWFUpload.prototype.setUseQueryString = function (useQueryString) { + this.settings.use_query_string = useQueryString; + this.callFlash("SetUseQueryString", [useQueryString]); +}; + +// Public: setRequeueOnError changes the requeue_on_error setting +SWFUpload.prototype.setRequeueOnError = function (requeueOnError) { + this.settings.requeue_on_error = requeueOnError; + this.callFlash("SetRequeueOnError", [requeueOnError]); +}; + +// Public: setHTTPSuccess changes the http_success setting +SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) { + if (typeof http_status_codes === "string") { + http_status_codes = http_status_codes.replace(" ", "").split(","); + } + + this.settings.http_success = http_status_codes; + this.callFlash("SetHTTPSuccess", [http_status_codes]); +}; + +// Public: setHTTPSuccess changes the http_success setting +SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) { + this.settings.assume_success_timeout = timeout_seconds; + this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]); +}; + +// Public: setDebugEnabled changes the debug_enabled setting +SWFUpload.prototype.setDebugEnabled = function (debugEnabled) { + this.settings.debug_enabled = debugEnabled; + this.callFlash("SetDebugEnabled", [debugEnabled]); +}; + +// Public: setButtonImageURL loads a button image sprite +SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) { + if (buttonImageURL == undefined) { + buttonImageURL = ""; + } + + this.settings.button_image_url = buttonImageURL; + this.callFlash("SetButtonImageURL", [buttonImageURL]); +}; + +// Public: setButtonDimensions resizes the Flash Movie and button +SWFUpload.prototype.setButtonDimensions = function (width, height) { + this.settings.button_width = width; + this.settings.button_height = height; + + var movie = this.getMovieElement(); + if (movie != undefined) { + movie.style.width = width + "px"; + movie.style.height = height + "px"; + } + + this.callFlash("SetButtonDimensions", [width, height]); +}; +// Public: setButtonText Changes the text overlaid on the button +SWFUpload.prototype.setButtonText = function (html) { + this.settings.button_text = html; + this.callFlash("SetButtonText", [html]); +}; +// Public: setButtonTextPadding changes the top and left padding of the text overlay +SWFUpload.prototype.setButtonTextPadding = function (left, top) { + this.settings.button_text_top_padding = top; + this.settings.button_text_left_padding = left; + this.callFlash("SetButtonTextPadding", [left, top]); +}; + +// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button +SWFUpload.prototype.setButtonTextStyle = function (css) { + this.settings.button_text_style = css; + this.callFlash("SetButtonTextStyle", [css]); +}; +// Public: setButtonDisabled disables/enables the button +SWFUpload.prototype.setButtonDisabled = function (isDisabled) { + this.settings.button_disabled = isDisabled; + this.callFlash("SetButtonDisabled", [isDisabled]); +}; +// Public: setButtonAction sets the action that occurs when the button is clicked +SWFUpload.prototype.setButtonAction = function (buttonAction) { + this.settings.button_action = buttonAction; + this.callFlash("SetButtonAction", [buttonAction]); +}; + +// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button +SWFUpload.prototype.setButtonCursor = function (cursor) { + this.settings.button_cursor = cursor; + this.callFlash("SetButtonCursor", [cursor]); +}; + +/* ******************************* + Flash Event Interfaces + These functions are used by Flash to trigger the various + events. + + All these functions a Private. + + Because the ExternalInterface library is buggy the event calls + are added to a queue and the queue then executed by a setTimeout. + This ensures that events are executed in a determinate order and that + the ExternalInterface bugs are avoided. +******************************* */ + +SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) { + // Warning: Don't call this.debug inside here or you'll create an infinite loop + + if (argumentArray == undefined) { + argumentArray = []; + } else if (!(argumentArray instanceof Array)) { + argumentArray = [argumentArray]; + } + + var self = this; + if (typeof this.settings[handlerName] === "function") { + // Queue the event + this.eventQueue.push(function () { + this.settings[handlerName].apply(this, argumentArray); + }); + + // Execute the next queued event + setTimeout(function () { + self.executeNextEvent(); + }, 0); + + } else if (this.settings[handlerName] !== null) { + throw "Event handler " + handlerName + " is unknown or is not a function"; + } +}; + +// Private: Causes the next event in the queue to be executed. Since events are queued using a setTimeout +// we must queue them in order to garentee that they are executed in order. +SWFUpload.prototype.executeNextEvent = function () { + // Warning: Don't call this.debug inside here or you'll create an infinite loop + + var f = this.eventQueue ? this.eventQueue.shift() : null; + if (typeof(f) === "function") { + f.apply(this); + } +}; + +// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have +// properties that contain characters that are not valid for JavaScript identifiers. To work around this +// the Flash Component escapes the parameter names and we must unescape again before passing them along. +SWFUpload.prototype.unescapeFilePostParams = function (file) { + var reg = /[$]([0-9a-f]{4})/i; + var unescapedPost = {}; + var uk; + + if (file != undefined) { + for (var k in file.post) { + if (file.post.hasOwnProperty(k)) { + uk = k; + var match; + while ((match = reg.exec(uk)) !== null) { + uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16))); + } + unescapedPost[uk] = file.post[k]; + } + } + + file.post = unescapedPost; + } + + return file; +}; + +// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working) +SWFUpload.prototype.testExternalInterface = function () { + try { + return this.callFlash("TestExternalInterface"); + } catch (ex) { + return false; + } +}; + +// Private: This event is called by Flash when it has finished loading. Don't modify this. +// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded. +SWFUpload.prototype.flashReady = function () { + // Check that the movie element is loaded correctly with its ExternalInterface methods defined + var movieElement = this.getMovieElement(); + + if (!movieElement) { + this.debug("Flash called back ready but the flash movie can't be found."); + return; + } + + this.cleanUp(movieElement); + + this.queueEvent("swfupload_loaded_handler"); +}; + +// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE. +// This function is called by Flash each time the ExternalInterface functions are created. +SWFUpload.prototype.cleanUp = function (movieElement) { + // Pro-actively unhook all the Flash functions + try { + if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE + this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)"); + for (var key in movieElement) { + try { + if (typeof(movieElement[key]) === "function") { + movieElement[key] = null; + } + } catch (ex) { + } + } + } + } catch (ex1) { + + } + + // Fix Flashes own cleanup code so if the SWFMovie was removed from the page + // it doesn't display errors. + window["__flash__removeCallback"] = function (instance, name) { + try { + if (instance) { + instance[name] = null; + } + } catch (flashEx) { + + } + }; + +}; + + +/* This is a chance to do something before the browse window opens */ +SWFUpload.prototype.fileDialogStart = function () { + this.queueEvent("file_dialog_start_handler"); +}; + + +/* Called when a file is successfully added to the queue. */ +SWFUpload.prototype.fileQueued = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("file_queued_handler", file); +}; + + +/* Handle errors that occur when an attempt to queue a file fails. */ +SWFUpload.prototype.fileQueueError = function (file, errorCode, message) { + file = this.unescapeFilePostParams(file); + this.queueEvent("file_queue_error_handler", [file, errorCode, message]); +}; + +/* Called after the file dialog has closed and the selected files have been queued. + You could call startUpload here if you want the queued files to begin uploading immediately. */ +SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) { + this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]); +}; + +SWFUpload.prototype.uploadStart = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("return_upload_start_handler", file); +}; + +SWFUpload.prototype.returnUploadStart = function (file) { + var returnValue; + if (typeof this.settings.upload_start_handler === "function") { + file = this.unescapeFilePostParams(file); + returnValue = this.settings.upload_start_handler.call(this, file); + } else if (this.settings.upload_start_handler != undefined) { + throw "upload_start_handler must be a function"; + } + + // Convert undefined to true so if nothing is returned from the upload_start_handler it is + // interpretted as 'true'. + if (returnValue === undefined) { + returnValue = true; + } + + returnValue = !!returnValue; + + this.callFlash("ReturnUploadStart", [returnValue]); +}; + + + +SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]); +}; + +SWFUpload.prototype.uploadError = function (file, errorCode, message) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_error_handler", [file, errorCode, message]); +}; + +SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_success_handler", [file, serverData, responseReceived]); +}; + +SWFUpload.prototype.uploadComplete = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_complete_handler", file); +}; + +/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the + internal debug console. You can override this event and have messages written where you want. */ +SWFUpload.prototype.debug = function (message) { + this.queueEvent("debug_handler", message); +}; + + +/* ********************************** + Debug Console + The debug console is a self contained, in page location + for debug message to be sent. The Debug Console adds + itself to the body if necessary. + + The console is automatically scrolled as messages appear. + + If you are using your own debug handler or when you deploy to production and + have debug disabled you can remove these functions to reduce the file size + and complexity. +********************************** */ + +// Private: debugMessage is the default debug_handler. If you want to print debug messages +// call the debug() function. When overriding the function your own function should +// check to see if the debug setting is true before outputting debug information. +SWFUpload.prototype.debugMessage = function (message) { + if (this.settings.debug) { + var exceptionMessage, exceptionValues = []; + + // Check for an exception object and print it nicely + if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") { + for (var key in message) { + if (message.hasOwnProperty(key)) { + exceptionValues.push(key + ": " + message[key]); + } + } + exceptionMessage = exceptionValues.join("\n") || ""; + exceptionValues = exceptionMessage.split("\n"); + exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: "); + SWFUpload.Console.writeLine(exceptionMessage); + } else { + SWFUpload.Console.writeLine(message); + } + } +}; + +SWFUpload.Console = {}; +SWFUpload.Console.writeLine = function (message) { + var console, documentForm; + + try { + console = document.getElementById("SWFUpload_Console"); + + if (!console) { + documentForm = document.createElement("form"); + document.getElementsByTagName("body")[0].appendChild(documentForm); + + console = document.createElement("textarea"); + console.id = "SWFUpload_Console"; + console.style.fontFamily = "monospace"; + console.setAttribute("wrap", "off"); + console.wrap = "off"; + console.style.overflow = "auto"; + console.style.width = "700px"; + console.style.height = "350px"; + console.style.margin = "5px"; + documentForm.appendChild(console); + } + + console.value += message + "\n"; + + console.scrollTop = console.scrollHeight - console.clientHeight; + } catch (ex) { + alert("Exception: " + ex.name + " Message: " + ex.message); + } +}; diff --git a/plugins/sys_plugin/setup.html b/plugins/sys_plugin/setup.html index a392127..c6c977f 100755 --- a/plugins/sys_plugin/setup.html +++ b/plugins/sys_plugin/setup.html @@ -6,10 +6,10 @@

    Notify Setting

    {% trans "Message to me:"%}
    - +
    {% trans "Message to commenter:"%}
    - +

    diff --git a/plugins/sys_plugin/setup2.html b/plugins/sys_plugin/setup2.html index 78c0ce1..7fda470 100755 --- a/plugins/sys_plugin/setup2.html +++ b/plugins/sys_plugin/setup2.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "../../views/admin/base.html" %} +{% load i18n %} {% block header %} {% endblock %} {% block nav2 %} @@ -18,28 +18,27 @@ {% endblock %} {% block content %}

    Sys Plugin

    -

    This is a system plugin for micolog.
    Also a demo for how to write plugin for micolog.

    -

    feature

    -

      -
    1. Add Meta <meta name="generator" content="Micolog x.x" />
    2. -
    3. Add footer "<!--Powered by micolog x.x-->"
    4. -
    5. Comments Filter with blocklist Setup
    6. -
    7. Comment Notify.
    8. -

    -

    Notify Setting

    -
    -
    {% trans "Enable comment notify by mail:"%} - -
    -
    {% trans "Message to me:"%}
    - - - -
    {% trans "Message to commenter:"%}
    - -

    - -

    - - +

    This is a system plugin for micolog.
    Also a demo for how to write plugin for micolog.

    +

    feature

    +

      +
    1. Add Meta <meta name="generator" content="Micolog x.x" />
    2. +
    3. Add footer "<!--Powered by micolog x.x-->"
    4. +
    5. Comments Filter with blocklist Setup
    6. +
    7. Comment Notify.
    8. +

    +

    Notify Setting

    +
    +
    {% trans "Enable comment notify by mail:"%} + +
    +
    {% trans "Message to me:"%}
    + + + +
    {% trans "Message to commenter:"%}
    + +

    + +

    + {%endblock%} \ No newline at end of file diff --git a/plugins/sys_plugin/sys_plugin.py b/plugins/sys_plugin/sys_plugin.py index a64ce02..0c5eff5 100644 --- a/plugins/sys_plugin/sys_plugin.py +++ b/plugins/sys_plugin/sys_plugin.py @@ -67,22 +67,18 @@ def __init__(self): self.register_urlmap('sys_plugin/setup',self.setup) self.register_urlhandler('/admin/sys_plugin/notify',NotifyHandler) - self.register_setupmenu('sysplugin_notify',_('Notify'),'/admin/sys_plugin/notify') + self.register_setupmenu('sysplugin_notify','Notify','/admin/sys_plugin/notify') self.register_action('pre_comment',self.pre_comment) self.register_action('save_comment',self.save_comment) self.sbody=OptionSet.getValue('sys_plugin_sbody',SBODY) self.bbody=OptionSet.getValue('sys_plugin_bbody',BBODY) - - - def head(self,content,blog=None,*arg1,**arg2): content=content+''%blog.version return content def footer(self,content,blog=None,*arg1,**arg2): - return content+''%blog.version def setup(self,page=None,*arg1,**arg2): @@ -119,12 +115,12 @@ def pre_comment(self,comment,*arg1,**arg2): for s in self.blocklist.splitlines(): if comment.content.find(s)>-1: raise Exception + def save_comment(self,comment,*arg1,**arg2): if self.blog.comment_notify_mail: self.notify(comment) def notify(self,comment): - try: sbody=self.sbody.decode('utf-8') except: diff --git a/plugins/wordpress/wordpress.py b/plugins/wordpress/wordpress.py index 0ac06cb..65bcf2a 100644 --- a/plugins/wordpress/wordpress.py +++ b/plugins/wordpress/wordpress.py @@ -1,6 +1,6 @@ from micolog_plugin import * from google.appengine.api import memcache -from google.appengine.api.labs import taskqueue +from google.appengine.api import taskqueue from wp_import import * from model import * import logging,math diff --git a/run.bat b/run.bat index 091e762..ed9909b 100644 --- a/run.bat +++ b/run.bat @@ -1 +1 @@ -dev_appserver.py . --debug +dev_appserver.py --skip_sdk_update_check --debug . \ No newline at end of file diff --git a/settings.py b/settings.py index 233e343..20e1a6e 100644 --- a/settings.py +++ b/settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Django settings for the example project. -import os - -DEBUG = True +DEBUG = False TEMPLATE_DEBUG = False ##LANGUAGE_CODE = 'zh-CN' diff --git a/static/admin/images/quote.gif b/static/admin/images/quote.gif new file mode 100644 index 0000000..2623ea0 Binary files /dev/null and b/static/admin/images/quote.gif differ diff --git a/static/admin/style.css b/static/admin/style.css index eeced75..594604d 100644 --- a/static/admin/style.css +++ b/static/admin/style.css @@ -510,7 +510,6 @@ background : #d54e21; color : #fff; } #footer .flogo { -background : url(/static/images/mlog.png) no-repeat; width : 400px; height : 32px; padding-left : 40px; @@ -819,7 +818,7 @@ width:300px; #postarea a{cursor:pointer} #postarea .title{float:left} #postarea .editor-toolbar span { -font-weight : blod; +font-weight : bold; height : 20px; margin : 0 5px; padding : 3px 5px 0; @@ -900,7 +899,7 @@ padding:0 0 6px; } #nav2 ul{height:20px;margin-bottom:0;list-style-type:none;margin-left:0;padding:0} -#nav2 ul li{display:bolck;float:left;} +#nav2 ul li{display:block;float:left;} #nav2 a { color : #2583ad; diff --git a/static/js/pagenavi.js b/static/js/pagenavi.js new file mode 100644 index 0000000..eb4eb65 --- /dev/null +++ b/static/js/pagenavi.js @@ -0,0 +1,28 @@ +function showPageLink(sUrl,iPage,iCount,sAnchor){ +var i; +i=Math.max(1,iPage-1); +if(iPage==1){ +document.write("Home "); +} +else{ +document.write("Home "); +document.write("< "); +} +if(iPage>5) document.write("... "); +for(i=Math.max(1,iPage-4);i" + i + " "); +} +document.write("" + iPage + " "); +for(i=iPage+1;i<=Math.min(iCount,iPage+4);i++){ +document.write("" + i + " "); +} +i=Math.min(iCount,iPage+1); +if(iCount>iPage+4) document.write("... "); +if(iPage==iCount){ +document.write(" Last "); +} +else{ +document.write("> "); +document.write("Last "); +} +} \ No newline at end of file diff --git a/static/js/tiny_mce_config_zh-cn.js b/static/js/tiny_mce_config_zh-cn.js index 5a2ef33..33a1c4a 100644 --- a/static/js/tiny_mce_config_zh-cn.js +++ b/static/js/tiny_mce_config_zh-cn.js @@ -1,10 +1,10 @@ tinyMCE.init({ // General options - mode : "exact", - elements: "content", + mode : "exact", + elements: "content", theme : "advanced", skin:"wp_theme", - language : "zh", + language : "en", plugins : "wordpress,safari,pagebreak,save,advhr,advimage,advlink,emotions, inlinepopups,media,directionality,visualchars,nonbreaking,emotions,fullscreen", // Theme options theme_advanced_buttons1:"bold,italic,strikethrough,|,bullist,numlist,blockquote,|,justifyleft,justifycenter,justifyright,|,link,unlink,image,wp_more,|,fullscreen,wp_adv", @@ -20,13 +20,8 @@ theme_advanced_resizing : true, // Example content CSS (should be your site CSS) content_css : "/tinymce/wordpress.css", - // Drop lists for link/image/media/template dialogs template_external_list_url : "lists/template_list.js", - - - - external_link_list_url : "lists/link_list.js", external_image_list_url : "lists/image_list.js", media_external_list_url : "lists/media_list.js" diff --git a/theme_files.py b/theme_files.py index ee2953a..514df68 100644 --- a/theme_files.py +++ b/theme_files.py @@ -1,13 +1,12 @@ # -*- coding: utf-8 -*- -import os,stat -import sys -import logging +import os,sys,stat +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +from google.appengine.dist import use_library +use_library('django', '1.2') import wsgiref.handlers from mimetypes import types_map -from datetime import datetime, timedelta -from google.appengine.ext import webapp +from datetime import timedelta from google.appengine.ext.webapp import template -from google.appengine.api import memcache from google.appengine.ext.zipserve import * sys.path.append('modules') from model import * @@ -20,99 +19,99 @@ max_age = 600 #expires in 10 minutes def Error404(handler): - handler.response.set_status(404) - html = template.render(os.path.join(cwd,'views/404.html'), {'error':404}) - handler.response.out.write(html) + handler.response.set_status(404) + html = template.render(os.path.join(cwd,'views/404.html'), {'error':404}) + handler.response.out.write(html) class GetFile(webapp.RequestHandler): - def get(self,prefix,name): - request_path = self.request.path[8:] - - - server_path = os.path.normpath(os.path.join(cwd, 'themes', request_path)) - try: - fstat=os.stat(server_path) - except: - #use zipfile - theme_file=os.path.normpath(os.path.join(cwd, 'themes', prefix)) - if os.path.exists(theme_file+".zip"): - #is file exist? - fstat=os.stat(theme_file+".zip") - zipdo=ZipHandler() - zipdo.initialize(self.request,self.response) - return zipdo.get(theme_file,name) - else: - Error404(self) - return - - - fmtime=datetime.fromtimestamp(fstat[stat.ST_MTIME]) - if self.request.if_modified_since and self.request.if_modified_since.replace(tzinfo=None) >= fmtime: - self.response.headers['Date'] = format_date(datetime.utcnow()) - self.response.headers['Last-Modified'] = format_date(fmtime) - cache_expires(self.response, max_age) - self.response.set_status(304) - self.response.clear() - - elif server_path.startswith(theme_path): - ext = os.path.splitext(server_path)[1] - if types_map.has_key(ext): - mime_type = types_map[ext] - else: - mime_type = 'application/octet-stream' - try: - self.response.headers['Content-Type'] = mime_type - self.response.headers['Last-Modified'] = format_date(fmtime) - cache_expires(self.response, max_age) - self.response.out.write(open(server_path, 'rb').read()) - except Exception, e: - Error404(self) - else: - Error404(self) + def get(self,prefix,name): + request_path = self.request.path[8:] + + + server_path = os.path.normpath(os.path.join(cwd, 'themes', request_path)) + try: + fstat=os.stat(server_path) + except: + #use zipfile + theme_file=os.path.normpath(os.path.join(cwd, 'themes', prefix)) + if os.path.exists(theme_file+".zip"): + #is file exist? + fstat=os.stat(theme_file+".zip") + zipdo=ZipHandler() + zipdo.initialize(self.request,self.response) + return zipdo.get(theme_file,name) + else: + Error404(self) + return + + + fmtime=datetime.fromtimestamp(fstat[stat.ST_MTIME]) + if self.request.if_modified_since and self.request.if_modified_since.replace(tzinfo=None) >= fmtime: + self.response.headers['Date'] = format_date(datetime.utcnow()) + self.response.headers['Last-Modified'] = format_date(fmtime) + cache_expires(self.response, max_age) + self.response.set_status(304) + self.response.clear() + + elif server_path.startswith(theme_path): + ext = os.path.splitext(server_path)[1] + if types_map.has_key(ext): + mime_type = types_map[ext] + else: + mime_type = 'application/octet-stream' + try: + self.response.headers['Content-Type'] = mime_type + self.response.headers['Last-Modified'] = format_date(fmtime) + cache_expires(self.response, max_age) + self.response.out.write(open(server_path, 'rb').read()) + except Exception, e: + Error404(self) + else: + Error404(self) class NotFound(webapp.RequestHandler): - def get(self): - Error404(self) + def get(self): + Error404(self) #}}} def format_date(dt): - return dt.strftime('%a, %d %b %Y %H:%M:%S GMT') + return dt.strftime('%a, %d %b %Y %H:%M:%S GMT') def cache_expires(response, seconds=0, **kw): - """ - Set expiration on this request. This sets the response to - expire in the given seconds, and any other attributes are used - for cache_control (e.g., private=True, etc). - - this function is modified from webob.Response - it will be good if google.appengine.ext.webapp.Response inherits from this class... - """ - if not seconds: - # To really expire something, you have to force a - # bunch of these cache control attributes, and IE may - # not pay attention to those still so we also set - # Expires. - response.headers['Cache-Control'] = 'max-age=0, must-revalidate, no-cache, no-store' - response.headers['Expires'] = format_date(datetime.utcnow()) - if 'last-modified' not in self.headers: - self.last_modified = format_date(datetime.utcnow()) - response.headers['Pragma'] = 'no-cache' - else: - response.headers['Cache-Control'] = 'max-age=%d' % seconds - response.headers['Expires'] = format_date(datetime.utcnow() + timedelta(seconds=seconds)) + """ + Set expiration on this request. This sets the response to + expire in the given seconds, and any other attributes are used + for cache_control (e.g., private=True, etc). + + this function is modified from webob.Response + it will be good if google.appengine.ext.webapp.Response inherits from this class... + """ + if not seconds: + # To really expire something, you have to force a + # bunch of these cache control attributes, and IE may + # not pay attention to those still so we also set + # Expires. + response.headers['Cache-Control'] = 'max-age=0, must-revalidate, no-cache, no-store' + response.headers['Expires'] = format_date(datetime.utcnow()) + if 'last-modified' not in self.headers: + self.last_modified = format_date(datetime.utcnow()) + response.headers['Pragma'] = 'no-cache' + else: + response.headers['Cache-Control'] = 'max-age=%d' % seconds + response.headers['Expires'] = format_date(datetime.utcnow() + timedelta(seconds=seconds)) def main(): - application = webapp.WSGIApplication( - [ - ('/themes/[\\w\\-]+/templates/.*', NotFound), - ('/themes/(?P[\\w\\-]+)/(?P.+)', GetFile), - ('.*', NotFound), - ], - debug=True) - wsgiref.handlers.CGIHandler().run(application) + application = webapp.WSGIApplication( + [ + ('/themes/[\\w\\-]+/templates/.*', NotFound), + ('/themes/(?P[\\w\\-]+)/(?P.+)', GetFile), + ('.*', NotFound), + ], + debug=True) + wsgiref.handlers.CGIHandler().run(application) if __name__ == '__main__': - main() + main() diff --git a/themes/default/style.css b/themes/default/style.css index 4584cb7..49021f4 100644 --- a/themes/default/style.css +++ b/themes/default/style.css @@ -103,7 +103,7 @@ acronym, abbr, span.caps { font-size: 0.9em; letter-spacing: .07em; } /*...... comments ........*/ h3#comments, h3#respond { font:1.3em Georgia, serif; margin-bottom:30px; background:#fcfcfc; display:block; padding:5px; border-bottom:1px dashed #ccc; text-transform:uppercase; } -.box ol.commentlist { width:670px; clear:both; padding: 0 0 0 -25px; } +.box ol.commentlist { width:670px; clear:both; padding: 0; } .box ol.commentlist li.alt { background:#fafafa; color:#333; padding:5px; margin-bottom:5px; list-style:none; } .box ol.commentlist li { color:#333; padding:5px; margin-bottom:5px; } .box ol.commentlist li .gravatar { float:left; border:3px double #dadada; width:55px; margin-right:5px;} diff --git a/themes/default/templates/base.html b/themes/default/templates/base.html index 9e58813..edcb21d 100644 --- a/themes/default/templates/base.html +++ b/themes/default/templates/base.html @@ -6,9 +6,10 @@ {% block title %} {{ blog.title}}{% endblock %} + - + {% block head_block %}{% endblock %}

    @@ -43,25 +49,29 @@

    Leave a Reply

    +

    +

    + {%ifequal blog.comment_check_type 1%}

    - - + +

    {%endifequal%} {%ifequal blog.comment_check_type 2%}
    - +
    {%endifequal%} -

    +

    -

    +

    @@ -70,15 +80,15 @@

    Leave a Reply

    commentuser=$.cookie('comment_user'); if (commentuser) { - //[user,email,url]=commentuser.split('#@#'); - var tuple=commentuser.split('#@#'); + //[user,email,url]=commentuser.split('#@#'); + var tuple=commentuser.split('#@#'); var user,email,url; user=tuple[0]; email=tuple[1]; url=tuple[2]; - $('#author').val(user); - $('#email').val(email); - $('#url').val(url); + $('#author').val(user); + $('#email').val(email); + $('#url').val(url); } $('#commentform').submit(function(){ @@ -112,5 +122,5 @@

    Leave a Reply

    {% else %} -

    ѹر

    +

    评论关闭

    {% endif %} diff --git a/themes/default/templates/index.html b/themes/default/templates/index.html index 9ea0788..e5c03f3 100644 --- a/themes/default/templates/index.html +++ b/themes/default/templates/index.html @@ -12,7 +12,7 @@
    -{{entry.content_excerpt}} +{{entry.content_excerpt|safe}}
    {% if entry.strtags %}Tags: {%for tag in entry.tags%}{{tag}} {%endfor%}

    {%endif%} Posted in {%for cate in entry.categories%} {{cate.name}} {%endfor%} | {%if self.is_admin%}Edit | {%endif%} Comments({{entry.commentcount}})»
    @@ -26,7 +26,11 @@
    {% if show_next %}« Older Entries{%endif%}
    -
    {% if show_prev %}« Newer Entries{%endif%}
    +
    + + +
    +
    {% if show_prev %}Newer Entries »{%endif%}
    diff --git a/themes/default/templates/msg.html b/themes/default/templates/msg.html index 147a8a1..c484123 100755 --- a/themes/default/templates/msg.html +++ b/themes/default/templates/msg.html @@ -1,5 +1,5 @@ {% extends "base.html" %} {% block content %}

    {{title}}

    -{{ message }} +{{ message|safe }} {% endblock %} \ No newline at end of file diff --git a/themes/default/templates/page.html b/themes/default/templates/page.html index a24fc66..5a1d319 100644 --- a/themes/default/templates/page.html +++ b/themes/default/templates/page.html @@ -13,11 +13,11 @@ {% if entry %}
    -

    {{entry.title}} {{entry.date|datetz:"F jS, Y"}}

    +

    {{entry.title}} {{entry.date|datetz:"F jS, Y"}}

    -{{ entry.content }} +{{ entry.content|safe }}
    -
    {%if self.is_admin%}Edit this entry {%endif%}
    +
    {%if self.is_admin%}Edit this entry {%endif%}
    diff --git a/themes/default/templates/single.html b/themes/default/templates/single.html index 582cb69..885a250 100644 --- a/themes/default/templates/single.html +++ b/themes/default/templates/single.html @@ -8,10 +8,10 @@ {% if entry %}
    -

    {{entry.title}} {{entry.date|datetz:"F jS, Y"}}

    +

    {{entry.title}} {{entry.date|datetz:"F jS, Y"}}

    {%mf entry_content %} -{{ entry.content }} +{{ entry.content|safe }} {%endmf%} {%mf after_entry_content%} {%if relateposts%} @@ -24,10 +24,10 @@

    Relate Posts:

    {%endif%}
    -{% if entry.strtags %}Tags: {%for tag in entry.tags%}{{tag}} {%endfor%}

    {%endif%} Posted in {%for cate in entry.categories%} {{cate.name}} {%endfor%} | {%if self.is_admin%}Edit {%endif%} +{% if entry.strtags %}Tags: {%for tag in entry.tags%}{{tag}} {%endfor%}

    {%endif%} Posted in {%for cate in entry.categories%} {{cate.name}} {%endfor%} | {%if self.is_admin%}Edit {%endif%} {%if blog.allow_trackback and entry.allow_trackback%} {%endif%}
    diff --git a/themes/pixel-en/ie.css b/themes/pixel-en/ie.css new file mode 100644 index 0000000..ddd4158 --- /dev/null +++ b/themes/pixel-en/ie.css @@ -0,0 +1,16 @@ +#toprss { + margin: 9px 15px 0 0; + } + +#main { + margin-top: 10px; + } + +#main #welcome { + padding: 0; + } + +.submitbutton { + padding: 2px 1px 1px 1px; + } + diff --git a/themes/pixel-en/ie6.css b/themes/pixel-en/ie6.css new file mode 100644 index 0000000..3bf0037 --- /dev/null +++ b/themes/pixel-en/ie6.css @@ -0,0 +1,17 @@ +#main #welcome { + background: #020202; + } + +#contentwrapper .pageTitle, #contentwrapper2 .pageTitle { + background: #020202; + } + +.topPost span.topComments, .topPost span.topMore, .topPost span.topTags { + background: #020202; + padding: 0 10px; + } + +#sidebars .sidebarbox { + background: #020202; + } + diff --git a/themes/pixel-en/images/bg-trans.png b/themes/pixel-en/images/bg-trans.png new file mode 100644 index 0000000..e7702a6 Binary files /dev/null and b/themes/pixel-en/images/bg-trans.png differ diff --git a/themes/pixel-en/images/bgbody.jpg b/themes/pixel-en/images/bgbody.jpg new file mode 100644 index 0000000..711d912 Binary files /dev/null and b/themes/pixel-en/images/bgbody.jpg differ diff --git a/themes/pixel-en/images/bggrad.jpg b/themes/pixel-en/images/bggrad.jpg new file mode 100644 index 0000000..fa269ca Binary files /dev/null and b/themes/pixel-en/images/bggrad.jpg differ diff --git a/themes/pixel-en/images/bullet.gif b/themes/pixel-en/images/bullet.gif new file mode 100644 index 0000000..d803652 Binary files /dev/null and b/themes/pixel-en/images/bullet.gif differ diff --git a/themes/pixel-en/images/comments.gif b/themes/pixel-en/images/comments.gif new file mode 100644 index 0000000..d4cdac2 Binary files /dev/null and b/themes/pixel-en/images/comments.gif differ diff --git a/themes/pixel-en/images/gravatar-trans.png b/themes/pixel-en/images/gravatar-trans.png new file mode 100644 index 0000000..18a11bd Binary files /dev/null and b/themes/pixel-en/images/gravatar-trans.png differ diff --git a/themes/pixel-en/images/lookingbg.jpg b/themes/pixel-en/images/lookingbg.jpg new file mode 100644 index 0000000..38341b3 Binary files /dev/null and b/themes/pixel-en/images/lookingbg.jpg differ diff --git a/themes/pixel-en/images/more.gif b/themes/pixel-en/images/more.gif new file mode 100644 index 0000000..0b70d8f Binary files /dev/null and b/themes/pixel-en/images/more.gif differ diff --git a/themes/pixel-en/images/rss-trans.png b/themes/pixel-en/images/rss-trans.png new file mode 100644 index 0000000..bc62738 Binary files /dev/null and b/themes/pixel-en/images/rss-trans.png differ diff --git a/themes/pixel-en/images/tags.gif b/themes/pixel-en/images/tags.gif new file mode 100644 index 0000000..e6495bc Binary files /dev/null and b/themes/pixel-en/images/tags.gif differ diff --git a/themes/pixel-en/js/sfhover.js b/themes/pixel-en/js/sfhover.js new file mode 100644 index 0000000..73c27f4 --- /dev/null +++ b/themes/pixel-en/js/sfhover.js @@ -0,0 +1,20 @@ +sfHover = function() { + if (!document.getElementsByTagName) return false; + var nav = document.getElementById("nav"); + if (!nav) return false; + + var sfEls = nav.getElementsByTagName("li"); + + for (var i=0; iGPL. +. +*/ + +/* Defaults */ +* { + margin: 0; + padding: 0; + } +:focus { + outline:0; + } +h1, h2, h3, h4, h5, h6, p { + margin: 0; + padding: 10px 0; + } + +h1, h2, h3, h4, h5, h6 { + font-family: Verdana; + } + +hr { + color: #333; + border: 1px solid #121212; + } + +pre { + height: auto; + overflow-x:scroll; + } + +fieldset { + margin: 0; + padding: 0; + border: 0; + } + +dd { + padding-left: 15px; + } + + +p { + font-size: 9pt; + } + +a { + color: #6598b8; + text-decoration: none; + } + +a:hover { + color: #222; + text-decoration: underline; + } + +a img { + border: none; + } + +blockquote { + background: #070707; + border: 1px solid #060606; + padding: 5px 15px; + margin: 10px 10px 5px 15px; + font-style: italic; + color: #fff; + } + +code { + color: #3366cc; + font-style: italic; + } + +strong { + font-size: 110%; + } + +body { + text-align: center; + margin: 0; + padding: 0 0 15px 0; + font-family: trebuchet ms, arial, helvetica, sans-serif; + background: #000 url(images/bgbody.jpg) top center no-repeat; + color: #eee; + } + + +/* Header and wrapper */ +#wrapper { + margin: 0 auto; + width: 960px; + text-align: left; + padding: 0; + } + +#header { + padding: 0; + margin: 0; + height: 80px; + text-shadow: 0 0 4px #555; + } + +#header #topright { + text-align: right; + float: right; + width: 930px; + margin: 3px 0 0 0; +} +#header #topright ul { + list-style: none; + padding: 0; + margin: 0; + } +#header #topright li { + display: inline; + } +#header #topright li a { + color: #fff; + font-size: 9pt; + padding: 0 0 0 17px; + text-transform: lowercase; + } +#header #topright li a:hover { + color: #ccc; + text-decoration: none; + } + +#header #logo { + margin: 0; + padding: 0; +} +#header h1 { + padding: 0; + margin: 0; + } +#header h1 a { + color: #fff; + font-size: 17pt; + text-decoration: none; + } +#header h1 a:hover { + color: #eee; + text-decoration: none; + } +#header span { + padding: 0; + color: #eee; + font-size: 10pt; + } + + +/* Menu */ + +#catnav { + margin: 20px 0 0 0; + padding: 0; + clear: both; + height: 44px; + width: 960px; + } + +#nav { + list-style: none; + margin: 0; + padding: 0; + } + +#nav ul { + margin: 0; + padding: 0; + } + +#nav li { + float: left; + margin: 0; + padding: 0; + } + +#nav a { + display: block; + line-height: 44px; + margin: 0; + padding: 0 15px 0 15px; + font-size: 10pt; + color: #fff; + letter-spacing: -1px; + } + +#nav li a:hover { + color: #6598b8; + text-decoration: none; + display: block; + } + +#nav li ul { + list-style: none; + position: absolute; + width: 150px; + left: -999em; + } + +#nav li:hover ul, #nav li.sfhover ul { + left: auto; + } + +#nav li li { + float: left; + margin: 0; + padding: 0; + width: 150px; + } + +#nav li li a { + width: 150px; + height: 24px; + line-height: 24px; + color: #fff; + border-top: 1px solid #131f27; + background: #040404; + margin: 0; + padding: 5px 20px 5px 15px; + } + +#nav li li a:hover { + border-top: 1px solid #131f27; + background: #000; + padding: 5px 20px 5px 15px; + } + +#nav li:hover, #nav li.sfhover { /* prevents IE7 drop-down menu bug (focus on a page element prevents nested menus from disappearing) */ + position: static; + } + +#toprss { + float: right; + margin: 9px 8px 0 0; + display: inline; + width: 180px; + line-height: 41px; + } + +/* Top Content */ + +#main { + margin: 5px 15px 0 15px; + clear: both; + width: 930px; + padding-bottom: 5px; + } +#contentwrapper { + float:left; + width: 560px; + margin: 0 0 15px 0; + padding: 0; + } +#contentwrapper2 { + float:left; + width: 900px; margin: 0 0 15px 0; + padding: 0; + } +#contentwrapper .pageTitle, #contentwrapper2 .pageTitle { + margin: 15px 0 10px 0; + font-size: 14pt; + letter-spacing: -1px; + color: #fff; + font-weight: normal; + background: url(images/bg-trans.png) repeat; + padding: 5px; + } +.topPost { + margin: 0; + padding: 0; + font-size: 9pt; + } +.topPost h2.topTitle a { + font-size: 15pt; + font-weight: normal; + color: #fff; + letter-spacing: -1px; + } +.topPost h2.topTitle a:hover { + text-decoration: none; + color: #abd1ea; + } +.topPost p.topMeta { + padding: 0; + margin: -10px 0 0 0; + font-size: 9pt; + color: #cdcdcd; + } +.topPost p.topMeta a { + text-decoration: none; + color: #fff; + } +.topPost p.topMeta a:hover { + color: #ccc; + text-decoration: underline; + } +.topPost div.topContent { + font-size: 9pt; + color: #efefef; + margin: 8px 0; + } +.topPost div.topContent p { + font-size: 1.05em; + line-height: 1.6em; + } +.topPost div.topContent a { + text-decoration: underline; + color: #fff; + } +.topPost div.topContent a:hover { + text-decoration: underline; + color: #ff9933; + } + +.topPost div.topContent ul, .topPost div.topContent ol { + padding: 0 0 0 30px; + } +.topPost div.topContent ul li, .topPost div.topContent ol li { + font-size: 1.05em; + color: #eee; + } +.topPost span.topComments, .topPost span.topMore, .topPost span.topTags { + font-size: 9pt; + font-style: italic; + background: url(images/bg-trans.png) repeat; + padding: 8px 10px; + margin-right: 5px; + line-height: 27pt; + color: #fff; + border: 1px solid #111; + } +.topPost span.linkpages { + font-size: 9pt; + font-style: italic; + color: #fff; + margin-bottom: 10px; display:block; + } +.topPost span.linkpages a:hover { + color: #ddd; + } +.topPost span.topComments:hover, .topPost span.topMore:hover, .topPost span.topTags:hover { + background: #040404; + } +.topPost span.topComments a { + color: #fff; + background: url(images/comments.gif) left no-repeat; + padding-left: 20px; + line-height: 27pt; + vertical-align: middle; + } +.topPost span.topMore a { + color: #fff; + background: url(images/more.gif) left no-repeat; + padding-left: 20px; + line-height: 27pt; + vertical-align: middle; + } +.topPost span.topTags em { + background: url(images/tags.gif) left no-repeat; + padding-left: 20px; + line-height: 27pt; + vertical-align: middle; + color: #0a0a0a; + } +.topPost span.topTags a { + color: #fff; + line-height: 27pt; + vertical-align: middle; + } +.topPost span.topComments a:hover, .topPost span.topMore a:hover, .topPost span.topTags a:hover { + color: #fff; + text-decoration: underline; + } + +#nextprevious { + margin: 10px 0 5px 0; + } +#nextprevious a { + color: #eee; + text-transform: lowercase; + border: 1px solid #111; + padding: 5px 8px; + font-size: 10pt; + line-height: 1.4em; + } +#nextprevious a:hover { + color: #fff; + } + + +/* Comments */ + +#comments { + margin-bottom: 10px; + margin-top: 15px; + background: #040404; + width: 530px; /* fixes bug in IE7 where putting focus on the text makes it disappear behind the background. yeah, weird. they call it a dimensional bug. */ + border: 1px solid #090909; + padding: 0px 15px 10px 15px; + color: #fff; + } +#comments h3 { + font-size: 10pt; + font-weight: normal; + color: #fff; + margin: 15px 0; + padding: 0; + } +#comments p a { + color: #6598b8; + } +#comments p a:hover { + color: #fff; + } +#comments p { + padding:0; + margin: 7px 0; + } +ul.commentlist, ul.trackback { + list-style-type: none; + margin: 10px 0; + font-size: 9pt; + } +.commentlist li, .trackback li { + margin: 10px 0 15px 0; + padding: 10px; + background: #080808; + border: 1px solid #040404; + list-style-type: none; + } +.commentlist li.alt { + background: #000; + border: 1px solid #020202; + border: none; + margin-top:10px; + } +.commentlist li.cleared { + background: transparent; + border: none; + } +.commentlist a:hover { + color: #eee; + } +.commentlist a.gravatar { + float: left; + margin: 0 15px 10px 0; + width: 60px; + } +.commentlist .commentbody { + margin: 0; + padding: 0; + } +.commentlist .commentbody p a { + text-decoration: underline; + } +.commentlist cite { + font-size: 10pt; + font-style: normal; + color: #ddd; + } +.commentlist small.commentmetadata a { + color: #666; + } +.commentlist p { + color: #dedede; + } +#respond { + clear: both; + margin-top: 15px; + } +textarea#comment { + width: 450px; + background: #fff; + color: #111; + padding: 10px; + } +#comments #submit { + margin: 0; + } +#extrastuff a {color:#999; font-size:0.8em;} +#extrastuff a:hover {color:#6598b8; text-decoration:none;} +#extrastuff #rssleft {float:left; width:250px;} +#extrastuff #trackright {float:right; width:180px; text-align:right;} + + +/* Welcome section */ + +#main #welcome { + margin: 0 0 20px 0; + padding: 0 0 5px 0; + background: url(images/bg-trans.png) repeat; + } +#main #welcome h2 { + font-size: 14pt; + font-weight: normal; + + color: #fff; + letter-spacing: -2px; + margin: 0 10px; + padding: 10px 0 3px 0; + } +#main #welcome p { + font-size: 9pt; + color: #eee; + margin: 0 10px; + padding: 6px 0; + } +#main #welcome a { + text-decoration: underline; + color: #ff9933; + } +#main #welcome a:hover { + color: #6598b8; + } +#main #welcome form { + margin: -5px 0 0 0; + background: transparent; /* required for IE */ + } +#main #welcome #feedbox { + border: 1px solid #ccc; + padding: 2px 1px; + width: 180px; + } +.submitbutton { + margin: -5px 0 0 5px; + background: #010101; + color: #fff; + font-size: 8pt; + padding: 3px 6px; + vertical-align: middle; + border: 1px solid #111; + } +.submitbutton:hover { + cursor: pointer; + background: #050505; + } + + +/* Sidebars */ +#sidebars { + margin: 15px 0 0 0; + float: right; + width: 340px; + color: #eee; + } +#sidebars .sidebarbox { + background: url(images/bg-trans.png) repeat; + padding: 4px 7px; + } + +#sidebar_full { + padding: 0; + margin: 0; + } + +#sidebar_left { + float: left; + width: 160px; + } + +#sidebar_right { + float: right; + width: 160px; + } + +#sidebars h2 { + margin: 0; + padding: 0 0 8px 0; + font-weight: normal; + font-size: 11pt; + color: #fff; + letter-spacing: -1px; + } + +#sidebars table { + width: 130px; + } + +#sidebars ul { + margin: 0; + padding: 0 0 5px 0; + } + +#sidebars li { + margin-bottom: 20px; + list-style: none; + font-size: 9pt; + } + +#sidebars li ul { + padding: 0; + } + +#sidebars ul ul li { + margin: 0; + padding: 2px 2px 2px 16px; + color: #eee; + background: url(images/bullet.gif) left 8px no-repeat; + line-height: 17px; /* Required for cross-browser consistency; cross-browser defaults vary. */ + } + +#sidebars a { + display: inline; + color: #eee; + } +#sidebars a:hover { + color: #6598b8; + text-decoration: underline; + } + +#sidebars p { + padding: 2px 0; + font-size: 9pt; + color: #eee; + } + +#tag_cloud a { + display: inline; + } + +#sidebars ul.children li {border-bottom:none;} +#sidebars ul.children {margin-bottom:0;} + + + +/* Before footer */ + +#morefoot { + background: #000 url(images/bggrad.jpg) top left repeat-x; + border: 1px solid #070707; + padding: 15px; + color: #dfdfdf; + margin: 0 0 10px 0; + } +#morefoot p { + margin: 5px 0; + padding: 5px 0; + } +#morefoot ul { + list-style-type: none; + margin-top: 5px; + } +#morefoot ul li, #morefoot p { + font-size: 9pt; + } +#morefoot a { + text-decoration: underline; + color: #ddd; + } +#morefoot a:hover { + text-decoration: underline; + color: #6598b8; + } +#morefoot h3 { + font-size: 12pt; + font-weight: normal; + color: #dfdfdf; + letter-spacing: -1px; + border-bottom: 1px dotted #444; + margin: 0; + padding: 0 0 2px 0; + } +#morefoot #searchbox { + padding: 1px; + width: 180px; + } +#morefoot .col1 { + float: left; + width: 340px; + } +#morefoot .col2 { + margin: 0 25px; + } +#morefoot .col2, #morefoot .col3 { + float: left; + width: 260px; + } +#morefoot li { + padding-left: 20px; + margin-bottom: 5px; + background: url(images/more.gif) left 2px no-repeat; + line-height: 17px; /* Required for cross-browser consistency; cross-browser defaults vary. */ + } + + +/* footer*/ +#footer { + clear: both; + background: #000; + border: 1px solid #070707; + padding: 15px 15px 10px 15px; + } +#footer p { + font-size: 9pt; + color: #bbb; + padding: 0; + } +#footer a { + color: #bbb; + text-decoration:none; + } +#footer a:hover { + color: #fff; + } +#footerleft { + float: left; + width: 600px; + } +#footerright { + float: right; + width: 300px; + text-align: right; + } + + + +/* Misc */ + +.highlight { + color: #222; + font-weight: bold; + } + +.cleared { + margin: 0; + padding: 0; + clear: both; + } + +.alignleft { + float: left; + margin: 10px 15px 5px 0; + } +.alignright { + float: right; + margin: 10px 0 5px 15px; + } +.aligncenter, +div.aligncenter { + margin: 10px auto; + text-align: center; + display: block; + } + +img.alignleft { + float: left; + margin: 10px 15px 5px 0; + } +img.alignright { + float: right; + margin: 10px 0 5px 15px; + } +img.aligncenter { + margin: 10px auto; + text-align: center; + display: block; + } + +.wp-caption { + border: 1px solid #ddd; + text-align: center; + background-color: #f3f3f3; + padding-top: 4px; + + /* optional rounded corners for browsers that support it */ + -moz-border-radius: 3px; + -khtml-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + } + +.wp-caption img { + margin: 0; + padding: 0; + border: none; + } + +.wp-caption p.wp-caption-text { + font-size: 11px; + line-height: 17px; + padding: 0 4px 5px; + margin: 0; + color: #111; + } + + + diff --git a/themes/pixel-en/templates/base.html b/themes/pixel-en/templates/base.html new file mode 100644 index 0000000..c6a2e5b --- /dev/null +++ b/themes/pixel-en/templates/base.html @@ -0,0 +1,75 @@ + + + +{%mf head%} + +{% block title %}{{ blog.title}}{% endblock %} + + + + + + + + + + + + +{% block head_block %}{% endblock %} + + + + + + +{%endmf%} + + +
    + + +
    +
    RSS feed
    + +
    +
    + + {%block content %}{%endblock%} + + +
    + + \ No newline at end of file diff --git a/themes/pixel-en/templates/category.html b/themes/pixel-en/templates/category.html new file mode 100644 index 0000000..4081bef --- /dev/null +++ b/themes/pixel-en/templates/category.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% block content%} + + +
    +
    +{% if entries %} + +{% for entry in entries %} +
    +

    {{entry.title}}

    +

    by {{entry.author}} on {{entry.date|datetz:"F jS, Y"}}, under {%for cate in entry.categories%} {{cate.name}} {%endfor%}

    +
    {{entry.content_excerpt|safe}}
    + Comments({{entry.commentcount}})» + {% if entry.strtags %}{%for tag in entry.tags%}{{tag}} {%endfor%}{%endif%} + more... +
    +

    +{% endfor %} + +{% else %} + +
    +

    Not Found

    +

    Sorry, but you are looking for something that isn't here. You can search again by using this form...

    +
    + +{% endif %} + +
    +
    {% if show_next %}« Older Entries{%endif%}
    +
    {% if show_prev %}« Newer Entries{%endif%}
    +
    +
    + +
    + +{% include "sidebar.html" %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/comments.html b/themes/pixel-en/templates/comments.html new file mode 100644 index 0000000..04ce15d --- /dev/null +++ b/themes/pixel-en/templates/comments.html @@ -0,0 +1,126 @@ + + +{% if entry.allow_comment %} + {% if entry.comments.count%} +

    {{entry.comments.count}} Responses to “{{entry.title}}”

    + +
      + + {% for comment in entry.comments %} +
    1. + + +
      + {%if comment.weburl %}{{comment.author}}{% else %}{{comment.author}}{%endif%} +
      + + +
      +

      {{comment.content|safe }}

      +
      + +
      +
    2. + {%endfor%} + +
    + {%endif%} +
    + +

    Leave a Reply

    + +
    + +

    +

    + +

    +

    + +

    +

    + +

    +

    + + {%ifequal blog.comment_check_type 1%} +

    + + +

    + {%endifequal%} + + {%ifequal blog.comment_check_type 2%} +
    +
    + +
    + +
    + {%endifequal%} + +

    + +

    +

    + + + + + +{% else %} + +

    Comments are closed.

    + +{% endif %} diff --git a/themes/pixel-en/templates/error-101.html b/themes/pixel-en/templates/error-101.html new file mode 100644 index 0000000..319f303 --- /dev/null +++ b/themes/pixel-en/templates/error-101.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} +{% block content %} + +
    +
    +

    Errror

    +

    + Please input user name, email address and the content of comment. +

    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/error-102.html b/themes/pixel-en/templates/error-102.html new file mode 100644 index 0000000..766141e --- /dev/null +++ b/themes/pixel-en/templates/error-102.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block content %} + +
    +
    +

    验证码错误

    +

    验证码无效

    +
    +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/error.html b/themes/pixel-en/templates/error.html new file mode 100644 index 0000000..19b2e9c --- /dev/null +++ b/themes/pixel-en/templates/error.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

    Error {{errorcode}}

    +{{ message }} +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/error404.html b/themes/pixel-en/templates/error404.html new file mode 100644 index 0000000..fcac8fb --- /dev/null +++ b/themes/pixel-en/templates/error404.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block content %} +
    +
    +

    404 Error

    + +
    + +{% include "sidebar.html" %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/index.html b/themes/pixel-en/templates/index.html new file mode 100644 index 0000000..f459549 --- /dev/null +++ b/themes/pixel-en/templates/index.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block content%} + + +
    +
    +{% if entries %} + +{% for entry in entries %} +
    +

    {{entry.title}}

    +

    by {{entry.author_name}} on {{entry.date|datetz:"F jS, Y"}}, under {%for cate in entry.categories%} {{cate.name}} {%endfor%}

    +
    {{entry.content_excerpt|safe}}
    + Comments({{entry.commentcount}})» + {% if entry.strtags %}{%for tag in entry.tags%}{{tag}} {%endfor%}{%endif%} + more... +
    +

    +{% endfor %} + +{% else %} + +
    +

    Not Found

    +

    Sorry, but you are looking for something that isn't here. You can search again by using this form...

    +
    + +{% endif %} + +
    +
    {% if show_next %}« Older Entries{%endif%}
    +
    + + +
    +
    {% if show_prev %}Newer Entries »{%endif%}
    +
    +
    + +
    + +{% include "sidebar.html" %} +
    +
    +{% endblock %} diff --git a/themes/pixel-en/templates/month.html b/themes/pixel-en/templates/month.html new file mode 100644 index 0000000..29c07bf --- /dev/null +++ b/themes/pixel-en/templates/month.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% block content%} + + +
    +
    +{% if entries %} + +{% for entry in entries %} +
    +

    {{entry.title}}

    +

    by {{entry.author}} on {{entry.date|datetz:"F jS, Y"}}, under {%for cate in entry.categories%} {{cate.name}} {%endfor%}

    +
    {{entry.content_excerpt|safe}}
    + Comments({{entry.commentcount}})» + {% if entry.strtags %}{%for tag in entry.tags%}{{tag}} {%endfor%}{%endif%} + more... +
    +

    +{% endfor %} + +{% else %} + +
    +

    Not Found

    +

    Sorry, but you are looking for something that isn't here. You can search again by using this form...

    +
    + +{% endif %} + +
    +
    {% if show_next %}« Older Entries{%endif%}
    +
    {% if show_prev %}« Newer Entries{%endif%}
    +
    +
    + +
    + +{% include "sidebar.html" %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/msg.html b/themes/pixel-en/templates/msg.html new file mode 100644 index 0000000..1c365be --- /dev/null +++ b/themes/pixel-en/templates/msg.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

    {{title}}

    +{{ message }} +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/page.html b/themes/pixel-en/templates/page.html new file mode 100644 index 0000000..0aa4db0 --- /dev/null +++ b/themes/pixel-en/templates/page.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% block title %} {{entry.title}} - {{blog.title}} {% endblock %} +{% block content%} + +
    + +
    +{% if entry %} + +
    +

    {{entry.title}}

    +
    + {%mf entry_content %} + {{ entry.content|safe }} + {%endmf%} +
    +
    +
    +
    {%if self.is_admin%}Edit{%endif%} + +
    +{% include "comments.html" %} +
    + +{%endif%} +
    +{% include "sidebar.html" %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/sidebar.html b/themes/pixel-en/templates/sidebar.html new file mode 100644 index 0000000..d833983 --- /dev/null +++ b/themes/pixel-en/templates/sidebar.html @@ -0,0 +1,109 @@ + \ No newline at end of file diff --git a/themes/pixel-en/templates/single.html b/themes/pixel-en/templates/single.html new file mode 100644 index 0000000..124415f --- /dev/null +++ b/themes/pixel-en/templates/single.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% block title %} {{entry.title}} - {{blog.title}} {% endblock %} +{% block content%} + +
    + +
    +{% if entry %} + +
    +

    {{entry.title}}

    +

    by {{entry.author}} on {{entry.date|datetz:"F jS, Y"}}, under {%for cate in entry.categories%} {{cate.name}} {%endfor%}

    +
    + {%mf entry_content %} + {{ entry.content|safe }} + {%endmf%} +
    +
    +
    + {% if entry.strtags %}{%for tag in entry.tags%}{{tag}} {%endfor%}{%endif%} +
    +
    +
    {%if self.is_admin%}Edit{%endif%} + +
    +{% include "comments.html" %} +
    + +{%endif%} +
    +{% include "sidebar.html" %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/themes/pixel-en/templates/tag.html b/themes/pixel-en/templates/tag.html new file mode 100644 index 0000000..4081bef --- /dev/null +++ b/themes/pixel-en/templates/tag.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% block content%} + + +
    +
    +{% if entries %} + +{% for entry in entries %} +
    +

    {{entry.title}}

    +

    by {{entry.author}} on {{entry.date|datetz:"F jS, Y"}}, under {%for cate in entry.categories%} {{cate.name}} {%endfor%}

    +
    {{entry.content_excerpt|safe}}
    + Comments({{entry.commentcount}})» + {% if entry.strtags %}{%for tag in entry.tags%}{{tag}} {%endfor%}{%endif%} + more... +
    +

    +{% endfor %} + +{% else %} + +
    +

    Not Found

    +

    Sorry, but you are looking for something that isn't here. You can search again by using this form...

    +
    + +{% endif %} + +
    +
    {% if show_next %}« Older Entries{%endif%}
    +
    {% if show_prev %}« Newer Entries{%endif%}
    +
    +
    + +
    + +{% include "sidebar.html" %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/update.bat b/update.bat index 2242b34..53ac3c0 100644 --- a/update.bat +++ b/update.bat @@ -1 +1 @@ -appcfg.py update ../micolog \ No newline at end of file +appcfg.py update . \ No newline at end of file diff --git a/version.txt b/version.txt index 2335a15..9d78958 100644 --- a/version.txt +++ b/version.txt @@ -1,4 +1,26 @@ -#history +#history + +2011/11/09 0.743 release (http://eric.cloud-mes.com) +Final version of Google App Engine python runtime 2.5. + +2011/10/05 0.742 release (http://eric.cloud-mes.com/) +Minor Page/CSS layout adjust for Chromium. + +2011/09/12 0.741 release (http://eric.cloud-mes.com/) +Upgrade to use Django 1.2 according to below blog: +http://blog.spbk.net/Micolog%E5%8D%87%E7%BA%A7%E9%BB%98%E8%AE%A40_96%E7%89%88%E6%9C%ACDjango%E8%87%B3Django1_2#reply +Upgrade the newest version of microlog in github and svn +https://github.com/xuming/micolog/commits/master +Fix IE6 can not comment bug +Fix sys_plugin can no load error +Minor change Pixel theme css +Add IE9 pin site feature in Pixel +Do some python code standard layout and import optimize +Allow suppress logo and other not needed copyright in admin page +Fix email notify in sys_plugin after upgrade to 1.2 +Add replay via comments +New paging facility + 2010/10/10 0.7 release @@ -29,68 +51,74 @@ Sitemap support Multi author support -#ʷ +#更新历史 -2010/10/10 0.7 release 汾 -ģ԰ -԰ -԰ -ӣбʾ״ʹʱûtoolsбֶ£ -ӣԶ֪ͨҪѡpingbackʹشƣGAEʱϳֲ©ӲѯԷȷ룻 -ͨ۵Ķѯ -ҳ½ҳӱǩбѡȡ -ͷʾʽѡ +2010/10/10 0.7 release 版本发布 +增加日语模板和日语语言包 +增加意大利语言包 +增加西班牙语言包 +增加,评论列表序号显示,首次使用时,用户需在tools列表手动更新; +添加,文章引用自动通知,需要在设置栏选择允许pingback,使用重传机制,减少因GAE的暂时故障出现差漏;增加查询对方引用情况的内容正确解码; +添加文章引用通告和评论的独立查询; +管理页面文章新建页增加标签列表鼠标选取; +增加头像显示方式选项 -Windows Live Writerͻˡͨ桱Ĭַͨ󣬺ͽͨ Bug -XML-RPCʱԶ壻 -޸վԴԴFEEDΪRSS 2.0汾 -޸slugΪĵĴ -޸ɾĿ¼޷Ŀ¼ -µһƪһƪΪʱ㣬Postid -һĬϵ -ȥxuming +修正,Windows Live Writer客户端“引用通告”默认字符导致误发引用通告请求,和接受通过解析 Bug +修正,XML-RPC发布文章时间自定义; +修改网站文章源和评论源(FEED)为RSS 2.0版本; +修复,保存文章slug不能为中文的错误 +修复,删除父目录后无法进行目录管理的问题 +文章的上一篇,下一篇改为按时间计算,而不是Postid +美化了一下默认的主题 +去除了主题xuming 0.7RC2 -޸Ĺ鵵˹鵵Ч -̨Plugin.register_setupmenųýӲ˵Ŀ -ѹƶsys_pluginʵ֣֧ۻظ -Ӷ༶ۺͶ༶Ŀ¼ -wp_importֻܵĿ¼͵ļС -tinymce༭IE6޷ -plugin.register_handlerlist ֧ʹBaseRequestHandlerչURL -޸»ʾδ´ -BasePublicPageӷsticky_entrysڻȡö -Entryʵmod_dateֶ,ڼ¼޸ʱ -޸sitemap +修改归档处理,提高了归档效率 +调整后台管理界面增加了Plugin.register_setupmenu函数用来向后台设置界面增加菜单项目 +将评论提醒功能移动到sys_plugin实现,并且支持评论回复提醒 +增加多级评论和多级目录 +修正wp_import插件只能导入目录和导入文件过小的问题 +修正tinymce编辑器在IE6下无法保存文章问题 +增加plugin.register_handlerlist 支持使用BaseRequestHandler来扩展处理URL +修复相关文章会显示未发布文章错误 +BasePublicPage增加方法sticky_entrys用于获取置顶文章 +Entry实体增加mod_date字段,用于记录最后修改时间 +修复sitemap更新问题 0.7RC1 -ļϴ -֧ -֧sitemap -ֶ֧ûд -highsyntax -api_rpc.pyӶsystem.listmethod,system.methodSignature,system.methodHelp֧ -޸ȫ© -api_rpc.pyģӶȫ֧metaweblogwpapi sticky -ʹxmlrpcʽɾʱɾ۵ -wordpressɶIJadmin.pyеķ -֤ģʽ3ṩɰ汾ļݣxmlprcģһϵк -鵵⣬ȥEntry.publish,ʹEntry.save(True) -ɾĿ¼ʹwlw޷ȡ -޸Ŀ¼ʱĽ -޸api_rpc.py,˶xmlrpc֧֣ʹwordpresslib.pyͨ -highsyntax -ͼƬ·޸Ϊ· - - - -޸slugΪĵĴ -޸ɾĿ¼޷Ŀ¼ -µһƪһƪΪʱ㣬Postid - -޸ ĵһĸдʱMicolog ͷʾ - -AvatarĬͷѡ \ No newline at end of file +文件上传 +插件支持 +支持sitemap +支持多用户写作 +增加highsyntax插件 +api_rpc.py增加对system.listmethod,system.methodSignature,system.methodHelp的支持 +修复导入插件安全漏洞 +api_rpc.py模块增加多个函数,全面支持metaweblog和wpapi 增加文章sticky属性 +修正了使用xmlrpc方式删除文章时,不删除相关评论的问题 +将wordpress导入插件做成独立的插件,不在依赖admin.py中的方法 +增加验证码模式3,提供与旧版本的兼容,xmlprc模块增加了一系列函数 +修正归档问题,去除函数Entry.publish,使用Entry.save(True)代替 +更正删除目录后使用wlw无法获取最新文章问题 +修改增加中文目录时的解析错误 +修改api_rpc.py,更新了对xmlrpc的支持,使用wordpresslib.py测试通过 +增加highsyntax插件 +将图片插入路径修改为绝对路径 + + + +修复,保存文章slug不能为中文的错误 +修复,删除父目录后无法进行目录管理的问题 +文章的上一篇,下一篇改为按时间计算,而不是Postid + +修复 邮箱的第一个字母大写时,Micolog 头像显示的问题 + +添加Avatar默认头像风格选择 + +0.741 + +添加Pingback对默认链接结构支持 +修复一篇文章添加太多tags回报错的问题 +修正导入文件中未允许评论会无法导入的异常 \ No newline at end of file diff --git a/views/admin/404.html b/views/admin/404.html index bd73939..6bfc744 100644 --- a/views/admin/404.html +++ b/views/admin/404.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base.html" %} {% block content %}

    404,page is been buliding...

    {% endblock %} \ No newline at end of file diff --git a/views/admin/author.html b/views/admin/author.html index d6542ef..06e8b4c 100644 --- a/views/admin/author.html +++ b/views/admin/author.html @@ -1,8 +1,8 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block h_message %} {%if postback%} -
    {{msg}}
    +
    {{msg|safe}}
    {%endif%} {% endblock %} {% block content %} diff --git a/views/admin/authors.html b/views/admin/authors.html index f316aad..30f05eb 100644 --- a/views/admin/authors.html +++ b/views/admin/authors.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block content %}
    diff --git a/views/admin/base.html b/views/admin/base.html index 1513f82..361bc94 100644 --- a/views/admin/base.html +++ b/views/admin/base.html @@ -6,7 +6,7 @@ - + @@ -28,11 +28,19 @@ {% endblock %} -{% block nav2_1 %} +{% block nav2 %} +
    + +
    +
    {% endblock %} {% block content %} diff --git a/views/admin/link.html b/views/admin/link.html index e7b70d9..d586be9 100644 --- a/views/admin/link.html +++ b/views/admin/link.html @@ -1,8 +1,8 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block h_message %} {%if postback%} -
    {{msg}}
    +
    {{msg|safe}}
    {%endif%} {% endblock %} {% block content %} diff --git a/views/admin/links.html b/views/admin/links.html index c5a3ab0..c850021 100644 --- a/views/admin/links.html +++ b/views/admin/links.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block content %}
    diff --git a/views/admin/pages.html b/views/admin/pages.html index 566e060..f519971 100644 --- a/views/admin/pages.html +++ b/views/admin/pages.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block content %}
    diff --git a/views/admin/plugin_action.html b/views/admin/plugin_action.html index 3dc62d0..94d7386 100644 --- a/views/admin/plugin_action.html +++ b/views/admin/plugin_action.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block header %} {% endblock %} @@ -10,7 +10,7 @@ {% block content %}
    -{{pcontent}} +{{pcontent|safe}}
    diff --git a/views/admin/plugins.html b/views/admin/plugins.html index 6d123c8..a58f60d 100644 --- a/views/admin/plugins.html +++ b/views/admin/plugins.html @@ -1,7 +1,7 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block content %} -
    +

    {% trans "Manage Plugins"%}

    Get more plugins @@ -12,7 +12,7 @@

    {% trans "Manage Plugins"%}

    - + - + @@ -137,7 +145,12 @@

    {%trans "Setup and Configuraiton for this Blog"%}

    + + + + +
    {{plugin.name}} {{plugin.version}}{{plugin.description}}{{plugin.description|safe}} {{plugin.author}} {%if plugin.active%} diff --git a/views/admin/posts.html b/views/admin/posts.html index e366573..5e42011 100644 --- a/views/admin/posts.html +++ b/views/admin/posts.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block content %}
    diff --git a/views/admin/setup.html b/views/admin/setup.html index 6c0e27b..75b60bf 100644 --- a/views/admin/setup.html +++ b/views/admin/setup.html @@ -1,11 +1,19 @@ +{% extends "base.html" %} {% load i18n %} -{% extends "setup_base.html" %} -{% block nav2_1 %} -
  • {%trans "Basic" %}
  • +{% block nav2 %} +
    + +
    +
    +{% endblock %} {% block content %}
    @@ -123,7 +131,7 @@

    {%trans "Setup and Configuraiton for this Blog"%}

    {% trans "Enable memcache:"%}
    {% trans "Allow Pingback:"%} -
    {% trans "Admin Essential:"%} +

    {% trans "Xmlrpc setting"%}

    @@ -160,7 +173,7 @@

    {% trans "Xmlrpc setting"%}

    - + diff --git a/views/admin/setup_base.html b/views/admin/setup_base.html index 9cc3d2e..2defa89 100755 --- a/views/admin/setup_base.html +++ b/views/admin/setup_base.html @@ -1,10 +1,10 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block header %} {% endblock %} {% block nav2 %} -

    -
    {% endblock %} {% block content%} -{{content}} +{{content|safe}} {% endblock %} diff --git a/views/admin/sitemap.html b/views/admin/sitemap.html index 05522e4..678ace2 100644 --- a/views/admin/sitemap.html +++ b/views/admin/sitemap.html @@ -1,12 +1,19 @@ -{% load i18n %} -{% extends "setup_base.html" %} -{% block nav2_1 %} +{% extends "base.html" %} +{% load i18n %} +{% block nav2 %} +
    + +
    +
    {% endblock %} - {% block content %} diff --git a/views/admin/status.html b/views/admin/status.html index e48c065..a89fdcc 100644 --- a/views/admin/status.html +++ b/views/admin/status.html @@ -1,5 +1,5 @@ -{% load i18n %} {% extends "base.html" %} +{% load i18n %} {% block header %} +