Merge pull request #1 from pelican-plugins/migrate

Migrate Share Post and convert to namespace plugin
This commit is contained in:
Justin Mayer 2021-03-19 10:33:23 +01:00 committed by GitHub
commit 41d4995b5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 612 additions and 0 deletions

15
.editorconfig Normal file
View file

@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.py]
max_line_length = 88
[*.yml]
indent_size = 2

104
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,104 @@
name: build
on: [push, pull_request]
env:
PYTEST_ADDOPTS: "--color=yes"
jobs:
test:
name: Test - ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Set up Pip cache
uses: actions/cache@v2
id: pip-cache
with:
path: ~/.cache/pip
key: pip-${{ hashFiles('**/pyproject.toml') }}
- name: Upgrade Pip
run: python -m pip install --upgrade pip
- name: Install Poetry
run: python -m pip install poetry
- name: Set up Poetry cache
uses: actions/cache@v2
id: poetry-cache
with:
path: ~/.cache/pypoetry/virtualenvs
key: poetry-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: |
poetry run pip install --upgrade pip
poetry install
- name: Run tests
run: poetry run invoke tests
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: Set Poetry cache
uses: actions/cache@v2
id: poetry-cache
with:
path: ~/.cache/pypoetry/virtualenvs
key: poetry-${{ hashFiles('**/poetry.lock') }}
- name: Upgrade Pip
run: python -m pip install --upgrade pip
- name: Install Poetry
run: python -m pip install poetry
- name: Install dependencies
run: |
poetry run pip install --upgrade pip
poetry install
- name: Run linters
run: poetry run invoke lint
deploy:
name: Deploy
environment: Deployment
needs: [test, lint]
runs-on: ubuntu-latest
if: ${{ github.ref=='refs/heads/main' && github.event_name!='pull_request' }}
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: Check release
id: check_release
run: |
python -m pip install --upgrade pip
python -m pip install poetry githubrelease httpx==0.16.1 autopub
echo "##[set-output name=release;]$(autopub check)"
- name: Publish
if: ${{ steps.check_release.outputs.release=='' }}
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
git remote set-url origin https://$GITHUB_TOKEN@github.com/${{ github.repository }}
autopub prepare
poetry build
autopub commit
autopub githubrelease
poetry publish -u __token__ -p $PYPI_PASSWORD

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
poetry.lock

31
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,31 @@
# See https://pre-commit.com/hooks.html for info on hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
hooks:
- id: check-added-large-files
- id: check-ast
- id: check-case-conflict
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 19.10b0
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.0
hooks:
- id: flake8
args: [--max-line-length=88]
language_version: python3.7
- repo: https://github.com/PyCQA/isort
rev: 5.7.0
hooks:
- id: isort

9
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,9 @@
Contributing
============
Contributions are welcome and much appreciated. Every little bit helps. You can contribute by improving the documentation, adding missing features, and fixing bugs. You can also help out by reviewing and commenting on [existing issues][].
To start contributing to this plugin, review the [Contributing to Pelican][] documentation, beginning with the **Contributing Code** section.
[existing issues]: https://github.com/pelican-plugins/share-post/issues
[Contributing to Pelican]: https://docs.getpelican.com/en/latest/contribute.html

86
README.md Normal file
View file

@ -0,0 +1,86 @@
# Share Post: A Plugin for Pelican
[![Build Status](https://img.shields.io/github/workflow/status/pelican-plugins/share-post/build)](https://github.com/pelican-plugins/share-post/actions)
[![PyPI Version](https://img.shields.io/pypi/v/pelican-share-post)](https://pypi.org/project/pelican-share-post/)
![License](https://img.shields.io/pypi/l/pelican-share-post?color=blue)
Share Post is a Pelican plugin that creates share links in articles that allow site visitors to share the current article with others in a privacy-friendly manner.
Many web sites have share widgets to let readers share posts on social networks. Most of these widgets are used by vendors for online tracking. These widgets can also be visually-distracting and negatively affect readers attention.
Share Post creates old-school URLs for some popular sites which your theme can use. These links do not have the ability to track site visitors. They can also be unobtrusive depending on how Pelican theme uses them.
Installation
------------
This plugin can be installed via:
python -m pip install pelican-share-post
Usage
-----
This plugin adds to each Pelican article a dictionary of URLs that, when followed, allows the reader to easily share the article via specific channels. When activated, the plugin adds the attribute `share_post` to each article with the following format:
``` python
article.share_post = {
"facebook": "<URL>",
"email": "<URL>",
"twitter": "<URL>",
"diaspora": "<URL>",
"linkedin": "<URL>",
"hacker-news": "<URL>",
"reddit": "<URL>",
}
```
You can then access those variables in your template. For example:
``` html+jinja
{% if article.share_post and article.status != 'draft' %}
<section>
<p id="post-share-links">
Share on:
<a href="{{article.share_post['diaspora']}}" title="Share on Diaspora">Diaspora*</a>
<a href="{{article.share_post['twitter']}}" title="Share on Twitter">Twitter</a>
<a href="{{article.share_post['facebook']}}" title="Share on Facebook">Facebook</a>
<a href="{{article.share_post['linkedin']}}" title="Share on LinkedIn">LinkedIn</a>
<a href="{{article.share_post['hacker-news']}}" title="Share on HackerNews">HackerNews</a>
<a href="{{article.share_post['email']}}" title="Share via Email">Email</a>
<a href="{{article.share_post['reddit']}}" title="Share via Reddit">Reddit</a>
</p>
</section>
{% endif %}
```
Contributing
------------
Contributions are welcome and much appreciated. Every little bit helps. You can contribute by improving the documentation, adding missing features, and fixing bugs. You can also help out by reviewing and commenting on [existing issues][].
To start contributing to this plugin, review the [Contributing to Pelican][] documentation, beginning with the **Contributing Code** section.
[existing issues]: https://github.com/pelican-plugins/share-post/issues
[Contributing to Pelican]: https://docs.getpelican.com/en/latest/contribute.html
Contributors
------------
* [Talha Mansoor](https://www.oncrashreboot.com) - talha131@gmail.com
* [Jonathan DEKHTIAR](https://github.com/DEKHTIARJonathan) - contact@jonathandekhtiar.eu
* [Justin Mayer](https://justinmayer.com)
* [Leonardo Giordani](https://www.thedigitalcatonline.com)
License
-------
This project is licensed under the MIT license.

3
RELEASE.md Normal file
View file

@ -0,0 +1,3 @@
Release type: major
Initial release as namespace plugin

View file

@ -0,0 +1 @@
from .share_post import * # noqa

View file

@ -0,0 +1,9 @@
import pytest
from pelican.tests.support import temporary_folder
@pytest.fixture
def tmp_folder():
with temporary_folder() as tf:
yield tf

View file

@ -0,0 +1,125 @@
"""
Share Post
==========
This plugin was originally created by
Talha Mansoor <talha131@gmail.com>
This plugin adds social share URLs to each article.
"""
# If you want to add a new link_processor please
# have a look at the create_link decorator and
# follow the example of the other functions
from urllib.parse import quote
from bs4 import BeautifulSoup
from pelican import contents, signals
from pelican.generators import ArticlesGenerator, PagesGenerator
_create_link_functions = []
# Use this decorator to mark a function as
# a link creator. The function's prototype shall be
# create_link_NAME(title, url, content)
# where
# NAME is the name of the target, e.g. "dispora" or "facebook"
# title is the HTML-safe title of the content
# url is the content URL
# content is the full object, should you need to extract more data.
def create_link(f):
_create_link_functions.append(f)
return f
@create_link
def create_link_email(title, url, content):
return f"mailto:?subject={title}&amp;body={url}"
@create_link
def create_link_hacker_news(title, url, content):
return f"https://news.ycombinator.com/submitlink?t={title}&u={url}"
@create_link
def create_link_diaspora(title, url, content):
return f"https://sharetodiaspora.github.io/?title={title}&url={url}"
@create_link
def create_link_facebook(title, url, content):
return f"https://www.facebook.com/sharer/sharer.php?u={url}"
@create_link
def create_link_twitter(title, url, content):
twitter_username = content.settings.get("TWITTER_USERNAME", "")
via = f"&via={twitter_username}" if twitter_username else ""
tags = getattr(content, "tags", [])
tags = ",".join([tag.slug for tag in tags])
hashtags = f"&hashtags={tags}" if tags else ""
return f"https://twitter.com/intent/tweet?text={title}&url={url}{via}{hashtags}"
@create_link
def create_link_reddit(title, url, content):
return f"https://www.reddit.com/submit?url={url}&title={title}"
@create_link
def create_link_linkedin(title, url, content):
summary = quote(
BeautifulSoup(content.summary, "html.parser").get_text().strip().encode("utf-8")
)
return (
f"https://www.linkedin.com/shareArticle?"
f"mini=true&url={url}&title={title}&"
f"summary={summary}&source={url}"
)
def create_share_links(content):
if isinstance(content, contents.Static):
return
main_title = BeautifulSoup(content.title, "html.parser").get_text().strip()
try:
sub_title = (
" " + BeautifulSoup(content.subtitle, "html.parser").get_text().strip()
)
except AttributeError:
sub_title = ""
title = quote(f"{main_title}{sub_title}".encode("utf-8"))
site_url = content.settings["SITEURL"]
url = quote(f"{site_url}/{content.url}".encode("utf-8"))
content.share_post = {}
for func in _create_link_functions:
key = func.__name__.replace("create_link_", "").replace("_", "-")
content.share_post[key] = func(title, url, content)
def run_plugin(generators):
for generator in generators:
if isinstance(generator, ArticlesGenerator):
for article in generator.articles:
create_share_links(article)
for translation in article.translations:
create_share_links(translation)
elif isinstance(generator, PagesGenerator):
for page in generator.pages:
create_share_links(page)
def register():
signals.all_generators_finalized.connect(run_plugin)

View file

@ -0,0 +1,8 @@
Title: Test post
Date: 2021-02-01 13:00:00
Category: test
Tags: foo, bar, foobar
Summary: I have a lot to test
Series: test_series
Content

View file

@ -0,0 +1,65 @@
import os
from share_post import run_plugin
from pelican.generators import ArticlesGenerator
from pelican.tests.support import get_context, get_settings
from . import share_post
def test_share_post(tmp_folder):
base_path = os.path.dirname(os.path.abspath(__file__))
test_data_path = os.path.join(base_path, "test_data")
share_post.register()
settings = get_settings()
context = get_context(settings)
generator = ArticlesGenerator(
context=context,
settings=settings,
path=test_data_path,
theme=settings["THEME"],
output_path=tmp_folder,
)
generator.generate_context()
run_plugin([generator])
share_links = generator.articles[0].share_post
assert (
share_links["diaspora"]
== "https://sharetodiaspora.github.io/?title=Test%20post&url=/test-post.html"
)
assert share_links["twitter"] == (
"https://twitter.com/intent/tweet?text=Test%20post"
"&url=/test-post.html&hashtags=foo,bar,foobar"
)
assert (
share_links["facebook"]
== "https://www.facebook.com/sharer/sharer.php?u=/test-post.html"
)
assert share_links["linkedin"] == (
"https://www.linkedin.com/shareArticle?"
"mini=true&url=/test-post.html&title=Test%20post&"
"summary=I%20have%20a%20lot%20to%20test&source=/test-post.html"
)
assert (
share_links["hacker-news"]
== "https://news.ycombinator.com/submitlink?t=Test%20post&u=/test-post.html"
)
assert (
share_links["email"] == "mailto:?subject=Test%20post&amp;body=/test-post.html"
)
assert (
share_links["reddit"]
== "https://www.reddit.com/submit?url=/test-post.html&title=Test%20post"
)

73
pyproject.toml Normal file
View file

@ -0,0 +1,73 @@
[tool.poetry]
name = "pelican-share-post"
version = "0.0.0"
description = "A Pelican plugin to create share URLs of article"
authors = ["Talha Mansoor <talha131@gmail.com>"]
license = "MIT"
readme = "README.md"
keywords = ["pelican", "plugin", "social"]
repository = "https://github.com/pelican-plugins/share-post"
documentation = "https://docs.getpelican.com"
packages = [
{ include = "pelican" },
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Framework :: Pelican",
"Framework :: Pelican :: Plugins",
"Intended Audience :: End Users/Desktop",
"Operating System :: OS Independent",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Software Development :: Libraries :: Python Modules",
]
[tool.poetry.urls]
"Funding" = "https://donate.getpelican.com/"
"Issue Tracker" = "https://github.com/pelican-plugins/share-post/issues"
[tool.poetry.dependencies]
python = "^3.6"
pelican = "^4.5"
markdown = {version = "^3.2.2", optional = true}
beautifulsoup4 = "^4.9.3"
[tool.poetry.dev-dependencies]
black = {version = "^19.10b0", allow-prereleases = true}
flake8 = "^3.9"
flake8-black = "^0.2.0"
invoke = "^1.3"
isort = "^5.4"
livereload = "^2.6"
markdown = "^3.2.2"
pytest = "^6.0"
pytest-cov = "^2.8"
pytest-pythonpath = "^0.7.3"
pytest-sugar = "^0.9.4"
Werkzeug = "^1.0"
[tool.poetry.extras]
markdown = ["markdown"]
[tool.autopub]
project-name = "Share Post"
git-username = "botpub"
git-email = "botpub@autopub.rocks"
append-github-contributor = true
[tool.isort]
# Maintain compatibility with Black
profile = "black"
multi_line_output = 3
# Sort imports within their section independent of the import type
force_sort_within_sections = true
# Designate "pelican" as separate import section
known_pelican = "pelican"
sections = "FUTURE,STDLIB,THIRDPARTY,PELICAN,FIRSTPARTY,LOCALFOLDER"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

79
tasks.py Normal file
View file

@ -0,0 +1,79 @@
import os
from pathlib import Path
from shutil import which
from invoke import task
PKG_NAME = "share_post"
PKG_PATH = Path(f"pelican/plugins/{PKG_NAME}")
ACTIVE_VENV = os.environ.get("VIRTUAL_ENV", None)
VENV_HOME = Path(os.environ.get("WORKON_HOME", "~/.local/share/virtualenvs"))
VENV_PATH = Path(ACTIVE_VENV) if ACTIVE_VENV else (VENV_HOME / PKG_NAME)
VENV = str(VENV_PATH.expanduser())
TOOLS = ["poetry", "pre-commit"]
POETRY = which("poetry") if which("poetry") else (VENV / Path("bin") / "poetry")
PRECOMMIT = (
which("pre-commit") if which("pre-commit") else (VENV / Path("bin") / "pre-commit")
)
@task
def tests(c):
"""Run the test suite"""
c.run(f"{VENV}/bin/pytest", pty=True)
@task
def black(c, check=False, diff=False):
"""Run Black auto-formatter, optionally with --check or --diff"""
check_flag, diff_flag = "", ""
if check:
check_flag = "--check"
if diff:
diff_flag = "--diff"
c.run(f"{VENV}/bin/black {check_flag} {diff_flag} {PKG_PATH} tasks.py")
@task
def isort(c, check=False, diff=False):
check_flag, diff_flag = "", ""
if check:
check_flag = "-c"
if diff:
diff_flag = "--diff"
c.run(f"{VENV}/bin/isort {check_flag} {diff_flag} .")
@task
def flake8(c):
c.run(f"{VENV}/bin/flake8 {PKG_PATH} tasks.py")
@task
def lint(c):
isort(c, check=True)
black(c, check=True)
flake8(c)
@task
def tools(c):
"""Install tools in the virtual environment if not already on PATH"""
for tool in TOOLS:
if not which(tool):
c.run(f"{VENV}/bin/pip install {tool}")
@task
def precommit(c):
"""Install pre-commit hooks to .git/hooks/pre-commit"""
c.run(f"{PRECOMMIT} install")
@task
def setup(c):
c.run(f"{VENV}/bin/pip install -U pip")
tools(c)
c.run(f"{POETRY} install")
precommit(c)

3
tox.ini Normal file
View file

@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
ignore = E203, W503