parent
eacda197ac
commit
21f4ca8713
@ -0,0 +1,112 @@ |
||||
# Byte-compiled / optimized / DLL files |
||||
__pycache__/ |
||||
*.py[cod] |
||||
*$py.class |
||||
|
||||
# C extensions |
||||
*.so |
||||
|
||||
# Distribution / packaging |
||||
.Python |
||||
build/ |
||||
develop-eggs/ |
||||
dist/ |
||||
downloads/ |
||||
eggs/ |
||||
.eggs/ |
||||
lib/ |
||||
lib64/ |
||||
parts/ |
||||
sdist/ |
||||
var/ |
||||
wheels/ |
||||
*.egg-info/ |
||||
.installed.cfg |
||||
*.egg |
||||
MANIFEST |
||||
|
||||
# PyInstaller |
||||
# Usually these files are written by a python script from a template |
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it. |
||||
*.manifest |
||||
*.spec |
||||
|
||||
# Installer logs |
||||
pip-log.txt |
||||
pip-delete-this-directory.txt |
||||
|
||||
# Unit test / coverage reports |
||||
htmlcov/ |
||||
.tox/ |
||||
.coverage |
||||
.coverage.* |
||||
.cache |
||||
nosetests.xml |
||||
coverage.xml |
||||
*.cover |
||||
.hypothesis/ |
||||
.pytest_cache/ |
||||
|
||||
# Translations |
||||
*.mo |
||||
*.pot |
||||
|
||||
# Django stuff: |
||||
*.log |
||||
local_settings.py |
||||
db.sqlite3 |
||||
|
||||
# Flask stuff: |
||||
instance/ |
||||
.webassets-cache |
||||
|
||||
# Scrapy stuff: |
||||
.scrapy |
||||
|
||||
# Sphinx documentation |
||||
docs/_build/ |
||||
|
||||
# PyBuilder |
||||
target/ |
||||
|
||||
# Jupyter Notebook |
||||
.ipynb_checkpoints |
||||
|
||||
# pyenv |
||||
.python-version |
||||
|
||||
# celery beat schedule file |
||||
celerybeat-schedule |
||||
|
||||
# SageMath parsed files |
||||
*.sage.py |
||||
|
||||
# Environments |
||||
.env |
||||
.venv |
||||
env/ |
||||
venv/ |
||||
ENV/ |
||||
env.bak/ |
||||
venv.bak/ |
||||
|
||||
# Spyder project settings |
||||
.spyderproject |
||||
.spyproject |
||||
|
||||
# Rope project settings |
||||
.ropeproject |
||||
|
||||
# mkdocs documentation |
||||
/site |
||||
|
||||
# mypy |
||||
.mypy_cache/ |
||||
*.secret |
||||
|
||||
|
||||
.bumpversion.cfg |
||||
|
||||
.bandit |
||||
|
||||
*.sqlite* |
@ -0,0 +1,13 @@ |
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
Version 2, December 2004 |
||||
|
||||
Copyright (C) 2021 FoxMaSk <foxmask+wtfpl@pm.me> |
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified |
||||
copies of this license document, and changing it is allowed as long |
||||
as the name is changed. |
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO. |
@ -1,3 +1,107 @@ |
||||
# shaarpy |
||||
# ShaaPy |
||||
|
||||
Shaarli in Python :P |
||||
The very nice Shaarli (https://sebsauvage.net/wiki/doku.php?id=php:shaarli) 'cloned' in Python |
||||
|
||||
Save your notes and links to share ... or not ;) |
||||
|
||||
 |
||||
|
||||
## Installation |
||||
## :package: Installation |
||||
|
||||
### Requirements |
||||
|
||||
* Python from 3.8 to 3.10 |
||||
* Django 4.0 |
||||
* pandoc |
||||
|
||||
### Installation |
||||
|
||||
pandoc |
||||
|
||||
```bash |
||||
sudo apt install pandoc |
||||
``` |
||||
|
||||
create a virtualenv |
||||
|
||||
```bash |
||||
python3 -m venv shaarpy |
||||
cd shaarpy |
||||
source bin/activate |
||||
``` |
||||
|
||||
install the project |
||||
|
||||
```bash |
||||
git clone https://framagit.org/foxmask/shaarpy |
||||
cd shaarpy |
||||
``` |
||||
|
||||
## :wrench: Settings |
||||
|
||||
copy the sample config file |
||||
|
||||
```bash |
||||
cp env.sample .env |
||||
``` |
||||
|
||||
and set the following values |
||||
|
||||
```ini |
||||
SHAARPY_NAME=Home Sweet Links |
||||
SHAARPY_DESCRIPTION=Links, tech links, life links |
||||
|
||||
SECRET=!DONTFORGETTOCHANGETHISVALUE! |
||||
|
||||
DEBUG=True # or False in prod |
||||
DB_ENGINE='django.db.backends.sqlite3' |
||||
DB_NAME='db.sqlite3' |
||||
DB_USER='' |
||||
DB_PASSWORD='' |
||||
DB_HOST='' |
||||
DB_PORT='' |
||||
|
||||
TIME_ZONE='Europe/Paris' |
||||
LANGUAGE_CODE='en-en' |
||||
USE_I18N=True |
||||
USE_L10N=True |
||||
USE_TZ=True |
||||
|
||||
SECRET_KEY=!TOBEDEFINED! |
||||
``` |
||||
|
||||
## :dvd: Database |
||||
|
||||
setup the database |
||||
|
||||
```bash |
||||
cd shaarpy |
||||
python manage.py createsuperuser |
||||
python manage.py migrate |
||||
``` |
||||
|
||||
## :mega: Running the Server |
||||
### start the project |
||||
|
||||
```bash |
||||
python manage.py runserver localhost:8001 |
||||
``` |
||||
|
||||
then, access the project with your browser http://127.0.0.1:8001/ |
||||
|
||||
## Usage |
||||
|
||||
### Add a new link |
||||
|
||||
 |
||||
|
||||
### Tags list |
||||
|
||||
 |
||||
|
||||
to easyly find links by tags |
||||
|
||||
### Daily links |
||||
|
||||
 |
||||
|
@ -0,0 +1 @@ |
||||
0.1.0 |
After Width: | Height: | Size: 168 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,22 @@ |
||||
#!/usr/bin/env python |
||||
"""Django's command-line utility for administrative tasks.""" |
||||
import os |
||||
import sys |
||||
|
||||
|
||||
def main(): |
||||
"""Run administrative tasks.""" |
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shaarpy.settings') |
||||
try: |
||||
from django.core.management import execute_from_command_line |
||||
except ImportError as exc: |
||||
raise ImportError( |
||||
"Couldn't import Django. Are you sure it's installed and " |
||||
"available on your PYTHONPATH environment variable? Did you " |
||||
"forget to activate a virtual environment?" |
||||
) from exc |
||||
execute_from_command_line(sys.argv) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,4 @@ |
||||
Django==4.0.1 |
||||
django-environ==0.8.1 |
||||
newspaper3k==0.2.8 |
||||
pypandoc==1.7.2 |
@ -0,0 +1,44 @@ |
||||
[metadata] |
||||
name=shaarpy |
||||
description="Shaarli in Python" |
||||
author=ํญ์ค๋ง์คํฌ |
||||
author_email=foxmask+git@pm.me |
||||
url=https://framagit.org/foxmask/shaarpy |
||||
long_description=file: README.md |
||||
long_description_content_type=text/markdown |
||||
license=WTFPL |
||||
keywords=python starlette |
||||
version=file: VERSION.txt |
||||
|
||||
classifiers= |
||||
Development Status :: 4 - Beta |
||||
Environment :: Web Environment |
||||
License :: Public Domain |
||||
Operating System :: OS Independent |
||||
Programming Language :: Python :: 3.8 |
||||
Programming Language :: Python :: 3.9 |
||||
Programming Language :: Python :: 3.10 |
||||
Topic :: Internet |
||||
Topic :: Communications |
||||
Topic :: Database |
||||
|
||||
[options] |
||||
zip_safe=false |
||||
|
||||
include_package_data=true |
||||
|
||||
install_requires= |
||||
Django==4.0.1 |
||||
django-environ==0.8.1 |
||||
newspaper3k==0.2.8 |
||||
pypandoc==1.7.2 |
||||
|
||||
|
||||
[options.packages.find] |
||||
exclude= |
||||
tests |
||||
|
||||
[flake8] |
||||
max-line-length=120 |
||||
exclude=.tox,build |
||||
ignore = E402, F401 |
@ -0,0 +1,16 @@ |
||||
""" |
||||
ASGI config for shaarpy project. |
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``. |
||||
|
||||
For more information on this file, see |
||||
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ |
||||
""" |
||||
|
||||
import os |
||||
|
||||
from django.core.asgi import get_asgi_application |
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shaarpy.settings') |
||||
|
||||
application = get_asgi_application() |
@ -0,0 +1,19 @@ |
||||
SHAARPY_NAME=Home Sweet Links |
||||
SHAARPY_DESCRIPTION=Links, tech links, life links |
||||
|
||||
SECRET=!DONTFORGETTOCHANGETHISVALUE! |
||||
|
||||
DEBUG=True |
||||
ALLOWED_HOSTS='127.0.0.1,localhost' |
||||
DB_ENGINE='django.db.backends.sqlite3' |
||||
DB_NAME='db.sqlite3' |
||||
DB_USER='' |
||||
DB_PASSWORD='' |
||||
DB_HOST='' |
||||
DB_PORT='' |
||||
|
||||
TIME_ZONE='Europe/Paris' |
||||
LANGUAGE_CODE='en-en' |
||||
USE_I18N=True |
||||
USE_L10N=True |
||||
USE_TZ=True |
@ -0,0 +1,52 @@ |
||||
# coding: utf-8 |
||||
""" |
||||
ShaarPy |
||||
""" |
||||
from django.contrib.auth.models import User |
||||
from django.utils.translation import gettext_lazy as _ |
||||
from django.forms import ModelForm, TextInput, Textarea, CheckboxInput, PasswordInput, EmailInput |
||||
|
||||
from shaarpy.models import Links |
||||
|
||||
|
||||
class LinksForm(ModelForm): |
||||
|
||||
class Meta: |
||||
model = Links |
||||
fields = ('url', 'title', 'text', 'tags', 'private') |
||||
widgets = { |
||||
'tags': TextInput(attrs={'class': 'form-control'}), |
||||
'url': TextInput(attrs={'class': 'form-control', 'placeholder': _('URL or leave if empty for creating a note')}), |
||||
'title': TextInput(attrs={'class': 'form-control', 'placeholder': _('Note:')}), |
||||
'text': Textarea(attrs={'class': 'form-control', 'placeholder': _('content')}), |
||||
'private': CheckboxInput(attrs={'class': 'form-check-input'}), |
||||
} |
||||
|
||||
|
||||
#class LoginForm(ModelForm): |
||||
# """ |
||||
# Form to manage the login page |
||||
# """ |
||||
# class Meta: |
||||
# model = User |
||||
# fields = ('username', 'password') |
||||
# widgets = { |
||||
# 'username': TextInput(attrs={'class': 'form-control', 'placeholder': 'Username'}), |
||||
# 'password': PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Password'}), |
||||
# } |
||||
|
||||
|
||||
class MeForm(ModelForm): |
||||
|
||||
""" |
||||
form to edit its profile |
||||
""" |
||||
|
||||
class Meta: |
||||
|
||||
model = User |
||||
fields = ('email', 'last_name') |
||||
widgets = { |
||||
'last_name': TextInput(attrs={'class': 'form-control', 'placeholder': _('Last name')}), |
||||
'email': EmailInput(attrs={'class': 'form-control', 'placeholder': _('Email')}), |
||||
} |
@ -0,0 +1,31 @@ |
||||
# Generated by Django 4.0.1 on 2022-01-10 19:59 |
||||
|
||||
import datetime |
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
initial = True |
||||
|
||||
dependencies = [ |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='Links', |
||||
fields=[ |
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
('tags', models.CharField(blank=True, max_length=255, null=True)), |
||||
('url', models.URLField(blank=True, max_length=2048, null=True)), |
||||
('title', models.CharField(max_length=255, blank=True, null=True)), |
||||
('text', models.TextField(blank=True, null=True)), |
||||
('private', models.BooleanField(default=False)), |
||||
('date_created', models.DateTimeField(default=datetime.datetime.now)), |
||||
], |
||||
options={ |
||||
'verbose_name_plural': 'Links', |
||||
'ordering': ['-date_created'], |
||||
}, |
||||
), |
||||
] |
@ -0,0 +1,24 @@ |
||||
# coding: utf-8 |
||||
""" |
||||
ShaarPy |
||||
""" |
||||
import datetime |
||||
from django.db import models |
||||
from django.urls import reverse |
||||
|
||||
|
||||
class Links(models.Model): |
||||
tags = models.CharField(max_length=255, null=True, blank=True) |
||||
url = models.URLField(max_length=2048, null=True, blank=True) |
||||
title = models.CharField(max_length=255, null=True, blank=True) |
||||
text = models.TextField(null=True, blank=True) |
||||
private = models.BooleanField(default=False) |
||||
date_created = models.DateTimeField(default=datetime.datetime.now) |
||||
|
||||
class Meta: |
||||
verbose_name_plural = "Links" |
||||
ordering = ['-date_created'] |
||||
|
||||
def get_absolute_url(self): |
||||
return reverse('home') |
||||
|
@ -0,0 +1,140 @@ |
||||
""" |
||||
Django settings for shaarpy project. |
||||
|
||||
Generated by 'django-admin startproject' using Django 4.0.1. |
||||
|
||||
For more information on this file, see |
||||
https://docs.djangoproject.com/en/4.0/topics/settings/ |
||||
|
||||
For the full list of settings and their values, see |
||||
https://docs.djangoproject.com/en/4.0/ref/settings/ |
||||
""" |
||||
# from django.urls import reverse |
||||
from pathlib import Path |
||||
import environ |
||||
import os |
||||
|
||||
env = environ.Env() |
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'. |
||||
BASE_DIR = Path(__file__).resolve().parent.parent |
||||
|
||||
env_file_path = os.path.join(BASE_DIR, 'shaarpy', '.env') |
||||
env_file = environ.Env.read_env(env_file_path) |
||||
env.read_env(env_file) |
||||
|
||||
# Quick-start development settings - unsuitable for production |
||||
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ |
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret! |
||||
SECRET_KEY = env.str('SECRET_KEY', default='!DONTFORGETTOCHANGETHISVALUE!') |
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production! |
||||
DEBUG = env.bool('DEBUG', default=False) # set to False when using in production |
||||
|
||||
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['127.0.0.1', 'localhost']) |
||||
|
||||
# Application definition |
||||
|
||||
INSTALLED_APPS = [ |
||||
'django.contrib.admin', |
||||
'django.contrib.auth', |
||||
'django.contrib.contenttypes', |
||||
'django.contrib.sessions', |
||||
'django.contrib.messages', |
||||
'django.contrib.staticfiles', |
||||
'shaarpy', |
||||
] |
||||
|
||||
MIDDLEWARE = [ |
||||
'django.middleware.security.SecurityMiddleware', |
||||
'django.contrib.sessions.middleware.SessionMiddleware', |
||||
'django.middleware.common.CommonMiddleware', |
||||
'django.middleware.csrf.CsrfViewMiddleware', |
||||
'django.contrib.auth.middleware.AuthenticationMiddleware', |
||||
'django.contrib.messages.middleware.MessageMiddleware', |
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware', |
||||
] |
||||
|
||||
ROOT_URLCONF = 'shaarpy.urls' |
||||
|
||||
TEMPLATES = [ |
||||
{ |
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates', |
||||
'DIRS': [], |
||||
'APP_DIRS': True, |
||||
'OPTIONS': { |
||||
'context_processors': [ |
||||
'django.template.context_processors.debug', |
||||
'django.template.context_processors.request', |
||||
'django.contrib.auth.context_processors.auth', |
||||
'django.contrib.messages.context_processors.messages', |
||||
], |
||||
}, |
||||
}, |
||||
] |
||||
|
||||
WSGI_APPLICATION = 'shaarpy.wsgi.application' |
||||
|
||||
|
||||
# Database |
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases |
||||
|
||||
DATABASES = { |
||||
'default': { |
||||
'ENGINE': env.str('DB_ENGINE', default='django.db.backends.sqlite3'), |
||||
'NAME': env.str('DB_NAME', default=BASE_DIR / 'db.sqlite3'), |
||||
'USER': env.str('DB_USER', default=''), |
||||
'PASSWORD': env.str('DB_PASSWORD', default=''), |
||||
'HOST': env.str('DB_HOST', default=''), |
||||
'PORT': env.str('DB_PORT', default=''), |
||||
}, |
||||
'TEST': { |
||||
'ENGINE': 'django.db.backends.sqlite3', |
||||
'NAME': BASE_DIR / 'nyuseu-test.sqlite3', |
||||
} |
||||
} |
||||
|
||||
|
||||
# Password validation |
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators |
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [ |
||||
{ |
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', |
||||
}, |
||||
{ |
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', |
||||
}, |
||||
{ |
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', |
||||
}, |
||||
{ |
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', |
||||
}, |
||||
] |
||||
|
||||
|
||||
# Internationalization |
||||
# https://docs.djangoproject.com/en/4.0/topics/i18n/ |
||||
LANGUAGE_CODE = env.str('LANGUAGE_CODE', default='en-en') |
||||
TIME_ZONE = env.str('TIME_ZONE', default='UTC') |
||||
USE_I18N = env.bool('USE_I18N', default=True) |
||||
USE_L10N = env.bool('USE_L10N', default=True) |
||||
USE_TZ = env.bool('USE_TZ', default=True) |
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images) |
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/ |
||||
|
||||
STATIC_URL = 'static/' |
||||
STATIC_ROOT = BASE_DIR / 'static' |
||||
|
||||
# Default primary key field type |
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field |
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' |
||||
|
||||
LOGOUT_REDIRECT_URL = '/' |
||||
SHAARPY_NAME = env.str('SHAARPY_NAME', default="Shaarpy - My Links") |
||||
SHAARPY_DESCRIPTION = env.str('SHAARPY_DESCRIPTION') |
@ -0,0 +1,77 @@ |
||||
{% load static %} |
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<title>{% block title %}{{ SHAARPY_NAME }}{% endblock %}</title> |
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> |
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"/> |
||||
<link rel="stylesheet" href="{% static 'shaarpy/css/base.css' %}"/> |
||||
<meta name="author" content="FoxMaSk"> |
||||
</head> |
||||
<body role="document"> |
||||
<nav class="navbar navbar-expand-md navbar-light fixed-top sticky-top" style="background-color: #ebcb8b;"> |
||||
<div class="container-fluid"> |
||||
<a class="navbar-brand" href="{% url 'home' %}" title="Home"> |
||||
<span class="menu-collapsed">ShaarPy {{ SHAARPY_NAME }}</span> |
||||
</a> |
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> |
||||
<span class="navbar-toggler-icon"></span> |
||||
</button> |
||||
<div class="collapse navbar-collapse" id="navbarNav"> |
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> |
||||
<li class="nav-item"> |
||||
<a class="nav-link shaarpy-link" href="{% url 'home' %}">Home</a> |
||||
</li> |
||||
<li class="nav-item"> |
||||
<a class="nav-link shaarpy-link" href="{% url 'link_create' %}" title="Add a link or note">+Shaarpy</a> |
||||
</li> |
||||
<li class="nav-item"> |
||||
<a class="nav-link" href="{% url 'tags_list' %}" title="Cloud of tags">Tags Cloud</a> |
||||
</li> |
||||
<li class="nav-item"> |
||||
<a class="nav-link" href="{% url 'daily' %}" title="Daily links">Daily links</a> |
||||
</li> |
||||
</ul> |
||||
<ul class="navbar-nav"> |
||||
<li class="nav-item"> |
||||
<a class="nav-link shaarpy-link" href="{% url 'atom' %}"><i class="fas fa-rss"></i></a> |
||||
</li> |
||||
</ul> |
||||
<ul class="navbar-nav"> |
||||
<li class="nav-item"> |
||||
<a class="nav-link" href="{% url 'me' %}"><i class="far fa-user"></i></a> |
||||
</li> |
||||
{% if user.is_authenticated %} |
||||
<li class="nav-item"> |
||||
<a class="nav-link" href="{% url 'logout' %}"><i class="fas fa-sign-out-alt"></i></a> |
||||
</li> |
||||
{% endif %} |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
</nav> |
||||
<div class="container-fluid"> |
||||
<div class="row"> |
||||
<!-- container --> |
||||
{% block content %} |
||||
{% endblock %} |
||||
<!--/.container--> |
||||
</div> |
||||
</div> |
||||
<hr/> |
||||
<footer class="footer"> |
||||
{% block footer %} |
||||
<div class="container-fluid"> |
||||
<div class="row"> |
||||
<div class="col-xs-4 col-md-4 col-lg-4"> |
||||
<p class="fs-5">Since 2022 <a href="https://framagit.org/foxmask/shaarpy" title="Shaarpy project homepage">Shaarpy</a></p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
</footer> |
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,49 @@ |
||||
{% extends "base.html" %} |
||||
{% load static %} |
||||
{% load i18n %} |
||||
|
||||
{% block title %}{% trans "My Profile" %} - {{ SHAARPY_NAME }}{% endblock %} |
||||
|
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2"> |
||||
<h1 class="page-header">{% trans "My Profile" %}</h1> |
||||
<form role="form" method="post" class="form-horizontal">{% csrf_token %} |
||||
{% if form.errors %} |
||||
<div class="alert alert-danger" role="alert"> |
||||
{% if form.errors.items %}You should fix the error(s) below {% endif %} |
||||
</div> |
||||
{% endif %} |
||||
|
||||
<div class="tab-content"> |
||||
<table class='table table-hover table-bordered table-striped caption-top'> |
||||
<caption>{% trans "Profile Details" %}</caption> |
||||
<thead> |
||||
<tr> |
||||
<th>{% trans "Last name" %}</th> |
||||
<th>{% trans "Email" %}</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td> |
||||
{{ form.last_name }} |
||||
</td> |
||||
<td> |
||||
{{ form.email }} |
||||
</td> |
||||
</tr> |
||||
{% if form.last_name.errors or form.email.errors %} |
||||
<tr> |
||||
<td class="alert alert-danger">{% if form.last_name.errors %}{{ form.last_name.errors }}{% endif %}</td> |
||||
<td class="alert alert-danger">{% if form.email.errors %}{{ form.email.errors }}{% endif %}</td> |
||||
</tr> |
||||
{% endif %} |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
<div class="form-group"> |
||||
<input type="submit" class="btn btn-primary" value="{% trans 'Save' %}" /> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,30 @@ |
||||
{% extends "base.html" %} |
||||
{% load static %} |
||||
{% load i18n %} |
||||
{% block title %}{% trans "My Profile" %} - {{ SHAARPY_NAME }}{% endblock %} |
||||
|
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2"> |
||||
<h2 class="page-header">{% trans "My Profile" %}</h2> |
||||
<table class='table table-hover table-bordered table-striped caption-top'> |
||||
<caption>{% trans "Profile Details" %}</caption> |
||||
<thead> |
||||
<tr> |
||||
<th>{% trans "Last name" %}</th> |
||||
<th>{% trans "Email" %}</th> |
||||
<th>{% trans "Last connection" %}</th> |
||||
<th>{% trans "Created" %}</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td>{{ object.last_name }}</td> |
||||
<td>{{ object.email }}</td> |
||||
<td>{{ object.last_login }}</td> |
||||
<td>{{ object.date_joined }}</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<a href="{% url 'edit_me' %}"><span class="btn btn-primary"> {% trans "Edit" %}</span></a> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,36 @@ |
||||
{% extends "base.html" %} |
||||
{% load i18n %} |
||||
|
||||
{% block title %}{% trans "Log in" %} - {{ SHAARPY_NAME }} {% endblock %} |
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2"> |
||||
<h1 class="page-header">{% trans "Log in" %}</h1> |
||||
{% if form.non_field_errors %} |
||||
<div class="alert alert-danger" role="alert"> |
||||
<p>{{ form.non_field_errors }}</p> |
||||
</div> |
||||
{% endif %} |
||||
|
||||
<form role="form" method="post" class="form-horizontal" action="{% url 'login' %}"> |
||||
{% csrf_token %} |
||||
<div class="form-group"> |
||||
<label class="form-label col-md-2" for="id_username">{% trans 'Login' %}</label> |
||||
{{ form.username }} |
||||
{% if form.username.errors %} |
||||
<p class="help-block">{{ form.username.errors.as_text }}</p> |
||||
{% endif %} |
||||
</div> |
||||
<div class="form-group"> |
||||
<label class="form-label col-md-2" for="id_password">{% trans 'Password' %}</label> |
||||
{{ form.password }} |
||||
{% if form.password.errors %} |
||||
<p class="help-block">{{ form.password.errors.as_text }}</p> |
||||
{% endif %} |
||||
</div> |
||||
<div class="form-group"> |
||||
<input type="submit" class="btn btn-primary" value="{% trans 'Log in' %}" /> |
||||
<input type="hidden" name="next" value="{{ next }}" /> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,35 @@ |
||||
{% extends "base.html" %} |
||||
{% load i18n %} |
||||
{% load shaarpy_extras %} |
||||
<title>{% block title %}{{ SHAARPY_NAME }} :: {% trans 'Daily Links' %} {{ tag }}{% endblock %}</title> |
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2 mt-3"> |
||||
<h1>{% trans 'Daily Links' %}</h1> |
||||
<h5>list of the links of each day</h5> |
||||
<div class="row row-cols-1 row-cols-md-3 g-4"> |
||||
{% for data in object_list %} |
||||
<div class="col"> |
||||
<div class="card"> |
||||
<div class="card-body"> |
||||
{% if data.url %} |
||||
<h5><a href="{{ data.url }}" target="_blank">{{ data.title }}</a></h5> |
||||
{% else %} |
||||
<h5><a href=" url 'link_detail' data.id ">{{ data.title }}</a></h5> |
||||
{% endif %} |
||||
<p class="card-text">{{ data.text | markdown |safe }}</p> |
||||
</div> |
||||
<div class="card-footer text-muted"> |
||||
{% if data.private %} |
||||
<i class="fas fa-key"></i> - |
||||
{% endif %} |
||||
<i class="far fa-clock"></i> {{ data.date_created }} |
||||
{% if data.tags %} |
||||
- <i class="fas fa-tags"></i> {{ data.tags | tags | safe }} |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endfor %} |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,59 @@ |
||||
{% extends "base.html" %} |
||||
{% load i18n %} |
||||
{% load shaarpy_extras %} |
||||
<title>{% block title %}{{ SHAARPY_NAME }} :: {% trans "View" %} {{ object.title }} {% endblock %}</title> |
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2"> |
||||
<div class="card"> |
||||
<div class="card-body"> |
||||
{% if object.url %} |
||||
<h5><a href="{{ object.url }}" target="_blank">{{ object.title }}</a></h5> |
||||
{% else %} |
||||
<h5><a href="{% url 'link_detail' object.id %}">{{ object.title }}</a></h5> |
||||
{% endif %} |
||||
<p class="card-text">{{ object.text | markdown |safe }}</p> |
||||
</div> |
||||
<div class="card-footer text-muted"> |
||||
{% if object.private %} |
||||
<i class="fas fa-key"></i> - |
||||
{% endif %} |
||||
<i class="far fa-clock"></i> {{ object.date_created }} |
||||
{% if request.user.is_authenticated == True %} |
||||
<a type="button" href="{% url 'link_edit' object.id %}" class="btn btn-outline-primary btn-sm"> |
||||
<i class="far fa-edit"></i> |
||||
</a> |
||||
<a type="button" class="btn btn-outline-danger btn-sm" data-bs-toggle="modal" data-bs-target="#deleteModal{{ object.id }}"> |
||||
<i class="far fa-trash-alt"></i> |
||||
</a> - |
||||
<!-- Modal --> |
||||
<div class="modal fade" id="deleteModal{{ object.id }}" tabindex="-1" aria-labelledby="deleteModalLabel{{ object.id }}" aria-hidden="true"> |
||||
<div class="modal-dialog"> |
||||
<div class="modal-content"> |
||||
<div class="modal-header"> |
||||
<h5 class="modal-title" id="deleteModalLabel{{ object.id }}">{% trans 'Deletion of your link' %}</h5> |
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
</div> |
||||
<div class="modal-body"> |
||||
{% blocktrans %} |
||||
Are you sure you want to remove this link ? |
||||
{% endblocktrans %} |
||||
<p> |
||||
{% if object.link %}{{ object.link }}{% endif %} |
||||
{% if object.title %}{{ object.title }}{% endif %} |
||||
</p> |
||||
</div> |
||||
<div class="modal-footer"> |
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">No</button> |
||||
<a href="{% url 'link_delete' object.id %}" type="button" class="btn btn-danger">Yes</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endif %} |
||||
<a href="{% url 'link_detail' object.id %}">Permalink</a> - {% if object.url %}{{ object.url }}{% else %}{% url 'link_detail' object.id %}{% endif %} |
||||
{% if object.tags %} |
||||
- <i class="fas fa-tags"></i> {{ object.tags | tags | safe }} |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,59 @@ |
||||
{% extends "base.html" %} |
||||
{% load i18n %} |
||||
<title>{% block title %}{{ SHAARPY_NAME }} :: New shaarpy/note{% endblock %}</title> |
||||
{% block content %} |
||||
{% if messages %} |
||||
<div class="alert alert-warning"> |
||||
<ul class="messages"> |
||||
{% for message in messages %} |
||||
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li> |
||||
{% endfor %} |
||||
</ul> |
||||
</div> |
||||
{% endif %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2"> |
||||
<form action="" method="post" role="form" class="form-horizontal"> |
||||
{% csrf_token %} |
||||
<fieldset> |
||||
{{ form.non_field_errors }} |
||||
{% if edit %} |
||||
<legend> Update the link/note</legend> |
||||
{% else %} |
||||
<legend> Share a link/a note</legend> |
||||
{% endif %} |
||||
<div class="mb-3"> |
||||
<label class="col-sm-9 control-label" for="id_url"> URL</label> |
||||
<div class="col-sm-9">{{ form.url }}</div> |
||||
<div class="col-sm-offset-2 col-sm-8">{{ form.url.errors }}</div> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label class="col-sm-9 control-label" for="id_title"> {% trans 'Title' %}</label> |
||||
<div class="col-sm-9">{{ form.title }}</div> |
||||
<div class="col-sm-offset-2 col-sm-8">{{ form.title.errors }}</div> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label class="col-sm-9 control-label" for="id_text"> {% trans 'Text' %}</label> |
||||
<div class="col-sm-9">{{ form.text }}</div> |
||||
<div class="col-sm-offset-2 col-sm-8">{{ form.text.errors }}</div> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label class="col-sm-9 control-label" for="id_tags"> {% trans 'Tags' %}</label> |
||||
<div class="col-sm-9">{{ form.tags }}</div> |
||||
<div class="col-sm-offset-2 col-sm-8">{{ form.tags.errors }}</div> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label class="col-sm-9 control-label" for="id_private"> {% trans 'Private' %}</label> |
||||
<div class="col-sm-9">{{ form.private }}</div> |
||||
<div class="col-sm-offset-2 col-sm-8">{{ form.private.errors }}</div> |
||||
</div> |
||||
<a href="{% url 'home' %}" class="btn btn-success"><i class="fas fa-long-arrow-alt-left"></i> {% trans "Back" %}</a> |
||||
{% if edit %} |
||||
<button class="btn btn-primary">{% trans "Update link" %}</button> |
||||
{% else %} |
||||
<button class="btn btn-primary"><i class="fas fa-plus"></i> {% trans "Add link" %}</button> |
||||
{% endif %} |
||||
|
||||
</fieldset> |
||||
</form> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,92 @@ |
||||
{% extends "base.html" %} |
||||
{% load i18n %} |
||||
{% load shaarpy_extras %} |
||||
{% if tag %} |
||||
<title>{% block title %}{{ SHAARPY_NAME }} :: {% trans 'Links for tag' %} {{ tag }}{% endblock %}</title> |
||||
{% endif %} |
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2"> |
||||
<nav aria-label="Page navigation" class="mt-3"> |
||||
<ul class="pagination"> |
||||
{% if page_obj.has_previous %} |
||||
<li class="page-item"><a class="page-link" href="?page=1">« first</a></li> |
||||
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">previous</a></li> |
||||
{% endif %} |
||||
<li class="page-item active" aria-current="page"> |
||||
<a class="page-link" href="#">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</a> |
||||
</li> |
||||
{% if page_obj.has_next %} |
||||
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">next</a></li> |
||||
<li class="page-item"><a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">last »</a></li> |
||||
{% endif %} |
||||
</ul> |
||||
</nav> |
||||
{% for data in object_list %} |
||||
<div class="card"> |
||||
<div class="card-body"> |
||||
{% if data.url %} |
||||
<h5><a href="{{ data.url }}" target="_blank">{{ data.title }}</a></h5> |
||||
{% else %} |
||||
<h5><a href="{% url 'link_detail' data.id %}">{{ data.title }}</a></h5> |
||||
{% endif %} |
||||
<p class="card-text">{{ data.text | markdown |safe }}</p> |
||||
</div> |
||||
<div class="card-footer text-muted"> |
||||
{% if data.private %} |
||||
<i class="fas fa-key"></i> - |
||||
{% endif %} |
||||
<i class="far fa-clock"></i> {{ data.date_created }} |
||||
<a type="button" href="{% url 'link_edit' data.id %}" class="btn btn-outline-primary btn-sm"> |
||||
<i class="far fa-edit"></i> |
||||
</a> |
||||
<a type="button" class="btn btn-outline-danger btn-sm" data-bs-toggle="modal" data-bs-target="#deleteModal{{ data.id }}"> |
||||
<i class="far fa-trash-alt"></i> |
||||
</a> - |
||||
<!-- Modal --> |
||||
<div class="modal fade" id="deleteModal{{ data.id }}" tabindex="-1" aria-labelledby="deleteModalLabel{{ data.id }}" aria-hidden="true"> |
||||
<div class="modal-dialog"> |
||||
<div class="modal-content"> |
||||
<div class="modal-header"> |
||||
<h5 class="modal-title" id="deleteModalLabel{{ data.id }}">{% trans 'Deletion of your link' %}</h5> |
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
</div> |
||||
<div class="modal-body"> |
||||
{% blocktrans %} |
||||
Are you sure you want to remove this link ? |
||||
{% endblocktrans %} |
||||
<p> |
||||
{% if data.link %}{{ data.link }}{% endif %} |
||||
{% if data.title %}{{ data.title }}{% endif %} |
||||
</p> |
||||
</div> |
||||
<div class="modal-footer"> |
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">No</button> |
||||
<a href="{% url 'link_delete' data.id %}" type="button" class="btn btn-danger">Yes</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<a href="{% url 'link_detail' data.id %}">Permalink</a> - {% if data.url %}{{ data.url }}{% else %}{% url 'link_detail' data.id %}{% endif %} |
||||
{% if data.tags %} |
||||
- <i class="fas fa-tags"></i> {{ data.tags | tags | safe }} |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
{% endfor %} |
||||
<nav aria-label="Page navigation" class="mt-3"> |
||||
<ul class="pagination"> |
||||
{% if page_obj.has_previous %} |
||||
<li class="page-item"><a class="page-link" href="?page=1">« first</a></li> |
||||
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">previous</a></li> |
||||
{% endif %} |
||||
<li class="page-item active" aria-current="page"> |
||||
<a class="page-link" href="#">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</a> |
||||
</li> |
||||
{% if page_obj.has_next %} |
||||
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">next</a></li> |
||||
<li class="page-item"><a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">last »</a></li> |
||||
{% endif %} |
||||
</ul> |
||||
</nav> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,17 @@ |
||||
{% extends "base.html" %} |
||||
{% load i18n %} |
||||
{% load shaarpy_extras %} |
||||
<title>{% block title %}{{ SHAARPY_NAME }} :: {% trans "Tags list" %}{% endblock %}</title> |
||||
{% block content %} |
||||
<div class="col-xs-8 col-md-8 col-lg-8 offset-xs-2 offset-md-2 offset-lg-2 mt-3"> |
||||
<h1>{% trans "Tags list" %}</h1> |
||||
{% for key, value in tags.items %} |
||||
<a href="{% url 'links_by_tag_list' key %}" type="button" class="p-3 m-2 btn btn-dark position-relative"> |
||||
{{ key }} |
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"> |
||||
{{ value }} |
||||
</span> |
||||
</a> |
||||
{% endfor %} |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,20 @@ |
||||
|
||||
from django import template |
||||
import pypandoc |
||||
|
||||
register = template.Library() |
||||
|
||||
|
||||
@register.filter(name='tags') |
||||
def tags(value): |
||||
out = '' |
||||
for tag in value.split(','): |
||||
out += f'<a href="/links/{tag}"><span class="badge rounded-pill bg-secondary">{tag}</span></a> ' |
||||
return out |
||||
|
||||
|
||||
@register.filter(name='markdown') |
||||
def makrdown(text): |
||||
return pypandoc.convert_text(text, "html", format="md") |
||||
|
||||
|
@ -0,0 +1,44 @@ |
||||
"""shaarpy URL Configuration |
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see: |
||||
https://docs.djangoproject.com/en/4.0/topics/http/urls/ |
||||
Examples: |
||||
Function views |
||||
1. Add an import: from my_app import views |
||||
2. Add a URL to urlpatterns: path('', views.home, name='home') |
||||
Class-based views |
||||
1. Add an import: from other_app.views import Home |
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') |
||||
Including another URLconf |
||||
1. Import the include() function: from django.urls import include, path |
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) |
||||
""" |
||||
from django.contrib import admin |
||||
from django.contrib.auth import views as auth_views |
||||
from django.urls import path, re_path, include |
||||
from shaarpy.views import (HomeView, LinksCreate, LinksDetail, LinksUpdate, link_delete, TagsList, LinksByTagList) |
||||
from shaarpy.views import (DailyLinks, LatestLinksFeed, me, MeUpdate) |
||||
from shaarpy import settings |
||||
|
||||
|
||||
urlpatterns = [ |
||||
# MANAGE USERS |
||||
path('accounts/login/', auth_views.LoginView.as_view(extra_context={'SHAARPY_NAME': settings.SHAARPY_NAME}), name="login"), |
||||
path('accounts/profile/', me, name="me"), |
||||
path('accounts/profile/edit/', MeUpdate.as_view(), name='edit_me'), |
||||
path('accounts/', include('django.contrib.auth.urls')), |
||||
# THE APP |
||||
path('', HomeView.as_view(), name="home"), |
||||
path('new/', LinksCreate.as_view(), name='link_create'), |
||||
path('edit/<int:pk>/', LinksUpdate.as_view(), name='link_edit'), |
||||
path('view/<int:pk>/', LinksDetail.as_view(), name='link_detail'), |
||||
path('delete/<int:pk>/', link_delete, name='link_delete'), |
||||
path('tags/', TagsList.as_view(), name='tags_list'), |
||||
re_path('links/(?P<tags>\w+)$', LinksByTagList.as_view(), name='links_by_tag_list'), |
||||
path('daily/', DailyLinks.as_view(), name='daily'), |
||||
re_path('daily/(?P<yesterday>\d\d\d\d-\d\d-\d\d)', DailyLinks.as_view(), name='daily'), |
||||
# FEEDS |
||||
path('feed/', LatestLinksFeed(), name='atom'), |
||||
# ADMIN |
||||
path('admin/', admin.site.urls), |
||||
] |
@ -0,0 +1,301 @@ |
||||
# coding: utf-8 |
||||
""" |
||||
ShaarPy |
||||
""" |
||||
from datetime import date |
||||
from datetime import timedelta |
||||
|
||||
from django.contrib.auth import logout |
||||
from django.contrib.auth.decorators import login_required |
||||
from django.contrib.auth.mixins import LoginRequiredMixin |
||||
from django.contrib.auth.models import User |
||||
|
||||
from django.contrib.syndication.views import Feed |
||||
|
||||
from django.shortcuts import redirect, render |
||||
from django.urls import reverse, reverse_lazy |
||||
from django.utils.text import Truncator |
||||
from django.views.generic import ListView, CreateView, UpdateView, DetailView |
||||
|
||||
from newspaper import Article |
||||
import newspaper |
||||
import pypandoc |
||||
|
||||
from shaarpy.forms import LinksForm, MeForm |
||||
from shaarpy.models import Links |
||||
from shaarpy import settings |
||||
from urllib.parse import urlparse |
||||
|
||||
|
||||
# Beginning of Handling content of Article with NewsPaPer |
||||
def get_host(url): |
||||
o = urlparse(url) |
||||
return o.scheme + '://' + o.hostname |
||||
|
||||
|
||||
def get_brand(url): |
||||
brand = newspaper.build(get_host(url)) |
||||
brand.download() |
||||
brand.parse() |
||||
return brand.brand |
||||
|
||||
|
||||
def grab_full_article(url): |
||||
""" |
||||
get the complete article page from the URL |
||||
""" |
||||
# get the complete article |
||||
r = Article(url, keep_article_html=True) |
||||
r.download() |
||||
r.parse() |
||||
# convert into markdown |
||||
output = Truncator(r.article_html).chars("400", html=False) |
||||
text = pypandoc.convert_text(output, 'md', format='html') |
||||
title = r.title + ' - ' + get_brand(url) |
||||
return title, text |
||||
# End of Handling content of Article with NewsPaPer |
||||
|
||||
|
||||
@login_required |
||||
def link_delete(request, pk): |
||||
link = Links.objects.get(pk=pk) |
||||
link.delete() |
||||
return redirect('home') |
||||
|
||||
|
||||
class SettingsMixin: |
||||
""" |
||||
mixin to add settings data to the templates |
||||
""" |
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs): |
||||
# get only the unread articles of the folders |
||||
context = super(SettingsMixin, self).get_context_data(**kwargs) |
||||
context['SHAARPY_NAME'] = settings.SHAARPY_NAME |
||||
context['SHAARPY_DESCRIPTION'] = settings.SHAARPY_DESCRIPTION |
||||
|
||||
return context |
||||
|
||||
|
||||
class HomeView(SettingsMixin, ListView): |
||||
""" |
||||
Links List |
||||
""" |
||||
|
||||
queryset = Links.objects.none() |
||||
paginate_by = 10 |
||||
ordering = ['-date_created'] |
||||
|
||||
def get_queryset(self): |
||||
if self.request.user.is_authenticated: |
||||
queryset = Links.objects.all() |
||||
else: |
||||
queryset = Links.objects.filter(private=False) |
||||
return queryset |
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs): |
||||
queryset = object_list if object_list is not None else self.object_list |
||||
|
||||
page_size = self.paginate_by |
||||
context_object_name = self.get_context_object_name(queryset) |
||||
|
||||
context = super(HomeView, self).get_context_data(**kwargs) |
||||
|
||||
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size) |
||||
context['paginator'] = paginator |
||||
context['page_obj'] = page |
||||
context['is_paginated'] = is_paginated |
||||
context['object_list'] = queryset |
||||
|
||||
if context_object_name is not None: |
||||
context[context_object_name] = queryset |
||||
context.update(kwargs) |
||||
|
||||
return context |
||||
|
||||
|
||||
class LinksCreate(SettingsMixin, LoginRequiredMixin, CreateView): |
||||
""" |
||||
Create Links |
||||
""" |
||||
model = Links |
||||
form_class = LinksForm |
||||
|
||||
def form_valid(self, form): |
||||
url = form.cleaned_data['url'] |
||||
if url is None: |
||||
self.object = form.save() |
||||
title = form.cleaned_data['title'] |
||||
text = form.cleaned_data['text'] |
||||
# just a note |
||||
if url == '' and title == '' and text != '': |
||||
self.object.title = "Note:" |
||||
else: |
||||
try: |
||||
links = Links.objects.get(url=url) |
||||
return redirect('link_detail', **{'pk': links.id}) |
||||
except Links.DoesNotExist: |
||||
self.object = form.save() |
||||
title = form.cleaned_data['title'] |
||||
text = form.cleaned_data['text'] |
||||
# just a note |
||||
if url == '' and title == '' and text != '': |
||||
self.object.title = "Note:" |
||||
# just an url |
||||
elif url != '': |
||||
self.object.title, self.object.text = grab_full_article(url) |
||||
|
||||
return super().form_valid(form) |
||||
|
||||
|
||||
class LinksDetail(SettingsMixin, DetailView): |
||||
""" |
||||
Link Detail |
||||
""" |
||||
model = Links |
||||
|
||||
|
||||
class LinksUpdate(SettingsMixin, LoginRequiredMixin, UpdateView): |
||||
""" |
||||
Link Update |
||||
""" |
||||
|
||||
model = Links |
||||
form_class = LinksForm |
||||
|
||||
def get_context_data(self, **kwargs): |
||||
context = {} |
||||
if self.object: |
||||
context['object'] = self.object |
||||
context_object_name = self.get_context_object_name(self.object) |
||||
if context_object_name: |
||||
context[context_object_name] = self.object |
||||
context['edit'] = True |
||||
context.update(kwargs) |
||||
return super().get_context_data(**context) |
||||
|
||||
|
||||
class LinksByTagList(SettingsMixin, ListView): |
||||
""" |
||||
LinksByTag List |
||||
""" |
||||
|
||||
queryset = Links.objects.none() |
||||
paginate_by = 10 |
||||
|
||||
def get_queryset(self): |
||||
tags = self.kwargs['tags'] |
||||
if self.request.user.is_authenticated: |
||||
queryset = Links.objects.filter(tags__contains=tags) |
||||
else: |
||||
queryset = Links.objects.filter(tags__contains=tags, private=False) |
||||
return queryset |
||||
|
||||
def get_context_data(self, **kwargs): |
||||
context = super(LinksByTagList, self).get_context_data(**kwargs) |
||||
context['tag'] = self.kwargs['tags'] |
||||
context.update(kwargs |