From 04788ddd83c5e80a09a468d07427f0e365db71d7 Mon Sep 17 00:00:00 2001 From: Bobby Date: Mon, 16 Dec 2024 00:41:02 -0500 Subject: pagoda realm --- apps/pagoda/admin.py | 3 + apps/pagoda/apps.py | 2 +- apps/pagoda/migrations/0001_initial.py | 53 +++++ ...e_pagodasites_verficationrecordname_and_more.py | 29 +++ apps/pagoda/models.py | 29 +++ apps/pagoda/urls.py | 11 ++ apps/pagoda/views.py | 186 ++++++++++++++++++ internal/pagoda_utilities.py | 44 +++++ middleware/userprofilemiddleware.py | 4 + requirements.txt | 1 + ...file_balance_userprofile_experience_and_more.py | 27 +++ .../0014_userprofile_journal_streak_and_more.py | 27 +++ services/users/models.py | 8 +- static/css/pagoda/pagoda.css | 213 +++++++++++++++++++++ static/css/shared/core.css | 32 ++++ static/images/pagoda/pagoda_banner.png | Bin 0 -> 2186420 bytes templates/en/pagoda/home.html | 213 +++++++++++++++++++++ templates/en/pagoda/site_verification.html | 165 ++++++++++++++++ templates/ja/pagoda/home.html | 3 + templates/shared/left_sidebar.html | 6 +- templates/shared/right_sidebar.html | 12 +- thatcomputerscientist/settings.py | 2 + thatcomputerscientist/urls.py | 1 + 23 files changed, 1060 insertions(+), 11 deletions(-) create mode 100644 apps/pagoda/migrations/0001_initial.py create mode 100644 apps/pagoda/migrations/0002_rename_verficationrecordname_pagodasites_verficationrecordname_and_more.py create mode 100644 apps/pagoda/urls.py create mode 100644 internal/pagoda_utilities.py create mode 100644 services/users/migrations/0013_userprofile_balance_userprofile_experience_and_more.py create mode 100644 services/users/migrations/0014_userprofile_journal_streak_and_more.py create mode 100644 static/css/pagoda/pagoda.css create mode 100644 static/images/pagoda/pagoda_banner.png create mode 100644 templates/en/pagoda/home.html create mode 100644 templates/en/pagoda/site_verification.html create mode 100644 templates/ja/pagoda/home.html diff --git a/apps/pagoda/admin.py b/apps/pagoda/admin.py index 8c38f3f3..94502d0f 100644 --- a/apps/pagoda/admin.py +++ b/apps/pagoda/admin.py @@ -1,3 +1,6 @@ from django.contrib import admin # Register your models here. +from apps.pagoda.models import PagodaSites + +admin.site.register(PagodaSites) diff --git a/apps/pagoda/apps.py b/apps/pagoda/apps.py index acc7a0eb..54a5ecdd 100644 --- a/apps/pagoda/apps.py +++ b/apps/pagoda/apps.py @@ -3,4 +3,4 @@ from django.apps import AppConfig class PagodaConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "pagoda" + name = "apps.pagoda" diff --git a/apps/pagoda/migrations/0001_initial.py b/apps/pagoda/migrations/0001_initial.py new file mode 100644 index 00000000..9ba0607e --- /dev/null +++ b/apps/pagoda/migrations/0001_initial.py @@ -0,0 +1,53 @@ +# Generated by Django 5.0.7 on 2024-12-16 02:41 + +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="PagodaSites", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50)), + ("description", models.TextField(blank=True)), + ("url", models.URLField()), + ("verified", models.BooleanField(default=False)), + ("siteUniqueIdentifier", models.TextField(unique=True)), + ("VerficationRecordName", models.CharField(blank=True, max_length=50)), + ( + "VerificationRecordValue", + models.CharField(blank=True, max_length=50), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name_plural": "Pagoda Sites", + "ordering": ["-created_at"], + }, + ), + ] diff --git a/apps/pagoda/migrations/0002_rename_verficationrecordname_pagodasites_verficationrecordname_and_more.py b/apps/pagoda/migrations/0002_rename_verficationrecordname_pagodasites_verficationrecordname_and_more.py new file mode 100644 index 00000000..2a22d9a4 --- /dev/null +++ b/apps/pagoda/migrations/0002_rename_verficationrecordname_pagodasites_verficationrecordname_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.7 on 2024-12-16 02:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("pagoda", "0001_initial"), + ] + + operations = [ + migrations.RenameField( + model_name="pagodasites", + old_name="VerficationRecordName", + new_name="verficationRecordName", + ), + migrations.RenameField( + model_name="pagodasites", + old_name="VerificationRecordValue", + new_name="verificationRecordValue", + ), + migrations.AddField( + model_name="pagodasites", + name="verificationMethod", + field=models.CharField( + choices=[("DNS", "DNS"), ("Meta", "Meta")], default="DNS", max_length=4 + ), + ), + ] diff --git a/apps/pagoda/models.py b/apps/pagoda/models.py index 71a83623..11657953 100644 --- a/apps/pagoda/models.py +++ b/apps/pagoda/models.py @@ -1,3 +1,32 @@ from django.db import models +from django.conf import settings + # Create your models here. +class PagodaSites(models.Model): + owner = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + ) + name = models.CharField(max_length=50) + description = models.TextField(blank=True) + url = models.URLField() + verified = models.BooleanField(default=False) + siteUniqueIdentifier = models.TextField(unique=True) + verificationMethod = models.CharField( + max_length=4, choices=((x, x) for x in ["DNS", "Meta"]), default="DNS" + ) + verficationRecordName = models.CharField(max_length=50, blank=True) + verificationRecordValue = models.CharField(max_length=50, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.name + + def get_sites_created_by_user(user): + return PagodaSites.objects.filter(owner=user) + + class Meta: + verbose_name_plural = "Pagoda Sites" + ordering = ["-created_at"] diff --git a/apps/pagoda/urls.py b/apps/pagoda/urls.py new file mode 100644 index 00000000..1ca11d2d --- /dev/null +++ b/apps/pagoda/urls.py @@ -0,0 +1,11 @@ +from django.urls import path + +from . import views + +app_name = "pagoda" +urlpatterns = [ + path("", views.home, name="home"), + path("m/", views.site_dashboard, name="site"), + path("m//status", views.check_verification_status, name="site_status"), + path("m//delete", views.delete_site, name="delete_site"), +] diff --git a/apps/pagoda/views.py b/apps/pagoda/views.py index 91ea44a2..ad8f3079 100644 --- a/apps/pagoda/views.py +++ b/apps/pagoda/views.py @@ -1,3 +1,189 @@ from django.shortcuts import render +from thatcomputerscientist.utils import i18npatterns +from apps.pagoda.models import PagodaSites +from django.contrib.auth.decorators import login_required +from internal.pagoda_utilities import ( + pagoda_unique_site_id_generator, + pagoda_verification_record_generator, + pagoda_url_sanitizer, +) +from django.http import Http404, HttpResponseRedirect +from django.urls import reverse +from django.contrib import messages +import dns.resolver +import requests +from bs4 import BeautifulSoup + # Create your views here. +@login_required +def home(request): + META = { + "title": "The Pagoda Realm", + } + LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) + request.meta.update(META) + + pagoda_sites = PagodaSites.get_sites_created_by_user(request.user) + if request.method == "GET": + context = { + "pagoda_sites": pagoda_sites, + } + return render(request, f"{LANGUAGE_CODE}/pagoda/home.html", context) + elif request.method == "POST": + name = request.POST.get("site_name") + url = request.POST.get("root_url") + url = pagoda_url_sanitizer(url) + verificationMethod = request.POST.get("verification_method") + siteUniqueIdentifier = pagoda_unique_site_id_generator() + verficationRecordName, verificationRecordValue = ( + pagoda_verification_record_generator(verificationMethod) + ) + if not url: + messages.error( + request, + "The URL provided is not valid. Please provide a valid URL.", + extra_tags="pagodaError", + ) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) + + if url.replace("http://", "").replace("https://", "") in [ + site.url.replace("http://", "").replace("https://", "") + for site in pagoda_sites + ]: + messages.error( + request, + "This site can not be added to The Pagoda Realm at this moment. If you alredy have this site listed in The Pagoda Realm, please verify it or delete and re-add the website if you wish to change the verification method.", + extra_tags="pagodaError", + ) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) + + PagodaSites.objects.create( + owner=request.user, + name=name, + url=url, + siteUniqueIdentifier=siteUniqueIdentifier, + verificationMethod=verificationMethod, + verficationRecordName=verficationRecordName, + verificationRecordValue=verificationRecordValue, + ) + + return HttpResponseRedirect(reverse("pagoda:home")) + else: + pass + + return render(request, f"{LANGUAGE_CODE}/pagoda/home.html") + + +@login_required +def site_dashboard(request, site_id): + LANGUAGE_CODE = i18npatterns(request.LANGUAGE_CODE) + site = PagodaSites.objects.get(siteUniqueIdentifier=site_id, owner=request.user) + context = { + "site": site, + } + META = { + "title": f"Manage {site.name} — The Pagoda Realm", + } + request.meta.update(META) + if site.verified: + return render( + request, f"{LANGUAGE_CODE}/pagoda/site_verification.html", context + ) + elif not site.verified: + return render( + request, f"{LANGUAGE_CODE}/pagoda/site_verification.html", context + ) + else: + return Http404() + + +@login_required +def check_verification_status(request, site_id): + site = PagodaSites.objects.get(siteUniqueIdentifier=site_id, owner=request.user) + + if site.verified: + messages.success( + request, + "The site has been verified successfully.", + extra_tags="pagodaSuccess", + ) + return HttpResponseRedirect(reverse("pagoda:site", args=[site_id])) + + if site.verificationMethod == "DNS": + domain = site.url.replace("http://", "").replace("https://", "") + txt_name = site.verficationRecordName + txt_value = site.verificationRecordValue + domain = txt_name + "." + domain.split("/")[0] + + try: + txt_records = dns.resolver.resolve(domain, "TXT") + for txt_record in txt_records: + for txt_string in txt_record.strings: + if txt_string.decode("utf-8") == txt_value: + site.verified = True + site.save() + messages.success( + request, + "The site has been verified successfully.", + extra_tags="pagodaSuccess", + ) + return HttpResponseRedirect( + reverse("pagoda:site", args=[site_id]) + ) + except dns.resolver.NoAnswer: + pass + messages.error( + request, + "The site could not be verified. Please check the DNS records and try again.", + extra_tags="pagodaError", + ) + return HttpResponseRedirect(reverse("pagoda:site", args=[site_id])) + elif site.verificationMethod == "Meta": + domain = site.url + response = requests.get(domain) + soup = BeautifulSoup(response.text, "html.parser") + meta_tags = soup.find_all("meta") + for meta_tag in meta_tags: + if ( + meta_tag.get("name") == site.verficationRecordName + and meta_tag.get("content") == site.verificationRecordValue + ): + site.verified = True + site.save() + messages.success( + request, + "The site has been verified successfully.", + extra_tags="pagodaSuccess", + ) + return HttpResponseRedirect(reverse("pagoda:site", args=[site_id])) + messages.error( + request, + "The site could not be verified. Please check the Meta tags and try again.", + extra_tags="pagodaError", + ) + return HttpResponseRedirect(reverse("pagoda:site", args=[site_id])) + else: + pass + + return Http404() + + +@login_required +def delete_site(request, site_id): + site = PagodaSites.objects.get(siteUniqueIdentifier=site_id, owner=request.user) + if site: + site.delete() + messages.success( + request, + "The site has been deleted successfully.", + extra_tags="pagodaSuccess", + ) + return HttpResponseRedirect(reverse("pagoda:home")) + else: + messages.error( + request, + "The site could not be deleted. Please try again.", + extra_tags="pagodaError", + ) + return HttpResponseRedirect(reverse("pagoda:home")) diff --git a/internal/pagoda_utilities.py b/internal/pagoda_utilities.py new file mode 100644 index 00000000..37c5ca7e --- /dev/null +++ b/internal/pagoda_utilities.py @@ -0,0 +1,44 @@ +from apps.pagoda.models import PagodaSites +import random +import string +import re + + +def pagoda_unique_site_id_generator(): + site_id = "".join(random.choices(string.ascii_letters + string.digits, k=16)) + if PagodaSites.objects.filter(siteUniqueIdentifier=site_id).exists(): + return pagoda_unique_site_id_generator() + return site_id + + +def pagoda_verification_record_generator(record_type): + if record_type == "DNS": + txtName = "".join(random.choices(string.ascii_letters + string.digits, k=8)) + txtValue = "".join(random.choices(string.ascii_letters + string.digits, k=48)) + return txtName, txtValue + elif record_type == "Meta": + metaName = "".join(random.choices(string.ascii_letters + string.digits, k=8)) + metaValue = "".join(random.choices(string.ascii_letters + string.digits, k=48)) + return metaName, metaValue + else: + return None, None + + +def pagoda_url_sanitizer(url): + if not url.startswith("http://") and not url.startswith("https://"): + url = f"http://{url}" + + # Validate if the URL is valid + regex = re.compile( + r"^(?:http|ftp)s?://" + r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}|" + r"localhost|" + r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" + r"(?::\d+)?" + r"(?:/?|[/?]\S+)$", + re.IGNORECASE, + ) + + if not regex.match(url): + return None + return url diff --git a/middleware/userprofilemiddleware.py b/middleware/userprofilemiddleware.py index dc6c8bac..96a7997e 100644 --- a/middleware/userprofilemiddleware.py +++ b/middleware/userprofilemiddleware.py @@ -1,5 +1,6 @@ from django.utils.deprecation import MiddlewareMixin from services.users.models import UserProfile +from apps.blog.models import Post class UserProfileMiddleware(MiddlewareMixin): @@ -7,5 +8,8 @@ class UserProfileMiddleware(MiddlewareMixin): if request.user.is_authenticated: user_profile = UserProfile.objects.get(user=request.user) request.user.profile = user_profile + request.user.profile.weblogs_created = Post.objects.filter( + author=request.user + ).count() else: request.user.profile = None diff --git a/requirements.txt b/requirements.txt index d8b03c97..424177b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ django +dnspython django-redis python-dotenv gunicorn diff --git a/services/users/migrations/0013_userprofile_balance_userprofile_experience_and_more.py b/services/users/migrations/0013_userprofile_balance_userprofile_experience_and_more.py new file mode 100644 index 00000000..a7b2c565 --- /dev/null +++ b/services/users/migrations/0013_userprofile_balance_userprofile_experience_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.7 on 2024-12-16 02:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0012_alter_tokenstore_expires"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="balance", + field=models.IntegerField(default=500), + ), + migrations.AddField( + model_name="userprofile", + name="experience", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="userprofile", + name="level", + field=models.IntegerField(default=1), + ), + ] diff --git a/services/users/migrations/0014_userprofile_journal_streak_and_more.py b/services/users/migrations/0014_userprofile_journal_streak_and_more.py new file mode 100644 index 00000000..81a9c90b --- /dev/null +++ b/services/users/migrations/0014_userprofile_journal_streak_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.7 on 2024-12-16 02:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0013_userprofile_balance_userprofile_experience_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="journal_streak", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="userprofile", + name="journal_streak_best", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="userprofile", + name="journal_streak_reset", + field=models.BooleanField(default=False), + ), + ] diff --git a/services/users/models.py b/services/users/models.py index f11f2f46..31e7f291 100644 --- a/services/users/models.py +++ b/services/users/models.py @@ -14,7 +14,13 @@ class UserProfile(models.Model): is_public = models.BooleanField(default=False) email_public = models.BooleanField(default=False) email_verified = models.BooleanField(default=False) - blinkie_url = models.TextField(blank=True, default='') + blinkie_url = models.TextField(blank=True, default="") + experience = models.IntegerField(default=0) + level = models.IntegerField(default=1) + balance = models.IntegerField(default=500) + journal_streak = models.IntegerField(default=0) + journal_streak_reset = models.BooleanField(default=False) + journal_streak_best = models.IntegerField(default=0) def __str__(self): return self.user.username diff --git a/static/css/pagoda/pagoda.css b/static/css/pagoda/pagoda.css new file mode 100644 index 00000000..c8f3469a --- /dev/null +++ b/static/css/pagoda/pagoda.css @@ -0,0 +1,213 @@ +/* Main Container */ +.pagoda-realm { + background-color: rgba(134, 99, 229, 0.1); + border: 1px solid #8d8dff; + border-radius: 8px; + padding: 24px; + margin: 16px 0; + position: relative; + overflow: hidden; +} + +.pagoda-realm::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, #df23c4, transparent); + animation: scanline 2s linear infinite; +} + +/* Banner */ +.pagoda-banner { + width: 100%; + max-height: 200px; + object-fit: cover; + border-radius: 4px; + margin-bottom: 16px; +} + +/* Typography */ +.pagoda-realm h2 { + color: #8d8dff; + margin: 24px 0 16px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(141, 141, 255, 0.3); +} + +.pagoda-realm h3 { + color: #da73ff; + margin: 16px 0 12px; +} + +.pagoda-realm p { + margin: 12px 0; + line-height: 1.6; +} + +.pagoda-realm em { + color: #df23c4; + font-style: normal; + font-weight: 500; +} + +.pagoda-realm strong { + color: #da73ff; + font-weight: 600; +} + +/* Lists */ +.pagoda-realm ol, +.pagoda-realm ul { + margin: 16px 0; + padding-left: 24px; +} + +.pagoda-realm li { + margin: 8px 0; + line-height: 1.5; +} + +/* Table Styles */ +.pagoda-realm table { + width: 100%; + border-collapse: collapse; + margin: 16px 0; + background: rgba(0, 0, 0, 0.2); +} + +.pagoda-realm th, +.pagoda-realm td { + padding: 12px; + border: 1px solid rgba(141, 141, 255, 0.3); + text-align: left; +} + +.pagoda-realm th { + background: rgba(134, 99, 229, 0.2); + color: #8d8dff; + font-weight: 600; +} + +.pagoda-realm td { + background: rgba(0, 0, 0, 0.1); +} + +.pagoda-realm td a { + color: #8d8dff; + text-decoration: none; +} + +.pagoda-realm td a:hover { + color: #df23c4; + text-decoration: underline; +} + +/* Forms */ +.pagoda-dashboard-empty form { + background: rgba(98, 55, 149, 0.2); + padding: 16px; + border-radius: 4px; + margin: 16px 0; +} + +.pagoda-realm label { + display: block; + margin: 12px 0 4px; + color: #da73ff; + font-weight: 500; +} + +.pagoda-realm input[type="text"], +.pagoda-realm select { + width: 100%; + padding: 8px 12px; + background: rgba(0, 0, 0, 0.3); + border: 1px solid #8663e5; + border-radius: 4px; + color: #fff; + margin-bottom: 8px; +} + +.pagoda-realm input[type="text"]:focus, +.pagoda-realm select:focus { + border-color: #df23c4; + box-shadow: 0 0 8px rgba(223, 35, 196, 0.3); +} + +.pagoda-realm select { + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238d8dff' viewBox='0 0 16 16'%3E%3Cpath d='M8 10l4-4H4z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + padding-right: 36px; +} + +/* Note Text */ +.pagoda-realm .note { + font-size: 0.9em; + color: rgba(255, 255, 255, 0.7); + font-style: italic; + margin: 4px 0 12px; +} + +/* Buttons */ +.pagoda-realm button { + background: linear-gradient(45deg, #8663e5, #df23c4); + border: none; + padding: 12px 24px; + border-radius: 4px; + color: #fff; + font-weight: 500; + cursor: pointer; + margin: 8px 0; + transition: all 0.3s ease; +} + +.pagoda-realm button:hover { + transform: translateY(-2px); + box-shadow: 0 0 15px rgba(223, 35, 196, 0.5); +} + +/* Dashboard Sites Table */ +.pagoda-dashboard-sites table { + margin-bottom: 24px; +} + +.pagoda-dashboard-sites td { + text-align: left; +} + +/* Error Messages */ +.pagoda-realm [class*="Error"] { + color: #ff9a9a; + background: rgba(255, 0, 0, 0.1); + padding: 8px 12px; + border-radius: 4px; + margin: 8px 0; +} + +/* Animation */ +@keyframes scanline { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(100%); + } +} + +/* Responsive Adjustments */ +@media (max-width: 1200px) { + .pagoda-realm { + margin: 16px; + } + + .pagoda-realm table { + display: block; + overflow-x: auto; + } +} \ No newline at end of file diff --git a/static/css/shared/core.css b/static/css/shared/core.css index 94e9e2e1..ac629b03 100644 --- a/static/css/shared/core.css +++ b/static/css/shared/core.css @@ -63,6 +63,38 @@ a:active { color: #df23c4; } +button { + background: #4444b1; + color: #fff; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + outline: none; + font-weight: bold; +} + +button:hover { + background: #df23c4; +} + +button:active { + background: #8d8dff; +} + +button:disabled { + background: #8d8dff; + color: #ccc; + cursor: not-allowed; +} + +button:disabled:hover { + background: #8d8dff; +} + +button:focus { + outline: none; +} + a:hover { text-decoration: underline; } diff --git a/static/images/pagoda/pagoda_banner.png b/static/images/pagoda/pagoda_banner.png new file mode 100644 index 00000000..5d3b11a3 Binary files /dev/null and b/static/images/pagoda/pagoda_banner.png differ diff --git a/templates/en/pagoda/home.html b/templates/en/pagoda/home.html new file mode 100644 index 00000000..d5910d11 --- /dev/null +++ b/templates/en/pagoda/home.html @@ -0,0 +1,213 @@ +{% extends 'shared/base.html' %} +{% load static %} +{% block head %} + +{% endblock head %} +{% block content %} +
+{% if not request.COOKIES.visitedPagodaRealm %} + The Pagoda Realm Banner +
+
+

Welcome to The Pagoda Realm, {{ user.first_name }}!

+

What is The Pagoda Realm, you might ask? If you recall, it was a service in the list of services that I promised to + provide access you to when you signed up for an account. But in reality, I lied to you. The Pagoda Realm is not a + single service, but a collection of services that I have carefully designed to help you in with your + Neocities and other retro static site hosting projects like Nekoweb. The web is a canvas, and you + are the artist, limited only by your imagination. The Pagoda Realm is a place where you can integrate various tools + on your own website, which would have been otherwise hard to do. +

+

All services are built to be very simple to use and beginner-friendly. A lot of these services provide data in JSON + and as well as prerendered iframes, so you can use them in your own website without having to worry about the + technical details. The components are designed to be fashionable and usually support variety of themes and + custom CSS to match your website's design. +

+

How do I use the services in The Pagoda Realm?

+

Using the services in The Pagoda Realm is very simple. If you are looking at this page, that means that you have already + cleared the first step, which is to sign up for an account. Once you have signed up for an account, you need to connect + your website to The Pagoda Realm. This can be done in two ways:

+
    +
  1. Using DNS Verification - This is the recommended method. You can add a TXT record to your website's + DNS settings to verify that you own the website. Once the DNS record is added, you can connect your website to The + Pagoda Realm.
  2. +
  3. Using Meta Tag Verification - This is the alternative method if you are using a host like Neocities + or Nekoweb and do not have access to the DNS settings. You can add a meta tag to your website's HTML to verify that + you own the website. Once the meta tag is added, you can connect your website to The Pagoda Realm.
  4. +
+

Once your website is connected to The Pagoda Realm, you can start using the services. You can create a journal, write a + weblog post, add comments to your weblog post, join the webring, add entries to the guestbook, show ads on your website, + and view the number of visitors that have visited your website. You can also embed the services on your website using + the JSON API or the prerendered iframe.

+

What services are available in The Pagoda Realm?

+

Here are some of the services that are available in The Pagoda Realm:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ServiceDescriptionEmbeddable asAPI
JournalsA daily diary like journal that you can use to write about your day, thoughts, and feelings.JSON, iframeYes
WeblogsA simple weblog service that you can use to write articles, stories, and blog posts.JSON, iframeYes
Weblog CommentsA simple comment system that you can use to add comments to your weblog posts.JSON, iframeYes
CaravanA webring service that binds all the websites registered on The Pagoda Realm together.iframeNo
LedgerLedger is a guestbook service that you can use to receive feedback and comments from your website visitors.JSON, iframeYes
PamphletPamphlet is a ad-simulation service which shows random fake period specific ads on your website.JavaScriptNo
CensusCensus is a service that shows the number of visitors that have visited your website.iframeYes
+
    +
  • Journals - A daily diary like journal that you can use to write about your day, thoughts, and + feelings. Journals are both private and public, and you can create multiple journals for different purposes. + Public journals can be embedded on your website. You will be able to view your journal entries in a calendar + view, and you can also export your journal entries in JSON format. Furthermore, you can also use the journal + entries in your own website using the JSON API.
  • +
  • Weblogs - A simple weblog service that you can use to write articles, stories, and blog posts. + Weblogs are always public by default (unless in draft mode), and you can only have one weblog per account. You + can categorize your posts, tag them, and edit the post in full HTML mode and even change CSS on per post basis. + You can also embed your weblog posts on your website using the JSON API or the prerendered iframe.
  • +
  • Weblog Comments - A simple comment system that you can use to add comments to your weblog posts. + You can customize the comment system to match your website's design, and you can also moderate the comments + before they are published. The comment system is also available as a JSON API and prerendered iframe.
  • +
  • Caravan - A webring service that binds all the websites registered on The Pagoda Realm together. + You automatically become a member of the webring when you link your website to the Pagoda Realm. You can also + upload custom banner image for your website preview, embed the badge on your website, and view the list of + websites in the webring. The webring is also available as a prerendered iframe.
  • +
  • Ledger - Ledger is a guestbook service that you can use to receive feedback and comments from + your website visitors. You can customize the guestbook to match your website's design, and you can moderate the + entries before they are published. The guestbook is also available as a JSON API and prerendered iframe.
  • +
  • Pamphlet - Pamphlet is a ad-simulation service which shows random fake period specific ads on your + website. Pamphlet can show banner ads and square ads. It can also show 88 x 31 pixel buttons. It is available as a + JavaScript library which you need to initialize on your website.
  • +
  • Census - Census is a service that shows the number of visitors that have visited your website. It + can show the number of visitors in the last 24 hours, the last 7 days, and the total number of visitors. Census is + available as a prerendered iframe and as a JSON API.
  • +
+

These are just some of the services that are available in The Pagoda Realm. I will be adding more services in the + future, so stay tuned for more updates. If you have any questions or feedback, please feel free to contact me. I hope + you enjoy using The Pagoda Realm!

+

Thank you for using The Pagoda Realm!

+ +
+{% else %} + {% if not pagoda_sites %} +
+

Welcome to The Pagoda Realm, {{ user.first_name }}!

+

It looks like you have not connected any websites to The Pagoda Realm yet. To get started, you need to connect your + website to The Pagoda Realm. This can be done in two ways:

+
    +
  1. Using DNS Verification - This is the recommended method. You can add a TXT record to your website's + DNS settings to verify that you own the website. Once the DNS record is added, you can connect your website to The + Pagoda Realm.
  2. +
  3. Using Meta Tag Verification - This is the alternative method if you are using a host like Neocities + or Nekoweb and do not have access to the DNS settings. You can add a meta tag to your website's HTML to verify that + you own the website. Once the meta tag is added, you can connect your website to The Pagoda Realm.
  4. +
+

Connect Your Website to The Pagoda Realm

+
+ {% csrf_token %} + + + + +

Note: The root URL should be the URL of your website's homepage. It can also be your custom domain + if you have one.

+ + + +
+
+ {% else %} +
+

Your Connected Websites

+ + + + + + + + + + + {% for site in pagoda_sites %} + + + + + + + {% endfor %} + +
Site NameRoot URLVerifiedActions
{{ site.name }}{{ site.url }}{% if site.verified %}✅{% else %}❌{% endif %} + Manage Site +
+
+

Connect Another Website to The Pagoda Realm

+
+ {% csrf_token %} + + + + +

Note: The root URL should be the URL of your website's homepage. It can also be your custom domain + if you have one.

+ + + +
+ {% for message in messages %} + {% if 'pagodaError' in message.tags %} +

{{ message }}

+ {% endif %} + {% endfor %} +
+
+ {% endif %} +{% endif %} +
+{% endblock content %} \ No newline at end of file diff --git a/templates/en/pagoda/site_verification.html b/templates/en/pagoda/site_verification.html new file mode 100644 index 00000000..9d5e1aad --- /dev/null +++ b/templates/en/pagoda/site_verification.html @@ -0,0 +1,165 @@ +{% extends 'shared/base.html' %} +{% load static %} +{% block head %} + +{% endblock head %} +{% block content %} +
+

Verification Details for {{ site.name }}

+

Here are the details you need to verify your site with The Pagoda Realm:

+

Verification Method: {% if site.verificationMethod == 'DNS' %}DNS{% else %}Meta Tag{% endif %}

+
+ {% if site.verificationMethod == 'DNS' %} +

TXT Record Name: {{ site.verficationRecordName }}

+

TXT Record Value: {{ site.verificationRecordValue }}

+ {% else %} +

Meta Tag:<meta name="{{ site.verficationRecordName }}" content="{{ site.verificationRecordValue }}">

+ {% endif %} +
+

+ Verification Status: + + {% if site.verified %}Verified{% else %}Not Verified{% endif %} + +

+ +

Verification Instructions

+

To verify your site, you need to add the following details to your site's code:

+ {% if site.verificationMethod == 'DNS' %} +

Log in to your domain registrar's control panel and add a new TXT record with the following details:

+

Record Name: {{ site.verficationRecordName }}

+

Record Value: {{ site.verificationRecordValue }}

+ {% else %} +

Open your site's HTML code and add the following meta tag to the <head> section:

+

<meta name="{{ site.verficationRecordName }}" content="{{ site.verificationRecordValue }}">

+ {% endif %} +

Once you've added the details, click the button below to check the verification status.

+ + + +
+{% endblock %} diff --git a/templates/ja/pagoda/home.html b/templates/ja/pagoda/home.html new file mode 100644 index 00000000..1a0f2414 --- /dev/null +++ b/templates/ja/pagoda/home.html @@ -0,0 +1,3 @@ +{% extends 'shared/base.html' %} +{% load static %} +Welcome to the \ No newline at end of file diff --git a/templates/shared/left_sidebar.html b/templates/shared/left_sidebar.html index 1603f5f1..5815b373 100644 --- a/templates/shared/left_sidebar.html +++ b/templates/shared/left_sidebar.html @@ -19,7 +19,7 @@ - + @@ -38,7 +38,7 @@
My Page Icon @@ -59,7 +59,7 @@