diff options
53 files changed, 3340 insertions, 202 deletions
diff --git a/demo/settings.py b/demo/settings.py index 10ed306..b085956 100644 --- a/demo/settings.py +++ b/demo/settings.py @@ -19,24 +19,25 @@ import os TIME_ZONE = None gettext = lambda s: s - +os.system("mkdir /tmp/beta/") DEBUG = True TEMPLATE_DEBUG78 = True - +#INTERNAL_IPS = ('127.0.0.1','158.144.44.212','158.144.42.67') +#DEBUG_TOOLBAR_CONFIG = { 'INTERCEPT-REDIRECTS':False,} DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(os.path.dirname(__file__), 'demo.db')} } STATIC_URL = '/static/' +STATIC_ROOT = '/static' RECAPTCHA_PUBLIC_KEY = '6LcBr9USAAAAAJNHxpA5_2nQK9JnKQCU3kTUstEK' RECAPTCHA_PRIVATE_KEY = '6LcBr9USAAAAABYW6VgsQeupDHy2R42G4aGsHxXr' - MEDIA_URL = '/static' -MEDIA_ROOT = os.path.join(os.path.dirname(__file__), '../demo/grappelli/static/grappelli/img') -MEDIA_ROOTNEW2 = os.path.join(os.path.dirname(__file__), '../demo/grappelli/static/grappelli/img') -MEDIA_ROOTNEW3 = os.path.join(os.path.dirname(__file__), '../gstudio/static/gstudio/documents') -MEDIA_ROOTNEW = os.path.join(os.path.dirname(__file__), '../demo/media') +MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'static/img') +MEDIA_ROOTNEW2 = os.path.join(os.path.dirname(__file__), 'static/img') +MEDIA_ROOTNEW3 = os.path.join(os.path.dirname(__file__), 'static/img') +MEDIA_ROOTNEW = os.path.join(os.path.dirname(__file__), 'static/img') #MEDIA_ROOT = '/static' #MEDIA_ROOT = os.path.join(os.path.dirname(__file__), '../gstudio/static') PYSCRIPT_URL_GSTUDIO = os.path.join(os.path.dirname(__file__), '../gstudio/createhtml.py') @@ -45,11 +46,13 @@ VIDEO_PANDORA_URL = os.getenv("HOME")+"/.ox/client.json" FILE_URL = os.path.join(os.path.dirname(__file__), '/tmp/beta/') FILE_UPLOAD_MAX_MEMORY_SIZE= 524288000 JPEG_ROOT = None +STATICFILES_DIRS = ( + os.path.join(os.path.dirname(__file__),'grappelli/static'), +) +GSTUDIO_UPLOAD_TO = 'static/img/' -GSTUDIO_UPLOAD_TO = 'img/' - -ADMIN_MEDIA_PREFIX = STATIC_URL + "grappelli/" +ADMIN_MEDIA_PREFIX = STATIC_URL+ "grappelli/" SECRET_KEY = 'jo-1rzm(%sf)3#n+fb7h955yu$3(pt63abhi12_t7e^^5q8dyw' @@ -154,6 +157,8 @@ INSTALLED_APPS = ( 'HTTP4Store', 'html5lib', 'pagination', + 'notification', + 'fixture_magic', # Uncomment the south entry to activate south for database migrations # Please do install south before uncommenting # command: sudo pip install south @@ -165,6 +170,11 @@ INSTALLED_APPS = ( # EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' -#from gstudio.xmlrpc import GSTUDIO_XMLRPC_METHODS -#XMLRPC_METHODS = GSTUDIO_XMLRPC_METHODS - +from gstudio.xmlrpc import GSTUDIO_XMLRPC_METHODS +XMLRPC_METHODS = GSTUDIO_XMLRPC_METHODS +try: + from local_settings import * + #print "Local settings applied" +except: + #print "Default settings applied" + pass diff --git a/gstudio/methods.py b/gstudio/methods.py index b0b75d3..3d3b263 100644 --- a/gstudio/methods.py +++ b/gstudio/methods.py @@ -6,6 +6,16 @@ import os from demo.settings import PYSCRIPT_URL_GSTUDIO from demo.settings import FILE_URL +def get_threadbox_of_twist(twistid): + thid="" + for each in System.objects.all(): + sys_set=each.system_set.all() + if sys_set: + sys_set=each.system_set.all()[0] + for eachsys in sys_set.gbobject_set.all(): + if eachsys.id==twistid: + return sys_set + return thid def delete(idnum): del_ob = Gbobject.objects.get(id=idnum) @@ -41,8 +51,9 @@ def make_rep_object(title,auth_id,usr): data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -83,8 +94,9 @@ def edit_section(sec_id,title,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -119,8 +131,9 @@ def make_topic_object(title,auth_id,content,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -163,8 +176,9 @@ def make_sectionreply_object(content_org,title,auth_id,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -208,8 +222,9 @@ def make_section_object(title,auth_id,content_org,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -278,8 +293,9 @@ def create_meeting(title,idusr,content,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -340,8 +356,9 @@ def create_wikipage(title,idusr,content_org,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -351,15 +368,15 @@ def create_wikipage(title,idusr,content_org,usr): sys.systemtypes.add(Systemtype.objects.get(title="Wikipage")) sys.authors.add(Author.objects.get(id=idusr)) - a = Attribute() - a.title = "released button of " + title - a.slug = slugify(a.title) - a.content = a.slug - a.status = 2 - a.subject = sys - a.svalue = "False" - a.attributetype_id = Attributetype.objects.get(title="pagerelease").id - a.save() + #a = Attribute() + #a.title = "released button of " + title + #a.slug = slugify(a.title) + #a.content = a.slug + #a.status = 2 + #a.subject = sys + #a.svalue = "False" + #a.attributetype_id = Attributetype.objects.get(title="pagerelease").id + #a.save() sys1 = System() sys1.title = "page box of " + title sys1.status = 2 @@ -387,19 +404,19 @@ def make_att_false(meet_ob): meet_ob.subject_of.add(each) break -def make_att1_true(page_ob): - for each in page_ob.subject_of.all(): - if(each.attributetype.title=='pagerelease'): - each.svalue = "true" - page_ob.subject_of.add(each) - break +#def make_att1_true(page_ob): + # for each in page_ob.subject_of.all(): + # if(each.attributetype.title=='pagerelease'): + # each.svalue = "true" + # page_ob.subject_of.add(each) + # break -def make_att1_false(page_ob): - for each in page_ob.subject_of.all(): - if(each.attributetype.title=='pagerelease'): - each.svalue = "" - page_ob.subject_of.add(each) - break +#def make_att1_false(page_ob): + # for each in page_ob.subject_of.all(): + # if(each.attributetype.title=='pagerelease'): + # each.svalue = "" + # page_ob.subject_of.add(each) + # break def schedule_time(stTime, endTime, sys_id): sys=System.objects.get(id=sys_id) @@ -413,7 +430,7 @@ def schedule_time(stTime, endTime, sys_id): ate.subject=sys; ate.value=endTime; ate.attributetype=atty2; ate.save() ats.save() - sys.save() + #sys.save() return sys.id @@ -440,7 +457,7 @@ def get_time(sys_id): meetover=True else: meetover=False - return (later, meetover, starttime, endtime) + return (later, meetover, starttime, endtime) def del_comment(comment_id): ob = Gbobject.objects.get(id=int(comment_id)) for each in ob.posterior_nodes.all(): @@ -488,8 +505,9 @@ def edit_topic(topic_id,title,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -523,8 +541,9 @@ def edit_thread(thread_id,title,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -561,8 +580,9 @@ def edit_nodetype(iden,rep,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -576,6 +596,28 @@ def check_release_or_not(meet_ob): if (each.attributetype.title=='release' and each.svalue=='true'): fl=1 return fl +def check_subscribe_or_not(meet_ob,user): + fl=0 + box=meet_ob.system_set.all()[0] + ch=Author.objects.filter(id=user.id) + if ch: + ch=Author.objects.get(id=user.id) + else: + ch="" + for each in box.member_set.all(): + if each == ch: + fl=1 + return fl + +def check_usr_admin(userid): + fl=0 + aut=Author.objects.filter(id=userid) + if aut: + aut=Author.objects.get(id=userid) + if aut.is_superuser: + fl=1 + return fl + def get_factory_loom_OTs(): retlist=[] @@ -584,3 +626,24 @@ def get_factory_loom_OTs(): if ((each.parent.title=='Factory_Object') and (str(each.slug)[0:4]=='loom')): retlist.append(each.title) return retlist + +def get_home_content(): + homeobj=Gbobject.objects.filter(title="home_specific_detail") + if homeobj: + homeobj=Gbobject.objects.get(title="home_specific_detail") + return homeobj.content + +def get_more_content(): + moreobj=Gbobject.objects.filter(title="more_specific_detail") + if moreobj: + moreobj=Gbobject.objects.get(title="more_specific_detail") + return moreobj.content + +def get_home_title(): + homeobj=Gbobject.objects.filter(title="home_title") + content = "" + if homeobj: + homeobj=Gbobject.objects.get(title="home_title") + content = homeobj.content + return content + diff --git a/gstudio/models.py b/gstudio/models.py index cd529c4..98ff1be 100644 --- a/gstudio/models.py +++ b/gstudio/models.py @@ -952,7 +952,7 @@ class Nodetype(Node): g_json["relations"].append({"from":self.id ,"type":str(key),"value":1,"to":predicate_id[key] }) - if not isinstance(nbh[key],basestring) and len(nbh[key])<=2: + if not isinstance(nbh[key],basestring) and len(nbh[key])<=10: for item in nbh[key]: if isinstance(item,unicode): g_json["node_metadata"].append({"_id":(str(attr_counter)+"b"),"screen_name":str(item)}) diff --git a/gstudio/static/gstudio/js/addcontent.js b/gstudio/static/gstudio/js/addcontent.js index 5c0c4bc..54be47a 100644 --- a/gstudio/static/gstudio/js/addcontent.js +++ b/gstudio/static/gstudio/js/addcontent.js @@ -8,6 +8,7 @@ var isSubsection=false; var editSubsection=false; var isNode=false; + var isObject=false; function subsecsave(objid){ var org_data = $("#gnoweditor").val(); var encode_data = encodeURIComponent(org_data); @@ -40,13 +41,15 @@ }); $("#pagecontent1").one("click",function() { + $(this).replaceWith('<textarea id="gnoweditor" style="visibility:hidden;width:450px"></textarea>'); isWikipage=true; - $("#chart").hide(); - document.getElementById('gnoweditor').style.visibility="visible"; - $("#gnoweditor").orgitdown(mySettings); + $("#chart").hide(); + document.getElementById('gnoweditor').style.visibility="visible"; + $("#gnoweditor").orgitdown(mySettings); + $(".orgitdownContainer").css({"margin-top":"0px","margin-left":"10px"}); //$("#save1").show(); - $("#pagecontent1").hide(); - $("#content").css({"width":"300px",}) + $("#pagecontent1").hide(); + $("#content").css({"width":"300px",}) }); $("#save1").one("click",function() { var org_data = $("#gnoweditor").val(); @@ -125,6 +128,7 @@ $(".deletesec").hide(); }); $(".editpagecontent").one("click",function(){ + $(this).replaceWith('<textarea id="gnoweditor" style="visibility:hidden;width:450px"></textarea>'); editWikipage=true; $("#chart").hide(); $(".editpagecontent").hide(); @@ -139,11 +143,11 @@ var elmts = document.getElementsByClassName("editval"); for (var i = 0; i < elmts.length; i++){ elmts[i].setAttribute("value","edited");} - var screenTop = $(document).scrollTop(); + // var screenTop = $(document).scrollTop(); $(".orgitdownContainer").css({ - "margin-top":screenTop,}); - $(".tag").hide(); - $(".tagtext").hide(); + "margin-top":"0px","margin-left":"10px"}); + //$(".tag").hide(); + //$(".tagtext").hide(); $("#newsection1").hide(); $(".createsubsection").hide(); $("#rating").hide(); @@ -163,6 +167,7 @@ elmts[i].setAttribute("value",decode_data);} $(".pagedit").trigger('click'); $(".savepagecontent").hide(); + $(".orgitdownContainer").hide(); }); $("#editnodecontent").one("click",function(){ @@ -194,6 +199,37 @@ $("#nodedit").hide(); }); + + $("#editobjectcontent").one("click",function(){ + isObject=true; + $("#chart").hide(); + $("#content img").css({"max-width":"600px",}) + + $("#content").css({"width":"600px",}) + document.getElementById('gnoweditor').style.visibility="visible"; + $("#gnoweditor").orgitdown(mySettings); + var a = this.name; + + $("#gnoweditor").val(a); + var screenTop = $(document).scrollTop(); + $(".orgitdownContainer").css({ + "margin-top":screenTop,}); + $("#editnodecontent").hide(); + //$("#savenodecontent").show(); + $("#objectedit").hide(); + + }); + $("#saveobjectcontent").one("click",function(){ + var org_data = $("#gnoweditor").val(); + var encode_data = encodeURIComponent(org_data); + + var decode_data = decodeURIComponent(encode_data.replace(/\+/g, " ")); + $("#reptext").val(decode_data); + $("#objectedit").trigger('click'); + $("#objectedit").hide(); + + }); + $(".createsubsection").one("click",function(){ isSubsection=true; diff --git a/gstudio/static/gstudio/js/orgitdown/orgitdown/jquery.orgitdown.js b/gstudio/static/gstudio/js/orgitdown/orgitdown/jquery.orgitdown.js index 6e207a0..78974fd 100644 --- a/gstudio/static/gstudio/js/orgitdown/orgitdown/jquery.orgitdown.js +++ b/gstudio/static/gstudio/js/orgitdown/orgitdown/jquery.orgitdown.js @@ -474,7 +474,8 @@ $("#save1").trigger('click');} if (isNode){ $("#savenodecontent").trigger('click');} - + if (isObject){ + $("#saveobjectcontent").trigger('click');} if (editWikipage){ $(".savepagecontent").trigger('click');} if (isSection){ @@ -505,7 +506,8 @@ $("#save"+objid).trigger('click');} if (isVideotitle){ $("#titlesave").trigger('click');} - + if(isSubResponse){ + saveclick(objid);} } // open preview window diff --git a/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/images/video.jpeg b/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/images/video.jpeg Binary files differnew file mode 100644 index 0000000..8edf9d6 --- /dev/null +++ b/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/images/video.jpeg diff --git a/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/set.js b/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/set.js index 17121d7..3ed79ff 100644 --- a/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/set.js +++ b/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/set.js @@ -26,6 +26,7 @@ var mySettings = { {name:'Picture', key:'P', replaceWith:'\n#+CAPTION: \n#+ATTR_HTML: width="600" \n[[http:fileName.jpg]]\n' }, {name:'Link', key:'L', openWith:'[[http://your.address.com here/][Your visible link text here]]', closeWith:'', placeHolder:'' }, {separator:'---------------' }, + {name:'Insert Embed Html',replaceWith:'\n#+BEGIN_HTML \n#+END_HTML\n' }, {name:'Close',call:'close' } //{name:'Clean', className:'clean', replaceWith:function(orgitdown) { return orgitdown.selection.replace(/<(.*?)>/g, "") } } diff --git a/gstudio/templates/gstudio/_header.html b/gstudio/templates/gstudio/_header.html index 123b05d..6523fcf 100644 --- a/gstudio/templates/gstudio/_header.html +++ b/gstudio/templates/gstudio/_header.html @@ -18,8 +18,10 @@ | <a href="{{ get_absolute_url }}/gstudio/resources/documents" title="My Documents">Documents</a> | <a href="{{ get_absolute_url }}/gstudio/resources/images" title="Images">Images</a> | <a href="{{ get_absolute_url }}/gstudio/resources/videos" title="My Videos">Videos</a> +<!-- | <a href="{{ get_absolute_url }}/nodetypes" title="Nodes">Node Types</a> | <a href="{{ get_absolute_url }}/objects" title="Objects">Node Objects</a> +--> {% if user.is_authenticated %} {% if user.is_staff %} diff --git a/gstudio/templates/gstudio/addingtag.html b/gstudio/templates/gstudio/addingtag.html new file mode 100644 index 0000000..2e50501 --- /dev/null +++ b/gstudio/templates/gstudio/addingtag.html @@ -0,0 +1,82 @@ +{% load gstudio_tags %} +{% load tagging_tags comments i18n %} + +<script type="text/javascript" > +var availableTags = []; +$.merge(availableTags, {% get_add_tag %}) + + +jQuery(document).ready(function($) { +$(".tag{{objectid}}").click(function(){ +data = $("#tags{{objectid}}").attr('value'); +$.ajax({ +url: '/gstudio/resources/images/addtag/', +//type: 'POST', +data: {objectid:{{objectid}},data:data}, +success: function(data){ +$('.tags{{objectid}}').html(data); + +} +}); +$("#tags{{objectid}}").val(""); +}); + +/*$(".deletetags").click(function(){ +tagname = $(this).attr('value'); + +$.ajax({ +url: '/gstudio/resources/images/deletetag/', +//type: 'POST', +data: {objectid:{{objectid}},data:tagname}, +success: function(data){ +$('.tags{{objectid}}').html(data); +} +}); + +});*/ +}); +function tagfunction(arr,id){ +tagname = arr; +$.ajax({ +url: '/gstudio/resources/images/deletetag/', +//type: 'POST', +data: {objectid:id,data:tagname}, +success: function(data){ +$('.tags'+id).html(data); +} +}); + +} +</script> + +<div class="tags{{objectid}}"> + <!-- <p class="gbobject-tags span-16 last"> --> + <strong>{% trans "Tags" %}</strong> : + {% tags_for_object viewtag as tag_list %} + {% for tag in tag_list %} + <a href="{% url objectapp_tag_detail tag %}" + title="Tag {{ tag }}" rel="tag">{{ tag }}</a> +{% if user.is_authenticated %} + <a class="deletetags" value="{{tag}}" title="delete {{tag}}" onclick='tagfunction("{{tag}}","{{objectid}}");' ><img src="/static/gstudio/js/orgitdown/orgitdown/sets/org/images/close.jpeg" width = 8px; style="vertical-align: super;"/></a> +{% endif %} + {% if not forloop.last %},{% endif %} + {% empty %} + <span>{% trans "No tags" %}</span> + {% endfor %} +<!-- </p> --> + </div> + {% if user.is_authenticated %} + <script type="text/javascript" > + $(document).ready(function(){ + $( "#tags{{objectid}}" ).autocomplete({ + source: availableTags + }); + }); + </script> +{% csrf_token %} + <input type="hidden" name="docid" value={{objectid}}> + <!-- <input type="text" class="tagtext" value="" name="texttags" />--> + <input id="tags{{objectid}}" class="tagtext" name="texttags"> + <input type="button" class="tag{{objectid}}" value="Add Tags" name="addtags" /> + <br> + {% endif %} diff --git a/gstudio/templates/gstudio/addreln.html b/gstudio/templates/gstudio/addreln.html new file mode 100644 index 0000000..b484b8b --- /dev/null +++ b/gstudio/templates/gstudio/addreln.html @@ -0,0 +1,76 @@ +{% load gstudio_tags %} +<script type="text/javascript"> +var availableobjs = []; +$.merge(availableobjs, {% get_available_objects %}) +var availablerts=[] +$.merge(availablerts, {% get_available_rts %}) +$(document).ready(function(){ + $("#res_relation").autocomplete({ + source: availablerts + }); + $("#res_object").autocomplete({ + source: availableobjs + }); +$("#addreln").click(function(){ +alert("Please wait till the relation is shown in the existing relations..."); +var ajurl ="/gstudio/resources/addreln/thread/"+{{meetingob.id}} + +var res_reln = $("#res_relation").val() +var res_obj= $("#res_object").val() +if (res_reln == "" || res_obj == "" ) +{ alert("Please select relation and right object"); +} +else +{ +$.ajax({ + url: ajurl, + data: {relnobj:res_reln,obobject:res_obj}, + success: function(data){ + + $('#res_relation').val(""); + $('#res_object').val(""); + alert("Added relation"); + $('#relndiv').html(data); + + } +}); +} +}); +}); +function target_popup(form) { + window.open('', 'formpopup', 'left=360,width=500,height=300,resizeable,scro\ +llbars'); + form.target = 'formpopup'; + + + } + +</script> +<div id="relndiv"> +<br/> + +<b>Existing Relations for this page: </b> + {% if not meetingob.get_relations_for_view.items %} + No relations + {% endif %} + {% for key,value in meetingob.get_relations_for_view.items %} + {{key}} - + {% for rel in value %} + {{rel}}; + {% endfor %} + {% endfor %} + +</div> +<form method="post" action=""> {% csrf_token %} +Select/Add a Relationtype: <nbsp> +<input value="" name="textreln" id="res_relation"> +<!-- +<a href="/admin/gstudio/relationtype/add" class="add-another" id="add_id_{{ field }}" onclick="return showAddAnotherPopup(this);"> +--> +<a href="/gstudio/resources/addreln/form/{{meetingob.id}}" class="add-another" id="addrel" onclick="target_popup(this)"> +<img width="10" height="10" alt="Add Another" src="/static/grappelli/img/admin/icon_addlink.gif"> +</a> +Select an object: <nbsp> +<input value="" name="textobj" id="res_object"> +<input type="button" id="addreln" name="" value="Add Relation"> +</form> diff --git a/gstudio/templates/gstudio/addrelnform.html b/gstudio/templates/gstudio/addrelnform.html new file mode 100644 index 0000000..113a450 --- /dev/null +++ b/gstudio/templates/gstudio/addrelnform.html @@ -0,0 +1,51 @@ +{% load gstudio_tags %} +{% load adminmedia grp_tags %} +<script src="{% admin_media_prefix %}jquery/jquery-1.6.2.min.js" type="text/javascript"> + +</script> +<script> +function close_window() +{ window.close() +} + + +function saverelnform() +{ +var reln=$("#res_relation").val(); +var obj=$("#res_object").val(); +var slug=$("#res_slug").val(); + +$.ajax({ + method:'POST', + url: '/gstudio/resources/addreln/form/{{meetob}}', + data: {reln:reln,obj:obj,slug:slug}, + success: function(){ + window.close() + } + }); +} + +</script> +<html> <body bgcolor="#E6E6FA"> +<form method="post" name="addrelnfrm" action="."> {% csrf_token %} +<h1>Add Relation(link) name</h1> +<h2>Title:                 +<input value="" name="reln" id="res_relation"> +<br/></h2> +<h2>Inverse Name:   +<input value="" name="obj" id="res_object"> +<br/></h2> +<h2>Slug:                 +<input value="" name="slug" id="res_slug"> +<br/></h2> +<br/>                               +<!-- +<input type="submit" id="addsavereln" name="" value="Save" ><nbsp> +--> +        +<input type="button" id="addrel" value="Save" onclick="saverelnform()"> + +<input type="submit" id="addcancelreln" name="" value="Cancel" onclick="close_window()"> +</form> +</body> +</html> diff --git a/gstudio/templates/gstudio/addrelnform_refresh.html b/gstudio/templates/gstudio/addrelnform_refresh.html new file mode 100644 index 0000000..f959235 --- /dev/null +++ b/gstudio/templates/gstudio/addrelnform_refresh.html @@ -0,0 +1,15 @@ +{% load gstudio_tags %} + +<div id="relndiv"> +<br/> +<b>Existing Relations for this page: </b> + {% if not meetobj.get_relations_for_view.items %} + No relations + {% endif %} + {% for key,value in meetobj.get_relations_for_view.items %} + {{key}} - + {% for rel in value %} + {{rel}}; + {% endfor %} + {% endfor %} +</div> diff --git a/gstudio/templates/gstudio/edittitle.html b/gstudio/templates/gstudio/edittitle.html new file mode 100644 index 0000000..b8207e9 --- /dev/null +++ b/gstudio/templates/gstudio/edittitle.html @@ -0,0 +1,37 @@ +<script type="text/javascript" > +jQuery(document).ready(function($) { +$("#titleeditcontent").click(function(){ +document.getElementById('texttagtitle').style.visibility="visible"; +$("#texttagtitle").val($("#titleeditortext").val()); +document.getElementById('titlesave').style.visibility="visible"; +$("#titleeditcontent").hide(); +}); +$("#titlesave").click(function(){ +var org_data = $("#texttagtitle").val(); +var encode_data = encodeURIComponent(org_data); +var decode_data = decodeURIComponent(encode_data.replace(/\+/g, " ")); +$("#titlecommenttext").val(decode_data); + +$.ajax({ +url: '/gstudio/resources/images/edittitle/', +<!-- type:"POST", --> +data: {title:decode_data,titleid:{{objectid}}}, +success: function(){ +document.location.reload(true) +} +}); + +}); +}); +</script> + + +<!-- <form method="get" action=""> -->{% csrf_token %} +<input type="hidden" name="imgid" value={{objectid}}> +<input type="button" id="titleeditcontent" name="" value="Edit title"/> +<input type="text" value="" name="texttags" id="texttagtitle" style="visibility:hidden" /> +<input type="button" class="titlecommentsavecontent" id="titlesave" value="Save" style="visibility:hidden" /> +<input type="hidden" name="titlecontenttext" id="titlecommenttext" style="visibility:hidden" /> +<input type="hidden" id="titleeditortext" style="visibility:hidden" value="{{ objecttitle }}"> +<!-- </form> --> + diff --git a/gstudio/templates/gstudio/home.html b/gstudio/templates/gstudio/home.html index f1e7a02..44dd468 100644 --- a/gstudio/templates/gstudio/home.html +++ b/gstudio/templates/gstudio/home.html @@ -90,7 +90,8 @@ </style> <script src="http://code.jquery.com/jquery-latest.js"></script> - <script type="text/javascript" > + <scipt src="{% admin_media_prefix %}jquery/jquery-1.6.2.min.js" type="text/javascript"></script> + <script type="text/javascript" > $(window).load(function() { @@ -123,6 +124,10 @@ <div id="header" > {% include "gstudio/_header.html" %} </div> +{% put_home_title as home_content %} +{%with home_content|safe as home %} +{{home}} +{% endwith %} <div class="tags" align="center" id="tag"> <!--<h2>{% trans "Tag Cloud" %}</h2>--> {% get_tag_cloud %} @@ -138,7 +143,12 @@ </form> </div><br/><br/><br/> <link rel="stylesheet" type="text/css" media="screen, projection" href="/static/gstudio/css/screen.css" /> -<center><p style = " font-size : 19px ; color : #4d659e"><b>The site provides a loom (place to exchange views from the members of the site on a topic, or seek responses from members); collaboratively construct wikipages; share electronic documents, images and videos and use them in your discussions or wikipages; and create semantic networks. We will soon provide a link on how to use the site prominently on the home page. All resources are released under creative commons license. <a href="/more/"><font color="red">more</font></a></b></p></center> +<center><p style = " font-size : 19px ; color : #4d659e"><b> +{% put_home_content as home_content %} +{% with home_content|safe as home %} +{{home}} +{% endwith %} +<a href="/more/"><font color="red">more</font></a></b></p></center> <div class="homebottom"> <div class="authors"> <h3>{% trans "Recents Authors" %}</h3> @@ -147,7 +157,7 @@ <div class="recentsnodetype"> <h3>{% trans "Recent Objects" %}</h3> -{% get_recent_gbobjects 12%} +{% get_random_gbobjects 12%} </div> <div class="comments"> <h3>{% trans "Recent Comments" %}</h3> diff --git a/gstudio/templates/gstudio/priorpost.html b/gstudio/templates/gstudio/priorpost.html new file mode 100644 index 0000000..49e4a8a --- /dev/null +++ b/gstudio/templates/gstudio/priorpost.html @@ -0,0 +1,68 @@ +{% load gstudio_tags %} +{% load i18n %} +<script type="text/javascript" > + +var availableObjects = []; +$.merge(availableObjects, {% get_pri_post_page %}) + +jQuery(document).ready(function($) { +$("#addpriorpost").click(function(){ + +var data +data = $("#priorpage").attr('value'); +$.ajax({ +url: '/gstudio/resources/images/addpriorpost/', +//type:"POST", +data: {title:data,titleid:{{objectid}}}, +success: function(data){ +$('#priorpostpages').html(data); + +} +}); +$("#priorpage").val(""); +}); +}); + +$(document).ready(function(){ +$( "#priorpage" ).autocomplete({ +source: availableObjects +}); +}); + +</script> + +<div id = "priorpostpages"> +<br/> +<strong>Prior Pages:</strong> +{% for each in priorgbobject %} +<a href="{{each.get_view_object_url}}">{{each}}</a> +{% if not forloop.last %},{% endif %} +{% empty %} + <span>No prior page</span> +{% endfor %} +<br/> +<strong>Posterior Pages:</strong> +{% for each in posteriorgbobject %} +<a href="{{each.get_view_object_url}}">{{each}}</a> +{% if not forloop.last %},{% endif %} +{% empty %} + <span>No Posterior Page</span> +{% endfor %} +<br/> +</div> + +{% if user.is_authenticated %} +<!-- <form method="get" action=""> -->{% csrf_token %} + +<input type="hidden" name="docid" value={{objectid}}> +<input id="priorpage" class="tagpriorpage" name="textpriorpage"> +<input type="button" class="priorpost" id="addpriorpost" value="Add Prior Page" name="addtags" /> +<br/><br/> +<!-- <input type="button" id="titleeditcontent" name="" value="Edit a title"/> +<input type="text" value="" name="texttags" id="texttagtitle" style="visibility:hidden" /> +<input type="button" class="titlecommentsavecontent" id="titlesave" value="Save" style="visibility:hidden" /> +<input type="hidden" name="titlecontenttext" id="titlecommenttext" style="visibility:hidden" /> +<input type="hidden" id="titleeditortext" style="visibility:hidden" value="{{ objecttitle }}"> +<!-- </form> --> +{% endif %} + diff --git a/gstudio/templates/gstudio/repriorpost.html b/gstudio/templates/gstudio/repriorpost.html new file mode 100644 index 0000000..3499db0 --- /dev/null +++ b/gstudio/templates/gstudio/repriorpost.html @@ -0,0 +1,34 @@ +{% load tagging_tags comments i18n %} +{% ifequal optionpriorpost "priorpost" %} +<br/> +<strong>Prior Pages:</strong> +{% for each in priorgbobject %} +<a href="{{each.get_view_object_url}}">{{each}}</a> +{% if not forloop.last %},{% endif %} +{% empty %} + <span>No prior page</span> +{% endfor %} +<br/> +<strong>Posterior Pages:</strong> +{% for each in posteriorgbobject %} +<a href="{{each.get_view_object_url}}">{{each}}</a> +{% if not forloop.last %},{% endif %} +{% empty %} + <span>No Posterior Pages</span> +{% endfor %} +<br/> +{% endifequal %} +{% ifequal optiontag "tag" %} + <strong>{% trans "Tags" %}</strong> : + {% tags_for_object viewtag as tag_list %} + {% for tag in tag_list %} + <a href="{% url objectapp_tag_detail tag %}" + title="Tag {{ tag }}" rel="tag">{{ tag }}</a> + <a class="deletetags" value="{{tag}}" title="delete {{tag}}" onclick='tagfunction("{{tag}}","{{objectid}}");'><img src="/static/gstudio/js/orgitdown/orgitdown/sets/org/images/close.jpeg" width = 8px; style="vertical-align: super;"/></a> + {% if not forloop.last %},{% endif %} + {% empty %} + <span>{% trans "No tags" %}</span> + {% endfor %} + +{% endifequal %} + diff --git a/gstudio/templates/gstudio/tags/comment.html b/gstudio/templates/gstudio/tags/comment.html index efb462f..423fc93 100644 --- a/gstudio/templates/gstudio/tags/comment.html +++ b/gstudio/templates/gstudio/tags/comment.html @@ -6,6 +6,12 @@ $(window).load(function() { $(".commentsavecontent").hide(); $(".postreply").hide(); }); +function deleteclick(delobj) +{ + activity ='deleted_response' + not_obj=delobj +notifedtdel(); +} </script> {% if comment.posterior_nodes.count %} <!--<ul style="display: none;">--> @@ -26,7 +32,12 @@ else { document.getElementById("divchange").setAttribute("id","div2"); } +function submtfrm() +{ +alert("form to post"); +document.forms["response"].submit(); +} </script> @@ -35,7 +46,7 @@ document.getElementById("divchange").setAttribute("id","div2"); <h5>Current rating is {{ child.rating.get_rating }}<h5/> {% endif %} <font style = "color:red;" size = 3>{{child.content}}</font> - <form method="post" action=".">{% csrf_token %} + <form method="post" name="response" action=".">{% csrf_token %} <input type="hidden" class="commentreptext" id="text{{child.id}}" name="reply" style="visibility:hidden"> <input type="hidden" id="hidden{{child.id}}" value={{child.id}} name="parentid"> <input type="hidden" value={{idusr}} name="idusr"> @@ -45,8 +56,10 @@ document.getElementById("divchange").setAttribute("id","div2"); <input type="button" class="commenteditor" id="{{ child.id }}" value="Add a Response"> <input type="button" class="commentsavecontent" id="save{{child.id}}" value="Save" onclick="saveclick(document.getElementById('hidden{{child.id}}').value)"> <input type="submit" class="postreply" id="submit{{child.id}}" value="Submit"> + <input type="checkbox" class="chkdel" id="chk{{child.id}}" name="del_comment" value="delete_comment"> <input type="submit" class="submitdelete" id="delete{{child.id}}" value="Delete" onclick="deleteclick(document.getElementById('hidden{{child.id}}').value)"> + <br/> <div class="rating"> Rate the response </br> @@ -87,7 +100,7 @@ Current rating is {{ child.rating.get_rating }}<br/> {% endif %} <font style = "color:red;" size ="3">{{child.content}}</font> - <form method="post" action="">{% csrf_token %} + <form method="post" name="response" action="">{% csrf_token %} <input type="hidden" class="commentreptext" id="text{{child.id}}" name="reply" style="visibility:hidden"> <input type="hidden" id="hidden{{child.id}}" value={{child.id}} name="parentid"> <input type="hidden" value={{idusr}} name="idusr"> @@ -97,6 +110,9 @@ Current rating is {{ child.rating.get_rating }}<br/> <input type="button" class="commenteditor" id="{{ child.id }}" value="Add a Response"> <input type="button" class="commentsavecontent" id="save{{child.id}}" value="Save" onclick="saveclick(document.getElementById('hidden{{child.id}}').value)"> <input type="submit" class="postreply" id="submit{{child.id}}" value="Submit"> + + + <input type="checkbox" class="chkdel" id="chk{{child.id}}" name="del_comment" value="delete_comment"> <input type="submit" class="submitdelete" id="delete{{child.id}}" value="Delete" onclick="deleteclick(document.getElementById('hidden{{child.id}}').value)"> @@ -138,7 +154,7 @@ Current rating is {{ child.rating.get_rating }}<br/> {% endif %} <font style = "color:red;" size = 3>{{child.content}}</font> - <form method="post" action=".">{% csrf_token %} + <form method="post" name="response" action=".">{% csrf_token %} <input type="hidden" class="commentreptext" id="text{{child.id}}" name="reply" style="visibility:hidden"> <input type="hidden" id="hidden{{child.id}}" value={{child.id}} name="parentid"> <input type="hidden" value={{idusr}} name="idusr"> @@ -148,8 +164,25 @@ Current rating is {{ child.rating.get_rating }}<br/> <input type="button" class="commenteditor" id="{{ child.id }}" value="Add a Response"> <input type="button" class="commentsavecontent" id="save{{child.id}}" value="Save" onclick="saveclick(document.getElementById('hidden{{child.id}}').value)"> <input type="submit" class="postreply" id="submit{{child.id}}" value="Submit"> + +{% check_user_admin idusr as admin_usercheck %} +{% if admin_usercheck %} + <input type="checkbox" class="chkdel" id="chk{{child.id}}" name="del_comment" value="delete_comment"> - <input type="submit" class="submitdelete" id="delete{{child.id}}" value="Delete" onclick="deleteclick(document.getElementById('hidden{{child.id}}').value)"> <br/> + <input type="submit" class="submitdelete" id="delete{{child.id}}" value="Delete" onclick="deleteclick(document.getElementById('hidden{{child.id}}').value)"> + +<br/> +{% endif %} +{% for author in child.authors.all %} +{% ifequal author.id idusr %} + + <input type="checkbox" class="chkdel" id="chk{{child.id}}" name="del_comment" value="delete_comment"> + <input type="submit" class="submitdelete" id="delete{{child.id}}" value="Delete" onclick="deleteclick(document.getElementById('hidden{{child.id}}').value)"> + +<br/> +{% endifequal %} +{% endfor %} + <div class="rating"> Rate this response </br> <input name="star1" type="radio" value=1 class="star"/> diff --git a/gstudio/templates/gstudio/video.html b/gstudio/templates/gstudio/video.html index c50b6df..201a335 100644 --- a/gstudio/templates/gstudio/video.html +++ b/gstudio/templates/gstudio/video.html @@ -80,7 +80,7 @@ background: green; {% autopaginate vids 10 %} -<h2 style="color: teal;">Video Library</h2> +<h2 style="">Video Library</h2> <br/> <div id="upperdiv"> @@ -173,9 +173,9 @@ document.getElementById('headvideo').style.visibility="visible"; {% if fav %} -<h2 style="color: teal;">Favourite Videos</h2><br/> +<h2 style="">Favourite Videos</h2><br/> {% else %} -<h2 style="color: teal;">List of Videos in the library </h2><br/> +<h2 style="">List of Videos in the library </h2><br/> {% endif %} <div id="listvideo"> @@ -195,7 +195,7 @@ document.getElementById("divvideo").setAttribute("id","div2"); } </script> -<font size="4">{% if video.title %}{{video.title}}{% else %}{{video.altnames}}{% endif %}</font> +<font size="4">{% if video.title %}{{video.title}}{% else %}{{video.altnames}}{% endif %}[ <a href="{{ video.get_absolute_url }}" title="{{ video.title }}" rel="bookmark">Show Graph</a> ]</font> <form enctype="multipart/form-data" method="post" action="" target="_blank"> {% csrf_token %} <input type="hidden" name = "full" value ={{video.slug}}> diff --git a/gstudio/templates/metadashboard/grpdashboard.html b/gstudio/templates/metadashboard/grpdashboard.html index 6ad36ac..8a1b335 100644 --- a/gstudio/templates/metadashboard/grpdashboard.html +++ b/gstudio/templates/metadashboard/grpdashboard.html @@ -4,7 +4,7 @@ {% load adminmedia grp_tags %} {% load i18n comments gstudio_tags %} {% load tagging_tags comments i18n %} - + {% block title %}{{meet_ob.title}}{% endblock %} {% block content %} <script src="http://code.jquery.com/jquery-latest.js"></script> <script type="text/javascript" > @@ -21,6 +21,120 @@ $(".submitresponse").hide(); $("#editthreadsave").hide(); }); var i=0; +var availableTags = []; +$.merge(availableTags, {% get_add_tag %}) + +function notifedtdel() +{ + + +urlnot="/gstudio/group/notify/"+activity+"/"+not_obj+"/{{user.id}}" + +subactivity(urlnot,activity); +} + +function subactivity(urlnot,activity) +{ + if(activity=='added_response') + { + submitform(activity); + } + + if (activity != 'undefined') + { + + $.ajax({ + url: urlnot, + type:'GET', + success: function(){ + + submitform(activity); + } + }); + } + else + { + submitform(activity); + } +} + +function submitform(activity) +{ + + +if (activity=='edited_thread') +{ + +document.forms["threadform"].submit(); +} +if(activity=='edited_twist') +{ + + +$("#topicsubmit"+submtobj).trigger('click'); +} +if(activity=='added_response') +{ + +$("#topicsubmit"+submtobj).trigger('click'); +} +if(activity=='deleted_response') +{ + + document.forms["response"].submit(); +} +} + + + + + + + + +function Subscribeuser() +{ +if (confirm("{{user.username}},Do you want to Subscribe to get all alerts in your mail for this page")) +{ + +url = "/gstudio/group/notify/{{meet_ob.id}}/{{user.id}}" + +<!--alert(url);--> +<!-- $.get(url,function(){ --> +<!-- alert("notified user"); --> +<!-- }); --> +$.ajax({ + url: '/gstudio/group/notify/{{meet_ob.id}}/{{user.id}}', + success: function(){ + <!--alert('notified user');--> + } + }); +$("#sub2").val("UnSubscribe"); +$("#sub2").attr("onclick","UnSubscribeuser()"); +} +} + + + + + +function UnSubscribeuser() +{ +url = "/gstudio/group/notify/unsubscribe/{{meet_ob.id}}/{{user.id}}" + +<!--alert(url);--> +<!-- $.get(url,function(){ --> +<!-- alert("notified user"); --> +<!-- }); --> +$.ajax({ + url: '/gstudio/group/notify/unsubscribe/{{meet_ob.id}}/{{user.id}}', + success: function(){ + <!--alert('notified user');--> + } + }); +$("#sub1").val("Subscribe"); +$("#sub1").attr("onclick","Subscribeuser()"); +} </script> <style type="text/css"> #div2 @@ -36,17 +150,25 @@ var i=0; <!-- <input type="button" value="Home" onClick="location.href=parseURL('/gstudio/user/{{user.username}}');"> <h>Hello Welcome to the {{ meet_ob.title }}</h></br> --> - <font size ="3" > {{admin_m.username}}'s initial twist to the thread</font> - <!-- <h>You are logged in as <font color="blue">{{user.username}}</font></h> --> - </br> -<h2>{{meet_ob.title}}</h2> +<h2 style="line-height:1">{{meet_ob.title}}{% if user.is_authenticated %} {% edit_title meet_ob.id meet_ob.title %} {% endif %}</h2> +<font size ="3" > {{admin_m.username}}'s initial twist to the thread [ <a href="{{ meet_ob.get_absolute_url }}" title="{{ meet_ob.title }}" rel="bookmark">Show Graph</a> ] </font> +</br> +{% if user.is_authenticated %} + +{% check_subscribe meet_ob user as subscribe %} +{%if subscribe %} +<input type="button" id="sub1" value="UnSubscribe" name="edittitle" onclick="UnSubscribeuser()"> +{% else %} +<input type="button" id="sub2" value="Subscribe" name="edittitle" onclick="Subscribeuser()"> +{% endif %} +{% endif %} {% with meet_ob.html_content|safe as meet_ob_content %} <font size ="3" >{{meet_ob_content}} </font> {% endwith %} {% if user.is_authenticated %} -<form method="post" action=".">{% csrf_token %} +<form method="post" name="threadform" action=".">{% csrf_token %} <input type="hidden" id="threadid" value={{meet_ob.id}} style="visibility:hidden;" name="editiden"> <input type="hidden" id="threadedit{{meet_ob.id}}" style="visibility:hidden;" name="editval"> <input type="hidden" id="threadcontent{{meet_ob.id}}" value="{{meet_ob.content_org}}" style="visibility:hidden;" name="editcont"> @@ -54,7 +176,8 @@ var i=0; <br/> <input type="button" id="threadedit" value="Edit"> <input type="button" id="editthreadsave" value="Save"> -<input type="submit" id="subeditresp" value=Submit" style="visibility:hidden;"></br> +<input type="button" id="subeditresp" value="Submit" style="visibility:hidden;"> + </form> @@ -77,28 +200,13 @@ var i=0; {% endif %} </br> {% endif %} +{% endif %} - <form method="post" action="">{% csrf_token %} - <input type="hidden" name="docid" value={{meet_ob.id}}> - <input type="text" class="tagtext" value="" name="texttags" /> - <input type="submit" class="tag" value="Add Tags" name="addtags" /> - </form> - - <br> - <div class="tags"> - <p class="gbobject-tags span-16 last"> - <strong>{% trans "Tags" %}</strong> : - {% tags_for_object ot as tag_list %} - {% for tag in tag_list %} - <a href="{% url objectapp_tag_detail tag %}" - title="Tag {{ tag }}" rel="tag">{{ tag }}</a> - {% empty %} - <span>{% trans "No tags" %}</span> - {% endfor %} - </p> - </div> +<!--ADDING AND DISPLAY TAG--> +{% addtag ot meet_ob.id user %} <br/> +{% if user.is_authenticated %} <form method="post" action=".">{% csrf_token %} <!-- <input type="hidden" value="{{meet_ob.id}}" name = "meetid">--> <input type="button" id="twistaddbtn" value="Add your twist" onClick="location.href=parseURL('topicadd1/{{meet_ob.id}}');"> @@ -119,7 +227,7 @@ var i=0; --> </br> {% autoescape off %} {% for each in topic %} - <h4><font size="5" >{{each.title}}</font></h4> + <h4><font size="5" >{{each.title}}</font> [ <a href="{{ each.get_absolute_url }}" title="{{ each.title }}" rel="bookmark">Show Graph</a> ]</h4> Posted on : {{each.creation_date}} by : {% for author in each.authors.all %} @@ -141,10 +249,11 @@ var i=0; <input type="hidden" value="{{each.id}}" name = "iden"> <input type="hidden" value="{{user.id}}" name = "idusr"> <br/> {% if user.is_authenticated %} + <input type="button" class="editcontent" name="{{each.content_org}}" id="{{each.id}}" value="Edit"> <input type="button" class="editor" id="{{ each.id }}" value="Add Response"/> <input type="hidden" class="editval" id="edit{{each.id}}" name="edit" value="empty"> <input type="button" class="savecontent" id="save{{ each.id }}" value="Save" onclick="topicsaveclick(document.getElementById('hidden{{ each.id }}').value)"><br/> - <input type="button" class="editcontent" name="{{each.content_org}}" id="{{each.id}}" value="Edit"> + <input type="submit" class="submitresponse" id="topicsubmit{{ each.id }}" value="Submit"></br> {% endif %} {% ifequal user.id admin_id %} @@ -171,27 +280,10 @@ var i=0; <input type="submit" value="Rate it!!!"> --> </form> </div> - {% if user.is_authenticated %} - <form method="post" action="">{% csrf_token %} - <input type="hidden" name="docid" value={{each.id}}> - <input type="text" class="tagtext" value="" name="texttags" /> - <input type="submit" class="tag" value="Add Tags" name="addtags" /> - </form> - - <br> - <div class="tags"> - <p class="gbobject-tags span-16 last"> - <strong>{% trans "Tags" %}</strong> : - {% tags_for_object each as tag_list %} - {% for tag in tag_list %} - <a href="{% url objectapp_tag_detail tag %}" - title="Tag {{ tag }}" rel="tag">{{ tag }}</a> - {% empty %} - <span>{% trans "No tags" %}</span> - {% endfor %} - </p> - </div> - {% endif %} + +<!--ADDING AND DISPLAY TAG--> +{% addtag each each.id user %} + <br/> <p> Responses : diff --git a/gstudio/templates/metadashboard/pgedashboard.html b/gstudio/templates/metadashboard/pgedashboard.html index 1e48755..e20b93a 100644 --- a/gstudio/templates/metadashboard/pgedashboard.html +++ b/gstudio/templates/metadashboard/pgedashboard.html @@ -5,7 +5,7 @@ {% load pagination_tags %} {% load i18n objectapp_tags %} {% load tagging_tags comments i18n %} - +{% block title %}{{ page_ob.title }}{% endblock %} <!-- {% load i18n %} --> {% block content %} <style> @@ -14,6 +14,8 @@ </style> <script src="http://code.jquery.com/jquery-latest.js"></script> <script type="text/javascript" > +var availableTags = []; +$.merge(availableTags, {% get_add_tag %}) $(window).load(function() { $("#content").css({ "width": "1000px",});}); @@ -43,11 +45,11 @@ $(".savesubsec1").hide(); {% autopaginate section 1 %} <!-- <input type="button" value="Home" onClick="location.href=parseURL('/gstudio/user/wikipage/{{user.username}}');"> --> - <h1>{{ page_ob.title }}</h1> + <h1 style="line-height:0.5">{{ page_ob.title }} {% if user.is_authenticated %} {% edit_title page_ob.id page_ob.title %} {% endif %}</h1> Wikipage posted on : {{page_ob.creation_date}} by {% for author in page_ob.authors.all %} {{author}} - {% endfor %}</br></br> + {% endfor %} [ <a href="{{ page_ob.get_absolute_url }}" title="{{ page_ob.title }}" rel="bookmark">Show Graph</a> ] </br></br> {% with page_ob.html_content|safe as page_ob_content %} <font size="3"> @@ -64,37 +66,51 @@ $(".savesubsec1").hide(); <input type="button" class="savepagecontent" name="{{page_ob.id}}" value="Save"/></br> <input type="submit" class="pagedit" value="Submit"/> </form> + {% endif %} + <!-- Add relations for a page --> + + {% add_res_relation page_ob %} + <br/> - <form method="post" action="">{% csrf_token %} - <input type="hidden" name="docid" value={{page_ob.id}}> - <input type="text" class="tagtext" value="" name="texttags" /> - <input type="submit" class="tag" value="Add Tags" name="addtags" /> - </form> - {% endif %} - <br> - <div class="tags"> - <p class="gbobject-tags span-16 last"> - <strong>{% trans "Tags" %}</strong> : - {% tags_for_object ot as tag_list %} - {% for tag in tag_list %} - <a href="{% url objectapp_tag_detail tag %}" - title="Tag {{ tag }}" rel="tag">{{ tag }}</a> - {% empty %} - <span>{% trans "No tags" %}</span> - {% endfor %} - </p> - </div> - </br> + + <!--ADDING PRIOR PAGE--> + {% addpriorpost page_ob.id user %} + <!--ADDING AND DISPLAY TAG--> + {% addtag ot page_ob.id user %} + + {% ifequal document.rating.get_rating 0 %} + + <font color = 'black'>Current rating is: <font > No rating yet </font></font> + {% else %} + <font color = 'black'>Average Current rating is: </font><h4>{{ page_ob.rating.get_rating }}</h4> + <font color = 'black'>Total Number of votes is: <font ><b>{{ page_ob.rating_votes }}</b></font> </font> + {% endifequal %} + <br> + {% if user.is_authenticated %} + <form method="post" action=""> + {% csrf_token %} + <input name="star1" type="radio" value=1 class="star"/> + <input name="star1" type="radio" value=2 class="star"/> + <input name="star1" type="radio" value=3 class="star"/> + <input name="star1" type="radio" value=4 class="star"/> + <input name="star1" type="radio" value=5 class="star"/> + </br> + <input type="hidden" name="iden" value={{page_ob.id}}> + <input type="submit" value="Rate it!!!"> + </form><br> + {% endif %} + + {% if user.is_authenticated %} <form method="post" action=".">{% csrf_token %} - <input type="button" id="newsection1" value="Add a new Section" onClick="location.href=parseURL('sectionadd1/{{page_ob.id}}');"> + <!--<input type="button" id="newsection1" value="Add a new Section" onClick="location.href=parseURL('sectionadd1/{{page_ob.id}}');">--> <input type="hidden" id="pageid1" value="{{page_ob.id}}"/> </form> {% endif %} </br> {% autoescape off %} {% for each in section %} - <h4><font size="6">{{each.title}}</h4></font> + <h4><font size="6">{{each.title}} [ <a href="{{ each.get_absolute_url }}" title="{{ each.title }}" rel="bookmark">Show Graph</a> ]</h4></font> Posted on : {{each.creation_date}} by {% for author in each.authors.all %} {{author}} @@ -121,33 +137,21 @@ $(".savesubsec1").hide(); <input type="button" class="saveseccontent" name="{{each.id}}" value="Save"/> <input type="submit" class="submitresponse" value="Submit"/> </form> - <form method="post" action="">{% csrf_token %} - <input type="hidden" name="docid" value={{each.id}}> - <input type="text" class="tagtext" value="" name="texttags" /> - <input type="submit" class="tag" value="Add Tags" name="addtags" /> - </form> {% endif %} - <br> - <div class="tags"> - <p class="gbobject-tags span-16 last"> - <strong>{% trans "Tags" %}</strong> : - {% tags_for_object each as tag_list %} - {% for tag in tag_list %} - <a href="{% url objectapp_tag_detail tag %}" - title="Tag {{ tag }}" rel="tag">{{ tag }}</a> - {% empty %} - <span>{% trans "No tags" %}</span> - {% endfor %} - </p> - </div> + <br> + + <!--ADDING AND DISPLAY TAG--> + {% addtag each each.id user %} + + {% if user.is_authenticated %} <form method="post" action=".">{% csrf_token %}<br/> - Create Subsection: + <!-- Create Subsection:--> <input type="hidden" name = "replytosection" class="reptext" style="visibility:hidden;"/></br> <input type="hidden" value="{{each.id}}" name = "iden"> <input type="hidden" value="{{user.username}}" name ="usr"> <input type="hidden" value="{{user.id}}" name = "idusr"> - <input type="button" value="Create Subsection" class="createsubsection"> + <!--<input type="button" value="Create Subsection" class="createsubsection">--> <input type="hidden" value="empty" name = "edit" class="editval"/> <input type="button" value="save" class="savesubsec" name="{{each.id}}"> diff --git a/gstudio/templates/metadashboard/wikidashboard.html b/gstudio/templates/metadashboard/wikidashboard.html index 1af53ce..75382f2 100644 --- a/gstudio/templates/metadashboard/wikidashboard.html +++ b/gstudio/templates/metadashboard/wikidashboard.html @@ -1,6 +1,7 @@ {% extends "gstudio/base.html" %} {% load gstudio_tags %} {% load i18n %} +{% block title %}Wikipages{% endblock %} {% block content %} @@ -8,6 +9,10 @@ <p> Here is a list of all the pages:</p> </br> + {% if user.is_authenticated %} + <input type="button" value="Create a new page" onClick="location.href=parseURL('pageadd/');"> + <br/> + {% endif %} {% for each in pages.member_systems.all %} <a href="/gstudio/page/gnowsys-page/{{each.id}}/">{{each.title}}</a></br> {% endfor %} diff --git a/gstudio/templatetags/gstudio_tags.py b/gstudio/templatetags/gstudio_tags.py index 0ead510..2071f15 100644 --- a/gstudio/templatetags/gstudio_tags.py +++ b/gstudio/templatetags/gstudio_tags.py @@ -60,6 +60,9 @@ VECTORS_FACTORY = lambda: VectorBuilder(Nodetype.published.all(), CACHE_NODETYPES_RELATED = {} + + + @register.inclusion_tag('gstudio/tags/dummy.html') def get_metatypes(template='gstudio/tags/metatypes.html'): """Return the metatypes""" @@ -425,6 +428,17 @@ def check_release(meeting): return var @register.assignment_tag +def check_subscribe(meeting,user): + var = check_subscribe_or_not(meeting,user) + return var + +@register.assignment_tag +def check_user_admin(userid): + var=check_usr_admin(userid) + return var + + +@register.assignment_tag def get_static_url(): var = os.path.join(os.path.dirname(__file__),STATIC_URL) return var @@ -434,3 +448,82 @@ def get_factory_looms(): fs = [] fs = get_factory_loom_OTs() return fs + +@register.assignment_tag +def put_home_content(): + var = get_home_content() + return var + +@register.assignment_tag +def put_more_content(): + var = get_more_content() + return var + +@register.assignment_tag +def put_home_title(): + var = get_home_title() + return var + +@register.inclusion_tag('gstudio/addreln.html') +def add_res_relation(meetingob): + template='gstudio/addreln.html' + print "meetob=",meetingob + return {'template':template,'meetingob':meetingob} + +@register.simple_tag +def get_available_rts(): + listrts=[] + for each in Relationtype.objects.all(): + s=each.title + listrts.append(str(s)) + return str(listrts) + +@register.simple_tag +def get_available_objects(): + listsubjs=[] + for each in Gbobject.objects.all(): + s=each.title + listsubjs.append(str(s)) + return str(listsubjs) + + +@register.inclusion_tag('gstudio/edittitle.html') +def edit_title(objectid,objecttitle): + template='gstudio/edititle.html' + return {'template':template,'objectid':objectid,'objecttitle':objecttitle} + +@register.simple_tag +def get_add_tag(): + listtag = [] + tag = Tag.objects.all() + for each in tag: + listtag.append(each.__str__()) + return str(listtag) + + +@register.inclusion_tag('gstudio/priorpost.html') +def addpriorpost(objectid,user): + template='gstudio/priorpost.html' + gbobject = Gbobject.objects.get(id=objectid) + priorgbobject = gbobject.prior_nodes.all() + posteriorgbobject = gbobject.posterior_nodes.all() + return {'template':template,'objectid':objectid,'priorgbobject':priorgbobject,'posteriorgbobject':posteriorgbobject,'user':user} + +@register.inclusion_tag('gstudio/addingtag.html') +def addtag(viewtag,objectid,user): + template='gstudio/addingtag.html' + return {'viewtag':viewtag,'objectid':objectid,'user':user} + +@register.simple_tag +def get_pri_post_page(): + listobject = [] + gbobject = Gbobject.objects.all() + for each in gbobject: + listobject.append(each.__str__()) + return str(listobject) + +@register.inclusion_tag("gstudio/templatetags/comment_security_hash.html") +def comment_security_hash(blogentry,opts): + targ='%s:%s'%(ContentType.objects.get_for_model(blogentry).id,blogentry.id) + return {"hash":Comment.objects.get_security_hash(opts,'','',targ)} + diff --git a/gstudio/urls/__init__.py b/gstudio/urls/__init__.py index 8a4e015..b236cde 100644 --- a/gstudio/urls/__init__.py +++ b/gstudio/urls/__init__.py @@ -51,6 +51,7 @@ urlpatterns = patterns( url(r'pageadd/', include('gstudio.urls.pageadd')), url(r'group/',include('gstudio.urls.group')), url(r'page/',include('gstudio.urls.page')), + url(r'^resources/addreln/',include('gstudio.urls.addreln')), url(r'^resources/images/',include('gstudio.urls.image')), url(r'^resources/videos/',include('gstudio.urls.video')), url(r'^resources/documents',include('gstudio.urls.docu')), diff --git a/gstudio/urls/addreln.py b/gstudio/urls/addreln.py new file mode 100644 index 0000000..edcb125 --- /dev/null +++ b/gstudio/urls/addreln.py @@ -0,0 +1,22 @@ +# Copyright (c) 2011, 2012 Free Software Foundation +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + + +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns + +urlpatterns = patterns('gstudio.views.addreln', + url(r'^form/(\d+)/$','addrelnform',name = 'addrelfrm'), + url(r'^thread/(\d+)/$', 'addreln',name='addreln'), + ) diff --git a/gstudio/urls/image.py b/gstudio/urls/image.py index f088b29..319999c 100644 --- a/gstudio/urls/image.py +++ b/gstudio/urls/image.py @@ -21,5 +21,9 @@ from django.conf.urls.defaults import patterns urlpatterns = patterns('gstudio.views.image', url(r'^$', 'image', name='image'), - url(r'show/(\d+)/$','show',name='showimage'), + url(r'show/(\d+)/$','show',name='showimage'), + url(r'edittitle/$','edit_title',name='edittitle'), + url(r'addpriorpost/$','addpriorpost',name='addpriorpost'), + url(r'addtag/$','addtag',name='addtag'), + url(r'deletetag/$','deletetag',name='deletetag'), ) diff --git a/gstudio/views/addreln.py b/gstudio/views/addreln.py new file mode 100644 index 0000000..763e47e --- /dev/null +++ b/gstudio/views/addreln.py @@ -0,0 +1,84 @@ +# Copyright (c) 2011, 2012 Free Software Foundation +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from django.http import HttpResponse +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import RequestContext +from gstudio.models import Relation,Relationtype +from objectapp.models import System,Gbobject +from gstudio.models import NID +from django.template.loader import get_template +from django.template import Context + + +def addrelnform(request,meetob): + title="" + if request.method == 'POST' : + title=request.POST.get("reln","") + inverse =request.POST.get("obj","") + slug=request.POST.get("slug","") + + if request.method == 'GET' : + title=request.GET.get("reln","") + inverse=request.GET.get("obj","") + slug=request.GET.get("slug","") + if title: + ob=Relationtype() + ob.title=title + ob.inverse=inverse + ob.slug=slug + left_subjecttype=NID.objects.get(title='Page') + left_applicable_nodetypes =unicode('OT') + right_subjecttype=NID.objects.get(title='Page') + right_applicable_nodetypes =unicode('OT') + ob.left_subjecttype=left_subjecttype + ob.left_applicable_nodetypes=left_applicable_nodetypes + ob.right_subjecttype=right_subjecttype + ob.right_applicable_nodetypes=right_applicable_nodetypes + ob.save() + + variables = RequestContext(request,{'meetob':meetob}) + template = "gstudio/addrelnform.html" + return render_to_response(template, variables) + + + +def addreln(request,meetob): + a=Relation() + try: + if request.method == 'GET' : + relntype=request.GET['relnobj'] + obobj=request.GET['obobject'] + rt=Relationtype.objects.filter(title=relntype) + a.left_subject=Gbobject.objects.get(id=meetob) + obt=Gbobject.objects.filter(title=obobj) + rt=Relationtype.objects.filter(title=relntype) + if rt: + a.relationtype=Relationtype.objects.get(title=relntype) + if obt: + obt=Gbobject.objects.get(title=obobj) + a.right_subject=obt + a.save() + j=System.objects.get(id=meetob) + p=j.get_view_url + if not obobj: + return HttpResponseRedirect(p) + else: + t = get_template('gstudio/addrelnform_refresh.html') + html = t.render(Context({'meetobj':j})) + return HttpResponse(html) + except: + t = get_template('gstudio/addrelnform_refresh.html') + html = t.render(Context({'meetingobj':j})) + return HttpResponse(html) diff --git a/gstudio/views/image.py b/gstudio/views/image.py index ec1b2fc..919b291 100644 --- a/gstudio/views/image.py +++ b/gstudio/views/image.py @@ -16,6 +16,7 @@ from django.http import HttpResponse +from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from demo.settings import * @@ -27,6 +28,8 @@ from PIL import Image import glob, os import hashlib from django.template.defaultfilters import slugify +from django.template.loader import get_template +from django.template import Context size = 128, 128 report = "true" @@ -49,11 +52,25 @@ def image(request): addtags = request.POST.get("addtags","") texttags = unicode(request.POST.get("texttags","")) contenttext = request.POST.get("contenttext","") + fav=request.POST.get("fav","") if show != "": i=Gbobject.objects.get(id=fulid) vars=RequestContext(request,{'image':i}) template="gstudio/fullscreen.html" return render_to_response(template, vars) + if fav != "" : + list1=[] + t=Gbobject.objects.filter(title=user+"image") + if t: + t=Gbobject.objects.get(title=user+"image") + if t.get_relations(): + for each in t.get_nbh['has_favourite']: + d=each.right_subject_id + x=Gbobject.objects.get(id=d) + list1.append(x) + variables = RequestContext(request,{'images':list1,'fav':fav}) + template = "gstudio/image.html" + return render_to_response(template, variables) if rating : rate_it(int(imgid),request,int(rating)) if delete != "": @@ -213,8 +230,9 @@ def create_object(f,log,title,content,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -239,6 +257,10 @@ def show(request,imageid): addtags = request.POST.get("addtags","") texttags = unicode(request.POST.get("texttags","")) contenttext = unicode(request.POST.get("contenttext","")) + favid=request.POST.get("favid","") + favusr=request.POST.get("favusr","") + removefavid=request.POST.get("removefavid", "") + titlecontenttext=request.POST.get("titlecontenttext", "") if rating : rate_it(int(imgid),request,int(rating)) if addtags != "": @@ -247,6 +269,37 @@ def show(request,imageid): i.save() if contenttext !="": edit_description(imgid,contenttext,str(request.user)) + if favid !="": + e=0 + r = Objecttype.objects.get(title="user") + for each in r.get_nbh['contatins_members']: + if favusr+"image" == each.title: + e=1 + if e ==0 : + t=Gbobject() + t.title=favusr+"image" + t.slug=favusr+"image" + t.content=' ' + t.satus=2 + t.save() + t.objecttypes.add(Objecttype.objects.get(title="user")) + t.save() + t=Gbobject.objects.get(title=favusr+"image") + rel=Relation() + rt=Relationtype.objects.get(title="has_favourite") + rel.relationtype_id=rt.id + f1=Gbobject.objects.get(id=favid) + rel.left_subject_id=t.id + rel.right_subject_id=f1.id + rel.save() + t.save() + if removefavid !="": + objects = Gbobject.objects.get(id=removefavid) + objects.get_relations()['is_favourite_of'][0].delete() + if titlecontenttext !="": + new_ob = Gbobject.objects.get(id=int(imgid)) + new_ob.title = titlecontenttext + new_ob.save() gbobject = Gbobject.objects.get(id=imageid) vars=RequestContext(request,{'image':gbobject}) template="gstudio/fullscreen.html" @@ -279,8 +332,9 @@ def edit_description(sec_id,title,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -298,3 +352,66 @@ def md5Checksum(filePath): m.update(data) return m.hexdigest() +def edit_title(request): + print "post" + if request.method == "GET": + print "iin get" + title=request.GET['title'] + titleid=request.GET['titleid'] + nid=NID.objects.get(id=titleid) + nid.title=title + nid.save() + return HttpResponseRedirect("/gstudio/resources/images/") + +def addpriorpost(request): + titleid="" + gbid1="" + if request.method == "GET": + print "in get" + title=request.GET['title'] + titleid=request.GET['titleid'] + gbid1=Gbobject.objects.get(id=titleid) + gbid2=Gbobject.objects.get(title=title) + gbid1.prior_nodes.add(gbid2) + gbid1.save() + gbid2.posterior_nodes.add(gbid1) + gbid2.save() + gbid1=Gbobject.objects.get(id=titleid) + priorgbobject = gbid1.prior_nodes.all() + posteriorgbobject = gbid1.posterior_nodes.all() + t = get_template('gstudio/repriorpost.html') + html = t.render(Context({'priorgbobject':priorgbobject,'posteriorgbobject':posteriorgbobject,'objectid':titleid,'optionpriorpost':"priorpost"})) + return HttpResponse(html) + #return HttpResponseRedirect("gstudio/resources/image/") +def addtag(request): + i="" + if request.method == "GET": + objectid=request.GET['objectid'] + data=request.GET['data'] + i=Gbobject.objects.get(id=objectid) + i.tags=i.tags+ ","+(data) + i.save() + i=Gbobject.objects.get(id=objectid) + print i,"in addtag" + t=get_template('gstudio/repriorpost.html') + html = t.render(RequestContext(request,{'viewtag':i,'optiontag':"tag","objectid":objectid})) + return HttpResponse(html) + +def deletetag(request): + i="" + objectid="" + if request.method =="GET": + objectid=request.GET['objectid'] + data=request.GET['data'] + i=Gbobject.objects.get(id=objectid) + delval=i.tags.replace(data+",","") + delval1=delval.replace(data,"") + i.tags=delval1 + i.save() + i=Gbobject.objects.get(id=objectid) + t=get_template('gstudio/repriorpost.html') + html=t.render(RequestContext(request,{'viewtag':i,'optiontag':"tag","objectid":objectid})) + return HttpResponse(html) + + + diff --git a/gstudio/views/video.py b/gstudio/views/video.py index 6eb86d9..a3bf12f 100644 --- a/gstudio/views/video.py +++ b/gstudio/views/video.py @@ -82,11 +82,14 @@ def video(request): return render_to_response(template,variables) if fav != "" : list1=[] - t=Gbobject.objects.get(title=user+"video") - for each in t.get_nbh['has_favourite']: - d=each.right_subject_id - x=Gbobject.objects.get(id=d) - list1.append(x) + t=Gbobject.objects.filter(title=user+"video") + if t: + t=Gbobject.objects.get(title=user+"video") + if t.get_relations(): + for each in t.get_nbh['has_favourite']: + d=each.right_subject_id + x=Gbobject.objects.get(id=d) + list1.append(x) variables = RequestContext(request,{'vids':list1,'val':svid,'fav':fav}) template = "gstudio/video.html" return render_to_response(template, variables) @@ -230,8 +233,9 @@ def video(request): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -364,8 +368,9 @@ def video(request): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('div id="content">\n')]='<div id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() @@ -465,6 +470,7 @@ def show(request,videoid): contenttext = request.POST.get("contenttext","") contenttext = unicode(request.POST.get("contenttext","")) titlecontenttext = request.POST.get("titlecontenttext") + removefavid = request.POST.get("removefavid","") if rating : rate_it(int(vidid),request,int(rating)) @@ -492,7 +498,9 @@ def show(request,videoid): rel.right_subject_id=f1.id rel.save() t.save() - + if removefavid !="": + objects = Gbobject.objects.get(id=removefavid) + objects.get_relations()['is_favourite_of'][0].delete() if addtags != "": i=Gbobject.objects.get(id=vidid) @@ -500,12 +508,18 @@ def show(request,videoid): i.save() if contenttext !="": edit_description(vidid,contenttext,str(request.user)) - if titlecontenttext !="": - new_ob = Gbobject.objects.get(id=int(vidid)) - new_ob.title = titlecontenttext - new_ob.save() + gbobject = Gbobject.objects.get(id=videoid) - vars=RequestContext(request,{'video':gbobject}) + relation = "" + if gbobject.get_relations(): + if gbobject.get_relations()['is_favourite_of']: + rel= gbobject.get_relations()['is_favourite_of'][0] + print rel + reluser = rel._left_subjecy_cache.title + if str(reluser) == str(request.user)+str("video"): + relation = "rel" + + vars=RequestContext(request,{'video':gbobject,'relation':relation}) template="gstudio/transcript.html" return render_to_response(template,vars) @@ -537,8 +551,9 @@ def edit_description(sec_id,title,usr): output = stdout.read() data = open(os.path.join(FILE_URL,fname+html)) data1 = data.readlines() - data2 = data1[72:] - data3 = data2[:-3] + data2 = data1[107:] + dataa = data2[data2.index('<div id="content">\n')]='<idv id=" "\n' + data3 = data2[:-6] newdata="" for line in data3: newdata += line.lstrip() diff --git a/notification/.gitignore b/notification/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/notification/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/notification/__init__.py b/notification/__init__.py new file mode 100644 index 0000000..ced9e9a --- /dev/null +++ b/notification/__init__.py @@ -0,0 +1,16 @@ +VERSION = (0, 2, 0, "f") # following PEP 386 +DEV_N = None + + +def get_version(): + version = "%s.%s" % (VERSION[0], VERSION[1]) + if VERSION[2]: + version = "%s.%s" % (version, VERSION[2]) + if VERSION[3] != "f": + version = "%s%s%s" % (version, VERSION[3], VERSION[4]) + if DEV_N: + version = "%s.dev%s" % (version, DEV_N) + return version + + +__version__ = get_version() diff --git a/notification/admin.py b/notification/admin.py new file mode 100644 index 0000000..e754e87 --- /dev/null +++ b/notification/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin + +from notification.models import NoticeType, NoticeSetting, Notice, ObservedItem, NoticeQueueBatch + + +class NoticeTypeAdmin(admin.ModelAdmin): + list_display = ["label", "display", "description", "default"] + + +class NoticeSettingAdmin(admin.ModelAdmin): + list_display = ["id", "user", "notice_type", "medium", "send"] + + +class NoticeAdmin(admin.ModelAdmin): + list_display = ["message", "recipient", "sender", "notice_type", "added", "unseen", "archived"] + + +admin.site.register(NoticeQueueBatch) +admin.site.register(NoticeType, NoticeTypeAdmin) +admin.site.register(NoticeSetting, NoticeSettingAdmin) +admin.site.register(Notice, NoticeAdmin) +admin.site.register(ObservedItem) diff --git a/notification/atomformat.py b/notification/atomformat.py new file mode 100644 index 0000000..a307edd --- /dev/null +++ b/notification/atomformat.py @@ -0,0 +1,547 @@ +# +# django-atompub by James Tauber <http://jtauber.com/> +# http://code.google.com/p/django-atompub/ +# An implementation of the Atom format and protocol for Django +# +# For instructions on how to use this module to generate Atom feeds, +# see http://code.google.com/p/django-atompub/wiki/UserGuide +# +# +# Copyright (c) 2007, James Tauber +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +from xml.sax.saxutils import XMLGenerator +from datetime import datetime + + +GENERATOR_TEXT = 'django-atompub' +GENERATOR_ATTR = { + 'uri': 'http://code.google.com/p/django-atompub/', + 'version': 'r33' +} + + + +## based on django.utils.xmlutils.SimplerXMLGenerator +class SimplerXMLGenerator(XMLGenerator): + def addQuickElement(self, name, contents=None, attrs=None): + "Convenience method for adding an element with no children" + if attrs is None: attrs = {} + self.startElement(name, attrs) + if contents is not None: + self.characters(contents) + self.endElement(name) + + + +## based on django.utils.feedgenerator.rfc3339_date +def rfc3339_date(date): + return date.strftime('%Y-%m-%dT%H:%M:%SZ') + + + +## based on django.utils.feedgenerator.get_tag_uri +def get_tag_uri(url, date): + "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id" + parts = urlparse.urlparse(url) + date_part = "" + if date is not None: + date_part = ",%s:" % date.strftime("%Y-%m-%d") + return "tag:%s%s%s/%s" % ( + parts.hostname, + date_part, + parts.path, + parts.fragment, + ) + + + +## based on django.contrib.syndication.feeds.Feed +class Feed(object): + + + VALIDATE = True + + + def __init__(self, slug, feed_url): + # @@@ slug and feed_url are not used yet + pass + + + def __get_dynamic_attr(self, attname, obj, default=None): + try: + attr = getattr(self, attname) + except AttributeError: + return default + if callable(attr): + # Check func_code.co_argcount rather than try/excepting the + # function and catching the TypeError, because something inside + # the function may raise the TypeError. This technique is more + # accurate. + if hasattr(attr, 'func_code'): + argcount = attr.func_code.co_argcount + else: + argcount = attr.__call__.func_code.co_argcount + if argcount == 2: # one argument is 'self' + return attr(obj) + else: + return attr() + return attr + + + def get_feed(self, extra_params=None): + + if extra_params: + try: + obj = self.get_object(extra_params.split('/')) + except (AttributeError, LookupError): + raise LookupError('Feed does not exist') + else: + obj = None + + feed = AtomFeed( + atom_id = self.__get_dynamic_attr('feed_id', obj), + title = self.__get_dynamic_attr('feed_title', obj), + updated = self.__get_dynamic_attr('feed_updated', obj), + icon = self.__get_dynamic_attr('feed_icon', obj), + logo = self.__get_dynamic_attr('feed_logo', obj), + rights = self.__get_dynamic_attr('feed_rights', obj), + subtitle = self.__get_dynamic_attr('feed_subtitle', obj), + authors = self.__get_dynamic_attr('feed_authors', obj, default=[]), + categories = self.__get_dynamic_attr('feed_categories', obj, default=[]), + contributors = self.__get_dynamic_attr('feed_contributors', obj, default=[]), + links = self.__get_dynamic_attr('feed_links', obj, default=[]), + extra_attrs = self.__get_dynamic_attr('feed_extra_attrs', obj), + hide_generator = self.__get_dynamic_attr('hide_generator', obj, default=False) + ) + + items = self.__get_dynamic_attr('items', obj) + if items is None: + raise LookupError('Feed has no items field') + + for item in items: + feed.add_item( + atom_id = self.__get_dynamic_attr('item_id', item), + title = self.__get_dynamic_attr('item_title', item), + updated = self.__get_dynamic_attr('item_updated', item), + content = self.__get_dynamic_attr('item_content', item), + published = self.__get_dynamic_attr('item_published', item), + rights = self.__get_dynamic_attr('item_rights', item), + source = self.__get_dynamic_attr('item_source', item), + summary = self.__get_dynamic_attr('item_summary', item), + authors = self.__get_dynamic_attr('item_authors', item, default=[]), + categories = self.__get_dynamic_attr('item_categories', item, default=[]), + contributors = self.__get_dynamic_attr('item_contributors', item, default=[]), + links = self.__get_dynamic_attr('item_links', item, default=[]), + extra_attrs = self.__get_dynamic_attr('item_extra_attrs', None, default={}), + ) + + if self.VALIDATE: + feed.validate() + return feed + + + +class ValidationError(Exception): + pass + + + +## based on django.utils.feedgenerator.SyndicationFeed and django.utils.feedgenerator.Atom1Feed +class AtomFeed(object): + + + mime_type = 'application/atom+xml' + ns = u'http://www.w3.org/2005/Atom' + + + def __init__(self, atom_id, title, updated=None, icon=None, logo=None, rights=None, subtitle=None, + authors=[], categories=[], contributors=[], links=[], extra_attrs={}, hide_generator=False): + if atom_id is None: + raise LookupError('Feed has no feed_id field') + if title is None: + raise LookupError('Feed has no feed_title field') + # if updated == None, we'll calculate it + self.feed = { + 'id': atom_id, + 'title': title, + 'updated': updated, + 'icon': icon, + 'logo': logo, + 'rights': rights, + 'subtitle': subtitle, + 'authors': authors, + 'categories': categories, + 'contributors': contributors, + 'links': links, + 'extra_attrs': extra_attrs, + 'hide_generator': hide_generator, + } + self.items = [] + + + def add_item(self, atom_id, title, updated, content=None, published=None, rights=None, source=None, summary=None, + authors=[], categories=[], contributors=[], links=[], extra_attrs={}): + if atom_id is None: + raise LookupError('Feed has no item_id method') + if title is None: + raise LookupError('Feed has no item_title method') + if updated is None: + raise LookupError('Feed has no item_updated method') + self.items.append({ + 'id': atom_id, + 'title': title, + 'updated': updated, + 'content': content, + 'published': published, + 'rights': rights, + 'source': source, + 'summary': summary, + 'authors': authors, + 'categories': categories, + 'contributors': contributors, + 'links': links, + 'extra_attrs': extra_attrs, + }) + + + def latest_updated(self): + """ + Returns the latest item's updated or the current time if there are no items. + """ + updates = [item['updated'] for item in self.items] + if len(updates) > 0: + updates.sort() + return updates[-1] + else: + return datetime.now() # @@@ really we should allow a feed to define its "start" for this case + + + def write_text_construct(self, handler, element_name, data): + if isinstance(data, tuple): + text_type, text = data + if text_type == 'xhtml': + handler.startElement(element_name, {'type': text_type}) + handler._write(text) # write unescaped -- it had better be well-formed XML + handler.endElement(element_name) + else: + handler.addQuickElement(element_name, text, {'type': text_type}) + else: + handler.addQuickElement(element_name, data) + + + def write_person_construct(self, handler, element_name, person): + handler.startElement(element_name, {}) + handler.addQuickElement(u'name', person['name']) + if 'uri' in person: + handler.addQuickElement(u'uri', person['uri']) + if 'email' in person: + handler.addQuickElement(u'email', person['email']) + handler.endElement(element_name) + + + def write_link_construct(self, handler, link): + if 'length' in link: + link['length'] = str(link['length']) + handler.addQuickElement(u'link', None, link) + + + def write_category_construct(self, handler, category): + handler.addQuickElement(u'category', None, category) + + + def write_source(self, handler, data): + handler.startElement(u'source', {}) + if data.get('id'): + handler.addQuickElement(u'id', data['id']) + if data.get('title'): + self.write_text_construct(handler, u'title', data['title']) + if data.get('subtitle'): + self.write_text_construct(handler, u'subtitle', data['subtitle']) + if data.get('icon'): + handler.addQuickElement(u'icon', data['icon']) + if data.get('logo'): + handler.addQuickElement(u'logo', data['logo']) + if data.get('updated'): + handler.addQuickElement(u'updated', rfc3339_date(data['updated'])) + for category in data.get('categories', []): + self.write_category_construct(handler, category) + for link in data.get('links', []): + self.write_link_construct(handler, link) + for author in data.get('authors', []): + self.write_person_construct(handler, u'author', author) + for contributor in data.get('contributors', []): + self.write_person_construct(handler, u'contributor', contributor) + if data.get('rights'): + self.write_text_construct(handler, u'rights', data['rights']) + handler.endElement(u'source') + + + def write_content(self, handler, data): + if isinstance(data, tuple): + content_dict, text = data + if content_dict.get('type') == 'xhtml': + handler.startElement(u'content', content_dict) + handler._write(text) # write unescaped -- it had better be well-formed XML + handler.endElement(u'content') + else: + handler.addQuickElement(u'content', text, content_dict) + else: + handler.addQuickElement(u'content', data) + + + def write(self, outfile, encoding): + handler = SimplerXMLGenerator(outfile, encoding) + handler.startDocument() + feed_attrs = {u'xmlns': self.ns} + if self.feed.get('extra_attrs'): + feed_attrs.update(self.feed['extra_attrs']) + handler.startElement(u'feed', feed_attrs) + handler.addQuickElement(u'id', self.feed['id']) + self.write_text_construct(handler, u'title', self.feed['title']) + if self.feed.get('subtitle'): + self.write_text_construct(handler, u'subtitle', self.feed['subtitle']) + if self.feed.get('icon'): + handler.addQuickElement(u'icon', self.feed['icon']) + if self.feed.get('logo'): + handler.addQuickElement(u'logo', self.feed['logo']) + if self.feed['updated']: + handler.addQuickElement(u'updated', rfc3339_date(self.feed['updated'])) + else: + handler.addQuickElement(u'updated', rfc3339_date(self.latest_updated())) + for category in self.feed['categories']: + self.write_category_construct(handler, category) + for link in self.feed['links']: + self.write_link_construct(handler, link) + for author in self.feed['authors']: + self.write_person_construct(handler, u'author', author) + for contributor in self.feed['contributors']: + self.write_person_construct(handler, u'contributor', contributor) + if self.feed.get('rights'): + self.write_text_construct(handler, u'rights', self.feed['rights']) + if not self.feed.get('hide_generator'): + handler.addQuickElement(u'generator', GENERATOR_TEXT, GENERATOR_ATTR) + + self.write_items(handler) + + handler.endElement(u'feed') + + + def write_items(self, handler): + for item in self.items: + entry_attrs = item.get('extra_attrs', {}) + handler.startElement(u'entry', entry_attrs) + + handler.addQuickElement(u'id', item['id']) + self.write_text_construct(handler, u'title', item['title']) + handler.addQuickElement(u'updated', rfc3339_date(item['updated'])) + if item.get('published'): + handler.addQuickElement(u'published', rfc3339_date(item['published'])) + if item.get('rights'): + self.write_text_construct(handler, u'rights', item['rights']) + if item.get('source'): + self.write_source(handler, item['source']) + + for author in item['authors']: + self.write_person_construct(handler, u'author', author) + for contributor in item['contributors']: + self.write_person_construct(handler, u'contributor', contributor) + for category in item['categories']: + self.write_category_construct(handler, category) + for link in item['links']: + self.write_link_construct(handler, link) + if item.get('summary'): + self.write_text_construct(handler, u'summary', item['summary']) + if item.get('content'): + self.write_content(handler, item['content']) + + handler.endElement(u'entry') + + + def validate(self): + + def validate_text_construct(obj): + if isinstance(obj, tuple): + if obj[0] not in ['text', 'html', 'xhtml']: + return False + # @@@ no validation is done that 'html' text constructs are valid HTML + # @@@ no validation is done that 'xhtml' text constructs are well-formed XML or valid XHTML + + return True + + if not validate_text_construct(self.feed['title']): + raise ValidationError('feed title has invalid type') + if self.feed.get('subtitle'): + if not validate_text_construct(self.feed['subtitle']): + raise ValidationError('feed subtitle has invalid type') + if self.feed.get('rights'): + if not validate_text_construct(self.feed['rights']): + raise ValidationError('feed rights has invalid type') + + alternate_links = {} + for link in self.feed.get('links'): + if link.get('rel') == 'alternate' or link.get('rel') == None: + key = (link.get('type'), link.get('hreflang')) + if key in alternate_links: + raise ValidationError('alternate links must have unique type/hreflang') + alternate_links[key] = link + + if self.feed.get('authors'): + feed_author = True + else: + feed_author = False + + for item in self.items: + if not feed_author and not item.get('authors'): + if item.get('source') and item['source'].get('authors'): + pass + else: + raise ValidationError('if no feed author, all entries must have author (possibly in source)') + + if not validate_text_construct(item['title']): + raise ValidationError('entry title has invalid type') + if item.get('rights'): + if not validate_text_construct(item['rights']): + raise ValidationError('entry rights has invalid type') + if item.get('summary'): + if not validate_text_construct(item['summary']): + raise ValidationError('entry summary has invalid type') + source = item.get('source') + if source: + if source.get('title'): + if not validate_text_construct(source['title']): + raise ValidationError('source title has invalid type') + if source.get('subtitle'): + if not validate_text_construct(source['subtitle']): + raise ValidationError('source subtitle has invalid type') + if source.get('rights'): + if not validate_text_construct(source['rights']): + raise ValidationError('source rights has invalid type') + + alternate_links = {} + for link in item.get('links'): + if link.get('rel') == 'alternate' or link.get('rel') == None: + key = (link.get('type'), link.get('hreflang')) + if key in alternate_links: + raise ValidationError('alternate links must have unique type/hreflang') + alternate_links[key] = link + + if not item.get('content'): + if not alternate_links: + raise ValidationError('if no content, entry must have alternate link') + + if item.get('content') and isinstance(item.get('content'), tuple): + content_type = item.get('content')[0].get('type') + if item.get('content')[0].get('src'): + if item.get('content')[1]: + raise ValidationError('content with src should be empty') + if not item.get('summary'): + raise ValidationError('content with src requires a summary too') + if content_type in ['text', 'html', 'xhtml']: + raise ValidationError('content with src cannot have type of text, html or xhtml') + if content_type: + if '/' in content_type and \ + not content_type.startswith('text/') and \ + not content_type.endswith('/xml') and not content_type.endswith('+xml') and \ + not content_type in ['application/xml-external-parsed-entity', 'application/xml-dtd']: + # @@@ check content is Base64 + if not item.get('summary'): + raise ValidationError('content in Base64 requires a summary too') + if content_type not in ['text', 'html', 'xhtml'] and '/' not in content_type: + raise ValidationError('content type does not appear to be valid') + + # @@@ no validation is done that 'html' text constructs are valid HTML + # @@@ no validation is done that 'xhtml' text constructs are well-formed XML or valid XHTML + + return + + return + + + +class LegacySyndicationFeed(AtomFeed): + """ + Provides an SyndicationFeed-compatible interface in its __init__ and + add_item but is really a new AtomFeed object. + """ + + def __init__(self, title, link, description, language=None, author_email=None, + author_name=None, author_link=None, subtitle=None, categories=[], + feed_url=None, feed_copyright=None): + + atom_id = link + title = title + updated = None # will be calculated + rights = feed_copyright + subtitle = subtitle + author_dict = {'name': author_name} + if author_link: + author_dict['uri'] = author_uri + if author_email: + author_dict['email'] = author_email + authors = [author_dict] + if categories: + categories = [{'term': term} for term in categories] + links = [{'rel': 'alternate', 'href': link}] + if feed_url: + links.append({'rel': 'self', 'href': feed_url}) + if language: + extra_attrs = {'xml:lang': language} + else: + extra_attrs = {} + + # description ignored (as with Atom1Feed) + + AtomFeed.__init__(self, atom_id, title, updated, rights=rights, subtitle=subtitle, + authors=authors, categories=categories, links=links, extra_attrs=extra_attrs) + + + def add_item(self, title, link, description, author_email=None, + author_name=None, author_link=None, pubdate=None, comments=None, + unique_id=None, enclosure=None, categories=[], item_copyright=None): + + if unique_id: + atom_id = unique_id + else: + atom_id = get_tag_uri(link, pubdate) + title = title + updated = pubdate + if item_copyright: + rights = item_copyright + else: + rights = None + if description: + summary = 'html', description + else: + summary = None + author_dict = {'name': author_name} + if author_link: + author_dict['uri'] = author_uri + if author_email: + author_dict['email'] = author_email + authors = [author_dict] + categories = [{'term': term} for term in categories] + links = [{'rel': 'alternate', 'href': link}] + if enclosure: + links.append({'rel': 'enclosure', 'href': enclosure.url, 'length': enclosure.length, 'type': enclosure.mime_type}) + + AtomFeed.add_item(self, atom_id, title, updated, rights=rights, summary=summary, + authors=authors, categories=categories, links=links) diff --git a/notification/context_processors.py b/notification/context_processors.py new file mode 100644 index 0000000..11a2b41 --- /dev/null +++ b/notification/context_processors.py @@ -0,0 +1,10 @@ +from notification.models import Notice + + +def notification(request): + if request.user.is_authenticated(): + return { + "notice_unseen_count": Notice.objects.unseen_count_for(request.user, on_site=True), + } + else: + return {} diff --git a/notification/decorators.py b/notification/decorators.py new file mode 100644 index 0000000..aab2b2d --- /dev/null +++ b/notification/decorators.py @@ -0,0 +1,65 @@ +from django.utils.translation import ugettext as _ +from django.http import HttpResponse +from django.conf import settings + +from django.contrib.auth import authenticate, login + + +def simple_basic_auth_callback(request, user, *args, **kwargs): + """ + Simple callback to automatically login the given user after a successful + basic authentication. + """ + login(request, user) + request.user = user + + +def basic_auth_required(realm=None, test_func=None, callback_func=None): + """ + This decorator should be used with views that need simple authentication + against Django's authentication framework. + + The ``realm`` string is shown during the basic auth query. + + It takes a ``test_func`` argument that is used to validate the given + credentials and return the decorated function if successful. + + If unsuccessful the decorator will try to authenticate and checks if the + user has the ``is_active`` field set to True. + + In case of a successful authentication the ``callback_func`` will be + called by passing the ``request`` and the ``user`` object. After that the + actual view function will be called. + + If all of the above fails a "Authorization Required" message will be shown. + """ + if realm is None: + realm = getattr(settings, "HTTP_AUTHENTICATION_REALM", _("Restricted Access")) + if test_func is None: + test_func = lambda u: u.is_authenticated() + + def decorator(view_func): + def basic_auth(request, *args, **kwargs): + # Just return the original view because already logged in + if test_func(request.user): + return view_func(request, *args, **kwargs) + + # Not logged in, look if login credentials are provided + if "HTTP_AUTHORIZATION" in request.META: + auth_method, auth = request.META["HTTP_AUTHORIZATION"].split(" ", 1) + if "basic" == auth_method.lower(): + auth = auth.strip().decode("base64") + username, password = auth.split(":",1) + user = authenticate(username=username, password=password) + if user is not None: + if user.is_active: + if callback_func is not None and callable(callback_func): + callback_func(request, user, *args, **kwargs) + return view_func(request, *args, **kwargs) + + response = HttpResponse(_("Authorization Required"), mimetype="text/plain") + response.status_code = 401 + response["WWW-Authenticate"] = "Basic realm='%s'" % realm + return response + return basic_auth + return decorator diff --git a/notification/engine.py b/notification/engine.py new file mode 100644 index 0000000..d0f4c9f --- /dev/null +++ b/notification/engine.py @@ -0,0 +1,78 @@ +import sys +import time +import logging +import traceback + +try: + import cPickle as pickle +except ImportError: + import pickle + +from django.conf import settings +from django.core.mail import mail_admins +from django.contrib.auth.models import User +from django.contrib.sites.models import Site + +from lockfile import FileLock, AlreadyLocked, LockTimeout + +from notification.models import NoticeQueueBatch +from notification import models as notification + +# lock timeout value. how long to wait for the lock to become available. +# default behavior is to never wait for the lock to be available. +LOCK_WAIT_TIMEOUT = getattr(settings, "NOTIFICATION_LOCK_WAIT_TIMEOUT", -1) + + +def send_all(): + lock = FileLock("send_notices") + + logging.debug("acquiring lock...") + try: + lock.acquire(LOCK_WAIT_TIMEOUT) + except AlreadyLocked: + logging.debug("lock already in place. quitting.") + return + except LockTimeout: + logging.debug("waiting for the lock timed out. quitting.") + return + logging.debug("acquired.") + + batches, sent = 0, 0 + start_time = time.time() + + try: + # nesting the try statement to be Python 2.4 + try: + for queued_batch in NoticeQueueBatch.objects.all(): + notices = pickle.loads(str(queued_batch.pickled_data).decode("base64")) + for user, label, extra_context, on_site, sender in notices: + try: + user = User.objects.get(pk=user) + logging.info("emitting notice %s to %s" % (label, user)) + # call this once per user to be atomic and allow for logging to + # accurately show how long each takes. + notification.send_now([user], label, extra_context, on_site, sender) + except User.DoesNotExist: + # Ignore deleted users, just warn about them + logging.warning("not emitting notice %s to user %s since it does not exist" % (label, user)) + sent += 1 + queued_batch.delete() + batches += 1 + except: + # get the exception + exc_class, e, t = sys.exc_info() + # email people + current_site = Site.objects.get_current() + subject = "[%s emit_notices] %r" % (current_site.name, e) + message = "%s" % ("\n".join(traceback.format_exception(*sys.exc_info())),) + mail_admins(subject, message, fail_silently=True) + # log it as critical + logging.critical("an exception occurred: %r" % e) + finally: + logging.debug("releasing lock...") + lock.release() + logging.debug("released.") + + logging.info("") + logging.info("%s batches, %s sent" % (batches, sent,)) + logging.info("done in %.2f seconds" % (time.time() - start_time)) diff --git a/notification/feeds.py b/notification/feeds.py new file mode 100644 index 0000000..7c1f46d --- /dev/null +++ b/notification/feeds.py @@ -0,0 +1,81 @@ +import datetime + +from django.core.urlresolvers import reverse +from django.conf import settings +from django.shortcuts import get_object_or_404 +from django.template.defaultfilters import linebreaks, escape, striptags +from django.utils.translation import ugettext_lazy as _ + +from django.contrib.auth.models import User +from django.contrib.sites.models import Site + +from notification.models import Notice +from notification.atomformat import Feed + + +ITEMS_PER_FEED = getattr(settings, "ITEMS_PER_FEED", 20) +DEFAULT_HTTP_PROTOCOL = getattr(settings, "DEFAULT_HTTP_PROTOCOL", "http") + + +class BaseNoticeFeed(Feed): + + def item_id(self, notification): + return "%s://%s%s" % ( + DEFAULT_HTTP_PROTOCOL, + Site.objects.get_current().domain, + notification.get_absolute_url(), + ) + + def item_title(self, notification): + return striptags(notification.message) + + def item_updated(self, notification): + return notification.added + + def item_published(self, notification): + return notification.added + + def item_content(self, notification): + return {"type": "html"}, linebreaks(escape(notification.message)) + + def item_links(self, notification): + return [{"href": self.item_id(notification)}] + + def item_authors(self, notification): + return [{"name": notification.recipient.username}] + + +class NoticeUserFeed(BaseNoticeFeed): + + def get_object(self, params): + return get_object_or_404(User, username=params[0].lower()) + + def feed_id(self, user): + return "%s://%s%s" % ( + DEFAULT_HTTP_PROTOCOL, + Site.objects.get_current().domain, + reverse("notification_feed_for_user"), + ) + + def feed_title(self, user): + return _("Notices Feed") + + def feed_updated(self, user): + qs = Notice.objects.filter(recipient=user) + # We return an arbitrary date if there are no results, because there + # must be a feed_updated field as per the Atom specifications, however + # there is no real data to go by, and an arbitrary date can be static. + if qs.count() == 0: + return datetime.datetime(year=2008, month=7, day=1) + return qs.latest("added").added + + def feed_links(self, user): + complete_url = "%s://%s%s" % ( + DEFAULT_HTTP_PROTOCOL, + Site.objects.get_current().domain, + reverse("notification_notices"), + ) + return ({"href": complete_url},) + + def items(self, user): + return Notice.objects.notices_for(user).order_by("-added")[:ITEMS_PER_FEED] diff --git a/notification/full.html b/notification/full.html new file mode 100644 index 0000000..e70ad6e --- /dev/null +++ b/notification/full.html @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %}
\ No newline at end of file diff --git a/notification/lockfile.py b/notification/lockfile.py new file mode 100644 index 0000000..d170703 --- /dev/null +++ b/notification/lockfile.py @@ -0,0 +1,500 @@ + +""" +lockfile.py - Platform-independent advisory file locks. + +Requires Python 2.5 unless you apply 2.4.diff +Locking is done on a per-thread basis instead of a per-process basis. + +Usage: + +>>> lock = FileLock('somefile') +>>> try: +... lock.acquire() +... except AlreadyLocked: +... print 'somefile', 'is locked already.' +... except LockFailed: +... print 'somefile', 'can\\'t be locked.' +... else: +... print 'got lock' +got lock +>>> print lock.is_locked() +True +>>> lock.release() + +>>> lock = FileLock('somefile') +>>> print lock.is_locked() +False +>>> with lock: +... print lock.is_locked() +True +>>> print lock.is_locked() +False +>>> # It is okay to lock twice from the same thread... +>>> with lock: +... lock.acquire() +... +>>> # Though no counter is kept, so you can't unlock multiple times... +>>> print lock.is_locked() +False + +Exceptions: + + Error - base class for other exceptions + LockError - base class for all locking exceptions + AlreadyLocked - Another thread or process already holds the lock + LockFailed - Lock failed for some other reason + UnlockError - base class for all unlocking exceptions + AlreadyUnlocked - File was not locked. + NotMyLock - File was locked but not by the current thread/process +""" + +from __future__ import division + +import sys +import socket +import os +import threading +import time +import errno + +# Work with PEP8 and non-PEP8 versions of threading module. +try: + threading.current_thread +except AttributeError: + threading.current_thread = threading.currentThread +try: + # python 2.6 has threading.current_thread so we need to do this separately. + threading.Thread.get_name +except AttributeError: + threading.Thread.get_name = threading.Thread.getName + +__all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked', + 'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock', + 'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock'] + +class Error(Exception): + """ + Base class for other exceptions. + + >>> try: + ... raise Error + ... except Exception: + ... pass + """ + pass + +class LockError(Error): + """ + Base class for error arising from attempts to acquire the lock. + + >>> try: + ... raise LockError + ... except Error: + ... pass + """ + pass + +class LockTimeout(LockError): + """Raised when lock creation fails within a user-defined period of time. + + >>> try: + ... raise LockTimeout + ... except LockError: + ... pass + """ + pass + +class AlreadyLocked(LockError): + """Some other thread/process is locking the file. + + >>> try: + ... raise AlreadyLocked + ... except LockError: + ... pass + """ + pass + +class LockFailed(LockError): + """Lock file creation failed for some other reason. + + >>> try: + ... raise LockFailed + ... except LockError: + ... pass + """ + pass + +class UnlockError(Error): + """ + Base class for errors arising from attempts to release the lock. + + >>> try: + ... raise UnlockError + ... except Error: + ... pass + """ + pass + +class NotLocked(UnlockError): + """Raised when an attempt is made to unlock an unlocked file. + + >>> try: + ... raise NotLocked + ... except UnlockError: + ... pass + """ + pass + +class NotMyLock(UnlockError): + """Raised when an attempt is made to unlock a file someone else locked. + + >>> try: + ... raise NotMyLock + ... except UnlockError: + ... pass + """ + pass + +class LockBase: + """Base class for platform-specific lock classes.""" + def __init__(self, path, threaded=True): + """ + >>> lock = LockBase('somefile') + >>> lock = LockBase('somefile', threaded=False) + """ + self.path = path + self.lock_file = os.path.abspath(path) + ".lock" + self.hostname = socket.gethostname() + self.pid = os.getpid() + if threaded: + tname = "%s-" % threading.current_thread().get_name() + else: + tname = "" + dirname = os.path.dirname(self.lock_file) + self.unique_name = os.path.join(dirname, + "%s.%s%s" % (self.hostname, + tname, + self.pid)) + + def acquire(self, timeout=None): + """ + Acquire the lock. + + * If timeout is omitted (or None), wait forever trying to lock the + file. + + * If timeout > 0, try to acquire the lock for that many seconds. If + the lock period expires and the file is still locked, raise + LockTimeout. + + * If timeout <= 0, raise AlreadyLocked immediately if the file is + already locked. + """ + raise NotImplemented("implement in subclass") + + def release(self): + """ + Release the lock. + + If the file is not locked, raise NotLocked. + """ + raise NotImplemented("implement in subclass") + + def is_locked(self): + """ + Tell whether or not the file is locked. + """ + raise NotImplemented("implement in subclass") + + def i_am_locking(self): + """ + Return True if this object is locking the file. + """ + raise NotImplemented("implement in subclass") + + def break_lock(self): + """ + Remove a lock. Useful if a locking thread failed to unlock. + """ + raise NotImplemented("implement in subclass") + + def __enter__(self): + """ + Context manager support. + """ + self.acquire() + return self + + def __exit__(self, *_exc): + """ + Context manager support. + """ + self.release() + +class LinkFileLock(LockBase): + """Lock access to a file using atomic property of link(2).""" + + def acquire(self, timeout=None): + try: + open(self.unique_name, "wb").close() + except IOError: + raise LockFailed + + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a hard link to it. + try: + os.link(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + nlinks = os.stat(self.unique_name).st_nlink + if nlinks == 2: + # The original link plus the one I created == 2. We're + # good to go. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + os.unlink(self.unique_name) + if timeout > 0: + raise LockTimeout + else: + raise AlreadyLocked + time.sleep(timeout is not None and timeout/10 or 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked + elif not os.path.exists(self.unique_name): + raise NotMyLock + os.unlink(self.unique_name) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name) and + os.stat(self.unique_name).st_nlink == 2) + + def break_lock(self): + if os.path.exists(self.lock_file): + os.unlink(self.lock_file) + +class MkdirFileLock(LockBase): + """Lock file by creating a directory.""" + def __init__(self, path, threaded=True): + """ + >>> lock = MkdirFileLock('somefile') + >>> lock = MkdirFileLock('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded) + if threaded: + tname = "%x-" % thread.get_ident() + else: + tname = "" + # Lock file itself is a directory. Place the unique file name into + # it. + self.unique_name = os.path.join(self.lock_file, + "%s.%s%s" % (self.hostname, + tname, + self.pid)) + + def acquire(self, timeout=None): + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + else: + wait = max(0, timeout / 10) + + while True: + try: + os.mkdir(self.lock_file) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EEXIST: + # Already locked. + if os.path.exists(self.unique_name): + # Already locked by me. + return + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout + else: + # Someone else has the lock. + raise AlreadyLocked + time.sleep(wait) + else: + # Couldn't create the lock for some other reason + raise LockFailed + else: + open(self.unique_name, "wb").close() + return + + def release(self): + if not self.is_locked(): + raise NotLocked + elif not os.path.exists(self.unique_name): + raise NotMyLock + os.unlink(self.unique_name) + os.rmdir(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name)) + + def break_lock(self): + if os.path.exists(self.lock_file): + for name in os.listdir(self.lock_file): + os.unlink(os.path.join(self.lock_file, name)) + os.rmdir(self.lock_file) + +class SQLiteFileLock(LockBase): + "Demonstration of using same SQL-based locking." + + import tempfile + _fd, testdb = tempfile.mkstemp() + os.close(_fd) + os.unlink(testdb) + del _fd, tempfile + + def __init__(self, path, threaded=True): + LockBase.__init__(self, path, threaded) + self.lock_file = unicode(self.lock_file) + self.unique_name = unicode(self.unique_name) + + import sqlite3 + self.connection = sqlite3.connect(SQLiteFileLock.testdb) + + c = self.connection.cursor() + try: + c.execute("create table locks" + "(" + " lock_file varchar(32)," + " unique_name varchar(32)" + ")") + except sqlite3.OperationalError: + pass + else: + self.connection.commit() + import atexit + atexit.register(os.unlink, SQLiteFileLock.testdb) + + def acquire(self, timeout=None): + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + elif timeout <= 0: + wait = 0 + else: + wait = timeout / 10 + + cursor = self.connection.cursor() + + while True: + if not self.is_locked(): + # Not locked. Try to lock it. + cursor.execute("insert into locks" + " (lock_file, unique_name)" + " values" + " (?, ?)", + (self.lock_file, self.unique_name)) + self.connection.commit() + + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) > 1: + # Nope. Someone else got there. Remove our lock. + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + else: + # Yup. We're done, so go home. + return + else: + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) == 1: + # We're the locker, so go home. + return + + # Maybe we should wait a bit longer. + if timeout is not None and time.time() > end_time: + if timeout > 0: + # No more waiting. + raise LockTimeout + else: + # Someone else has the lock and we are impatient.. + raise AlreadyLocked + + # Well, okay. We'll give it a bit longer. + time.sleep(wait) + + def release(self): + if not self.is_locked(): + raise NotLocked + if not self.i_am_locking(): + raise NotMyLock((self._who_is_locking(), self.unique_name)) + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + + def _who_is_locking(self): + cursor = self.connection.cursor() + cursor.execute("select unique_name from locks" + " where lock_file = ?", + (self.lock_file,)) + return cursor.fetchone()[0] + + def is_locked(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?", + (self.lock_file,)) + rows = cursor.fetchall() + return not not rows + + def i_am_locking(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?" + " and unique_name = ?", + (self.lock_file, self.unique_name)) + return not not cursor.fetchall() + + def break_lock(self): + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where lock_file = ?", + (self.lock_file,)) + self.connection.commit() + +if hasattr(os, "link"): + FileLock = LinkFileLock +else: + FileLock = MkdirFileLock diff --git a/notification/management/__init__.py b/notification/management/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/notification/management/__init__.py diff --git a/notification/management/commands/__init__.py b/notification/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/notification/management/commands/__init__.py diff --git a/notification/management/commands/emit_notices.py b/notification/management/commands/emit_notices.py new file mode 100644 index 0000000..af2ffcf --- /dev/null +++ b/notification/management/commands/emit_notices.py @@ -0,0 +1,15 @@ + +import logging + +from django.core.management.base import NoArgsCommand + +from notification.engine import send_all + +class Command(NoArgsCommand): + help = "Emit queued notices." + + def handle_noargs(self, **options): + logging.basicConfig(level=logging.DEBUG, format="%(message)s") + logging.info("-" * 72) + send_all() +
\ No newline at end of file diff --git a/notification/models.py b/notification/models.py new file mode 100644 index 0000000..88dc31a --- /dev/null +++ b/notification/models.py @@ -0,0 +1,469 @@ +import datetime + +try: + import cPickle as pickle +except ImportError: + import pickle + +from django.db import models +from django.db.models.query import QuerySet +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.mail import send_mail +from django.core.urlresolvers import reverse +from django.template import Context +from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext, get_language, activate + +from django.contrib.sites.models import Site +from django.contrib.auth.models import User +from django.contrib.auth.models import AnonymousUser +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes import generic + +QUEUE_ALL = getattr(settings, "NOTIFICATION_QUEUE_ALL", False) + + +class LanguageStoreNotAvailable(Exception): + pass + +class NoticeType(models.Model): + + label = models.CharField(_("label"), max_length=40) + display = models.CharField(_("display"), max_length=50) + description = models.CharField(_("description"), max_length=100) + + # by default only on for media with sensitivity less than or equal to this number + default = models.IntegerField(_("default")) + + def __unicode__(self): + return self.label + + class Meta: + verbose_name = _("notice type") + verbose_name_plural = _("notice types") + + +# if this gets updated, the create() method below needs to be as well... +NOTICE_MEDIA = ( + ("1", _("Email")), +) + +# how spam-sensitive is the medium +NOTICE_MEDIA_DEFAULTS = { + "1": 2 # email +} + +class NoticeSetting(models.Model): + """ + Indicates, for a given user, whether to send notifications + of a given type to a given medium. + """ + + user = models.ForeignKey(User, verbose_name=_("user")) + notice_type = models.ForeignKey(NoticeType, verbose_name=_("notice type")) + medium = models.CharField(_("medium"), max_length=1, choices=NOTICE_MEDIA) + send = models.BooleanField(_("send")) + + class Meta: + verbose_name = _("notice setting") + verbose_name_plural = _("notice settings") + unique_together = ("user", "notice_type", "medium") + + +def get_notification_setting(user, notice_type, medium): + try: + return NoticeSetting.objects.get(user=user, notice_type=notice_type, medium=medium) + except NoticeSetting.DoesNotExist: + default = (NOTICE_MEDIA_DEFAULTS[medium] <= notice_type.default) + setting = NoticeSetting(user=user, notice_type=notice_type, medium=medium, send=default) + setting.save() + return setting + + +def should_send(user, notice_type, medium): + return get_notification_setting(user, notice_type, medium).send + + +class NoticeManager(models.Manager): + + def notices_for(self, user, archived=False, unseen=None, on_site=None, sent=False): + """ + returns Notice objects for the given user. + + If archived=False, it only include notices not archived. + If archived=True, it returns all notices for that user. + + If unseen=None, it includes all notices. + If unseen=True, return only unseen notices. + If unseen=False, return only seen notices. + """ + if sent: + lookup_kwargs = {"sender": user} + else: + lookup_kwargs = {"recipient": user} + qs = self.filter(**lookup_kwargs) + nqs = [] + for each in qs: + nqs.append(each.message) + if not archived: + self.filter(archived=archived) + if unseen is not None: + qs = qs.filter(unseen=unseen) + if on_site is not None: + qs = qs.filter(on_site=on_site) + return nqs + + def unseen_count_for(self, recipient, **kwargs): + """ + returns the number of unseen notices for the given user but does not + mark them seen + """ + return self.notices_for(recipient, unseen=True, **kwargs).count() + + def received(self, recipient, **kwargs): + """ + returns notices the given recipient has recieved. + """ + kwargs["sent"] = False + return self.notices_for(recipient, **kwargs) + + def sent(self, sender, **kwargs): + """ + returns notices the given sender has sent + """ + kwargs["sent"] = True + return self.notices_for(sender, **kwargs) + + +class Notice(models.Model): + + recipient = models.ForeignKey(User, related_name="recieved_notices", verbose_name=_("recipient")) + sender = models.ForeignKey(User, null=True, related_name="sent_notices", verbose_name=_("sender")) + message = models.TextField(_("message")) + notice_type = models.ForeignKey(NoticeType, verbose_name=_("notice type")) + added = models.DateTimeField(_("added"), default=datetime.datetime.now) + unseen = models.BooleanField(_("unseen"), default=True) + archived = models.BooleanField(_("archived"), default=False) + on_site = models.BooleanField(_("on site")) + + objects = NoticeManager() + + def __unicode__(self): + return self.message + + def archive(self): + self.archived = True + self.save() + + def is_unseen(self): + """ + returns value of self.unseen but also changes it to false. + + Use this in a template to mark an unseen notice differently the first + time it is shown. + """ + unseen = self.unseen + if unseen: + self.unseen = False + self.save() + return unseen + + class Meta: + ordering = ["-added"] + verbose_name = _("notice") + verbose_name_plural = _("notices") + + def get_absolute_url(self): + return reverse("notification_notice", args=[str(self.pk)]) + + +class NoticeQueueBatch(models.Model): + """ + A queued notice. + Denormalized data for a notice. + """ + pickled_data = models.TextField() + + +def create_notice_type(label, display, description, default=2, verbosity=1): + """ + Creates a new NoticeType. + + This is intended to be used by other apps as a post_syncdb manangement step. + """ + try: + notice_type = NoticeType.objects.get(label=label) + updated = False + if display != notice_type.display: + notice_type.display = display + updated = True + if description != notice_type.description: + notice_type.description = description + updated = True + if default != notice_type.default: + notice_type.default = default + updated = True + if updated: + notice_type.save() + if verbosity > 1: + print "Updated %s NoticeType" % label + except NoticeType.DoesNotExist: + NoticeType(label=label, display=display, description=description, default=default).save() + if verbosity > 1: + print "Created %s NoticeType" % label + + +def get_notification_language(user): + """ + Returns site-specific notification language for this user. Raises + LanguageStoreNotAvailable if this site does not use translated + notifications. + """ + if getattr(settings, "NOTIFICATION_LANGUAGE_MODULE", False): + try: + app_label, model_name = settings.NOTIFICATION_LANGUAGE_MODULE.split(".") + model = models.get_model(app_label, model_name) + language_model = model._default_manager.get(user__id__exact=user.id) + if hasattr(language_model, "language"): + return language_model.language + except (ImportError, ImproperlyConfigured, model.DoesNotExist): + raise LanguageStoreNotAvailable + raise LanguageStoreNotAvailable + + +def get_formatted_messages(formats, label, context): + """ + Returns a dictionary with the format identifier as the key. The values are + are fully rendered templates with the given context. + """ + format_templates = {} + for format in formats: + # conditionally turn off autoescaping for .txt extensions in format + if format.endswith(".txt"): + context.autoescape = False + else: + context.autoescape = True + print label,"labels" + format_templates[format] = label #("gstudio/notification/%s/%s" % (label, format), + #"gstudio/notification/%s" % format) + return format_templates + + +def send_now(users, label, extra_context=None, on_site=True, sender=None): + """ + Creates a new notice. + + This is intended to be how other apps create new notices. + + notification.send(user, "friends_invite_sent", { + "spam": "eggs", + "foo": "bar", + ) + + You can pass in on_site=False to prevent the notice emitted from being + displayed on the site. + """ + if extra_context is None: + extra_context = {} + + notice_type = NoticeType.objects.get(label=label) + + protocol = getattr(settings, "DEFAULT_HTTP_PROTOCOL", "http") + current_site = Site.objects.get_current() + + notices_url = u"%s://%s%s" % ( + protocol, + unicode(current_site), + reverse("notification_notices"), + ) + + current_language = get_language() + + formats = ( + "short.txt", + "full.txt", + "notice.html", + "full.html", + ) # TODO make formats configurable + + for user in users: + recipients = [] + # get user language for user from language store defined in + # NOTIFICATION_LANGUAGE_MODULE setting + try: + language = get_notification_language(user) + except LanguageStoreNotAvailable: + language = None + + if language is not None: + # activate the user's language + activate(language) + + # update context with user specific translations + context = Context({ + "recipient": user, + "sender": sender, + "notice": ugettext(notice_type.display), + "notices_url": notices_url, + "current_site": current_site, + }) + context.update(extra_context) + + # get prerendered format messages + messages = get_formatted_messages(formats, label, context) + + # Strip newlines from subject + subject = "".join(render_to_string("gstudio/notification/email_subject.txt", { + "message": messages["short.txt"], + }, context).splitlines()) + + body = render_to_string("gstudio/notification/email_body.txt", { + "message": messages["full.txt"], + }, context) + + notice = Notice.objects.create(recipient=user, message=messages["notice.html"], + notice_type=notice_type, on_site=on_site, sender=sender) + if should_send(user, notice_type, "1") and user.email and user.is_active: # Email + recipients.append(user.email) + send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, recipients) + + # reset environment to original language + activate(current_language) + + +def send(*args, **kwargs): + """ + A basic interface around both queue and send_now. This honors a global + flag NOTIFICATION_QUEUE_ALL that helps determine whether all calls should + be queued or not. A per call ``queue`` or ``now`` keyword argument can be + used to always override the default global behavior. + """ + queue_flag = kwargs.pop("queue", False) + now_flag = kwargs.pop("now", False) + assert not (queue_flag and now_flag), "'queue' and 'now' cannot both be True." + if queue_flag: + return queue(*args, **kwargs) + elif now_flag: + return send_now(*args, **kwargs) + else: + if QUEUE_ALL: + return queue(*args, **kwargs) + else: + return send_now(*args, **kwargs) + + +def queue(users, label, extra_context=None, on_site=True, sender=None): + """ + Queue the notification in NoticeQueueBatch. This allows for large amounts + of user notifications to be deferred to a seperate process running outside + the webserver. + """ + if extra_context is None: + extra_context = {} + if isinstance(users, QuerySet): + users = [row["pk"] for row in users.values("pk")] + else: + users = [user.pk for user in users] + notices = [] + for user in users: + notices.append((user, label, extra_context, on_site, sender)) + NoticeQueueBatch(pickled_data=pickle.dumps(notices).encode("base64")).save() + + +class ObservedItemManager(models.Manager): + + def all_for(self, observed, signal): + """ + Returns all ObservedItems for an observed object, + to be sent when a signal is emited. + """ + content_type = ContentType.objects.get_for_model(observed) + observed_items = self.filter(content_type=content_type, object_id=observed.id, signal=signal) + return observed_items + + def get_for(self, observed, observer, signal): + content_type = ContentType.objects.get_for_model(observed) + observed_item = self.get(content_type=content_type, object_id=observed.id, user=observer, signal=signal) + return observed_item + + +class ObservedItem(models.Model): + + user = models.ForeignKey(User, verbose_name=_("user")) + + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + observed_object = generic.GenericForeignKey("content_type", "object_id") + + notice_type = models.ForeignKey(NoticeType, verbose_name=_("notice type")) + + added = models.DateTimeField(_("added"), default=datetime.datetime.now) + + # the signal that will be listened to send the notice + signal = models.TextField(verbose_name=_("signal")) + + objects = ObservedItemManager() + + class Meta: + ordering = ["-added"] + verbose_name = _("observed item") + verbose_name_plural = _("observed items") + + def send_notice(self, extra_context=None): + if extra_context is None: + extra_context = {} + extra_context.update({"observed": self.observed_object}) + send([self.user], self.notice_type.label, extra_context) + + +def observe(observed, observer, notice_type_label, signal="post_save"): + """ + Create a new ObservedItem. + + To be used by applications to register a user as an observer for some object. + """ + notice_type = NoticeType.objects.get(label=notice_type_label) + observed_item = ObservedItem( + user=observer, observed_object=observed, + notice_type=notice_type, signal=signal + ) + observed_item.save() + return observed_item + + +def stop_observing(observed, observer, signal="post_save"): + """ + Remove an observed item. + """ + observed_item = ObservedItem.objects.get_for(observed, observer, signal) + observed_item.delete() + + +def send_observation_notices_for(observed, signal="post_save", extra_context=None): + """ + Send a notice for each registered user about an observed object. + """ + if extra_context is None: + extra_context = {} + observed_items = ObservedItem.objects.all_for(observed, signal) + for observed_item in observed_items: + observed_item.send_notice(extra_context) + return observed_items + + +def is_observing(observed, observer, signal="post_save"): + if isinstance(observer, AnonymousUser): + return False + try: + observed_items = ObservedItem.objects.get_for(observed, observer, signal) + return True + except ObservedItem.DoesNotExist: + return False + except ObservedItem.MultipleObjectsReturned: + return True + + +def handle_observations(sender, instance, *args, **kw): + send_observation_notices_for(instance) diff --git a/notification/notice.html b/notification/notice.html new file mode 100644 index 0000000..01f21b3 --- /dev/null +++ b/notification/notice.html @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %} diff --git a/notification/templates/notification/email_body.txt b/notification/templates/notification/email_body.txt new file mode 100644 index 0000000..037cdb4 --- /dev/null +++ b/notification/templates/notification/email_body.txt @@ -0,0 +1,6 @@ +{% load i18n %}{% blocktrans %}You have received the following notice from {{ current_site }}: + +{{ message }} + +To see other notices or change how you receive notifications, please go to {{ notices_url }}. +{% endblocktrans %} diff --git a/notification/templates/notification/email_subject.txt b/notification/templates/notification/email_subject.txt new file mode 100644 index 0000000..91d1953 --- /dev/null +++ b/notification/templates/notification/email_subject.txt @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}[{{ current_site }}] {{ message }}{% endblocktrans %}
\ No newline at end of file diff --git a/notification/templates/notification/full.html b/notification/templates/notification/full.html new file mode 100644 index 0000000..e70ad6e --- /dev/null +++ b/notification/templates/notification/full.html @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %}
\ No newline at end of file diff --git a/notification/templates/notification/full.txt b/notification/templates/notification/full.txt new file mode 100644 index 0000000..01f21b3 --- /dev/null +++ b/notification/templates/notification/full.txt @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %} diff --git a/notification/templates/notification/notice.html b/notification/templates/notification/notice.html new file mode 100644 index 0000000..e70ad6e --- /dev/null +++ b/notification/templates/notification/notice.html @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %}
\ No newline at end of file diff --git a/notification/templates/notification/short.txt b/notification/templates/notification/short.txt new file mode 100644 index 0000000..e70ad6e --- /dev/null +++ b/notification/templates/notification/short.txt @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %}
\ No newline at end of file diff --git a/notification/urls.py b/notification/urls.py new file mode 100644 index 0000000..dd36972 --- /dev/null +++ b/notification/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls.defaults import * + +from notification.views import notices, mark_all_seen, feed_for_user, single, notice_settings + + +urlpatterns = patterns("", + url(r"^$", notices, name="notification_notices"), + url(r"^settings/$", notice_settings, name="notification_notice_settings"), + url(r"^(\d+)/$", single, name="notification_notice"), + url(r"^feed/$", feed_for_user, name="notification_feed_for_user"), + url(r"^mark_all_seen/$", mark_all_seen, name="notification_mark_all_seen"), +) diff --git a/notification/views.py b/notification/views.py new file mode 100644 index 0000000..175815f --- /dev/null +++ b/notification/views.py @@ -0,0 +1,196 @@ + + +from django.core.urlresolvers import reverse +from django.shortcuts import render_to_response, get_object_or_404 +from django.http import HttpResponseRedirect, Http404 +from django.template import RequestContext + +from django.contrib.auth.decorators import login_required +from django.contrib.syndication.views import Feed + +from notification.models import * +from notification.decorators import basic_auth_required, simple_basic_auth_callback +from notification.feeds import NoticeUserFeed + + +@basic_auth_required(realm="Notices Feed", callback_func=simple_basic_auth_callback) +def feed_for_user(request): + """ + An atom feed for all unarchived :model:`notification.Notice`s for a user. + """ + url = "feed/%s" % request.user.username + return feed(request, url, { + "feed": NoticeUserFeed, + }) + + +@login_required +def notices(request): + """ + The main notices index view. + + Template: :template:`notification/notices.html` + + Context: + + notices + A list of :model:`notification.Notice` objects that are not archived + and to be displayed on the site. + """ + notices = Notice.objects.notices_for(request.user, on_site=True) + + return render_to_response("gstudio/notification/notice.html", { + "notices": notices, + }, context_instance=RequestContext(request)) + + +@login_required +def notice_settings(request): + """ + The notice settings view. + + Template: :template:`notification/notice_settings.html` + + Context: + + notice_types + A list of all :model:`notification.NoticeType` objects. + + notice_settings + A dictionary containing ``column_headers`` for each ``NOTICE_MEDIA`` + and ``rows`` containing a list of dictionaries: ``notice_type``, a + :model:`notification.NoticeType` object and ``cells``, a list of + tuples whose first value is suitable for use in forms and the second + value is ``True`` or ``False`` depending on a ``request.POST`` + variable called ``form_label``, whose valid value is ``on``. + """ + notice_types = NoticeType.objects.all() + settings_table = [] + for notice_type in notice_types: + settings_row = [] + for medium_id, medium_display in NOTICE_MEDIA: + form_label = "%s_%s" % (notice_type.label, medium_id) + setting = get_notification_setting(request.user, notice_type, medium_id) + if request.method == "POST": + if request.POST.get(form_label) == "on": + if not setting.send: + setting.send = True + setting.save() + else: + if setting.send: + setting.send = False + setting.save() + settings_row.append((form_label, setting.send)) + settings_table.append({"notice_type": notice_type, "cells": settings_row}) + + if request.method == "POST": + next_page = request.POST.get("next_page", ".") + return HttpResponseRedirect(next_page) + + notice_settings = { + "column_headers": [medium_display for medium_id, medium_display in NOTICE_MEDIA], + "rows": settings_table, + } + + return render_to_response("notification/notice_settings.html", { + "notice_types": notice_types, + "notice_settings": notice_settings, + }, context_instance=RequestContext(request)) + + +@login_required +def single(request, id, mark_seen=True): + """ + Detail view for a single :model:`notification.Notice`. + + Template: :template:`notification/single.html` + + Context: + + notice + The :model:`notification.Notice` being viewed + + Optional arguments: + + mark_seen + If ``True``, mark the notice as seen if it isn't + already. Do nothing if ``False``. Default: ``True``. + """ + notice = get_object_or_404(Notice, id=id) + if request.user == notice.recipient: + if mark_seen and notice.unseen: + notice.unseen = False + notice.save() + return render_to_response("notification/single.html", { + "notice": notice, + }, context_instance=RequestContext(request)) + raise Http404 + + +@login_required +def archive(request, noticeid=None, next_page=None): + """ + Archive a :model:`notices.Notice` if the requesting user is the + recipient or if the user is a superuser. Returns a + ``HttpResponseRedirect`` when complete. + + Optional arguments: + + noticeid + The ID of the :model:`notices.Notice` to be archived. + + next_page + The page to redirect to when done. + """ + if noticeid: + try: + notice = Notice.objects.get(id=noticeid) + if request.user == notice.recipient or request.user.is_superuser: + notice.archive() + else: # you can archive other users' notices + # only if you are superuser. + return HttpResponseRedirect(next_page) + except Notice.DoesNotExist: + return HttpResponseRedirect(next_page) + return HttpResponseRedirect(next_page) + + +@login_required +def delete(request, noticeid=None, next_page=None): + """ + Delete a :model:`notices.Notice` if the requesting user is the recipient + or if the user is a superuser. Returns a ``HttpResponseRedirect`` when + complete. + + Optional arguments: + + noticeid + The ID of the :model:`notices.Notice` to be archived. + + next_page + The page to redirect to when done. + """ + if noticeid: + try: + notice = Notice.objects.get(id=noticeid) + if request.user == notice.recipient or request.user.is_superuser: + notice.delete() + else: # you can delete other users' notices + # only if you are superuser. + return HttpResponseRedirect(next_page) + except Notice.DoesNotExist: + return HttpResponseRedirect(next_page) + return HttpResponseRedirect(next_page) + + +@login_required +def mark_all_seen(request): + """ + Mark all unseen notices for the requesting user as seen. Returns a + ``HttpResponseRedirect`` when complete. + """ + + for notice in Notice.objects.notices_for(request.user, unseen=True): + notice.unseen = False + notice.save() + return HttpResponseRedirect(reverse("notification_notices")) diff --git a/objectapp/models.py b/objectapp/models.py index 41d6639..009a132 100644 --- a/objectapp/models.py +++ b/objectapp/models.py @@ -155,6 +155,7 @@ class Gbobject(Node): base. System and Process classes also inherit this class. """ + STATUS_CHOICES = ((DRAFT, _('draft')), (HIDDEN, _('hidden')), (PUBLISHED, _('published'))) @@ -209,6 +210,54 @@ class Gbobject(Node): objects = models.Manager() published = GbobjectPublishedManager() + @property + def getthread_of_response(self): + """ + Returns the thread corresponding to a reply(response)" + """ + obj=self + try: + loop=True + while loop: + obj=obj.prior_nodes.all()[0] + if not obj.prior_nodes.all(): + loop=False + return obj.getthread_of_twist + except: + return None + + @property + def gettwist_of_response(self): + """ + Returns twist of a response(reply) + """ + obj=self + try: + loop=True + while loop: + obj=obj.prior_nodes.all()[0] + if not obj.prior_nodes.all(): + loop=False + return obj + except: + return None + @property + def getthread_of_twist(self): + """ + Returns thread of a twist + """ + try: + obj=self + for each in System.objects.all(): + if each.system_set.all(): + sys_set=each.system_set.all()[0] + for eachsys in sys_set.gbobject_set.all(): + if eachsys==self: + return each + except: + pass + + @property def getattributetypes(self): @@ -319,7 +368,7 @@ class Gbobject(Node): if relation.relationtype.title not in rel_dict['left-subjecttypes'].keys(): # create a new list field and add to it rel_dict['left-subjecttypes'][str(relation.relationtype.title)] = [] - # add + # add rel_dict['left-subjecttypes'][str(relation.relationtype.title)].append(relation) for relation in right_relset: @@ -328,12 +377,54 @@ class Gbobject(Node): # create a new list key field and add to it rel_dict['right_subjecttypes'][str(relation.relationtype.inverse)] = [] # add to the existing key + rel_dict['right_subjecttypes'][str(relation.relationtype.inverse)].append(relation) relation_set.update(rel_dict['left-subjecttypes']) relation_set.update(rel_dict['right_subjecttypes']) return relation_set + def get_relations_for_view(self): + relation_set = {} + # ALGO to find the relations and their left-subjecttypes and right_subjecttypes + # 1. Get the relations containing a reference to the object. Retrieve where it occurs (left or right) + # 2. Find out which RT they come from. + # 3. For each RT, create a dict key and a value as a dict. And add the relation as a new key-value pair (rid:subject). + # 4. If self is in right value, then add inverse relation as RT and add the relation as a new key-value pair (rid:subject). + + left_relset = Relation.objects.filter(left_subject=self.id) + right_relset = Relation.objects.filter(right_subject=self.id) + + #return left_relset + right_relset + + # RT dictionary to store a single relation + rel_dict ={} + rel_dict['left-subjecttypes'] = {} + rel_dict['right_subjecttypes'] ={} + + + for relation in left_relset: + # check if relation already exists + if relation.relationtype.title not in rel_dict['left-subjecttypes'].keys(): + # create a new list field and add to it + rel_dict['left-subjecttypes'][str(relation.relationtype.title)] = [] + # add + obj=Gbobject.objects.get(id=relation.right_subject_id) + rel_dict['left-subjecttypes'][str(relation.relationtype.title)].append(obj.title) + + for relation in right_relset: + # check if relation exists + if relation.relationtype.inverse not in rel_dict['right_subjecttypes'].keys(): + # create a new list key field and add to it + rel_dict['right_subjecttypes'][str(relation.relationtype.inverse)] = [] + # add to the existing key + obj=Gbobject.objects.get(id=relation.left_subject_id) + rel_dict['right_subjecttypes'][str(relation.relationtype.inverse)].append(obj.title) + + relation_set.update(rel_dict['left-subjecttypes']) + relation_set.update(rel_dict['right_subjecttypes']) + + return relation_set @@ -341,6 +432,8 @@ class Gbobject(Node): attributes_dict = {} all_attributes=self.subject_of.all() for attributes in all_attributes: + atype=attributes.attributetype.subtypeof() + if not (atype=='Factory_Object' or atype=='factory_object') : val=[] atr_key=attributes.attributetype.title val.append(attributes.svalue) @@ -439,7 +532,10 @@ class Gbobject(Node): nbh['content'] = self.content #return all OTs the object is linked to nbh['member_of'] = self.objecttypes.all() - + nbh['prior_nodes'] = self.prior_nodes.all() + + nbh['posterior_nodes'] = self.posterior_nodes.all() + # get all the relations of the object nbh.update(self.get_relations()) nbh.update(self.get_attributes()) @@ -481,7 +577,7 @@ class Gbobject(Node): g_json["relations"].append({"from":self.id ,"type":str(key),"value":1,"to":predicate_id[key] }) - if not isinstance(nbh[key],basestring) and len(nbh[key])<=2: + if not isinstance(nbh[key],basestring) and len(nbh[key])<=10: for item in nbh[key]: if isinstance(item,unicode): g_json["node_metadata"].append({"_id":(str(attr_counter)+"b"),"screen_name":str(item)}) @@ -861,7 +957,61 @@ class Gbobject(Node): def save_revert_or_merge(self, *args, **kwargs): if OBJECTAPP_VERSIONING: with reversion.create_revision(): - super(Gbobject, self).save(*args, **kwargs) # Call the "real" save() method. + super(Gbobject, self).save(*args, **kwargs) # Call the "real" save() method. + + @property + def get_view_object_url(self, *args, **kwargs): + def show_systemobjecturl(object_id): + search=object_id + nbh="" + url="" + for each in System.objects.all(): + sysid=each.id + for eachsys in each.systemtypes.all(): + if eachsys.title=="Meeting": + url="group/gnowsys-grp/" + objecttitle = "TWIST" + elif eachsys.title=="Wikipage": + url="page/gnowsys-page/" + objecttitle = "WIKIPAGE" + for eachob in each.system_set.all(): + if eachob.gbobject_set.all(): + for eachgbob in eachob.gbobject_set.all(): + if search==eachgbob.id: + nbh=url+str(sysid) + + if search==sysid: + nbh=url+str(sysid) + + return nbh + + objmem = self.get_nbh['member_of'] + objectname = "" + objstr = str(objmem) + if objstr != '[]': + objectname = objmem[0].title + if objectname == "Image": + return '/gstudio/resources/images/show/'+ str(self.id) + elif objectname == "Document": + return '/gstudio/resources/documents/show/'+str(self.id) + elif objectname == "Video": + return '/gstudio/resources/videos/show/'+str(self.id) + elif objectname == "Topic": + return '/gstudio/'+show_systemobjecturl(self.id) + elif objectname == "Section": + return '/gstudio/'+show_systemobjecturl(self.id) + elif objectname == "Subsection": + return '/gstudio/'+show_systemobjecturl(self.id) + elif objectname == "Reply": + tes = self.gettwist_of_response + systes = tes.getthread_of_twist + return '/'+systes.get_view_url + else: + show=show_systemobjecturl(self.id) + if show != "": + return '/gstudio/'+show + else: + return self.get_absolute_url class Meta: """Gbobject's Meta""" @@ -878,7 +1028,6 @@ class Process(Gbobject): """ A store processes, events or changes described as changes in attributes and relations """ - processtypes = models.ManyToManyField(Processtype, verbose_name=_('member of process type'), related_name='member_processes', blank=True, null=True) @@ -973,7 +1122,13 @@ class System(Gbobject): super(System, self).save(*args, **kwargs) # Call the "real" save() method. - + @property + def get_view_url(self, *args, **kwargs): + stype = self.systemtypes.all()[0] + if stype.title == 'Meeting': + return 'gstudio/group/gnowsys-grp/' + str(self.id) + else: + return 'gstudio/page/gnowsys-page/' + str(self.id) def __unicode__(self): return self.title @@ -73,4 +73,5 @@ setup(name='gnowsys-studio', 'pandora_client>=0.2.94', 'django-pagination>=1.0.7', 'inflect>=0.2.3', - ]) + 'django-fixture-magic>=0.0.3', + ]) |