[MERGE] note: remove optionals tags, pad: fix lazy pad creation

bzr revid: al@openerp.com-20120920235343-tveo33j410atkt2y
This commit is contained in:
Antony Lesuisse 2012-09-21 01:53:43 +02:00
commit 283131289d
7 changed files with 320 additions and 56 deletions

View File

@ -185,5 +185,4 @@ class note_base_config_settings(osv.osv_memory):
_columns = {
'module_note_pad': fields.boolean('Use collaborative pads (etherpad)'),
'group_note_fancy': fields.boolean('Use fancy layouts for notes', implied_group='note.group_note_fancy'),
'group_note_tags': fields.boolean('Allow setting tags on notes', implied_group='note.group_note_tags'),
}

View File

@ -4,28 +4,27 @@
<record model="note.stage" id="demo_note_stage_01">
<field name="name">Today</field>
<field name="sequence">1</field>
<field name="user_id" eval="ref('base.user_demo')"/>
<field name="user_id" ref="base.user_demo"/>
</record>
<record model="note.stage" id="demo_note_stage_02">
<field name="name">Tomorrow</field>
<field name="sequence">2</field>
<field name="user_id" eval="ref('base.user_demo')"/>
<field name="user_id" ref="base.user_demo"/>
</record>
<record model="note.stage" id="demo_note_stage_03">
<field name="name">Later</field>
<field name="sequence">3</field>
<field name="user_id" eval="ref('base.user_demo')"/>
<field name="user_id" ref="base.user_demo"/>
</record>
<record model="note.stage" id="demo_note_stage_04">
<field name="name">Notes</field>
<field name="name">Morning</field>
<field name="sequence">4</field>
<field name="user_id" eval="ref('base.user_demo')"/>
<field name="user_id" ref="base.user_root"/>
</record>
<record id="note_1" model="note.note">
<field name="name">Customer report #349872</field>
<field name="memo"><![CDATA[<b>Customer report #349872</b>
@ -35,8 +34,9 @@
<br/>* We should use the term Calendar, not Meeting.
]]>
</field>
<field name="stage_id" ref="note_stage_01"/>
<field name="stage_id" ref="demo_note_stage_01"/>
<field name="color">2</field>
<field name="message_follower_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="note_2" model="note.note">
@ -44,14 +44,7 @@
<br/><br/>* Followed by the telephone conversation and mail about D.544.3
]]>
</field>
<field name="stage_id" ref="note_stage_01"/>
</record>
<record id="note_3" model="note.note">
<field name="memo"><![CDATA[<b>Call Marc</b>
<br/><br/>]]>
</field>
<field name="stage_id" ref="note_stage_01"/>
<field name="stage_id" ref="demo_note_stage_01"/>
</record>
<record id="note_4" model="note.note">
@ -67,7 +60,8 @@
<br/>* wine
]]>
</field>
<field name="stage_id" ref="note_stage_02"/>
<field name="stage_id" ref="demo_note_stage_02"/>
<field name="message_follower_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="note_6" model="note.note">
@ -79,15 +73,6 @@
<field name="stage_id" ref="note_stage_02"/>
</record>
<record id="note_7" model="note.note">
<field name="memo"><![CDATA[<b>Read some documentation about OpenERP before diving into the code</b>
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
</field>
<field name="stage_id" ref="note_stage_03"/>
</record>
<record id="note_8" model="note.note">
<field name="memo"><![CDATA[<b>New computer specs</b>
<br/><br/>* Motherboard
@ -108,6 +93,7 @@
</field>
<field name="stage_id" ref="note_stage_03"/>
<field name="color">3</field>
<field name="message_follower_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="note_9" model="note.note">
@ -116,16 +102,7 @@
<br/>* Open ERP for Retail and Industrial Management
]]>
</field>
<field name="stage_id" ref="note_stage_02"/>
</record>
<record id="note_10" model="note.note">
<field name="memo"><![CDATA[<b>Read some documentation about OpenERP before diving into the code</b>
<br/><br/>* Open ERP: a modern approach to integrated business management
<br/>* Open ERP for Retail and Industrial Management
]]>
</field>
<field name="stage_id" ref="note_stage_03"/>
<field name="stage_id" ref="demo_note_stage_02"/>
</record>
<record id="note_12" model="note.note">
@ -134,8 +111,9 @@
<br/>* Open ERP for Retail and Industrial Management
]]>
</field>
<field name="stage_id" ref="note_stage_03"/>
<field name="message_follower_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="color">7</field>
<field name="stage_ids" eval="['note_stage_03','demo_note_stage_04']"/>
</record>
</data>

View File

@ -101,7 +101,7 @@
<field name="name"/>
<field name="open"/>
<field name="stage_id"/>
<field name="tag_ids" widget="many2many_tags" groups="note.group_note_tags"/>
<field name="tag_ids" widget="many2many_tags"/>
</tree>
</field>
</record>
@ -130,7 +130,7 @@
<field name="arch" type="xml">
<search string="Notes">
<field name="memo" string="Note"/>
<field name="tag_ids" groups="note.group_note_tags"/>
<field name="tag_ids"/>
<filter name="open_true" string="Active" domain="['|',('open', '=', True),('date_done','=',time.strftime('%%Y-%%m-%%d'))]"/>
<filter name="open_false" string="Archive" domain="[('open', '=', False)]"/>
<group expand="0" string="Group By...">
@ -159,10 +159,6 @@
<field name="group_note_fancy" class="oe_inline"/>
<label for="group_note_fancy"/>
</div>
<div>
<field name="group_note_tags" class="oe_inline"/>
<label for="group_note_tags"/>
</div>
</div>
</group>
</xpath>

View File

@ -1,3 +1,2 @@
id,name,implied_ids/id
group_note_tags,Memo / Display tags,
group_note_fancy,Memo / Fancy mode,

1 id name implied_ids/id
group_note_tags Memo / Display tags
2 group_note_fancy Memo / Fancy mode

View File

@ -5,24 +5,56 @@ import re
import string
import urllib2
from tools.translate import _
from openerp.tools.misc import html2plaintext
from py_etherpad import EtherpadLiteClient
class pad_common(osv.osv_memory):
_name = 'pad.common'
def pad_generate_url(self, cr, uid, context=None):
pad_server = self.pool.get('res.users').browse(cr, uid, uid, context).company_id.pad_server
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id;
pad = {
"server" : company.pad_server,
"key" : company.pad_key or "4DxmsNIbnQUVQMW9S9tx2oLOSjFdrx1l",
}
# make sure pad server in the form of http://hostname
if not pad_server:
if not pad["server"]:
return ''
if not pad_server.startswith('http'):
pad_server = 'http://' + pad_server
pad_server = pad_server.rstrip('/')
if not pad["server"].startswith('http'):
pad["server"] = 'http://' + pad["server"]
pad["server"] = pad["server"].rstrip('/')
# generate a salt
s = string.ascii_uppercase + string.digits
salt = ''.join([s[random.randint(0, len(s) - 1)] for i in range(10)])
#path
path = '%s-%s-%s' % (cr.dbname.replace('_','-'), self._name, salt)
# contruct the url
url = '%s/p/%s-%s-%s' % (pad_server, cr.dbname.replace('_','-'), self._name, salt)
return url
url = '%s/p/%s' % (pad["server"], path)
#if create with content
if "field_name" in context and "model" in context and "object_id" in context:
myPad = EtherpadLiteClient( pad["key"], pad["server"]+'/api')
myPad.createPad(path)
#get attr on the field model
model = self.pool.get(context["model"])
field = model._all_columns[context['field_name']]
real_field = field.column.pad_content_field
#get content of the real field
for record in model.browse(cr, uid, [context["object_id"]]):
if record[real_field]:
myPad.setText(path, html2plaintext(record[real_field]))
#Etherpad for html not functional
#myPad.setHTML(path, record[real_field])
return {
"server": pad["server"],
"path": path,
"url": url,
}
def pad_get_content(self, cr, uid, url, context=None):
content = ''
@ -57,7 +89,8 @@ class pad_common(osv.osv_memory):
for k,v in self._all_columns:
field = v.column
if hasattr(field,'pad_content_field'):
default[k] = self.pad_generate_url(cr, uid, context)
pad = self.pad_generate_url(cr, uid, context)
default[k] = pad['url']
return super(pad_common, self).copy(cr, uid, id, default, context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,253 @@
#!/usr/bin/env python
"""Module to talk to EtherpadLite API."""
import json
import urllib
import urllib2
class EtherpadLiteClient:
"""Client to talk to EtherpadLite API."""
API_VERSION = 1 # TODO probably 1.1 sometime soon
CODE_OK = 0
CODE_INVALID_PARAMETERS = 1
CODE_INTERNAL_ERROR = 2
CODE_INVALID_FUNCTION = 3
CODE_INVALID_API_KEY = 4
TIMEOUT = 20
apiKey = ""
baseUrl = "http://localhost:9001/api"
def __init__(self, apiKey=None, baseUrl=None):
if apiKey:
self.apiKey = apiKey
if baseUrl:
self.baseUrl = baseUrl
def call(self, function, arguments=None):
"""Create a dictionary of all parameters"""
url = '%s/%d/%s' % (self.baseUrl, self.API_VERSION, function)
params = arguments or {}
params.update({'apikey': self.apiKey})
data = urllib.urlencode(params, True)
try:
opener = urllib2.build_opener()
request = urllib2.Request(url=url, data=data)
response = opener.open(request, timeout=self.TIMEOUT)
result = response.read()
response.close()
except urllib2.HTTPError:
raise
result = json.loads(result)
if result is None:
raise ValueError("JSON response could not be decoded")
return self.handleResult(result)
def handleResult(self, result):
"""Handle API call result"""
if 'code' not in result:
raise Exception("API response has no code")
if 'message' not in result:
raise Exception("API response has no message")
if 'data' not in result:
result['data'] = None
if result['code'] == self.CODE_OK:
return result['data']
elif result['code'] == self.CODE_INVALID_PARAMETERS or result['code'] == self.CODE_INVALID_API_KEY:
raise ValueError(result['message'])
elif result['code'] == self.CODE_INTERNAL_ERROR:
raise Exception(result['message'])
elif result['code'] == self.CODE_INVALID_FUNCTION:
raise Exception(result['message'])
else:
raise Exception("An unexpected error occurred whilst handling the response")
# GROUPS
# Pads can belong to a group. There will always be public pads that do not belong to a group (or we give this group the id 0)
def createGroup(self):
"""creates a new group"""
return self.call("createGroup")
def createGroupIfNotExistsFor(self, groupMapper):
"""this functions helps you to map your application group ids to etherpad lite group ids"""
return self.call("createGroupIfNotExistsFor", {
"groupMapper": groupMapper
})
def deleteGroup(self, groupID):
"""deletes a group"""
return self.call("deleteGroup", {
"groupID": groupID
})
def listPads(self, groupID):
"""returns all pads of this group"""
return self.call("listPads", {
"groupID": groupID
})
def createGroupPad(self, groupID, padName, text=''):
"""creates a new pad in this group"""
params = {
"groupID": groupID,
"padName": padName,
}
if text:
params['text'] = text
return self.call("createGroupPad", params)
# AUTHORS
# Theses authors are bind to the attributes the users choose (color and name).
def createAuthor(self, name=''):
"""creates a new author"""
params = {}
if name:
params['name'] = name
return self.call("createAuthor", params)
def createAuthorIfNotExistsFor(self, authorMapper, name=''):
"""this functions helps you to map your application author ids to etherpad lite author ids"""
params = {
'authorMapper': authorMapper
}
if name:
params['name'] = name
return self.call("createAuthorIfNotExistsFor", params)
# SESSIONS
# Sessions can be created between a group and a author. This allows
# an author to access more than one group. The sessionID will be set as
# a cookie to the client and is valid until a certain date.
def createSession(self, groupID, authorID, validUntil):
"""creates a new session"""
return self.call("createSession", {
"groupID": groupID,
"authorID": authorID,
"validUntil": validUntil
})
def deleteSession(self, sessionID):
"""deletes a session"""
return self.call("deleteSession", {
"sessionID": sessionID
})
def getSessionInfo(self, sessionID):
"""returns informations about a session"""
return self.call("getSessionInfo", {
"sessionID": sessionID
})
def listSessionsOfGroup(self, groupID):
"""returns all sessions of a group"""
return self.call("listSessionsOfGroup", {
"groupID": groupID
})
def listSessionsOfAuthor(self, authorID):
"""returns all sessions of an author"""
return self.call("listSessionsOfAuthor", {
"authorID": authorID
})
# PAD CONTENT
# Pad content can be updated and retrieved through the API
def getText(self, padID, rev=None):
"""returns the text of a pad"""
params = {"padID": padID}
if rev is not None:
params['rev'] = rev
return self.call("getText", params)
# introduced with pull request merge
def getHtml(self, padID, rev=None):
"""returns the html of a pad"""
params = {"padID": padID}
if rev is not None:
params['rev'] = rev
return self.call("getHTML", params)
def setText(self, padID, text):
"""sets the text of a pad"""
return self.call("setText", {
"padID": padID,
"text": text
})
def setHtml(self, padID, html):
"""sets the text of a pad from html"""
return self.call("setHTML", {
"padID": padID,
"html": html
})
# PAD
# Group pads are normal pads, but with the name schema
# GROUPID$PADNAME. A security manager controls access of them and its
# forbidden for normal pads to include a in the name.
def createPad(self, padID, text=''):
"""creates a new pad"""
params = {
"padID": padID,
}
if text:
params['text'] = text
return self.call("createPad", params)
def getRevisionsCount(self, padID):
"""returns the number of revisions of this pad"""
return self.call("getRevisionsCount", {
"padID": padID
})
def deletePad(self, padID):
"""deletes a pad"""
return self.call("deletePad", {
"padID": padID
})
def getReadOnlyID(self, padID):
"""returns the read only link of a pad"""
return self.call("getReadOnlyID", {
"padID": padID
})
def setPublicStatus(self, padID, publicStatus):
"""sets a boolean for the public status of a pad"""
return self.call("setPublicStatus", {
"padID": padID,
"publicStatus": publicStatus
})
def getPublicStatus(self, padID):
"""return true of false"""
return self.call("getPublicStatus", {
"padID": padID
})
def setPassword(self, padID, password):
"""returns ok or a error message"""
return self.call("setPassword", {
"padID": padID,
"password": password
})
def isPasswordProtected(self, padID):
"""returns true or false"""
return self.call("isPasswordProtected", {
"padID": padID
})

View File

@ -16,10 +16,17 @@ instance.web.form.FieldPad = instance.web.form.AbstractField.extend({
var self = this;
var _super = self._super;
_super.apply(self,[val]);
if (val === false || val === "") {
self.field_manager.dataset.call('pad_generate_url').then(function(r) {
_super.apply(self,[r]);
self.render_value();
self.field_manager.dataset.call('pad_generate_url',{context:{
model: self.field_manager.model,
field_name: self.name,
object_id: self.field_manager.datarecord.id
}}).then(function(data) {
if(data&&data.url){
_super.apply(self,[data.url]);
self.render_value();
}
});
} else {
self.render_value();
@ -27,7 +34,6 @@ instance.web.form.FieldPad = instance.web.form.AbstractField.extend({
this._dirty_flag = true;
},
render_value: function() {
console.log("display");
var self = this;
var value = this.get('value');