Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions website_membership/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

========================
Online Members Directory
========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:c33e448d2b96706000e9f15994bcef2cd8153a1a437fec61eb0d80e8ce1b18e9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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/license-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fvertical--association-lightgray.png?logo=github
:target: https://github.com/OCA/vertical-association/tree/19.0/website_membership
:alt: OCA/vertical-association
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/vertical-association-19-0/vertical-association-19-0-website_membership
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/vertical-association&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Publish your members/association directory publicly.

This module is a backport from Odoo SA and as such, it is not included
in the OCA CLA. That means we do not have a copy of the copyright on it
like all other OCA modules.

**Table of contents**

.. contents::
:local:

Use Cases / Context
===================

This module was ported from Odoo v18.

Related to membership context

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/vertical-association/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/vertical-association/issues/new?body=module:%20website_membership%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* Odoo

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/vertical-association <https://github.com/OCA/vertical-association/tree/19.0/website_membership>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions website_membership/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import controllers
from . import models
25 changes: 25 additions & 0 deletions website_membership/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

{
"name": "Online Members Directory",
"summary": "Publish your members directory",
"version": "19.0.1.0.0",
"category": "Membership",
"author": "Odoo, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/vertical-association",
"depends": ["website_partner", "website_google_map", "membership", "website_sale"],
"data": [
"views/product_template_views.xml",
"views/website_membership_templates.xml",
"security/ir.model.access.csv",
"security/website_membership.xml",
],
"demo": ["demo/membership_demo.xml"],
"installable": True,
"license": "LGPL-3",
"assets": {
"website.website_builder_assets": [
"website_membership/static/src/website_builder/**/*",
],
},
}
3 changes: 3 additions & 0 deletions website_membership/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import main
230 changes: 230 additions & 0 deletions website_membership/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import werkzeug.urls

from odoo import fields, http
from odoo.http import request


class WebsiteMembership(http.Controller):
_references_per_page = 20

@http.route(
[
"/members",
"/members/page/<int:page>",
"/members/association/<membership_id>",
"/members/association/<membership_id>/page/<int:page>",
"/members/country/<int:country_id>",
"/members/country/<country_name>-<int:country_id>",
"/members/country/<int:country_id>/page/<int:page>",
"/members/country/<country_name>-<int:country_id>/page/<int:page>",
"/members/association/<membership_id>/country/<country_name>-<int:country_id>",
"/members/association/<membership_id>/country/<int:country_id>",
"/members/association/<membership_id>/country/<country_name>-<int:country_id>/page/<int:page>",
"/members/association/<membership_id>/country/<int:country_id>/page/<int:page>",
],
type="http",
auth="public",
website=True,
sitemap=True,
)
# flake8: noqa: C901
def members(
self, membership_id=None, country_name=None, country_id=0, page=1, **post
):
Product = request.env["product.product"]
Country = request.env["res.country"]
MembershipLine = request.env["membership.membership_line"]
Partner = request.env["res.partner"]
post_name = post.get("search") or post.get("name", "")
current_country = None
today = fields.Date.today()
# base domain for groupby / searches
base_line_domain = [
("partner_id.website_published", "=", True),
("state", "=", "paid"),
("date_to", ">=", today),
("date_from", "<=", today),
]
if membership_id and membership_id != "free":
membership_id = int(membership_id)
base_line_domain.append(("membership_id", "=", membership_id))
if post_name:
base_line_domain += [
"|",
("partner_id.name", "ilike", post_name),
("partner_id.website_description", "ilike", post_name),
]
# Only include membership products visible to the current user
memberships = Product.search(
[("membership", "=", True)], order="website_sequence"
)
base_line_domain.append(("membership_id", "in", memberships.ids))
# group by country, based on all customers (base domain)
if membership_id != "free":
membership_lines = MembershipLine.sudo().search(base_line_domain)
country_domain = [("member_line_ids", "in", membership_lines.ids)]
if not membership_id:
country_domain = [
"|",
country_domain[0],
("membership_state", "=", "free"),
]
else:
country_domain = [("membership_state", "=", "free")]
if post_name:
country_domain += [
"|",
("name", "ilike", post_name),
("website_description", "ilike", post_name),
]
countries_data = Partner.sudo()._read_group(
country_domain + [("website_published", "=", True)],
groupby=["country_id"],
aggregates=["__count"],
)
countries = [
{
"country_id_count": count,
"country_id": (country.id, country.name),
}
for country, count in countries_data
]
countries_total = sum(count for _, count in countries_data)
line_domain = list(base_line_domain)
if country_id:
line_domain.append(("partner_id.country_id", "=", country_id))
current_country = Country.browse(country_id).read(["id", "name"])[0]
if not any(
x["country_id"][0] == country_id for x in countries if x["country_id"]
):
countries.append(
{
"country_id_count": 0,
"country_id": (country_id, current_country["name"]),
}
)
countries = [d for d in countries if d["country_id"]]
countries.sort(key=lambda d: d["country_id"][1])
countries.insert(
0,
{
"country_id_count": countries_total,
"country_id": (0, request.env._("All Countries")),
},
)
limit = self._references_per_page
offset = limit * (page - 1)
count_members = 0
membership_lines = MembershipLine.sudo()
# displayed non-free membership lines
if membership_id != "free":
count_members = MembershipLine.sudo().search_count(line_domain)
if offset <= count_members:
membership_lines = MembershipLine.sudo().search(
line_domain, offset, limit
)
page_partner_ids = set(m.partner_id.id for m in membership_lines)
# get google maps localization of partners
google_map_partner_ids = []
if request.website.is_view_active("website_membership.opt_index_google_map"):
google_map_partner_ids = MembershipLine.search(
line_domain
)._get_published_companies(limit=2000)
search_domain = [
("membership_state", "=", "free"),
("website_published", "=", True),
]
if post_name:
search_domain += [
"|",
("name", "ilike", post_name),
("website_description", "ilike", post_name),
]
if country_id:
search_domain += [("country_id", "=", country_id)]
free_partners = Partner.sudo().search(search_domain)
memberships_data = []
for membership_record in memberships:
memberships_data.append(
{"id": membership_record.id, "name": membership_record.name}
)
memberships_partner_ids = {}
for line in membership_lines:
memberships_partner_ids.setdefault(line.membership_id.id, []).append(
line.partner_id.id
)
if free_partners:
memberships_data.append(
{"id": "free", "name": request.env._("Free Members")}
)
if not membership_id or membership_id == "free":
if count_members < offset + limit:
free_start = max(offset - count_members, 0)
free_end = max(offset + limit - count_members, 0)
memberships_partner_ids["free"] = free_partners.ids[
free_start:free_end
]
page_partner_ids |= set(memberships_partner_ids["free"])
google_map_partner_ids += free_partners.ids[
: 2000 - len(google_map_partner_ids)
]
count_members += len(free_partners)
google_map_partner_ids = ",".join(str(it) for it in google_map_partner_ids)
google_maps_api_key = request.website.google_maps_api_key
partners = {p.id: p for p in Partner.sudo().browse(list(page_partner_ids))}
base_url = "/members{}{}".format(
f"/association/{membership_id}" if membership_id else "",
f"/country/{country_id}" if country_id else "",
)
# request pager for lines
pager = request.website.pager(
url=base_url,
total=count_members,
page=page,
step=limit,
scope=7,
url_args=post,
)
values = {
"partners": partners,
"memberships_data": memberships_data,
"memberships_partner_ids": memberships_partner_ids,
"membership_id": membership_id,
"countries": countries,
"current_country": current_country
and [current_country["id"], current_country["name"]]
or None,
"current_country_id": current_country and current_country["id"] or 0,
"google_map_partner_ids": google_map_partner_ids,
"pager": pager,
"post": post,
"search": f"?{werkzeug.urls.url_encode(post)}",
"search_count": count_members,
"google_maps_api_key": google_maps_api_key,
}
return request.render("website_membership.index", values)

# Do not use semantic controller due to SUPERUSER_ID
@http.route(["/members/<partner_id>"], type="http", auth="public", website=True)
def partners_detail(self, partner_id, **post):
current_slug = partner_id
_, partner_id = request.env["ir.http"]._unslug(partner_id)
if partner_id:
partner = request.env["res.partner"].sudo().browse(partner_id)
if (
partner.exists() and partner.website_published
): # TODO should be done with access rules
if request.env["ir.http"]._slug(partner) != current_slug:
return request.redirect(
f"/members/{request.env['ir.http']._slug(partner)}"
)
values = {}
values["main_object"] = values["partner"] = partner
# See REVIEW_CAN_PUBLISH_UNSUDO
values["main_object"] = values["main_object"].with_context(
can_publish_unsudo_main_object=True
)
return request.render("website_membership.partner", values)
raise request.not_found()
9 changes: 9 additions & 0 deletions website_membership/demo/membership_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="base.res_partner_12" model="res.partner">
<field name="is_published" eval="True" />
</record>
<record id="base.res_partner_2" model="res.partner">
<field name="is_published" eval="True" />
</record>
</odoo>
Loading
Loading