[MERGE] note: remove optionals tags, pad: fix lazy pad creation
bzr revid: al@openerp.com-20120920235343-tveo33j410atkt2y
This commit is contained in:
commit
283131289d
|
@ -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'),
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
id,name,implied_ids/id
|
||||
group_note_tags,Memo / Display tags,
|
||||
group_note_fancy,Memo / Fancy mode,
|
||||
|
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
})
|
|
@ -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');
|
||||
|
||||
|
|
Loading…
Reference in New Issue