~hristoast/modding-openmw.com

ace8318b9af589a61eec402ee9b7fcb3b6da2312 — Hristos N. Triantafillou 20 days ago 6c2b146
Split views out into several files
M momw/momw/tests.py => momw/momw/tests.py +28 -21
@@ 20,37 20,46 @@ from .cfg import generate_cfg
from .feeds import LatestModsFeed
from .models import ListedMod, Mod, ModList, Category, Tag
from .sitemaps import sitemap_dict
from .views import (
    about,
    add_a_list,
from .views.dynamicpages import (
    all_mods,
    categories,
    category_detail,
    cfg_generator,
    compat_unknown,
    fully_working,
    gs_directories,
    latest_mods,
    mod_alts,
    mod_detail,
    mod_list_detail,
    mod_list_step_detail,
    mod_lists,
    no_plugin,
    not_working,
    partial_working,
    tag_detail,
    tags,
    tips_cleaning,
    with_plugin,
)
from .views.forms import cfg_generator, feedback
from .views.staticpages import (
    about,
    add_a_list,
    compatibility,
    cookie,
    faq,
    feedback,
    getting_started,
    gs_buy,
    gs_directories,
    gs_install,
    gs_settings,
    gs_tips,
    how_to_get_a_mod_added,
    index,
    load_order,
    mod_alts,
    mod_detail,
    mod_todo,
    mod_list_detail,
    mod_list_step_detail,
    mod_lists,
    no_plugin,
    privacy,
    resources,
    site_versions,
    tags,
    tag_detail,
    tes3mp_server,
    tips,
    tips_atlased_meshes,


@@ 61,13 70,6 @@ from .views import (
    tips_register_bsas,
    tips_shiny_meshes,
    tips_tr_patcher,
    with_plugin,
    latest_mods,
    fully_working,
    partial_working,
    not_working,
    compat_unknown,
    tips_cleaning,
    user_settings,
)
from .settings import MOMW_VER


@@ 197,6 199,11 @@ class MomwBaseSeleniumTests:
        v = gs_tips
        self.check_http_status_code(u, v)

    def test_how_to_get_a_mod_added(self):
        u = "how-to-get-a-mod-added"
        v = how_to_get_a_mod_added
        self.check_http_status_code(u, v)

    def test_gs_directories(self):
        u = "gs-directories"
        v = gs_directories

M momw/momw/urls.py => momw/momw/urls.py +23 -24
@@ 6,40 6,46 @@ from utilz.views import app_info, app_info_json, contact
from .feeds import LatestModsFeed
from .settings import CACHE_MIDDLEWARE_SECONDS, DEBUG, USE_ROBOTS
from .sitemaps import sitemap_dict
from .views import (
    about,
    add_a_list,
from .views.dynamicpages import (
    all_mods,
    categories,
    category_detail,
    cfg_generator,
    compat_unknown,
    fully_working,
    gs_directories,
    latest_mods,
    mod_alts,
    mod_detail,
    mod_list_detail,
    mod_list_step_detail,
    mod_lists,
    no_plugin,
    not_working,
    partial_working,
    tag_detail,
    tags,
    tips_cleaning,
    with_plugin,
)
from .views.forms import cfg_generator, feedback
from .views.staticpages import (
    about,
    add_a_list,
    compatibility,
    cookie,
    faq,
    feedback,
    getting_started,
    gs_buy,
    gs_directories,
    gs_install,
    gs_settings,
    gs_tips,
    how_to_get_a_mod_added,
    index,
    legacy_url_redirect,
    load_order,
    mod_alts,
    mod_detail,
    mod_todo,
    mod_list_detail,
    mod_list_step_detail,
    mod_lists,
    no_plugin,
    privacy,
    resources,
    robots_txt,
    site_versions,
    tags,
    tag_detail,
    tes3mp_server,
    tips,
    tips_atlased_meshes,


@@ 50,17 56,10 @@ from .views import (
    tips_register_bsas,
    tips_shiny_meshes,
    tips_tr_patcher,
    with_plugin,
    latest_mods,
    fully_working,
    partial_working,
    not_working,
    compat_unknown,
    tips_cleaning,
    user_settings,
    whatismyip,
)

from .views.utility import robots_txt, legacy_url_redirect

urlpatterns = [
    path("_app/", app_info, name="app-info"),

D momw/momw/views.py => momw/momw/views.py +0 -567
@@ 1,567 0,0 @@
from collections import OrderedDict
from django.conf import settings
from django.core.mail import send_mail
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.cache import cache_page
from utilz.queries import merge_querysets
from .cfg import generate_cfg, get_visitor_os
from .forms import UserFeedbackForm
from .helpers import paginator_for_queryset
from .models import Category, ListedMod, Mod, ModList, Tag


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def robots_txt(request):
    return HttpResponse(
        """User-agent: *
Disallow: /""",
        content_type="text/plain",
    )


def legacy_url_redirect(request, new_slug, new_view="mod_detail", **kwargs):
    return redirect(new_view, slug=new_slug, **kwargs)


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def about(request):
    return render(request, "about.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def all_mods(request):
    mods = Mod.live.all().order_by("name")
    return render(request, "all_mods.html", {"mods": mods})


def user_settings(request):
    return render(request, "settings.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def how_to_get_a_mod_added(request):
    return render(request, "how-to-get-a-mod-added.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def fully_working(request):
    mods = Mod.fully_working.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "known to be fully working with OpenMW",
            "title": "Compatibility: Fully Working",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def partial_working(request):
    mods = Mod.partial_working.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "known to be partially working with OpenMW, some tweaks may be needed",
            "title": "Compatibility: Partially Working",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def not_working(request):
    mods = Mod.not_working.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "known to not work with OpenMW",
            "title": "Compatibility: Not Working",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def compat_unknown(request):
    mods = Mod.compat_unknown.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "of unknown compatibility",
            "title": "Compatibility: Unknown",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def compatibility(request):
    return render(request, "compatibility.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def latest_mods(request):
    latest_count = 100
    return render(
        request,
        "latest_mods.html",
        {"latest_count": latest_count, "mods": Mod.latest.all()[:latest_count]},
    )


def cfg_generator(request, t="cfg_generator.html"):
    _bsa_dict = OrderedDict()
    _plugin_dict = OrderedDict()

    bsa_mods, plugin_mods, used_mods, data_paths, extra_cfg, preset = generate_cfg(
        request.GET, get_visitor_os(request)
    )

    # Create the bsa load order list
    for m in bsa_mods:
        if isinstance(m.has_bsa, dict):
            for k in m.has_bsa.keys():
                if "." in k:
                    # TODO: 032.1 always gets added in, despite the above
                    # conditional.  Below exists to handle this speshul case.
                    if k in _bsa_dict.keys():
                        _bsa_dict.pop(k)
                else:
                    _bsa_dict.update(m.has_bsa.items())

    # Create the plugin load order list
    for m in plugin_mods:
        if isinstance(m.has_plugin, dict):
            for k in m.has_plugin.keys():
                if "." in k:
                    # TODO: 032.1 always gets added in, despite the above
                    # conditional.  Below exists to handle this speshul case.
                    if k in _plugin_dict.keys():
                        _plugin_dict.pop(k)
                else:
                    if (
                        "NX9_Guards_Complete.ESP" in m.has_plugin.values()
                        and "NX9_Guards_Complete.ESP" in _plugin_dict.values()
                    ):
                        continue
                    _plugin_dict.update(m.has_plugin.items())

    bsa_dict = sorted(_bsa_dict.items(), key=lambda t: t[0])

    plugin_dict = sorted(_plugin_dict.items(), key=lambda t: t[0])

    almost_all_mods = (
        Mod.live.all()
        .exclude(category__title__in=settings.IGNORED_CATS)
        .exclude(name__in=settings.IGNORED_MODS)
        .order_by("name")
    )

    # Re-inject OMWLLF back in since it provides a plugin.
    omwllf = Mod.objects.filter(slug="omwllf")

    all_mods = merge_querysets(almost_all_mods, omwllf, key="name", reverse=False)

    context = {
        "all_mods": all_mods,
        "bsa_dict": bsa_dict,
        "data_paths": data_paths,
        "extra_cfg": extra_cfg,
        "no_focus": True,
        "plugin_dict": plugin_dict,
        "preset": preset,
        "used_mods": used_mods,
    }

    return render(request, t, context)


def feedback(request, t="feedback.html"):
    form = UserFeedbackForm()
    mod = None
    if request.method == "GET":
        get_data = request.GET.copy()
        _mod = get_data.get("mod")
        if _mod:
            try:
                mod = Mod.objects.get(slug=_mod)
                form = UserFeedbackForm(initial={"existing_mod": mod.pk})
            except Mod.DoesNotExist:
                # Some garbage was sent in the get data, bingbot likes to do this.
                # It might be "correct" to display a warning here, but I don't see
                # a legitimate user ever hitting this so there's no point.
                mod = None
                form = UserFeedbackForm()
    elif request.method == "POST":
        form = UserFeedbackForm(request.POST)
        if form.is_valid():
            post_data = request.POST.copy()

            existing_mod = post_data.get("existing_mod", None)
            _new_mod = post_data.get("new_mod", None)
            openmw_version = post_data.get("openmw_version")
            compat = post_data.get("compat")
            submitter_name = post_data.get("submitter_name")
            submitter_email = post_data.get("submitter_email")
            is_anon = post_data.get("is_anon", False)
            is_public = post_data.get("is_public", False)
            notes = post_data.get("notes", None)
            subject_trimmed = False

            if existing_mod:
                _emod = str(Mod.objects.get(pk=existing_mod))
            else:
                _emod = None

            if _new_mod == "":
                new_mod = None

            elif _new_mod:
                # Don't put newlines into the subject
                __new_mod = _new_mod.replace("\n", " ").replace("\r", " ")

                if len(__new_mod) > 77:
                    subject_trimmed = True
                    new_mod = __new_mod[:77]
                else:
                    new_mod = __new_mod

            if notes == "":
                notes = None

            subject = "Feedback form submission from '{sname}' for '{mname}'".format(
                sname=submitter_name, mname=_emod or new_mod or "An Unlisted Mod"
            )

            _body = """Existing Mod: {emod}
New Mod: {nmod}
OpenMW Version: {omwver}
Compat: {compat}
Submitter Name: {sname}
Submitter Email: {semail}
Is Anonymous: {isanon}
Is Public: {ispub}
Notes: {notes}"""

            if subject_trimmed:
                pre = (
                    "This subject was trimmed due to being longer than 77 characters:\n"
                )
                pre += _new_mod + "\n\n\nBegin message as normal:\n\n"

                body = pre + _body

            else:
                body = _body

            send_mail(
                subject,
                # Body
                body.format(
                    emod=_emod,
                    nmod=new_mod,
                    omwver=openmw_version,
                    compat=Mod.COMPAT_CHOICES[int(compat) - 4][1],
                    sname=submitter_name,
                    semail=submitter_email,
                    isanon=is_anon,
                    ispub=is_public,
                    notes=notes,
                ),
                # From
                submitter_email,
                # To
                ["admin@modding-openmw.com"],
                fail_silently=False,
            )
    context = {"form": form, "mod": mod}
    return render(request, t, context)


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tes3mp_server(request, t="tes3mp_server.html"):
    return render(request, t)


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def getting_started(request):
    return render(request, "getting_started.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_buy(request):
    return render(request, "gs_buy.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_install(request):
    return render(request, "gs_install.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_settings(request):
    return render(request, "gs_settings.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_directories(request):
    clean_cats = []
    clean_names = []
    categories = Category.objects.all()
    [clean_cats.append(cat.clean_name) for cat in categories]
    cats_string = ",".join(clean_cats)
    mods = Mod.total_overhaul.all()
    [clean_names.append(mod.clean_name) for mod in mods]
    mods_string = ",".join(clean_names)
    win_string = ""
    for n in clean_names:
        win_string += '{{"games/OpenMWMods/{}$_"}}'.format(n)
    for n in clean_names:
        win_string += '{{"games/OpenMWMods/{}$_"}}'.format(n)
    # win_string = '{"games/OpenMWMods$_"}'.join(clean_names)

    return render(
        request,
        "gs_directories.html",
        {
            "cats_string": cats_string,
            "mods_string": mods_string,
            "rec_count": mods.count(),
            "win_string": win_string,
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_tips(request):
    return render(request, "gs_tips.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def index(request):
    return render(request, "index.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def categories(request):
    categories = Category.used.all()
    return render(request, "categories.html", {"categories": categories})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def category_detail(request, slug):
    category = get_object_or_404(Category, slug=slug)
    cat_mods = category.mod_set.all().order_by("name")
    return render(
        request,
        "mod_category_detail.html",
        {"category": category, "cat_mods": cat_mods},
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def faq(request):
    return render(request, "faq.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def load_order(request):
    return render(request, "load_order.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_alts(request):
    alt_mods = Mod.alts.all()
    return render(request, "mod_alts.html", {"mods": alt_mods})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def no_plugin(request):
    no_plugin = Mod.no_plugins.all()
    return render(request, "no_plugin.html", {"mods": no_plugin})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def with_plugin(request):
    with_plugin = Mod.with_plugins.all()
    return render(request, "with_plugin.html", {"mods": with_plugin})


def mod_detail(request, slug):
    # TODO send context var for prev/next so they can easily be moved to
    mod = get_object_or_404(Mod, slug=slug)
    return render(
        request, "mod_detail.html", {"mod": mod, "visitor_os": get_visitor_os(request)}
    )


def mod_todo(request):
    return render(request, "mod_todo.html")


def cookie(request):
    return render(request, "cookie.html")


def privacy(request):
    return render(request, "privacy.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def resources(request):
    return render(request, "resources.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def site_versions(request):
    return render(request, "site-versions.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def add_a_list(request):
    return render(request, "add_a_list.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_lists(request):
    return render(request, "mod_lists.html", {"mod_lists": ModList.objects.all()})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_list_detail(request, slug):
    modlist = get_object_or_404(ModList, slug=slug)
    return render(
        request,
        "mod_list_detail.html",
        {
            "listed_mods": ListedMod.objects.select_related("mod")
            .select_related("modlist")
            .filter(modlist=modlist),
            "mod_list": modlist,
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_list_step_detail(request, slug, step):
    mod_next = False
    mod_prev = False

    # Legacy support
    _step = request.GET.get("step", step)

    # Handle garbage requests
    if not isinstance(_step, int):
        _step = 1

    modlist = get_object_or_404(ModList, slug=slug)
    modlist_mods = ListedMod.objects.select_related("mod").filter(modlist=modlist)

    if _step > modlist.listedmod_set.count():
        _step = modlist.listedmod_set.count()

    lm = get_object_or_404(ListedMod, modlist=modlist, order_number=_step)

    pag_range, pag_prev, pag_next = paginator_for_queryset(request, modlist_mods, _step)

    if lm.order_number > 1:
        mod_prev = lm.order_number - 1

    if lm.order_number < modlist.listedmod_set.count():
        mod_next = lm.order_number + 1

    return render(
        request,
        "mod_detail.html",
        {
            "listed_mod": lm,
            "mod": lm.mod,
            "mod_list": modlist,
            "mod_next": mod_next,
            "mod_prev": mod_prev,
            "pag_next": pag_next,
            "pag_prev": pag_prev,
            "pag_range": pag_range,
            "visitor_os": get_visitor_os(request),
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tags(request):
    tags = Tag.objects.all().order_by("name")
    return render(request, "tags.html", {"tags": tags})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tag_detail(request, slug):
    tag = get_object_or_404(Tag, slug=slug)
    tag_mods = tag.tagged_mods.all().order_by("name")
    return render(request, "mod_tag_detail.html", {"tag": tag, "tag_mods": tag_mods})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips(request):
    return render(request, "tips.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_atlased_meshes(request):
    return render(request, "tips_atlased_meshes.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_bbc_patching(request):
    return render(request, "tips_bbc_patching.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_cleaning(request):
    plugin_names = []
    for mod in Mod.with_plugins.all():
        if mod.needs_cleaning:
            for p in mod.plugins:
                plugin_names.append(p)
    return render(
        request, "tips_cleaning_with_tes3cmd.html", {"plugin_names": plugin_names}
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_file_renames(request):
    return render(request, "tips_file_renames.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_ini_importer(request):
    return render(request, "tips_ini_importer.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_openmw_physics_fps(request):
    return render(request, "tips_openmw_physics_fps.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_register_bsas(request):
    return render(request, "tips_register_bsas.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_shiny_meshes(request):
    return render(request, "tips_shiny_meshes.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_tr_patcher(request):
    return render(request, "tips_tr_patcher.html")


def whatismyip(request):
    return render(request, "ip.html")

A momw/momw/views/dynamicpages.py => momw/momw/views/dynamicpages.py +241 -0
@@ 0,0 1,241 @@
from django.conf import settings
from django.shortcuts import get_object_or_404, render
from django.views.decorators.cache import cache_page
from ..cfg import get_visitor_os
from ..helpers import paginator_for_queryset
from ..models import Category, ListedMod, Mod, ModList, Tag


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def all_mods(request):
    mods = Mod.live.all().order_by("name")
    return render(request, "all_mods.html", {"mods": mods})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def categories(request):
    categories = Category.used.all()
    return render(request, "categories.html", {"categories": categories})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def category_detail(request, slug):
    category = get_object_or_404(Category, slug=slug)
    cat_mods = category.mod_set.all().order_by("name")
    return render(
        request,
        "mod_category_detail.html",
        {"category": category, "cat_mods": cat_mods},
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def compat_unknown(request):
    mods = Mod.compat_unknown.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "of unknown compatibility",
            "title": "Compatibility: Unknown",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def fully_working(request):
    mods = Mod.fully_working.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "known to be fully working with OpenMW",
            "title": "Compatibility: Fully Working",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_directories(request):
    clean_cats = []
    clean_names = []
    categories = Category.objects.all()
    [clean_cats.append(cat.clean_name) for cat in categories]
    cats_string = ",".join(clean_cats)
    mods = Mod.total_overhaul.all()
    [clean_names.append(mod.clean_name) for mod in mods]
    mods_string = ",".join(clean_names)
    win_string = ""
    for n in clean_names:
        win_string += '{{"games/OpenMWMods/{}$_"}}'.format(n)
    for n in clean_names:
        win_string += '{{"games/OpenMWMods/{}$_"}}'.format(n)
    # win_string = '{"games/OpenMWMods$_"}'.join(clean_names)

    return render(
        request,
        "gs_directories.html",
        {
            "cats_string": cats_string,
            "mods_string": mods_string,
            "rec_count": mods.count(),
            "win_string": win_string,
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def latest_mods(request):
    latest_count = 100
    return render(
        request,
        "latest_mods.html",
        {"latest_count": latest_count, "mods": Mod.latest.all()[:latest_count]},
    )


# TODO: Make this view show mods that arent in lists perhaps
@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_alts(request):
    alt_mods = Mod.alts.all()
    return render(request, "mod_alts.html", {"mods": alt_mods})


def mod_detail(request, slug):
    # TODO send context var for prev/next so they can easily be moved to
    mod = get_object_or_404(Mod, slug=slug)
    return render(
        request, "mod_detail.html", {"mod": mod, "visitor_os": get_visitor_os(request)}
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_list_detail(request, slug):
    modlist = get_object_or_404(ModList, slug=slug)
    return render(
        request,
        "mod_list_detail.html",
        {
            "listed_mods": ListedMod.objects.select_related("mod")
            .select_related("modlist")
            .filter(modlist=modlist),
            "mod_list": modlist,
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_list_step_detail(request, slug, step):
    mod_next = False
    mod_prev = False

    # Legacy support
    _step = request.GET.get("step", step)

    # Handle garbage requests
    if not isinstance(_step, int):
        _step = 1

    modlist = get_object_or_404(ModList, slug=slug)
    modlist_mods = ListedMod.objects.select_related("mod").filter(modlist=modlist)

    if _step > modlist.listedmod_set.count():
        _step = modlist.listedmod_set.count()

    lm = get_object_or_404(ListedMod, modlist=modlist, order_number=_step)

    pag_range, pag_prev, pag_next = paginator_for_queryset(request, modlist_mods, _step)

    if lm.order_number > 1:
        mod_prev = lm.order_number - 1

    if lm.order_number < modlist.listedmod_set.count():
        mod_next = lm.order_number + 1

    return render(
        request,
        "mod_detail.html",
        {
            "listed_mod": lm,
            "mod": lm.mod,
            "mod_list": modlist,
            "mod_next": mod_next,
            "mod_prev": mod_prev,
            "pag_next": pag_next,
            "pag_prev": pag_prev,
            "pag_range": pag_range,
            "visitor_os": get_visitor_os(request),
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_lists(request):
    return render(request, "mod_lists.html", {"mod_lists": ModList.objects.all()})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def no_plugin(request):
    no_plugin = Mod.no_plugins.all()
    return render(request, "no_plugin.html", {"mods": no_plugin})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def not_working(request):
    mods = Mod.not_working.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "known to not work with OpenMW",
            "title": "Compatibility: Not Working",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def partial_working(request):
    mods = Mod.partial_working.all().order_by("name")
    return render(
        request,
        "compat_lists.html",
        {
            "mods": mods,
            "status": "known to be partially working with OpenMW, some tweaks may be needed",
            "title": "Compatibility: Partially Working",
        },
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tag_detail(request, slug):
    tag = get_object_or_404(Tag, slug=slug)
    tag_mods = tag.tagged_mods.all().order_by("name")
    return render(request, "mod_tag_detail.html", {"tag": tag, "tag_mods": tag_mods})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tags(request):
    tags = Tag.objects.all().order_by("name")
    return render(request, "tags.html", {"tags": tags})


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_cleaning(request):
    plugin_names = []
    for mod in Mod.with_plugins.all():
        if mod.needs_cleaning:
            for p in mod.plugins:
                plugin_names.append(p)
    return render(
        request, "tips_cleaning_with_tes3cmd.html", {"plugin_names": plugin_names}
    )


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def with_plugin(request):
    with_plugin = Mod.with_plugins.all()
    return render(request, "with_plugin.html", {"mods": with_plugin})

A momw/momw/views/forms.py => momw/momw/views/forms.py +177 -0
@@ 0,0 1,177 @@
from collections import OrderedDict
from django.conf import settings
from django.core.mail import send_mail
from django.shortcuts import render
from utilz.queries import merge_querysets
from ..cfg import generate_cfg, get_visitor_os
from ..forms import UserFeedbackForm
from ..models import Mod


def cfg_generator(request, t="cfg_generator.html"):
    _bsa_dict = OrderedDict()
    _plugin_dict = OrderedDict()

    bsa_mods, plugin_mods, used_mods, data_paths, extra_cfg, preset = generate_cfg(
        request.GET, get_visitor_os(request)
    )

    # Create the bsa load order list
    for m in bsa_mods:
        if isinstance(m.has_bsa, dict):
            for k in m.has_bsa.keys():
                if "." in k:
                    # TODO: 032.1 always gets added in, despite the above
                    # conditional.  Below exists to handle this speshul case.
                    if k in _bsa_dict.keys():
                        _bsa_dict.pop(k)
                else:
                    _bsa_dict.update(m.has_bsa.items())

    # Create the plugin load order list
    for m in plugin_mods:
        if isinstance(m.has_plugin, dict):
            for k in m.has_plugin.keys():
                if "." in k:
                    # TODO: 032.1 always gets added in, despite the above
                    # conditional.  Below exists to handle this speshul case.
                    if k in _plugin_dict.keys():
                        _plugin_dict.pop(k)
                else:
                    if (
                        "NX9_Guards_Complete.ESP" in m.has_plugin.values()
                        and "NX9_Guards_Complete.ESP" in _plugin_dict.values()
                    ):
                        continue
                    _plugin_dict.update(m.has_plugin.items())

    bsa_dict = sorted(_bsa_dict.items(), key=lambda t: t[0])

    plugin_dict = sorted(_plugin_dict.items(), key=lambda t: t[0])

    almost_all_mods = (
        Mod.live.all()
        .exclude(category__title__in=settings.IGNORED_CATS)
        .exclude(name__in=settings.IGNORED_MODS)
        .order_by("name")
    )

    # Re-inject OMWLLF back in since it provides a plugin.
    omwllf = Mod.objects.filter(slug="omwllf")

    all_mods = merge_querysets(almost_all_mods, omwllf, key="name", reverse=False)

    context = {
        "all_mods": all_mods,
        "bsa_dict": bsa_dict,
        "data_paths": data_paths,
        "extra_cfg": extra_cfg,
        "no_focus": True,
        "plugin_dict": plugin_dict,
        "preset": preset,
        "used_mods": used_mods,
    }

    return render(request, t, context)


def feedback(request, t="feedback.html"):
    form = UserFeedbackForm()
    mod = None
    if request.method == "GET":
        get_data = request.GET.copy()
        _mod = get_data.get("mod")
        if _mod:
            try:
                mod = Mod.objects.get(slug=_mod)
                form = UserFeedbackForm(initial={"existing_mod": mod.pk})
            except Mod.DoesNotExist:
                # Some garbage was sent in the get data, bingbot likes to do this.
                # It might be "correct" to display a warning here, but I don't see
                # a legitimate user ever hitting this so there's no point.
                mod = None
                form = UserFeedbackForm()
    elif request.method == "POST":
        form = UserFeedbackForm(request.POST)
        if form.is_valid():
            post_data = request.POST.copy()

            existing_mod = post_data.get("existing_mod", None)
            _new_mod = post_data.get("new_mod", None)
            openmw_version = post_data.get("openmw_version")
            compat = post_data.get("compat")
            submitter_name = post_data.get("submitter_name")
            submitter_email = post_data.get("submitter_email")
            is_anon = post_data.get("is_anon", False)
            is_public = post_data.get("is_public", False)
            notes = post_data.get("notes", None)
            subject_trimmed = False

            if existing_mod:
                _emod = str(Mod.objects.get(pk=existing_mod))
            else:
                _emod = None

            if _new_mod == "":
                new_mod = None

            elif _new_mod:
                # Don't put newlines into the subject
                __new_mod = _new_mod.replace("\n", " ").replace("\r", " ")

                if len(__new_mod) > 77:
                    subject_trimmed = True
                    new_mod = __new_mod[:77]
                else:
                    new_mod = __new_mod

            if notes == "":
                notes = None

            subject = "Feedback form submission from '{sname}' for '{mname}'".format(
                sname=submitter_name, mname=_emod or new_mod or "An Unlisted Mod"
            )

            _body = """Existing Mod: {emod}
New Mod: {nmod}
OpenMW Version: {omwver}
Compat: {compat}
Submitter Name: {sname}
Submitter Email: {semail}
Is Anonymous: {isanon}
Is Public: {ispub}
Notes: {notes}"""

            if subject_trimmed:
                pre = (
                    "This subject was trimmed due to being longer than 77 characters:\n"
                )
                pre += _new_mod + "\n\n\nBegin message as normal:\n\n"

                body = pre + _body

            else:
                body = _body

            send_mail(
                subject,
                # Body
                body.format(
                    emod=_emod,
                    nmod=new_mod,
                    omwver=openmw_version,
                    compat=Mod.COMPAT_CHOICES[int(compat) - 4][1],
                    sname=submitter_name,
                    semail=submitter_email,
                    isanon=is_anon,
                    ispub=is_public,
                    notes=notes,
                ),
                # From
                submitter_email,
                # To
                ["admin@modding-openmw.com"],
                fail_silently=False,
            )
    context = {"form": form, "mod": mod}
    return render(request, t, context)

A momw/momw/views/staticpages.py => momw/momw/views/staticpages.py +146 -0
@@ 0,0 1,146 @@
from django.conf import settings
from django.shortcuts import render
from django.views.decorators.cache import cache_page


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def about(request):
    return render(request, "about.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def add_a_list(request):
    return render(request, "add_a_list.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def compatibility(request):
    return render(request, "compatibility.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def cookie(request):
    return render(request, "cookie.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def faq(request):
    return render(request, "faq.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def getting_started(request):
    return render(request, "getting_started.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_buy(request):
    return render(request, "gs_buy.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_install(request):
    return render(request, "gs_install.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_settings(request):
    return render(request, "gs_settings.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def gs_tips(request):
    return render(request, "gs_tips.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def how_to_get_a_mod_added(request):
    return render(request, "how-to-get-a-mod-added.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def index(request):
    return render(request, "index.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def load_order(request):
    return render(request, "load_order.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def mod_todo(request):
    return render(request, "mod_todo.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def privacy(request):
    return render(request, "privacy.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def resources(request):
    return render(request, "resources.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def site_versions(request):
    return render(request, "site-versions.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tes3mp_server(request, t="tes3mp_server.html"):
    return render(request, t)


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips(request):
    return render(request, "tips.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_atlased_meshes(request):
    return render(request, "tips_atlased_meshes.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_bbc_patching(request):
    return render(request, "tips_bbc_patching.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_file_renames(request):
    return render(request, "tips_file_renames.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_ini_importer(request):
    return render(request, "tips_ini_importer.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_openmw_physics_fps(request):
    return render(request, "tips_openmw_physics_fps.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_register_bsas(request):
    return render(request, "tips_register_bsas.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_shiny_meshes(request):
    return render(request, "tips_shiny_meshes.html")


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def tips_tr_patcher(request):
    return render(request, "tips_tr_patcher.html")


def user_settings(request):
    return render(request, "settings.html")


def whatismyip(request):
    return render(request, "ip.html")

A momw/momw/views/utility.py => momw/momw/views/utility.py +17 -0
@@ 0,0 1,17 @@
from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import redirect
from django.views.decorators.cache import cache_page


@cache_page(settings.CACHE_MIDDLEWARE_SECONDS)
def robots_txt(request):
    return HttpResponse(
        """User-agent: *
Disallow: /""",
        content_type="text/plain",
    )


def legacy_url_redirect(request, new_slug, new_view="mod_detail", **kwargs):
    return redirect(new_view, slug=new_slug, **kwargs)