diff --git a/Blogger/Blogger/__init__.py b/Blogger/Blogger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Blogger/Blogger/asgi.py b/Blogger/Blogger/asgi.py new file mode 100644 index 0000000..52ba7b4 --- /dev/null +++ b/Blogger/Blogger/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for Blogger project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Blogger.settings') + +application = get_asgi_application() diff --git a/Blogger/Blogger/settings.py b/Blogger/Blogger/settings.py new file mode 100644 index 0000000..0d648f4 --- /dev/null +++ b/Blogger/Blogger/settings.py @@ -0,0 +1,123 @@ +""" +Django settings for Blogger project. + +Generated by 'django-admin startproject' using Django 6.0.4. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/6.0/ref/settings/ +""" + +from pathlib import Path +import os + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-47shw72dbedi^x$jw5)-91cv$udm#i(47nlf#ucqbqa^_aqi&m' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'main', + 'posts', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'Blogger.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'Blogger.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/6.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/6.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/6.0/howto/static-files/ + +STATIC_URL = 'static/' + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') \ No newline at end of file diff --git a/Blogger/Blogger/urls.py b/Blogger/Blogger/urls.py new file mode 100644 index 0000000..b03ed63 --- /dev/null +++ b/Blogger/Blogger/urls.py @@ -0,0 +1,28 @@ +""" +URL configuration for Blogger project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/6.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path,include +from django.conf import settings +from django.conf.urls.static import static +from . import settings + + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include("main.urls")), + path('posts/', include('posts.urls')), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/Blogger/Blogger/wsgi.py b/Blogger/Blogger/wsgi.py new file mode 100644 index 0000000..03bf872 --- /dev/null +++ b/Blogger/Blogger/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for Blogger project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Blogger.settings') + +application = get_wsgi_application() diff --git a/Blogger/main/__init__.py b/Blogger/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Blogger/main/admin.py b/Blogger/main/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Blogger/main/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Blogger/main/apps.py b/Blogger/main/apps.py new file mode 100644 index 0000000..833bff6 --- /dev/null +++ b/Blogger/main/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MainConfig(AppConfig): + name = 'main' diff --git a/Blogger/main/migrations/0001_initial.py b/Blogger/main/migrations/0001_initial.py new file mode 100644 index 0000000..6ad0b99 --- /dev/null +++ b/Blogger/main/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 6.0.4 on 2026-04-14 15:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Post', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=2048)), + ('content', models.TextField()), + ('is_published', models.BooleanField(default=True)), + ('published_at', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/Blogger/main/migrations/0002_delete_post.py b/Blogger/main/migrations/0002_delete_post.py new file mode 100644 index 0000000..d14a077 --- /dev/null +++ b/Blogger/main/migrations/0002_delete_post.py @@ -0,0 +1,16 @@ +# Generated by Django 6.0.4 on 2026-04-15 07:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0001_initial'), + ] + + operations = [ + migrations.DeleteModel( + name='Post', + ), + ] diff --git a/Blogger/main/migrations/__init__.py b/Blogger/main/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Blogger/main/models.py b/Blogger/main/models.py new file mode 100644 index 0000000..e69de29 diff --git a/Blogger/main/static/css/style.css b/Blogger/main/static/css/style.css new file mode 100644 index 0000000..b327b20 --- /dev/null +++ b/Blogger/main/static/css/style.css @@ -0,0 +1,624 @@ + + +*{ + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg-color: #F2EDC2; + --nav-color: #346739; + --span-color: #79AE6F; + --title-color: #346739; + --text-color: #4a4a4a; + --btn-color: #79AE6F; + --txtbox-color: #ffffff; + --font-title: 'Playfair Display', serif; + --font-para: 'Montserrat', sans-serif; +} + + +.container { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + flex-direction: column; +} +a{ + text-decoration: none; +} +/* nav styling start*/ + +.header__hero { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + background-color: var(--bg-color); + border-bottom: 1px solid var(--span-color); + width: 100%; + height: 15vh; +} + +.header__title { + color: var(--title-color); + font-family: var(--font-title); + margin: 0; +} + +.header__tagline { + color: var(--text-color); + font-family: var(--font-para); + margin-top: 5px; +} + + +.nav-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 5%; + background-color: var(--nav-color); +} + +.nav_logo img { + height: 70px; +} + +.nav_link { + display: flex; + list-style: none; + gap: 2rem; + margin: 0; + padding: 0; + font-family: var(--font-para); +} + +.nav__item a { + text-decoration: none; + color: var(--bg-color); + font-family: var(--font-para); + font-weight: 500; + transition: 0.3s; +} + +.nav__item a:hover { + color: var(--span-color); +} + +/* home styling start*/ +.bg-img { + width: 100%; + height: 600px; + overflow: hidden; +} + +.bg-img img { + width: 100%; + height: 100%; + object-fit: cover; +} + +#explore { + padding: 50px 5%; + background-color: var(--bg-color); + width: 100%; + min-height: 100vh; +} + +.explore__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; +} + +.explore__title { + color: var(--title-color); + font-family: var(--font-title); +} + +.posts-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 25px; + align-items: stretch; +} + +.post-card { + background-color: #faf9f0; + padding: 25px; + border-radius: 15px; + border: 1px solid rgba(121, 174, 111, 0.3); + box-shadow: 0 4px 15px rgba(52, 103, 57, 0.05); + display: flex; + flex-direction: column; +} + +.post-card__title { + color: var(--title-color); + font-family: var(--font-title); + margin-bottom: 10px; +} + +.post-card__excerpt { + color: var(--text-color); + font-family: var(--font-para); + line-height: 1.6; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + margin-bottom: auto; +} + +.btn-create { + background-color: var(--title-color); + color: var(--bg-color); + padding: 12px 24px; + text-decoration: none; + border-radius: 30px; + font-weight: bold; + transition: 0.3s; +} + +.btn-read-more { + color: var(--title-color); + font-weight: bold; + text-decoration: none; + font-size: 0.9rem; + border-bottom: 2px solid var(--span-color); +} + +/* footer styling start */ + +.main-footer { + background-color: var(--title-color); + color: var(--bg-color); + padding: 60px 5% 20px; + font-family: var(--font-para); +} + +.footer-content { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 40px; + margin-bottom: 40px; +} + +.footer-section { + flex: 1; + min-width: 250px; +} + +.footer-section h3 { + font-family: var(--font-title); + color: var(--span-color); + margin-bottom: 20px; +} + +.footer-section ul { + list-style: none; + padding: 0; +} + +.footer-section ul li { + margin-bottom: 10px; +} + +.footer-section ul li a { + color: var(--bg-color); + text-decoration: none; + transition: 0.3s; +} + +.footer-section ul li a:hover { + color: var(--span-color); +} + +.footer-bottom { + text-align: center; + border-top: 1px solid rgba(242, 237, 194, 0.2); + padding-top: 20px; + font-size: 0.9rem; + opacity: 0.8; +} + +.social-icons a { + color: var(--span-color); + font-size: 1.8rem; + text-decoration: none; + transition: 0.3s ease; + display: inline-block; +} + +.social-icons a:hover { + color: #faf9f0; + transform: translateY(-5px); +} + +.create-container { + padding: 60px 5%; + display: flex; + justify-content: center; + background-color: var(--bg-color); + min-height: 80vh; +} + +.form-card { + background: #faf9f0; + width: 100%; + max-width: 700px; + padding: 40px; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(52, 103, 57, 0.08); +} + +.form-header { + margin-bottom: 30px; + text-align: center; +} + +.form-header h2 { + color: var(--title-color); + font-family: var(--font-title); + font-size: 2rem; +} + +.input-group { + margin-bottom: 20px; +} + +.input-group label { + display: block; + margin-bottom: 8px; + color: var(--title-color); + font-weight: 600; +} + +.input-group input, +.input-group textarea { + width: 100%; + padding: 12px 15px; + border: 1px solid rgba(121, 174, 111, 0.3); + border-radius: 10px; + background-color: white; + font-family: var(--font-para); + transition: 0.3s; +} + +.input-group input:focus, +.input-group textarea:focus { + outline: none; + border-color: var(--span-color); + box-shadow: 0 0 0 3px rgba(121, 174, 111, 0.1); +} + +.switch-group { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 30px; +} + +.form-actions { + display: flex; + justify-content: flex-end; + gap: 15px; + border-top: 1px solid rgba(0,0,0,0.05); + padding-top: 25px; +} + +.btn-cancel { + text-decoration: none; + color: #888; + padding: 12px 25px; + transition: 0.3s; +} + +.btn-submit { + background-color: var(--title-color); + color: var(--bg-color); + border: none; + padding: 12px 35px; + border-radius: 30px; + font-weight: bold; + cursor: pointer; + transition: 0.3s; +} + +.btn-submit:hover { + background-color: var(--span-color); + transform: translateY(-2px); +} + + +@media (max-width: 992px) { + .nav-bar { + padding: 15px 3%; + } + + .bg-img { + height: 400px; + } +} + +@media (max-width: 768px) { + .nav-bar { + flex-direction: column; + gap: 15px; + text-align: center; + } + + .nav_link { + gap: 1.2rem; + flex-wrap: wrap; + justify-content: center; + } + + .header__hero { + height: auto; + padding: 20px 0; + } + + .header__title { + font-size: 1.8rem; + } + + .explore__header { + flex-direction: column; + gap: 20px; + text-align: center; + } + + .posts-grid { + grid-template-columns: 1fr; + } + + .footer-content { + flex-direction: column; + text-align: center; + } + + .footer-section { + min-width: 100%; + } + + .form-card { + padding: 25px 20px; + } + + .form-actions { + flex-direction: column-reverse; + gap: 10px; + } + + .btn-submit, .btn-cancel { + width: 100%; + text-align: center; + } +} + +img { + max-width: 100%; + height: auto; +} + +.post-details { + background-color: var(--bg-color); + min-height: 100vh; + padding: 4rem 20px; + display: flex; + justify-content: center; +} + +article { + max-width: 800px; + width: 100%; +} + +h1 { + font-family: var(--font-title); + color: var(--title-color); + font-size: 3.5rem; + line-height: 1.1; + margin-bottom: 1.5rem; +} + +.meta-info { + font-family: var(--font-para); + color: var(--text-color); + font-size: 0.9rem; + margin-bottom: 3rem; + display: flex; + gap: 15px; + align-items: center; +} + +.status-badge { + background-color: var(--span-color); + color: white; + padding: 2px 12px; + border-radius: 20px; + font-size: 0.75rem; +} + +.featured-image { + width: 100%; + margin-bottom: 3rem; +} + +.featured-image img { + width: 100%; + border-radius: 8px; + box-shadow: 0 10px 30px rgba(52, 103, 57, 0.1); +} + +.content-area { + font-family: var(--font-para); + color: var(--text-color); + line-height: 1.8; + font-size: 1.15rem; +} + +.back-btn { + display: inline-block; + margin-top: 4rem; + padding: 10px 25px; + background-color: var(--btn-color); + color: white; + text-decoration: none; + border-radius: 4px; + font-family: var(--font-para); + transition: 0.3s; +} + +.back-btn:hover { + filter: brightness(0.9); + transform: translateX(-5px); +} + +.btn-edit { + display: inline-block; + padding: 10px 20px; + background-color: transparent; + color: var(--title-color); + border: 1px solid var(--title-color); + text-decoration: none; + border-radius: 4px; + font-family: var(--font-para); + font-weight: 600; + transition: 0.3s; +} + +.btn-edit:hover { + background-color: var(--title-color); + color: var(--bg-color); + transform: translateY(-2px); +} + +.current-poster-preview { + margin-bottom: 1.5rem; + padding: 12px; + border: 1px solid var(--span-color); + border-radius: 8px; + background-color: #ffffff; + display: inline-block; +} + +.current-poster-preview p { + font-family: var(--font-para); + font-size: 0.85rem; + color: var(--title-color); + margin-bottom: 8px; + font-weight: 600; +} + +.current-poster-preview img { + max-width: 180px; + height: auto; + border-radius: 4px; + box-shadow: 0 4px 10px rgba(0,0,0,0.1); + display: block; +} + +.current-poster-preview { + margin-top: 2rem; + margin-bottom: 1.5rem; + padding: 12px; + border: 1px solid var(--span-color); + border-radius: 8px; + background-color: #ffffff; + display: inline-block; +} + +.current-poster-preview img { + max-width: 180px; + height: auto; + border-radius: 4px; + display: block; +} + +#poster { + margin-top: 5px; + font-family: var(--font-para); +} + +.post-actions { + display: flex; + gap: 15px; + margin-bottom: 2.5rem; +} + +.btn-delete { + display: inline-block; + padding: 8px 18px; + background-color: transparent; + color: #c0392b; + border: 1px solid #c0392b; + text-decoration: none; + border-radius: 4px; + font-family: var(--font-para); + font-size: 0.9rem; + font-weight: 600; + transition: 0.3s; +} + +.btn-delete:hover { + background-color: #c0392b; + color: white; + transform: translateY(-2px); +} + +.post-card__image { + width: 100%; + height: 200px; + overflow: hidden; + border-radius: 10px; + margin-bottom: 15px; +} + +.post-card__image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +@media (max-width: 768px) { + h1 { + font-size: 2.2rem; + margin-bottom: 1rem; + } + + .post-actions { + flex-direction: column; + gap: 10px; + width: 100%; + } + + .btn-edit, .btn-delete { + width: 100%; + text-align: center; + padding: 12px; + } + + .current-poster-preview { + display: block; + width: 100%; + text-align: center; + } + + .current-poster-preview img { + max-width: 100%; + margin: 0 auto; + } + + .post-details { + padding: 2rem 15px; + } +} + diff --git a/Blogger/main/static/imges/home-img/home-bg.jpeg b/Blogger/main/static/imges/home-img/home-bg.jpeg new file mode 100644 index 0000000..e0a6888 Binary files /dev/null and b/Blogger/main/static/imges/home-img/home-bg.jpeg differ diff --git a/Blogger/main/static/imges/home-img/home-bg1.jpeg b/Blogger/main/static/imges/home-img/home-bg1.jpeg new file mode 100644 index 0000000..150ad46 Binary files /dev/null and b/Blogger/main/static/imges/home-img/home-bg1.jpeg differ diff --git a/Blogger/main/static/imges/home-img/home-bg2.jpeg b/Blogger/main/static/imges/home-img/home-bg2.jpeg new file mode 100644 index 0000000..3331a2c Binary files /dev/null and b/Blogger/main/static/imges/home-img/home-bg2.jpeg differ diff --git a/Blogger/main/static/imges/home-img/home-bg3.jpeg b/Blogger/main/static/imges/home-img/home-bg3.jpeg new file mode 100644 index 0000000..7435eb6 Binary files /dev/null and b/Blogger/main/static/imges/home-img/home-bg3.jpeg differ diff --git a/Blogger/main/static/imges/home-img/logo.png b/Blogger/main/static/imges/home-img/logo.png new file mode 100644 index 0000000..0221137 Binary files /dev/null and b/Blogger/main/static/imges/home-img/logo.png differ diff --git a/Blogger/main/static/imges/home-img/logo1.png b/Blogger/main/static/imges/home-img/logo1.png new file mode 100644 index 0000000..c26ecd6 Binary files /dev/null and b/Blogger/main/static/imges/home-img/logo1.png differ diff --git a/Blogger/main/templates/main/base.html b/Blogger/main/templates/main/base.html new file mode 100644 index 0000000..78cc6d6 --- /dev/null +++ b/Blogger/main/templates/main/base.html @@ -0,0 +1,70 @@ +{% load static %} + + + + + + {% block title %} ROAM {% endblock %} + + + + + + + +
+
+

ROAM

+

Stories from the road, captured in moments.

+
+ + +
+ + {% block content %} + {% endblock %} + + + + + \ No newline at end of file diff --git a/Blogger/main/templates/main/home.html b/Blogger/main/templates/main/home.html new file mode 100644 index 0000000..f47bd4c --- /dev/null +++ b/Blogger/main/templates/main/home.html @@ -0,0 +1,59 @@ +{% extends 'main/base.html' %} +{% load static %} + +{% block title %} Home - ROAM Travel Blog {% endblock %} + +{% block content %} +
+
+
+ Travel Background +
+
+ +
+
+
+

Latest Posts

+

Welcome to Blogger, explore the latest stories and thoughts.

+
+ +
+ +
+ +
+ {% for post in posts %} +
+
+
+ Post Image + +

{{ post.title }}

+
+ + {% if post.is_published %}Published{% else %}Draft{% endif %} + +
+ + + + +
+ +
+ {% empty %} +
+

No Posts Yet!

+

Be the first to share your thoughts on the blog.

+ Add First Post +
+ {% endfor %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/Blogger/main/tests.py b/Blogger/main/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Blogger/main/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Blogger/main/urls.py b/Blogger/main/urls.py new file mode 100644 index 0000000..7eebf75 --- /dev/null +++ b/Blogger/main/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from . import views + +app_name = "main" + +urlpatterns = [ + path("", views.home_view, name="home_view"), +] \ No newline at end of file diff --git a/Blogger/main/views.py b/Blogger/main/views.py new file mode 100644 index 0000000..d6577eb --- /dev/null +++ b/Blogger/main/views.py @@ -0,0 +1,7 @@ +from django.shortcuts import render +from django.http import HttpRequest, HttpResponse +from posts.models import Post + +def home_view(request: HttpRequest): + posts = Post.objects.all() + return render(request, "main/home.html", {"posts": posts}) \ No newline at end of file diff --git a/Blogger/manage.py b/Blogger/manage.py new file mode 100644 index 0000000..c7803d0 --- /dev/null +++ b/Blogger/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Blogger.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/Blogger/media/images/aluli.jpeg b/Blogger/media/images/aluli.jpeg new file mode 100644 index 0000000..4f5a980 Binary files /dev/null and b/Blogger/media/images/aluli.jpeg differ diff --git a/Blogger/media/images/default.jpeg b/Blogger/media/images/default.jpeg new file mode 100644 index 0000000..92a8425 Binary files /dev/null and b/Blogger/media/images/default.jpeg differ diff --git a/Blogger/media/images/paris.jpg b/Blogger/media/images/paris.jpg new file mode 100644 index 0000000..e7c5f7d Binary files /dev/null and b/Blogger/media/images/paris.jpg differ diff --git a/Blogger/media/images/paris_X9upC6h.jpg b/Blogger/media/images/paris_X9upC6h.jpg new file mode 100644 index 0000000..e7c5f7d Binary files /dev/null and b/Blogger/media/images/paris_X9upC6h.jpg differ diff --git a/Blogger/media/images/rome.jpg b/Blogger/media/images/rome.jpg new file mode 100644 index 0000000..fd96f58 Binary files /dev/null and b/Blogger/media/images/rome.jpg differ diff --git a/Blogger/media/images/rome1.jpg b/Blogger/media/images/rome1.jpg new file mode 100644 index 0000000..d213d4e Binary files /dev/null and b/Blogger/media/images/rome1.jpg differ diff --git a/Blogger/posts/__init__.py b/Blogger/posts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Blogger/posts/admin.py b/Blogger/posts/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Blogger/posts/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Blogger/posts/apps.py b/Blogger/posts/apps.py new file mode 100644 index 0000000..2c2b982 --- /dev/null +++ b/Blogger/posts/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PostsConfig(AppConfig): + name = 'posts' diff --git a/Blogger/posts/migrations/0001_initial.py b/Blogger/posts/migrations/0001_initial.py new file mode 100644 index 0000000..b41f004 --- /dev/null +++ b/Blogger/posts/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 6.0.4 on 2026-04-15 07:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Post', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=2048)), + ('content', models.TextField()), + ('is_published', models.BooleanField(default=True)), + ('published_at', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/Blogger/posts/migrations/0002_post_poster.py b/Blogger/posts/migrations/0002_post_poster.py new file mode 100644 index 0000000..882a184 --- /dev/null +++ b/Blogger/posts/migrations/0002_post_poster.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.4 on 2026-04-15 12:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='poster', + field=models.ImageField(default='images/default.jpg', upload_to='images/'), + ), + ] diff --git a/Blogger/posts/migrations/0003_alter_post_poster.py b/Blogger/posts/migrations/0003_alter_post_poster.py new file mode 100644 index 0000000..befc9ae --- /dev/null +++ b/Blogger/posts/migrations/0003_alter_post_poster.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.4 on 2026-04-15 16:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0002_post_poster'), + ] + + operations = [ + migrations.AlterField( + model_name='post', + name='poster', + field=models.ImageField(default='images/default.jpeg', upload_to='images/'), + ), + ] diff --git a/Blogger/posts/migrations/__init__.py b/Blogger/posts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Blogger/posts/models.py b/Blogger/posts/models.py new file mode 100644 index 0000000..01cb41d --- /dev/null +++ b/Blogger/posts/models.py @@ -0,0 +1,9 @@ +from django.db import models + +# Create your models here. +class Post(models.Model): + title = models.CharField(max_length=2048) + content = models.TextField() + is_published = models.BooleanField(default=True) + published_at = models.DateTimeField(auto_now_add=True) + poster = models.ImageField(upload_to="images/", default="images/default.jpeg") diff --git a/Blogger/posts/templates/posts/create_post.html b/Blogger/posts/templates/posts/create_post.html new file mode 100644 index 0000000..a008ee1 --- /dev/null +++ b/Blogger/posts/templates/posts/create_post.html @@ -0,0 +1,46 @@ +{% extends 'main/base.html' %} +{% load static %} +{% block title %} Create New Post | ROAM {% endblock %} + +{% block content %} +
+
+
+

Add New Post

+

Share your latest journey with the world.

+
+ +
+ {% csrf_token %} + + +
+ + +
+ +
+ + + Optional: Choose a high-quality visual for your post. +
+ + +
+ + +
+ +
+ + +
+ +
+ Cancel + +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/Blogger/posts/templates/posts/post_details.html b/Blogger/posts/templates/posts/post_details.html new file mode 100644 index 0000000..ac99ca1 --- /dev/null +++ b/Blogger/posts/templates/posts/post_details.html @@ -0,0 +1,36 @@ +{% extends 'main/base.html' %} +{% load static %} +{% block title %} Create New Post | ROAM {% endblock %} + +{% block content %} +
+
+
+

{{ post.title }}

+
+ + {% if post.is_published %}Published{% else %}Draft{% endif %} +
+
+ + {% if post.poster %} + + {% endif %} + +
+ Edit Post + Delete Post +
+ +
+ {{ post.content|linebreaks }} +
+ + +
+
+{% endblock %} diff --git a/Blogger/posts/templates/posts/post_update.html b/Blogger/posts/templates/posts/post_update.html new file mode 100644 index 0000000..fe0dc3e --- /dev/null +++ b/Blogger/posts/templates/posts/post_update.html @@ -0,0 +1,52 @@ +{% extends 'main/base.html' %} +{% load static %} +{% block title %} Update Post | ROAM {% endblock %} + +{% block content %} +
+
+
+

Update Post

+

Modify your journey details below.

+
+ +
+ {% csrf_token %} + +
+ + +
+ +
+ + + {% if post.poster %} +
+

Current Image:

+ Current Poster +
+ {% endif %} + + + Select a new image if you want to change the current one. +
+ +
+ + +
+ +
+ + +
+ +
+ Cancel + +
+
+
+
+{% endblock %} diff --git a/Blogger/posts/tests.py b/Blogger/posts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Blogger/posts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Blogger/posts/urls.py b/Blogger/posts/urls.py new file mode 100644 index 0000000..13c5c91 --- /dev/null +++ b/Blogger/posts/urls.py @@ -0,0 +1,13 @@ +from django.urls import path +from . import views + +app_name = "posts" + +urlpatterns = [ + path("create/", views.create_post_view, name="create_post_view"), + path("details//", views.post_details_view, name="post_details_view"), + path("update//", views.post_update_view, name="post_update_view"), + path("delete//", views.post_delete_view, name="post_delete_view"), + + +] \ No newline at end of file diff --git a/Blogger/posts/views.py b/Blogger/posts/views.py new file mode 100644 index 0000000..7020717 --- /dev/null +++ b/Blogger/posts/views.py @@ -0,0 +1,46 @@ +from django.shortcuts import render , redirect +from django.http import HttpRequest, HttpResponse +from posts.models import Post + +# Create your views here. +def create_post_view(request): + if request.method == "POST": + new_post = Post( + title = request.POST["title"], + content = request.POST["content"], + is_published = "is_published" in request.POST, + poster = request.FILES.get("poster") + ) + new_post.save() + return redirect("main:home_view") + + return render(request, "posts/create_post.html") + +def post_details_view(request: HttpRequest, post_id: int): + post = Post.objects.get(pk=post_id) + + return render(request, "posts/post_details.html", {"post": post}) + +def post_update_view(request: HttpRequest, post_id: int): + post = Post.objects.get(pk=post_id) + + if request.method == "POST": + post.title = request.POST["title"] + post.content = request.POST["content"] + post.is_published = "is_published" in request.POST + + if "poster" in request.FILES: + post.poster = request.FILES["poster"] + + post.save() + + return redirect("posts:post_details_view", post_id=post.id) + + return render(request, "posts/post_update.html", {"post": post}) + + +def post_delete_view(request: HttpRequest, post_id: int): + post = Post.objects.get(pk=post_id) + post.delete() + + return redirect("main:home_view") \ No newline at end of file