diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index ac82959e..e0340db5 100644 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -108,7 +108,7 @@ def lookups(self, request, model_admin): def queryset(self, request, queryset): try: if "sites" in self.used_parameters: - return queryset.on_site(Site.objects.get(pk=self.used_parameters["sites"])) + return queryset.filter(models.Q(sites__isnull=True) | models.Q(sites=self.used_parameters["sites"])) return queryset except Site.DoesNotExist as e: # pragma: no cover raise admin.options.IncorrectLookupParameters(e) @@ -760,7 +760,7 @@ def get_fieldsets(self, request, obj=None): { "fields": ( "paginate_by", - ("urlconf", "url_patterns"), + "url_patterns", ("menu_structure", "menu_empty_categories"), "template_prefix", ("default_image_full", "default_image_thumbnail"), diff --git a/djangocms_blog/migrations/0048_alter_blogcategory_options_and_more.py b/djangocms_blog/migrations/0048_alter_blogcategory_options_and_more.py new file mode 100644 index 00000000..37b43a7f --- /dev/null +++ b/djangocms_blog/migrations/0048_alter_blogcategory_options_and_more.py @@ -0,0 +1,167 @@ +# Generated by Django 4.2.16 on 2024-10-03 12:48 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import taggit_autosuggest.managers + + +class Migration(migrations.Migration): + dependencies = [ + ("filer", "0017_image__transparent"), + ( + "taggit", + "0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx", + ), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("djangocms_blog", "0001_squashed_0047_migrate_config"), + ] + + operations = [ + migrations.AlterModelOptions( + name="blogcategory", + options={ + "ordering": (models.OrderBy(models.F("priority"), nulls_last=True),), + "verbose_name": "post category", + "verbose_name_plural": "post categories", + }, + ), + migrations.AlterModelOptions( + name="blogcategorytranslation", + options={ + "default_permissions": (), + "managed": True, + "verbose_name": "post category Translation", + }, + ), + migrations.AlterModelOptions( + name="post", + options={ + "get_latest_by": "date_published", + "ordering": ( + models.OrderBy(models.F("pinned"), nulls_last=True), + "-date_published", + "-date_created", + ), + "verbose_name": "post", + "verbose_name_plural": "posts", + }, + ), + migrations.AlterField( + model_name="authorentriesplugin", + name="authors", + field=models.ManyToManyField( + to=settings.AUTH_USER_MODEL, verbose_name="authors" + ), + ), + migrations.AlterField( + model_name="authorentriesplugin", + name="latest_posts", + field=models.IntegerField( + default=5, + help_text="The number of author entries to be displayed.", + verbose_name="entries", + ), + ), + migrations.AlterField( + model_name="authorentriesplugin", + name="template_folder", + field=models.CharField( + choices=[("plugins", "Default template")], + default="plugins", + help_text="Select plugin layout to load for this instance", + max_length=200, + verbose_name="Plugin layout", + ), + ), + migrations.AlterField( + model_name="blogconfig", + name="default_image_full", + field=models.ForeignKey( + blank=True, + help_text="If left empty the image size will have to be set for every newly created post.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="default_images_full", + to="filer.thumbnailoption", + verbose_name="default size of full images", + ), + ), + migrations.AlterField( + model_name="blogconfig", + name="default_image_thumbnail", + field=models.ForeignKey( + blank=True, + help_text="If left empty the thumbnail image size will have to be set for every newly created post.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="default_images_thumbnail", + to="filer.thumbnailoption", + verbose_name="default size of thumbnail images", + ), + ), + migrations.AlterField( + model_name="blogconfig", + name="namespace", + field=models.CharField( + default=None, + max_length=100, + unique=True, + verbose_name="instance namespace", + ), + ), + migrations.AlterField( + model_name="blogconfig", + name="type", + field=models.CharField(max_length=100, verbose_name="type"), + ), + migrations.AlterField( + model_name="blogconfigtranslation", + name="app_title", + field=models.CharField( + default="blog", max_length=200, verbose_name="application title" + ), + ), + migrations.AlterField( + model_name="genericblogplugin", + name="template_folder", + field=models.CharField( + choices=[("plugins", "Default template")], + default="plugins", + help_text="Select plugin layout to load for this instance", + max_length=200, + verbose_name="Plugin layout", + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="latest_posts", + field=models.IntegerField( + default=5, + help_text="The number of latests entries to be displayed.", + verbose_name="entries", + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="tags", + field=taggit_autosuggest.managers.TaggableManager( + blank=True, + help_text="Show only the entries tagged with chosen tags.", + through="taggit.TaggedItem", + to="taggit.Tag", + verbose_name="filter by tag", + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="template_folder", + field=models.CharField( + choices=[("plugins", "Default template")], + default="plugins", + help_text="Select plugin layout to load for this instance", + max_length=200, + verbose_name="Plugin layout", + ), + ), + ] diff --git a/djangocms_blog/migrations/0049_remove_blogconfig_urlconf_alter_blogcategory_id_and_more.py b/djangocms_blog/migrations/0049_remove_blogconfig_urlconf_alter_blogcategory_id_and_more.py new file mode 100644 index 00000000..f11130d5 --- /dev/null +++ b/djangocms_blog/migrations/0049_remove_blogconfig_urlconf_alter_blogcategory_id_and_more.py @@ -0,0 +1,61 @@ +# Generated by Django 4.2.16 on 2024-11-26 10:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0048_alter_blogcategory_options_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="blogconfig", + name="urlconf", + ), + migrations.AlterField( + model_name="blogcategory", + name="id", + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="id", + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="blogconfig", + name="id", + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="blogconfig", + name="paginate_by", + field=models.SmallIntegerField( + default=20, + help_text="When paginating list views, how many articles per page?", + null=True, + verbose_name="Paginate size", + ), + ), + migrations.AlterField( + model_name="blogconfig", + name="type", + field=models.CharField(default="Article", max_length=100, verbose_name="type"), + ), + migrations.AlterField( + model_name="blogconfigtranslation", + name="id", + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="post", + name="id", + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="postcontent", + name="id", + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + ] diff --git a/tests/utils.py b/tests/utils.py index bf81334c..60b45e91 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,200 +1,7 @@ -import contextlib -import os -import shutil -import sys -from tempfile import mkdtemp -from unittest.mock import patch +from django.contrib.auth.models import AbstractUser -import django -import six -from django.contrib.auth.models import Permission -from django.core.management import call_command -from django.contrib.auth import get_user_model -from django.urls import clear_url_caches -from packaging.version import Version +class CustomUser(AbstractUser): + class Meta: + app_label = "tests.test_utils" -try: - import cms # NOQA - - CMS = True - CMS_41 = Version("4.1") <= Version(cms.__version__) < Version("4.0") - CMS_40 = Version("4.0") <= Version(cms.__version__) < Version("4.1") -except ImportError: # pragma: no cover - CMS = False - CMS_41 = False - CMS_40 = False - - -DJANGO_3_2 = Version(django.get_version()) < Version("4.0") - -@contextlib.contextmanager -def work_in(dirname=None): - """ - Context manager version of os.chdir. When exited, returns to the working - directory prior to entering. - - Grabbed from cookiecutter, thanks audreyr! - """ - curdir = os.getcwd() - try: - if dirname is not None: - if dirname not in sys.path: - sys.path.insert(0, dirname) - os.chdir(dirname) - yield - finally: - os.chdir(curdir) - - -@contextlib.contextmanager -def captured_output(): - with patch("sys.stdout", new_callable=six.StringIO) as out: - with patch("sys.stderr", new_callable=six.StringIO) as err: - yield out, err - - -# Borrowed from django CMS codebase -@contextlib.contextmanager -def temp_dir(suffix="", container="/dev/shm/"): - name = make_temp_dir(suffix, container) - yield name - shutil.rmtree(name) - - -def make_temp_dir(suffix="", container="/dev/shm/"): - if os.path.exists(container): - return mkdtemp(suffix=suffix, dir=container) - return mkdtemp(suffix=suffix) - - -@contextlib.contextmanager -def persistent_dir(suffix, container="data"): - name = os.path.abspath(os.path.join(container, suffix)) - if not os.path.exists(name): - os.makedirs(name) - yield name - - -class DisableMigrations: - def __contains__(self, item): # pragma: no cover - return True - - def __getitem__(self, item): # pragma: no cover - return None - - -def reload_urls(settings, urlconf=None, cms_apps=True): - if "cms.urls" in sys.modules: - six.moves.reload_module(sys.modules["cms.urls"]) - if urlconf is None: - urlconf = settings.ROOT_URLCONF - if urlconf in sys.modules: - six.moves.reload_module(sys.modules[urlconf]) - clear_url_caches() - if cms_apps: - from cms.appresolver import clear_app_resolvers, get_app_patterns - - clear_app_resolvers() - get_app_patterns() - - -def _create_db(migrate_cmd=False): - call_command("migrate") - - -def get_user_model(): - from django.contrib.auth import get_user_model - - return get_user_model() - - -def create_user( - username, - email, - password, - is_staff=False, - is_superuser=False, - base_cms_permissions=False, - permissions=None, -): - User = get_user_model() # NOQA - - try: - if User.USERNAME_FIELD == "email": - user = User.objects.get(**{User.USERNAME_FIELD: email}) - else: - user = User.objects.get(**{User.USERNAME_FIELD: username}) - except User.DoesNotExist: - user = User() - - if User.USERNAME_FIELD != "email": - setattr(user, User.USERNAME_FIELD, username) - - try: - user.email = email - except AttributeError: - pass - user.set_password(password) - if is_superuser: - user.is_superuser = True - if is_superuser or is_staff: - user.is_staff = True - user.is_active = True - user.save() - if user.is_staff and not is_superuser and base_cms_permissions: - user.user_permissions.add(Permission.objects.get(codename="add_text")) - user.user_permissions.add(Permission.objects.get(codename="delete_text")) - user.user_permissions.add(Permission.objects.get(codename="change_text")) - user.user_permissions.add(Permission.objects.get(codename="publish_page")) - - user.user_permissions.add(Permission.objects.get(codename="add_page")) - user.user_permissions.add(Permission.objects.get(codename="change_page")) - user.user_permissions.add(Permission.objects.get(codename="delete_page")) - if is_staff and not is_superuser and permissions: - for permission in permissions: - user.user_permissions.add(Permission.objects.get(codename=permission)) - return user - - -def get_user_model_labels(): - User = get_user_model() # NOQA - - user_orm_label = "{}.{}".format(User._meta.app_label, User._meta.object_name) - user_model_label = "{}.{}".format(User._meta.app_label, User._meta.model_name) - return user_orm_label, user_model_label - - -class UserLoginContext: - def __init__(self, testcase, user, password=None): - self.testcase = testcase - self.user = user - if password is None: - password = getattr(user, get_user_model().USERNAME_FIELD) - self.password = password - - def __enter__(self): - loginok = self.testcase.client.login( - username=getattr(self.user, get_user_model().USERNAME_FIELD), - password=self.password, - ) - self.testcase.assertTrue(loginok) - self.testcase._login_context = self - - def __exit__(self, exc, value, tb): - self.testcase._login_context = None - self.testcase.client.logout() - - -def ensure_unicoded_and_unique(args_list, application): - """ - Iterate over args_list, make it unicode if needed and ensure that there - are no duplicates. - Returns list of unicoded arguments in the same order. - """ - unicoded_args = [] - for argument in args_list: - argument = argument if not isinstance(argument, str) else argument - if argument not in unicoded_args or argument == application: - unicoded_args.append(argument) - return unicoded_args