diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 6cd3608..ac5832c 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,4 +1,4 @@ # List the OCA project dependencies, one per line # Add a repository url and branch if you need a forked version interface-github - +website diff --git a/website_apps_store/README.rst b/website_apps_store/README.rst new file mode 100644 index 0000000..b939a87 --- /dev/null +++ b/website_apps_store/README.rst @@ -0,0 +1,85 @@ +================== +Website Apps Store +================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fapps--store-lightgray.png?logo=github + :target: https://github.com/OCA/apps-store/tree/12.0/website_apps_store + :alt: OCA/apps-store +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/apps-store-12-0/apps-store-12-0-website_apps_store + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/249/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Providing facility for displaying the module specific information in a standard Odoo +e-commerce website (front-end) and download the zip file of the selected product. + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +* Creation of an error queue if the exception occurs while generating zip file of the product + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* BizzAppDev + +Contributors +~~~~~~~~~~~~ + +* Ruchir Shukla +* Eric Caudal +* Oscar Alcala +* `Tecnativa `_: + + * Víctor M.M. Torres + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/apps-store `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/website_apps_store/__init__.py b/website_apps_store/__init__.py new file mode 100644 index 0000000..91c5580 --- /dev/null +++ b/website_apps_store/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/website_apps_store/__manifest__.py b/website_apps_store/__manifest__.py new file mode 100644 index 0000000..e9036c1 --- /dev/null +++ b/website_apps_store/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2018 BizzAppDev +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Website Apps Store", + "version": "12.0.1.0.0", + 'author': 'Odoo Community Association (OCA), BizzAppDev', + "website": "https://github.com/OCA/apps-store", + "license": "AGPL-3", + "category": "Sales", + "depends": [ + 'website_sale', + 'website_form_recaptcha', + 'apps_download', + 'apps_product_creator', + ], + "summary": "Website Apps Store", + "data": [ + 'views/assets.xml', + 'views/templates.xml', + ], + 'demo': [ + 'demo/product_template_demo.xml', + ], + 'installable': True, + 'auto_install': False, + 'application': False +} diff --git a/website_apps_store/controllers/__init__.py b/website_apps_store/controllers/__init__.py new file mode 100644 index 0000000..12a7e52 --- /dev/null +++ b/website_apps_store/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/website_apps_store/controllers/main.py b/website_apps_store/controllers/main.py new file mode 100644 index 0000000..b9d3ee6 --- /dev/null +++ b/website_apps_store/controllers/main.py @@ -0,0 +1,221 @@ +# Copyright 2017-2018 BizzAppDev +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +import base64 + +from odoo import http +from odoo.http import request +from odoo.addons.http_routing.models.ir_http import slug +from odoo.addons.website.controllers.main import QueryURL +from odoo.addons.website_sale.controllers.main import WebsiteSale, TableCompute +from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) + +PPG = 20 # Products Per Page +PPR = 4 # Products Per Row + + +class WebsiteSaleCustom(WebsiteSale): + + def _get_search_domain(self, search, category, attrib_values): + domain = request.website.sale_product_domain() + if search: + for srch in search.split(" "): + domain += [ + '|', '|', '|', '|', '|', '|', '|', '|', + ('name', 'ilike', srch), + ('technical_name', 'ilike', srch), + ('description', 'ilike', srch), + ('description_sale', 'ilike', srch), + ('product_variant_ids.default_code', 'ilike', srch), + ('product_variant_ids.attribute_value_ids.name', + 'ilike', srch), + ('product_variant_ids.app_description_rst_html', + 'ilike', srch), + ('product_variant_ids.app_author_ids.name', 'ilike', srch), + ('product_variant_ids.app_summary', 'ilike', srch)] + if category: + domain += [('public_categ_ids', 'child_of', int(category))] + + if attrib_values: + attrib = None + ids = [] + for value in attrib_values: + if not attrib: + attrib = value[0] + ids.append(value[1]) + elif value[0] == attrib: + ids.append(value[1]) + else: + domain += [('attribute_line_ids.value_ids', 'in', ids)] + attrib = value[0] + ids = [value[1]] + if attrib: + domain += [('attribute_line_ids.value_ids', 'in', ids)] + + return domain + + @http.route() + def shop(self, page=0, category=None, search='', ppg=False, **post): + res = super(WebsiteSaleCustom, self).shop( + page=page, category=category, search=search, ppg=ppg, **post) + + if ppg: + try: + ppg = int(ppg) + except ValueError: + ppg = PPG + post["ppg"] = ppg + else: + ppg = PPG + + attrib_list = request.httprequest.args.getlist('attrib') + attrib_values = [ + [int(x) for x in v.split("-")] for v in attrib_list if v] + attributes_ids = {v[0] for v in attrib_values} + attrib_set = {v[1] for v in attrib_values} + domain = self._get_search_domain(search, category, attrib_values) + + keep = QueryURL('/shop', category=category and int(category), + search=search, attrib=attrib_list, + order=post.get('order'), + maturity=post.get('maturity'), + version=post.get('version'), author=post.get('author')) + if post.get('version'): + domain += [('product_variant_ids.attribute_value_ids.id', + '=', post.get('version'))] + if post.get('author'): + domain += [('product_variant_ids.app_author_ids.id', + '=', post.get('author'))] + if post.get('maturity', False): + domain += [('product_variant_ids.app_development_status', + '=', post.get('maturity'))] + + url = "/shop" + if search: + post["search"] = search + if attrib_list: + post['attrib'] = attrib_list + + if category: + category = request.env['product.public.category'].browse( + int(category)) + url = "/shop/category/%s" % slug(category) + + attribute_id = request.env.ref( + 'apps_product_creator.attribute_odoo_version') + category_all = request.env['product.public.category'].search([]) + versions = request.env['product.attribute.value'].search([ + ('attribute_id', '=', attribute_id.id)]) + authors = request.env['odoo.author'].search([]) + Product = request.env['product.template'] + + product_count = Product.search_count(domain) + pager = request.website.pager(url=url, total=product_count, + page=page, step=ppg, + scope=7, url_args=post) + products = Product.search(domain, limit=ppg, offset=pager['offset'], + order=self._get_search_order(post)) + + ProductAttribute = request.env['product.attribute'] + if products: + # get all products without limit + selected_products = Product.search(domain, limit=False) + attributes = ProductAttribute.search( + [('attribute_line_ids.product_tmpl_id', 'in', + selected_products.ids)]) + else: + attributes = ProductAttribute.browse(attributes_ids) + + res.qcontext.update({ + 'search': search, + 'category': category, + 'attrib_values': attrib_values, + 'attrib_set': attrib_set, + 'pager': pager, + 'products': products, + 'search_count': product_count, # common for all searchbox + 'bins': TableCompute().process(products, ppg), + 'category_all': category_all, + 'versions': versions, + 'authors': authors, + 'version': post.get('version'), + 'author': post.get('author'), + 'attributes': attributes, + 'keep': keep, + 'maturity': post.get('maturity'), + }) + return res + + @http.route(['/shop/change_attribute_version'], type='json', + auth="public", website=True) + def change_product_attribute_version(self, **kwargs): + product_id = kwargs.get('product_id', False) + product = request.env['product.product'].sudo().browse(product_id) + vals = { + 'name_product': product.name, + 'technical_name': + product.odoo_module_version_id.module_id.technical_name, + 'license': product.app_license_id.name, + 'license_url': product.app_license_id.website, + 'author': ', '.join( + author.name for author in product.app_author_ids), + 'website': product.app_website, + 'repository': product.app_github_url, + 'rst_html': product.app_description_rst_html, + 'app_summary': product.app_summary, + } + return vals + + def validate_recaptcha(self, captcha): + """ Function for validating Recaptcha """ + captcha_obj = request.env['website.form.recaptcha'] + ip_addr = request.httprequest.environ.get('HTTP_X_FORWARDED_FOR') + if ip_addr: + ip_addr = ip_addr.split(',')[0] + else: + ip_addr = request.httprequest.remote_addr + try: + captcha_obj.action_validate( + captcha, ip_addr + ) + except ValidationError: + raise ValidationError([captcha_obj.RESPONSE_ATTR]) + + @http.route( + ['/shop/download_product_zip/' + '//' + '', + '/shop/download_product_zip//' + ''], + type='http', auth="public", website=True) + def download_product_zip(self, product_tmpl, product=False, + google_captcha='', **kwargs): + self.validate_recaptcha(google_captcha) + if not product: + product = product_tmpl.get_version_info() + + attachment = request.env['ir.attachment'].sudo().search([ + ('res_id', '=', product.id), + ('res_model', '=', product._name), + ], limit=1) + if not attachment: + product.sudo().generate_zip_file() + attachment = request.env['ir.attachment'].sudo().search([ + ('res_id', '=', product.id), + ('res_model', '=', product._name), + ], limit=1) + + if attachment: + filecontent = base64.b64decode(attachment.datas) + disposition = 'attachment; filename="%s"' % attachment.datas_fname + # increasing count for the product download + product.sudo().download_count = product.sudo().download_count + 1 + return request.make_response( + filecontent, + [('Content-Type', 'application/zip, application/octet-stream'), + ('Content-Length', len(filecontent)), + ('Content-Disposition', disposition)]) + return False diff --git a/website_apps_store/demo/product_template_demo.xml b/website_apps_store/demo/product_template_demo.xml new file mode 100644 index 0000000..eca790e --- /dev/null +++ b/website_apps_store/demo/product_template_demo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/website_apps_store/i18n/website_apps_store.pot b/website_apps_store/i18n/website_apps_store.pot new file mode 100644 index 0000000..a1dcb48 --- /dev/null +++ b/website_apps_store/i18n/website_apps_store.pot @@ -0,0 +1,149 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_apps_store +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Author: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "License: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Maintainer: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Repository: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Technical Name: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Version: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Website: " +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "https://odoo-community.org" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.category_display +#: model:ir.ui.view,arch_db:website_apps_store.maturity_display +#: model:ir.ui.view,arch_db:website_apps_store.version_display +msgid "All" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.maturity_display +msgid "Alpha" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.maturity_display +msgid "Beta" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.category_display +msgid "Category :" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Download\n" +" " +msgstr "" + +#. module: website_apps_store +#: model:ir.model.fields,field_description:website_apps_store.field_product_product_download_count +msgid "Download Count" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.sort_customize +msgid "Download Count - Max to Min" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.sort_customize +msgid "Download Count - Min to Max" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.maturity_display +msgid "Mature" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.maturity_display +msgid "Maturity :" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.search_placeholder +msgid "Name, Author, Version..." +msgstr "" + +#. module: website_apps_store +#: model:ir.model,name:website_apps_store.model_product_product +msgid "Product" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.product +msgid "Product Name" +msgstr "" + +#. module: website_apps_store +#: model:ir.model,name:website_apps_store.model_product_template +msgid "Product Template" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.maturity_display +msgid "Production/Stable" +msgstr "" + +#. module: website_apps_store +#: model:ir.model.fields,field_description:website_apps_store.field_product_product_total_download_count +#: model:ir.model.fields,field_description:website_apps_store.field_product_template_total_download_count +msgid "Total Download Count" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.version_display +msgid "Version :" +msgstr "" + +#. module: website_apps_store +#: model:ir.ui.view,arch_db:website_apps_store.variants +msgid "Versions Available:" +msgstr "" + diff --git a/website_apps_store/models/__init__.py b/website_apps_store/models/__init__.py new file mode 100644 index 0000000..6a7d91c --- /dev/null +++ b/website_apps_store/models/__init__.py @@ -0,0 +1,2 @@ +from . import product +from . import product_template diff --git a/website_apps_store/models/product.py b/website_apps_store/models/product.py new file mode 100644 index 0000000..8631ff6 --- /dev/null +++ b/website_apps_store/models/product.py @@ -0,0 +1,10 @@ +# Copyright (C) 2017-Today: Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + download_count = fields.Integer() diff --git a/website_apps_store/models/product_template.py b/website_apps_store/models/product_template.py new file mode 100644 index 0000000..79caf47 --- /dev/null +++ b/website_apps_store/models/product_template.py @@ -0,0 +1,26 @@ +# Copyright (C) 2017-Today: Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + @api.multi + @api.depends('product_variant_ids', 'product_variant_ids.download_count') + def _compute_total_download_count(self): + for product in self: + product.total_download_count = sum( + product.mapped('product_variant_ids.download_count')) + + total_download_count = fields.Integer( + compute="_compute_total_download_count", store=True) + + def get_author_details(self): + return self.mapped('product_variant_ids.app_author_ids') + + def get_version_info(self): + products = self.product_variant_ids.sorted( + lambda a: a.attribute_value_ids.sequence, reverse=True) + return products[0] diff --git a/website_apps_store/readme/CONTRIBUTORS.rst b/website_apps_store/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..0f2df77 --- /dev/null +++ b/website_apps_store/readme/CONTRIBUTORS.rst @@ -0,0 +1,7 @@ +* Ruchir Shukla +* Eric Caudal +* Oscar Alcala +* `Tecnativa `_: + + * Víctor M.M. Torres diff --git a/website_apps_store/readme/DESCRIPTION.rst b/website_apps_store/readme/DESCRIPTION.rst new file mode 100644 index 0000000..7310974 --- /dev/null +++ b/website_apps_store/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +Providing facility for displaying the module specific information in a standard Odoo +e-commerce website (front-end) and download the zip file of the selected product. diff --git a/website_apps_store/readme/ROADMAP.rst b/website_apps_store/readme/ROADMAP.rst new file mode 100644 index 0000000..d7d17d9 --- /dev/null +++ b/website_apps_store/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Creation of an error queue if the exception occurs while generating zip file of the product diff --git a/website_apps_store/static/description/icon.png b/website_apps_store/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/website_apps_store/static/description/icon.png differ diff --git a/website_apps_store/static/description/index.html b/website_apps_store/static/description/index.html new file mode 100644 index 0000000..ef79ca6 --- /dev/null +++ b/website_apps_store/static/description/index.html @@ -0,0 +1,434 @@ + + + + + + +Website Apps Store + + + +
+

Website Apps Store

+ + +

Beta License: AGPL-3 OCA/apps-store Translate me on Weblate Try me on Runbot

+

Providing facility for displaying the module specific information in a standard Odoo +e-commerce website (front-end) and download the zip file of the selected product.

+

Table of contents

+ +
+

Known issues / Roadmap

+
    +
  • Creation of an error queue if the exception occurs while generating zip file of the product
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • BizzAppDev
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/apps-store project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/website_apps_store/static/src/css/main.less b/website_apps_store/static/src/css/main.less new file mode 100644 index 0000000..cf0d8e3 --- /dev/null +++ b/website_apps_store/static/src/css/main.less @@ -0,0 +1,5 @@ +.desc_rst > { + .document > .reference{ + display: none; + } +} diff --git a/website_apps_store/static/src/css/style.css b/website_apps_store/static/src/css/style.css new file mode 100644 index 0000000..8004787 --- /dev/null +++ b/website_apps_store/static/src/css/style.css @@ -0,0 +1,6 @@ +.padd_0{ + padding: 0; +} +.font_12{ + font-size: 12px; +} diff --git a/website_apps_store/static/src/js/website_apps_store_tour.js b/website_apps_store/static/src/js/website_apps_store_tour.js new file mode 100644 index 0000000..b1f66e3 --- /dev/null +++ b/website_apps_store/static/src/js/website_apps_store_tour.js @@ -0,0 +1,106 @@ +odoo.define('website_apps_store.tour_custom', function (require) { +'use strict'; + +var Tour = require("web_tour.tour"); +var base = require("web_editor.base"); + + Tour.register('download_zip', { + name: "Download Zip File", + url: '/shop/page/2', + test: true, + wait_for: base.ready() + },[ + { + content: "Shop", + trigger: ".oe_product_cart a:contains('Odoo Module 1')" + }, + { + content: "Select Version", + trigger: "input[type=radio]", + run: function(){ + $('input[type=radio]:last').attr("checked", "checked"); + } + }, + { + content: "Download", + trigger: "button:contains(Download)" + } + ] + ); + + Tour.register('select_version_search', { + name: "Select Version", + url: '/shop', + test: true, + wait_for: base.ready() + },[ + { + content: "Shop", + trigger: ".dropdown_version_by a.dropdown-toggle" + }, + { + content: "Select Version", + trigger: ".dropdown_version_by .dropdown-menu a:contains('10.0')", + }, + ] + ); + + Tour.register('select_category_search', { + name: "Select Category", + url: '/shop', + test: true, + wait_for: base.ready() + },[ + { + content: "Shop", + trigger: ".dropdown_category_by a.dropdown-toggle" + }, + { + content: "Select Category", + trigger: ".dropdown_category_by .dropdown-menu a:contains('Category1')", + }, + ] + ); + + Tour.register('module_search', { + name: "Select Product(Module)", + url: '/shop', + test: true, + wait_for: base.ready() + },[ + { + content: "Shop", + trigger: ".search-query", + run: function(){ + $('.search-query').attr('value', 'Test'); + } + }, + { + content: "Search Button", + trigger: ".oe_search_button", + }, + ] + ); + + Tour.register('technical_name_search', { + name: "Select Product(Module) by Technical Name", + url: '/shop', + test: true, + wait_for: base.ready() + },[ + { + content: "Shop", + trigger: ".search-query", + run: function(){ + $('.search-query').attr('value', 'odoo_module1'); + } + }, + { + content: "Search Button", + trigger: ".oe_search_button", + }, + ] + ); + + +}); diff --git a/website_apps_store/static/src/js/website_sale.js b/website_apps_store/static/src/js/website_sale.js new file mode 100644 index 0000000..cd1eebd --- /dev/null +++ b/website_apps_store/static/src/js/website_sale.js @@ -0,0 +1,188 @@ +/** +* Copyright 2018 BizzAppDev +* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +**/ + +odoo.define('website_apps_store.website_sale', function (require) { + 'use strict'; + + require('web.dom_ready'); + var base = require("web_editor.base"); + var ajax = require('web.ajax'); + var utils = require('web.utils'); + var core = require('web.core'); + var config = require('web.config'); + require("website.content.zoomodoo"); + var _t = core._t; + + + $('.oe_website_sale').each(function () { + var oe_website_sale = this; + var $product_global; + + function price_to_str(price) { + var l10n = _t.database.parameters; + var precision = 2; + + if ($(".decimal_precision").length) { + precision = parseInt($(".decimal_precision").last().data('precision')); + } + var formatted = _.str.sprintf('%.' + precision + 'f', price).split('.'); + formatted[0] = utils.insert_thousand_seps(formatted[0]); + return formatted.join(l10n.decimal_point); + } + + + function update_product_image(event_source, product_id) { + var $img; + if ($('#o-carousel-product').length) { + $img = $(event_source).closest('tr.js_product, .oe_website_sale').find('img.js_variant_img'); + $img.attr("src", "/web/image/product.product/" + product_id + "/image"); + $img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id) + .data('oe-model', 'product.product').data('oe-id', product_id); + + var $thumbnail = $(event_source).closest('tr.js_product, .oe_website_sale').find('img.js_variant_img_small'); + if ($thumbnail.length !== 0) { // if only one, thumbnails are not displayed + $thumbnail.attr("src", "/web/image/product.product/" + product_id + "/image/90x90"); + $('.carousel').carousel(0); + } + } + else { + $img = $(event_source).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img:first, img.product_detail_img'); + $img.attr("src", "/web/image/product.product/" + product_id + "/image"); + $img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id) + .data('oe-model', 'product.product').data('oe-id', product_id); + } + // reset zooming constructs + $img.filter('[data-zoom-image]').attr('data-zoom-image', $img.attr('src')); + if ($img.data('zoomOdoo') !== undefined) { + $img.data('zoomOdoo').isReady = false; + } + } + + $(oe_website_sale).on('change', 'input.js_variant_change, select.js_variant_change, ul[data-attribute_value_ids]', function (ev) { + var $ul = $(ev.target).closest('.js_add_cart_variants'); + var $parent = $ul.closest('.js_product'); + var $product_id = $parent.find('.product_id').first(); + var $price = $parent.find(".oe_price:first .oe_currency_value"); + var $default_price = $parent.find(".oe_default_price:first .oe_currency_value"); + var $tech_deatil = $(".tech_deatil"); + var $license_detail = $(".license_detail"); + var $license_url = $(".license_url"); + var $author_detail = $(".author_detail"); + var $website_detail = $(".website_detail"); + var $repo_detail = $(".repo_detail"); + var $rst_html = $(".desc_rst"); + var $app_summary = $(".app_summary"); + var $name_product = $(".name_product"); + var $optional_price = $parent.find(".oe_optional:first .oe_currency_value"); + var variant_ids = $ul.data("attribute_value_ids"); + if(_.isString(variant_ids)) { + variant_ids = JSON.parse(variant_ids.replace(/'/g, '"')); + } + var values = []; + var unchanged_values = $parent.find('div.oe_unchanged_value_ids').data('unchanged_value_ids') || []; + + $parent.find('input.js_variant_change:checked, select.js_variant_change').each(function () { + values.push(+$(this).val()); + }); + values = values.concat(unchanged_values); + + $parent.find("label").removeClass("text-muted css_not_available"); + + var product_id = false; + for (var k in variant_ids) { + if (_.isEmpty(_.difference(variant_ids[k][1], values))) { + $.when(base.ready()).then(function() { + $price.html(price_to_str(variant_ids[k][2])); + $default_price.html(price_to_str(variant_ids[k][3])); + }); + if (variant_ids[k][3]-variant_ids[k][2]>0.01) { + $default_price.closest('.oe_website_sale').addClass("discount"); + $optional_price.closest('.oe_optional').show().css('text-decoration', 'line-through'); + $default_price.parent().removeClass('hidden'); + } else { + $optional_price.closest('.oe_optional').hide(); + $default_price.parent().addClass('hidden'); + } + product_id = variant_ids[k][0]; + $product_global = product_id; + update_product_image(this, product_id); + ajax.jsonRpc("/shop/change_attribute_version", 'call', { + 'product_id': product_id, + }).then(function (data) { + if(data){ + $tech_deatil.text(data['technical_name']); + $license_detail.text(data['license']); + $('.license_url').attr('href', data['license_url']); + $author_detail.text(data['author']); + $website_detail.text(data['website']); + $repo_detail.text(data['repository']); + $repo_detail.parent()[0].href = data['repository']; + $rst_html.html(data['rst_html']); + $app_summary.text(data['app_summary']); + $name_product.text(data['name_product']); + } + }); + break; + } + } + + $parent.find("input.js_variant_change:radio, select.js_variant_change").each(function () { + var $input = $(this); + var id = +$input.val(); + var values = [id]; + + $parent.find("ul:not(:has(input.js_variant_change[value='" + id + "'])) input.js_variant_change:checked, select.js_variant_change").each(function () { + values.push(+$(this).val()); + }); + + for (var k in variant_ids) { + if (!_.difference(values, variant_ids[k][1]).length) { + return; + } + } + $input.closest("label").addClass("css_not_available"); + $input.find("option[value='" + id + "']").addClass("css_not_available"); + }); + + if (product_id) { + $parent.removeClass("css_not_available"); + $product_id.val(product_id); + $parent.find("#add_to_cart").removeClass("disabled"); + } else { + $parent.addClass("css_not_available"); + $product_id.val(0); + $parent.find("#add_to_cart").addClass("disabled"); + } + }); + + $('#download_zip').on('click', function(ev){ + var product_template_id = $(this).data('tmpl-id'); + ev.preventDefault(); + var google_captcha = $('#g-recaptcha-response').val(); + if (!google_captcha) + return ; + if (grecaptcha !== 'undefined'){ + grecaptcha.reset(); + } + if ($product_global){ + window.location.href = "/shop/download_product_zip/" + product_template_id + '/' + $product_global + '/' + google_captcha; + }else{ + window.location.href = "/shop/download_product_zip/" + product_template_id + '/' + google_captcha; + } + }); + var $captchas = $('.o_website_form_recaptcha'); + ajax.post('/website/recaptcha/', {}).then( + function (result) { + var data = JSON.parse(result); + $captchas.append($( + '
' + )); + if ($captchas.length) { + $.getScript('https://www.google.com/recaptcha/api.js'); + } + } + ); + }); +}); diff --git a/website_apps_store/tests/__init__.py b/website_apps_store/tests/__init__.py new file mode 100644 index 0000000..b450fb9 --- /dev/null +++ b/website_apps_store/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2017-Today: Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_website_apps_store +from . import test_tour_website_apps_store diff --git a/website_apps_store/tests/test_tour_website_apps_store.py b/website_apps_store/tests/test_tour_website_apps_store.py new file mode 100644 index 0000000..c85a6ff --- /dev/null +++ b/website_apps_store/tests/test_tour_website_apps_store.py @@ -0,0 +1,64 @@ +# Copyright 2017-2018 BizzAppDev +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import tests +import mock +import os + +mock_get_module_path = ( + 'odoo.addons.apps_download.models.product_product.ProductProduct' + '._get_module_path' +) + + +@tests.common.at_install(False) +@tests.common.post_install(True) +class TestUi(tests.HttpCase): + + def setUp(self): + super(TestUi, self).setUp() + self.tour = "odoo.__DEBUG__.services['web_tour.tour']" + + def test_download_zip_tour(self): + test_path = os.path.dirname(os.path.realpath(__file__)) + test_path = test_path.split('/website_apps_store')[0] + test_module_path = os.path.join( + test_path + '/apps_download' + '/tests', 'test_modules', + 'second_module') + with mock.patch(mock_get_module_path) as mock_func: + mock_func.return_value = test_module_path + self.browser_js( + "/shop/page/2", self.tour + ".run('download_zip')", + self.tour + ".tours.download_zip.ready", login="admin" + ) + + def test_select_version_search_tour(self): + self.browser_js( + "/shop", + self.tour + ".run('select_version_search')", + self.tour + ".tours.select_version_search.ready", + login="admin" + ) + + def test_select_category_search_tour(self): + self.browser_js( + "/shop", + self.tour + ".run('select_category_search')", + self.tour + ".tours.select_category_search.ready", + login="admin" + ) + + def test_module_search_tour(self): + self.browser_js( + "/shop", + self.tour + ".run('module_search')", + self.tour + ".tours.module_search.ready", + login="admin" + ) + + def test_technical_name_search_tour(self): + self.browser_js( + "/shop", + self.tour + ".run('technical_name_search')", + self.tour + ".tours.technical_name_search.ready", + login="admin" + ) diff --git a/website_apps_store/tests/test_website_apps_store.py b/website_apps_store/tests/test_website_apps_store.py new file mode 100644 index 0000000..aeb757d --- /dev/null +++ b/website_apps_store/tests/test_website_apps_store.py @@ -0,0 +1,67 @@ +# Copyright (C) 2018-Today: Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo.tests.common import TransactionCase + + +class TestWebsiteAppsStore(TransactionCase): + + def setUp(self): + super(TestWebsiteAppsStore, self).setUp() + + self.organization2 = self.env['github.organization'].create({ + 'name': 'Organization 2', + 'github_login': 'login', + }) + + self.organization_serie2 = self.env[ + 'github.organization.serie'].create({ + 'name': '12.0', + 'sequence': 1, + 'organization_id': self.organization2.id, + }) + + self.repository2 = self.env['github.repository'].create({ + 'name': 'Repository1', + 'organization_id': self.organization2.id, + }) + + self.branch2 = self.env['github.repository.branch'].create({ + 'name': '12.0', + 'repository_id': self.repository2.id, + 'organization_id': self.organization2.id, + }) + + self.odoo_module2 = self.env['odoo.module'].create({ + 'technical_name': 'odoo_module2', + }) + + self.attribute = self.env.ref( + 'apps_product_creator.attribute_odoo_version') + self.version = self.env.ref('apps_product_creator.odoo_version_100') + + self.odoo_module2_version2 = self.env['odoo.module.version'].create({ + 'name': 'Odoo Module 2', + 'technical_name': 'odoo_module2', + 'module_id': self.odoo_module2.id, + 'repository_branch_id': self.branch2.id, + 'license': 'AGPL-3', + 'summary': 'Summary Test', + 'website': 'Website Test', + 'description_rst': 'Description Test', + 'version': '10.0', + 'author': 'OCA', + 'depends': 'base', + 'external_dependencies': '{}', + }) + + def test_product_author_version_info(self): + self.assertFalse(self.odoo_module2.product_template_id) + self.odoo_module2.action_create_product() + self.assertTrue(self.odoo_module2.product_template_id) + action = self.odoo_module2.action_view_products() + self.assertEqual( + self.odoo_module2.product_template_id.product_variant_ids.ids[0], + action['res_id'] + ) + self.odoo_module2.product_template_id.get_author_details() + self.odoo_module2.product_template_id.get_version_info() diff --git a/website_apps_store/views/assets.xml b/website_apps_store/views/assets.xml new file mode 100644 index 0000000..eb7bef8 --- /dev/null +++ b/website_apps_store/views/assets.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/website_apps_store/views/templates.xml b/website_apps_store/views/templates.xml new file mode 100644 index 0000000..35732c1 --- /dev/null +++ b/website_apps_store/views/templates.xml @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + +