-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1518 from nextcloud/feat/odoo-mailing-list
implemented Odoo integration(newsletter mail list)
- Loading branch information
Showing
9 changed files
with
319 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -363,3 +363,11 @@ | |
NEXTCLOUD_INTEGRATIONS_APPROVAL_EMAILS = ["[email protected]"] | ||
|
||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField" | ||
|
||
|
||
# Do not fill in these values here, use the "development.py" or "production.py" files. | ||
ODOO_URL = "" | ||
ODOO_DB = "" | ||
ODOO_USERNAME = "" | ||
ODOO_PASSWORD = "" | ||
ODOO_MAILING_LIST_ID = 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
nextcloudappstore/user/management/commands/create_missing_profiles.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from django.contrib.auth.models import User | ||
from django.core.management.base import BaseCommand | ||
|
||
from nextcloudappstore.user.models import UserProfile | ||
|
||
|
||
class Command(BaseCommand): | ||
help = "Create missing UserProfile instances for existing users" | ||
|
||
def handle(self, *args, **kwargs): | ||
users_without_profiles = User.objects.filter(profile__isnull=True) | ||
for user in users_without_profiles: | ||
UserProfile.objects.create(user=user, subscribe_to_news=False) | ||
self.stdout.write(f"Created profile for user: {user.username}") | ||
|
||
self.stdout.write("Finished creating missing profiles.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Generated by Django 4.2.16 on 2024-12-06 11:59 | ||
|
||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="UserProfile", | ||
fields=[ | ||
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), | ||
( | ||
"subscribe_to_news", | ||
models.BooleanField( | ||
default=True, help_text="User has opted in to receive Nextcloud news and updates." | ||
), | ||
), | ||
( | ||
"user", | ||
models.OneToOneField( | ||
on_delete=django.db.models.deletion.CASCADE, related_name="profile", to=settings.AUTH_USER_MODEL | ||
), | ||
), | ||
], | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,45 @@ | ||
from django.db import models # noqa | ||
from django.contrib.auth.models import User | ||
from django.db import models | ||
from django.db.models.signals import post_save, pre_save | ||
from django.dispatch import receiver | ||
|
||
# Create your models here. | ||
from .odoo import subscribe_user_to_news, unsubscribe_user_from_news | ||
|
||
|
||
class UserProfile(models.Model): | ||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile") | ||
subscribe_to_news = models.BooleanField( | ||
default=True, help_text="User has opted in to receive Nextcloud news and updates." | ||
) | ||
|
||
def __str__(self): | ||
return f"Profile of {self.user.username}" | ||
|
||
|
||
# Signal to create a profile for each new user | ||
@receiver(post_save, sender=User) | ||
def create_user_profile(sender, instance, created, **kwargs): | ||
if created: | ||
UserProfile.objects.create(user=instance) | ||
|
||
|
||
@receiver(post_save, sender=User) | ||
def save_user_profile(sender, instance, **kwargs): | ||
instance.profile.save() | ||
|
||
|
||
# Detect changes to subscribe_to_news and trigger actions | ||
@receiver(pre_save, sender=UserProfile) | ||
def handle_subscription_change(sender, instance, **kwargs): | ||
if instance.pk: # Ensure this is an update, not a creation | ||
# Fetch the old value of subscribe_to_news | ||
old_value = UserProfile.objects.filter(pk=instance.pk).values_list("subscribe_to_news", flat=True).first() | ||
new_value = instance.subscribe_to_news | ||
|
||
if old_value != new_value: | ||
if new_value: | ||
# Logic to subscribe the user | ||
subscribe_user_to_news(instance.user) | ||
else: | ||
# Logic to unsubscribe the user | ||
unsubscribe_user_from_news(instance.user) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
from defusedxml import xmlrpc | ||
from django.conf import settings | ||
|
||
xmlrpc.monkey_patch() | ||
|
||
import xmlrpc.client # noqa: E402 # nosec | ||
|
||
|
||
def authenticate(): | ||
common = xmlrpc.client.ServerProxy(f"{settings.ODOO_URL}/xmlrpc/2/common") | ||
uid = common.authenticate(settings.ODOO_DB, settings.ODOO_USERNAME, settings.ODOO_PASSWORD, {}) | ||
if not uid: | ||
raise Exception("Authentication failed with Odoo") | ||
return uid | ||
|
||
|
||
def is_odoo_config_valid(): | ||
"""Validate Odoo configuration settings.""" | ||
required_fields = [ | ||
settings.ODOO_URL, | ||
settings.ODOO_DB, | ||
settings.ODOO_USERNAME, | ||
settings.ODOO_PASSWORD, | ||
settings.ODOO_MAILING_LIST_ID, | ||
] | ||
return all(required_fields) | ||
|
||
|
||
def subscribe_user_to_news(user): | ||
if not is_odoo_config_valid(): | ||
print("Odoo configuration is invalid. Skipping subscription.") | ||
return | ||
|
||
uid = authenticate() | ||
models = xmlrpc.client.ServerProxy(f"{settings.ODOO_URL}/xmlrpc/2/object") | ||
|
||
# Check if the contact already exists in Odoo | ||
contact_ids = models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact", | ||
"search", | ||
[[("email", "=", user["email"])]], | ||
) | ||
|
||
if not contact_ids: | ||
# Create a new contact if it doesn't exist | ||
contact_data = { | ||
"name": user.get("name", user["email"]), # Use name if provided, fallback to email | ||
"email": user["email"], | ||
} | ||
contact_id = models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact", | ||
"create", | ||
[contact_data], | ||
) | ||
print(f"New user created with ID {contact_id}") | ||
else: | ||
contact_id = contact_ids[0] | ||
print(f"User exists with ID {contact_id}") | ||
|
||
# Check if the user is subscribed to the specific mailing list | ||
subscription_ids = models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact.subscription", | ||
"search", | ||
[ | ||
[ | ||
("contact_id", "=", contact_id), | ||
("list_id", "=", settings.ODOO_MAILING_LIST_ID), | ||
] | ||
], | ||
) | ||
|
||
if not subscription_ids: | ||
# Subscribe the user to the mailing list | ||
subscription_data = { | ||
"contact_id": contact_id, | ||
"list_id": settings.ODOO_MAILING_LIST_ID, | ||
"opt_out": False, | ||
} | ||
subscription_id = models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact.subscription", | ||
"create", | ||
[subscription_data], | ||
) | ||
print(f"User subscribed to mailing list with subscription ID {subscription_id}") | ||
else: | ||
# Update the existing subscription to ensure opt_out is False | ||
models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact.subscription", | ||
"write", | ||
[subscription_ids, {"opt_out": False}], | ||
) | ||
print(f"User's subscription updated to opt-in for mailing list {settings.ODOO_MAILING_LIST_ID}") | ||
|
||
|
||
def unsubscribe_user_from_news(user): | ||
if not is_odoo_config_valid(): | ||
print("Odoo configuration is invalid. Skipping subscription.") | ||
return | ||
uid = authenticate() | ||
models = xmlrpc.client.ServerProxy(f"{settings.ODOO_URL}/xmlrpc/2/object") | ||
|
||
# Find the contact by email | ||
contact_ids = models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact", | ||
"search", | ||
[[("email", "=", user["email"])]], | ||
) | ||
|
||
if not contact_ids: | ||
print(f"No contact found for email {user['email']} to unsubscribe") | ||
return | ||
|
||
contact_id = contact_ids[0] | ||
|
||
# Find the subscription for the specific mailing list | ||
subscription_ids = models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact.subscription", | ||
"search", | ||
[ | ||
[ | ||
("contact_id", "=", contact_id), | ||
("list_id", "=", settings.ODOO_MAILING_LIST_ID), | ||
] | ||
], | ||
) | ||
|
||
if not subscription_ids: | ||
print(f"No subscription found for contact {contact_id} to mailing list {settings.ODOO_MAILING_LIST_ID}") | ||
return | ||
|
||
# Update the subscription to set opt_out to True | ||
models.execute_kw( | ||
settings.ODOO_DB, | ||
uid, | ||
settings.ODOO_PASSWORD, | ||
"mailing.contact.subscription", | ||
"write", | ||
[subscription_ids, {"opt_out": True}], | ||
) | ||
|
||
print(f"User {user['email']} has been unsubscribed from mailing list {settings.ODOO_MAILING_LIST_ID}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.