Merge lp:~serpentcs/web-addons/multi_image_7.0 into lp:~webaddons-core-editors/web-addons/7.0

Proposed by Serpent Consulting Services
Status: Work in progress
Proposed branch: lp:~serpentcs/web-addons/multi_image_7.0
Merge into: lp:~webaddons-core-editors/web-addons/7.0
Diff against target: 1423 lines (+1313/-0)
13 files modified
multi_image/__init__.py (+25/-0)
multi_image/__openerp__.py (+50/-0)
multi_image/controllers/__init__.py (+25/-0)
multi_image/controllers/main.py (+61/-0)
multi_image/static/lib/lightbox/css/lightbox.css (+128/-0)
multi_image/static/lib/lightbox/js/jquery.lightbox.js (+501/-0)
multi_image/static/src/css/hoverbox.css (+52/-0)
multi_image/static/src/js/multi_image.js (+227/-0)
multi_image/static/src/xml/image_multi.xml (+126/-0)
multi_image_sample/__init__.py (+25/-0)
multi_image_sample/__openerp__.py (+39/-0)
multi_image_sample/product.py (+34/-0)
multi_image_sample/product_view.xml (+20/-0)
To merge this branch: bzr merge lp:~serpentcs/web-addons/multi_image_7.0
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp Abstain
Nhomar - Vauxoo Needs Fixing
Serpent Consulting Services (community) Needs Resubmitting
Review via email: mp+179857@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Hello.

I am testing this development.

I have some comments.

We are saving files wired in a folder inside the server code, IMHO it is a bad practice, we have a config on server:

    --static-http-document-root=STATIC_HTTP_DOCUMENT_ROOT
                        specify the directory containing your static HTML
                        files (e.g '/var/www/')
    --static-http-url-prefix=STATIC_HTTP_URL_PREFIX
                        specify the URL root prefix where you want web
                        browsers to access your static HTML files (e.g '/')

IMHO it should be saves there, with the unix security it will be safe enought i think.

I really like this development but why not use the attachment approach in the core?

I was working in our side behind OpenERP and i think it can be done in a generic widget that only show list of "url" this url can be uploaded files, i think work with a wired path in the server side is too dangerous.

What do you think?

I continue working on it to see more thing.

BTW EXCELLENT job.

review: Needs Information
Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Other point (writting in the speed i read the code)

To be a communitary module with a good approach like this i think it needs demo data what do you think?

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Idea: We should have one module that just add the lightbox lib and the widget.

And other modules that enable this feature in different models.

Because theorically it is an special widget that can be used in almost any model right?

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Problem.

If I am behind nginx with max_file_size setted on the web server the error is not correctly managed, the widget stay loading and the request response is not shown.

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

The aspect ratio have some little problems.

https://dl.dropboxusercontent.com/u/2428846/multi_image.png

....

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Some conclusions, IMHO you give me personally a good approach in terms of algorith to include this widget.

Can i improve it before merge? or split the job to make a better approach of this amazing feature?

Regards.

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

In Icons.

If you use class oe_e or the ui-icon-* (from bootstrap) in this way you dont need images.

BTW, openerp itself have all this images alreade availables on: /web/addons/web/static/src/img/icons

IMHO 2 options, or use fonts (who really see cool) or use the available icons.

Regards.

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

About This: Correction... To be a communitary module with a good approach like this i think it needs demo data what do you think?

It is done because you set the widget on Product, sorry my mistake, BTW documentation in __openerp__.py should be a little more complete.

Regards.

Revision history for this message
Serpent Consulting Services (serpent-consulting-services) wrote :

Nhomar,

Yo!

Yogesh will take a look and proceed. I really appreciate your feedback, will help us improve and also will help this widget land into v8.

Thanks.

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Other tip (I will make it anyway.)

https://github.com/jbutz/bootstrap-lightbox

This library mix bootstrap 2 with lightbox, as you should know boostrap 2 is almost embeded in openerp 7.0 and will be totally embeded in OpenERP 8.0 in its version 3.0 giving us a big power in terms of css.

I think we should:

1.- MAke a first module that just put available the library in openerp no more.
2.- Make a module that build the widget.

IMHO if we make in this way we can mix several concepts.

For example:

1.- You way to do things, list of texts to mix with the product view.
2.- Build some kind of "View Gallery" inheriting the kanban view.
3.- Use the library to build static content as helps, improve process view and so on.

We are working actually in 2 and 3 because we need internally, and you are working in multi_image widget.

We as team can not depend of your widget to have the lib available, and i think it is a dirty hacking just overwrit it.

I weill come back with a MP with a modules called web_bootstrap3 web_bootstrap2 and web_lightbox_bootstrap web to have feature independent the libraries...

What do you think?.

REgards.

Revision history for this message
Yogesh (SerpentCS) (yogesh-serpentcs) wrote :

Nhomar,

Thanks for your suggestion.

We will check it and Improve the module.

We will create separate module for multi image widget and multi image sample.

We are already in good stage for progress.

Revision history for this message
Serpent Consulting Services (serpent-consulting-services) wrote :

Team,

Updates:
Improved the view of list of all images. now we didn't set static height.

Created separate module for widget and multi_image sample for adde field in product object.

Removed the image file in multi_image module and used icon of web module.

Thanks.

review: Needs Resubmitting
6. By Serpent Consulting Services

[FIX/IMP] updates for the improvement of widget

7. By Serpent Consulting Services

Merged with parent

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Hello Jay/Husen

Thanks for take care about details mentioned, Just few remarks.

1.- The widget module should be called "web_*" to follow the line of web-specific modules.
2.- The "sample" can be called by its name, may be "product_multi_image_field", because as it is, it is functional i think.

Some questions:

1.- I really dont like write thinks in the code path (even the official ftp module allow configure thing out the path) can we have some extra entry in the config file for it (or use one of the existing)

2.- You are using ID for positioning the "lightbox" thing, as i remember i think it is a bad practice due to the generic way how things are done in openerp, can you analyse how can we namespace this IDS with classes or with other strategy?

I think we should explain in the __openerp__.py a little deeper the behaviour i.e:

For the widget module: Explain basic things about how to use (i.e. what attribute i should use in xml definition), explain limitations and the way it technically works, reading the code is possible for sure i did myself, but as technical module and with apps without explanation in the terp file, people will no be able to use it.

For the product module: Explain that paths will be saved as text and blah blah blah.

Regards.

review: Needs Information
Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Now some technical remarks, after using it and check deeper.

1.-
In the save algorithm, you are verifying if the file is really an image i find an algorithm that i used frequently to do that:

http://stackoverflow.com/questions/6640605/detecting-if-a-file-is-an-image-in-python

We should use something like this.

2.- I couldn't find the algorithm when somebody remove the file from list can you enlight me there please?

3.- Why the name is mixed with timestamp on server?

http://stackoverflow.com/questions/2961509/python-how-to-create-a-unique-file-name

In this thread there is a more ellegant way to solve the unique name part.

BTW: 2 and 3 are solved in document_* module we should is mix both.

TIP: We are trying to find what is the algorithm behind the mail module who makes something like i am asking for, may be is better study these in beep and not re - invent the wheel ;-).

If you think technically the document_* have some problem, it should be great if you comment about this and we can figure out a Final Solution for this.

Good Job and really thanks for the effort here friend.

review: Needs Fixing
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, I want to test this interesting module, but I'll wait until you fix Nhomar remarks, so please comment this MP to get notified when you do the changes.

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

Hello Friend.

I found the support for the security issue, "at least from my point of view".

Point 1:

167 + file_name = current_dat_time + "_" + ufile.filename

With this line we brake a Python Standard, we have some libraries to manage the file names in a secure way, "Why use dates?".

One option:

http://stackoverflow.com/questions/10501247/best-way-to-generate-random-file-names-in-python

AFAIK It is already implemented on document_ftp

Point 2:

Your module "without ask the user or/and the technicians" is writing over the web/module, it is a big security issue even with community addons, you can find the support about why name space correctly where you put your files.

168 + addons_path = openerpweb.addons_manifest['web']['addons_path'] + "/web/static/src/img/image_multi/"

Option 1:
In this case you can use the static_http_document_root in your server config file, and if it is not setted ask for one with a raise may be.
Option 2:
Extend the document module giving the access to the folders to physical folders.

IOH, FYI, we are working on it, and we think it is a best approach, do you think we can merge the concepts in your module?

http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html [[Section 15.2]]

IMHO, it should be done extending the dms in OpenERP (with your approach of upload and make public), if you have some point against it it should be great if you share your thoughts.

For now and until we can not solve the security issues (or support them)

ICONS: Please use generic icons with a little mention to your company specifying the use of the module, avoind use your branding __only__

Descriptors.

Can you please explain a little how use the module in the descriptor?

Conclusion,

For me it is a great approach __really__ but it should be great if you can argue my points or improve in your code, please (or better put the branch with openerp-community owner and we can help ;-) )

review: Needs Fixing
Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :

A little hole is:

158 + def upload_image_multi(self, req, callback, ufile):

As you can see it is not asking for the session object to validate ACL. I dob have time now to prepare an use case, but it is dangerous.

See how i think we have an important security hole with the approach as you solve it to upload files.

https://dl.dropboxusercontent.com/u/2428846/Captura%20de%20pantalla%202013-09-28%20a%20la%28s%29%2001.14.21.png

Revision history for this message
Yogesh (SerpentCS) (yogesh-serpentcs) wrote :

> Hello Friend.
>
> I found the support for the security issue, "at least from my point of view".
>
> Point 1:
>
> 167 + file_name = current_dat_time + "_" + ufile.filename
>
> With this line we brake a Python Standard, we have some libraries to manage
> the file names in a secure way, "Why use dates?".
>
> One option:
>
> http://stackoverflow.com/questions/10501247/best-way-to-generate-random-file-
> names-in-python

We set unique file name. if we use uuid python library then may be it will generate duplicate number so it will be conflicts with existing file name. let me check it there another lib for generate unique number not generate duplicate number in any case.

>
> AFAIK It is already implemented on document_ftp
>
> Point 2:
>
> Your module "without ask the user or/and the technicians" is writing over the
> web/module, it is a big security issue even with community addons, you can
> find the support about why name space correctly where you put your files.
>
> 168 + addons_path =
> openerpweb.addons_manifest['web']['addons_path'] +
> "/web/static/src/img/image_multi/"

We must be store the image in openerp server directory we cannot set file any other directory because for displaying image we just give path for display image in web-browser.
>
> Option 1:
> In this case you can use the static_http_document_root in your server config
> file, and if it is not setted ask for one with a raise may be.
> Option 2:
> Extend the document module giving the access to the folders to physical
> folders.
if we store file in any other directory on server like path store in config file or use static_http_document_root then we can store file on that path so it's time consuming it's take much time for loading images. User open the all images then fetch the image datas using rpc call and display image base on datas.
>
> IOH, FYI, we are working on it, and we think it is a best approach, do you
> think we can merge the concepts in your module?
>
>
> http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html [[Section 15.2]]
>
> IMHO, it should be done extending the dms in OpenERP (with your approach of
> upload and make public), if you have some point against it it should be great
> if you share your thoughts.

DMS also store file in server directory with random unique file name.

We will improve module for security reason.

> For now and until we can not solve the security issues (or support them)
>
> ICONS: Please use generic icons with a little mention to your company
> specifying the use of the module, avoind use your branding __only__
>
> Descriptors.
>
> Can you please explain a little how use the module in the descriptor?
>
> Conclusion,
>
> For me it is a great approach __really__ but it should be great if you can
> argue my points or improve in your code, please (or better put the branch with
> openerp-community owner and we can help ;-) )

We will Improve code as per your suggestions and we will add description in openerp.py file too.

Thanks for nice suggestions.

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :
Download full text (5.0 KiB)

Hello Yogesh.

COmments inline (it should be grate in the MP) ;-)

2013/9/28 Yogesh (SerpentCS) <email address hidden>

> > Hello Friend.
> >
> > I found the support for the security issue, "at least from my point of
> view".
> >
> > Point 1:
> >
> > 167 + file_name = current_dat_time + "_" + ufile.filename
> >
> > With this line we brake a Python Standard, we have some libraries to
> manage
> > the file names in a secure way, "Why use dates?".
> >
> > One option:
> >
> >
> http://stackoverflow.com/questions/10501247/best-way-to-generate-random-file-
> > names-in-python
>
> We set unique file name. if we use uuid python library then may be it will
> generate duplicate number so it will be conflicts with existing file name.
> let me check it there another lib for generate unique number not generate
> duplicate number in any case.
>

Yes i got it, but the "datetime" imho is an ugly solution, we can use
hash, uuid, ond / or your own algorithm It is my point, in the link you
have the 3 options explained.

>
> >
> > AFAIK It is already implemented on document_ftp
> >
> > Point 2:
> >
> > Your module "without ask the user or/and the technicians" is writing
> over the
> > web/module, it is a big security issue even with community addons, you
> can
> > find the support about why name space correctly where you put your files.
> >
> > 168 + addons_path =
> > openerpweb.addons_manifest['web']['addons_path'] +
> > "/web/static/src/img/image_multi/"
>
> We must be store the image in openerp server directory we cannot set file
> any other directory because for displaying image we just give path for
> display image in web-browser.
>

Noup, it is not true, you can use the "static" folder of any module, not
just from web, it means, if we install a new module, i can not "Without
ask" save in the server folder.

document module have a exchangeable folder with an configured by default,
if you think it is possible, We are working this week on it, we can share
efforts to do that.

> >
> > Option 1:
> > In this case you can use the static_http_document_root in your server
> config
> > file, and if it is not setted ask for one with a raise may be.
> > Option 2:
> > Extend the document module giving the access to the folders to physical
> > folders.
> if we store file in any other directory on server like path store in
> config file or use static_http_document_root then we can store file on that
> path so it's time consuming it's take much time for loading images. User
> open the all images then fetch the image datas using rpc call and display
> image base on datas.
>

Noup, I think i can not explain by myself well.

The document module give you the ability to "index" all uploaded files, the
file storage is extensible, and you can serve them directly http (exactly
as the static folder does) then, IMHO it is a matter of dedicate some time
to it.

Even we have a ir.attachment type called "url" that can be used to share it
trhought http.

It is a littel more complicated but by far more secure, due to in "Creation
TIme" you can manage the own ACL of OpenERP and in "Serve TIme" just serve
them with an standard http request, (the...

Read more...

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

Since this is a large MP, it would be very helpful for reviewers if you could take a few minutes to give an overview of the modules you are contributing in the 'description' section at the top of this page (listing the different addons and their purpose...)

review: Abstain
Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Unmerged revisions

7. By Serpent Consulting Services

Merged with parent

6. By Serpent Consulting Services

[FIX/IMP] updates for the improvement of widget

5. By Serpent Consulting Services

[ADD] Added multi_image module : more info at http://www.serpentcs.com/serpentcs-openerp-multi-image-lightbox-widget

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'multi_image'
2=== added file 'multi_image/__init__.py'
3--- multi_image/__init__.py 1970-01-01 00:00:00 +0000
4+++ multi_image/__init__.py 2013-09-11 08:32:35 +0000
5@@ -0,0 +1,25 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
11+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
12+#
13+# This program is free software: you can redistribute it and/or modify
14+# it under the terms of the GNU Affero General Public License as
15+# published by the Free Software Foundation, either version 3 of the
16+# License, or (at your option) any later version.
17+#
18+# This program is distributed in the hope that it will be useful,
19+# but WITHOUT ANY WARRANTY; without even the implied warranty of
20+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+# GNU Affero General Public License for more details.
22+#
23+# You should have received a copy of the GNU Affero General Public License
24+# along with this program. If not, see <http://www.gnu.org/licenses/>.
25+#
26+##############################################################################
27+
28+import controllers
29+
30+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
31\ No newline at end of file
32
33=== added file 'multi_image/__openerp__.py'
34--- multi_image/__openerp__.py 1970-01-01 00:00:00 +0000
35+++ multi_image/__openerp__.py 2013-09-11 08:32:35 +0000
36@@ -0,0 +1,50 @@
37+# -*- coding: utf-8 -*-
38+##############################################################################
39+#
40+# OpenERP, Open Source Management Solution
41+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
42+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
43+#
44+# This program is free software: you can redistribute it and/or modify
45+# it under the terms of the GNU Affero General Public License as
46+# published by the Free Software Foundation, either version 3 of the
47+# License, or (at your option) any later version.
48+#
49+# This program is distributed in the hope that it will be useful,
50+# but WITHOUT ANY WARRANTY; without even the implied warranty of
51+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52+# GNU Affero General Public License for more details.
53+#
54+# You should have received a copy of the GNU Affero General Public License
55+# along with this program. If not, see <http://www.gnu.org/licenses/>.
56+#
57+##############################################################################
58+
59+{
60+ "name" : "Multi Image",
61+ "version" : "1.0",
62+ "author" : "Serpent Consulting Services Pvt. Ltd.",
63+ "category": 'Image',
64+ 'complexity': "easy",
65+ 'depends': ['web'],
66+ "description": """
67+ This module provides the functionality to store multiple images for one record.
68+ All images store in server directory. so database size doesnot increase.
69+ """,
70+ 'update_xml': [
71+ ],
72+ 'js':[
73+ "static/lib/lightbox/js/jquery.lightbox.js",
74+ "static/src/js/multi_image.js"
75+ ],
76+ 'css':[
77+ "static/src/css/hoverbox.css",
78+ "static/lib/lightbox/css/lightbox.css",
79+ ],
80+ 'website': 'http://www.serpentcs.com',
81+ 'qweb': ['static/src/xml/image_multi.xml'],
82+ 'installable': True,
83+ 'auto_install': False,
84+}
85+
86+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
87
88=== added directory 'multi_image/controllers'
89=== added file 'multi_image/controllers/__init__.py'
90--- multi_image/controllers/__init__.py 1970-01-01 00:00:00 +0000
91+++ multi_image/controllers/__init__.py 2013-09-11 08:32:35 +0000
92@@ -0,0 +1,25 @@
93+# -*- coding: utf-8 -*-
94+##############################################################################
95+#
96+# OpenERP, Open Source Management Solution
97+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
98+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
99+#
100+# This program is free software: you can redistribute it and/or modify
101+# it under the terms of the GNU Affero General Public License as
102+# published by the Free Software Foundation, either version 3 of the
103+# License, or (at your option) any later version.
104+#
105+# This program is distributed in the hope that it will be useful,
106+# but WITHOUT ANY WARRANTY; without even the implied warranty of
107+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
108+# GNU Affero General Public License for more details.
109+#
110+# You should have received a copy of the GNU Affero General Public License
111+# along with this program. If not, see <http://www.gnu.org/licenses/>.
112+#
113+##############################################################################
114+
115+from . import main
116+
117+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
118\ No newline at end of file
119
120=== added file 'multi_image/controllers/main.py'
121--- multi_image/controllers/main.py 1970-01-01 00:00:00 +0000
122+++ multi_image/controllers/main.py 2013-09-11 08:32:35 +0000
123@@ -0,0 +1,61 @@
124+# -*- coding: utf-8 -*-
125+##############################################################################
126+#
127+# OpenERP, Open Source Management Solution
128+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
129+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
130+#
131+# This program is free software: you can redistribute it and/or modify
132+# it under the terms of the GNU Affero General Public License as
133+# published by the Free Software Foundation, either version 3 of the
134+# License, or (at your option) any later version.
135+#
136+# This program is distributed in the hope that it will be useful,
137+# but WITHOUT ANY WARRANTY; without even the implied warranty of
138+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
139+# GNU Affero General Public License for more details.
140+#
141+# You should have received a copy of the GNU Affero General Public License
142+# along with this program. If not, see <http://www.gnu.org/licenses/>.
143+#
144+##############################################################################
145+
146+from web import http
147+from openerp.addons.web.controllers.main import Binary
148+openerpweb = http
149+import simplejson
150+import time
151+import openerp
152+import os
153+import StringIO
154+
155+class Binary_multi(Binary):
156+
157+ @openerpweb.httprequest
158+ def upload_image_multi(self, req, callback, ufile):
159+ # TODO: might be useful to have a configuration flag for max-length file uploads
160+ out = """<script language="javascript" type="text/javascript">
161+ var win = window.top.window;
162+ win.jQuery(win).trigger(%s, %s);
163+ </script>"""
164+ data = ufile.read()
165+ if data:
166+ current_dat_time = time.strftime("%d%m%y%H%M%S")
167+ file_name = current_dat_time + "_" + ufile.filename
168+ addons_path = openerpweb.addons_manifest['web']['addons_path'] + "/web/static/src/img/image_multi/"
169+ if not os.path.isdir(addons_path):
170+ os.mkdir(addons_path)
171+ addons_path += file_name
172+ buff = StringIO.StringIO()
173+ buff.write(data)
174+ buff.seek(0)
175+ file_name = "/web/static/src/img/image_multi/" + file_name
176+ file = open(addons_path, 'wb')
177+ file.write(buff.read())
178+ file.close()
179+ args = [len(data), file_name, ufile.content_type, ufile.filename, time.strftime("%m/%d/%Y %H:%M:%S")]
180+ else:
181+ args = []
182+ return out % (simplejson.dumps(callback), simplejson.dumps(args))
183+
184+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
185\ No newline at end of file
186
187=== added directory 'multi_image/static'
188=== added directory 'multi_image/static/lib'
189=== added directory 'multi_image/static/lib/lightbox'
190=== added directory 'multi_image/static/lib/lightbox/css'
191=== added file 'multi_image/static/lib/lightbox/css/lightbox.css'
192--- multi_image/static/lib/lightbox/css/lightbox.css 1970-01-01 00:00:00 +0000
193+++ multi_image/static/lib/lightbox/css/lightbox.css 2013-09-11 08:32:35 +0000
194@@ -0,0 +1,128 @@
195+#lightbox{
196+ position: absolute;
197+ left: 0;
198+ width: 100%;
199+ z-index: 100;
200+ text-align: center;
201+ line-height: 0;
202+ }
203+
204+#lightbox a img{ border: none; }
205+
206+#outerImageContainer{
207+ position: relative;
208+ background-color: #fff;
209+ width: 250px;
210+ height: 250px;
211+ margin: 0 auto;
212+ }
213+
214+#imageContainer{
215+ padding: 10px;
216+ }
217+
218+#loading{
219+ position: absolute;
220+ top: 40%;
221+ left: 0%;
222+ height: 25%;
223+ width: 100%;
224+ text-align: center;
225+ line-height: 0;
226+ }
227+#hoverNav{
228+ position: absolute;
229+ top: 0;
230+ left: 0;
231+ height: 100%;
232+ width: 100%;
233+ z-index: 10;
234+ }
235+#imageContainer>#hoverNav{ left: 0;}
236+#hoverNav a{ outline: none;}
237+
238+#prevLink, #nextLink{
239+ width: 49%;
240+ height: 100%;
241+ background: transparent url(../images/blank.gif) no-repeat; /* Trick IE into showing hover */
242+ display: block;
243+ }
244+#prevLink { left: 0; float: left;}
245+#nextLink { right: 0; float: right;}
246+#prevLink:hover, #prevLink:visited:hover { background: url(../images/prev.gif) left 50% no-repeat; }
247+#nextLink:hover, #nextLink:visited:hover { background: url(../images/next.gif) right 50% no-repeat; }
248+/*** START : next / previous text links ***/
249+nextLinkText, #prevLinkText{
250+color: #FF9834;
251+font-weight:bold;
252+text-decoration: none;
253+}
254+#nextLinkText{
255+padding-left: 20px;
256+}
257+#prevLinkText{
258+padding-right: 20px;
259+}
260+/*** END : next / previous text links ***/
261+/*** START : added padding when navbar is on top ***/
262+
263+.ontop #imageData {
264+ padding-top: 5px;
265+}
266+
267+/*** END : added padding when navbar is on top ***/
268+
269+#imageDataContainer{
270+ font: 10px Verdana, Helvetica, sans-serif;
271+ background-color: #fff;
272+ margin: 0 auto;
273+ line-height: 1.4em;
274+ }
275+
276+#imageData{
277+ padding:0 10px;
278+ }
279+#imageData #imageDetails{ width: 70%; float: left; text-align: left; }
280+#imageData #caption{ font-weight: bold; }
281+#imageData #numberDisplay{ display: block; clear: left; padding-bottom: 1.0em; }
282+#imageData #numberDisplay a { color: black; padding-left: 5px; padding-right: 5px;}
283+#imageData #bottomNavClose{float: right; padding-bottom: 0.7em; }
284+#imageData #helpDisplay {clear: left; float: left; display: block; }
285+
286+#overlay{
287+ position: absolute;
288+ top: 0;
289+ left: 0;
290+ z-index: 90;
291+ width: 100%;
292+ height: 500px;
293+ background-color: #000;
294+ filter:alpha(opacity=60);
295+ -moz-opacity: 0.6;
296+ opacity: 0.6 !important;
297+ display: none;
298+ }
299+
300+
301+.clearfix:after {
302+ content: ".";
303+ display: block;
304+ height: 0;
305+ clear: both;
306+ visibility: hidden;
307+ }
308+
309+* html>body .clearfix {
310+ display: inline-block;
311+ width: 100%;
312+ }
313+
314+* html .clearfix {
315+ /* Hides from IE-mac \*/
316+ height: 1%;
317+ /* End hide from IE-mac */
318+ }
319+
320+#lightboxIframe {
321+ display: none;
322+}
323
324=== added directory 'multi_image/static/lib/lightbox/images'
325=== added file 'multi_image/static/lib/lightbox/images/blank.gif'
326Binary files multi_image/static/lib/lightbox/images/blank.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/blank.gif 2013-09-11 08:32:35 +0000 differ
327=== added file 'multi_image/static/lib/lightbox/images/close.gif'
328Binary files multi_image/static/lib/lightbox/images/close.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/close.gif 2013-09-11 08:32:35 +0000 differ
329=== added file 'multi_image/static/lib/lightbox/images/closelabel.gif'
330Binary files multi_image/static/lib/lightbox/images/closelabel.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/closelabel.gif 2013-09-11 08:32:35 +0000 differ
331=== added file 'multi_image/static/lib/lightbox/images/loading.gif'
332Binary files multi_image/static/lib/lightbox/images/loading.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/loading.gif 2013-09-11 08:32:35 +0000 differ
333=== added file 'multi_image/static/lib/lightbox/images/next.gif'
334Binary files multi_image/static/lib/lightbox/images/next.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/next.gif 2013-09-11 08:32:35 +0000 differ
335=== added file 'multi_image/static/lib/lightbox/images/nextlabel.gif'
336Binary files multi_image/static/lib/lightbox/images/nextlabel.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/nextlabel.gif 2013-09-11 08:32:35 +0000 differ
337=== added file 'multi_image/static/lib/lightbox/images/prev.gif'
338Binary files multi_image/static/lib/lightbox/images/prev.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/prev.gif 2013-09-11 08:32:35 +0000 differ
339=== added file 'multi_image/static/lib/lightbox/images/prevlabel.gif'
340Binary files multi_image/static/lib/lightbox/images/prevlabel.gif 1970-01-01 00:00:00 +0000 and multi_image/static/lib/lightbox/images/prevlabel.gif 2013-09-11 08:32:35 +0000 differ
341=== added directory 'multi_image/static/lib/lightbox/js'
342=== added file 'multi_image/static/lib/lightbox/js/jquery.lightbox.js'
343--- multi_image/static/lib/lightbox/js/jquery.lightbox.js 1970-01-01 00:00:00 +0000
344+++ multi_image/static/lib/lightbox/js/jquery.lightbox.js 2013-09-11 08:32:35 +0000
345@@ -0,0 +1,501 @@
346+/**
347+ * jQuery Lightbox
348+ * @author Warren Krewenki
349+ *
350+ * This package is distributed under the BSD license.
351+ * For full license information, see LICENSE.TXT
352+ *
353+ * Based on Lightbox 2 by Lokesh Dhakar (http://www.huddletogether.com/projects/lightbox2/)
354+ *
355+ *
356+ **/
357+
358+(function($) {
359+ $.fn.lightbox = function(options) {
360+ // build main options
361+ var opts = $.extend({}, $.fn.lightbox.defaults, options);
362+
363+ $(window).resize(resizeOverlayToFitWindow);
364+
365+ initialize();
366+ showLightbox(this);
367+ /*
368+ # Initialize the lightbox by creating our html and reading some image data
369+ # This method is called by the constructor after any click events trigger it
370+ # You will never call it by itself, to my knowledge.
371+ */
372+ function initialize() {
373+ $('#overlay, #lightbox').remove();
374+ opts.inprogress = false;
375+
376+ // if jsonData, build the imageArray from data provided in JSON format
377+ if (opts.jsonData && opts.jsonData.length > 0) {
378+ var parser = opts.jsonDataParser ? opts.jsonDataParser : $.fn.lightbox.parseJsonData;
379+ opts.imageArray = [];
380+ opts.imageArray = parser(opts.jsonData);
381+ }
382+
383+ var outerImage = '<div id="outerImageContainer"><div id="imageContainer" style="height: 520px; width: 720px;"><iframe id="lightboxIframe"></iframe><img id="lightboxImage" /><div id="hoverNav"><a href="javascript://" title="' + opts.strings.prevLinkTitle + '" id="prevLink"></a><a href="javascript://" id="nextLink" title="' + opts.strings.nextLinkTitle + '"></a></div><div id="loading"><a href="javascript://" id="loadingLink"><img src="'+opts.fileLoadingImage+'"></a></div></div></div>';
384+ var imageData = '<div id="imageDataContainer" class="clearfix"><div id="imageData"><div id="imageDetails"><span id="caption"></span><span id="numberDisplay"></span></div><div id="bottomNav">';
385+
386+// var outerImage = '<div id="outerImageContainer"><div id="imageContainer" style="height: 520px; width: 720px;"><iframe id="lightboxIframe"></iframe><img id="lightboxImage" syle="vertical-align: middle;" /><div id="hoverNav"><a href="javascript://" title="' + opts.strings.prevLinkTitle + '" id="prevLink"></a><a href="javascript://" id="nextLink" title="' + opts.strings.nextLinkTitle + '"></a></div><div id="loading"><a href="javascript://" id="loadingLink"><img src="'+opts.fileLoadingImage+'"></a></div></div></div>';
387+// var imageData = '<div id="imageDataContainer" class="clearfix"><div id="imageData"><div id="imageDetails"><span id="caption"></span><span id="numberDisplay"></span></div><div id="bottomNav">';
388+
389+ if (opts.displayHelp) {
390+ imageData += '<span id="helpDisplay">' + opts.strings.help + '</span>';
391+ }
392+
393+ imageData += '<a href="javascript://" id="bottomNavClose" class="ui-dialog-titlebar-close ui-corner-all ui-state-hover" title="' + opts.strings.closeTitle + '"><span class="ui-icon ui-icon-closethick">close</span></a></div></div></div>';
394+
395+ var string;
396+
397+ if (opts.navbarOnTop) {
398+ string = '<div id="overlay"></div><div id="lightbox">' + imageData + outerImage + '</div>';
399+ $("body").append(string);
400+ $("#imageDataContainer").addClass('ontop');
401+ } else {
402+ string = '<div id="overlay"></div><div id="lightbox">' + outerImage + imageData + '</div>';
403+ $("body").append(string);
404+ }
405+
406+ //$("#overlay, #lightbox").click(function(){ end(); }).hide();
407+ $("#loadingLink, #bottomNavClose").click(function(){ end(); return false;});
408+ $('#outerImageContainer').width(opts.widthCurrent).height(opts.heightCurrent);
409+ $('#imageDataContainer').width(opts.widthCurrent);
410+
411+ if (!opts.imageClickClose) {
412+ $("#lightboxImage").click(function(){ return false; });
413+ $("#hoverNav").click(function(){ return false; });
414+ }
415+
416+ return true;
417+ };
418+
419+ /*
420+ # Get the document and window width/heigh
421+ #
422+ # Examples
423+ #
424+ # getPageSize()
425+ # # => [1024,768,1024,768]
426+ #
427+ # Returns a numerically indexed array of document width/height and window width/height
428+ */
429+ function getPageSize() {
430+ var jqueryPageSize = new Array($(document).width(),$(document).height(), $(window).width(), $(window).height());
431+ return jqueryPageSize;
432+ };
433+
434+ function getPageScroll() {
435+ var xScroll, yScroll;
436+
437+ if (self.pageYOffset) {
438+ yScroll = self.pageYOffset;
439+ xScroll = self.pageXOffset;
440+ } else if (document.documentElement && document.documentElement.scrollTop){ // Explorer 6 Strict
441+ yScroll = document.documentElement.scrollTop;
442+ xScroll = document.documentElement.scrollLeft;
443+ } else if (document.body) {// all other Explorers
444+ yScroll = document.body.scrollTop;
445+ xScroll = document.body.scrollLeft;
446+ }
447+
448+ var arrayPageScroll = new Array(xScroll,yScroll);
449+ return arrayPageScroll;
450+ };
451+
452+ /*
453+ # Deploy the sexy overlay and display the lightbox
454+ #
455+ # imageObject - the jQuery object passed via the click event in the constructor
456+ #
457+ # Examples
458+ #
459+ # showLightbox($('#CheesusCrust'))
460+ #
461+ # Returns a boolean true, because it's got nothing else to return. It should give visual feedback when run
462+ */
463+ function showLightbox(imageObject) {
464+ /**
465+ * select, embed and object tags render over the lightbox in some browsers
466+ * Right now, the best way to fix it is to hide them, but that can trigger reloading of some flash content
467+ * I don't have a better fix for this right now, but I want ot leave this comment here so you and I both
468+ * know that i'm aware of it, and I would love to fix it, if you have any suggestions.
469+ **/
470+ $("select, embed, object").hide();
471+
472+ // Resize and display the sexy, sexy overlay.
473+ resizeOverlayToFitWindow();
474+ $("#overlay").hide().css({ opacity : opts.overlayOpacity }).fadeIn();
475+ imageNum = 0;
476+
477+ // if data is not provided by jsonData parameter
478+ if (!opts.jsonData) {
479+ opts.imageArray = [];
480+ // if image is NOT part of a set..
481+ if ((!imageObject.rel || (imageObject.rel == '')) && !opts.allSet) {
482+ // add single image to Lightbox.imageArray
483+ opts.imageArray.push(new Array(imageObject.href, opts.displayTitle ? imageObject.title : ''));
484+ } else {
485+ // if image is part of a set..
486+ $("a").each(function() {
487+ if(this.href && (this.rel == imageObject.rel)) {
488+ opts.imageArray.push(new Array(this.href, opts.displayTitle ? this.title : ''));
489+ }
490+ });
491+ }
492+ }
493+
494+ if (opts.imageArray.length > 1) {
495+ for (i = 0; i < opts.imageArray.length; i++) {
496+ for (j = opts.imageArray.length - 1; j > i; j--) {
497+ if (opts.imageArray[i][0] == opts.imageArray[j][0]) {
498+ opts.imageArray.splice(j, 1);
499+ }
500+ }
501+ }
502+ while (opts.imageArray[imageNum][0] != imageObject.href) {
503+ imageNum++;
504+ if (imageNum < opts.imageArray.length) {
505+ break;
506+ }
507+ }
508+ }
509+
510+ // calculate top and left offset for the lightbox
511+ var arrayPageScroll = getPageScroll();
512+ var lightboxTop = arrayPageScroll[1] + ($(window).height() / 10);
513+ var lightboxLeft = arrayPageScroll[0];
514+ $('#lightbox').css({top: lightboxTop+'px', left: lightboxLeft+'px'}).show();
515+
516+ if (!opts.slideNavBar) {
517+ $('#imageData').hide();
518+ }
519+
520+ changeImage(imageNum);
521+ };
522+
523+ function changeImage(imageNum) {
524+ if (opts.inprogress == false) {
525+ opts.inprogress = true;
526+
527+ // update global var
528+ opts.activeImage = imageNum;
529+
530+ // hide elements during transition
531+ $('#loading').show();
532+ $('#lightboxImage, #hoverNav, #prevLink, #nextLink').hide();
533+
534+ // delay preloading image until navbar will slide up
535+ if (opts.slideNavBar) {
536+ $('#imageDataContainer').hide();
537+ $('#imageData').hide();
538+ }
539+ doChangeImage();
540+ }
541+ };
542+
543+ function doChangeImage() {
544+ imgPreloader = new Image();
545+
546+ // once image is preloaded, resize image container
547+ imgPreloader.onload = function() {
548+ var newWidth = imgPreloader.width;
549+ var newHeight = imgPreloader.height;
550+
551+ if (opts.scaleImages) {
552+ newWidth = parseInt(opts.xScale * newWidth);
553+ newHeight = parseInt(opts.yScale * newHeight);
554+ }
555+
556+ if (opts.fitToScreen) {
557+ var arrayPageSize = getPageSize();
558+ var ratio;
559+ var initialPageWidth = arrayPageSize[2] - 2 * opts.borderSize;
560+ var initialPageHeight = arrayPageSize[3] - 200;
561+
562+ var dI = initialPageWidth/initialPageHeight;
563+ var dP = imgPreloader.width/imgPreloader.height;
564+
565+ if ((imgPreloader.height > initialPageHeight) || (imgPreloader.width > initialPageWidth)) {
566+ if (dI > dP) {
567+ newWidth = parseInt((initialPageHeight/imgPreloader.height) * imgPreloader.width);
568+ newHeight = initialPageHeight;
569+ } else {
570+ newHeight = parseInt((initialPageWidth/imgPreloader.width) * imgPreloader.height);
571+ newWidth = initialPageWidth;
572+ }
573+ }
574+ }
575+ if (newWidth > 700) {
576+ newWidth = 700
577+ }
578+ if (newHeight > 500) {
579+ newHeight = 500
580+ }
581+ var line_height = (newHeight / 100) * 7 + 24
582+ $("#imageContainer").attr("style", "line-height:" + line_height)
583+ $("#lightboxImage").attr("style", "line-height:" + line_height)
584+ $('#lightboxImage').
585+ attr('src', opts.imageArray[opts.activeImage][0]).
586+ width(newWidth).
587+ height(newHeight);
588+
589+ resizeImageContainer(newWidth, newHeight);
590+ };
591+
592+ imgPreloader.src = opts.imageArray[opts.activeImage][0];
593+ };
594+
595+ function end() {
596+ $('#overlay, #lightbox, #outerImageContainer, #imageDataContainer').remove();
597+ };
598+
599+ function preloadNeighborImages() {
600+ if (opts.loopImages && opts.imageArray.length > 1) {
601+ preloadNextImage = new Image();
602+ preloadNextImage.src = opts.imageArray[(opts.activeImage == (opts.imageArray.length - 1)) ? 0 : opts.activeImage + 1][0];
603+
604+ preloadPrevImage = new Image();
605+ preloadPrevImage.src = opts.imageArray[(opts.activeImage == 0) ? (opts.imageArray.length - 1) : opts.activeImage - 1][0];
606+ } else {
607+ if ((opts.imageArray.length - 1) > opts.activeImage) {
608+ preloadNextImage = new Image();
609+ preloadNextImage.src = opts.imageArray[opts.activeImage + 1][0];
610+ }
611+ if (opts.activeImage > 0) {
612+ preloadPrevImage = new Image();
613+ preloadPrevImage.src = opts.imageArray[opts.activeImage - 1][0];
614+ }
615+ }
616+ };
617+
618+ function resizeImageContainer(imgWidth, imgHeight) {
619+ // get current width and height
620+ opts.widthCurrent = $("#outerImageContainer").outerWidth();
621+ opts.heightCurrent = $("#outerImageContainer").outerHeight();
622+
623+ // get new width and height
624+ var widthNew = 730;
625+ var heightNew = 520;
626+
627+ // calculate size difference between new and old image, and resize if necessary
628+ wDiff = opts.widthCurrent - widthNew;
629+ hDiff = opts.heightCurrent - heightNew;
630+
631+ $('#imageDataContainer').animate({width: widthNew},opts.resizeSpeed,'linear');
632+ $('#outerImageContainer').animate({width: widthNew},opts.resizeSpeed,'linear', function() {
633+ $('#outerImageContainer').animate({height: heightNew},opts.resizeSpeed,'linear', function() {
634+ showImage();
635+ });
636+ });
637+
638+ afterTimeout = function () {
639+ $('#prevLink').height(500);
640+ $('#nextLink').height(500);
641+ };
642+
643+ // if new and old image are same size and no scaling transition is necessary,
644+ // do a quick pause to prevent image flicker.
645+ if((hDiff == 0) && (wDiff == 0)) {
646+ if (jQuery.browser.msie) {
647+ setTimeout(afterTimeout, 250);
648+ } else {
649+ setTimeout(afterTimeout, 100);
650+ }
651+ } else {
652+ // otherwise just trigger the height and width change
653+ afterTimeout();
654+ }
655+
656+ };
657+
658+ function showImage() {
659+ $('#loading').hide();
660+ $('#lightboxImage').fadeIn("fast");
661+ updateDetails();
662+ preloadNeighborImages();
663+
664+ opts.inprogress = false;
665+ };
666+
667+ function updateDetails() {
668+ $('#numberDisplay').html('');
669+
670+ if (opts.imageArray[opts.activeImage][1]) {
671+ $('#caption').html(opts.imageArray[opts.activeImage][1]).show();
672+ }
673+
674+ // if image is part of set display 'Image x of x'
675+ if (opts.imageArray.length > 1) {
676+ var nav_html;
677+
678+ nav_html = opts.strings.image + (opts.activeImage + 1) + opts.strings.of + opts.imageArray.length;
679+
680+ if (opts.displayDownloadLink) {
681+ nav_html += "<a href='" + opts.imageArray[opts.activeImage][0] + "'>" + opts.strings.download + "</a>";
682+ }
683+
684+ if (!opts.disableNavbarLinks) {
685+ // display previous / next text links
686+ if ((opts.activeImage) > 0 || opts.loopImages) {
687+ nav_html = '<a title="' + opts.strings.prevLinkTitle + '" href="#" id="prevLinkText">' + opts.strings.prevLinkText + "</a>" + nav_html;
688+ }
689+
690+ if (((opts.activeImage + 1) < opts.imageArray.length) || opts.loopImages) {
691+ nav_html += '<a title="' + opts.strings.nextLinkTitle + '" href="#" id="nextLinkText">' + opts.strings.nextLinkText + "</a>";
692+ }
693+ }
694+
695+ $('#numberDisplay').html(nav_html).show();
696+ }
697+
698+ if (opts.slideNavBar) {
699+ $("#imageData").slideDown(opts.navBarSlideSpeed);
700+ } else {
701+ $("#imageData").show();
702+ }
703+
704+ resizeOverlayToFitWindow();
705+ updateNav();
706+ };
707+
708+ /*
709+ # Resize the sexy overlay to fit the constraints of your current viewing environment
710+ #
711+ # This should now happen whenever a window is resized, so you should always see a full overlay
712+ */
713+ function resizeOverlayToFitWindow(){
714+ $('#overlay').css({width: $(document).width(), height: $(document).height()});
715+ // ^^^^^^^ <- sexy!
716+ };
717+
718+ function updateNav() {
719+ if (opts.imageArray.length > 1) {
720+ $('#hoverNav').show();
721+
722+ // if loopImages is true, always show next and prev image buttons
723+ if(opts.loopImages) {
724+ $('#prevLink,#prevLinkText').show().click(function() {
725+ changeImage((opts.activeImage == 0) ? (opts.imageArray.length - 1) : opts.activeImage - 1);
726+ return false;
727+ });
728+
729+ $('#nextLink,#nextLinkText').show().click(function() {
730+ changeImage((opts.activeImage == (opts.imageArray.length - 1)) ? 0 : opts.activeImage + 1);
731+ return false;
732+ });
733+ } else {
734+ // if not first image in set, display prev image button
735+ if(opts.activeImage != 0) {
736+ $('#prevLink,#prevLinkText').show().click(function() {
737+ changeImage(opts.activeImage - 1);
738+ return false;
739+ });
740+ }
741+
742+ // if not last image in set, display next image button
743+ if(opts.activeImage != (opts.imageArray.length - 1)) {
744+ $('#nextLink,#nextLinkText').show().click(function() {
745+ changeImage(opts.activeImage +1);
746+ return false;
747+ });
748+ }
749+ }
750+
751+ enableKeyboardNav();
752+ }
753+ };
754+
755+ function keyboardAction(e) {
756+ var o = e.data.opts;
757+ var keycode = e.keyCode;
758+ var escapeKey = 27;
759+
760+ var key = String.fromCharCode(keycode).toLowerCase();
761+
762+ // close lightbox
763+ if ((key == 'x') || (key == 'o') || (key == 'c') || (keycode == escapeKey)) {
764+ end();
765+
766+ // display previous image
767+ } else if ((key == 'p') || (keycode == 37)) {
768+ if(o.loopImages) {
769+ disableKeyboardNav();
770+ changeImage((o.activeImage == 0) ? (o.imageArray.length - 1) : o.activeImage - 1);
771+ } else if (o.activeImage != 0) {
772+ disableKeyboardNav();
773+ changeImage(o.activeImage - 1);
774+ }
775+
776+ // display next image
777+ } else if ((key == 'n') || (keycode == 39)) {
778+ if (opts.loopImages) {
779+ disableKeyboardNav();
780+ changeImage((o.activeImage == (o.imageArray.length - 1)) ? 0 : o.activeImage + 1);
781+ } else if (o.activeImage != (o.imageArray.length - 1)) {
782+ disableKeyboardNav();
783+ changeImage(o.activeImage + 1);
784+ }
785+ }
786+ };
787+
788+ function enableKeyboardNav() {
789+ $(document).bind('keydown', {opts: opts}, keyboardAction);
790+ };
791+
792+ function disableKeyboardNav() {
793+ $(document).unbind('keydown');
794+ };
795+ };
796+
797+ $.fn.lightbox.parseJsonData = function(data) {
798+ var imageArray = [];
799+
800+ $.each(data, function() {
801+ imageArray.push(new Array(this.url, this.title));
802+ });
803+
804+ return imageArray;
805+ };
806+ $.fn.lightbox.defaults = {
807+ triggerEvent: "click",
808+ allSet: false,
809+ fileLoadingImage: '/multi_image/static/lib/lightbox/images/loading.gif',
810+ fileBottomNavCloseImage: '/multi_image/static/lib/lightbox/images/closelabel.gif',
811+ overlayOpacity: 0.6,
812+ borderSize: 10,
813+ imageArray: new Array,
814+ activeImage: null,
815+ inprogress: false,
816+ resizeSpeed: 350,
817+ widthCurrent: 250,
818+ heightCurrent: 250,
819+ scaleImages: false,
820+ xScale: 1,
821+ yScale: 1,
822+ displayTitle: true,
823+ navbarOnTop: true,
824+ displayDownloadLink: false,
825+ slideNavBar: false,
826+ navBarSlideSpeed: 350,
827+ displayHelp: false,
828+ strings: {
829+ help: ' \u2190 / P - previous image\u00a0\u00a0\u00a0\u00a0\u2192 / N - next image\u00a0\u00a0\u00a0\u00a0ESC / X - close image gallery',
830+ prevLinkTitle: 'previous image',
831+ nextLinkTitle: 'next image',
832+ prevLinkText: '&laquo; Previous',
833+ nextLinkText: 'Next &raquo;',
834+ closeTitle: 'close image gallery',
835+ image: 'Image ',
836+ of: ' of ',
837+ download: 'Download'
838+ },
839+ fitToScreen: false,
840+ disableNavbarLinks: false,
841+ loopImages: false,
842+ imageClickClose: true,
843+ jsonData: null,
844+ jsonDataParser: null
845+ };
846+})(jQuery);
847\ No newline at end of file
848
849=== added directory 'multi_image/static/src'
850=== added directory 'multi_image/static/src/css'
851=== added file 'multi_image/static/src/css/hoverbox.css'
852--- multi_image/static/src/css/hoverbox.css 1970-01-01 00:00:00 +0000
853+++ multi_image/static/src/css/hoverbox.css 2013-09-11 08:32:35 +0000
854@@ -0,0 +1,52 @@
855+.hoverbox a .preview
856+{
857+ display: none;
858+}
859+
860+.hoverbox a:active .preview
861+{
862+ display: block;
863+ position: absolute;
864+ top: 0px;
865+ left: 0px;
866+ z-index: 1;
867+}
868+
869+.hoverbox .list_image
870+{
871+ background: #fff;
872+ border-color: #aaa #ccc #ddd #bbb;
873+ border-style: solid;
874+ border-width: 1px;
875+ color: inherit;
876+ padding: 2px;
877+ vertical-align: top;
878+ width: 150px;
879+}
880+
881+.hoverbox
882+{
883+ background: #eee;
884+ border-color: #ddd #bbb #aaa #ccc;
885+ border-style: solid;
886+ border-width: 1px;
887+ color: inherit;
888+ margin: 1px;
889+ padding: 0px;
890+ position: relative;
891+}
892+
893+.hoverbox .preview
894+{
895+ border-color: #000;
896+ width: 320px;
897+}
898+
899+.oe-remove-image
900+{
901+ cursor: pointer;
902+ vertical-align: middle;
903+ text-align: right;
904+ height: 15px;
905+ width: 15px;
906+}
907\ No newline at end of file
908
909=== added directory 'multi_image/static/src/img'
910=== added file 'multi_image/static/src/img/icon.png'
911Binary files multi_image/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and multi_image/static/src/img/icon.png 2013-09-11 08:32:35 +0000 differ
912=== added directory 'multi_image/static/src/js'
913=== added file 'multi_image/static/src/js/multi_image.js'
914--- multi_image/static/src/js/multi_image.js 1970-01-01 00:00:00 +0000
915+++ multi_image/static/src/js/multi_image.js 2013-09-11 08:32:35 +0000
916@@ -0,0 +1,227 @@
917+openerp.multi_image = function(openerp) {
918+ var _t = openerp.web._t;
919+ var QWeb = openerp.web.qweb;
920+openerp.web.form.FieldBinaryImageMulti = openerp.web.form.FieldBinaryImage.extend({
921+ template: 'FieldBinaryImageMulti',
922+ init: function(field_manager, node) {
923+ var self = this;
924+ this._super(field_manager, node);
925+ this.binary_value = false;
926+ this.useFileAPI = !window.FileReader;
927+ this.max_upload_size = 25 * 1024 * 1024; // 25Mo
928+ if (!this.useFileAPI) {
929+ this.fileupload_id = _.uniqueId('oe_fileupload');
930+ $(window).on(this.fileupload_id, function() {
931+ var args = [].slice.call(arguments).slice(1);
932+ self.on_file_uploaded.apply(self, args);
933+ });
934+ }
935+ },
936+ initialize_content: function() {
937+ var self = this;
938+ var dataset = new openerp.web.DataSetSearch(this, 'res.users', {}, []);
939+ dataset.read_ids([openerp.session.uid], ['name']).then(function(res) {
940+ if (res)
941+ self.user_name = res[0].name;
942+ });
943+ this._super();
944+ },
945+ on_file_uploaded_and_valid: function(size, name, content_type, orignal_file_name, date) {
946+ if (name) {
947+ var data_dict = {"size": openerp.web.human_size(size), "name": name, "content_type": content_type, "date": date, "orignal_name": orignal_file_name, 'user':this.user_name};
948+ var data = JSON.parse(this.get('value'));
949+ if (data)
950+ data.push(data_dict);
951+ else
952+ data = [data_dict];
953+ this.internal_set_value(JSON.stringify(data));
954+ this.binary_value = true;
955+ this.set_filename(name);
956+ this.render_value();
957+ this.do_warn(_t("File Upload"), _t("File Upload Successfully !"));
958+ }
959+ else{
960+ this.do_warn(_t("File Upload"), _t("There was a problem while uploading your file"));
961+ }
962+ },
963+ on_list_image: function() {
964+ var images_list = this.get('value');
965+ var self = this;
966+ if (!this.get('value')) {
967+ this.do_warn(_t("Image"), _t("Image not available !"));
968+ return false;
969+ }
970+ this.image_list_dialog = new openerp.web.Dialog(this, {
971+ title: _t("Image List"),
972+ width: '700px',
973+ height: '70%',
974+ min_width: '600px',
975+ min_height: '500px',
976+ buttons: [
977+ {text: _t("Close"), click: function() { self.image_list_dialog.close();}}
978+ ]
979+ }).open();
980+ this.on_render_dialog();
981+ },
982+ on_render_dialog: function() {
983+ var images_list = JSON.parse(this.get('value'));
984+ var self = this;
985+ var url_list = [];
986+ if (images_list) {
987+ _.each(images_list, function (index) {
988+ if (index) {
989+ if(index['name_1']){
990+ url_list.push({'name' : index['name_1'], 'path' : index['name']})
991+ }else{
992+ url_list.push({'name' : index['orignal_name'], 'path' : index['name']})
993+ }
994+ }
995+ });
996+ }
997+ else { return false; }
998+ var image_list = [];
999+ var start = 0;
1000+ for(var i=1; i <= Math.ceil(url_list.length/4); i++) {
1001+ image_list.push(url_list.slice(start, start + 4))
1002+ start = i * 4;
1003+ }
1004+ this.image_list_dialog.$el.html(QWeb.render('DialogImageList', {'widget': this, 'image_list': image_list}));
1005+ this.image_list_dialog.$el.find(".oe-remove-image").click(function() {
1006+ self.do_remove_image(this, true);
1007+ });
1008+ },
1009+
1010+ render_value: function() {
1011+ var self = this;
1012+ this.$el.find('.oe-image-preview').click(this.on_preview_button);
1013+ this.$el.find('.oe_image_list').click(this.on_list_image);
1014+ var images_list = JSON.parse(this.get('value'));
1015+ this.$el.find('#imagedescription').remove();
1016+ var $img = QWeb.render("ImageDescription", { image_list: images_list, widget: this});
1017+ this.$el.append($img);
1018+ this.$el.find(".oe_image_row").click(function() {
1019+ if (this.id) {
1020+ var clicked = this.id;
1021+ var name_desc = "";
1022+ _.each(images_list, function (index) {
1023+ if (index['name'] == clicked ) {
1024+ var title = index['name_1'] ? index['name_1'] : ''
1025+ var description = index['description'] ? index['description'] : ''
1026+ name_desc = 'Title:-' + title + '<br/>Description:-' +description
1027+ }
1028+ });
1029+ self.do_display_image(this, name_desc);
1030+ }
1031+ });
1032+ this.$el.find(".oe_list_record_delete").click(function() {
1033+ if (this.id) {
1034+ self.do_remove_image(this, false);
1035+ }
1036+ });
1037+ this.$el.find(".oe-record-edit-link").click(function() {
1038+ var self_1 = this;
1039+ var data = JSON.parse(self.get('value'));
1040+ _.each(data, function(d){
1041+ if(d.name == self_1.id){
1042+ self.name_display = d.name_1 ? d.name_1 : '';
1043+ self.description_display =d.description ? d.description : '';
1044+ }
1045+ });
1046+ self.select_mo_dialog = $(QWeb.render('edit_name_description', {widget:self})).dialog({
1047+ resizable: false,
1048+ modal: true,
1049+ title: _t("Image Description"),
1050+ width: 500,
1051+ buttons: {
1052+ "Ok": function() {
1053+ var new_list = [];
1054+ var data = JSON.parse(self.get('value'));
1055+ if (self_1.id && data) {
1056+ _.each(data, function (index) {
1057+ if (index['name'] != self_1.id ) {
1058+ new_list.push(index)
1059+ }
1060+ else {
1061+ index["name_1"] = self.select_mo_dialog.find('#name_1').val()
1062+ index["description"] = self.select_mo_dialog.find('#description').val()
1063+ new_list.push(index)
1064+ }
1065+ });
1066+ self.internal_set_value(JSON.stringify(new_list));
1067+ self.invalid = false
1068+ self.dirty = true
1069+ self.render_value();
1070+ $(this).dialog( "close" );
1071+ }
1072+ },
1073+ "Close": function() {
1074+ $(this).dialog( "close" );
1075+ }
1076+ },
1077+ });
1078+ });
1079+ },
1080+ do_display_image: function(curr_id, name_desc) {
1081+ this.$el.find('.oe-image-preview').lightbox({
1082+ fitToScreen: true,
1083+ jsonData: [{"url" :curr_id.id, "title": name_desc}],
1084+ loopImages: true,
1085+ imageClickClose: false,
1086+ disableNavbarLinks: true
1087+ });
1088+ },
1089+ do_remove_image: function(curr_id, dialog) {
1090+ var self = this;
1091+ var images_list = JSON.parse(this.get('value'));
1092+ if (images_list) {
1093+ var new_list = [];
1094+ if (confirm(_t("Are you sure to remove this image?"))) {
1095+ _.each(images_list, function (index) {
1096+ if (index['name'] != curr_id.id ) {
1097+ new_list.push(index)
1098+ }
1099+ });
1100+ self.internal_set_value(JSON.stringify(new_list));
1101+ this.invalid = false
1102+ this.dirty = true
1103+ if (dialog) {
1104+ this.on_render_dialog();
1105+ }
1106+ else{
1107+ this.render_value();
1108+ }
1109+ }
1110+ }
1111+ },
1112+ on_preview_button: function() {
1113+ console.log(this.get('value'))
1114+ var images_list = JSON.parse(this.get('value'));
1115+ var url_list = [];
1116+ var self = this;
1117+ if (images_list) {
1118+ _.each(images_list, function (index) {
1119+ if (index) {
1120+ var title = index['name_1'] ? index['name_1'] : ''
1121+ var description = index['description'] ? index['description'] : ''
1122+ url_list.push({"url" :index['name'], "title": 'Title:-' + title + '<br/>Description:-' +description})
1123+ }
1124+ });
1125+ }
1126+ else {
1127+ this.do_warn("Image", "Image not available !");
1128+ return false;
1129+ }
1130+ this.$el.find('.oe-image-preview').lightbox({
1131+ fitToScreen: true,
1132+ jsonData: url_list,
1133+ loopImages: true,
1134+ imageClickClose: false,
1135+ disableNavbarLinks: true
1136+ });
1137+ },
1138+});
1139+
1140+openerp.web.form.widgets = openerp.web.form.widgets.extend({
1141+ 'image_multi' : 'openerp.web.form.FieldBinaryImageMulti',
1142+});
1143+}
1144
1145=== added directory 'multi_image/static/src/xml'
1146=== added file 'multi_image/static/src/xml/image_multi.xml'
1147--- multi_image/static/src/xml/image_multi.xml 1970-01-01 00:00:00 +0000
1148+++ multi_image/static/src/xml/image_multi.xml 2013-09-11 08:32:35 +0000
1149@@ -0,0 +1,126 @@
1150+<?xml version="1.0" encoding="UTF-8"?>
1151+<templates id="template" xml:space="preserve">
1152+
1153+ <t t-name="HiddenInputMultiFile">
1154+ <div t-attf-class="oe_hidden_input_file #{fileupload_class or ''}" t-att-style="fileupload_style">
1155+ <form class="oe_form_binary_form" t-att-target="fileupload_id"
1156+ method="post" enctype="multipart/form-data" action="/web/binary/upload_image_multi">
1157+ <input type="hidden" name="session_id" value=""/>
1158+ <input type="hidden" name="callback" t-att-value="fileupload_id"/>
1159+ <t t-raw="__content__"/>
1160+ <input type="file" class="oe_form_binary_file" name="ufile" accept="image/*"/>
1161+ </form>
1162+ <iframe t-att-id="fileupload_id" t-att-name="fileupload_id" style="display: none"/>
1163+ </div>
1164+ </t>
1165+
1166+ <t t-name="FieldBinaryImageMulti">
1167+ <table class="oe_form_field oe_form_field_binary" cellpadding="0" cellspacing="0" border="0" t-att-style="widget.node.attrs.style">
1168+ <tr>
1169+ <td class="oe_form_binary" nowrap="true">
1170+ <table cellspacing="0" cellpadding="0" border="0">
1171+ <tr>
1172+ <td>
1173+ <t t-if="!widget.get('effective_readonly')">
1174+ <t t-call="HiddenInputMultiFile">
1175+ <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
1176+ <t t-set="fileupload_style" t-translation="off">width: 83px;</t>
1177+ <button class="oe_button oe_field_button" type="button">
1178+ <img t-att-src='_s + "/web/static/src/img/icons/STOCK_ADD.png"'/>
1179+ <span>ADD</span>
1180+ </button>
1181+ </t>
1182+ </t>
1183+ </td>
1184+ <td>
1185+ <button class="oe-image-preview oe_button oe_field_button oe_form_binary" type="button" title="Preview">
1186+ <img t-att-src='_s + "/web/static/src/img/icons/gtk-zoom-fit.png"'/>
1187+ <span>Image Preview</span>
1188+ </button>
1189+ </td>
1190+ <td>
1191+ <button class="oe_image_list oe_button oe_field_button oe_form_binary" type="button" title="All Image">
1192+ <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DND_MULTIPLE.png"'/>
1193+ <span>List All Image</span>
1194+ </button>
1195+ </td>
1196+ </tr>
1197+ </table>
1198+ </td>
1199+ <td class="oe_form_binary_progress" style="display: none" nowrap="true">
1200+ <div class="oe_form_binary_progress" style="display: none">
1201+ <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
1202+ <b>Uploading ...</b>
1203+ </div>
1204+ </td>
1205+ </tr>
1206+ </table>
1207+ </t>
1208+
1209+ <t t-name="DialogImageList">
1210+ <table id="DialogImageList">
1211+ <tr t-foreach="image_list" t-as="row">
1212+ <td t-foreach="row" t-as="column" >
1213+ <table class="hoverbox">
1214+ <tr background-color="white">
1215+ <t t-if="!widget.get('effective_readonly')">
1216+ <td style="height:17px;">
1217+ <span class="oe-remove-image oe_i" t-att-id="column.path" title="Delete Image" style="float:right;margin-top: -4px;">d</span>
1218+ </td>
1219+ </t>
1220+ </tr>
1221+ <tr>
1222+ <td>
1223+ <a style="cursor: default;" href='#' id="image_preview">
1224+ <img class="list_image" t-att-src="column.path" t-att-title="column.name"/> <img t-att-title="column.name" t-att-src="column.path" class="preview list_image" /></a>
1225+ </td>
1226+ </tr>
1227+ </table>
1228+ </td>
1229+ </tr>
1230+ </table>
1231+ </t>
1232+
1233+ <t t-name="ImageDescription">
1234+ <div id="imagedescription" class="openerp oe_list oe_view" style="display: block; height:160px;width:100%;overflow: scroll">
1235+ <table class="openerp oe_list_content">
1236+ <thead>
1237+ <tr class="oe_list_header_columns">
1238+ <th class="oe_list_header_char" t-if="!widget.get('effective_readonly')" style="width:3%;"></th>
1239+ <th class="oe_list_header_char" style="width:12%;">File Name</th>
1240+ <th class="oe_list_header_char" style="width:12%;">Title</th>
1241+ <th class="oe_list_header_char" style="width:25%;">Description</th>
1242+ <th class="oe_list_header_char" style="width:10%;">Size</th>
1243+ <th class="oe_list_header_char" style="width:20%;">Date</th>
1244+ <th class="oe_list_header_char" style="width:15%;">User</th>
1245+ <th class="oe_list_header_char" t-if="!widget.get('effective_readonly')" style="width:3%;"></th>
1246+ </tr>
1247+ </thead>
1248+ <tbody>
1249+ <tr t-foreach="image_list" t-as="row">
1250+ <td width="1" t-att-id="row.name" class="oe-record-edit-link" t-if="!widget.get('effective_readonly')" style="width:3%;">
1251+ <img t-if="!widget.get('effective_readonly')" src="/web/static/src/img/pencil.gif" />
1252+ </td>
1253+ <td class="oe_image_row" t-att-id="row.name" style="width:12%;"><t t-esc="row.orignal_name" /></td>
1254+ <td class="oe_image_row" t-att-id="row.name" style="width:12%;"><t t-esc="row.name_1"/></td>
1255+ <td class="oe_image_row" t-att-id="row.name" style="width:25%;"><t t-esc="row.description"/></td>
1256+ <td class="oe_image_row" t-att-id="row.name" style="width:10%;"><t t-esc="row.size"/></td>
1257+ <td class="oe_image_row" t-att-id="row.name" style="width:20%;"><t t-esc="row.date"/></td>
1258+ <td class="oe_image_row" t-att-id="row.name" style="width:15%"><t t-esc="row.user"/></td>
1259+ <td class='oe_list_record_delete' width="3%" t-att-id="row.name" t-if="!widget.get('effective_readonly')">
1260+ <button t-if="!widget.get('effective_readonly')" type="button" name="delete" class="oe_i">d</button>
1261+ </td>
1262+ </tr>
1263+ </tbody>
1264+ </table>
1265+ </div>
1266+ </t>
1267+
1268+ <t t-name="edit_name_description">
1269+ <div>
1270+ <h3>Title : <input t-att-value="widget.name_display" type="textbox" class="name_textbox" id="name_1" style="display: block; height:20px; width:450px;overflow: scroll"/></h3>
1271+ <h3>Description : <textarea type="textarea" class="field_text" id="description" style="display: text; height:160px;width:450px;overflow: scroll"><t t-esc="widget.description_display" /></textarea></h3>
1272+ </div>
1273+ </t>
1274+
1275+</templates>
1276\ No newline at end of file
1277
1278=== added directory 'multi_image_sample'
1279=== added file 'multi_image_sample/__init__.py'
1280--- multi_image_sample/__init__.py 1970-01-01 00:00:00 +0000
1281+++ multi_image_sample/__init__.py 2013-09-11 08:32:35 +0000
1282@@ -0,0 +1,25 @@
1283+# -*- coding: utf-8 -*-
1284+##############################################################################
1285+#
1286+# OpenERP, Open Source Management Solution
1287+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
1288+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
1289+#
1290+# This program is free software: you can redistribute it and/or modify
1291+# it under the terms of the GNU Affero General Public License as
1292+# published by the Free Software Foundation, either version 3 of the
1293+# License, or (at your option) any later version.
1294+#
1295+# This program is distributed in the hope that it will be useful,
1296+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1297+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1298+# GNU Affero General Public License for more details.
1299+#
1300+# You should have received a copy of the GNU Affero General Public License
1301+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1302+#
1303+##############################################################################
1304+
1305+import product
1306+
1307+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1308\ No newline at end of file
1309
1310=== added file 'multi_image_sample/__openerp__.py'
1311--- multi_image_sample/__openerp__.py 1970-01-01 00:00:00 +0000
1312+++ multi_image_sample/__openerp__.py 2013-09-11 08:32:35 +0000
1313@@ -0,0 +1,39 @@
1314+# -*- coding: utf-8 -*-
1315+##############################################################################
1316+#
1317+# OpenERP, Open Source Management Solution
1318+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
1319+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
1320+#
1321+# This program is free software: you can redistribute it and/or modify
1322+# it under the terms of the GNU Affero General Public License as
1323+# published by the Free Software Foundation, either version 3 of the
1324+# License, or (at your option) any later version.
1325+#
1326+# This program is distributed in the hope that it will be useful,
1327+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1328+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1329+# GNU Affero General Public License for more details.
1330+#
1331+# You should have received a copy of the GNU Affero General Public License
1332+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1333+#
1334+##############################################################################
1335+
1336+{
1337+ "name" : "Multi Image Sample",
1338+ "version" : "1.0",
1339+ "author" : "Serpent Consulting Services Pvt. Ltd.",
1340+ 'website': 'http://www.serpentcs.com',
1341+ "category": 'Image',
1342+ 'complexity': "easy",
1343+ 'depends': ['multi_image', 'product'],
1344+ "description": """
1345+ This module is used for added multi image widget in product.
1346+ """,
1347+ 'update_xml': ['product_view.xml'],
1348+ 'installable': True,
1349+ 'auto_install': False,
1350+}
1351+
1352+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1353
1354=== added file 'multi_image_sample/product.py'
1355--- multi_image_sample/product.py 1970-01-01 00:00:00 +0000
1356+++ multi_image_sample/product.py 2013-09-11 08:32:35 +0000
1357@@ -0,0 +1,34 @@
1358+# -*- coding: utf-8 -*-
1359+##############################################################################
1360+#
1361+# OpenERP, Open Source Management Solution
1362+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
1363+# Copyright (C) 2011-2013 Serpent Consulting Services Pvt. Ltd. (<http://www.serpentcs.com>).
1364+#
1365+# This program is free software: you can redistribute it and/or modify
1366+# it under the terms of the GNU Affero General Public License as
1367+# published by the Free Software Foundation, either version 3 of the
1368+# License, or (at your option) any later version.
1369+#
1370+# This program is distributed in the hope that it will be useful,
1371+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1372+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1373+# GNU Affero General Public License for more details.
1374+#
1375+# You should have received a copy of the GNU Affero General Public License
1376+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1377+#
1378+##############################################################################
1379+
1380+from openerp.osv import fields, osv
1381+
1382+class product_product(osv.osv):
1383+ _inherit = "product.product"
1384+
1385+ _columns = {
1386+ 'multi_images': fields.text("Multi Images"),
1387+ }
1388+
1389+product_product()
1390+
1391+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1392\ No newline at end of file
1393
1394=== added file 'multi_image_sample/product_view.xml'
1395--- multi_image_sample/product_view.xml 1970-01-01 00:00:00 +0000
1396+++ multi_image_sample/product_view.xml 2013-09-11 08:32:35 +0000
1397@@ -0,0 +1,20 @@
1398+<?xml version="1.0" encoding="utf-8"?>
1399+<openerp>
1400+ <data>
1401+
1402+ <record id="product_normal_multi_imageform_view" model="ir.ui.view">
1403+ <field name="name">product.normal..multi.image.form.inherit</field>
1404+ <field name="model">product.product</field>
1405+ <field name="priority">5</field>
1406+ <field name="inherit_id" ref="product.product_normal_form_view"/>
1407+ <field name="arch" type="xml">
1408+ <notebook position="inside">
1409+ <page string="Images">
1410+ <field name="multi_images" widget="image_multi"/>
1411+ </page>
1412+ </notebook>
1413+ </field>
1414+ </record>
1415+
1416+ </data>
1417+</openerp>
1418
1419=== added directory 'multi_image_sample/static'
1420=== added directory 'multi_image_sample/static/src'
1421=== added directory 'multi_image_sample/static/src/img'
1422=== added file 'multi_image_sample/static/src/img/icon.png'
1423Binary files multi_image_sample/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and multi_image_sample/static/src/img/icon.png 2013-09-11 08:32:35 +0000 differ

Subscribers

People subscribed via source and target branches