├── .github └── workflows │ ├── django.yml │ ├── dockerpublish.yml │ └── nodejs.yml ├── .gitignore ├── License.md ├── README.md ├── docker-compose.yml ├── docker └── nginx │ ├── Dockerfile │ └── nginx.conf ├── requirements.txt └── src ├── .env.example ├── Dockerfile ├── api ├── __init__.py ├── admin.py ├── apps.py ├── comments_api │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── permissions.cpython-36.pyc │ │ ├── serializers.cpython-36.pyc │ │ └── views.cpython-36.pyc │ ├── permissions.py │ ├── serializers.py │ └── views.py ├── migrations │ └── __init__.py ├── models.py ├── posts_api │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── permissions.cpython-36.pyc │ │ ├── serializers.cpython-36.pyc │ │ └── views.cpython-36.pyc │ ├── permissions.py │ ├── serializers.py │ └── views.py ├── seminars_api │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── permissions.cpython-36.pyc │ │ ├── serializers.cpython-36.pyc │ │ └── views.cpython-36.pyc │ ├── permissions.py │ ├── serializers.py │ └── views.py ├── serializers.py ├── tests.py ├── urls.py ├── user_api │ ├── __init__.py │ ├── serializers.py │ ├── urls.py │ └── views.py └── views.py ├── blog ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20200412_1112.py │ ├── 0003_auto_20200412_1115.py │ └── __init__.py ├── models.py ├── templates │ ├── post │ │ ├── include │ │ │ └── meta.html │ │ ├── layout.html │ │ ├── layout │ │ │ ├── footer.html │ │ │ └── header.html │ │ └── sections │ │ │ ├── edit.html │ │ │ ├── index.html │ │ │ ├── post_create.html │ │ │ └── show.html │ └── user │ │ ├── login.html │ │ ├── password_change.html │ │ ├── register.html │ │ └── settings.html ├── tests.py ├── urls.py └── views.py ├── comments ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── admin.cpython-36.pyc │ └── models.cpython-36.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-36.pyc │ │ └── __init__.cpython-36.pyc ├── models.py ├── tests.py └── views.py ├── error_pages ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── templates │ ├── 404.html │ └── 500.html ├── tests.py └── views.py ├── manage.py ├── package.json ├── pcw ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── settings.cpython-36.pyc │ ├── urls.cpython-36.pyc │ └── wsgi.cpython-36.pyc ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── populate_script.py ├── resources ├── js │ ├── app.js │ └── components │ │ ├── index.vue │ │ └── plugins │ │ └── pagination.vue └── scss │ ├── _footer.scss │ ├── _header.scss │ ├── _variables.scss │ ├── app.scss │ └── pagination.scss ├── seminars ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── admin.cpython-36.pyc │ ├── models.cpython-36.pyc │ ├── urls.cpython-36.pyc │ └── views.cpython-36.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_seminar_seminar_photo.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-36.pyc │ │ ├── 0002_seminar_seminar_photo.cpython-36.pyc │ │ └── __init__.cpython-36.pyc ├── models.py ├── templates │ └── seminar │ │ ├── seminar_create.html │ │ ├── seminar_detail.html │ │ ├── seminar_edit.html │ │ └── seminar_list.html ├── tests.py ├── urls.py └── views.py ├── static ├── .gitignore ├── css │ ├── .gitignore │ └── app.css ├── fonts │ ├── materialdesignicons-webfont.ttf │ ├── materialdesignicons-webfont.woff │ └── materialdesignicons-webfont.woff2 ├── img │ ├── copyright.svg │ ├── dislike.svg │ ├── facebook-icon.svg │ ├── github-icon.svg │ ├── like.svg │ ├── linkedin-icon.svg │ ├── pcw.png │ ├── share-icon.svg │ ├── show-img.jpg │ └── user.png ├── js │ ├── .gitignore │ └── app.js └── mix-manifest.json ├── user ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20200410_1021.py │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py └── webpack.mix.js /.github/workflows/django.yml: -------------------------------------------------------------------------------- 1 | name: Django CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | max-parallel: 4 15 | matrix: 16 | python-version: [3.6, 3.7, 3.8] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Copy .env 21 | run: php -r "file_exists('.env') || copy('.env.example', '.env');" 22 | working-directory: ./src 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install Dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | - name: Run Tests 32 | run: | 33 | python manage.py test 34 | working-directory: ./src 35 | -------------------------------------------------------------------------------- /.github/workflows/dockerpublish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | # Publish `master` as Docker `latest` image. 6 | branches: 7 | - master 8 | 9 | # Publish `v1.2.3` tags as releases. 10 | tags: 11 | - v* 12 | 13 | # Run tests for any PRs. 14 | pull_request: 15 | 16 | env: 17 | # TODO: Change variable to your image's name. 18 | IMAGE_NAME: image 19 | 20 | jobs: 21 | # Run tests. 22 | # See also https://docs.docker.com/docker-hub/builds/automated-testing/ 23 | test: 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | - name: Run tests 30 | run: | 31 | if [ -f docker-compose.test.yml ]; then 32 | docker-compose --file docker-compose.test.yml build 33 | docker-compose --file docker-compose.test.yml run sut 34 | else 35 | docker build . --file src/Dockerfile 36 | fi 37 | 38 | # Push image to GitHub Packages. 39 | # See also https://docs.docker.com/docker-hub/builds/ 40 | push: 41 | # Ensure test job passes before pushing image. 42 | needs: test 43 | 44 | runs-on: ubuntu-latest 45 | if: github.event_name == 'push' 46 | 47 | steps: 48 | - uses: actions/checkout@v2 49 | 50 | - name: Build image 51 | run: docker build . --file src/Dockerfile --tag image 52 | 53 | - name: Log into registry 54 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin 55 | 56 | # - name: Push image 57 | # run: | 58 | # IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME 59 | 60 | # # Change all uppercase to lowercase 61 | # IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 62 | 63 | # # Strip git ref prefix from version 64 | # VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 65 | 66 | # # Strip "v" prefix from tag name 67 | # [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') 68 | 69 | # # Use Docker `latest` tag convention 70 | # [ "$VERSION" == "master" ] && VERSION=latest 71 | 72 | # echo IMAGE_ID=$IMAGE_ID 73 | # echo VERSION=$VERSION 74 | 75 | # docker tag image $IMAGE_ID:$VERSION 76 | # docker push $IMAGE_ID:$VERSION 77 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Nodejs CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | test: 17 | name: Test on node ${{ matrix.node_version }} and ${{ matrix.os }} 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | matrix: 21 | node_version: ['8', '10', '12'] 22 | os: [ubuntu-latest, windows-latest, macOS-latest] 23 | 24 | steps: 25 | - uses: actions/checkout@v1 26 | - name: Use Node.js ${{ matrix.node_version }} 27 | uses: actions/setup-node@v1 28 | with: 29 | node-version: ${{ matrix.node_version }} 30 | 31 | 32 | - name: npm install, build and test 33 | run: | 34 | npm install 35 | npm run build --if-present 36 | npm test 37 | working-directory: ./src 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | src/venv/ 3 | src/PCW/__pycache__/ 4 | src/.env 5 | src/blog/__pycache__/ 6 | src/blog/migrations/__pycache__/ 7 | src/user/__pycache__/ 8 | src/user/migrations/__pycache__/ 9 | src/db.sqlite3 10 | src/error_pages/__pycache__/ 11 | src/error_pages/migrations/__pycache__/ 12 | src/node_modules/ 13 | src/__pycache__/ 14 | src/api/__pycache__/ 15 | src/api/migrations/__pycache__/ 16 | src/package-lock.json 17 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright © 2020 - 2020 [DevHub](https://github.com/DevHub-Azerbaycan) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Web site for Python Community for Azerbaijan developers 2 | 3 | Used: Python Django, Docker, NPM (Yarn), Postgresql 4 | 5 | ## Installation & Set Up 🛠 6 | 7 | 1. Install Docker 8 | 9 | 2. Build project 10 | 11 | ``` 12 | docker-compose build 13 | ``` 14 | 15 | 3. Edit env file 16 | 17 | 3.1 In src folder copy and paste in same folder .env.example with new name .env 18 | 19 | 3.2 Edit .env file 20 | 21 | 4. Start the development server 22 | 23 | 4.1 In demon (background) 24 | ``` 25 | docker-compose up -d 26 | ``` 27 | 4.2 In bash 28 | ``` 29 | docker-compose up 30 | ``` 31 | 32 | ## Contributors ✨ 33 | 34 | [![contributors](https://badges.implements.io/api/contributors?org=DevHub-Azerbaycan&repo=python_web_site&width=1280&size=180&padding=1&type=jpeg)](https://github.com/DevHub-Azerbaycan/python_web_site/graphs/contributors) 35 | 36 | ## LICENSE 37 | 38 | [MIT](https://github.com/DevHub-Azerbaycan/python_web_site/blob/master/License.md) 39 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | web: 5 | build: ./src 6 | # command: django-admin startproject src #This will create the Django project 7 | # command: python manage.py test #This will run the server so we can test it is working 8 | command: python manage.py runserver 0.0.0.0:8000 #This will run the server so we can test it is working 9 | # command: gunicorn pcw.wsgi:application --bind 0.0.0.0:8000 #From this point we use gunicorn to serve the project 10 | volumes: 11 | - ./src:/src 12 | ports: 13 | - "8000:8000" 14 | depends_on: 15 | - db 16 | nginx: 17 | build: ./docker/nginx 18 | depends_on: 19 | - web 20 | command: nginx -g 'daemon off;' 21 | ports: 22 | - "1314:9002" 23 | volumes: 24 | - ./src/static:/var/www/static 25 | db: 26 | image: postgres:10.1-alpine 27 | environment: 28 | - "POSTGRES_HOST_AUTH_METHOD=trust" 29 | ports: 30 | - '5432:5432' 31 | volumes: 32 | - postgres_data:/var/lib/postgresql/data/ 33 | 34 | volumes: 35 | postgres_data: -------------------------------------------------------------------------------- /docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | 3 | ADD nginx.conf /etc/nginx/nginx.conf 4 | 5 | RUN mkdir -p /var/www/static 6 | 7 | WORKDIR /var/www/static 8 | RUN chown -R nginx:nginx /var/www/static -------------------------------------------------------------------------------- /docker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user nginx; 3 | 4 | worker_processes 1; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | client_max_body_size 100M; 13 | 14 | server { 15 | listen 9002; 16 | charset utf-8; 17 | server_name myserver1.org; 18 | 19 | access_log /dev/stdout; 20 | error_log /dev/stdout info; 21 | 22 | location /static/ { 23 | alias /var/www/static/; 24 | } 25 | 26 | location / { 27 | proxy_pass http://web:8000; #The name or ip of the django container 28 | proxy_set_header Host $host; 29 | proxy_set_header X-Real-IP $remote_addr; 30 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 31 | proxy_set_header X-Forwarded-Host $server_name; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.1 2 | Django==3.1.4 3 | django-crispy-forms==1.10.0 4 | django-debugtools==1.8 5 | django-environ==0.4.5 6 | django-seed==0.2.2 7 | django-webpack-loader==0.7.0 8 | djangomix==1.1.3 9 | djangorestframework==3.12.2 10 | Faker==5.3.0 11 | Pillow==8.1.0 12 | psycopg2==2.8.6 13 | psycopg2-binary==2.8.6 14 | python-dateutil==2.8.1 15 | pytz==2020.5 16 | six==1.15.0 17 | sqlparse==0.4.1 18 | text-unidecode==1.3 19 | gunicorn==20.0.4 20 | gevent -------------------------------------------------------------------------------- /src/.env.example: -------------------------------------------------------------------------------- 1 | DEBUG=on 2 | 3 | #DB SETTINGS 4 | DB_NAME=postgres 5 | DB_USER=postgres 6 | DB_PASSWORD= 7 | DB_HOST=db 8 | DB_PORT=5432 -------------------------------------------------------------------------------- /src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | ENV PYTHONBUFFERED=1 4 | ENV WEBAPP_DIR=/src 5 | 6 | RUN mkdir -p /var/log/gunicorn 7 | RUN mkdir $WEBAPP_DIR 8 | 9 | WORKDIR $WEBAPP_DIR 10 | 11 | COPY ./requirements.txt $WEBAPP_DIR/ 12 | RUN pip install -r requirements.txt 13 | 14 | ADD . $WEBAPP_DIR -------------------------------------------------------------------------------- /src/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/__init__.py -------------------------------------------------------------------------------- /src/api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /src/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = 'api' 6 | -------------------------------------------------------------------------------- /src/api/comments_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/comments_api/__init__.py -------------------------------------------------------------------------------- /src/api/comments_api/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/comments_api/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/comments_api/__pycache__/permissions.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/comments_api/__pycache__/permissions.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/comments_api/__pycache__/serializers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/comments_api/__pycache__/serializers.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/comments_api/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/comments_api/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/comments_api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission,SAFE_METHODS 2 | 3 | 4 | class IsUserOrReadOnly(BasePermission): 5 | def has_object_permission(self,request,view,obj): 6 | if request.method in SAFE_METHODS: 7 | return True 8 | return obj.user == request.user -------------------------------------------------------------------------------- /src/api/comments_api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from comments.models import Comment 3 | from ..posts_api.serializers import PostSerializer 4 | from django.contrib.auth.models import User 5 | 6 | 7 | comment_detail_url = serializers.HyperlinkedIdentityField( 8 | view_name = 'api:comment-detail', 9 | lookup_field = 'pk', 10 | ) 11 | 12 | class UserSerializer(serializers.ModelSerializer): 13 | 14 | class Meta: 15 | model = User 16 | fields = ['username','email'] 17 | 18 | class CommentSerializer(serializers.ModelSerializer): 19 | detail_url = comment_detail_url 20 | post = PostSerializer(many=False , read_only=False) 21 | user = UserSerializer(many=False, read_only=True) 22 | 23 | class Meta: 24 | model = Comment 25 | fields = '__all__' 26 | 27 | 28 | class CommentCreateSerializer(serializers.ModelSerializer): 29 | detail_url = comment_detail_url 30 | class Meta: 31 | model = Comment 32 | fields = ['post','user','body','created_on','parent','detail_url'] 33 | -------------------------------------------------------------------------------- /src/api/comments_api/views.py: -------------------------------------------------------------------------------- 1 | from comments.models import Comment 2 | from rest_framework import generics,status 3 | from rest_framework.response import Response 4 | from django.contrib.auth.models import User 5 | from django.http import Http404 6 | from rest_framework.response import Response 7 | from rest_framework import viewsets 8 | from .serializers import ( 9 | CommentSerializer, 10 | CommentCreateSerializer 11 | ) 12 | from .permissions import IsUserOrReadOnly 13 | from rest_framework.routers import DefaultRouter 14 | from django.utils import timezone 15 | from rest_framework.views import exception_handler 16 | 17 | router = DefaultRouter() 18 | 19 | 20 | class CommentView(viewsets.ModelViewSet): 21 | queryset = Comment.objects.all() 22 | permission_classes = [IsUserOrReadOnly] 23 | lookup_field = 'pk' 24 | 25 | 26 | def get_queryset(self): 27 | return Comment.objects.filter() 28 | 29 | def get_serializer_class(self): 30 | if self.request.method == 'GET': 31 | return CommentSerializer 32 | else: 33 | return CommentCreateSerializer 34 | 35 | 36 | def perform_destroy(self,instance): 37 | comment = instance 38 | comment.active = False 39 | comment.save() 40 | 41 | def perform_update(self, serializer): 42 | serializer.save(updated=True,updated_at=timezone.now()) 43 | 44 | def update(self, request, *args, **kwargs): 45 | super(CommentView, self).update(request, args, kwargs) 46 | response = { 47 | "status_code": status.HTTP_200_OK, 48 | "message": "Successfully updated", 49 | } 50 | return Response(response) 51 | 52 | 53 | def perform_create(self,serializer): 54 | serializer.save(user=self.request.user) 55 | 56 | def create(self, request, *args, **kwargs): 57 | super(CommentView, self).create(request, args, kwargs) 58 | response = { 59 | "status_code": status.HTTP_200_OK, 60 | "message": "Successfully created", 61 | } 62 | 63 | return Response(response) 64 | 65 | commentapiRouter = DefaultRouter() 66 | commentapiRouter.register(r'comment', CommentView) -------------------------------------------------------------------------------- /src/api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/migrations/__init__.py -------------------------------------------------------------------------------- /src/api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /src/api/posts_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/posts_api/__init__.py -------------------------------------------------------------------------------- /src/api/posts_api/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/posts_api/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/posts_api/__pycache__/permissions.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/posts_api/__pycache__/permissions.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/posts_api/__pycache__/serializers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/posts_api/__pycache__/serializers.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/posts_api/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/posts_api/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/posts_api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission,SAFE_METHODS 2 | 3 | 4 | class IsAuthorOrReadOnly(BasePermission): 5 | def has_object_permission(self,request,view,obj): 6 | if request.method in SAFE_METHODS: 7 | return True 8 | return obj.author == request.user -------------------------------------------------------------------------------- /src/api/posts_api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from blog.models import Post 3 | from django.contrib.auth.models import User 4 | 5 | class UserSerializer(serializers.ModelSerializer): 6 | fullName = serializers.SerializerMethodField() 7 | class Meta: 8 | model = User 9 | fields = ['id','username','first_name','last_name','fullName'] 10 | 11 | def get_fullName(self,obj): 12 | return obj.get_full_name() 13 | 14 | 15 | post_detail_url = serializers.HyperlinkedIdentityField( 16 | view_name = 'api:post-detail', 17 | lookup_field = 'pk' 18 | ) 19 | 20 | class PostSerializer(serializers.ModelSerializer): 21 | detail_url = post_detail_url 22 | author = UserSerializer() 23 | likes_count = serializers.SerializerMethodField() 24 | dislikes_count = serializers.SerializerMethodField() 25 | class Meta: 26 | model = Post 27 | fields = '__all__' 28 | 29 | def get_likes_count(self,obj): 30 | return obj.total_likes() 31 | 32 | def get_dislikes_count(self,obj): 33 | return obj.total_dislikes() 34 | 35 | 36 | class PostCreateSerializer(serializers.ModelSerializer): 37 | detail_url = post_detail_url 38 | class Meta: 39 | model = Post 40 | fields = ['name','body','image','detail_url'] -------------------------------------------------------------------------------- /src/api/posts_api/views.py: -------------------------------------------------------------------------------- 1 | # from rest_framework.decorators import api_view 2 | from django.core.paginator import Paginator 3 | from rest_framework import routers 4 | from rest_framework import viewsets 5 | from rest_framework.pagination import PageNumberPagination 6 | from rest_framework.response import Response 7 | from rest_framework import permissions 8 | from blog.models import Post 9 | from .permissions import IsAuthorOrReadOnly 10 | from .serializers import ( 11 | PostSerializer, 12 | PostCreateSerializer, 13 | ) 14 | from rest_framework import filters 15 | from rest_framework.decorators import action 16 | from rest_framework.response import Response 17 | # Create your views here. 18 | 19 | class CustomPagination(PageNumberPagination): 20 | page_size = 10 21 | 22 | def get_paginated_response(self, data): 23 | posts = Post.objects.filter(deleted=False) 24 | current_page = int(self.request.GET.get('page', 1)) 25 | paginator = Paginator(posts, self.page_size) 26 | from_ = paginator.page(current_page).start_index() 27 | to_ = paginator.page(current_page).end_index() 28 | return Response({ 29 | 'meta': { 30 | 'count': self.page.paginator.count, 31 | 'per_page': self.page_size, 32 | 'first_page': 1, 33 | 'last_page': self.page.paginator.num_pages, 34 | 'next': self.get_next_link(), 35 | 'current_page': current_page, 36 | 'previous': self.get_previous_link(), 37 | 'from': from_, 38 | 'to': to_, 39 | }, 40 | 'data': data 41 | }) 42 | 43 | 44 | """ 45 | @api_view(['GET']) 46 | def api_post_list(request): 47 | paginator = CustomPagination() 48 | posts = Post.objects.all() 49 | result_page = paginator.paginate_queryset(posts, request) 50 | serializer = PostSerializer(result_page,many=True) 51 | return paginator.get_paginated_response(serializer.data) 52 | 53 | @api_view(['GET']) 54 | def api_post_detail(request,post_id): 55 | post = get_object_or_404(Post,id=post_id) 56 | serializer = PostSerializer(post,many=False) 57 | return Response(serializer.data) 58 | """ 59 | 60 | 61 | class PostViews(viewsets.ModelViewSet): 62 | queryset = Post.objects.filter(deleted=False) 63 | permission_classes = [IsAuthorOrReadOnly] 64 | pagination_class = CustomPagination 65 | lookup_field = 'pk' 66 | filter_backends = [filters.SearchFilter] 67 | search_fields = ['name'] 68 | 69 | def get_queryset(self): 70 | return Post.objects.filter(deleted=False) 71 | 72 | def get_object(self): 73 | obj = super().get_object() 74 | obj.viewed += 1 75 | obj.save() 76 | return obj 77 | 78 | def get_serializer_class(self): 79 | if self.request.method == 'GET': 80 | return PostSerializer 81 | else: 82 | return PostCreateSerializer 83 | 84 | def perform_create(self, serializer): 85 | serializer.save(author=self.request.user) 86 | 87 | def perform_destroy(self, instance): 88 | post = instance 89 | post.deleted = True 90 | post.save() 91 | 92 | def perform_update(self, serializer): 93 | serializer.save(updated=True) 94 | 95 | @action(detail=True,methods=['get'],permission_classes=[permissions.IsAuthenticated]) 96 | def likes(self,request,pk=None): 97 | obj = self.get_object() 98 | current_user = request.user 99 | liked = False 100 | likes_count = obj.total_likes() 101 | dislikes_count = obj.total_dislikes() 102 | if current_user in obj.likes.all(): 103 | liked = False 104 | obj.likes.remove(current_user) 105 | likes_count -= 1 106 | else: 107 | liked = True 108 | if current_user in obj.dislikes.all(): 109 | obj.dislikes.remove(current_user) 110 | dislikes_count -= 1 111 | obj.likes.add(current_user) 112 | likes_count += 1 113 | else: 114 | obj.likes.add(current_user) 115 | likes_count += 1 116 | data = { 117 | "liked": liked, 118 | "id_": obj.id, 119 | 'likes_count': likes_count, 120 | 'dislikes_count': dislikes_count, 121 | } 122 | return Response(data) 123 | 124 | @action(detail=True, methods=['get'], permission_classes=[permissions.IsAuthenticated]) 125 | def dislikes(self, request, pk=None): 126 | obj = self.get_object() 127 | current_user = request.user 128 | disliked = False 129 | likes_count = obj.total_likes() 130 | dislikes_count = obj.total_dislikes() 131 | if current_user in obj.dislikes.all(): 132 | disliked = False 133 | obj.dislikes.remove(current_user) 134 | dislikes_count -= 1 135 | else: 136 | disliked = True 137 | if current_user in obj.likes.all(): 138 | obj.likes.remove(current_user) 139 | likes_count -= 1 140 | obj.dislikes.add(current_user) 141 | dislikes_count += 1 142 | else: 143 | obj.dislikes.add(current_user) 144 | dislikes_count += 1 145 | data = { 146 | "disliked": disliked, 147 | "id_": obj.id, 148 | 'likes_count': likes_count, 149 | 'dislikes_count': dislikes_count, 150 | } 151 | return Response(data) 152 | 153 | 154 | 155 | blogapiRouter = routers.DefaultRouter() 156 | blogapiRouter.register(r'posts', PostViews) 157 | -------------------------------------------------------------------------------- /src/api/seminars_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/seminars_api/__init__.py -------------------------------------------------------------------------------- /src/api/seminars_api/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/seminars_api/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/seminars_api/__pycache__/permissions.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/seminars_api/__pycache__/permissions.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/seminars_api/__pycache__/serializers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/seminars_api/__pycache__/serializers.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/seminars_api/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/seminars_api/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /src/api/seminars_api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission,SAFE_METHODS 2 | 3 | 4 | class IsOrganizerOrReadOnly(BasePermission): 5 | def has_object_permission(self,request,view,obj): 6 | if request.method in SAFE_METHODS: 7 | return True 8 | return obj.organizer == request.user -------------------------------------------------------------------------------- /src/api/seminars_api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from seminars.models import Seminar 3 | 4 | 5 | seminar_detail_url = serializers.HyperlinkedIdentityField( 6 | view_name = 'api:seminar-detail', 7 | lookup_field = 'pk' 8 | ) 9 | 10 | 11 | class SeminarSerializer(serializers.ModelSerializer): 12 | detail_url = seminar_detail_url 13 | class Meta: 14 | model = Seminar 15 | fields = '__all__' 16 | 17 | 18 | class SeminarCreateSerializer(serializers.ModelSerializer): 19 | detail_url = seminar_detail_url 20 | class Meta: 21 | model = Seminar 22 | fields = ['id','title','content','detail_url','seminar_photo','seminar_place','seminar_date',] -------------------------------------------------------------------------------- /src/api/seminars_api/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404 2 | #from rest_framework.decorators import api_view 3 | from rest_framework.response import Response 4 | from seminars.models import Seminar 5 | from .serializers import ( 6 | SeminarSerializer, 7 | SeminarCreateSerializer, 8 | ) 9 | from rest_framework.pagination import PageNumberPagination 10 | from django.core.paginator import Paginator 11 | from rest_framework import viewsets 12 | from .permissions import IsOrganizerOrReadOnly 13 | from rest_framework import routers 14 | from rest_framework.routers import DefaultRouter 15 | # Create your views here. 16 | 17 | 18 | class SeminarViews(viewsets.ModelViewSet): 19 | queryset = Seminar.objects.all() 20 | permission_classes = [IsOrganizerOrReadOnly] 21 | lookup_field = 'pk' 22 | 23 | def get_queryset(self): 24 | return Seminar.objects.all() 25 | 26 | def get_serializer_class(self): 27 | if self.request.method == 'GET': 28 | return SeminarSerializer 29 | else: 30 | return SeminarCreateSerializer 31 | 32 | def perform_create(self,serializer): 33 | serializer.save(organizer=self.request.user) 34 | 35 | seminarRouter = routers.DefaultRouter() 36 | seminarRouter.register(r'seminars', SeminarViews) -------------------------------------------------------------------------------- /src/api/serializers.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/serializers.py -------------------------------------------------------------------------------- /src/api/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path,include 2 | from . import views 3 | from .posts_api import views as post_views 4 | from .seminars_api import views as seminar_views 5 | from .user_api import urls 6 | 7 | app_name = 'api' 8 | 9 | 10 | 11 | 12 | urlpatterns = [ 13 | #path('posts/',views.api_post_list,name='api-post-list'), 14 | #path('post//',views.api_post_detail,name='api-post-detail'), 15 | path('',include(post_views.blogapiRouter.urls)), 16 | path('',include(seminar_views.seminarRouter.urls)), 17 | path('',include(urls)) 18 | ] 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/api/user_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/user_api/__init__.py -------------------------------------------------------------------------------- /src/api/user_api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.contrib.auth.models import User 3 | 4 | 5 | 6 | class UserSerializer(serializers.ModelSerializer): 7 | fullName = serializers.SerializerMethodField() 8 | 9 | class Meta: 10 | model = User 11 | fields = ['id','username','email','first_name','last_name','fullName'] 12 | 13 | def get_fullName(self,obj): 14 | return obj.get_full_na 15 | 16 | 17 | 18 | class RegistrationSerializer(serializers.ModelSerializer): 19 | password_confirm = serializers.CharField(style={'input_type': 'password'}, write_only=True) 20 | 21 | class Meta: 22 | model = User 23 | fields = ['username', 'email', 'password', 'password_confirm'] 24 | extra_kwargs ={ 25 | 'password': {'write_only': True} 26 | } 27 | 28 | def save(self): 29 | user = User( 30 | username=self.validated_data['username'], 31 | email=self.validated_data['email'] 32 | ) 33 | password = self.validated_data['password'] 34 | password_confirm = self.validated_data['password_confirm'] 35 | 36 | if password != password_confirm: 37 | raise serializers.ValidationError({'password': 'Şifrələr uygun gəlmir'}) 38 | user.set_password(password) 39 | user.save() 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/api/user_api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | from rest_framework.authtoken.views import obtain_auth_token 4 | 5 | app_name = 'user' 6 | 7 | urlpatterns = [ 8 | path('register/',views.register,name='register'), 9 | path('login/',obtain_auth_token,name='login'), 10 | ] -------------------------------------------------------------------------------- /src/api/user_api/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.response import Response 2 | from rest_framework.decorators import api_view 3 | from .serializers import RegistrationSerializer 4 | 5 | 6 | @api_view(['POST',]) 7 | def register(request): 8 | if request.method == 'POST': 9 | serializer = RegistrationSerializer(data=request.data) 10 | data = {} 11 | if serializer.is_valid(): 12 | user = serializer.save() 13 | data['response'] = 'Ugurla qeydiyyyatdan kecdiniz' 14 | data['email'] = user.email 15 | data['username'] = user.username 16 | else: 17 | data = serializer.errors 18 | return Response(data) 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/api/views.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/api/views.py -------------------------------------------------------------------------------- /src/blog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/blog/__init__.py -------------------------------------------------------------------------------- /src/blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | 5 | from .models import Post 6 | 7 | admin.site.register(Post) -------------------------------------------------------------------------------- /src/blog/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BlogConfig(AppConfig): 5 | name = 'blog' 6 | -------------------------------------------------------------------------------- /src/blog/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import Post 3 | 4 | 5 | 6 | class PostForm(forms.ModelForm): 7 | class Meta: 8 | model = Post 9 | fields = ['name','body','image',] -------------------------------------------------------------------------------- /src/blog/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-04-10 05:42 2 | 3 | import blog.models 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Post', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('name', models.CharField(max_length=191)), 23 | ('body', models.TextField()), 24 | ('image', models.ImageField(blank=True, default='', null=True, upload_to=blog.models.upload_to)), 25 | ('created_at', models.DateTimeField(auto_now_add=True)), 26 | ('updated_at', models.DateTimeField(auto_now=True)), 27 | ('updated', models.BooleanField(default=False)), 28 | ('deleted', models.BooleanField(default=False)), 29 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 30 | ('likes', models.ManyToManyField(blank=True, related_name='likes', to=settings.AUTH_USER_MODEL)), 31 | ], 32 | options={ 33 | 'verbose_name': 'Post', 34 | 'verbose_name_plural': 'Post', 35 | 'ordering': ['-created_at'], 36 | }, 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /src/blog/migrations/0002_auto_20200412_1112.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-04-12 07:12 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ('blog', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='post', 17 | name='dislikes', 18 | field=models.ManyToManyField(blank=True, related_name='dislikes', to=settings.AUTH_USER_MODEL), 19 | ), 20 | migrations.AddField( 21 | model_name='post', 22 | name='viewed', 23 | field=models.ManyToManyField(blank=True, related_name='viewed', to=settings.AUTH_USER_MODEL), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /src/blog/migrations/0003_auto_20200412_1115.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-04-12 07:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('blog', '0002_auto_20200412_1112'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='post', 15 | name='viewed', 16 | ), 17 | migrations.AddField( 18 | model_name='post', 19 | name='viewed', 20 | field=models.PositiveIntegerField(default=0), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /src/blog/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/blog/migrations/__init__.py -------------------------------------------------------------------------------- /src/blog/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | 5 | # Create your models here. 6 | def upload_to(instance,filename): 7 | return '%s/%s/%s'%('posts',instance.name,filename) 8 | 9 | class Post(models.Model): 10 | name = models.CharField(max_length=191) 11 | body = models.TextField() 12 | image = models.ImageField(upload_to = upload_to,default='',null=True,blank=True) 13 | author = models.ForeignKey(User, on_delete=models.CASCADE) 14 | likes = models.ManyToManyField(User, blank=True, related_name='likes') 15 | dislikes = models.ManyToManyField(User, blank=True, related_name='dislikes') 16 | viewed = models.PositiveIntegerField(default=0) 17 | created_at = models.DateTimeField(auto_now_add=True) 18 | updated_at = models.DateTimeField(auto_now=True) 19 | updated = models.BooleanField(default=False) 20 | deleted = models.BooleanField(default=False) 21 | 22 | def __str__(self): 23 | return "%s posted by %s" % (self.name, self.author.username) 24 | 25 | class Meta: 26 | verbose_name = 'Post' 27 | verbose_name_plural = 'Post' 28 | ordering = ['-created_at'] 29 | 30 | def snipped_body(self): 31 | return self.body[:150] + "..." 32 | 33 | def total_likes(self): 34 | return self.likes.count() 35 | 36 | def total_dislikes(self): 37 | return self.dislikes.count() 38 | 39 | -------------------------------------------------------------------------------- /src/blog/templates/post/include/meta.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/blog/templates/post/layout.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load mix %} 3 | 4 | 5 | 6 | {% include "post/include/meta.html" %} 7 | {% block tittle %}{% endblock %} | PCW 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | {% include "post/layout/header.html" %} 16 | {% block content %}{% endblock %} 17 | {% block scripts %}{% endblock %} 18 | {% include "post/layout/footer.html" %} 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /src/blog/templates/post/layout/footer.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | -------------------------------------------------------------------------------- /src/blog/templates/post/layout/header.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 |
3 | 4 |

Community

5 | 13 |
14 |

15 | test sem cre 16 | az 17 | en 18 | ru 19 | test change pass 20 | 21 |

22 | / 23 | 24 | 25 |
26 |
-------------------------------------------------------------------------------- /src/blog/templates/post/sections/edit.html: -------------------------------------------------------------------------------- 1 |

Post Edit

2 | 3 | 4 |
5 | {% csrf_token %} 6 | {{ form.as_p }} 7 | 8 |
-------------------------------------------------------------------------------- /src/blog/templates/post/sections/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Main page 5 | {% endblock %} 6 | 7 | {% block content %} 8 |

+Create Post

9 |
10 | 11 |
12 | {% for post in posts %} 13 | {% if post.deleted != True %} 14 |
15 | {{ post.name }} posted 16 | by {{ post.author.username }} 17 |

{{ post.snipped_body }}

18 |
Edit
19 |
Delete
20 |
21 |
22 | {% endif %} 23 | {% endfor %} 24 |
25 | 26 | 27 | {% if posts.has_previous %} 28 | 29 | {% endif %} 30 | {% for i in posts.paginator.page_range %} 31 | {% if posts.number == i %} 32 | {{i}} 33 | {% elif posts.number != i %} 34 | {{i}} 35 | {% endif %} 36 | {% endfor %} 37 | {% if posts.has_next %} 38 | 39 | {% endif %} 40 | 41 |
42 | {% endblock %} -------------------------------------------------------------------------------- /src/blog/templates/post/sections/post_create.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Post Create 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |

Post Create

11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 | {% endblock %} -------------------------------------------------------------------------------- /src/blog/templates/post/sections/show.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | {{ post.name }} 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
. 10 |

{{ post.name }}

11 |
12 | 13 |
14 | {{ post.author.username }}
15 | 4 March'365 words/365 like 17 |
18 | 23 |
24 |
25 |
26 | 27 |
28 |

29 | {{ post.body }} 30 |

31 |
32 |
33 | 39 |
40 | 41 | 125 42 | 43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores doloremque ex 51 | exercitationem incidunt, nam praesentium quas quibusdam reiciendis sapiente voluptatum. 52 |
53 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores doloremque ex 54 | exercitationem incidunt, nam praesentium quas quibusdam reiciendis sapiente voluptatum. 55 |
56 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores doloremque ex 57 | exercitationem incidunt, nam praesentium quas quibusdam reiciendis sapiente voluptatum. 58 |
59 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores doloremque ex 60 | exercitationem incidunt, nam praesentium quas quibusdam reiciendis sapiente voluptatum. 61 |
62 |
63 |
64 |
65 | {% endblock %} -------------------------------------------------------------------------------- /src/blog/templates/user/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Login 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |

Login

11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | {% endblock %} 22 | 23 | -------------------------------------------------------------------------------- /src/blog/templates/user/password_change.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Change Password 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |

Change Password

11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /src/blog/templates/user/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Register 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

Register

12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /src/blog/templates/user/settings.html: -------------------------------------------------------------------------------- 1 | {% block profile_tam %} 2 |
3 |
4 | 5 |
6 | 7 |
8 |
9 | 10 | {% csrf_token %} 11 | {% for field in form %} 12 |
13 | 14 | {{field}} 15 | {% if field.errors %} 16 | {% for error in field.errors %} 17 | {{ error }} 18 | {% endfor %} 19 | {% else %} 20 | {% if field.help_text %} 21 | {{ field.help_text }} 22 | {% endif %} 23 | {% endif %} 24 |
25 | {% endfor %} 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | {% endblock profile_tam %} -------------------------------------------------------------------------------- /src/blog/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | app_name = 'post' 5 | 6 | urlpatterns = [ 7 | path('', views.index, name='main-page'), 8 | path('create/', views.create, name='post-create'), 9 | path('post//', views.show, name='post-detail'), 10 | path('/edit/', views.edit, name='post-edit'), 11 | path('/delete/', views.delete, name='post-delete'), 12 | ] 13 | -------------------------------------------------------------------------------- /src/blog/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, get_object_or_404, redirect, reverse, HttpResponseRedirect 2 | from .models import Post 3 | from .forms import PostForm 4 | from django.http import JsonResponse 5 | from django.core.paginator import Paginator 6 | 7 | # Create your views here. 8 | 9 | 10 | def index(request): 11 | return render(request, 'post/sections/index.html',{}) 12 | 13 | 14 | def create(request): 15 | return render(request, 'post/sections/post_create.html', {}) 16 | 17 | 18 | def show(request, post_pk): 19 | return render(request, 'post/sections/show.html',{}) 20 | 21 | 22 | def edit(request, post_pk): 23 | return render(request, 'post/sections/edit.html',{}) 24 | 25 | 26 | def delete(request, post_pk): 27 | return redirect('post:main-page') 28 | 29 | -------------------------------------------------------------------------------- /src/comments/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/__init__.py -------------------------------------------------------------------------------- /src/comments/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/comments/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /src/comments/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /src/comments/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Comment 3 | # Register your models here. 4 | 5 | admin.site.register(Comment) -------------------------------------------------------------------------------- /src/comments/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CommentsConfig(AppConfig): 5 | name = 'comments' 6 | -------------------------------------------------------------------------------- /src/comments/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.5 on 2020-04-13 10:54 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('blog', '0003_auto_20200412_1115'), 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Comment', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('body', models.TextField()), 23 | ('created_on', models.DateTimeField(auto_now_add=True)), 24 | ('updated_on', models.DateTimeField(auto_now=True)), 25 | ('active', models.BooleanField(default=True)), 26 | ('updated', models.BooleanField(default=False)), 27 | ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.Comment')), 28 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='posts', to='blog.Post')), 29 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 30 | ], 31 | options={ 32 | 'ordering': ['created_on'], 33 | }, 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /src/comments/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/migrations/__init__.py -------------------------------------------------------------------------------- /src/comments/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /src/comments/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/comments/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/comments/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from blog.models import Post 3 | from django.contrib.auth.models import User 4 | 5 | # Create your models here. 6 | class Comment(models.Model): 7 | post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='posts') 8 | parent = models.ForeignKey('self',null=True,blank=True,on_delete=models.CASCADE) 9 | user = models.ForeignKey(User, on_delete=models.CASCADE) 10 | body = models.TextField() 11 | created_on = models.DateTimeField(auto_now_add=True) 12 | updated_on = models.DateTimeField(auto_now=True) 13 | active = models.BooleanField(default=True) 14 | updated = models.BooleanField(default=False) 15 | 16 | class Meta: 17 | ordering = ['created_on'] 18 | 19 | def __str__(self): 20 | return 'Comment {} by {}'.format(self.body, self.user) 21 | 22 | # def __init__(self, *args, **kwargs): 23 | 24 | # super(CommentCreateSerializer, self).__init__(*args, **kwargs) 25 | # self.fields["post"].error_messages["required"] = u"Post is required" 26 | # self.fields["user"].error_messages["required"] = u"User is required" 27 | # self.fields["body"].error_messages["required"] = u"Body is required" -------------------------------------------------------------------------------- /src/comments/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/comments/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /src/error_pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/error_pages/__init__.py -------------------------------------------------------------------------------- /src/error_pages/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /src/error_pages/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ErrorPagesConfig(AppConfig): 5 | name = 'error_pages' 6 | -------------------------------------------------------------------------------- /src/error_pages/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/error_pages/migrations/__init__.py -------------------------------------------------------------------------------- /src/error_pages/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /src/error_pages/templates/404.html: -------------------------------------------------------------------------------- 1 |

404 Page

-------------------------------------------------------------------------------- /src/error_pages/templates/500.html: -------------------------------------------------------------------------------- 1 |

500 Page

-------------------------------------------------------------------------------- /src/error_pages/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/error_pages/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.shortcuts import render_to_response 3 | from django.template import RequestContext 4 | # Create your views here. 5 | 6 | """If you want to check error pages,change these in settings: 7 | DEBUG = False 8 | ALLOWED_HOSTS = ['*'] 9 | """ 10 | 11 | def error_404(request,exception): 12 | response = render_to_response('404.html', {}, 13 | context_instance=RequestContext(request)) 14 | response.status_code = 404 15 | return response 16 | 17 | 18 | def error_500(request): 19 | response = render_to_response('500.html', {}, 20 | context_instance=RequestContext(request)) 21 | response.status_code = 500 22 | return response -------------------------------------------------------------------------------- /src/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pcw.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "npm run development -- --watch", 7 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 8 | "prod": "npm run production", 9 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 10 | }, 11 | "devDependencies": { 12 | "axios": "^0.19.2", 13 | "cross-env": "^5.2.1", 14 | "laravel-mix": "^4.0.15", 15 | "resolve-url-loader": "^3.1.0", 16 | "sass": "^1.26.3", 17 | "sass-loader": "^7.3.1", 18 | "@mdi/font": "^5.0.45", 19 | "vue-template-compiler": "^2.6.11" 20 | }, 21 | "dependencies": { 22 | "reset-css": "^5.0.1", 23 | "vue": "^2.6.11" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/pcw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/pcw/__init__.py -------------------------------------------------------------------------------- /src/pcw/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/pcw/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/pcw/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/pcw/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /src/pcw/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/pcw/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /src/pcw/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/pcw/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /src/pcw/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for PCW project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pcw.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /src/pcw/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import environ 3 | 4 | env = environ.Env() 5 | environ.Env.read_env('.env') 6 | 7 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 8 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 9 | 10 | # Quick-start development settings - unsuitable for production 11 | # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ 12 | 13 | # SECURITY WARNING: keep the secret key used in production secret! 14 | SECRET_KEY = 'sk5pog)0-1vr+c0t0+=y*+$0p$q(i88+*&p##^s_*s_l9j16pg' 15 | 16 | # SECURITY WARNING: don't run with debug turned on in production! 17 | DEBUG = env('DEBUG') 18 | 19 | ALLOWED_HOSTS = [] 20 | 21 | # Application definition 22 | 23 | INSTALLED_APPS = [ 24 | 'django.contrib.admin', 25 | 'django.contrib.auth', 26 | 'django.contrib.contenttypes', 27 | 'django.contrib.sessions', 28 | 'django.contrib.messages', 29 | 'django.contrib.staticfiles', 30 | 'user', 31 | 'blog', 32 | 'seminars', 33 | 'api', 34 | 'django_seed', 35 | 'djangomix', 36 | 'rest_framework', 37 | 'rest_framework.authtoken', 38 | # 'error_pages', 39 | 'crispy_forms', 40 | 'comments' 41 | 42 | ] 43 | REST_FRAMEWORK = { 44 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 45 | 'rest_framework.authentication.TokenAuthentication', 46 | ] 47 | } 48 | 49 | 50 | 51 | MIDDLEWARE = [ 52 | 'django.middleware.security.SecurityMiddleware', 53 | 'django.contrib.sessions.middleware.SessionMiddleware', 54 | 'django.middleware.common.CommonMiddleware', 55 | 'django.middleware.csrf.CsrfViewMiddleware', 56 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 57 | 'django.contrib.messages.middleware.MessageMiddleware', 58 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 59 | ] 60 | 61 | ROOT_URLCONF = 'pcw.urls' 62 | 63 | TEMPLATES = [ 64 | { 65 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 66 | 'DIRS': ['blog/templates/'], 67 | 'APP_DIRS': True, 68 | 'OPTIONS': { 69 | 'context_processors': [ 70 | 'django.template.context_processors.debug', 71 | 'django.template.context_processors.request', 72 | 'django.contrib.auth.context_processors.auth', 73 | 'django.contrib.messages.context_processors.messages', 74 | ], 75 | }, 76 | }, 77 | ] 78 | 79 | WSGI_APPLICATION = 'pcw.wsgi.application' 80 | 81 | # Database 82 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases 83 | 84 | DATABASES = { 85 | 'default': { 86 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', # django.db.backends.postgresql_psycopg2 87 | 'NAME': env.str('DB_NAME'), # set your db name 88 | 'USER': env.str('DB_USER'), # set your db user 89 | 'PASSWORD': env.str('DB_PASSWORD'), # set your password 90 | 'HOST': env.str('DB_HOST'), # localhost 91 | 'PORT': env.str('DB_PORT'), 92 | } 93 | } 94 | 95 | 96 | 97 | # Password validation 98 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators 99 | 100 | AUTH_PASSWORD_VALIDATORS = [ 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 109 | }, 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 112 | }, 113 | ] 114 | 115 | # Internationalization 116 | # https://docs.djangoproject.com/en/3.0/topics/i18n/ 117 | 118 | LANGUAGE_CODE = 'en-us' 119 | 120 | TIME_ZONE = 'UTC' 121 | 122 | USE_I18N = True 123 | 124 | USE_L10N = True 125 | 126 | USE_TZ = True 127 | 128 | # Static files (CSS, JavaScript, Images) 129 | # https://docs.djangoproject.com/en/3.0/howto/static-files/ 130 | 131 | STATIC_URL = '/static/' 132 | 133 | STATICFILES_DIRS = [ 134 | os.path.join(BASE_DIR, "static"), 135 | ] 136 | STATIC_ROOT = os.path.join(BASE_DIR, "static_root") 137 | 138 | MEDIA_URL = "/media/" 139 | MEDIA_ROOT = os.path.join(BASE_DIR, "media") 140 | -------------------------------------------------------------------------------- /src/pcw/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | from django.conf.urls.static import static 4 | from django.conf import settings 5 | from django.conf.urls import handler404, handler500 6 | 7 | urlpatterns = [ 8 | path('admin/', admin.site.urls), 9 | path('user/', include("user.urls")), 10 | path('api/', include('api.urls')), 11 | path('seminar/', include('seminars.urls')), 12 | path('', include('blog.urls')), 13 | ] 14 | 15 | # Error Pages urls 16 | # handler400 = 'error_pages.views.error_404' 17 | # handler500 = 'error_pages.views.error_500' 18 | 19 | 20 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 21 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 22 | -------------------------------------------------------------------------------- /src/pcw/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for PCW project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pcw.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /src/populate_script.py: -------------------------------------------------------------------------------- 1 | """ 2 | First of all, make zero database 3 | For run of seed process,write this in terminal: 4 | python populate_script.py 5 | 6 | This process take minutes. Just be patient))) 7 | 8 | """ 9 | import django 10 | import os 11 | import random 12 | 13 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pcw.settings") 14 | django.setup() 15 | 16 | from faker import Faker 17 | from blog.models import Post, User 18 | 19 | fake = Faker() 20 | 21 | 22 | def create_admin(): 23 | username = fake.name() 24 | password = fake.password() 25 | email = fake.email() 26 | user = User.objects.create_user(username, email, password, is_staff=True, is_superuser=True) 27 | 28 | 29 | def unique_username_generator(testName): 30 | qs = User.objects.filter(username=testName) 31 | if qs.exists(): 32 | testName = fake.name() 33 | return unique_username_generator(testName) 34 | else: 35 | return testName 36 | 37 | 38 | def create_author(N): 39 | for i in range(N): 40 | testName = fake.name() 41 | username = unique_username_generator(testName) 42 | password = fake.password() 43 | email = fake.email() 44 | User.objects.create_user(username, email, password, is_staff=False, is_superuser=False) 45 | 46 | 47 | def create_posts(N): 48 | for i in range(N): 49 | random_id = random.randint(1, 100) 50 | name = fake.name() 51 | body = fake.text() 52 | author = User.objects.get(id=random_id) 53 | Post.objects.create(name=name, body=body, author=author) 54 | 55 | 56 | create_admin() 57 | create_author(100) 58 | create_posts(1000) 59 | 60 | print('Data Is Populated Succesfully!') 61 | -------------------------------------------------------------------------------- /src/resources/js/app.js: -------------------------------------------------------------------------------- 1 | window.axios = require('axios'); 2 | window.Vue = require('vue'); 3 | 4 | Vue.component('index', require('./components/index').default); 5 | Vue.component('pagination', require('./components/plugins/pagination').default); 6 | 7 | Vue.config.productionTip = false; 8 | Vue.config.silent = false; 9 | Vue.config.keyCodes.backspace = 8; 10 | Vue.config.devtools = false 11 | 12 | if (document.getElementById('app')) { 13 | const app = new Vue({ 14 | el: '#app', 15 | }); 16 | } -------------------------------------------------------------------------------- /src/resources/js/components/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 107 | -------------------------------------------------------------------------------- /src/resources/js/components/plugins/pagination.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 53 | -------------------------------------------------------------------------------- /src/resources/scss/_footer.scss: -------------------------------------------------------------------------------- 1 | 2 | .footer { 3 | position: absolute; 4 | bottom: 0; 5 | width: 100%; 6 | color: var(--color2); 7 | background: var(--color1); 8 | font-size: 2rem; 9 | font-family: Arial, serif; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | 14 | .footer-content { 15 | display: flex; 16 | align-items: center; 17 | height: 54px; 18 | 19 | .copyright-icon { 20 | padding-right: .5rem; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/resources/scss/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | display: flex; 3 | align-items: center; 4 | position: relative; 5 | background: var(--color1); 6 | padding: 0 2rem; 7 | 8 | .logo { 9 | width: 6rem; 10 | height: 6rem; 11 | } 12 | 13 | .logo-text { 14 | display: inline-block; 15 | font-size: 2.2rem; 16 | color: var(--color2); 17 | font-family: "Arial Black", serif; 18 | padding: 0 1rem; 19 | } 20 | 21 | .menu-list { 22 | color: var(--color2); 23 | margin: 0; 24 | 25 | .list-item { 26 | display: inline-block; 27 | text-decoration: none; 28 | 29 | a { 30 | display: inline-block; 31 | font-size: 1.5rem; 32 | font-family: Arial, serif; 33 | font-weight: 900; 34 | padding: 2.7rem 1rem; 35 | cursor: pointer; 36 | color: var(--color2); 37 | text-decoration: none; 38 | transition: all .4s; 39 | } 40 | 41 | a:hover { 42 | background: var(--color2); 43 | color: var(--color1); 44 | transition: all .4s; 45 | } 46 | } 47 | } 48 | 49 | .header-right { 50 | color: var(--color2); 51 | font-size: 1.5rem; 52 | font-family: Arial, serif; 53 | position: absolute; 54 | right: 3rem; 55 | } 56 | 57 | .header-language { 58 | padding-right: 3rem; 59 | display: inline-block; 60 | 61 | span { 62 | cursor: pointer; 63 | } 64 | } 65 | 66 | .sign-in-location, .sign-up-location { 67 | color: var(--color2); 68 | text-decoration: none; 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/resources/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | :root, [data-theme="default"] { 2 | //Background 3 | --background-default: #f5f5f5; 4 | --background-paper: #fff; 5 | --background-tertiary: #fafbfc; 6 | --background-secondary: #f9f9fb; 7 | 8 | --background-header: #fafbfc; 9 | 10 | //Typography 11 | --text-black-primary: rgba(0, 0, 0, 0.87); 12 | --text-black-secondary: #4d4d4d; 13 | --text-black-disabled: #4c4c4e; 14 | 15 | --text-white-primary: #fff; 16 | --text-white-secondary: rgba(255, 255, 255, 0.7); 17 | --text-white-disabled: rgba(255, 255, 255, 0.5); 18 | 19 | --color-primary-main: #0086b5; 20 | --color-primary-main-button: #428bca; 21 | --color-primary-light: #4791db; 22 | --color-primary-dark: #115293; 23 | 24 | --color-secondary-main: #dc004e; 25 | --color-secondary-light: #e33371; 26 | --color-secondary-dark: #9a0036; 27 | 28 | --color-error-main: #f44336; 29 | --color-error-light: #e57373; 30 | --color-error-dark: #d32f2f; 31 | 32 | --color-warning-main: #ff9800; 33 | --color-warning-light: #ffb74d; 34 | --color-warning-dark: #f57c00; 35 | 36 | --color-info-main: #2196f3; 37 | --color-info-light: #64b5f6; 38 | --color-info-dark: #1976d2; 39 | 40 | --color-success-main: #4caf50; 41 | --color-success-light: #81c784; 42 | --color-success-dark: #388e3c; 43 | 44 | // Border 45 | --color-border: #e9e9e9; //#e9e9e9 46 | --color-border-button: .06rem solid hsla(0, 0%, 0%, 0.2); 47 | 48 | //Header 49 | --color-header: #757575; 50 | 51 | //Post 52 | --color-post-title: #212121; 53 | 54 | //Footer 55 | --footer: rgba(255, 255, 255, 0.7); 56 | 57 | //Color 58 | --color-black: rgba(0, 0, 0, 0.87); 59 | --color-black-secondary: #757575; 60 | --color-white: #fff; 61 | --color-white-secondary: rgba(255, 255, 255, 0.7); 62 | --color-grey: #757575; 63 | 64 | //Loading 65 | --loading-black: #0000002e; 66 | } 67 | 68 | :root, [data-theme="dark"] { 69 | //Background 70 | --background-default: #171c20; //#121212; //#181818 71 | --background-paper: #22272b; 72 | --background-tertiary: #757575; 73 | --background-secondary: #1a1e21; 74 | 75 | --background-header: #1f2327; 76 | 77 | //Typography 78 | --text-white-primary: rgba(0, 0, 0, 0.87); 79 | --text-white-secondary: #bfbfbf; 80 | --text-white-disabled: rgba(255, 255, 255, 0.3); 81 | 82 | --text-black-primary: #fff; 83 | --text-black-secondary: hsla(0,0%,100%,.5); 84 | --text-black-disabled: rgba(255, 255, 255, 0.3); 85 | 86 | --color-primary-main: #2093bb; 87 | --color-primary-main-button: #2371c1; 88 | --color-primary-light: #a6d4fa; 89 | --color-primary-dark: #648dae; 90 | 91 | --color-secondary-main: #f48fb1; 92 | --color-secondary-light: #f6a5c0; 93 | --color-secondary-dark: #aa647b; 94 | 95 | --color-error-main: #f44336; 96 | --color-error-light: #e57373; 97 | --color-error-dark: #d32f2f; 98 | 99 | --color-warning-main: #ff9800; 100 | --color-warning-light: #ffb74d; 101 | --color-warning-dark: #f57c00; 102 | 103 | --color-info-main: #2196f3; 104 | --color-info-light: #64b5f6; 105 | --color-info-dark: #1976d2; 106 | 107 | --color-success-main: #4caf50; 108 | --color-success-light: #81c784; 109 | --color-success-dark: #388e3c; 110 | 111 | // Border 112 | --color-border: #393d41; 113 | --color-border-button: .06rem solid hsla(0,0%,100%,.2); 114 | 115 | //Header 116 | --color-header: rgba(255, 255, 255, 0.7); 117 | 118 | //Post 119 | --color-post-title: #bfbfbf; 120 | 121 | //Footer 122 | --footer: rgba(255, 255, 255, 0.7); 123 | 124 | //Color 125 | --color-black: rgba(0, 0, 0, 0.87); 126 | --color-black-secondary: #757575; 127 | --color-white: #fff; 128 | --color-white-secondary: rgba(255, 255, 255, 0.7); 129 | --color-grey: #7d7d7d; 130 | 131 | //Loading 132 | --loading-black: #ffffff36; 133 | } 134 | -------------------------------------------------------------------------------- /src/resources/scss/app.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | //@import '~@mdi/font/scss/variables'; 4 | //@import '~@mdi/font/scss/functions'; 5 | ////@import '~@mdi/font/scss/path'; 6 | // 7 | //// 'path'-file fixes 8 | //@font-face { 9 | // font-family: '#{$mdi-font-name}'; 10 | // font-display: swap; 11 | // src: url('/static/fonts/#{$mdi-filename}-webfont.eot'); 12 | // src: url('/static/fonts/#{$mdi-filename}-webfont.eot?#iefix') format('embedded-opentype'), 13 | // url('/static/fonts/#{$mdi-filename}-webfont.woff2') format('woff2'), 14 | // url('/static/fonts/#{$mdi-filename}-webfont.woff') format('woff'), 15 | // url('/static/fonts/#{$mdi-filename}-webfont.ttf') format('truetype'), 16 | // url('/static/fonts/#{$mdi-filename}-webfont.svg##{$mdi-filename}#{$mdi-font-weight}') format('svg'); 17 | // font-weight: normal; 18 | // font-style: normal; 19 | //} 20 | // 21 | //@import '~@mdi/font/scss/core'; 22 | //@import '~@mdi/font/scss/icons'; 23 | //@import '~@mdi/font/scss/extras'; 24 | //@import '~@mdi/font/scss/animated'; 25 | 26 | /* all */ 27 | 28 | 29 | :root { 30 | --color1: rgba(0, 0, 0, 0.87) ; 31 | --color2: #eaeaea; 32 | } 33 | 34 | @import '~reset-css'; 35 | 36 | html { 37 | font-size: 10px; 38 | } 39 | body{ 40 | font-family: Arial, self; 41 | background: var(--color2); 42 | } 43 | 44 | .page-container { 45 | position: relative; 46 | min-height: 100vh; 47 | 48 | .wrap{ 49 | padding-bottom: 54px; 50 | } 51 | } 52 | 53 | @import 'variables'; 54 | @import "header"; 55 | @import 'footer'; 56 | @import "pagination"; 57 | 58 | /* show */ 59 | 60 | #show { 61 | background: #eaeaea; 62 | display: flex; 63 | justify-content: center; 64 | font-family: Arial, self, serif; 65 | 66 | .container { 67 | border: .1rem solid var(--color1); 68 | max-width: 85rem; 69 | background: #fff; 70 | position: relative; 71 | padding: 0 7rem; 72 | margin-bottom: 3rem; 73 | padding-bottom: 3rem; 74 | 75 | h1 { 76 | background: black; 77 | color: #fff; 78 | font-family: Arial; 79 | padding: .5rem; 80 | text-align: center; 81 | font-size: 4rem; 82 | margin-bottom: 1rem; 83 | } 84 | 85 | .comment-input { 86 | box-sizing: border-box; 87 | width: 100%; 88 | font-size: 1.6rem; 89 | padding: 1rem; 90 | border-radius: 1.5rem; 91 | outline: none; 92 | font-family: Arial; 93 | height: 10rem; 94 | } 95 | 96 | .button-container { 97 | text-align: right; 98 | } 99 | 100 | .comment-button { 101 | width: 15rem; 102 | height: 4rem; 103 | background: black; 104 | color: #fff; 105 | font-size: 2rem; 106 | border-radius: 1rem; 107 | border: none; 108 | margin: 1rem 0; 109 | outline: none; 110 | } 111 | 112 | .comment-item { 113 | padding: 1.5rem; 114 | font-size: 1.6rem; 115 | border: .1rem solid #000; 116 | margin: 1rem; 117 | border-radius: 1.5rem; 118 | } 119 | 120 | } 121 | 122 | .show-user { 123 | display: flex; 124 | align-items: center; 125 | padding-bottom: 2rem; 126 | position: relative; 127 | 128 | .user-icon { 129 | width: 6rem; 130 | height: 6rem; 131 | border-radius: 50%; 132 | 133 | } 134 | 135 | .show-user-container { 136 | font-size: 2rem; 137 | font-family: Arial; 138 | } 139 | 140 | .show-user-name { 141 | display: inline-block; 142 | margin: -.5rem 0; 143 | } 144 | 145 | .show-user-container span { 146 | padding: 1rem; 147 | } 148 | 149 | .social-icons { 150 | position: absolute; 151 | right: 0; 152 | } 153 | 154 | .social-icons img { 155 | width: 4rem; 156 | height: 4rem; 157 | } 158 | 159 | .social-icons li { 160 | text-decoration: none; 161 | display: inline-block; 162 | padding: 0 .5rem; 163 | 164 | a { 165 | border-radius: 5rem; 166 | } 167 | } 168 | } 169 | 170 | .show-content { 171 | .show-img { 172 | } 173 | 174 | img { 175 | width: 100%; 176 | max-height: 50rem 177 | } 178 | 179 | p { 180 | font-size: 2rem; 181 | font-family: Arial; 182 | line-height: 3rem; 183 | word-spacing: .4rem; 184 | padding-top: 1.5rem; 185 | } 186 | 187 | } 188 | 189 | .show-bottom { 190 | padding: 2rem 0; 191 | display: flex; 192 | align-items: center; 193 | font-size: 2rem; 194 | font-family: monospace; 195 | 196 | span { 197 | padding: 0 1rem; 198 | } 199 | 200 | .like-dislike-container { 201 | display: inline-block; 202 | display: flex; 203 | align-items: center; 204 | } 205 | 206 | img { 207 | width: 3rem; 208 | cursor: pointer; 209 | } 210 | 211 | .show-bottom-right { 212 | display: flex; 213 | align-items: center; 214 | position: absolute; 215 | right: 7.5rem; 216 | } 217 | } 218 | 219 | } 220 | 221 | 222 | /* post create */ 223 | .post-create { 224 | font-family: Arial, self; 225 | display: flex; 226 | background: var(--color2); 227 | .container { 228 | display: inline-block; 229 | margin: 0 auto; 230 | min-width: 70%; 231 | 232 | .post-create-title { 233 | color: var(--color1); 234 | font-size: 5rem; 235 | font-weight: 700; 236 | text-align: center; 237 | padding: 3rem 0; 238 | } 239 | 240 | form { 241 | padding: 5rem 0; 242 | input{ 243 | margin-left:0; 244 | } 245 | label { 246 | font-size: 2rem; 247 | padding: 1.5rem; 248 | 249 | } 250 | 251 | #post-content { 252 | height: 40rem; 253 | width: 80%; 254 | } 255 | 256 | #post-image { 257 | margin: 1rem 0 1.5rem 0; 258 | } 259 | } 260 | } 261 | } 262 | .post-input-style { 263 | margin: 1rem 0 3.5rem 0; 264 | width: 50%; 265 | min-width: 40rem; 266 | outline: none; 267 | border-radius: 1rem; 268 | padding: 1.5rem; 269 | border: .0005rem solid #000; 270 | box-shadow: 0px 3px 17px -8px rgba(112, 112, 112, 0.69); 271 | font-family: Arial, self; 272 | font-size: 1.6rem; 273 | } 274 | 275 | /* login */ 276 | 277 | .page-title{ 278 | color: var(--color1); 279 | font-size: 6rem; 280 | font-weight: 700; 281 | text-align:center; 282 | padding: 3rem 0; 283 | margin-bottom: 2rem; 284 | } 285 | 286 | form{ 287 | input[type = "text"],input[type = "password"],input[type = "email"]{ 288 | margin:2rem; 289 | margin-top:.5rem; 290 | font-size:2rem; 291 | outline:none; 292 | border-radius: 1.2rem; 293 | padding: 1rem; 294 | border: .0005rem solid #000; 295 | box-shadow: 0px 3px 17px -8px rgba(112,112,112,0.69); 296 | } 297 | button{ 298 | width:15rem; 299 | height:4rem; 300 | background: var(--color1); 301 | cursor: pointer; 302 | border: none; 303 | box-shadow: 0px 3px 17px -8px rgba(112,112,112,0.69); 304 | border-radius: 1rem; 305 | margin: 1.5rem 0; 306 | font-size:1.6rem; 307 | outline:none; 308 | font-weight: 700; 309 | color: var(--color2); 310 | } 311 | } 312 | .login{ 313 | display:flex; 314 | justify-content: center; 315 | .container{ 316 | width: 60%; 317 | text-align: center; 318 | form{ 319 | display:inline-block; 320 | font-size: 2.5rem; 321 | label{ 322 | } 323 | input{ 324 | width: 35rem; 325 | } 326 | } 327 | } 328 | } 329 | 330 | /* register */ 331 | 332 | .register{ 333 | display:flex; 334 | justify-content: center; 335 | .container{ 336 | width: 60%; 337 | text-align: center; 338 | form{ 339 | display:inline-block; 340 | font-size: 2.5rem; 341 | input{ 342 | width: 35rem; 343 | } 344 | } 345 | } 346 | } 347 | 348 | 349 | /* change password */ 350 | 351 | .change-password{ 352 | display:flex; 353 | justify-content: center; 354 | .container{ 355 | width: 60%; 356 | text-align: center; 357 | form{ 358 | display:inline-block; 359 | font-size: 2.5rem; 360 | input{ 361 | width: 35rem; 362 | } 363 | } 364 | } 365 | } 366 | 367 | /* seminar list */ 368 | 369 | .seminar-list-container{ 370 | display:flex; 371 | background:var(--color2); 372 | color: var(--color1); 373 | justify-content: center; 374 | font-family: Arial, self; 375 | .container{ 376 | width:90%; 377 | .seminar-list{ 378 | text-align:center; 379 | padding-bottom:5rem; 380 | .seminar-list-item:hover{ 381 | opacity: 1.5; 382 | } 383 | .seminar-list-item{ 384 | background:#fff; 385 | display:inline-block; 386 | margin:1rem; 387 | box-shadow: 0px 3px 17px -8px rgba(112,112,112,0.69); 388 | text-align:left; 389 | text-decoration:none; 390 | width:45rem; 391 | height:60rem; 392 | border-radius:1rem; 393 | font-size: 1.8rem; 394 | position:relative; 395 | cursor:pointer; 396 | //border:1px solid black; 397 | a{ 398 | text-decoration:none; 399 | color: var(--color1); 400 | } 401 | .seminar-item-bg{ 402 | background: rgba(0,0,0,0.7); 403 | opacity:0; 404 | z-indexx:2; 405 | width:100%; 406 | height:100%; 407 | position:absolute; 408 | border-radius:1rem; 409 | top:0; 410 | left:0; 411 | transition:all .5s; 412 | .seminar-item-read-more{ 413 | font-size:4rem; 414 | color: white; 415 | font-weight:bold; 416 | position:absolute; 417 | top:50%; 418 | left:50%; 419 | transform:translate(-50%,-50%); 420 | } 421 | } 422 | .seminar-item-bg:hover{ 423 | opacity:1; 424 | transition:all .5s; 425 | } 426 | img{ 427 | width:100%; 428 | height:25rem; 429 | padding:0; 430 | margin:0; 431 | border-top-right-radius: 1rem; 432 | border-top-left-radius: 1rem; 433 | } 434 | .seminar-item-content{ 435 | padding:2rem; 436 | p{ 437 | padding:.8rem 0; 438 | } 439 | } 440 | .seminar-item-about{ 441 | height: 16rem; 442 | } 443 | .seminar-item-about:after{ 444 | content:'....'; 445 | } 446 | .seminar-item-name{ 447 | font-size:2.5rem; 448 | font-weight:bold; 449 | } 450 | .seminar-item-owner,.seminar-item-date,.seminar-item-location{ 451 | font-size:2rem; 452 | font-weight:bold; 453 | font-style:italic; 454 | } 455 | } 456 | } 457 | } 458 | } 459 | 460 | /* Seminar Detail */ 461 | 462 | .seminar-detail{ 463 | display: flex; 464 | justify-content: center; 465 | .container{ 466 | width:60%; 467 | display:inline-block; 468 | .seminar-detail-content{ 469 | padding:.5rem 5rem 4rem 5rem; 470 | border:.1rem solid var(--color1); 471 | .seminar-detail-name{ 472 | background: var(--color1); 473 | color: var(--color2); 474 | font-family: Arial; 475 | padding: .5rem; 476 | text-align: center; 477 | font-size: 4rem; 478 | margin-bottom: 1rem; 479 | } 480 | img{ 481 | width:100%; 482 | } 483 | .seminar-detail-about{ 484 | font-size:2rem; 485 | line-height:3rem; 486 | padding:1rem 0; 487 | } 488 | .seminar-detail-owner, .seminar-detail-date, .seminar-detail-location{ 489 | font-size: 2.5rem; 490 | font-style:italic; 491 | font-weight:bold; 492 | padding:1rem 0; 493 | } 494 | .shadow-map{ 495 | width:100%; 496 | height:30rem; 497 | } 498 | } 499 | 500 | } 501 | } 502 | 503 | /* Seminar Create */ 504 | 505 | .seminar-create{ 506 | display:flex; 507 | justify-content: center; 508 | .container{ 509 | width: 80%; 510 | padding:0 5rem; 511 | form{ 512 | padding-bottom:4rem; 513 | label{ 514 | font-size:2.5rem; 515 | padding-left:3rem; 516 | } 517 | input{ 518 | width:40rem; 519 | } 520 | input[type = "file"]{ 521 | margin:1rem 2rem 522 | } 523 | textarea{ 524 | margin-left:2rem; 525 | width:50rem; 526 | height:15rem; 527 | } 528 | button{ 529 | margin-left:2rem; 530 | } 531 | } 532 | } 533 | } 534 | 535 | 536 | @media screen and (max-width: 992px) { 537 | html { 538 | font-size: 9px; 539 | } 540 | } 541 | @media screen and (max-width: 768px) { 542 | html { 543 | font-size: 8px; 544 | } 545 | } 546 | 547 | @media screen and (max-width: 576px) { 548 | html { 549 | font-size: 6px; 550 | } 551 | } 552 | 553 | 554 | 555 | -------------------------------------------------------------------------------- /src/resources/scss/pagination.scss: -------------------------------------------------------------------------------- 1 | .page{ 2 | position: relative; 3 | float: left; 4 | color: var(--text-black-primary); 5 | padding: 5px 10px; 6 | display: inline-block; 7 | margin: 12px 12px 12px -1px; 8 | cursor: pointer; 9 | border: 1px solid transparent; 10 | border-radius: 4px; 11 | box-sizing: border-box; 12 | 13 | &:hover{ 14 | color: var(--color-primary-main); 15 | border: 1px solid var(--color-border); 16 | } 17 | } 18 | 19 | .active{ 20 | background-color: var(--color-primary-main-button); 21 | color: var(--color-white); 22 | z-index: 2; 23 | 24 | &:hover{ 25 | color: var(--color-white); 26 | } 27 | } 28 | 29 | .pagination{ 30 | font-size: 14px; 31 | display: -webkit-box; 32 | } 33 | 34 | .page-ellipsis { 35 | pointer-events: none; 36 | } 37 | -------------------------------------------------------------------------------- /src/seminars/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/__init__.py -------------------------------------------------------------------------------- /src/seminars/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from .models import Seminar 5 | 6 | admin.site.register(Seminar) -------------------------------------------------------------------------------- /src/seminars/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SeminarsConfig(AppConfig): 5 | name = 'seminars' 6 | -------------------------------------------------------------------------------- /src/seminars/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-04-07 22:53 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Seminar', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('title', models.CharField(max_length=120)), 22 | ('content', models.TextField()), 23 | ('seminar_date', models.DateTimeField()), 24 | ('seminar_place', models.URLField()), 25 | ('organizer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | options={ 28 | 'verbose_name': 'Seminar', 29 | 'verbose_name_plural': 'Seminar', 30 | 'ordering': ['-seminar_date'], 31 | }, 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /src/seminars/migrations/0002_seminar_seminar_photo.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-04-07 23:00 2 | 3 | from django.db import migrations, models 4 | import seminars.models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('seminars', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='seminar', 16 | name='seminar_photo', 17 | field=models.ImageField(blank=True, default='', null=True, upload_to=seminars.models.upload_to), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /src/seminars/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/migrations/__init__.py -------------------------------------------------------------------------------- /src/seminars/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/migrations/__pycache__/0002_seminar_seminar_photo.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/migrations/__pycache__/0002_seminar_seminar_photo.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /src/seminars/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | # Create your models here. 4 | def upload_to(instance,filename): 5 | return '%s/%s/%s'%('seminars',instance.title,filename) 6 | 7 | class Seminar(models.Model): 8 | title = models.CharField(max_length=120) 9 | content=models.TextField() 10 | seminar_photo = models.ImageField(upload_to = upload_to,default='',null=True,blank=True) 11 | organizer = models.ForeignKey(User,on_delete=models.CASCADE) 12 | seminar_date = models.DateTimeField() 13 | seminar_place = models.URLField() 14 | 15 | def __str__(self): 16 | return '%s organized by %s'%(self.title,self.organizer.username) 17 | 18 | class Meta: 19 | ordering = ['-seminar_date'] 20 | verbose_name = 'Seminar' 21 | verbose_name_plural = 'Seminar' -------------------------------------------------------------------------------- /src/seminars/templates/seminar/seminar_create.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Seminar list 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

Seminar Create

12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 | {% endblock %} -------------------------------------------------------------------------------- /src/seminars/templates/seminar/seminar_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Seminar list 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |
12 |

Python Haqqinda Seminar

13 | 14 |

15 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci aspernatur assumenda consectetur debitis dolorem, enim est exercitationem harum ipsum laborum libero magni natus nesciunt, nihil non officia, optio quas quibusdam quis quisquam tempore tenetur unde vel? A accusamus, amet assumenda aut dolores ea eos ex harum inventore laboriosam odio odit omnis, perferendis praesentium tempore velit voluptatem! Autem blanditiis dicta illum ipsum nulla placeat possimus quos ratione recusandae suscipit. A, beatae dolor dolorem enim ex facilis fugit incidunt nobis qui quisquam ratione sequi soluta unde. Alias aliquid animi assumenda, autem consequatur delectus eum, excepturi exercitationem id iure, laudantium minima neque nobis nostrum obcaecati odio officia quis rem sequi suscipit ullam unde veritatis vitae. Asperiores consequatur eius error, ex ipsa magni minima modi nemo odio omnis perspiciatis saepe temporibus voluptas voluptatem voluptatibus. Eligendi nisi, quidem. A atque beatae, eligendi inventore magni, minus nobis officiis perferendis praesentium, quas sapiente sequi sint sit soluta velit! Atque autem consequatur, culpa debitis delectus deserunt dicta enim facere in ipsum libero maiores nesciunt, nulla numquam possimus quas quidem, quo repudiandae sapiente sed sit tempore voluptates! Fugit molestias nostrum quaerat rerum totam. Amet eos officiis placeat porro quod tempore vel voluptatibus! Architecto assumenda atque deleniti deserunt eligendi enim facilis nobis repellendus sunt unde? Alias at aut beatae consequuntur deleniti deserunt earum eius excepturi hic illum impedit ipsam labore laborum minus, nulla omnis pariatur perferendis provident quae quaerat qui repellendus saepe tenetur unde veniam. A alias deserunt, doloremque doloribus dolorum enim eveniet excepturi illum ipsum, itaque nulla quaerat, quasi. Assumenda cum dolore dolorem exercitationem, modi perferendis qui rerum veniam. Aut cum fugiat harum minima minus modi nihil ullam vero. Alias amet doloribus ducimus, ipsam nobis nostrum reiciendis sapiente tempora voluptatem voluptates. Aut commodi cupiditate earum fugiat in obcaecati omnis perferendis quod tempora tenetur, voluptas voluptate voluptatem! Animi iusto, reiciendis. 16 |

17 |

Owner: OwnerName

18 |

Location: INNOLAND

19 |

Date: 12 May 8 pm

20 | 22 |
23 |
24 |
25 | 26 | {% endblock %} -------------------------------------------------------------------------------- /src/seminars/templates/seminar/seminar_edit.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/seminars/templates/seminar/seminar_edit.html -------------------------------------------------------------------------------- /src/seminars/templates/seminar/seminar_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'post/layout.html' %} 2 | 3 | {% block tittle %} 4 | Seminar list 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |

Seminars

11 | 99 |
100 |
101 | 102 | 103 | {% endblock %} -------------------------------------------------------------------------------- /src/seminars/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/seminars/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | app_name = 'seminar' 5 | 6 | urlpatterns = [ 7 | path('', views.seminar_list, name='seminar-list'), 8 | path('create/', views.seminar_create, name='seminar-create'), 9 | path('/', views.seminar_detail, name='seminar-detail'), 10 | path('/edit', views.seminar_edit, name='seminar-edit'), 11 | ] 12 | -------------------------------------------------------------------------------- /src/seminars/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from .models import Seminar 3 | # Create your views here. 4 | 5 | def seminar_list(request): 6 | return render(request,'seminar/seminar_list.html') 7 | 8 | def seminar_create(request): 9 | return render(request,'seminar/seminar_create.html') 10 | 11 | def seminar_detail(request,pk): 12 | return render(request,'seminar/seminar_detail.html') 13 | 14 | def seminar_edit(request,pk): 15 | return render(request,'seminar/seminar_edit.html') 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/static/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore -------------------------------------------------------------------------------- /src/static/css/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore -------------------------------------------------------------------------------- /src/static/css/app.css: -------------------------------------------------------------------------------- 1 | /* all */ 2 | 3 | :root { 4 | --color1: rgba(0, 0, 0, 0.87); 5 | --color2: #eaeaea; 6 | } 7 | 8 | /* http://meyerweb.com/eric/tools/css/reset/ 9 | v5.0.1 | 20191019 10 | License: none (public domain) 11 | */ 12 | 13 | html, 14 | body, 15 | div, 16 | span, 17 | applet, 18 | object, 19 | iframe, 20 | h1, 21 | h2, 22 | h3, 23 | h4, 24 | h5, 25 | h6, 26 | p, 27 | blockquote, 28 | pre, 29 | a, 30 | abbr, 31 | acronym, 32 | address, 33 | big, 34 | cite, 35 | code, 36 | del, 37 | dfn, 38 | em, 39 | img, 40 | ins, 41 | kbd, 42 | q, 43 | s, 44 | samp, 45 | small, 46 | strike, 47 | strong, 48 | sub, 49 | sup, 50 | tt, 51 | var, 52 | b, 53 | u, 54 | i, 55 | center, 56 | dl, 57 | dt, 58 | dd, 59 | menu, 60 | ol, 61 | ul, 62 | li, 63 | fieldset, 64 | form, 65 | label, 66 | legend, 67 | table, 68 | caption, 69 | tbody, 70 | tfoot, 71 | thead, 72 | tr, 73 | th, 74 | td, 75 | article, 76 | aside, 77 | canvas, 78 | details, 79 | embed, 80 | figure, 81 | figcaption, 82 | footer, 83 | header, 84 | hgroup, 85 | main, 86 | menu, 87 | nav, 88 | output, 89 | ruby, 90 | section, 91 | summary, 92 | time, 93 | mark, 94 | audio, 95 | video { 96 | margin: 0; 97 | padding: 0; 98 | border: 0; 99 | font-size: 100%; 100 | font: inherit; 101 | vertical-align: baseline; 102 | } 103 | 104 | /* HTML5 display-role reset for older browsers */ 105 | 106 | article, 107 | aside, 108 | details, 109 | figcaption, 110 | figure, 111 | footer, 112 | header, 113 | hgroup, 114 | main, 115 | menu, 116 | nav, 117 | section { 118 | display: block; 119 | } 120 | 121 | /* HTML5 hidden-attribute fix for newer browsers */ 122 | 123 | *[hidden] { 124 | display: none; 125 | } 126 | 127 | body { 128 | line-height: 1; 129 | } 130 | 131 | menu, 132 | ol, 133 | ul { 134 | list-style: none; 135 | } 136 | 137 | blockquote, 138 | q { 139 | quotes: none; 140 | } 141 | 142 | blockquote:before, 143 | blockquote:after, 144 | q:before, 145 | q:after { 146 | content: ""; 147 | content: none; 148 | } 149 | 150 | table { 151 | border-collapse: collapse; 152 | border-spacing: 0; 153 | } 154 | 155 | html { 156 | font-size: 10px; 157 | } 158 | 159 | body { 160 | font-family: Arial, self; 161 | background: var(--color2); 162 | } 163 | 164 | .page-container { 165 | position: relative; 166 | min-height: 100vh; 167 | } 168 | 169 | .page-container .wrap { 170 | padding-bottom: 54px; 171 | } 172 | 173 | :root, 174 | [data-theme=default] { 175 | --background-default: #f5f5f5; 176 | --background-paper: #fff; 177 | --background-tertiary: #fafbfc; 178 | --background-secondary: #f9f9fb; 179 | --background-header: #fafbfc; 180 | --text-black-primary: rgba(0, 0, 0, 0.87); 181 | --text-black-secondary: #4d4d4d; 182 | --text-black-disabled: #4c4c4e; 183 | --text-white-primary: #fff; 184 | --text-white-secondary: rgba(255, 255, 255, 0.7); 185 | --text-white-disabled: rgba(255, 255, 255, 0.5); 186 | --color-primary-main: #0086b5; 187 | --color-primary-main-button: #428bca; 188 | --color-primary-light: #4791db; 189 | --color-primary-dark: #115293; 190 | --color-secondary-main: #dc004e; 191 | --color-secondary-light: #e33371; 192 | --color-secondary-dark: #9a0036; 193 | --color-error-main: #f44336; 194 | --color-error-light: #e57373; 195 | --color-error-dark: #d32f2f; 196 | --color-warning-main: #ff9800; 197 | --color-warning-light: #ffb74d; 198 | --color-warning-dark: #f57c00; 199 | --color-info-main: #2196f3; 200 | --color-info-light: #64b5f6; 201 | --color-info-dark: #1976d2; 202 | --color-success-main: #4caf50; 203 | --color-success-light: #81c784; 204 | --color-success-dark: #388e3c; 205 | --color-border: #e9e9e9; 206 | --color-border-button: .06rem solid hsla(0, 0%, 0%, 0.2); 207 | --color-header: #757575; 208 | --color-post-title: #212121; 209 | --footer: rgba(255, 255, 255, 0.7); 210 | --color-black: rgba(0, 0, 0, 0.87); 211 | --color-black-secondary: #757575; 212 | --color-white: #fff; 213 | --color-white-secondary: rgba(255, 255, 255, 0.7); 214 | --color-grey: #757575; 215 | --loading-black: #0000002e; 216 | } 217 | 218 | :root, 219 | [data-theme=dark] { 220 | --background-default: #171c20; 221 | --background-paper: #22272b; 222 | --background-tertiary: #757575; 223 | --background-secondary: #1a1e21; 224 | --background-header: #1f2327; 225 | --text-white-primary: rgba(0, 0, 0, 0.87); 226 | --text-white-secondary: #bfbfbf; 227 | --text-white-disabled: rgba(255, 255, 255, 0.3); 228 | --text-black-primary: #fff; 229 | --text-black-secondary: hsla(0,0%,100%,.5); 230 | --text-black-disabled: rgba(255, 255, 255, 0.3); 231 | --color-primary-main: #2093bb; 232 | --color-primary-main-button: #2371c1; 233 | --color-primary-light: #a6d4fa; 234 | --color-primary-dark: #648dae; 235 | --color-secondary-main: #f48fb1; 236 | --color-secondary-light: #f6a5c0; 237 | --color-secondary-dark: #aa647b; 238 | --color-error-main: #f44336; 239 | --color-error-light: #e57373; 240 | --color-error-dark: #d32f2f; 241 | --color-warning-main: #ff9800; 242 | --color-warning-light: #ffb74d; 243 | --color-warning-dark: #f57c00; 244 | --color-info-main: #2196f3; 245 | --color-info-light: #64b5f6; 246 | --color-info-dark: #1976d2; 247 | --color-success-main: #4caf50; 248 | --color-success-light: #81c784; 249 | --color-success-dark: #388e3c; 250 | --color-border: #393d41; 251 | --color-border-button: .06rem solid hsla(0,0%,100%,.2); 252 | --color-header: rgba(255, 255, 255, 0.7); 253 | --color-post-title: #bfbfbf; 254 | --footer: rgba(255, 255, 255, 0.7); 255 | --color-black: rgba(0, 0, 0, 0.87); 256 | --color-black-secondary: #757575; 257 | --color-white: #fff; 258 | --color-white-secondary: rgba(255, 255, 255, 0.7); 259 | --color-grey: #7d7d7d; 260 | --loading-black: #ffffff36; 261 | } 262 | 263 | .header { 264 | display: flex; 265 | align-items: center; 266 | position: relative; 267 | background: var(--color1); 268 | padding: 0 2rem; 269 | } 270 | 271 | .header .logo { 272 | width: 6rem; 273 | height: 6rem; 274 | } 275 | 276 | .header .logo-text { 277 | display: inline-block; 278 | font-size: 2.2rem; 279 | color: var(--color2); 280 | font-family: "Arial Black", serif; 281 | padding: 0 1rem; 282 | } 283 | 284 | .header .menu-list { 285 | color: var(--color2); 286 | margin: 0; 287 | } 288 | 289 | .header .menu-list .list-item { 290 | display: inline-block; 291 | text-decoration: none; 292 | } 293 | 294 | .header .menu-list .list-item a { 295 | display: inline-block; 296 | font-size: 1.5rem; 297 | font-family: Arial, serif; 298 | font-weight: 900; 299 | padding: 2.7rem 1rem; 300 | cursor: pointer; 301 | color: var(--color2); 302 | text-decoration: none; 303 | transition: all 0.4s; 304 | } 305 | 306 | .header .menu-list .list-item a:hover { 307 | background: var(--color2); 308 | color: var(--color1); 309 | transition: all 0.4s; 310 | } 311 | 312 | .header .header-right { 313 | color: var(--color2); 314 | font-size: 1.5rem; 315 | font-family: Arial, serif; 316 | position: absolute; 317 | right: 3rem; 318 | } 319 | 320 | .header .header-language { 321 | padding-right: 3rem; 322 | display: inline-block; 323 | } 324 | 325 | .header .header-language span { 326 | cursor: pointer; 327 | } 328 | 329 | .header .sign-in-location, 330 | .header .sign-up-location { 331 | color: var(--color2); 332 | text-decoration: none; 333 | } 334 | 335 | .footer { 336 | position: absolute; 337 | bottom: 0; 338 | width: 100%; 339 | color: var(--color2); 340 | background: var(--color1); 341 | font-size: 2rem; 342 | font-family: Arial, serif; 343 | display: flex; 344 | align-items: center; 345 | justify-content: center; 346 | } 347 | 348 | .footer .footer-content { 349 | display: flex; 350 | align-items: center; 351 | height: 54px; 352 | } 353 | 354 | .footer .footer-content .copyright-icon { 355 | padding-right: 0.5rem; 356 | } 357 | 358 | .page { 359 | position: relative; 360 | float: left; 361 | color: var(--text-black-primary); 362 | padding: 5px 10px; 363 | display: inline-block; 364 | margin: 12px 12px 12px -1px; 365 | cursor: pointer; 366 | border: 1px solid transparent; 367 | border-radius: 4px; 368 | box-sizing: border-box; 369 | } 370 | 371 | .page:hover { 372 | color: var(--color-primary-main); 373 | border: 1px solid var(--color-border); 374 | } 375 | 376 | .active { 377 | background-color: var(--color-primary-main-button); 378 | color: var(--color-white); 379 | z-index: 2; 380 | } 381 | 382 | .active:hover { 383 | color: var(--color-white); 384 | } 385 | 386 | .pagination { 387 | font-size: 14px; 388 | display: -webkit-box; 389 | } 390 | 391 | .page-ellipsis { 392 | pointer-events: none; 393 | } 394 | 395 | /* show */ 396 | 397 | #show { 398 | background: #eaeaea; 399 | display: flex; 400 | justify-content: center; 401 | font-family: Arial, self, serif; 402 | } 403 | 404 | #show .container { 405 | border: 0.1rem solid var(--color1); 406 | max-width: 85rem; 407 | background: #fff; 408 | position: relative; 409 | padding: 0 7rem; 410 | margin-bottom: 3rem; 411 | padding-bottom: 3rem; 412 | } 413 | 414 | #show .container h1 { 415 | background: black; 416 | color: #fff; 417 | font-family: Arial; 418 | padding: 0.5rem; 419 | text-align: center; 420 | font-size: 4rem; 421 | margin-bottom: 1rem; 422 | } 423 | 424 | #show .container .comment-input { 425 | box-sizing: border-box; 426 | width: 100%; 427 | font-size: 1.6rem; 428 | padding: 1rem; 429 | border-radius: 1.5rem; 430 | outline: none; 431 | font-family: Arial; 432 | height: 10rem; 433 | } 434 | 435 | #show .container .button-container { 436 | text-align: right; 437 | } 438 | 439 | #show .container .comment-button { 440 | width: 15rem; 441 | height: 4rem; 442 | background: black; 443 | color: #fff; 444 | font-size: 2rem; 445 | border-radius: 1rem; 446 | border: none; 447 | margin: 1rem 0; 448 | outline: none; 449 | } 450 | 451 | #show .container .comment-item { 452 | padding: 1.5rem; 453 | font-size: 1.6rem; 454 | border: 0.1rem solid #000; 455 | margin: 1rem; 456 | border-radius: 1.5rem; 457 | } 458 | 459 | #show .show-user { 460 | display: flex; 461 | align-items: center; 462 | padding-bottom: 2rem; 463 | position: relative; 464 | } 465 | 466 | #show .show-user .user-icon { 467 | width: 6rem; 468 | height: 6rem; 469 | border-radius: 50%; 470 | } 471 | 472 | #show .show-user .show-user-container { 473 | font-size: 2rem; 474 | font-family: Arial; 475 | } 476 | 477 | #show .show-user .show-user-name { 478 | display: inline-block; 479 | margin: -0.5rem 0; 480 | } 481 | 482 | #show .show-user .show-user-container span { 483 | padding: 1rem; 484 | } 485 | 486 | #show .show-user .social-icons { 487 | position: absolute; 488 | right: 0; 489 | } 490 | 491 | #show .show-user .social-icons img { 492 | width: 4rem; 493 | height: 4rem; 494 | } 495 | 496 | #show .show-user .social-icons li { 497 | text-decoration: none; 498 | display: inline-block; 499 | padding: 0 0.5rem; 500 | } 501 | 502 | #show .show-user .social-icons li a { 503 | border-radius: 5rem; 504 | } 505 | 506 | #show .show-content img { 507 | width: 100%; 508 | max-height: 50rem; 509 | } 510 | 511 | #show .show-content p { 512 | font-size: 2rem; 513 | font-family: Arial; 514 | line-height: 3rem; 515 | word-spacing: 0.4rem; 516 | padding-top: 1.5rem; 517 | } 518 | 519 | #show .show-bottom { 520 | padding: 2rem 0; 521 | display: flex; 522 | align-items: center; 523 | font-size: 2rem; 524 | font-family: monospace; 525 | } 526 | 527 | #show .show-bottom span { 528 | padding: 0 1rem; 529 | } 530 | 531 | #show .show-bottom .like-dislike-container { 532 | display: inline-block; 533 | display: flex; 534 | align-items: center; 535 | } 536 | 537 | #show .show-bottom img { 538 | width: 3rem; 539 | cursor: pointer; 540 | } 541 | 542 | #show .show-bottom .show-bottom-right { 543 | display: flex; 544 | align-items: center; 545 | position: absolute; 546 | right: 7.5rem; 547 | } 548 | 549 | /* post create */ 550 | 551 | .post-create { 552 | font-family: Arial, self; 553 | display: flex; 554 | background: var(--color2); 555 | } 556 | 557 | .post-create .container { 558 | display: inline-block; 559 | margin: 0 auto; 560 | min-width: 70%; 561 | } 562 | 563 | .post-create .container .post-create-title { 564 | color: var(--color1); 565 | font-size: 5rem; 566 | font-weight: 700; 567 | text-align: center; 568 | padding: 3rem 0; 569 | } 570 | 571 | .post-create .container form { 572 | padding: 5rem 0; 573 | } 574 | 575 | .post-create .container form input { 576 | margin-left: 0; 577 | } 578 | 579 | .post-create .container form label { 580 | font-size: 2rem; 581 | padding: 1.5rem; 582 | } 583 | 584 | .post-create .container form #post-content { 585 | height: 40rem; 586 | width: 80%; 587 | } 588 | 589 | .post-create .container form #post-image { 590 | margin: 1rem 0 1.5rem 0; 591 | } 592 | 593 | .post-input-style { 594 | margin: 1rem 0 3.5rem 0; 595 | width: 50%; 596 | min-width: 40rem; 597 | outline: none; 598 | border-radius: 1rem; 599 | padding: 1.5rem; 600 | border: 0.0005rem solid #000; 601 | box-shadow: 0px 3px 17px -8px rgba(112, 112, 112, 0.69); 602 | font-family: Arial, self; 603 | font-size: 1.6rem; 604 | } 605 | 606 | /* login */ 607 | 608 | .page-title { 609 | color: var(--color1); 610 | font-size: 6rem; 611 | font-weight: 700; 612 | text-align: center; 613 | padding: 3rem 0; 614 | margin-bottom: 2rem; 615 | } 616 | 617 | form input[type=text], 618 | form input[type=password], 619 | form input[type=email] { 620 | margin: 2rem; 621 | margin-top: 0.5rem; 622 | font-size: 2rem; 623 | outline: none; 624 | border-radius: 1.2rem; 625 | padding: 1rem; 626 | border: 0.0005rem solid #000; 627 | box-shadow: 0px 3px 17px -8px rgba(112, 112, 112, 0.69); 628 | } 629 | 630 | form button { 631 | width: 15rem; 632 | height: 4rem; 633 | background: var(--color1); 634 | cursor: pointer; 635 | border: none; 636 | box-shadow: 0px 3px 17px -8px rgba(112, 112, 112, 0.69); 637 | border-radius: 1rem; 638 | margin: 1.5rem 0; 639 | font-size: 1.6rem; 640 | outline: none; 641 | font-weight: 700; 642 | color: var(--color2); 643 | } 644 | 645 | .login { 646 | display: flex; 647 | justify-content: center; 648 | } 649 | 650 | .login .container { 651 | width: 60%; 652 | text-align: center; 653 | } 654 | 655 | .login .container form { 656 | display: inline-block; 657 | font-size: 2.5rem; 658 | } 659 | 660 | .login .container form input { 661 | width: 35rem; 662 | } 663 | 664 | /* register */ 665 | 666 | .register { 667 | display: flex; 668 | justify-content: center; 669 | } 670 | 671 | .register .container { 672 | width: 60%; 673 | text-align: center; 674 | } 675 | 676 | .register .container form { 677 | display: inline-block; 678 | font-size: 2.5rem; 679 | } 680 | 681 | .register .container form input { 682 | width: 35rem; 683 | } 684 | 685 | /* change password */ 686 | 687 | .change-password { 688 | display: flex; 689 | justify-content: center; 690 | } 691 | 692 | .change-password .container { 693 | width: 60%; 694 | text-align: center; 695 | } 696 | 697 | .change-password .container form { 698 | display: inline-block; 699 | font-size: 2.5rem; 700 | } 701 | 702 | .change-password .container form input { 703 | width: 35rem; 704 | } 705 | 706 | /* seminar list */ 707 | 708 | .seminar-list-container { 709 | display: flex; 710 | background: var(--color2); 711 | color: var(--color1); 712 | justify-content: center; 713 | font-family: Arial, self; 714 | } 715 | 716 | .seminar-list-container .container { 717 | width: 90%; 718 | } 719 | 720 | .seminar-list-container .container .seminar-list { 721 | text-align: center; 722 | padding-bottom: 5rem; 723 | } 724 | 725 | .seminar-list-container .container .seminar-list .seminar-list-item:hover { 726 | opacity: 1.5; 727 | } 728 | 729 | .seminar-list-container .container .seminar-list .seminar-list-item { 730 | background: #fff; 731 | display: inline-block; 732 | margin: 1rem; 733 | box-shadow: 0px 3px 17px -8px rgba(112, 112, 112, 0.69); 734 | text-align: left; 735 | text-decoration: none; 736 | width: 45rem; 737 | height: 60rem; 738 | border-radius: 1rem; 739 | font-size: 1.8rem; 740 | position: relative; 741 | cursor: pointer; 742 | } 743 | 744 | .seminar-list-container .container .seminar-list .seminar-list-item a { 745 | text-decoration: none; 746 | color: var(--color1); 747 | } 748 | 749 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-bg { 750 | background: rgba(0, 0, 0, 0.7); 751 | opacity: 0; 752 | z-indexx: 2; 753 | width: 100%; 754 | height: 100%; 755 | position: absolute; 756 | border-radius: 1rem; 757 | top: 0; 758 | left: 0; 759 | transition: all 0.5s; 760 | } 761 | 762 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-bg .seminar-item-read-more { 763 | font-size: 4rem; 764 | color: white; 765 | font-weight: bold; 766 | position: absolute; 767 | top: 50%; 768 | left: 50%; 769 | transform: translate(-50%, -50%); 770 | } 771 | 772 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-bg:hover { 773 | opacity: 1; 774 | transition: all 0.5s; 775 | } 776 | 777 | .seminar-list-container .container .seminar-list .seminar-list-item img { 778 | width: 100%; 779 | height: 25rem; 780 | padding: 0; 781 | margin: 0; 782 | border-top-right-radius: 1rem; 783 | border-top-left-radius: 1rem; 784 | } 785 | 786 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-content { 787 | padding: 2rem; 788 | } 789 | 790 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-content p { 791 | padding: 0.8rem 0; 792 | } 793 | 794 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-about { 795 | height: 16rem; 796 | } 797 | 798 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-about:after { 799 | content: "...."; 800 | } 801 | 802 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-name { 803 | font-size: 2.5rem; 804 | font-weight: bold; 805 | } 806 | 807 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-owner, 808 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-date, 809 | .seminar-list-container .container .seminar-list .seminar-list-item .seminar-item-location { 810 | font-size: 2rem; 811 | font-weight: bold; 812 | font-style: italic; 813 | } 814 | 815 | /* Seminar Detail */ 816 | 817 | .seminar-detail { 818 | display: flex; 819 | justify-content: center; 820 | } 821 | 822 | .seminar-detail .container { 823 | width: 60%; 824 | display: inline-block; 825 | } 826 | 827 | .seminar-detail .container .seminar-detail-content { 828 | padding: 0.5rem 5rem 4rem 5rem; 829 | border: 0.1rem solid var(--color1); 830 | } 831 | 832 | .seminar-detail .container .seminar-detail-content .seminar-detail-name { 833 | background: var(--color1); 834 | color: var(--color2); 835 | font-family: Arial; 836 | padding: 0.5rem; 837 | text-align: center; 838 | font-size: 4rem; 839 | margin-bottom: 1rem; 840 | } 841 | 842 | .seminar-detail .container .seminar-detail-content img { 843 | width: 100%; 844 | } 845 | 846 | .seminar-detail .container .seminar-detail-content .seminar-detail-about { 847 | font-size: 2rem; 848 | line-height: 3rem; 849 | padding: 1rem 0; 850 | } 851 | 852 | .seminar-detail .container .seminar-detail-content .seminar-detail-owner, 853 | .seminar-detail .container .seminar-detail-content .seminar-detail-date, 854 | .seminar-detail .container .seminar-detail-content .seminar-detail-location { 855 | font-size: 2.5rem; 856 | font-style: italic; 857 | font-weight: bold; 858 | padding: 1rem 0; 859 | } 860 | 861 | .seminar-detail .container .seminar-detail-content .shadow-map { 862 | width: 100%; 863 | height: 30rem; 864 | } 865 | 866 | /* Seminar Create */ 867 | 868 | .seminar-create { 869 | display: flex; 870 | justify-content: center; 871 | } 872 | 873 | .seminar-create .container { 874 | width: 80%; 875 | padding: 0 5rem; 876 | } 877 | 878 | .seminar-create .container form { 879 | padding-bottom: 4rem; 880 | } 881 | 882 | .seminar-create .container form label { 883 | font-size: 2.5rem; 884 | padding-left: 3rem; 885 | } 886 | 887 | .seminar-create .container form input { 888 | width: 40rem; 889 | } 890 | 891 | .seminar-create .container form input[type=file] { 892 | margin: 1rem 2rem; 893 | } 894 | 895 | .seminar-create .container form textarea { 896 | margin-left: 2rem; 897 | width: 50rem; 898 | height: 15rem; 899 | } 900 | 901 | .seminar-create .container form button { 902 | margin-left: 2rem; 903 | } 904 | 905 | @media screen and (max-width: 992px) { 906 | html { 907 | font-size: 9px; 908 | } 909 | } 910 | 911 | @media screen and (max-width: 768px) { 912 | html { 913 | font-size: 8px; 914 | } 915 | } 916 | 917 | @media screen and (max-width: 576px) { 918 | html { 919 | font-size: 6px; 920 | } 921 | } 922 | 923 | -------------------------------------------------------------------------------- /src/static/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/static/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /src/static/fonts/materialdesignicons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/static/fonts/materialdesignicons-webfont.woff -------------------------------------------------------------------------------- /src/static/fonts/materialdesignicons-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/static/fonts/materialdesignicons-webfont.woff2 -------------------------------------------------------------------------------- /src/static/img/copyright.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | c 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/static/img/dislike.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/static/img/facebook-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/static/img/github-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 11 | 13 | 15 | 35 | 37 | 39 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/static/img/like.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/static/img/linkedin-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/static/img/pcw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/static/img/pcw.png -------------------------------------------------------------------------------- /src/static/img/share-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/static/img/show-img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/static/img/show-img.jpg -------------------------------------------------------------------------------- /src/static/img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/static/img/user.png -------------------------------------------------------------------------------- /src/static/js/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore -------------------------------------------------------------------------------- /src/static/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css" 4 | } 5 | -------------------------------------------------------------------------------- /src/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/user/__init__.py -------------------------------------------------------------------------------- /src/user/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import UserProfile 3 | 4 | 5 | 6 | admin.site.register(UserProfile) -------------------------------------------------------------------------------- /src/user/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserConfig(AppConfig): 5 | name = 'user' 6 | -------------------------------------------------------------------------------- /src/user/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth import authenticate 3 | from django.contrib.auth.models import User 4 | from .models import UserProfile 5 | 6 | class RegisterForm(forms.ModelForm): 7 | password = forms.CharField(min_length=5, required=True , label = "Password" ,widget = forms.PasswordInput(attrs={'class':'form-control'})) 8 | password_confirm = forms.CharField(min_length=5 , label = "Password Confirm" ,widget = forms.PasswordInput(attrs={'class':'form-control'})) 9 | username = forms.CharField(max_length=30, min_length=3,label='Username') 10 | 11 | class Meta: 12 | model = User 13 | fields = ['first_name', 'last_name','username','email','password','password_confirm'] 14 | 15 | def __init__(self, *args, **kwargs): 16 | super(RegisterForm, self).__init__(*args, **kwargs) 17 | for field in self.fields: 18 | self.fields[field].widget.attrs = {'class': 'form-control'} 19 | self.fields['first_name'].required = True 20 | self.fields['last_name'].required = True 21 | self.fields['email'].required = True 22 | 23 | def clean(self): 24 | password = self.cleaned_data.get('password') 25 | password_confirm = self.cleaned_data.get('password_confirm') 26 | if password and password_confirm and password != password_confirm : 27 | raise forms.ValidationError('Şifrələr uyğun gəlmir!') 28 | 29 | def clean_email(self): 30 | email = self.cleaned_data.get('email') 31 | email = email.lower() 32 | if User.objects.filter(email = email).exists(): 33 | raise forms.ValidationError('Belə bir email mövcuddur') 34 | return email 35 | 36 | def clean_username(self): 37 | username = self.cleaned_data.get('username') 38 | if User.objects.filter(username = username).exists(): 39 | raise forms.ValidationError('Belə bir username mövcuddur') 40 | return username 41 | 42 | class LoginForm(forms.Form): 43 | username = forms.CharField(required=True, label="Username:",widget=forms.TextInput(attrs={'class': "form-control"})) 44 | password = forms.CharField(required=True, label="Password:",widget=forms.PasswordInput(attrs={'class': 'form-control'})) 45 | 46 | class UserProfileUpdateForm(forms.ModelForm): 47 | username = forms.CharField(max_length=30, min_length=3,label='Username') 48 | avatar = forms.ImageField(required=False, label="Avatar", help_text='Şəkil yüklə') 49 | about = forms.CharField(widget=forms.Textarea, required=False, label="About") 50 | 51 | class Meta: 52 | model = User 53 | fields = ['first_name', 'last_name', 'username', "email", 'avatar', 'about'] 54 | 55 | def __init__(self, *args, **kwargs): 56 | super(UserProfileUpdateForm, self).__init__(*args, **kwargs) 57 | for field in self.fields: 58 | self.fields[field].widget.attrs = {'class': 'form-control'} 59 | self.fields['about'].widget.attrs['rows'] = 8 60 | 61 | def clean_email(self): 62 | email = self.cleaned_data.get("email", None) 63 | if not email: 64 | raise forms.ValidationError("Email daxil edin") 65 | 66 | if User.objects.filter(email=email).exclude(username=self.instance.username).exists(): 67 | raise forms.ValidationError("Belə bir email adressi mövcuddur") 68 | return email 69 | 70 | class UserPasswordChangeForm(forms.Form): 71 | user = None 72 | old_password = forms.CharField(required=True,min_length=5,label='Password',widget=forms.PasswordInput(attrs={'class':'form-control'})) 73 | new_password = forms.CharField(required=True,min_length=5,label='New Password',widget=forms.PasswordInput(attrs={'class':'form-control'})) 74 | new_password_confirm = forms.CharField(required=True,min_length=5,label='New Password Confirm',widget=forms.PasswordInput(attrs={'class':'form-control'})) 75 | 76 | def __init__(self, user, *args, **kwargs): 77 | self.user = user 78 | super(UserPasswordChangeForm, self).__init__(*args, **kwargs) 79 | 80 | def clean(self): 81 | new_password = self.cleaned_data.get('new_password') 82 | new_password_confirm = self.cleaned_data.get('new_password_confirm') 83 | if new_password != new_password_confirm: 84 | self.add_error("new_password_confirm", 'Yeni şifrələr uygunlaşmadı') 85 | 86 | def clean_old_password(self): 87 | old_password = self.cleaned_data.get('old_password') 88 | if self.user.check_password(old_password): 89 | return old_password 90 | else: 91 | raise forms.ValidationError('Doğru şifrə qeyd edin') 92 | 93 | -------------------------------------------------------------------------------- /src/user/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-26 23:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Register', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('avatar', models.FileField(blank=True, null=True, upload_to='avatar', verbose_name='Profile Photo')), 19 | ('username', models.CharField(max_length=50, verbose_name='Username')), 20 | ('name', models.CharField(max_length=40, verbose_name='Name')), 21 | ('surname', models.CharField(max_length=40, verbose_name='Surname')), 22 | ('about', models.TextField(max_length=1000, verbose_name='About')), 23 | ('rating', models.IntegerField(verbose_name='Rating')), 24 | ('email', models.EmailField(max_length=80, verbose_name='Email')), 25 | ('email_verified', models.DateTimeField(blank=True, null=True)), 26 | ('password', models.CharField(max_length=40, verbose_name='Password')), 27 | ('remember_token', models.CharField(blank=True, max_length=50, null=True)), 28 | ('created_at', models.DateTimeField(auto_now_add=True)), 29 | ('updated_at', models.DateTimeField(auto_now=True)), 30 | ], 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /src/user/migrations/0002_auto_20200410_1021.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.7 on 2020-04-10 06:21 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('user', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='UserProfile', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('avatar', models.ImageField(blank=True, null=True, upload_to='avatar', verbose_name='Profile Photo')), 21 | ('about', models.TextField(blank=True, max_length=1000, verbose_name='About')), 22 | ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), 23 | ], 24 | options={ 25 | 'verbose_name_plural': 'User Profile', 26 | }, 27 | ), 28 | migrations.DeleteModel( 29 | name='Register', 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /src/user/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devhub-az/python_web_site/fd3b6f8d22998d47b0b1e2c74be742a5abd9f5bf/src/user/migrations/__init__.py -------------------------------------------------------------------------------- /src/user/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.db.models.signals import post_save 4 | from django.dispatch import receiver 5 | from rest_framework.authtoken.models import Token 6 | 7 | 8 | class UserProfile(models.Model): 9 | user = models.OneToOneField(User,on_delete=models.CASCADE,null=True,blank=False,verbose_name='User') 10 | avatar = models.ImageField(upload_to='avatar',blank = True,null= True,verbose_name='Profile Photo') 11 | about = models.TextField(max_length=1000,verbose_name='About',blank=True) 12 | class Meta: 13 | verbose_name_plural = 'User Profile' 14 | 15 | def __str__(self): 16 | return self.user.username 17 | 18 | def getAvatar(self): 19 | if self.avatar: 20 | return self.avatar.url 21 | else: 22 | return '/static/images/profile_images/default/default.jpg' 23 | 24 | def create_profile(sender, created, instance, **kwargs): 25 | if created: 26 | UserProfile.objects.create(user=instance) 27 | 28 | post_save.connect(create_profile, sender=User) 29 | 30 | @receiver (post_save, sender=User) 31 | def create_user_token(sender, instance=None, created=False, **kwargs): 32 | if created: 33 | Token.objects.create(user=instance) 34 | -------------------------------------------------------------------------------- /src/user/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/user/urls.py: -------------------------------------------------------------------------------- 1 | 2 | from django.urls import path 3 | from . import views 4 | 5 | app_name = 'user' 6 | 7 | 8 | urlpatterns = [ 9 | path('register/',views.user_register,name='register'), 10 | path('login/',views.user_login,name='login'), 11 | path('settings/', views.user_settings,name='settings'), 12 | path('logout/', views.user_logout, name='logout'), 13 | path('passwordchange/',views.user_password_change,name='passwordchange'), 14 | ] 15 | -------------------------------------------------------------------------------- /src/user/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render,HttpResponse,redirect 2 | from .forms import RegisterForm,LoginForm,UserProfileUpdateForm,UserPasswordChangeForm 3 | from django.contrib.auth import authenticate, login ,logout,update_session_auth_hash 4 | from django.contrib import messages 5 | 6 | 7 | def user_register(request): 8 | form = RegisterForm(request.POST or None) 9 | if request.method == 'POST': 10 | if form.is_valid(): 11 | user = form.save(commit = False) 12 | password = form.cleaned_data.get('password') 13 | user.set_password(password) 14 | user.save() 15 | login(request,user) 16 | messages.success(request,'Ugurla qeydiyyatdan keçdiniz') 17 | return redirect('post:main-page') 18 | return render(request,'user/register.html',{'form':form}) 19 | 20 | def user_login(request): 21 | form = LoginForm(request.POST or None) 22 | if request.method == 'POST': 23 | if form.is_valid(): 24 | username = form.cleaned_data.get('username') 25 | password = form.cleaned_data.get('password') 26 | user = authenticate(username= username,password= password) 27 | if user is None: 28 | print('Username ve ya password yalnisdir') 29 | return render(request,'user/login.html',{'form':form}) 30 | messages.success(request,'Ugurla daxil oldunuz ') 31 | return redirect('post:main-page') 32 | return render(request,'user/login.html',{'form':form}) 33 | 34 | def user_settings(request): 35 | about = request.user.userprofile.about 36 | avatar = request.user.userprofile.avatar 37 | initial = {'about': about , 'avatar': avatar} 38 | form = UserProfileUpdateForm(initial= initial,instance=request.user ,data=request.POST or None,files=request.FILES or None) 39 | if request.method == 'POST': 40 | if form.is_valid(): 41 | user = form.save(commit=True) 42 | about = form.cleaned_data.get('about',None) 43 | avatar = form.cleaned_data.get('avatar',None) 44 | user.userprofile.about = about 45 | user.userprofile.avatar = avatar 46 | user.userprofile.save() 47 | print('Burada mesaj gonderilecek') 48 | return HttpResponse('Burada yonlendirilecek link bilinecek') 49 | else: 50 | print('Melumatlarin dogru olduguna emin olun') 51 | return render(request,'user/settings.html',{'form':form}) 52 | 53 | def user_password_change(request): 54 | form = UserPasswordChangeForm(user= request.user,data= request.POST or None) 55 | if form.is_valid(): 56 | new_password = form.cleaned_data.get('new_password') 57 | request.user.set_password(new_password) 58 | request.user.save() 59 | update_session_auth_hash(request,request.user) 60 | print('Burada sizin mesajiniz') 61 | return HttpResponse('burada kecid linki') 62 | return render(request,'user/password_change.html',{'form':form}) 63 | 64 | def user_logout(request): 65 | logout(request) 66 | print('burada sizin mesajiniz') 67 | return redirect('post:main-page') 68 | 69 | -------------------------------------------------------------------------------- /src/webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix'); 2 | 3 | let staticPath = 'static' 4 | let resourcesPath = 'resources' 5 | 6 | mix.setResourceRoot('/static/') // setResroucesRoots add prefix to url() in scss on example: from /images/close.svg to /static/images/close.svg 7 | 8 | mix.setPublicPath('static') // Path where mix-manifest.json is created 9 | 10 | // if you don't need browser-sync feature you can remove this lines 11 | if (process.argv.includes('--browser-sync')) { 12 | mix.browserSync('localhost:8000') 13 | } 14 | 15 | // Now you can use full mix api 16 | mix.js(`${resourcesPath}/js/app.js`, `${staticPath}/js`) 17 | mix.sass(`${resourcesPath}/scss/app.scss`, `${staticPath}/css`) 18 | 19 | mix.disableNotifications() 20 | .sourceMaps() --------------------------------------------------------------------------------