summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--demo/settings.py36
-rw-r--r--gstudio/methods.py149
-rw-r--r--gstudio/models.py2
-rw-r--r--gstudio/static/gstudio/js/addcontent.js54
-rw-r--r--gstudio/static/gstudio/js/orgitdown/orgitdown/jquery.orgitdown.js6
-rw-r--r--gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/images/video.jpegbin0 -> 440 bytes
-rw-r--r--gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/set.js1
-rw-r--r--gstudio/templates/gstudio/_header.html2
-rw-r--r--gstudio/templates/gstudio/addingtag.html82
-rw-r--r--gstudio/templates/gstudio/addreln.html76
-rw-r--r--gstudio/templates/gstudio/addrelnform.html51
-rw-r--r--gstudio/templates/gstudio/addrelnform_refresh.html15
-rw-r--r--gstudio/templates/gstudio/edittitle.html37
-rw-r--r--gstudio/templates/gstudio/home.html16
-rw-r--r--gstudio/templates/gstudio/priorpost.html68
-rw-r--r--gstudio/templates/gstudio/repriorpost.html34
-rw-r--r--gstudio/templates/gstudio/tags/comment.html41
-rw-r--r--gstudio/templates/gstudio/video.html8
-rw-r--r--gstudio/templates/metadashboard/grpdashboard.html190
-rw-r--r--gstudio/templates/metadashboard/pgedashboard.html94
-rw-r--r--gstudio/templates/metadashboard/wikidashboard.html5
-rw-r--r--gstudio/templatetags/gstudio_tags.py93
-rw-r--r--gstudio/urls/__init__.py1
-rw-r--r--gstudio/urls/addreln.py22
-rw-r--r--gstudio/urls/image.py6
-rw-r--r--gstudio/views/addreln.py84
-rw-r--r--gstudio/views/image.py125
-rw-r--r--gstudio/views/video.py49
-rw-r--r--notification/.gitignore1
-rw-r--r--notification/__init__.py16
-rw-r--r--notification/admin.py22
-rw-r--r--notification/atomformat.py547
-rw-r--r--notification/context_processors.py10
-rw-r--r--notification/decorators.py65
-rw-r--r--notification/engine.py78
-rw-r--r--notification/feeds.py81
-rw-r--r--notification/full.html1
-rw-r--r--notification/lockfile.py500
-rw-r--r--notification/management/__init__.py0
-rw-r--r--notification/management/commands/__init__.py0
-rw-r--r--notification/management/commands/emit_notices.py15
-rw-r--r--notification/models.py469
-rw-r--r--notification/notice.html1
-rw-r--r--notification/templates/notification/email_body.txt6
-rw-r--r--notification/templates/notification/email_subject.txt1
-rw-r--r--notification/templates/notification/full.html1
-rw-r--r--notification/templates/notification/full.txt1
-rw-r--r--notification/templates/notification/notice.html1
-rw-r--r--notification/templates/notification/short.txt1
-rw-r--r--notification/urls.py12
-rw-r--r--notification/views.py196
-rw-r--r--objectapp/models.py167
-rw-r--r--setup.py3
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
new file mode 100644
index 0000000..8edf9d6
--- /dev/null
+++ b/gstudio/static/gstudio/js/orgitdown/orgitdown/sets/org/images/video.jpeg
Binary files differ
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: &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp
+<input value="" name="reln" id="res_relation">
+<br/></h2>
+<h2>Inverse Name: &nbsp
+<input value="" name="obj" id="res_object">
+<br/></h2>
+<h2>Slug: &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp
+<input value="" name="slug" id="res_slug">
+<br/></h2>
+<br/> &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp
+<!--
+<input type="submit" id="addsavereln" name="" value="Save" ><nbsp>
+-->
+&nbsp &nbsp &nbsp &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 &nbsp;[ <a href="{{ meet_ob.get_absolute_url }}" title="{{ meet_ob.title }}" rel="bookmark">Show Graph</a> ] &nbsp;</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>&nbsp;[ <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 %} &nbsp;[ <a href="{{ page_ob.get_absolute_url }}" title="{{ page_ob.title }}" rel="bookmark">Show Graph</a> ] &nbsp;</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: &nbsp;<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: &nbsp;<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}} &nbsp;[ <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
diff --git a/setup.py b/setup.py
index 5113d75..af1560d 100644
--- a/setup.py
+++ b/setup.py
@@ -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',
+ ])