Commit 3edd2867 by benjaoming

Adding possibility for Plugins to add media to rendering pages.

Adding advanced markdown extension for images.
Adding colorbox for images plugin to enlarge photos.
parent df4fd062
...@@ -22,9 +22,7 @@ def send_file(request, filepath, last_modified=None, filename=None): ...@@ -22,9 +22,7 @@ def send_file(request, filepath, last_modified=None, filename=None):
response["Last-Modified"] = http_date(statobj.st_mtime) response["Last-Modified"] = http_date(statobj.st_mtime)
else: else:
if isinstance(last_modified, datetime): if isinstance(last_modified, datetime):
print last_modified
last_modified = float(dateformat.format(last_modified, 'U')) last_modified = float(dateformat.format(last_modified, 'U'))
print last_modified
response["Last-Modified"] = http_date(epoch_seconds=last_modified) response["Last-Modified"] = http_date(epoch_seconds=last_modified)
response["Content-Length"] = statobj.st_size response["Content-Length"] = statobj.st_size
......
...@@ -31,7 +31,9 @@ class BasePlugin(object): ...@@ -31,7 +31,9 @@ class BasePlugin(object):
markdown_extensions = [] markdown_extensions = []
pass class RenderMedia:
js = []
css = {}
class PluginSidebarFormMixin(object): class PluginSidebarFormMixin(object):
......
...@@ -69,7 +69,7 @@ class EditForm(forms.Form): ...@@ -69,7 +69,7 @@ class EditForm(forms.Form):
def clean(self): def clean(self):
cd = self.cleaned_data cd = self.cleaned_data
if self.no_clean: if self.no_clean or self.preview:
return cd return cd
if not str(self.initial_revision.id) == str(self.presumed_revision): if not str(self.initial_revision.id) == str(self.presumed_revision):
raise forms.ValidationError(_(u'While you were editing, someone else changed the revision. Your contents have been automatically merged with the new contents. Please review the text below.')) raise forms.ValidationError(_(u'While you were editing, someone else changed the revision. Your contents have been automatically merged with the new contents. Please review the text below.'))
...@@ -245,7 +245,6 @@ class PermissionsForm(PluginSettingsFormMixin, forms.ModelForm): ...@@ -245,7 +245,6 @@ class PermissionsForm(PluginSettingsFormMixin, forms.ModelForm):
self.user = user self.user = user
kwargs['instance'] = article kwargs['instance'] = article
super(PermissionsForm, self).__init__(*args, **kwargs) super(PermissionsForm, self).__init__(*args, **kwargs)
print self.data
if user.has_perm("wiki.admin"): if user.has_perm("wiki.admin"):
self.fields['group'].queryset = models.Group.objects.all() self.fields['group'].queryset = models.Group.objects.all()
else: else:
......
...@@ -25,7 +25,7 @@ class AttachmentPreprocessor(markdown.preprocessors.Preprocessor): ...@@ -25,7 +25,7 @@ class AttachmentPreprocessor(markdown.preprocessors.Preprocessor):
if m: if m:
attachment_id = m.group('id').strip() attachment_id = m.group('id').strip()
try: try:
attachment = models.Attachment.objects.get(article=self.markdown.article, attachment = models.Attachment.objects.get(articles=self.markdown.article,
id=attachment_id) id=attachment_id)
url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id, url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id,
'attachment_id':attachment.id,}) 'attachment_id':attachment.id,})
......
import markdown
import re
from django.template.loader import render_to_string
from django.template import Context
from wiki.core import article_markdown
IMAGE_RE = re.compile(r'.*(\[image\:(?P<id>\d+)\s+align\:(?P<align>right|left|center)\s*\]).*',
re.IGNORECASE)
from wiki.plugins.images import models
class ImageExtension(markdown.Extension):
""" Images plugin markdown extension for django-wiki. """
def extendMarkdown(self, md, md_globals):
""" Insert ImagePreprocessor before ReferencePreprocessor. """
md.preprocessors.add('dw-images', ImagePreprocessor(md), '>html_block')
class ImagePreprocessor(markdown.preprocessors.Preprocessor):
"""django-wiki image preprocessor - parse text for [image:id align:left|right|center] references. """
def run(self, lines):
new_text = []
previous_line_was_image = False
image = None
image_id = None
alignment = None
caption = ""
for line in lines:
m = IMAGE_RE.match(line)
if m:
previous_line_was_image = True
image_id = m.group('id').strip()
alignment = m.group('align').strip()
try:
image = models.Image.objects.get(article=self.markdown.article,
id=image_id)
except models.Image.DoesNotExist:
pass
line = line.replace(m.group(1), "")
elif previous_line_was_image:
print line
if line.startswith(" "):
caption += line[4:]
line = ""
else:
html = render_to_string("wiki/plugins/images/render.html",
Context({'image': image,
'caption': article_markdown(caption, self.markdown.article,
extensions=self.markdown.registeredExtensions),
'align': alignment}))
line = html + line
previous_line_was_image = False
new_text.append(line)
return new_text
// ColorBox v1.3.20 - jQuery lightbox plugin
// (c) 2012 Jack Moore - jacklmoore.com
// License: http://www.opensource.org/licenses/mit-license.php
(function(e,t,n){function G(n,r,i){var o=t.createElement(n);return r&&(o.id=s+r),i&&(o.style.cssText=i),e(o)}function Y(e){var t=T.length,n=(U+e)%t;return n<0?t+n:n}function Z(e,t){return Math.round((/%/.test(e)?(t==="x"?N.width():N.height())/100:1)*parseInt(e,10))}function et(e){return B.photo||/\.(gif|png|jp(e|g|eg)|bmp|ico)((#|\?).*)?$/i.test(e)}function tt(){var t,n=e.data(R,i);n==null?(B=e.extend({},r),console&&console.log&&console.log("Error: cboxElement missing settings object")):B=e.extend({},n);for(t in B)e.isFunction(B[t])&&t.slice(0,2)!=="on"&&(B[t]=B[t].call(R));B.rel=B.rel||R.rel||"nofollow",B.href=B.href||e(R).attr("href"),B.title=B.title||R.title,typeof B.href=="string"&&(B.href=e.trim(B.href))}function nt(t,n){e.event.trigger(t),n&&n.call(R)}function rt(){var e,t=s+"Slideshow_",n="click."+s,r,i,o;B.slideshow&&T[1]?(r=function(){M.text(B.slideshowStop).unbind(n).bind(f,function(){if(B.loop||T[U+1])e=setTimeout(J.next,B.slideshowSpeed)}).bind(a,function(){clearTimeout(e)}).one(n+" "+l,i),g.removeClass(t+"off").addClass(t+"on"),e=setTimeout(J.next,B.slideshowSpeed)},i=function(){clearTimeout(e),M.text(B.slideshowStart).unbind([f,a,l,n].join(" ")).one(n,function(){J.next(),r()}),g.removeClass(t+"on").addClass(t+"off")},B.slideshowAuto?r():i()):g.removeClass(t+"off "+t+"on")}function it(t){V||(R=t,tt(),T=e(R),U=0,B.rel!=="nofollow"&&(T=e("."+o).filter(function(){var t=e.data(this,i),n;return t&&(n=t.rel||this.rel),n===B.rel}),U=T.index(R),U===-1&&(T=T.add(R),U=T.length-1)),W||(W=X=!0,g.show(),B.returnFocus&&e(R).blur().one(c,function(){e(this).focus()}),m.css({opacity:+B.opacity,cursor:B.overlayClose?"pointer":"auto"}).show(),B.w=Z(B.initialWidth,"x"),B.h=Z(B.initialHeight,"y"),J.position(),d&&N.bind("resize."+v+" scroll."+v,function(){m.css({width:N.width(),height:N.height(),top:N.scrollTop(),left:N.scrollLeft()})}).trigger("resize."+v),nt(u,B.onOpen),H.add(A).hide(),P.html(B.close).show()),J.load(!0))}function st(){!g&&t.body&&(Q=!1,N=e(n),g=G(K).attr({id:i,"class":p?s+(d?"IE6":"IE"):""}).hide(),m=G(K,"Overlay",d?"position:absolute":"").hide(),y=G(K,"Wrapper"),b=G(K,"Content").append(C=G(K,"LoadedContent","width:0; height:0; overflow:hidden"),L=G(K,"LoadingOverlay").add(G(K,"LoadingGraphic")),A=G(K,"Title"),O=G(K,"Current"),_=G(K,"Next"),D=G(K,"Previous"),M=G(K,"Slideshow").bind(u,rt),P=G(K,"Close")),y.append(G(K).append(G(K,"TopLeft"),w=G(K,"TopCenter"),G(K,"TopRight")),G(K,!1,"clear:left").append(E=G(K,"MiddleLeft"),b,S=G(K,"MiddleRight")),G(K,!1,"clear:left").append(G(K,"BottomLeft"),x=G(K,"BottomCenter"),G(K,"BottomRight"))).find("div div").css({"float":"left"}),k=G(K,!1,"position:absolute; width:9999px; visibility:hidden; display:none"),H=_.add(D).add(O).add(M),e(t.body).append(m,g.append(y,k)))}function ot(){return g?(Q||(Q=!0,j=w.height()+x.height()+b.outerHeight(!0)-b.height(),F=E.width()+S.width()+b.outerWidth(!0)-b.width(),I=C.outerHeight(!0),q=C.outerWidth(!0),g.css({"padding-bottom":j,"padding-right":F}),_.click(function(){J.next()}),D.click(function(){J.prev()}),P.click(function(){J.close()}),m.click(function(){B.overlayClose&&J.close()}),e(t).bind("keydown."+s,function(e){var t=e.keyCode;W&&B.escKey&&t===27&&(e.preventDefault(),J.close()),W&&B.arrowKey&&T[1]&&(t===37?(e.preventDefault(),D.click()):t===39&&(e.preventDefault(),_.click()))}),e("."+o,t).live("click",function(e){e.which>1||e.shiftKey||e.altKey||e.metaKey||(e.preventDefault(),it(this))})),!0):!1}var r={transition:"elastic",speed:300,width:!1,initialWidth:"600",innerWidth:!1,maxWidth:!1,height:!1,initialHeight:"450",innerHeight:!1,maxHeight:!1,scalePhotos:!0,scrolling:!0,inline:!1,html:!1,iframe:!1,fastIframe:!0,photo:!1,href:!1,title:!1,rel:!1,opacity:.9,preloading:!0,current:"image {current} of {total}",previous:"previous",next:"next",close:"close",xhrError:"This content failed to load.",imgError:"This image failed to load.",open:!1,returnFocus:!0,reposition:!0,loop:!0,slideshow:!1,slideshowAuto:!0,slideshowSpeed:2500,slideshowStart:"start slideshow",slideshowStop:"stop slideshow",onOpen:!1,onLoad:!1,onComplete:!1,onCleanup:!1,onClosed:!1,overlayClose:!0,escKey:!0,arrowKey:!0,top:!1,bottom:!1,left:!1,right:!1,fixed:!1,data:undefined},i="colorbox",s="cbox",o=s+"Element",u=s+"_open",a=s+"_load",f=s+"_complete",l=s+"_cleanup",c=s+"_closed",h=s+"_purge",p=!e.support.opacity&&!e.support.style,d=p&&!n.XMLHttpRequest,v=s+"_IE6",m,g,y,b,w,E,S,x,T,N,C,k,L,A,O,M,_,D,P,H,B,j,F,I,q,R,U,z,W,X,V,$,J,K="div",Q;if(e.colorbox)return;e(st),J=e.fn[i]=e[i]=function(t,n){var s=this;t=t||{},st();if(ot()){if(!s[0]){if(s.selector)return s;s=e("<a/>"),t.open=!0}n&&(t.onComplete=n),s.each(function(){e.data(this,i,e.extend({},e.data(this,i)||r,t))}).addClass(o),(e.isFunction(t.open)&&t.open.call(s)||t.open)&&it(s[0])}return s},J.position=function(e,t){function f(e){w[0].style.width=x[0].style.width=b[0].style.width=e.style.width,b[0].style.height=E[0].style.height=S[0].style.height=e.style.height}var n,r=0,i=0,o=g.offset(),u,a;N.unbind("resize."+s),g.css({top:-9e4,left:-9e4}),u=N.scrollTop(),a=N.scrollLeft(),B.fixed&&!d?(o.top-=u,o.left-=a,g.css({position:"fixed"})):(r=u,i=a,g.css({position:"absolute"})),B.right!==!1?i+=Math.max(N.width()-B.w-q-F-Z(B.right,"x"),0):B.left!==!1?i+=Z(B.left,"x"):i+=Math.round(Math.max(N.width()-B.w-q-F,0)/2),B.bottom!==!1?r+=Math.max(N.height()-B.h-I-j-Z(B.bottom,"y"),0):B.top!==!1?r+=Z(B.top,"y"):r+=Math.round(Math.max(N.height()-B.h-I-j,0)/2),g.css({top:o.top,left:o.left}),e=g.width()===B.w+q&&g.height()===B.h+I?0:e||0,y[0].style.width=y[0].style.height="9999px",n={width:B.w+q,height:B.h+I,top:r,left:i},e===0&&g.css(n),g.dequeue().animate(n,{duration:e,complete:function(){f(this),X=!1,y[0].style.width=B.w+q+F+"px",y[0].style.height=B.h+I+j+"px",B.reposition&&setTimeout(function(){N.bind("resize."+s,J.position)},1),t&&t()},step:function(){f(this)}})},J.resize=function(e){W&&(e=e||{},e.width&&(B.w=Z(e.width,"x")-q-F),e.innerWidth&&(B.w=Z(e.innerWidth,"x")),C.css({width:B.w}),e.height&&(B.h=Z(e.height,"y")-I-j),e.innerHeight&&(B.h=Z(e.innerHeight,"y")),!e.innerHeight&&!e.height&&(C.css({height:"auto"}),B.h=C.height()),C.css({height:B.h}),J.position(B.transition==="none"?0:B.speed))},J.prep=function(t){function o(){return B.w=B.w||C.width(),B.w=B.mw&&B.mw<B.w?B.mw:B.w,B.w}function u(){return B.h=B.h||C.height(),B.h=B.mh&&B.mh<B.h?B.mh:B.h,B.h}if(!W)return;var n,r=B.transition==="none"?0:B.speed;C.remove(),C=G(K,"LoadedContent").append(t),C.hide().appendTo(k.show()).css({width:o(),overflow:B.scrolling?"auto":"hidden"}).css({height:u()}).prependTo(b),k.hide(),e(z).css({"float":"none"}),d&&e("select").not(g.find("select")).filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one(l,function(){this.style.visibility="inherit"}),n=function(){function y(){p&&g[0].style.removeAttribute("filter")}var t,n,o=T.length,u,a="frameBorder",l="allowTransparency",c,d,v,m;if(!W)return;c=function(){clearTimeout($),L.hide(),nt(f,B.onComplete)},p&&z&&C.fadeIn(100),A.html(B.title).add(C).show();if(o>1){typeof B.current=="string"&&O.html(B.current.replace("{current}",U+1).replace("{total}",o)).show(),_[B.loop||U<o-1?"show":"hide"]().html(B.next),D[B.loop||U?"show":"hide"]().html(B.previous),B.slideshow&&M.show();if(B.preloading){t=[Y(-1),Y(1)];while(n=T[t.pop()])m=e.data(n,i),m&&m.href?(d=m.href,e.isFunction(d)&&(d=d.call(n))):d=n.href,et(d)&&(v=new Image,v.src=d)}}else H.hide();B.iframe?(u=G("iframe")[0],a in u&&(u[a]=0),l in u&&(u[l]="true"),u.name=s+ +(new Date),B.fastIframe?c():e(u).one("load",c),u.src=B.href,B.scrolling||(u.scrolling="no"),e(u).addClass(s+"Iframe").appendTo(C).one(h,function(){u.src="//about:blank"})):c(),B.transition==="fade"?g.fadeTo(r,1,y):y()},B.transition==="fade"?g.fadeTo(r,0,function(){J.position(0,n)}):J.position(r,n)},J.load=function(t){var n,r,i=J.prep;X=!0,z=!1,R=T[U],t||tt(),nt(h),nt(a,B.onLoad),B.h=B.height?Z(B.height,"y")-I-j:B.innerHeight&&Z(B.innerHeight,"y"),B.w=B.width?Z(B.width,"x")-q-F:B.innerWidth&&Z(B.innerWidth,"x"),B.mw=B.w,B.mh=B.h,B.maxWidth&&(B.mw=Z(B.maxWidth,"x")-q-F,B.mw=B.w&&B.w<B.mw?B.w:B.mw),B.maxHeight&&(B.mh=Z(B.maxHeight,"y")-I-j,B.mh=B.h&&B.h<B.mh?B.h:B.mh),n=B.href,$=setTimeout(function(){L.show()},100),B.inline?(G(K).hide().insertBefore(e(n)[0]).one(h,function(){e(this).replaceWith(C.children())}),i(e(n))):B.iframe?i(" "):B.html?i(B.html):et(n)?(e(z=new Image).addClass(s+"Photo").error(function(){B.title=!1,i(G(K,"Error").html(B.imgError))}).load(function(){var e;z.onload=null,B.scalePhotos&&(r=function(){z.height-=z.height*e,z.width-=z.width*e},B.mw&&z.width>B.mw&&(e=(z.width-B.mw)/z.width,r()),B.mh&&z.height>B.mh&&(e=(z.height-B.mh)/z.height,r())),B.h&&(z.style.marginTop=Math.max(B.h-z.height,0)/2+"px"),T[1]&&(B.loop||T[U+1])&&(z.style.cursor="pointer",z.onclick=function(){J.next()}),p&&(z.style.msInterpolationMode="bicubic"),setTimeout(function(){i(z)},1)}),setTimeout(function(){z.src=n},1)):n&&k.load(n,B.data,function(t,n,r){i(n==="error"?G(K,"Error").html(B.xhrError):e(this).contents())})},J.next=function(){!X&&T[1]&&(B.loop||T[U+1])&&(U=Y(1),J.load())},J.prev=function(){!X&&T[1]&&(B.loop||U)&&(U=Y(-1),J.load())},J.close=function(){W&&!V&&(V=!0,W=!1,nt(l,B.onCleanup),N.unbind("."+s+" ."+v),m.fadeTo(200,0),g.stop().fadeTo(300,0,function(){g.add(m).css({opacity:1,cursor:"auto"}).hide(),nt(h),C.remove(),setTimeout(function(){V=!1,nt(c,B.onClosed)},1)}))},J.remove=function(){e([]).add(g).add(m).remove(),g=null,e("."+o).removeData(i).removeClass(o).die()},J.element=function(){return e(R)},J.settings=r})(jQuery,document,this);
\ No newline at end of file
<div id='homer' style="background:url(../content/homer.jpg) right center no-repeat #ececec; height:135px; width:280px; padding:30px 10px;">
<strong>Homer</strong><br/>
<em>\noun\</em><br/>
<strong>1.</strong> American bonehead<br/>
<strong>2. Pull a Homer-</strong><br/>
to succeed despite<br/>
idiocy
</div>
<script>
$('#homer strong').css({color:'red'});
</script>
\ No newline at end of file
<div style="width:504px; height:412px; overflow:hidden;">
<object>
<param name="allowfullscreen" value="true" /><param name="wmode" value="opaque" />
<param name="allowscriptaccess" value="always" />
<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2285902&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" />
<embed src="http://vimeo.com/moogaloop.swf?clip_id=2285902&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" wmode="opaque" allowscriptaccess="always" width="504" height="412"></embed>
</object>
</div>
\ No newline at end of file
/*
ColorBox Core Style:
The following CSS is consistent between example themes and should not be altered.
*/
#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;}
#cboxOverlay{position:fixed; width:100%; height:100%;}
#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
#cboxContent{position:relative;}
#cboxLoadedContent{overflow:auto;}
#cboxTitle{margin:0;}
#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%; height:100%;}
#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
.cboxPhoto{float:left; margin:auto; border:0; display:block; max-width:none;}
.cboxIframe{width:100%; height:100%; display:block; border:0;}
#colorbox, #cboxContent, #cboxLoadedContent{box-sizing:content-box;}
/*
User Style:
Change the following styles to modify the appearance of ColorBox. They are
ordered & tabbed in a way that represents the nesting of the generated HTML.
*/
#cboxOverlay{background:url(images/overlay.png) repeat 0 0;}
#colorbox{}
#cboxTopLeft{width:21px; height:21px; background:url(images/controls.png) no-repeat -101px 0;}
#cboxTopRight{width:21px; height:21px; background:url(images/controls.png) no-repeat -130px 0;}
#cboxBottomLeft{width:21px; height:21px; background:url(images/controls.png) no-repeat -101px -29px;}
#cboxBottomRight{width:21px; height:21px; background:url(images/controls.png) no-repeat -130px -29px;}
#cboxMiddleLeft{width:21px; background:url(images/controls.png) left top repeat-y;}
#cboxMiddleRight{width:21px; background:url(images/controls.png) right top repeat-y;}
#cboxTopCenter{height:21px; background:url(images/border.png) 0 0 repeat-x;}
#cboxBottomCenter{height:21px; background:url(images/border.png) 0 -29px repeat-x;}
#cboxContent{background:#fff; overflow:hidden;}
.cboxIframe{background:#fff;}
#cboxError{padding:50px; border:1px solid #ccc;}
#cboxLoadedContent{margin-bottom:28px;}
#cboxTitle{position:absolute; bottom:4px; left:0; text-align:center; width:100%; color:#949494;}
#cboxCurrent{position:absolute; bottom:4px; left:58px; color:#949494;}
#cboxSlideshow{position:absolute; bottom:4px; right:30px; color:#0092ef;}
#cboxPrevious{position:absolute; bottom:0; left:0; background:url(images/controls.png) no-repeat -75px 0; width:25px; height:25px; text-indent:-9999px;}
#cboxPrevious:hover{background-position:-75px -25px;}
#cboxNext{position:absolute; bottom:0; left:27px; background:url(images/controls.png) no-repeat -50px 0; width:25px; height:25px; text-indent:-9999px;}
#cboxNext:hover{background-position:-50px -25px;}
#cboxLoadingOverlay{background:url(images/loading_background.png) no-repeat center center;}
#cboxLoadingGraphic{background:url(images/loading.gif) no-repeat center center;}
#cboxClose{position:absolute; bottom:0; right:0; background:url(images/controls.png) no-repeat -25px 0; width:25px; height:25px; text-indent:-9999px;}
#cboxClose:hover{background-position:-25px -25px;}
/*
The following fixes a problem where IE7 and IE8 replace a PNG's alpha transparency with a black fill
when an alpha filter (opacity change) is set on the element or ancestor element. This style is not applied to or needed in IE9.
See: http://jacklmoore.com/notes/ie-transparency-problems/
*/
.cboxIE #cboxTopLeft,
.cboxIE #cboxTopCenter,
.cboxIE #cboxTopRight,
.cboxIE #cboxBottomLeft,
.cboxIE #cboxBottomCenter,
.cboxIE #cboxBottomRight,
.cboxIE #cboxMiddleLeft,
.cboxIE #cboxMiddleRight {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF);
}
/*
The following provides PNG transparency support for IE6
Feel free to remove this and the /ie6/ directory if you have dropped IE6 support.
*/
.cboxIE6 #cboxTopLeft{background:url(images/ie6/borderTopLeft.png);}
.cboxIE6 #cboxTopCenter{background:url(images/ie6/borderTopCenter.png);}
.cboxIE6 #cboxTopRight{background:url(images/ie6/borderTopRight.png);}
.cboxIE6 #cboxBottomLeft{background:url(images/ie6/borderBottomLeft.png);}
.cboxIE6 #cboxBottomCenter{background:url(images/ie6/borderBottomCenter.png);}
.cboxIE6 #cboxBottomRight{background:url(images/ie6/borderBottomRight.png);}
.cboxIE6 #cboxMiddleLeft{background:url(images/ie6/borderMiddleLeft.png);}
.cboxIE6 #cboxMiddleRight{background:url(images/ie6/borderMiddleRight.png);}
.cboxIE6 #cboxTopLeft,
.cboxIE6 #cboxTopCenter,
.cboxIE6 #cboxTopRight,
.cboxIE6 #cboxBottomLeft,
.cboxIE6 #cboxBottomCenter,
.cboxIE6 #cboxBottomRight,
.cboxIE6 #cboxMiddleLeft,
.cboxIE6 #cboxMiddleRight {
_behavior: expression(this.src = this.src ? this.src : this.currentStyle.backgroundImage.split('"')[1], this.style.background = "none", this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + this.src + ", sizingMethod='scale')");
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'/>
<title>ColorBox Examples</title>
<style>
body{font:12px/1.2 Verdana, sans-serif; padding:0 10px;}
a:link, a:visited{text-decoration:none; color:#416CE5; border-bottom:1px solid #416CE5;}
h2{font-size:13px; margin:15px 0 0 0;}
</style>
<link rel="stylesheet" href="colorbox.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="../colorbox/jquery.colorbox.js"></script>
<script>
$(document).ready(function(){
//Examples of how to assign the ColorBox event to elements
$(".group1").colorbox({rel:'group1'});
$(".group2").colorbox({rel:'group2', transition:"fade"});
$(".group3").colorbox({rel:'group3', transition:"none", width:"75%", height:"75%"});
$(".group4").colorbox({rel:'group4', slideshow:true});
$(".ajax").colorbox();
$(".youtube").colorbox({iframe:true, innerWidth:425, innerHeight:344});
$(".iframe").colorbox({iframe:true, width:"80%", height:"80%"});
$(".inline").colorbox({inline:true, width:"50%"});
$(".callbacks").colorbox({
onOpen:function(){ alert('onOpen: colorbox is about to open'); },
onLoad:function(){ alert('onLoad: colorbox has started to load the targeted content'); },
onComplete:function(){ alert('onComplete: colorbox has displayed the loaded content'); },
onCleanup:function(){ alert('onCleanup: colorbox has begun the close process'); },
onClosed:function(){ alert('onClosed: colorbox has completely closed'); }
});
//Example of preserving a JavaScript event for inline calls.
$("#click").click(function(){
$('#click').css({"background-color":"#f00", "color":"#fff", "cursor":"inherit"}).text("Open this window again and this message will still be here.");
return false;
});
});
</script>
</head>
<body>
<h1>ColorBox Demonstration</h1>
<h2>Elastic Transition</h2>
<p><a class="group1" href="../content/ohoopee1.jpg" title="Me and my grandfather on the Ohoopee.">Grouped Photo 1</a></p>
<p><a class="group1" href="../content/ohoopee2.jpg" title="On the Ohoopee as a child">Grouped Photo 2</a></p>
<p><a class="group1" href="../content/ohoopee3.jpg" title="On the Ohoopee as an adult">Grouped Photo 3</a></p>
<h2>Fade Transition</h2>
<p><a class="group2" href="../content/ohoopee1.jpg" title="Me and my grandfather on the Ohoopee">Grouped Photo 1</a></p>
<p><a class="group2" href="../content/ohoopee2.jpg" title="On the Ohoopee as a child">Grouped Photo 2</a></p>
<p><a class="group2" href="../content/ohoopee3.jpg" title="On the Ohoopee as an adult">Grouped Photo 3</a></p>
<h2>No Transition + fixed width and height (75% of screen size)</h2>
<p><a class="group3" href="../content/ohoopee1.jpg" title="Me and my grandfather on the Ohoopee.">Grouped Photo 1</a></p>
<p><a class="group3" href="../content/ohoopee2.jpg" title="On the Ohoopee as a child">Grouped Photo 2</a></p>
<p><a class="group3" href="../content/ohoopee3.jpg" title="On the Ohoopee as an adult">Grouped Photo 3</a></p>
<h2>Slideshow</h2>
<p><a class="group4" href="../content/ohoopee1.jpg" title="Me and my grandfather on the Ohoopee.">Grouped Photo 1</a></p>
<p><a class="group4" href="../content/ohoopee2.jpg" title="On the Ohoopee as a child">Grouped Photo 2</a></p>
<p><a class="group4" href="../content/ohoopee3.jpg" title="On the Ohoopee as an adult">Grouped Photo 3</a></p>
<h2>Other Content Types</h2>
<p><a class='ajax' href="../content/ajax.html" title="Homer Defined">Outside HTML (Ajax)</a></p>
<p><a class='ajax' href="../content/flash.html" title="Royksopp: Remind Me">Flash / Video (Ajax/Embedded)</a></p>
<p><a class='youtube' href="http://www.youtube.com/embed/617ANIA5Rqs?rel=0&amp;wmode=transparent" title="The Knife: We Share Our Mother's Health">Flash / Video (Iframe/Direct Link To YouTube)</a></p>
<p><a class='iframe' href="http://threadless.com">Outside Webpage (Iframe)</a></p>
<p><a class='inline' href="#inline_content">Inline HTML</a></p>
<h2>Demonstration of using callbacks</h2>
<p><a class='callbacks' href="../content/marylou.jpg" title="Marylou on Cumberland Island">Example with alerts</a>. Callbacks and event-hooks allow users to extend functionality without having to rewrite parts of the plugin.</p>
<!-- This contains the hidden content for inline calls -->
<div style='display:none'>
<div id='inline_content' style='padding:10px; background:#fff;'>
<p><strong>This content comes from a hidden element on this page.</strong></p>
<p>The inline option preserves bound JavaScript events and changes, and it puts the content back where it came from when it is closed.</p>
<p><a id="click" href="#" style='padding:5px; background:#ccc;'>Click me, it will be preserved!</a></p>
<p><strong>If you try to open a new ColorBox while it is already open, it will update itself with the new content.</strong></p>
<p>Updating Content Example:<br />
<a class="ajax" href="../content/flash.html">Click here to load new content</a></p>
</div>
</div>
</body>
</html>
\ No newline at end of file
$(document).ready(function() {
$('.wiki-article .thumbnail a').colorbox();
});
{% load thumbnail %}
{% comment %}
This template is used for the markdown extension that renders images and captions
{% endcomment %}
{% with image.current_revision.imagerevision as revision %}
<div class="pull-{{ align }}" style="width: 250px; margin: 10px;">
<div class="thumbnail">
{% thumbnail revision.image "250x250" as thumb %}
<a href="{{ revision.image.url }}">
<img src="{{ thumb.url }}" alt="{{ revision.get_filename }}" />
</a>
<div class="caption">
{{ caption|safe }}
</div>
{% endthumbnail %}
</div>
</div>
{% endwith %}
{% load i18n wiki_tags wiki_images_tags humanize thumbnail %} {% load i18n wiki_tags wiki_images_tags humanize thumbnail %}
{% load url from future %} {% load url from future %}
<script type="text/javascript">
function insert_image(image_id) {
align=prompt('Enter an alignment. \'left\', \'right\' or \'center\':', 'right');
caption=prompt('Enter a caption text. You may use markdown. You can edit the text after...');
$('#id_content').insertAtCaret('\n[image:'+image_id+' align:'+align+']\n '+caption+'\n\n');
}
</script>
{% with article|images_for_article as images %} {% with article|images_for_article as images %}
<style type="text/css"> <style type="text/css">
#image-list tr:first-child td {border:0;} #image-list tr:first-child td {border:0;}
...@@ -12,9 +20,9 @@ ...@@ -12,9 +20,9 @@
{% thumbnail revision.image "50x50" crop="center" as thumb %} {% thumbnail revision.image "50x50" crop="center" as thumb %}
<tr> <tr>
<td style="white-space: nowrap;"> <td style="white-space: nowrap;">
<p>{{ revision.get_filename|truncatechars:30 }}</p> <p>{% trans "Image id" %}: {{ image.id }}</code></p>
<p> <p>
<a href=""><span class="icon-edit"></span> {% trans "Insert" %}</a><br /> <a href="javascript:void(insert_image({{ image.id }}))"><span class="icon-edit"></span> {% trans "Insert" %}</a><br />
{% if image|can_write:user %} {% if image|can_write:user %}
<a href="{% url 'wiki:images_add_revision' path=urlpath.path article_id=article.id image_id=image.id %}"><span class="icon-edit"></span> {% trans "Replace" %}</a> <a href="{% url 'wiki:images_add_revision' path=urlpath.path article_id=article.id image_id=image.id %}"><span class="icon-edit"></span> {% trans "Replace" %}</a>
{% endif %} {% endif %}
...@@ -99,7 +107,10 @@ ...@@ -99,7 +107,10 @@
{% trans "How to use images" %} {% trans "How to use images" %}
</h4> </h4>
<p>{% trans "After uploading an image, it is attached to this particular artice and can be used only here. Other users may replace the image, but older versions are kept. You probably want to show the image with a nice caption. To achieve this, press the Insert button and fill in the caption fields and possibly choose to have you image floating right or left of the content. You can use Markdown in the caption. The markdown code syntax for images looks like this:" %}<br /><code>[image:id alignment caption text]</code></p> <p>{% trans "After uploading an image, it is attached to this particular artice and can be used only here. Other users may replace the image, but older versions are kept. You probably want to show the image with a nice caption. To achieve this, press the Insert button and fill in the caption fields and possibly choose to have you image floating right or left of the content. You can use Markdown in the caption. The markdown code syntax for images looks like this, possible values for align are left/center/right:" %}<br />
<pre>[image:id align:right]
caption indented by 4 spaces</pre>
</p>
{% endwith %} {% endwith %}
...@@ -6,6 +6,7 @@ from wiki.core.plugins import registry ...@@ -6,6 +6,7 @@ from wiki.core.plugins import registry
from wiki.core.plugins.base import BasePlugin from wiki.core.plugins.base import BasePlugin
from wiki.plugins.images import views, models, settings, forms from wiki.plugins.images import views, models, settings, forms
from wiki.plugins.notifications import ARTICLE_EDIT from wiki.plugins.notifications import ARTICLE_EDIT
from wiki.plugins.images.markdown_extensions import ImageExtension
class ImagePlugin(BasePlugin): class ImagePlugin(BasePlugin):
...@@ -31,6 +32,14 @@ class ImagePlugin(BasePlugin): ...@@ -31,6 +32,14 @@ class ImagePlugin(BasePlugin):
'get_article': lambda obj: obj.article} 'get_article': lambda obj: obj.article}
] ]
class RenderMedia:
js = [
'wiki/colorbox/colorbox/jquery.colorbox-min.js',
'wiki/js/images.js',
]
css = {'screen': 'wiki/colorbox/example1/colorbox.css'}
urlpatterns = patterns('', urlpatterns = patterns('',
url('^$', views.ImageView.as_view(), name='images_index'), url('^$', views.ImageView.as_view(), name='images_index'),
url('^delete/(?P<image_id>\d+)/$', views.DeleteView.as_view(), name='images_delete'), url('^delete/(?P<image_id>\d+)/$', views.DeleteView.as_view(), name='images_delete'),
...@@ -39,7 +48,7 @@ class ImagePlugin(BasePlugin): ...@@ -39,7 +48,7 @@ class ImagePlugin(BasePlugin):
url('^(?P<image_id>\d+)/revision/add/$', views.RevisionAddView.as_view(), name='images_add_revision'), url('^(?P<image_id>\d+)/revision/add/$', views.RevisionAddView.as_view(), name='images_add_revision'),
) )
#markdown_extensions = [AttachmentExtension()] markdown_extensions = [ImageExtension()]
def __init__(self): def __init__(self):
#print "I WAS LOADED!" #print "I WAS LOADED!"
......
$.fn.extend({
insertAtCaret: function(myValue){
return this.each(function(i) {
if (document.selection) {
//For browsers like Internet Explorer
this.focus();
sel = document.selection.createRange();
sel.text = myValue;
this.focus();
}
else if (this.selectionStart || this.selectionStart == '0') {
//For browsers like Firefox and Webkit based
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
var scrollTop = this.scrollTop;
this.value = this.value.substring(0, startPos)+myValue+this.value.substring(endPos,this.value.length);
this.focus();
this.selectionStart = startPos + myValue.length;
this.selectionEnd = startPos + myValue.length;
this.scrollTop = scrollTop;
} else {
this.value += myValue;
this.focus();
}
})
}
});
{% load sekizai_tags %} {% load sekizai_tags %}
{% addtoblock "js" %} {% addtoblock "js" %}
<script type="text/javascript" src="{{ STATIC_URL }}wiki/js/editor.js"></script>
{% for js in editor.Media.js %} {% for js in editor.Media.js %}
<script type="text/javascript" src="{{ STATIC_URL }}{{ js }}"></script> <script type="text/javascript" src="{{ STATIC_URL }}{{ js }}"></script>
{% endfor %} {% endfor %}
......
{% load wiki_tags i18n cache %} {% load wiki_tags i18n cache %}
{% block wiki_contents %} {% block wiki_contents %}
{% for plugin in plugins %}
{% if plugin.RenderMedia.css %}
{% for media, url in plugin.RenderMedia.css.items %}
<link rel="stylesheet" href="{{ STATIC_URL }}{{ url }}" />
{% endfor %}
{% endif %}
{% if plugin.RenderMedia.js %}
{% for url in plugin.RenderMedia.js %}
<script type="text/javascript" src="{{ STATIC_URL }}{{ url }}"></script>
{% endfor %}
{% endif %}
{% endfor %}
<div class="wiki-article">
{% if not preview %} {% if not preview %}
{% if article.current_revision %}
{% cache 1 article article.current_revision.id %} {% if article.current_revision %}
{{ article.render }} {% cache 1 article article.current_revision.id %}
{% endcache %} {{ article.render }}
{% endif %} {% endcache %}
{% endif %}
{% else %} {% else %}
{{ content|default:"" }} {{ content|default:"" }}
{% endif %} {% endif %}
</div>
{% endblock %} {% endblock %}
from django.conf import settings as django_settings
from django import template from django import template
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Model from django.db.models import Model
...@@ -6,6 +7,7 @@ from django.forms import BaseForm ...@@ -6,6 +7,7 @@ from django.forms import BaseForm
register = template.Library() register = template.Library()
from wiki import models from wiki import models
from wiki.core.plugins import registry as plugin_registry
# Cache for looking up objects for articles... article_for_object is # Cache for looking up objects for articles... article_for_object is
# called more than once per page in multiple template blocks. # called more than once per page in multiple template blocks.
...@@ -39,6 +41,8 @@ def wiki_render(article, preview_content=None): ...@@ -39,6 +41,8 @@ def wiki_render(article, preview_content=None):
'article': article, 'article': article,
'content': content, 'content': content,
'preview': not preview_content is None, 'preview': not preview_content is None,
'plugins': plugin_registry.get_plugins(),
'STATIC_URL': django_settings.STATIC_URL,
} }
@register.inclusion_tag('wiki/includes/form.html', takes_context=True) @register.inclusion_tag('wiki/includes/form.html', takes_context=True)
......
...@@ -21,7 +21,7 @@ if settings.ACCOUNT_HANDLING: ...@@ -21,7 +21,7 @@ if settings.ACCOUNT_HANDLING:
urlpatterns += patterns('', urlpatterns += patterns('',
# This one doesn't work because it don't know where to redirect after... # This one doesn't work because it don't know where to redirect after...
url('^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'), url('^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'),
url('^_revision/preview/(?P<article_id>\d+)/$', 'wiki.views.article.preview', name='preview_revision'), url('^_revision/preview/(?P<article_id>\d+)/$', article.Preview.as_view(), name='preview_revision'),
url('^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$', 'wiki.views.article.merge', name='merge_revision_preview', kwargs={'preview': True}), url('^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$', 'wiki.views.article.merge', name='merge_revision_preview', kwargs={'preview': True}),
# Paths decided by article_ids # Paths decided by article_ids
...@@ -29,7 +29,7 @@ urlpatterns += patterns('', ...@@ -29,7 +29,7 @@ urlpatterns += patterns('',
url('^(?P<article_id>\d+)/delete/$', article.Delete.as_view(), name='delete'), url('^(?P<article_id>\d+)/delete/$', article.Delete.as_view(), name='delete'),
url('^(?P<article_id>\d+)/deleted/$', article.Deleted.as_view(), name='deleted'), url('^(?P<article_id>\d+)/deleted/$', article.Deleted.as_view(), name='deleted'),
url('^(?P<article_id>\d+)/edit/$', article.Edit.as_view(), name='edit'), url('^(?P<article_id>\d+)/edit/$', article.Edit.as_view(), name='edit'),
url('^(?P<article_id>\d+)/preview/$', 'wiki.views.article.preview', name='preview'), url('^(?P<article_id>\d+)/preview/$', article.Preview.as_view(), name='preview'),
url('^(?P<article_id>\d+)/history/$', article.History.as_view(), name='history'), url('^(?P<article_id>\d+)/history/$', article.History.as_view(), name='history'),
url('^(?P<article_id>\d+)/settings/$', article.Settings.as_view(), name='settings'), url('^(?P<article_id>\d+)/settings/$', article.Settings.as_view(), name='settings'),
url('^(?P<article_id>\d+)/revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'), url('^(?P<article_id>\d+)/revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'),
...@@ -53,7 +53,7 @@ urlpatterns += patterns('', ...@@ -53,7 +53,7 @@ urlpatterns += patterns('',
url('^(?P<path>.+/|)_delete/$', article.Delete.as_view(), name='delete'), url('^(?P<path>.+/|)_delete/$', article.Delete.as_view(), name='delete'),
url('^(?P<path>.+/|)_deleted/$', article.Deleted.as_view(), name='deleted'), url('^(?P<path>.+/|)_deleted/$', article.Deleted.as_view(), name='deleted'),
url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit'), url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit'),
url('^(?P<path>.+/|)_preview/$', 'wiki.views.article.preview', name='preview'), url('^(?P<path>.+/|)_preview/$', article.Preview.as_view(), name='preview'),
url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history'), url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history'),
url('^(?P<path>.+/|)_settings/$', article.Settings.as_view(), name='settings'), url('^(?P<path>.+/|)_settings/$', article.Settings.as_view(), name='settings'),
url('^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'), url('^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'),
......
...@@ -233,7 +233,7 @@ class Edit(FormView, ArticleMixin): ...@@ -233,7 +233,7 @@ class Edit(FormView, ArticleMixin):
otherwise removes the 'data' and 'files' kwargs from form initialisation. otherwise removes the 'data' and 'files' kwargs from form initialisation.
""" """
kwargs = self.get_form_kwargs() kwargs = self.get_form_kwargs()
if self.request.POST.get('save', '') != '1': if self.request.POST.get('save', '') != '1' and self.request.POST.get('preview') != '1':
kwargs['data'] = None kwargs['data'] = None
kwargs['files'] = None kwargs['files'] = None
kwargs['no_clean'] = True kwargs['no_clean'] = True
...@@ -466,33 +466,42 @@ def change_revision(request, article, revision_id=None, urlpath=None): ...@@ -466,33 +466,42 @@ def change_revision(request, article, revision_id=None, urlpath=None):
else: else:
return redirect('wiki:history', article_id=article.id) return redirect('wiki:history', article_id=article.id)
# TODO: Throw in a class-based view class Preview(ArticleMixin, TemplateView):
@get_article(can_read=True)
def preview(request, article, urlpath=None, template_file="wiki/preview_inline.html"):
content = article.current_revision.content template_name="wiki/preview_inline.html"
title = article.current_revision.title
revision_id = request.GET.get('r', None) @method_decorator(get_article(can_read=True))
revision = None def dispatch(self, request, article, *args, **kwargs):
revision_id = request.GET.get('r', None)
self.title = None
self.content = None
self.preview = False
if revision_id:
self.revision = get_object_or_404(models.ArticleRevision, article=article, id=revision_id)
else:
self.revision = None
return super(Preview, self).dispatch(request, article, *args, **kwargs)
if request.method == 'POST': def post(self, request, *args, **kwargs):
edit_form = forms.EditForm(article.current_revision, request.POST, preview=True) edit_form = forms.EditForm(self.article.current_revision, request.POST, preview=True)
if edit_form.is_valid(): if edit_form.is_valid():
title = edit_form.cleaned_data['title'] self.title = edit_form.cleaned_data['title']
content = edit_form.cleaned_data['content'] self.content = edit_form.cleaned_data['content']
self.preview = True
elif revision_id: return super(Preview, self).get(request, *args, **kwargs)
revision = get_object_or_404(models.ArticleRevision, article=article, id=revision_id)
title = revision.title def get(self, request, *args, **kwargs):
content = revision.content self.title = self.revision.title
self.content = self.revision.content
c = RequestContext(request, {'urlpath': urlpath, return super(Preview, self).get( request, *args, **kwargs)
'article': article,
'title': title, def get_context_data(self, **kwargs):
'revision': revision, kwargs['title'] = self.title
'content': content}) kwargs['revision'] = self.revision
return render_to_response(template_file, context_instance=c) kwargs['content'] = self.content
kwargs['preview'] = self.preview
return super(Preview, self).get_context_data(**kwargs)
@json_view @json_view
def diff(request, revision_id, other_revision_id=None): def diff(request, revision_id, other_revision_id=None):
......
...@@ -23,5 +23,6 @@ class ArticleMixin(TemplateResponseMixin): ...@@ -23,5 +23,6 @@ class ArticleMixin(TemplateResponseMixin):
kwargs['article_tabs'] = registry.get_article_tabs() kwargs['article_tabs'] = registry.get_article_tabs()
kwargs['children_slice'] = self.children_slice[:20] kwargs['children_slice'] = self.children_slice[:20]
kwargs['children_slice_more'] = len(self.children_slice) > 20 kwargs['children_slice_more'] = len(self.children_slice) > 20
kwargs['plugins'] = registry.get_plugins()
return kwargs return kwargs
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment