[REM] document directories
Keeping the attachments widget on form views and document indexation (currently limited to pdf/odt).
This commit is contained in:
@@ -1,8 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
import content_index
|
import models
|
||||||
import std_index
|
|
||||||
import document
|
|
||||||
import report
|
|
||||||
import wizard
|
|
||||||
|
|||||||
@@ -1,36 +1,21 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Document Management System',
|
'name': 'Attachments List and Document Indexation',
|
||||||
'version': '2.1',
|
'version': '2.1',
|
||||||
'category': 'Knowledge Management',
|
'category': 'Knowledge Management',
|
||||||
'description': """
|
'description': """
|
||||||
This is a complete document management system.
|
Attachments list and document indexation
|
||||||
==============================================
|
========================================
|
||||||
* User Authentication
|
* Show attachment on the top of the forms
|
||||||
* Document Indexation:- .pptx and .docx files are not supported in Windows platform.
|
* Document Indexation: odt
|
||||||
* Dashboard for Document that includes:
|
|
||||||
* New Files (list)
|
|
||||||
* Files by Resource Type (graph)
|
|
||||||
* Files by Partner (graph)
|
|
||||||
* Files Size by Month (graph)
|
|
||||||
""",
|
""",
|
||||||
'author': 'OpenERP SA',
|
'author': 'OpenERP SA',
|
||||||
'website': 'https://www.odoo.com',
|
'website': 'https://www.odoo.com',
|
||||||
'depends': ['mail'],
|
'depends': ['web'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/document_security.xml',
|
|
||||||
'document_view.xml',
|
|
||||||
'document_data.xml',
|
|
||||||
'wizard/document_configuration_view.xml',
|
|
||||||
'security/ir.model.access.csv',
|
|
||||||
'report/document_report_view.xml',
|
|
||||||
'views/document.xml',
|
'views/document.xml',
|
||||||
],
|
],
|
||||||
'demo': [ 'document_demo.xml' ],
|
|
||||||
'test': ['test/document_test2.yml'],
|
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'auto_install': False,
|
'auto_install': False,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import tempfile
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class indexer(object):
|
|
||||||
""" An indexer knows how to parse the content of some file.
|
|
||||||
|
|
||||||
Typically, one indexer should be instantiated per file
|
|
||||||
type.
|
|
||||||
Override this class to add more functionality. Note that
|
|
||||||
you should only override the Content or the File methods
|
|
||||||
that give an optimal result. """
|
|
||||||
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
""" Return supported mimetypes """
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _getDefMime(self, ext):
|
|
||||||
""" Return a mimetype for this document type, ideally the
|
|
||||||
closest to the extension ext. """
|
|
||||||
mts = self._getMimeTypes();
|
|
||||||
if len (mts):
|
|
||||||
return mts[0]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def indexContent(self, content, filename=None, realfile=None):
|
|
||||||
""" Use either content or the real file, to index.
|
|
||||||
Some parsers will work better with the actual
|
|
||||||
content, others parse a file easier. Try the
|
|
||||||
optimal.
|
|
||||||
"""
|
|
||||||
res = ''
|
|
||||||
try:
|
|
||||||
if content != None:
|
|
||||||
return self._doIndexContent(content)
|
|
||||||
except NotImplementedError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if realfile != None:
|
|
||||||
try:
|
|
||||||
return self._doIndexFile(realfile)
|
|
||||||
except NotImplementedError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
fp = open(realfile,'rb')
|
|
||||||
try:
|
|
||||||
content2 = fp.read()
|
|
||||||
finally:
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
# The not-handled exception may be raised here
|
|
||||||
return self._doIndexContent(content2)
|
|
||||||
|
|
||||||
|
|
||||||
# last try, with a tmp file
|
|
||||||
if content:
|
|
||||||
try:
|
|
||||||
fname,ext = filename and os.path.splitext(filename) or ('','')
|
|
||||||
fd, rfname = tempfile.mkstemp(suffix=ext)
|
|
||||||
os.write(fd, content)
|
|
||||||
os.close(fd)
|
|
||||||
res = self._doIndexFile(rfname)
|
|
||||||
os.unlink(rfname)
|
|
||||||
return res
|
|
||||||
except NotImplementedError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
raise ValueError('No appropriate method to index file.')
|
|
||||||
|
|
||||||
def _doIndexContent(self, content):
|
|
||||||
raise NotImplementedError("Content cannot be handled here.")
|
|
||||||
|
|
||||||
def _doIndexFile(self, fpath):
|
|
||||||
raise NotImplementedError("Content cannot be handled here.")
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<indexer %s.%s>" %(self.__module__, self.__class__.__name__)
|
|
||||||
|
|
||||||
def mime_match(mime, mdict):
|
|
||||||
if mdict.has_key(mime):
|
|
||||||
return (mime, mdict[mime])
|
|
||||||
if '/' in mime:
|
|
||||||
mpat = mime.split('/')[0]+'/*'
|
|
||||||
if mdict.has_key(mpat):
|
|
||||||
return (mime, mdict[mpat])
|
|
||||||
|
|
||||||
return (None, None)
|
|
||||||
|
|
||||||
class contentIndex(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.mimes = {}
|
|
||||||
self.exts = {}
|
|
||||||
|
|
||||||
def register(self, obj):
|
|
||||||
f = False
|
|
||||||
for mime in obj._getMimeTypes():
|
|
||||||
self.mimes[mime] = obj
|
|
||||||
f = True
|
|
||||||
|
|
||||||
for ext in obj._getExtensions():
|
|
||||||
self.exts[ext] = obj
|
|
||||||
f = True
|
|
||||||
|
|
||||||
if not f:
|
|
||||||
raise ValueError("Your indexer should at least support a mimetype or extension.")
|
|
||||||
_logger.debug('Register content indexer: %r.', obj)
|
|
||||||
|
|
||||||
def doIndex(self, content, filename=None, content_type=None, realfname=None, debug=False):
|
|
||||||
fobj = None
|
|
||||||
fname = None
|
|
||||||
mime = None
|
|
||||||
if content_type and self.mimes.has_key(content_type):
|
|
||||||
mime = content_type
|
|
||||||
fobj = self.mimes[content_type]
|
|
||||||
elif filename:
|
|
||||||
bname,ext = os.path.splitext(filename)
|
|
||||||
if self.exts.has_key(ext):
|
|
||||||
fobj = self.exts[ext]
|
|
||||||
mime = fobj._getDefMime(ext)
|
|
||||||
|
|
||||||
if content_type and not fobj:
|
|
||||||
mime,fobj = mime_match(content_type, self.mimes)
|
|
||||||
|
|
||||||
if not fobj:
|
|
||||||
try:
|
|
||||||
if realfname :
|
|
||||||
fname = realfname
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
bname,ext = os.path.splitext(filename or 'test.tmp')
|
|
||||||
except Exception:
|
|
||||||
bname, ext = filename, 'tmp'
|
|
||||||
fd, fname = tempfile.mkstemp(suffix=ext)
|
|
||||||
os.write(fd, content)
|
|
||||||
os.close(fd)
|
|
||||||
|
|
||||||
pop = Popen(['file','-b','--mime',fname], shell=False, stdout=PIPE)
|
|
||||||
(result, _) = pop.communicate()
|
|
||||||
|
|
||||||
mime2 = result.split(';')[0]
|
|
||||||
_logger.debug('File gives us: %s', mime2)
|
|
||||||
# Note that the temporary file still exists now.
|
|
||||||
mime,fobj = mime_match(mime2, self.mimes)
|
|
||||||
if not mime:
|
|
||||||
mime = mime2
|
|
||||||
except Exception:
|
|
||||||
_logger.info('Cannot determine mime type.', exc_info=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if fobj:
|
|
||||||
res = (mime, fobj.indexContent(content,filename,fname or realfname) )
|
|
||||||
else:
|
|
||||||
_logger.debug("Have no object, return (%s, None).", mime)
|
|
||||||
res = (mime, '')
|
|
||||||
except Exception:
|
|
||||||
_logger.info("Cannot index file %s (%s).",
|
|
||||||
filename, fname or realfname, exc_info=True)
|
|
||||||
res = (mime, '')
|
|
||||||
|
|
||||||
# If we created a tmp file, unlink it now
|
|
||||||
if not realfname and fname:
|
|
||||||
try:
|
|
||||||
os.unlink(fname)
|
|
||||||
except Exception:
|
|
||||||
_logger.exception("Cannot unlink %s.", fname)
|
|
||||||
return res
|
|
||||||
|
|
||||||
cntIndex = contentIndex()
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
Access control in the Document Management system
|
|
||||||
================================================
|
|
||||||
|
|
||||||
The purpose is to let the DMS act as a real-life management system for
|
|
||||||
the file handling of some small business.
|
|
||||||
The key concept, there, is the separation of access according to users
|
|
||||||
and groups.
|
|
||||||
|
|
||||||
Fact 1: Users, in general, must NOT see each other's documents, not even
|
|
||||||
their names (because they usually imply sensitive data, like eg. a doc:
|
|
||||||
"Acme Company's Patent 012356 about fooobars.odf" )
|
|
||||||
Fact 2: Users, sometimes, fail to comprehend complex ACL schemes, so we
|
|
||||||
owe to keep things simple, a main principle applied all over the place.
|
|
||||||
Fact 3: our system has both statically placed files and directories, as
|
|
||||||
well as dynamic (aka "resources" in our terminology) nodes.
|
|
||||||
|
|
||||||
We allow/limit the access based on 3 factors (fields):
|
|
||||||
- The "owner" field, which holds the user that created or "owns" the
|
|
||||||
file or directory.
|
|
||||||
- The "group_ids" field, on directories, which specifieds group-wise
|
|
||||||
access
|
|
||||||
- The "company_id" field, for multi-company access rules [1]
|
|
||||||
|
|
||||||
[1] at multi-company setups, we may want the same file hierarchy to apply
|
|
||||||
to different companies, and also nodes to be company-specific in it.
|
|
||||||
|
|
||||||
Principle of "owner"
|
|
||||||
----------------------
|
|
||||||
Files or directories that have an empty "owner" field are public. All users
|
|
||||||
will be able to _read_ them. Only the OpenERP Administrator or specified
|
|
||||||
groups, however, will be able to modify them!
|
|
||||||
Files or directories that have an "owner" are private. Only their users will
|
|
||||||
be able to read or modify (including delete) them.
|
|
||||||
By default, all user's files are created with "owner" field set, thus private.
|
|
||||||
|
|
||||||
Principle of "group_ids"
|
|
||||||
-------------------------
|
|
||||||
Directories that have any group ids set will only (apart from their owner)
|
|
||||||
allow members of these groups to read them.
|
|
||||||
Directories that are created into the above directories will initially inherit
|
|
||||||
(that is, copy) the group_ids of their parents, so that they also allow
|
|
||||||
access to the same users.
|
|
||||||
|
|
||||||
Implementation note
|
|
||||||
---------------------
|
|
||||||
Most of the principles are applied through record rules (see ir.rule object),
|
|
||||||
so an administrator can actually readjust them.
|
|
||||||
In order to have logical "areas" of folders, where different policies apply
|
|
||||||
(like group folders, personal areas), default values for directories' owners
|
|
||||||
and group_ids can be tuned (through the 'set default' functionality of
|
|
||||||
fields).
|
|
||||||
|
|
||||||
Summary
|
|
||||||
--------
|
|
||||||
|
|
||||||
Table of permissions and behavior
|
|
||||||
|
|
||||||
|| Type | Owner set | Groups set | Description ||
|
|
||||||
|| Public | - | - | Public-readable folders, admin can write ||
|
|
||||||
|| Group | - | X | Group can read, write, delete in them ||
|
|
||||||
|| Group-read | X | X | Group can read[2], owner can write/delete ||
|
|
||||||
|| Private | X | - | Only owner can read, write, delete in. ||
|
|
||||||
|
|
||||||
[2] hint: using a wide group, like "Internal users" at this setup creates the
|
|
||||||
effect of public-readable folders, with write permission to a non-admin user.
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,82 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record model="document.directory" id="dir_root">
|
|
||||||
<field name="name">Documents</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_my_folder">
|
|
||||||
<field name="name">Admin Folder</field>
|
|
||||||
<field name="parent_id" ref="dir_root"/>
|
|
||||||
<field name="user_id" ref="base.user_root"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<data noupdate="1">
|
|
||||||
<record model="document.directory.content.type" id="pdf">
|
|
||||||
<field name="code">.pdf</field>
|
|
||||||
<field name="name">PDF Report</field>
|
|
||||||
<field name="mimetype">application/pdf</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_partner">
|
|
||||||
<field name="name">Partners</field>
|
|
||||||
<field name="type">ressource</field>
|
|
||||||
<field name="ressource_type_id" search="[('model','=','res.partner')]" />
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="parent_id" ref="dir_root"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_personnal_folder">
|
|
||||||
<field name="name">Personal Folders</field>
|
|
||||||
<field name="parent_id" ref="dir_root"/>
|
|
||||||
<field name="type">ressource</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="ressource_type_id" ref="base.model_res_users" />
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
</record>
|
|
||||||
<record model="document.directory" id="dir_product">
|
|
||||||
<field name="name">Products</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="parent_id" ref="dir_root"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_sale_order">
|
|
||||||
<field name="name">Sales Order</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="parent_id" ref="dir_root"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_sale_order_all">
|
|
||||||
<field name="name">All Sales Order</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="parent_id" ref="dir_sale_order"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_sale_order_quote">
|
|
||||||
<field name="name">Quotations</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="parent_id" ref="dir_sale_order"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="document.directory" id="dir_project">
|
|
||||||
<field name="name">Projects</field>
|
|
||||||
<field name="user_id" eval="False"/>
|
|
||||||
<field name="parent_id" ref="dir_root"/>
|
|
||||||
<field name="ressource_id">0</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data noupdate="1">
|
|
||||||
<record id="base.user_demo" model="res.users">
|
|
||||||
<field eval="[(4, ref('base.group_document_user'))]" name="groups_id"/>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
<menuitem name="Knowledge" id="menu_document" groups="base.group_system,base.group_document_user" icon="fa-magic" sequence="300"/>
|
|
||||||
<menuitem name="Document Management" id="menu_document_management_configuration" parent="menu_document" sequence="1" groups="base.group_no_one"/>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_document_directory_form">
|
|
||||||
<field name="name">document.directory</field>
|
|
||||||
<field name="model">document.directory</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Directories" col="4">
|
|
||||||
<group col="4">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="parent_id"/>
|
|
||||||
<field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_document_user']}"/>
|
|
||||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
|
||||||
</group>
|
|
||||||
<notebook colspan="4">
|
|
||||||
<page string="Definition">
|
|
||||||
<group string="Directory Type" colspan="4">
|
|
||||||
<field name="type"/>
|
|
||||||
</group>
|
|
||||||
<group col="4" attrs="{'invisible': [('type','!=','ressource')]}">
|
|
||||||
<field name="ressource_type_id" on_change="onchange_content_id(ressource_type_id)"
|
|
||||||
attrs="{'required': [('type','=','ressource')] }"/>
|
|
||||||
<newline/>
|
|
||||||
<field name="resource_field" domain="[('model_id','=',ressource_type_id), ('ttype', 'in', ('char', 'selection', 'date', 'datetime'))]"/>
|
|
||||||
<field name="ressource_tree"/>
|
|
||||||
<newline/>
|
|
||||||
<field name="domain" attrs="{'required': [('type','=','ressource')], 'readonly': [('type','=','static')]}"/>
|
|
||||||
</group>
|
|
||||||
<group col="4">
|
|
||||||
<field name="ressource_parent_type_id"/>
|
|
||||||
<field name="ressource_id" readonly="1"/>
|
|
||||||
</group>
|
|
||||||
<group col="2" attrs="{'invisible': [('type','!=','ressource'),('ressource_parent_type_id','=',False)]}">
|
|
||||||
<field name="resource_find_all"/>
|
|
||||||
</group>
|
|
||||||
|
|
||||||
</page>
|
|
||||||
<page string="Generated Files" groups="base.group_no_one">
|
|
||||||
<label colspan="4" string="For each entry here, virtual files will appear in this folder." />
|
|
||||||
<field name="content_ids" nolabel="1" colspan="4" >
|
|
||||||
<form string="Contents">
|
|
||||||
<group col="4">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="sequence"/>
|
|
||||||
<field name="prefix"/>
|
|
||||||
<field name="suffix"/>
|
|
||||||
<field name="extension"/>
|
|
||||||
<field name="include_name"/>
|
|
||||||
<separator string="PDF Report" colspan="4"/>
|
|
||||||
<field name="report_id" domain="[('model_id','=',parent.ressource_type_id)]"/>
|
|
||||||
</group>
|
|
||||||
</form>
|
|
||||||
<tree string="Contents">
|
|
||||||
<field name="sequence" string="Seq."/>
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="suffix"/>
|
|
||||||
<field name="extension"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
<page string="Dynamic context" groups="base.group_no_one">
|
|
||||||
<label colspan="4" string="Define words in the context, for all child directories and files" />
|
|
||||||
<field name="dctx_ids" nolabel="1" colspan="4">
|
|
||||||
<tree string="Fields" editable="bottom">
|
|
||||||
<field name="field"/>
|
|
||||||
<field name="expr"/>
|
|
||||||
</tree>
|
|
||||||
<form string="Fields">
|
|
||||||
<group col="4">
|
|
||||||
<field name="field"/>
|
|
||||||
<field name="expr"/>
|
|
||||||
</group>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
<page string="Security">
|
|
||||||
<label colspan="4" string="Only members of these groups will have access to this directory and its files." />
|
|
||||||
<label colspan="4" string="These groups, however, do NOT apply to children directories, which must define their own groups." />
|
|
||||||
<field name="group_ids" colspan="4" nolabel="1"/>
|
|
||||||
</page>
|
|
||||||
</notebook>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="view_document_directory_tree">
|
|
||||||
<field name="name">document.directory</field>
|
|
||||||
<field name="model">document.directory</field>
|
|
||||||
<field name="field_parent">child_ids</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Directories" toolbar="1">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="type"/>
|
|
||||||
<field name="user_id"/>
|
|
||||||
<field name="company_id" groups="base.group_multi_company"/>
|
|
||||||
<field name="create_date"/>
|
|
||||||
<field name="write_date"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_document_directory_filter" model="ir.ui.view">
|
|
||||||
<field name="name">Search View: Document Directory</field>
|
|
||||||
<field name="model">document.directory</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<search string="Search Document Directory">
|
|
||||||
<field name="name" string="Document Directory"/>
|
|
||||||
<filter string="Static" domain="[('type','=','directory')]"/>
|
|
||||||
<filter string="Resources" icon="terp-personal" domain="[('type','=','ressource')]"/>
|
|
||||||
<field name="company_id" groups="base.group_multi_company"/>
|
|
||||||
<field name="user_id" />
|
|
||||||
<group expand="0" string="Group By">
|
|
||||||
<filter string="Owner" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
|
|
||||||
<filter string="Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'type'}"/>
|
|
||||||
<filter string="Company" domain="[]" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
|
|
||||||
</group>
|
|
||||||
</search>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_document_directory_form">
|
|
||||||
<field name="name">Directories</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">document.directory</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="search_view_id" ref="view_document_directory_filter"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="action_dir_view1" model="ir.actions.act_window.view">
|
|
||||||
<field eval="10" name="sequence"/>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
<field name="view_id" ref="view_document_directory_tree"/>
|
|
||||||
<field name="act_window_id" ref="action_document_directory_form"/>
|
|
||||||
</record>
|
|
||||||
<record id="action_dir_view2" model="ir.actions.act_window.view">
|
|
||||||
<field eval="20" name="sequence"/>
|
|
||||||
<field name="view_mode">form</field>
|
|
||||||
<field name="view_id" ref="view_document_directory_form"/>
|
|
||||||
<field name="act_window_id" ref="action_document_directory_form"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem
|
|
||||||
action="action_document_directory_form"
|
|
||||||
id="menu_document_directories"
|
|
||||||
parent="menu_document_management_configuration"/>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_document_directory_tree">
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="name">Directories' Structure</field>
|
|
||||||
<field name="res_model">document.directory</field>
|
|
||||||
<field name="view_type">tree</field>
|
|
||||||
<field name="view_id" ref="document.view_document_directory_tree"/>
|
|
||||||
<field name="domain">[('ressource_parent_type_id','=',False),('parent_id','=',False)]</field>
|
|
||||||
</record>
|
|
||||||
<menuitem
|
|
||||||
action="action_document_directory_tree"
|
|
||||||
id="menu_document_directories_tree"
|
|
||||||
parent="menu_document_management_configuration" sequence="5"/>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_document_file_form">
|
|
||||||
<field name="name">ir.attachment</field>
|
|
||||||
<field name="model">ir.attachment</field>
|
|
||||||
<field name="priority" eval="1"/>
|
|
||||||
<field name="inherit_id" ref="base.view_attachment_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//field[@name='datas_fname']" position="replace">
|
|
||||||
<field name="datas_fname" invisible="1"/>
|
|
||||||
</xpath>
|
|
||||||
<field name="url" position="after">
|
|
||||||
<field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_document_user']}"/>
|
|
||||||
</field>
|
|
||||||
<field name="company_id" position="before">
|
|
||||||
<field name="parent_id"/>
|
|
||||||
<field name="partner_id"/>
|
|
||||||
</field>
|
|
||||||
<xpath expr="//div[@name='creation_div']" position='after'>
|
|
||||||
<label for="write_uid" string="Modification"/>
|
|
||||||
<div>
|
|
||||||
<field name="write_uid" readonly="1" class="oe_inline"/> on
|
|
||||||
<field name="write_date" readonly="1" class="oe_inline"/>
|
|
||||||
</div>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_attach_filter_inherit0" model="ir.ui.view">
|
|
||||||
<field name="name">IR Attachment0</field>
|
|
||||||
<field name="model">ir.attachment</field>
|
|
||||||
<field name="inherit_id" ref="base.view_attachment_search"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//filter[@name='my_documents_filter']" position="replace">
|
|
||||||
<filter name="my_documents_filter" string="My Document(s)" icon="terp-personal" domain="[('user_id','=',uid)]" help="Filter on my documents"/>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//field[@name='create_uid']" position="replace">
|
|
||||||
<field name="user_id"/>
|
|
||||||
<field name="parent_id" />
|
|
||||||
<field name="index_content"/>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
<record id="view_attach_filter_inherit2" model="ir.ui.view">
|
|
||||||
<field name="name">IR Attachment2</field>
|
|
||||||
<field name="model">ir.attachment</field>
|
|
||||||
<field name="inherit_id" ref="base.view_attachment_search"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<filter name="owner" position="replace">
|
|
||||||
<filter string="Owner" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
|
|
||||||
<filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
|
||||||
<filter string="Directory" icon="terp-folder-green" domain="[]" context="{'group_by':'parent_id'}"/>
|
|
||||||
</filter>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_document_file_tree">
|
|
||||||
<field name="name">ir.attachment</field>
|
|
||||||
<field name="model">ir.attachment</field>
|
|
||||||
<field name="priority" eval="1"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree decoration-info="type in ('url',)" string="Attachments">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="parent_id" />
|
|
||||||
<field name="user_id"/>
|
|
||||||
<field name="company_id"/>
|
|
||||||
<field name="create_date" groups="base.group_no_one"/>
|
|
||||||
<field name="write_date" groups="base.group_no_one"/>
|
|
||||||
<field name="partner_id"/>
|
|
||||||
<field name="type"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_document_file_form">
|
|
||||||
<field name="name">Documents</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">ir.attachment</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">kanban,tree,form</field>
|
|
||||||
<field name="help" type="html">
|
|
||||||
<p class="oe_view_nocontent_create">
|
|
||||||
Click to create a new document.
|
|
||||||
</p><p>
|
|
||||||
The Documents repository gives you access to all attachments, such
|
|
||||||
as mails, project documents, invoices etc.
|
|
||||||
</p>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_document_file_directory_form">
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">ir.attachment</field>
|
|
||||||
<field name="name">Directory</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="domain">[('parent_id','child_of',active_id)]</field>
|
|
||||||
<field name="context">{'parent_id':active_id}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.values" id="ir_action_document_file_directory_form">
|
|
||||||
<field name="key2" eval="'tree_but_open'"/>
|
|
||||||
<field name="model" eval="'document.directory'"/>
|
|
||||||
<field name="name">Browse Files</field>
|
|
||||||
<field name="value" eval="'ir.actions.act_window,%d'%action_document_file_directory_form"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<act_window domain="[('partner_id', '=', active_id)]"
|
|
||||||
context="{'default_partner_id': active_id}"
|
|
||||||
id="act_res_partner_document" name="Related Documents"
|
|
||||||
res_model="ir.attachment"
|
|
||||||
src_model="res.partner"/>
|
|
||||||
|
|
||||||
<act_window
|
|
||||||
domain="[('parent_id', '=', active_id)]"
|
|
||||||
context="{'default_parent_id': active_id}"
|
|
||||||
id="zoom_directory" name="Related Documents"
|
|
||||||
res_model="ir.attachment"
|
|
||||||
src_model="document.directory"/>
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
import document_report
|
|
||||||
|
import ir_attachment
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import logging
|
||||||
|
import zipfile
|
||||||
|
import xml.dom.minidom
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
import pyPdf
|
||||||
|
|
||||||
|
import openerp
|
||||||
|
from openerp.osv import fields, osv
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class IrAttachment(osv.osv):
|
||||||
|
_inherit = 'ir.attachment'
|
||||||
|
|
||||||
|
def _index_odt(self, bin_data):
|
||||||
|
buf = u""
|
||||||
|
f = StringIO(bin_data)
|
||||||
|
if zipfile.is_zipfile(f):
|
||||||
|
try:
|
||||||
|
zf = zipfile.ZipFile(f)
|
||||||
|
self.content = xml.dom.minidom.parseString(zf.read("content.xml"))
|
||||||
|
for val in ["text:p", "text:h", "text:list"]:
|
||||||
|
for element in self.content.getElementsByTagName(val) :
|
||||||
|
for node in element.childNodes :
|
||||||
|
if node.nodeType == xml.dom.Node.TEXT_NODE :
|
||||||
|
buf += node.nodeValue
|
||||||
|
elif node.nodeType == xml.dom.Node.ELEMENT_NODE :
|
||||||
|
buf += self.textToString(node)
|
||||||
|
buf += "\n"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return buf
|
||||||
|
|
||||||
|
def _index_pdf(self, bin_data):
|
||||||
|
buf = u""
|
||||||
|
if bin_data.startswith('%PDF-'):
|
||||||
|
f = StringIO(bin_data)
|
||||||
|
try:
|
||||||
|
pdf = pyPdf.PdfFileReader(f)
|
||||||
|
for page in pdf.pages:
|
||||||
|
buf += page.extractText()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return buf
|
||||||
|
|
||||||
|
def _index(self, cr, uid, bin_data, datas_fname, mimetype):
|
||||||
|
# try to index odt content
|
||||||
|
buf = self._index_odt(bin_data)
|
||||||
|
if buf:
|
||||||
|
return buf
|
||||||
|
# try to index pdf content
|
||||||
|
buf = self._index_pdf(bin_data)
|
||||||
|
if buf:
|
||||||
|
return buf
|
||||||
|
|
||||||
|
return super(IrAttachment, self)._index(cr, uid, bin_data, datas_fname, mimetype)
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
|
|
||||||
import sys, zipfile, xml.dom.minidom
|
|
||||||
import StringIO
|
|
||||||
|
|
||||||
class OpenDocumentTextFile :
|
|
||||||
def __init__ (self, filepath):
|
|
||||||
zip = zipfile.ZipFile(filepath)
|
|
||||||
self.content = xml.dom.minidom.parseString(zip.read("content.xml"))
|
|
||||||
|
|
||||||
def toString (self):
|
|
||||||
""" Converts the document to a string. """
|
|
||||||
buffer = u""
|
|
||||||
for val in ["text:p", "text:h", "text:list"]:
|
|
||||||
for paragraph in self.content.getElementsByTagName(val) :
|
|
||||||
buffer += self.textToString(paragraph) + "\n"
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
def textToString(self, element):
|
|
||||||
buffer = u""
|
|
||||||
for node in element.childNodes :
|
|
||||||
if node.nodeType == xml.dom.Node.TEXT_NODE :
|
|
||||||
buffer += node.nodeValue
|
|
||||||
elif node.nodeType == xml.dom.Node.ELEMENT_NODE :
|
|
||||||
buffer += self.textToString(node)
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
if __name__ == "__main__" :
|
|
||||||
s =StringIO.StringIO(file(sys.argv[1]).read())
|
|
||||||
odt = OpenDocumentTextFile(s)
|
|
||||||
print odt.toString().encode('ascii','replace')
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
|
|
||||||
|
|
||||||
from openerp.osv import fields,osv
|
|
||||||
from openerp import tools
|
|
||||||
|
|
||||||
class report_document_user(osv.osv):
|
|
||||||
_name = "report.document.user"
|
|
||||||
_description = "Files details by Users"
|
|
||||||
_auto = False
|
|
||||||
_columns = {
|
|
||||||
'name': fields.char('Year', size=64,readonly=True),
|
|
||||||
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
|
|
||||||
('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
|
|
||||||
'user_id': fields.many2one('res.users', 'Owner', readonly=True),
|
|
||||||
'user': fields.related('user_id', 'name', type='char', size=64, readonly=True),
|
|
||||||
'directory': fields.char('Directory',size=64,readonly=True),
|
|
||||||
'datas_fname': fields.char('File Name',size=64,readonly=True),
|
|
||||||
'create_date': fields.datetime('Date Created', readonly=True),
|
|
||||||
'change_date': fields.datetime('Modified Date', readonly=True),
|
|
||||||
'file_size': fields.integer('File Size', readonly=True),
|
|
||||||
'nbr':fields.integer('# of Files', readonly=True),
|
|
||||||
'type':fields.char('Directory Type',size=64,readonly=True),
|
|
||||||
}
|
|
||||||
def init(self, cr):
|
|
||||||
tools.drop_view_if_exists(cr, 'report_document_user')
|
|
||||||
cr.execute("""
|
|
||||||
CREATE OR REPLACE VIEW report_document_user as (
|
|
||||||
SELECT
|
|
||||||
min(f.id) as id,
|
|
||||||
to_char(f.create_date, 'YYYY') as name,
|
|
||||||
to_char(f.create_date, 'MM') as month,
|
|
||||||
f.user_id as user_id,
|
|
||||||
count(*) as nbr,
|
|
||||||
d.name as directory,
|
|
||||||
f.datas_fname as datas_fname,
|
|
||||||
f.create_date as create_date,
|
|
||||||
f.file_size as file_size,
|
|
||||||
min(d.type) as type,
|
|
||||||
f.write_date as change_date
|
|
||||||
FROM ir_attachment f
|
|
||||||
left join document_directory d on (f.parent_id=d.id and d.name<>'')
|
|
||||||
group by to_char(f.create_date, 'YYYY'), to_char(f.create_date, 'MM'),d.name,f.parent_id,d.type,f.create_date,f.user_id,f.file_size,d.type,f.write_date,f.datas_fname
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
|
|
||||||
class report_document_file(osv.osv):
|
|
||||||
_name = "report.document.file"
|
|
||||||
_description = "Files details by Directory"
|
|
||||||
_auto = False
|
|
||||||
_columns = {
|
|
||||||
'file_size': fields.integer('File Size', readonly=True),
|
|
||||||
'nbr':fields.integer('# of Files', readonly=True),
|
|
||||||
'month': fields.char('Month', size=24, readonly=True),
|
|
||||||
}
|
|
||||||
_order = "month"
|
|
||||||
def init(self, cr):
|
|
||||||
tools.drop_view_if_exists(cr, 'report_document_file')
|
|
||||||
cr.execute("""
|
|
||||||
create or replace view report_document_file as (
|
|
||||||
select min(f.id) as id,
|
|
||||||
count(*) as nbr,
|
|
||||||
min(EXTRACT(MONTH FROM f.create_date)||'-'||to_char(f.create_date,'Month')) as month,
|
|
||||||
sum(f.file_size) as file_size
|
|
||||||
from ir_attachment f
|
|
||||||
group by EXTRACT(MONTH FROM f.create_date)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
<record model="ir.ui.view" id="view_document_user_form">
|
|
||||||
<field name="name">report.document.user.form</field>
|
|
||||||
<field name="model">report.document.user</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Files">
|
|
||||||
<group col="4">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="user"/>
|
|
||||||
<field name="directory"/>
|
|
||||||
<field name="datas_fname"/>
|
|
||||||
<field name="file_size"/>
|
|
||||||
<field name="create_date"/>
|
|
||||||
</group>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_document_user_tree">
|
|
||||||
<field name="name">report.document.user.tree</field>
|
|
||||||
<field name="model">report.document.user</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Files">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="month"/>
|
|
||||||
<field name="user" invisible="1"/>
|
|
||||||
<field name="directory" invisible="1"/>
|
|
||||||
<field name="file_size" invisible="1"/>
|
|
||||||
<field name="create_date" invisible="1"/>
|
|
||||||
<field name="nbr"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_report_document_user_search" model="ir.ui.view">
|
|
||||||
<field name="name">report.document.user.search</field>
|
|
||||||
<field name="model">report.document.user</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<search string="All users files">
|
|
||||||
<field name="name" filter_domain="['|', '|',('name','ilike',self), ('user','ilike',self), ('directory','ilike',self)]" string="Users File"/>
|
|
||||||
<field name="month"/>
|
|
||||||
</search>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_view_all_document_tree1">
|
|
||||||
<field name="name">All Users files</field>
|
|
||||||
<field name="res_model">report.document.user</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="context">{'group_by': ['name','month']}</field>
|
|
||||||
<field name="search_view_id" ref="view_report_document_user_search"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_size_month">
|
|
||||||
<field name="name">report.document.user.graph</field>
|
|
||||||
<field name="model">report.document.file</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph string="File Size by Month" type="line">
|
|
||||||
<field name="month"/>
|
|
||||||
<field name="file_size" operator="+"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_size_month_tree">
|
|
||||||
<field name="name">report.document.user.tree</field>
|
|
||||||
<field name="model">report.document.file</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="File Size by Month">
|
|
||||||
<field name="month" />
|
|
||||||
<field name="file_size"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_view_size_month">
|
|
||||||
<field name="name">File Size by Month</field>
|
|
||||||
<field name="res_model">report.document.file</field>
|
|
||||||
<field name="view_id" ref="view_size_month"></field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_files_by_month_graph">
|
|
||||||
<field name="name">report.file.month.graph</field>
|
|
||||||
<field name="model">report.document.user</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph string="Files by Month" type="pie">
|
|
||||||
<field name="month" />
|
|
||||||
<field name="nbr" operator="+"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_files_by_month_tree">
|
|
||||||
<field name="name">report.file.month.tree</field>
|
|
||||||
<field name="model">report.document.user</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Files by Month">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="month"/>
|
|
||||||
<field name="nbr"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_files_by_user_graph">
|
|
||||||
<field name="name">report.file.user.graph</field>
|
|
||||||
<field name="model">report.document.user</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph string="Files by User" type="pie">
|
|
||||||
<field name="user_id" />
|
|
||||||
<field name="nbr" operator="+"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_view_files_by_user_graph">
|
|
||||||
<field name="name">Files by User</field>
|
|
||||||
<field name="res_model">report.document.user</field>
|
|
||||||
<field name="view_id" ref="view_files_by_user_graph"></field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="action_view_files_by_month_graph">
|
|
||||||
<field name="name">Files by Month</field>
|
|
||||||
<field name="res_model">report.document.user</field>
|
|
||||||
<field name="view_id" ref="view_files_by_month_graph"></field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<openerp>
|
|
||||||
<data noupdate="0">
|
|
||||||
|
|
||||||
<record id="base.group_document_user" model="res.groups">
|
|
||||||
<field name="name">User</field>
|
|
||||||
<field name="category_id" ref="base.module_category_knowledge_management"/>
|
|
||||||
<field name="users" eval="[(4, ref('base.user_root'))]"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- <record id="group_document_manager" model="res.groups">-->
|
|
||||||
<!-- <field name="name">Document / Manager</field>-->
|
|
||||||
<!-- </record>-->
|
|
||||||
</data>
|
|
||||||
<data noupdate="1">
|
|
||||||
|
|
||||||
<record id="ir_rule_readpublicdirectories0" model="ir.rule">
|
|
||||||
<field name="model_id" ref="document.model_document_directory"/>
|
|
||||||
<field name="domain_force">['|','|',('group_ids','in',[g.id for g in user.groups_id]), ('user_id', '=', user.id), '&', ('user_id', '=', False), ('group_ids','=',False), '|','|', ('company_id','=',False), ('company_id','child_of',[user.company_id.id]),('company_id.child_ids','child_of',[user.company_id.id])]</field>
|
|
||||||
<field name="name">Read public directories</field>
|
|
||||||
<field eval="0" name="global"/>
|
|
||||||
<field eval="[(6,0,[ref('base.group_user')])]" name="groups"/>
|
|
||||||
<field eval="0" name="perm_unlink"/>
|
|
||||||
<field eval="0" name="perm_write"/>
|
|
||||||
<field eval="1" name="perm_read"/>
|
|
||||||
<field eval="0" name="perm_create"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="ir_rule_documentmodifyowndirectories0" model="ir.rule">
|
|
||||||
<field name="model_id" ref="document.model_document_directory"/>
|
|
||||||
<field name="domain_force">[ '|', ('user_id', '=', user.id), '&', ('group_ids','in',[g.id for g in user.groups_id]), ('user_id','=',False), '|','|', ('company_id','=',False), ('company_id','child_of',[user.company_id.id]),('company_id.child_ids','child_of',[user.company_id.id])]</field>
|
|
||||||
<field name="name">Document modify own directories</field>
|
|
||||||
<field eval="0" name="global"/>
|
|
||||||
<field eval="[(6,0,[ref('base.group_document_user')])]" name="groups"/>
|
|
||||||
<field eval="1" name="perm_unlink"/>
|
|
||||||
<field eval="1" name="perm_write"/>
|
|
||||||
<field eval="0" name="perm_read"/>
|
|
||||||
<field eval="1" name="perm_create"/>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
||||||
access_document_directory_all,document.directory all,model_document_directory,,1,0,0,0
|
|
||||||
access_document_directory_group_document_manager,document.directory document manager,model_document_directory,base.group_system,1,1,1,1
|
|
||||||
access_document_directory_group_knowledge,document.directory modify,model_document_directory,base.group_document_user,1,1,1,1
|
|
||||||
access_document_directory_content_all,document.directory.content all,model_document_directory_content,,1,0,0,0
|
|
||||||
access_document_directory_content_group_document_manager,document.directory.content document manager,model_document_directory_content,base.group_system,1,1,1,1
|
|
||||||
access_document_directory_content_type_group_document_manager,document.directory.content.type document manager,model_document_directory_content_type,base.group_system,1,1,1,1
|
|
||||||
access_document_directory_content_type_group_system,document.directory.content.type group system,model_document_directory_content_type,base.group_user,1,0,0,0
|
|
||||||
access_ir_attachment_group_user,ir.attachment user,base.model_ir_attachment,base.group_document_user,1,1,1,1
|
|
||||||
access_ir_attachment_group_system,ir.attachment system,base.model_ir_attachment,base.group_system,1,0,0,0
|
|
||||||
access_res_partner_group_user,res.partner user,base.model_res_partner,base.group_document_user,1,1,1,0
|
|
||||||
access_document_directory_dctx_all,document.directory.dctx all,model_document_directory_dctx,,1,0,0,0
|
|
||||||
access_document_directory_dctx_group_document_manager,document.directory.dctx document manager,model_document_directory_dctx,base.group_system,1,1,1,1
|
|
||||||
access_report_document_user_group_document_manager,report.document.user document manager,model_report_document_user,base.group_system,1,0,0,0
|
|
||||||
access_report_document_file_group_document_manager,report.document.file document manager,model_report_document_file,base.group_system,1,0,0,0
|
|
||||||
access_report_document_file_group_document,report.document.file document manager,model_report_document_file,base.group_document_user,1,0,0,0
|
|
||||||
access_report_document_user_knowledgeuser,report.document.user knowledgeuser,document.model_report_document_user,base.group_document_user,1,0,0,0
|
|
||||||
access_document_storage,access_document_storage,model_document_storage,base.group_system,1,1,1,1
|
|
||||||
|
@@ -1,206 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
|
|
||||||
from content_index import indexer, cntIndex
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
import StringIO
|
|
||||||
import odt2txt
|
|
||||||
import sys, zipfile, xml.dom.minidom
|
|
||||||
import logging
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def _to_unicode(s):
|
|
||||||
try:
|
|
||||||
return s.decode('utf-8')
|
|
||||||
except UnicodeError:
|
|
||||||
try:
|
|
||||||
return s.decode('latin')
|
|
||||||
except UnicodeError:
|
|
||||||
try:
|
|
||||||
return s.encode('ascii')
|
|
||||||
except UnicodeError:
|
|
||||||
return s
|
|
||||||
|
|
||||||
def textToString(element):
|
|
||||||
buffer = u""
|
|
||||||
for node in element.childNodes :
|
|
||||||
if node.nodeType == xml.dom.Node.TEXT_NODE :
|
|
||||||
buffer += node.nodeValue
|
|
||||||
elif node.nodeType == xml.dom.Node.ELEMENT_NODE :
|
|
||||||
buffer += textToString(node)
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
class TxtIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return ['text/plain','text/html','text/diff','text/xml', 'text/*',
|
|
||||||
'application/xml']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.txt', '.py']
|
|
||||||
|
|
||||||
def _doIndexContent(self, content):
|
|
||||||
return content
|
|
||||||
|
|
||||||
cntIndex.register(TxtIndex())
|
|
||||||
|
|
||||||
class PptxIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return [ 'application/vnd.openxmlformats-officedocument.presentationml.presentation']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.pptx']
|
|
||||||
|
|
||||||
def _doIndexFile(self, fname):
|
|
||||||
def toString () :
|
|
||||||
""" Converts the document to a string. """
|
|
||||||
buffer = u""
|
|
||||||
for val in ["a:t"]:
|
|
||||||
for paragraph in content.getElementsByTagName(val) :
|
|
||||||
buffer += textToString(paragraph) + "\n"
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
data = []
|
|
||||||
zip = zipfile.ZipFile(fname)
|
|
||||||
files = filter(lambda x: x.startswith('ppt/slides/slide'), zip.namelist())
|
|
||||||
for i in range(1, len(files) + 1):
|
|
||||||
content = xml.dom.minidom.parseString(zip.read('ppt/slides/slide%s.xml' % str(i)))
|
|
||||||
res = toString().encode('ascii','replace')
|
|
||||||
data.append(res)
|
|
||||||
|
|
||||||
return _to_unicode('\n'.join(data))
|
|
||||||
|
|
||||||
cntIndex.register(PptxIndex())
|
|
||||||
|
|
||||||
class DocIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return [ 'application/ms-word']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.doc']
|
|
||||||
|
|
||||||
def _doIndexFile(self, fname):
|
|
||||||
try:
|
|
||||||
pop = Popen(['antiword', fname], shell=False, stdout=PIPE)
|
|
||||||
(data, _) = pop.communicate()
|
|
||||||
return _to_unicode(data)
|
|
||||||
except OSError:
|
|
||||||
|
|
||||||
_logger.warning("Failed attempt to execute antiword (MS Word reader). Antiword is necessary to index the file %s of MIME type %s. Detailed error available at DEBUG level.", fname, self._getMimeTypes()[0])
|
|
||||||
_logger.debug("Trace of the failed file indexing attempt.", exc_info=True)
|
|
||||||
return u''
|
|
||||||
|
|
||||||
cntIndex.register(DocIndex())
|
|
||||||
|
|
||||||
class DocxIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return [ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.docx']
|
|
||||||
|
|
||||||
def _doIndexFile(self, fname):
|
|
||||||
zip = zipfile.ZipFile(fname)
|
|
||||||
content = xml.dom.minidom.parseString(zip.read("word/document.xml"))
|
|
||||||
def toString () :
|
|
||||||
""" Converts the document to a string. """
|
|
||||||
buffer = u""
|
|
||||||
for val in ["w:p", "w:h", "text:list"]:
|
|
||||||
for paragraph in content.getElementsByTagName(val) :
|
|
||||||
buffer += textToString(paragraph) + "\n"
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
res = toString().encode('ascii','replace')
|
|
||||||
|
|
||||||
return _to_unicode(res)
|
|
||||||
|
|
||||||
cntIndex.register(DocxIndex())
|
|
||||||
|
|
||||||
|
|
||||||
class XlsxIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.xlsx']
|
|
||||||
|
|
||||||
def _doIndexFile(self, fname):
|
|
||||||
zip = zipfile.ZipFile(fname)
|
|
||||||
content = xml.dom.minidom.parseString(zip.read("xl/sharedStrings.xml"))
|
|
||||||
def toString () :
|
|
||||||
""" Converts the document to a string. """
|
|
||||||
buffer = u""
|
|
||||||
for val in ["t"]:
|
|
||||||
for paragraph in content.getElementsByTagName(val) :
|
|
||||||
buffer += textToString(paragraph) + "\n"
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
res = toString().encode('ascii','replace')
|
|
||||||
|
|
||||||
return _to_unicode(res)
|
|
||||||
|
|
||||||
cntIndex.register(XlsxIndex())
|
|
||||||
|
|
||||||
class PdfIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return [ 'application/pdf']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.pdf']
|
|
||||||
|
|
||||||
def _doIndexFile(self, fname):
|
|
||||||
try:
|
|
||||||
pop = Popen(['pdftotext', '-enc', 'UTF-8', '-nopgbrk', fname, '-'], shell=False, stdout=PIPE)
|
|
||||||
(data, _) = pop.communicate()
|
|
||||||
return _to_unicode(data)
|
|
||||||
except OSError:
|
|
||||||
_logger.warning("Failed attempt to execute pdftotext. This program is necessary to index the file %s of MIME type %s. Detailed error available at DEBUG level.", fname, self._getMimeTypes()[0])
|
|
||||||
_logger.debug("Trace of the failed file indexing attempt.", exc_info=True)
|
|
||||||
return u''
|
|
||||||
|
|
||||||
cntIndex.register(PdfIndex())
|
|
||||||
|
|
||||||
class ImageNoIndex(indexer):
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
return [ 'image/*']
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
#better return no extension, and let 'file' do its magic
|
|
||||||
return []
|
|
||||||
#return ['.png','.jpg','.gif','.jpeg','.bmp','.tiff']
|
|
||||||
|
|
||||||
def _doIndexContent(self, content):
|
|
||||||
return 'image'
|
|
||||||
|
|
||||||
|
|
||||||
cntIndex.register(ImageNoIndex())
|
|
||||||
|
|
||||||
# other opendocument formats:
|
|
||||||
# chart-template chart database
|
|
||||||
# formula-template formula graphics-template graphics
|
|
||||||
# image
|
|
||||||
# presentation-template presentation spreadsheet-template spreadsheet
|
|
||||||
|
|
||||||
class OpenDoc(indexer):
|
|
||||||
""" Index OpenDocument files.
|
|
||||||
|
|
||||||
Q: is it really worth it to index spreadsheets, or do we only get a
|
|
||||||
meaningless list of numbers (cell contents) ?
|
|
||||||
"""
|
|
||||||
def _getMimeTypes(self):
|
|
||||||
otypes = [ 'text', 'text-web', 'text-template', 'text-master' ]
|
|
||||||
return map(lambda a: 'application/vnd.oasis.opendocument.'+a, otypes)
|
|
||||||
|
|
||||||
def _getExtensions(self):
|
|
||||||
return ['.odt', '.ott', ] # '.ods'
|
|
||||||
|
|
||||||
def _doIndexContent(self, content):
|
|
||||||
s = StringIO.StringIO(content)
|
|
||||||
o = odt2txt.OpenDocumentTextFile(s)
|
|
||||||
result = _to_unicode(o.toString())
|
|
||||||
s.close()
|
|
||||||
return result
|
|
||||||
|
|
||||||
cntIndex.register(OpenDoc())
|
|
||||||
|
|
||||||
|
|
||||||
#eof
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
- |
|
|
||||||
In order to test the Document Management System In OpenERP.
|
|
||||||
-
|
|
||||||
In order to test behaviour of resource Directory I will make one resource Directory "Partner Labels" in OpenERP having type "Other Resources" and Directory mapped to object "Partner"
|
|
||||||
-
|
|
||||||
!record {model: 'document.directory', id: dir_partner_label}:
|
|
||||||
name : "Partner Labels"
|
|
||||||
type : ressource
|
|
||||||
content_ids:
|
|
||||||
- name: "Label"
|
|
||||||
report_id : base.res_partner_address_report
|
|
||||||
-
|
|
||||||
Assign "res.partner" object to ressource_type_id.
|
|
||||||
-
|
|
||||||
!python {model: document.directory}: |
|
|
||||||
ids = self.pool.get('ir.model').search(cr, uid, [('model','=','res.partner')])
|
|
||||||
id = self.write(cr, uid, [ref("dir_partner_label")], {'ressource_type_id' : ids[0]}, context)
|
|
||||||
-
|
|
||||||
In order to check static directory in OpenERP which is the real directory just like system's local folders,
|
|
||||||
First I create a directory in OpenERP named "Directory 1" with type as "Static Directory"
|
|
||||||
-
|
|
||||||
!record {model: 'document.directory', id: directory_1}:
|
|
||||||
name : "Directory 1"
|
|
||||||
type : directory
|
|
||||||
-
|
|
||||||
In order to make a directory hierarchy in OpenERP I will make other static directory "Directory 2" and I put its Parent Item as "Directory 1",
|
|
||||||
So I can see the directory hierarchy in OpenERP's client like :- Directory1 -> Directory2
|
|
||||||
-
|
|
||||||
!record {model: 'document.directory', id: directory_2}:
|
|
||||||
name : "Directory 2"
|
|
||||||
type : directory
|
|
||||||
parent_id : directory_1
|
|
||||||
-
|
|
||||||
I am create a one Document Attachment name "My document" and select "Directory 2" as its Directory,
|
|
||||||
And in order to put a File content.
|
|
||||||
-
|
|
||||||
When I am creating the document then "Resource Title" is filled automatic with "My document".
|
|
||||||
-
|
|
||||||
!record {model: 'ir.attachment', id: document_1}:
|
|
||||||
name : "My document"
|
|
||||||
parent_id : directory_2
|
|
||||||
-
|
|
||||||
Attach a pdf file in "My document"
|
|
||||||
-
|
|
||||||
!python {model: 'ir.attachment'}: |
|
|
||||||
import tools
|
|
||||||
import base64
|
|
||||||
pdf_file = open(tools.config['addons_path'] + '/document/test/partner.pdf')
|
|
||||||
self.write(cr, uid, [ref("document_1")], {'datas_fname': 'partner.pdf', 'datas' : base64.encodestring(pdf_file.read())}, context)
|
|
||||||
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
-
|
|
||||||
I create a "Testing" folder where all the test data will go.
|
|
||||||
-
|
|
||||||
!record {model: document.directory, id: dir_tests }:
|
|
||||||
name: 'Testing (will be deleted!)'
|
|
||||||
parent_id: dir_root
|
|
||||||
-
|
|
||||||
I create an attachment into the root folder (w. empty fields, test that
|
|
||||||
defaults work)
|
|
||||||
-
|
|
||||||
!record {model: ir.attachment, id: file_test1 }:
|
|
||||||
name: Test file.txt
|
|
||||||
-
|
|
||||||
I delete the attachment from the root folder
|
|
||||||
-
|
|
||||||
!python {model: ir.attachment}: |
|
|
||||||
self.unlink(cr, uid, [ref('file_test1')])
|
|
||||||
-
|
|
||||||
I create an attachment into the Testing folder.
|
|
||||||
-
|
|
||||||
!record {model: ir.attachment, id: file_test2 }:
|
|
||||||
name: Test file 2
|
|
||||||
parent_id: dir_tests
|
|
||||||
-
|
|
||||||
I update the attachment with data, namely "abcd"
|
|
||||||
-
|
|
||||||
!record {model: ir.attachment, id: file_test2 }:
|
|
||||||
datas: "YWJjZA==\n"
|
|
||||||
-
|
|
||||||
I test that the datas of the attachment are correct
|
|
||||||
-
|
|
||||||
!assert {model: ir.attachment, id: file_test2 }:
|
|
||||||
- datas == "YWJjZA==\n"
|
|
||||||
- file_size == 4
|
|
||||||
-
|
|
||||||
I rename the attachment.
|
|
||||||
-
|
|
||||||
!record {model: ir.attachment, id: file_test2 }:
|
|
||||||
name: Test renamed 2
|
|
||||||
-
|
|
||||||
I search the testing folder for attachments.
|
|
||||||
-
|
|
||||||
!python {model: ir.attachment}: |
|
|
||||||
ids = self.search(cr, uid, [('parent_id.name','=', 'Testing (will be deleted!)'), ('name','=','Test renamed 2')])
|
|
||||||
assert ids == [ ref("file_test2") ], ids
|
|
||||||
-
|
|
||||||
I create an attachment to a 3rd resource, eg. a res.country
|
|
||||||
-
|
|
||||||
!record {model: ir.attachment, id: attach_3rd }:
|
|
||||||
name: 'Country attachment.txt'
|
|
||||||
parent_id: dir_tests
|
|
||||||
datas: "Q291bnRyeSBhdHRhY2htZW50IGNvbnRlbnQ=\n"
|
|
||||||
res_model: res.country
|
|
||||||
res_id: !eval ref("base.za")
|
|
||||||
-
|
|
||||||
I search for the res.country attachment
|
|
||||||
-
|
|
||||||
!python {model: ir.attachment}: |
|
|
||||||
ids = self.search(cr, uid, [('res_model', '=', 'res.country'), ('res_id', '=', ref("base.za"))])
|
|
||||||
assert ids == [ ref("attach_3rd")], ids
|
|
||||||
-
|
|
||||||
I test that I can't create duplicate directories (even when duplicates are hidden by a record rule)
|
|
||||||
-
|
|
||||||
!python {model: document.directory}: |
|
|
||||||
duplicate_detected = False
|
|
||||||
from openerp.osv.osv import except_osv
|
|
||||||
try:
|
|
||||||
demo_uid = ref('base.user_demo')
|
|
||||||
dir_vals = {
|
|
||||||
'name': 'Testing (will be deleted!)',
|
|
||||||
'parent_id': ref('document.dir_root')
|
|
||||||
}
|
|
||||||
new_dir_id = self.create(cr, demo_uid, dir_vals, context=None)
|
|
||||||
self.unlink(cr, uid, [new_dir_id], context=None)
|
|
||||||
except except_osv, e:
|
|
||||||
duplicate_detected = e.name == u'Directory name must be unique!'
|
|
||||||
assert duplicate_detected is True, 'Failed to detect duplicate directory'
|
|
||||||
-
|
|
||||||
I delete the attachments
|
|
||||||
-
|
|
||||||
!python {model: ir.attachment}: |
|
|
||||||
self.unlink(cr, uid, [ref('file_test2')])
|
|
||||||
self.unlink(cr, uid, [ref('attach_3rd')])
|
|
||||||
-
|
|
||||||
I delete the tests folder
|
|
||||||
-
|
|
||||||
!python {model: document.directory}: |
|
|
||||||
self.unlink(cr, uid, [ref('dir_tests')])
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
%PDF-1.3
|
|
||||||
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
|
|
||||||
% 'BasicFonts': class PDFDictionary
|
|
||||||
1 0 obj
|
|
||||||
% The standard fonts dictionary
|
|
||||||
<< /F1 2 0 R
|
|
||||||
/F2 3 0 R
|
|
||||||
/F3 4 0 R >>
|
|
||||||
endobj
|
|
||||||
% 'F1': class PDFType1Font
|
|
||||||
2 0 obj
|
|
||||||
% Font Helvetica
|
|
||||||
<< /BaseFont /Helvetica
|
|
||||||
/Encoding /WinAnsiEncoding
|
|
||||||
/Name /F1
|
|
||||||
/Subtype /Type1
|
|
||||||
/Type /Font >>
|
|
||||||
endobj
|
|
||||||
% 'F2': class PDFType1Font
|
|
||||||
3 0 obj
|
|
||||||
% Font Helvetica-Bold
|
|
||||||
<< /BaseFont /Helvetica-Bold
|
|
||||||
/Encoding /WinAnsiEncoding
|
|
||||||
/Name /F2
|
|
||||||
/Subtype /Type1
|
|
||||||
/Type /Font >>
|
|
||||||
endobj
|
|
||||||
% 'F3': class PDFType1Font
|
|
||||||
4 0 obj
|
|
||||||
% Font Times-Roman
|
|
||||||
<< /BaseFont /Times-Roman
|
|
||||||
/Encoding /WinAnsiEncoding
|
|
||||||
/Name /F3
|
|
||||||
/Subtype /Type1
|
|
||||||
/Type /Font >>
|
|
||||||
endobj
|
|
||||||
% 'Page1': class PDFPage
|
|
||||||
5 0 obj
|
|
||||||
% Page dictionary
|
|
||||||
<< /Contents 9 0 R
|
|
||||||
/MediaBox [ 0
|
|
||||||
0
|
|
||||||
841.8898
|
|
||||||
595.2756 ]
|
|
||||||
/Parent 8 0 R
|
|
||||||
/Resources << /Font 1 0 R
|
|
||||||
/ProcSet [ /PDF
|
|
||||||
/Text
|
|
||||||
/ImageB
|
|
||||||
/ImageC
|
|
||||||
/ImageI ] >>
|
|
||||||
/Rotate 0
|
|
||||||
/Trans << >>
|
|
||||||
/Type /Page >>
|
|
||||||
endobj
|
|
||||||
% 'R6': class PDFCatalog
|
|
||||||
6 0 obj
|
|
||||||
% Document Root
|
|
||||||
<< /Outlines 10 0 R
|
|
||||||
/PageMode /UseNone
|
|
||||||
/Pages 8 0 R
|
|
||||||
/Type /Catalog >>
|
|
||||||
endobj
|
|
||||||
% 'R7': class PDFInfo
|
|
||||||
7 0 obj
|
|
||||||
<< /Author (Generated by OpenERP, Fabien Pinckaers)
|
|
||||||
/CreationDate (20100421145542)
|
|
||||||
/Keywords ()
|
|
||||||
/Producer (ReportLab http://www.reportlab.com)
|
|
||||||
/Subject (\(unspecified\))
|
|
||||||
/Title (Partner) >>
|
|
||||||
endobj
|
|
||||||
% 'R8': class PDFPages
|
|
||||||
8 0 obj
|
|
||||||
% page tree
|
|
||||||
<< /Count 1
|
|
||||||
/Kids [ 5 0 R ]
|
|
||||||
/Type /Pages >>
|
|
||||||
endobj
|
|
||||||
% 'R9': class PDFStream
|
|
||||||
9 0 obj
|
|
||||||
% page stream
|
|
||||||
<< /Filter [ /ASCII85Decode
|
|
||||||
/FlateDecode ]
|
|
||||||
/Length 524 >>
|
|
||||||
stream
|
|
||||||
Gat=(?#P<K'R_@f5MSc<`6.HPV2s@dQRlarfjrE>3i<Y4(hW<qY=/IHPo2nd;U2u=SpPc46=:icfCoF922oN\83q_*El(+<i)0C.\*qSa'orY`+p(cC^oTpn8t,B-+=I?_Q1#SGXQbKWJ5-M7"aXRSQCBTu'NHJF046Jno-.K&2kW+&Ddh9bXc1q%)-e?DSC(5(4%=lk+ql;XaX3m]F#7K:"]hph/QW?(0_J4kXQiP</;'9K=]Wd,jeGe.Ma6pqJ&iD]r%qkNclhVq5Q/gSmK+r&6mr`*Z[m8(n)Usu)MZo%TNta^h'I^g6m5Vp92+>e[>S,B2ME\)S7:@oo6@nF8`I+h##OY3a-n#Qm!>5jiD.9pQ@E1#Xj$=)27)F3RmD%fbqZh,T`gJd0/,?A3`0:_VVm`"l`>oOf9p&HGF7>WCGVJoD2&_+c#BDb)/&0>>rL5@]KY$9YOA<rrt#g)1a61mS([/:0_[`)!ctOhSA/p@bq]>qZHTJq8k]!o+?4a5\-KbB#NYJC'`~>endstream
|
|
||||||
|
|
||||||
endobj
|
|
||||||
% 'R10': class PDFOutlines
|
|
||||||
10 0 obj
|
|
||||||
<< /Count 0
|
|
||||||
/Type /Outlines >>
|
|
||||||
endobj
|
|
||||||
xref
|
|
||||||
0 11
|
|
||||||
0000000000 65535 f
|
|
||||||
0000000113 00000 n
|
|
||||||
0000000233 00000 n
|
|
||||||
0000000398 00000 n
|
|
||||||
0000000573 00000 n
|
|
||||||
0000000740 00000 n
|
|
||||||
0000001017 00000 n
|
|
||||||
0000001152 00000 n
|
|
||||||
0000001395 00000 n
|
|
||||||
0000001500 00000 n
|
|
||||||
0000002169 00000 n
|
|
||||||
trailer
|
|
||||||
<< /ID
|
|
||||||
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
|
|
||||||
[(\210\344\006bE\230CUUi\010[\)Qif) (\210\344\006bE\230CUUi\010[\)Qif)]
|
|
||||||
|
|
||||||
/Info 7 0 R
|
|
||||||
/Root 6 0 R
|
|
||||||
/Size 11 >>
|
|
||||||
startxref
|
|
||||||
2221
|
|
||||||
%%EOF
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
|
|
||||||
parser = OptionParser()
|
|
||||||
parser.add_option("-q", "--quiet",
|
|
||||||
action="store_false", dest="verbose", default=True,
|
|
||||||
help="don't print status messages to stdout")
|
|
||||||
|
|
||||||
parser.add_option("-C", "--content",
|
|
||||||
action="store_true", dest="docontent", default=False,
|
|
||||||
help="Disect content, rather than the file.")
|
|
||||||
|
|
||||||
parser.add_option("--delay",
|
|
||||||
action="store_true", dest="delay", default=False,
|
|
||||||
help="delay after the operation, to inspect child processes")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
import content_index, std_index
|
|
||||||
|
|
||||||
from content_index import cntIndex
|
|
||||||
|
|
||||||
for fname in args:
|
|
||||||
try:
|
|
||||||
if options.docontent:
|
|
||||||
fp = open(fname,'rb')
|
|
||||||
content = fp.read()
|
|
||||||
fp.close()
|
|
||||||
res = cntIndex.doIndex(content, fname, None, None, True)
|
|
||||||
else:
|
|
||||||
res = cntIndex.doIndex(None, fname, None, fname,True)
|
|
||||||
|
|
||||||
if options.verbose:
|
|
||||||
for line in res[:5]:
|
|
||||||
print line
|
|
||||||
if options.delay:
|
|
||||||
time.sleep(30)
|
|
||||||
except Exception,e:
|
|
||||||
import traceback
|
|
||||||
tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback))
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print "Keyboard interrupt"
|
|
||||||
|
|
||||||
#eof
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
import document_configuration
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
||||||
|
|
||||||
from openerp.osv import fields, osv
|
|
||||||
|
|
||||||
class document_configuration(osv.osv_memory):
|
|
||||||
_name='document.configuration'
|
|
||||||
_description = 'Directory Configuration'
|
|
||||||
_inherit = 'res.config'
|
|
||||||
|
|
||||||
def execute(self, cr, uid, ids, context=None):
|
|
||||||
dir_pool = self.pool.get('document.directory')
|
|
||||||
data_pool = self.pool.get('ir.model.data')
|
|
||||||
model_pool = self.pool.get('ir.model')
|
|
||||||
content_pool = self.pool.get('document.directory.content')
|
|
||||||
if self.pool.get('sale.order'):
|
|
||||||
# Sale order
|
|
||||||
dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_sale_order_all')
|
|
||||||
if dir_data_id:
|
|
||||||
sale_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
|
|
||||||
else:
|
|
||||||
sale_dir_id = data_pool.create(cr, uid, {'name': 'Sale Orders'})
|
|
||||||
mid = model_pool.search(cr, uid, [('model','=','sale.order')])
|
|
||||||
dir_pool.write(cr, uid, [sale_dir_id], {
|
|
||||||
'type':'ressource',
|
|
||||||
'ressource_type_id': mid[0],
|
|
||||||
'domain': '[]',
|
|
||||||
})
|
|
||||||
# Qutation
|
|
||||||
dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_sale_order_quote')
|
|
||||||
if dir_data_id:
|
|
||||||
quta_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
|
|
||||||
else:
|
|
||||||
quta_dir_id = data_pool.create(cr, uid, {'name': 'Sale Quotations'})
|
|
||||||
|
|
||||||
dir_pool.write(cr, uid, [quta_dir_id], {
|
|
||||||
'type':'ressource',
|
|
||||||
'ressource_type_id': mid[0],
|
|
||||||
'domain': "[('state','=','draft')]",
|
|
||||||
})
|
|
||||||
# Sale Order Report
|
|
||||||
order_report_data_id = data_pool._get_id(cr, uid, 'sale', 'report_sale_order')
|
|
||||||
if order_report_data_id:
|
|
||||||
order_report_id = data_pool.browse(cr, uid, order_report_data_id, context=context).res_id
|
|
||||||
|
|
||||||
content_pool.create(cr, uid, {
|
|
||||||
'name': "Print Order",
|
|
||||||
'suffix': "_print",
|
|
||||||
'report_id': order_report_id,
|
|
||||||
'extension': '.pdf',
|
|
||||||
'include_name': 1,
|
|
||||||
'directory_id': sale_dir_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
content_pool.create(cr, uid, {
|
|
||||||
'name': "Print Quotation",
|
|
||||||
'suffix': "_print",
|
|
||||||
'report_id': order_report_id,
|
|
||||||
'extension': '.pdf',
|
|
||||||
'include_name': 1,
|
|
||||||
'directory_id': quta_dir_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
if self.pool.get('product.product'):
|
|
||||||
# Product
|
|
||||||
dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_product')
|
|
||||||
if dir_data_id:
|
|
||||||
product_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
|
|
||||||
else:
|
|
||||||
product_dir_id = data_pool.create(cr, uid, {'name': 'Products'})
|
|
||||||
|
|
||||||
mid = model_pool.search(cr, uid, [('model','=','product.product')])
|
|
||||||
dir_pool.write(cr, uid, [product_dir_id], {
|
|
||||||
'type':'ressource',
|
|
||||||
'ressource_type_id': mid[0],
|
|
||||||
})
|
|
||||||
|
|
||||||
if self.pool.get('account.analytic.account'):
|
|
||||||
# Project
|
|
||||||
dir_data_id = data_pool._get_id(cr, uid, 'document', 'dir_project')
|
|
||||||
if dir_data_id:
|
|
||||||
project_dir_id = data_pool.browse(cr, uid, dir_data_id, context=context).res_id
|
|
||||||
else:
|
|
||||||
project_dir_id = data_pool.create(cr, uid, {'name': 'Projects'})
|
|
||||||
|
|
||||||
mid = model_pool.search(cr, uid, [('model','=','account.analytic.account')])
|
|
||||||
dir_pool.write(cr, uid, [project_dir_id], {
|
|
||||||
'type':'ressource',
|
|
||||||
'ressource_type_id': mid[0],
|
|
||||||
'domain': '[]',
|
|
||||||
'ressource_tree': 1
|
|
||||||
})
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
<record id="view_auto_config_form" model="ir.ui.view">
|
|
||||||
<field name="name">Auto Configure Directories</field>
|
|
||||||
<field name="model">document.configuration</field>
|
|
||||||
<field name="inherit_id" ref="base.res_config_view_base"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<data>
|
|
||||||
<form position="attributes">
|
|
||||||
<attribute name="string">Knowledge Application Configuration</attribute>
|
|
||||||
</form>
|
|
||||||
<group name="res_config_contents" position="replace">
|
|
||||||
<separator string="Configure Directories"/>
|
|
||||||
<p>Odoo's Document Management System supports mapping virtual folders with documents. The virtual folder of a document can be used to manage the files attached to the document, or to print and download any report. This tool will create directories automatically according to modules installed.</p>
|
|
||||||
<p>When executing this wizard, it will configure your directories automatically according to modules installed.</p>
|
|
||||||
</group>
|
|
||||||
</data>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="action_config_auto_directory" model="ir.actions.act_window">
|
|
||||||
<field name="name">Configure Directories</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">document.configuration</field>
|
|
||||||
<field name="view_id" ref="view_auto_config_form"/>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">form</field>
|
|
||||||
<field name="target">new</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
@@ -383,8 +383,6 @@ class marketing_campaign_activity(osv.osv):
|
|||||||
"""),
|
"""),
|
||||||
'email_template_id': fields.many2one('mail.template', "Email Template", help='The email to send when this activity is activated'),
|
'email_template_id': fields.many2one('mail.template', "Email Template", help='The email to send when this activity is activated'),
|
||||||
'report_id': fields.many2one('ir.actions.report.xml', "Report", help='The report to generate when this activity is activated', ),
|
'report_id': fields.many2one('ir.actions.report.xml', "Report", help='The report to generate when this activity is activated', ),
|
||||||
'report_directory_id': fields.many2one('document.directory','Directory',
|
|
||||||
help="This folder is used to store the generated reports"),
|
|
||||||
'server_action_id': fields.many2one('ir.actions.server', string='Action',
|
'server_action_id': fields.many2one('ir.actions.server', string='Action',
|
||||||
help= "The action to perform when this activity is activated"),
|
help= "The action to perform when this activity is activated"),
|
||||||
'to_ids': fields.one2many('marketing.campaign.transition',
|
'to_ids': fields.one2many('marketing.campaign.transition',
|
||||||
@@ -428,9 +426,7 @@ class marketing_campaign_activity(osv.osv):
|
|||||||
activity.name,workitem.partner_id.name),
|
activity.name,workitem.partner_id.name),
|
||||||
'datas_fname': '%s.%s'%(activity.report_id.report_name,
|
'datas_fname': '%s.%s'%(activity.report_id.report_name,
|
||||||
activity.report_id.report_type),
|
activity.report_id.report_type),
|
||||||
'parent_id': activity.report_directory_id.id,
|
|
||||||
'datas': base64.encodestring(report_data),
|
'datas': base64.encodestring(report_data),
|
||||||
'file_type': format
|
|
||||||
}
|
}
|
||||||
self.pool.get('ir.attachment').create(cr, uid, attach_vals)
|
self.pool.get('ir.attachment').create(cr, uid, attach_vals)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -316,7 +316,6 @@
|
|||||||
context="{'default_model_id':object_id}"/>
|
context="{'default_model_id':object_id}"/>
|
||||||
<field name="server_action_id" attrs="{'required':[('type','=','action')],'invisible':[('type','!=','action')]}" domain="[('model_id','=',object_id)]"/>
|
<field name="server_action_id" attrs="{'required':[('type','=','action')],'invisible':[('type','!=','action')]}" domain="[('model_id','=',object_id)]"/>
|
||||||
<field name="report_id" attrs="{'required':[('type','=','report')],'invisible':[('type','!=','report')]}" context="{'object_id':object_id}"/>
|
<field name="report_id" attrs="{'required':[('type','=','report')],'invisible':[('type','!=','report')]}" context="{'object_id':object_id}"/>
|
||||||
<field name="report_directory_id" attrs="{'required':[('type','=','report')],'invisible':[('type','!=','report')]}"/>
|
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<separator string="Previous Activities"/>
|
<separator string="Previous Activities"/>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
blog_blog_all,blog.blog,model_blog_blog,,1,0,0,0
|
blog_blog_all,blog.blog,model_blog_blog,,1,0,0,0
|
||||||
blog_blog,blog.blog,model_blog_blog,base.group_document_user,1,1,1,1
|
blog_blog,blog.blog,model_blog_blog,base.group_website_designer,1,1,1,1
|
||||||
blog_post_all,blog.post,model_blog_post,,1,0,0,0
|
blog_post_all,blog.post,model_blog_post,,1,0,0,0
|
||||||
blog_post,blog.post,model_blog_post,base.group_document_user,1,1,1,1
|
blog_post,blog.post,model_blog_post,base.group_website_designer,1,1,1,1
|
||||||
blog_tag,blog.tag,model_blog_tag,,1,0,0,0
|
blog_tag,blog.tag,model_blog_tag,,1,0,0,0
|
||||||
blog_tag_edition,blog.tag,model_blog_tag,base.group_document_user,1,1,1,1
|
blog_tag_edition,blog.tag,model_blog_tag,base.group_website_designer,1,1,1,1
|
||||||
|
|||||||
|
@@ -8,7 +8,7 @@ class TestWebsiteBlogCommon(common.TransactionCase):
|
|||||||
|
|
||||||
Users = self.env['res.users']
|
Users = self.env['res.users']
|
||||||
|
|
||||||
group_blog_manager_id = self.ref('base.group_document_user')
|
group_blog_manager_id = self.ref('base.group_website_designer')
|
||||||
group_employee_id = self.ref('base.group_user')
|
group_employee_id = self.ref('base.group_user')
|
||||||
group_public_id = self.ref('base.group_public')
|
group_public_id = self.ref('base.group_public')
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
<p class="css_editable_hidden">
|
<p class="css_editable_hidden">
|
||||||
<h1>No blog post yet.</h1>
|
<h1>No blog post yet.</h1>
|
||||||
</p>
|
</p>
|
||||||
<p groups="base.group_document_user">
|
<p groups="base.group_website_designer">
|
||||||
Click on "Content" on the top menu to write your first blog post.
|
Click on "Content" on the top menu to write your first blog post.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -482,7 +482,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- User Navbar -->
|
<!-- User Navbar -->
|
||||||
<template id="content_new_blogpost" inherit_id="website.user_navbar" groups="base.group_document_user">
|
<template id="content_new_blogpost" inherit_id="website.user_navbar" groups="base.group_website_designer">
|
||||||
<xpath expr="//ul[@id='oe_systray']/li/ul[@class='dropdown-menu oe_content_menu']" position="inside">
|
<xpath expr="//ul[@id='oe_systray']/li/ul[@class='dropdown-menu oe_content_menu']" position="inside">
|
||||||
<li><a href="#" data-action="new_blog_post">New Blog Post</a></li>
|
<li><a href="#" data-action="new_blog_post">New Blog Post</a></li>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
<field name="ranking" invisible="1"/>
|
<field name="ranking" invisible="1"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="Technical" groups="base.group_no_one">
|
<group string="Technical" groups="base.group_no_one">
|
||||||
<field name="write_uid" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_document_user']}"/>
|
<field name="write_uid" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_website_designer']}"/>
|
||||||
<field name="write_date"/>
|
<field name="write_date"/>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
|
|||||||
Reference in New Issue
Block a user