diff --git a/README.rst b/README.rst index 1d8f94d..ed47913 100644 --- a/README.rst +++ b/README.rst @@ -2,10 +2,6 @@ django-flag-app =============== -.. image:: https://img.shields.io/github/license/abhiabhi94/django-flag-app?color=gr - :target: https://github.com/abhiabhi94/django-flag-app/blob/master/LICENSE - :alt: licence - .. image:: https://travis-ci.org/abhiabhi94/django-flag-app.svg?branch=master :target: https://travis-ci.org/abhiabhi94/django-flag-app :alt: build @@ -14,11 +10,32 @@ django-flag-app :target: https://coveralls.io/github/abhiabhi94/django-flag-app :alt: coverage -A pluggable django application to add flagging to your models. +.. image:: https://img.shields.io/pypi/pyversions/django-flag-app.svg + :target: https://pypi.python.org/pypi/django-flag-app/ + :alt: python + +.. image:: https://img.shields.io/pypi/djversions/django-flag-app.svg + :target: https://pypi.python.org/pypi/django-flag-app/ + :alt: django + +.. image:: https://readthedocs.org/projects/django-flag-app/badge/?version=latest + :target: https://django-flag-app.readthedocs.io/?badge=latest + :alt: docs + +.. image:: https://img.shields.io/github/license/abhiabhi94/django-flag-app?color=gr + :target: https://github.com/abhiabhi94/django-flag-app/blob/master/LICENSE + :alt: licence + +A pluggable django application that adds the ability for users to flag(or report) your models. .. image:: ./docs/_static/images/django-flag-app.gif :alt: flagging-process +For complete documentation you may visit `Read the Doc`_. or see the `docs`_ directory. + +.. _Read the Doc: https://django-flag-app.readthedocs.io +.. _docs: ./docs/ + Installation ------------ @@ -39,7 +56,8 @@ If you want, you may install it from the source, grab the source code and run `` Usage ----- -* Add app +Add app +```````` To enable ``django_flag_app`` in your project you need to add it to ``INSTALLED_APPS`` in your projects ``settings.py`` file: @@ -51,10 +69,10 @@ To enable ``django_flag_app`` in your project you need to add it to ``INSTALLED_ ... ) -Step-1: Add url -```````````````` +Add URL +```````` -In ``urls.py``: +In your root ``urls.py``: .. code:: python @@ -66,8 +84,8 @@ In ``urls.py``: ... ) -Step 2: Migrate -```````````````` +Migrate +```````` Run the migrations to add the new models to your database: @@ -76,8 +94,8 @@ Run the migrations to add the new models to your database: python manage.py migrate flag -Step 3: Connect the flag model with the target model -````````````````````````````````````````````````````` +Connect the flag model with the target model +````````````````````````````````````````````` In ``models.py`` add the field **flags** as a ``GenericRelation`` field to the required model. @@ -98,8 +116,12 @@ E.g. for a ``Post`` model, you may add the field as shown below: flags = GenericRelation(Flag) -Step 4: Use template tag -````````````````````````` +Use template tag +````````````````` + +If you want to use web API, this step is not required. See further instructions at `Web API`_. + +:: _Web API: ./docs/webAPI.rst ``render_flag_form`` tag requires 2 required positional arguments: @@ -113,6 +135,7 @@ To render the ``flag`` form for a the instance ``post``, place this inside your {% render_flag_form post user %} + Contributing ------------ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..6732f11 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,66 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +BASE_DIR = os.path.dirname((os.path.dirname(os.path.abspath(__file__)))) + + +def get_version(): + with open(os.path.join(BASE_DIR, 'VERSION')) as version_file: + version = version_file.read().strip() + return version + + +# -- Project information ----------------------------------------------------- + +project = 'Django Flag App' +copyright = '2020, Abhyudai' +author = 'Abhyudai' + +# The short X.Y version +version = get_version() +# The full version, including alpha/beta/rc tags +release = get_version() + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autosectionlabel', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..5251277 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,48 @@ +=============================== +Contributing to Django Flag App +=============================== + +There are many ways to contribute to the project. You may improve the documentation, address a bug, add some feature to the code or do something else. All sort of contributions are welcome. + + +Development +----------- + +To start development on this project, fork this repository and follow the following instructions. + +.. code:: sh + + # clone the forked repository + $ git clone YOUR_FORKED_REPO_URL + + # create a virtual environment + $ python3 -m venv venv + # activate the virtual environment + $ source venv/bin/activate + # install dependencies + (venv) $ pip install -e . -r testapp/requirements.txt + # migrate the migrations to the database + (venv) $ python manage.py migrate + # create data + (venv) $ python manage.py create_initial_data + # start the development server + (venv) $ python manage.py runserver + + +Testing +------- + +To run tests against a particular ``python`` and ``django`` version installed inside your virtual environment, you may use: + +.. code:: sh + + (venv) $ python manage.py test + +To run tests against all supported ``python`` and ``django`` versions, you may run: + +.. code:: sh + + # install dependency + (venv) $ pip install tox + # run tests + (venv) $ tox diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..f59ced1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,21 @@ +Welcome to Django Flag App's documentation! +=========================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + introduction + webAPI + settings + style-customisation + contributing + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/introduction.rst b/docs/introduction.rst new file mode 100644 index 0000000..7dcbd7e --- /dev/null +++ b/docs/introduction.rst @@ -0,0 +1,133 @@ +django-flag-app +=============== + +.. image:: https://travis-ci.org/abhiabhi94/django-flag-app.svg?branch=master + :target: https://travis-ci.org/abhiabhi94/django-flag-app + :alt: build + +.. image:: https://coveralls.io/repos/github/abhiabhi94/django-flag-app/badge.svg + :target: https://coveralls.io/github/abhiabhi94/django-flag-app + :alt: coverage + +.. image:: https://img.shields.io/pypi/pyversions/django-flag-app.svg + :target: https://pypi.python.org/pypi/django-flag-app/ + :alt: python + +.. image:: https://img.shields.io/pypi/djversions/django-flag-app.svg + :target: https://pypi.python.org/pypi/django-flag-app/ + :alt: django + +.. image:: https://readthedocs.org/projects/django-flag-app/badge/?version=latest + :target: https://django-flag-app.readthedocs.io/?badge=latest + :alt: docs + +.. image:: https://img.shields.io/github/license/abhiabhi94/django-flag-app?color=gr + :target: https://github.com/abhiabhi94/django-flag-app/blob/master/LICENSE + :alt: licence + +A pluggable django application that adds the ability for users to flag(or report) your models. + +.. image:: ./_static/images/django-flag-app.gif + :alt: flagging-process + +Installation +------------ + +Install using ``pip`` + +.. code:: sh + + $ pip install django-flag-app + +If you want, you may install it from the source, grab the source code and run ``setup.py``. + +.. code:: sh + + $ git clone git://github.com/abhiabhi94/django-flag-app.git + $ cd django-flag-app + $ python setup.py install + +Usage +----- + +Add app +```````` + +To enable ``django_flag_app`` in your project you need to add it to ``INSTALLED_APPS`` in your projects ``settings.py`` file: + +.. code:: python + + INSTALLED_APPS = ( + ... + 'flag', + ... + ) + +Add URL +```````` + +In your root ``urls.py``: + +.. code:: python + + urlpatterns = patterns( + path('admin/', admin.site.urls), + path('flag/', include('flag.urls')), + ... + path('api/', include('flag.api.urls')), # only required for API Framework + ... + ) + +Migrate +```````` + +Run the migrations to add the new models to your database: + +.. code:: sh + + python manage.py migrate flag + + +Connect the flag model with the target model +````````````````````````````````````````````` + +In ``models.py`` add the field **flags** as a ``GenericRelation`` field to the required model. + +E.g. for a ``Post`` model, you may add the field as shown below: + +.. code:: python + + from django.contrib.contenttypes.fields import GenericRelation + + from flag.models import Flag + + + class Post(models.Model): + user = models.ForeignKey(User) + title = models.CharField(max_length=200) + body = models.TextField() + # the field name should be flags + flags = GenericRelation(Flag) + + +Use template tag +````````````````` + +If you want to use web API, this step is not required. See further instructions at :ref:`API Actions`. + +``render_flag_form`` tag requires 2 required positional arguments: + + 1. Instance of the targeted model. + 2. User object. + +To render the ``flag`` form for a the instance ``post``, place this inside your detail view, perhaps in some template of the sort ``postdetail.html``. + +.. code:: jinja + + {% render_flag_form post user %} + + +Contributing +------------ + +Please see the instructions at :ref:`Contributing to Django Flag App`. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/settings.rst b/docs/settings.rst new file mode 100644 index 0000000..19a50d6 --- /dev/null +++ b/docs/settings.rst @@ -0,0 +1,25 @@ +Settings +======== + +django-flag-app has a few configuration options that allow you to customize it. + +FLAGS_ALLOWED +`````````````` +The number of flags allowed before a content is set as flagged. Defaults to ``10``. + + +FLAG_REASONS +````````````` +The reasons for which a content can be flagged. Users will have a choose one of these before they flag a content. This a list of tuples. Defaults to: + +.. code:: python + + from django.utils.translation import gettext_lazy as _ + + [ + (1, _('Spam | Exists only to promote a service')), + (2, _('Abusive | Intended at promoting hatred')), + ] + +Remember that ``(100, _('Something else')`` will always be appended to this list. + diff --git a/docs/style-customisation.rst b/docs/style-customisation.rst new file mode 100644 index 0000000..723e627 --- /dev/null +++ b/docs/style-customisation.rst @@ -0,0 +1,81 @@ +Style Customization +=================== + +The flag app has been built in a way that you can customise its look and feel completely. Most of the styles can be customised through HTML classes. + +In case, you feel there is some customisation that can be added, feel free to open an `issue`_. + +.. _issue: https://github.com/abhiabhi94/django-flag-app/issues + +The template structure of the flag app looks something like this: + + .. code:: sh + + ├── templates + └── flag + ├── flag_form.html + └── flag_icon.html + +Overriding templates +````````````````````` + +To customise a template, + + * Create ``flag`` folder inside templates directory. + + * Inside it, create a new template file, giving it the same name as that of the default template that needs to be overridden. + +For example, to override the HTML classes of ``submit button`` + +create ``templates/flag/flag_form.html`` (assuming all your templates are placed under the directory ``templates``) + + .. code:: jinja + + {% extends "flag/flag_form.html" %} + + {% block cls_flag_modal_submit %} + my-class + {% endblock cls_flag_modal_submit %} + + +Blocks +------ + +Please refer to this table when using blocks to customise HTML classes + ++----------------------------------------+-----------------------------------------------------------+--------------+ +| Block | Use | HTML element | ++========================================+===========================================================+==============+ +| ``{% block cls_flag %}`` | complete flag element | ``div`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_icon_img %}`` | flag icon image element | ``div`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal %}`` | modal that appears when flagging | ``div`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_content %}`` | modal content | ``div`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_close %}`` | the cross icon(close button) inside the modal | ``span`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_form_div %}``| the ``div`` element containing the modal form | ``div`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_form %}`` | modal form element | ``form`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_title %}`` | the text containing modal title | ``div`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_reasons %}`` | the element that displays reasons for flagging | ``tr`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_reason %}`` | individual reasons | ``input`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_info %}`` | text box which appears when ``Somthing else`` is selected | ``textarea`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ +| ``{% block cls_flag_modal_submit %}`` | submit button inside the modal | ``input`` | ++----------------------------------------+-----------------------------------------------------------+--------------+ + + +Flag Icon +--------- + +To change the flag icon, just override the template ``flag_icon.html`` as explained above. +Make sure that you add the property ``class="flag-icon {% if has_flagged %}user-has-flagged{% else %}user-has-not-flagged{% endif %}"`` to your HTML element. These classes are used by ``javascript`` files. + +For other customisation, please refer to the :ref:`Blocks` above diff --git a/docs/webAPI.rst b/docs/webAPI.rst new file mode 100644 index 0000000..117532a --- /dev/null +++ b/docs/webAPI.rst @@ -0,0 +1,57 @@ +Web API +======= + +django-flag-app uses `django-rest-framework`_ to expose a Web API that provides developers with access to the same functionality offered through the web user interface. + +.. _django-rest-framework: https://www.django-rest-framework.org/ + +The available actions with permitted user are as follows: + + * Flag content. (authenticated users) + * Unflag content. (user who has previously flagged that content) + +Setup +----- + +To integrate the flag API into your app, just follow the instructions as mentioned :ref:`Usage`. + +API Actions +----------- + +All actions can only be performed by authenticated users. Authorization must be provided as a TOKEN or USERNAME:PASSWORD. +``POST`` is the allowed method for all requests. + +All available actions are explained below: + +Flag content +````````````` + +This action can be performed by providing the URL with data queries related to the content type. + +The request requires the following parameters: + +- ``model_name``: is the model name of the content type that have flags associated with it. +- ``model_id``: is the id of an object of that model +- ``app_name``: is the name of the app that contains the model. +- ``reason``: number corresponding to the reason(e.g. 1, 2, 3). +- ``info``: '' (This is only required if the reason is ``100`` (``Something else``)) + + +For example, to flag a content of second object (id=1) of a model (content type) called ``post`` inside the app(django app) ``post``. +You may do the following: + +.. code:: sh + + $ curl -X POST -u USERNAME:PASSWORD -H "Content-Type: application/json" -d "{'app_name': 'post','model_name': 'post', 'model_id': 1,'reason': 1,'info': ''}" http://localhost:8000/api/flag/ + + +Un-Flag Content +```````````````` + +To un-flag a **FLAGGED** content, set reason value to ``0`` or remove it from the request. + +.. code:: sh + + $ curl -X POST -u USERNAME:PASSWORD -H "Content-Type: application/json" -d "{'app_name': 'post','model_name': 'post', 'model_id': 1}" http://localhost:8000/api/flag/ + + diff --git a/flag/models.py b/flag/models.py index 35b5465..2257bf1 100644 --- a/flag/models.py +++ b/flag/models.py @@ -82,7 +82,7 @@ def toggle_state(self, state, moderator): self.save() def toggle_flagged_state(self): - allowed_flags = getattr(settings, 'FLAGS_ALLOWED', 0) + allowed_flags = getattr(settings, 'FLAGS_ALLOWED', 10) self.refresh_from_db() field = 'state' if self.count > allowed_flags and ( diff --git a/flag/templates/flag/400-debug.html b/flag/templates/flag/400-debug.html deleted file mode 100644 index 7a09098..0000000 --- a/flag/templates/flag/400-debug.html +++ /dev/null @@ -1,115 +0,0 @@ -{% comment %} Almost whole of this code was copied from django-contrib-comments {% endcomment %} - - - - - Flagging not allowed (400) - - - - -
-

Flagging not allowed (400)

- - - - - -
Why:{{ why }}
-
-
-

- The post you tried to flag to this view wasn't saved because something - tampered with the security information in the flag form. The message - above should explain the problem. -

-
- -
-

- You're seeing this error because you have DEBUG = True in - your Django settings file. Change that to False, and Django - will display a standard 400 error page. -

-
- - diff --git a/flag/templates/flag/flag_icon.html b/flag/templates/flag/flag_icon.html index 5d56b02..4942424 100644 --- a/flag/templates/flag/flag_icon.html +++ b/flag/templates/flag/flag_icon.html @@ -1,5 +1,5 @@ {% block flag_icon %} - + {% endblock flag_icon %} diff --git a/setup.py b/setup.py index e486564..39df4eb 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def get_version(): version=get_version(), author='Abhyudai', author_email='', - description='A pluggable django application to add flagging to your models.', + description='A pluggable django application that adds the ability for users to flag(or report) your models', long_description=get_description(), long_description_content_type='text/markdown', url='https://github.com/abhiabhi94/django-flag-app', diff --git a/tests/base.py b/tests/base.py index 9ce8a69..2c2fe9e 100644 --- a/tests/base.py +++ b/tests/base.py @@ -2,7 +2,7 @@ from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse -from django.test import Client, RequestFactory, TestCase +from django.test import Client, RequestFactory, TestCase, override_settings from rest_framework.test import APITestCase from flag.models import Flag, FlagInstance @@ -94,6 +94,7 @@ def set_flag(cls, model_obj=None, user=None, reason=None, info=None): ) +@override_settings(FLAGS_ALLOWED=0) class BaseFlagTest(BaseFlagTestUtils, TestCase): pass @@ -138,5 +139,6 @@ def setUp(self): self.factory = RequestFactory() +@override_settings(FLAGS_ALLOWED=0) class BaseFlagAPITest(BaseFlagTestUtils, APITestCase): pass diff --git a/tests/test_api.py b/tests/test_api.py index 9ba54dd..fa5942f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -81,7 +81,7 @@ def test_flagging_unflagged_object(self): ) def test_unflagging_successfully(self): - # un-flag => no reason is passed and the comment must be already flagged by the user + # un-flag => no reason is passed and the content must be already flagged by the user post = self.post self.set_flag(model_obj=post) data = self.data.copy() diff --git a/tests/test_views.py b/tests/test_views.py index e22143f..4d6468d 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -63,7 +63,7 @@ def test_flagging_unflagged_object(self): self.assertDictEqual(response.json(), response_data) def test_unflagging_successfully(self): - # un-flag => no reason is passed and the comment must be already flagged by the user + # un-flag => no reason is passed and the content must be already flagged by the user post = self.post self.set_flag(model_obj=post) data = self.data.copy()