├── .directory ├── .gitignore ├── INSTALL.md ├── README.md ├── UNLICENSE ├── comments ├── __init__.py ├── admin.py ├── forms.py ├── jinja2 │ └── comments │ │ ├── comment_form.html │ │ └── comments.html ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20150731_1746.py │ └── __init__.py ├── models.py ├── templates │ └── comments │ │ ├── comment_form.html │ │ └── comments.html ├── tests.py ├── urls.py └── views.py ├── highlighting ├── __init__.py ├── formatter.py ├── get_languages.py └── settings.py ├── home ├── __init__.py ├── admin.py ├── jinja2 │ ├── home │ │ ├── faq.html │ │ ├── home.html │ │ ├── latest_pastes.html │ │ ├── submit_paste_form.html │ │ ├── submit_paste_languages.html │ │ └── submit_paste_tabs.html │ └── latest_pastes │ │ └── latest_pastes.html ├── migrations │ └── __init__.py ├── models.py ├── templates │ ├── home │ │ ├── faq.html │ │ ├── home.html │ │ ├── latest_pastes.html │ │ ├── submit_paste_form.html │ │ └── submit_paste_tabs.html │ └── latest_pastes │ │ └── latest_pastes.html ├── tests.py ├── urls.py └── views.py ├── manage.py ├── pastebin ├── .directory ├── __init__.py ├── debug_settings.py ├── jinja2.py ├── jinja_globals.py ├── manager.py ├── middleware.py ├── settings.py ├── templatetags │ ├── __init__.py │ └── extra_tags.py ├── testcase.py ├── urls.py ├── util.py └── wsgi.py ├── pastes ├── __init__.py ├── admin.py ├── admin_forms.py ├── admin_views.py ├── forms.py ├── jinja2 │ └── pastes │ │ ├── edit_paste │ │ ├── edit_error.html │ │ └── edit_paste.html │ │ ├── paste_history │ │ └── paste_history.html │ │ ├── remove_paste │ │ ├── paste_removed.html │ │ ├── remove_error.html │ │ └── remove_paste.html │ │ ├── report_paste │ │ ├── paste_reported.html │ │ ├── report_error.html │ │ └── report_paste.html │ │ └── show_paste │ │ ├── show_error.html │ │ └── show_paste.html ├── migrations │ ├── 0001_initial.py │ ├── 0002_paste_encrypted.py │ ├── 0003_auto_20150525_1612.py │ ├── 0004_auto_20150601_1634.py │ ├── 0005_auto_20150601_1642.py │ ├── 0006_paste_updated.py │ ├── 0007_auto_20150704_1510.py │ ├── 0008_pasteversion_encrypted.py │ ├── 0009_auto_20150731_1327.py │ ├── 0010_auto_20150731_1746.py │ └── __init__.py ├── models.py ├── templates │ └── pastes │ │ ├── admin │ │ └── process_report │ │ │ ├── process.html │ │ │ ├── process_error.html │ │ │ └── process_success.html │ │ ├── edit_paste │ │ ├── edit_error.html │ │ └── edit_paste.html │ │ ├── paste_history │ │ └── paste_history.html │ │ ├── remove_paste │ │ ├── paste_removed.html │ │ ├── remove_error.html │ │ └── remove_paste.html │ │ ├── report_paste │ │ ├── paste_reported.html │ │ ├── report_error.html │ │ └── report_paste.html │ │ └── show_paste │ │ ├── show_error.html │ │ └── show_paste.html ├── tests.py ├── urls.py └── views.py ├── requirements.txt ├── sql ├── __init__.py ├── add_test_data.sql ├── create_tables.sql ├── cursor.py ├── drop_tables.sql └── run_sql.sh ├── static ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── pastebin-django.css │ ├── prism.css │ └── pygments-style.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery.min.js │ ├── jquery.readmore.min.js │ ├── jquery.timeago.js │ ├── linkify-jquery.min.js │ ├── linkify.min.js │ ├── npm.js │ ├── pastebin-comments.js │ ├── pastebin-controls.js │ ├── pastebin-decrypt.js │ ├── pastebin-favorite.js │ ├── pastebin-submit.js │ ├── prism.js │ └── sjcl.js ├── templates ├── base.html ├── form.html ├── navbar.html ├── page_footer.html └── pagination.html ├── templates_jinja2 ├── base.html ├── form.html ├── navbar.html ├── page_footer.html └── pagination.html └── users ├── __init__.py ├── admin.py ├── forms.py ├── jinja2 └── users │ ├── login │ ├── logged_in.html │ └── login.html │ ├── logout │ └── logged_out.html │ ├── profile │ ├── favorites │ │ ├── favorites.html │ │ └── favorites_hidden.html │ ├── home │ │ ├── home.html │ │ ├── home_favorites.html │ │ └── home_pastes.html │ ├── pastes │ │ └── pastes.html │ ├── profile.html │ ├── profile_error.html │ ├── profile_favorites.html │ ├── profile_navbar.html │ └── profile_pastes.html │ ├── register │ ├── already_logged_in.html │ ├── register.html │ ├── register_error.html │ └── register_success.html │ └── settings │ ├── change_email_address │ └── change_email_address.html │ ├── change_password │ └── change_password.html │ ├── change_preferences │ └── change_preferences.html │ ├── delete_account │ ├── account_deleted.html │ └── delete_account.html │ └── settings_error.html ├── migrations ├── 0001_initial.py ├── 0002_sitesettings.py └── __init__.py ├── models.py ├── templates └── users │ ├── login │ ├── logged_in.html │ └── login.html │ ├── logout │ └── logged_out.html │ ├── profile │ ├── favorites │ │ ├── favorites.html │ │ └── favorites_hidden.html │ ├── home │ │ ├── home.html │ │ ├── home_favorites.html │ │ └── home_pastes.html │ ├── pastes │ │ └── pastes.html │ ├── profile.html │ ├── profile_error.html │ ├── profile_favorites.html │ ├── profile_navbar.html │ └── profile_pastes.html │ ├── register.html │ ├── register │ ├── already_logged_in.html │ ├── register.html │ └── register_success.html │ └── settings │ ├── change_password │ └── change_password.html │ ├── change_preferences │ └── change_preferences.html │ ├── delete_account │ ├── account_deleted.html │ └── delete_account.html │ └── settings_error.html ├── tests.py ├── urls.py └── views.py /.directory: -------------------------------------------------------------------------------- 1 | [Dolphin] 2 | Timestamp=2015,3,12,15,24,49 3 | Version=3 4 | 5 | [Settings] 6 | HiddenFilesShown=true 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Python ### 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | 62 | ### Django ### 63 | *.log 64 | *.pot 65 | *.pyc 66 | *.sqlite3 67 | __pycache__/ 68 | local_settings.py 69 | 70 | 71 | ### VirtualEnv ### 72 | # Virtualenv 73 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 74 | .Python 75 | [Bb]in 76 | [Ii]nclude 77 | [Ll]ib 78 | [Ll]ocal 79 | [Ss]cripts 80 | pyvenv.cfg 81 | pip-selfcheck.json 82 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | INSTALLING AND RUNNING PASTEBIN-DJANGO 2 | === 3 | These steps explain how to get pastebin-django working in such a way that it can be launched using the provided manage.py script (which is unsuitable for production use!), the database connection works correctly, etc. There are multiple ways to run Django projects in a production environment, although for pastesite.matoking.com I've used uWSGI with nginx. Instructions for running a Django application with that stack can be found here: 4 | https://uwsgi-docs.readthedocs.org/en/latest/tutorials/Django_and_nginx.html 5 | 6 | 7 | Installing dependencies 8 | -- 9 | pastebin-django requires Python and a few related dependencies to be installed (virtualenv, pip). The following command should install the required dependencies if you are running on Debian or a derivative (eg. Linux Mint, Ubuntu). 10 | 11 | sudo apt-get install python python-dev python-pip python-virtualenv 12 | 13 | Creating virtualenv container and installing required Python libraries 14 | -- 15 | Although this step is optional, it's recommended to install and run the web application inside a virtualenv environment, as this isolates the web application's environment from the system-wide Python installation, thus ensuring that your web application won't be accidentally broken by a system-wide update. 16 | 17 | To create the virtualenv environment, run the following command on the pastebin-django directory, which contains the project's apps such as pastes, comments. 18 | 19 | virtualenv pastebin-django 20 | 21 | Once you have created the virtualenv environment, you can start using it by running the following command. 22 | 23 | source bin/activate 24 | 25 | You can always deactivate the virtualenv environment by running the following command. 26 | 27 | deactivate 28 | 29 | But instead of leaving the environment, let's install the required Python libraries using pip. cd inside the pastebin-django directory and install the required Python libraries. Note that sudo isn't necessary, as we are installing all of the libraries inside our isolated Python environment. 30 | 31 | cd pastebin-django 32 | pip install -r requirements.txt 33 | 34 | Configuring the PostgreSQL database 35 | -- 36 | We'll assume you have already created a database and a role which can access the said database. Start by opening the settings.py file in pastebin/settings.py and changing the credentials in DATABASES['default']. If you're going to be running unit tests, you can change the database name in DATABASES['default']['TEST']['NAME'], which is the database that will be used when running the unit tests. 37 | 38 | After this is done, run the following command in the root of your virtualenv environment to create Django's in-built database tables. You may also be prompted to create a superuser, which you can use when logging into pastebin-django. 39 | 40 | python manage.py syncdb 41 | 42 | Configuring the Redis instance 43 | -- 44 | pastebin-django uses a data structure server to both to store persistent data that wouldn't be a good fit for a relational database (eg. paste hit counts). You can install Redis on Debian or a derivative using the following command. 45 | 46 | sudo apt-get install redis-server 47 | 48 | By default Redis runs on port 6379 and saves its data regularly. However, pastebin-django's default settings assume a persistent Redis storage runs on the port 6380, so you may need to change the port for the 'persistent' cache in settings.py to match this port. You can also run a non-persistent Redis server under the port 6379 and a persistent Redis server under port 6380, which matches the default settings. 49 | 50 | Running the unit tests 51 | -- 52 | At this point your web application should be configured correctly. But to make sure that everything will work nicely before we try running the web application, run the unit tests using the following command. You can use your "normal" database when running the unit tests, but you'll need to recreate the tables described in sql/create_tables.sql after running the tests, as those will be automatically dropped. 53 | 54 | python manage.py test 55 | 56 | If everything worked as intended, all of the tests should pass which means the web application <--> database connection is working correctly. 57 | 58 | Starting the development web server 59 | -- 60 | We are now ready to start the development web server. Run the following command to run the web application on 127.0.0.1:8000, which is also the URL you'll need to enter into your web browser in order to access the site. Feel free to change the address and/or port depending on your working environment. 61 | 62 | python manage.py runserver 127.0.0.1:8000 63 | 64 | Now, try opening http://127.0.0.1:8000 in your web browser. If everything worked out correctly, you should now be able to use the web application as normal. 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pastebin-django 2 | === 3 | A pastebin web application developed in Django featuring plenty of features, including: 4 | 5 | * User registration, allowing pastes to be updated and removed 6 | * Syntax highlighting (server-side Pygments used for unencrypted pastes, client-side Prism JS library for encrypted pastes) 7 | * Paste encryption using Stanford Javascript Crypto Library 8 | * Hidden pastes 9 | * Paste versioning and history 10 | * Feature to report pastes to site administrators 11 | * Paste favoriting 12 | * Paste comments 13 | 14 | Looking for the old version of pastebin-django as it was developed for Tsoha2015? [Check it out here.](https://github.com/Matoking/pastebin-django/tree/tsoha) 15 | 16 | Credits 17 | -- 18 | pastebin-django is built on the Django web framework 19 | 20 | [Django](https://www.djangoproject.com/) 21 | 22 | and it uses the following JavaScript libraries 23 | 24 | [Stanford Javascript Crypto Library](https://github.com/bitwiseshiftleft/sjcl) 25 | [Prism](http://prismjs.com/) 26 | [JQuery](http://jquery.com/) 27 | [Timeago](http://timeago.yarp.com/) 28 | [Readmore.js](http://jedfoster.com/Readmore.js/) 29 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /comments/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/comments/__init__.py -------------------------------------------------------------------------------- /comments/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /comments/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | class SubmitCommentForm(forms.Form): 4 | """ 5 | The form for submitting a comment 6 | """ 7 | text = forms.CharField(max_length=2000) -------------------------------------------------------------------------------- /comments/jinja2/comments/comment_form.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 6 |
7 | 8 |
9 |
10 |
11 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | You are posting a comment to {{ paste.title }} 20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /comments/jinja2/comments/comments.html: -------------------------------------------------------------------------------- 1 |
Loading...
2 | 3 | 7 |
8 |
9 | 10 | 18 | 19 | {% include "comments/comment_form.html" %} -------------------------------------------------------------------------------- /comments/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('pastes', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Comment', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('text', models.TextField()), 21 | ('submitted', models.DateTimeField(auto_now_add=True)), 22 | ('edited', models.DateTimeField(auto_now=True)), 23 | ('paste', models.ForeignKey(to='pastes.Paste')), 24 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 25 | ], 26 | options={ 27 | }, 28 | bases=(models.Model,), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /comments/migrations/0002_auto_20150731_1746.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('comments', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='comment', 16 | name='edited', 17 | field=models.DateTimeField(auto_now=True, db_index=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AlterField( 21 | model_name='comment', 22 | name='submitted', 23 | field=models.DateTimeField(auto_now_add=True, db_index=True), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /comments/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/comments/migrations/__init__.py -------------------------------------------------------------------------------- /comments/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | from sql import cursor 5 | 6 | from pastes.models import Paste 7 | 8 | import datetime 9 | 10 | class Comment(models.Model): 11 | COMMENTS_PER_PAGE = 10 12 | 13 | paste = models.ForeignKey(Paste) 14 | user = models.ForeignKey(User) 15 | 16 | text = models.TextField() 17 | 18 | submitted = models.DateTimeField(auto_now_add=True, db_index=True) 19 | edited = models.DateTimeField(auto_now=True, db_index=True) -------------------------------------------------------------------------------- /comments/templates/comments/comment_form.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 6 |
7 | 8 |
9 |
10 |
11 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | You are posting a comment to {{ paste.title }} 20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /comments/templates/comments/comments.html: -------------------------------------------------------------------------------- 1 |
Loading...
2 | 3 | 7 |
8 |
9 | 10 | 18 | 19 | {% include "comments/comment_form.html" %} -------------------------------------------------------------------------------- /comments/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.views.generic import TemplateView 3 | 4 | from comments import views 5 | 6 | urlpatterns = [ 7 | url(r'^get_comments/$', views.get_comments, name="get_comments"), 8 | url(r'^add_comment/$', views.add_comment, name="add_comment"), 9 | 10 | url(r'^edit_comment/$', views.edit_comment, name="edit_comment"), 11 | url(r'^delete_comment/$', views.delete_comment, name="delete_comment"), 12 | ] -------------------------------------------------------------------------------- /highlighting/__init__.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from itertools import chain 3 | 4 | from highlighting import settings 5 | from highlighting.formatter import ListHtmlFormatter 6 | 7 | from pygments import highlight 8 | from pygments.lexers import get_lexer_by_name 9 | from pygments.formatters import HtmlFormatter 10 | 11 | def language_exists(language): 12 | """ 13 | Check that the language exists in the tuple 14 | """ 15 | if language in chain(*settings.LANGUAGES): 16 | return True 17 | else: 18 | return False 19 | 20 | def format_text(text, format="text"): 21 | """ 22 | Format the text using Pygments and return the formatted text 23 | """ 24 | lexer = get_lexer_by_name(format) 25 | formatter = ListHtmlFormatter(linenos=False, 26 | prestyles="border-radius: 0px; background-color: white; border: 0px;") 27 | result = highlight(text, lexer, formatter) 28 | 29 | # A small hack to include CSS styles in the table containing the line numbers 30 | # This needs to be done because otherwise Bootstrap inserts its own styling to the pre-element, 31 | # which makes it look like crap 32 | result = result.replace("
", "
")
33 |     
34 |     return result


--------------------------------------------------------------------------------
/highlighting/formatter.py:
--------------------------------------------------------------------------------
 1 | from pygments.formatters import HtmlFormatter
 2 | 
 3 | class ListHtmlFormatter(HtmlFormatter):
 4 |     """
 5 |     Wraps the code inside an 
    element and each individual line around a
  1. element, 6 | giving us both free line numbers and better behavior with line wrapping. 7 | 8 | This imitates how GeSHi renders its highlighted code. 9 | """ 10 | def wrap(self, source, outfile): 11 | return self._wrap_ol(source) 12 | 13 | def _wrap_ol(self, source): 14 | yield 0, '
      ' 15 | for i, t in source: 16 | if i == 1: 17 | t = '
    1. ' + t + '
    2. ' 18 | yield i, t 19 | yield 0, '
    ' -------------------------------------------------------------------------------- /highlighting/get_languages.py: -------------------------------------------------------------------------------- 1 | """ 2 | Iterate through all languages supported by Pygments and print a sorted list of languages 3 | 4 | This can then be copied into settings.py 5 | """ 6 | from pygments.lexers import get_all_lexers 7 | import operator 8 | 9 | languages = {} 10 | 11 | for lexer in get_all_lexers(): 12 | if lexer[1][0] != "text": 13 | languages[lexer[1][0]] = lexer[0] 14 | 15 | sorted_list = sorted(languages.items(), key=operator.itemgetter(0)) 16 | 17 | # Add "text only" at the beginning 18 | sorted_list.insert(0, ("text", "Text only")) 19 | 20 | print(sorted_list) -------------------------------------------------------------------------------- /home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/home/__init__.py -------------------------------------------------------------------------------- /home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /home/jinja2/home/faq.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Frequently asked questions - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
    7 |
    8 |
    9 | 12 |

    13 | What is pastebin-django?
    14 | pastebin-django is a site that allows you to store 'pastes', short text snippets. 15 |

    16 |

    17 | What is the syntax highlighting feature?
    18 | If your paste contains code or markup, you can use the syntax highlighting to highlight the text. Note that encrypted pastes don't support all of the syntax highlighting formats that are available with unencrypted pastes. 19 |

    20 |

    21 | How does the encryption feature work?
    22 | Your pastes can be encrypted to ensure only people you trust will be able to read them. Encrypting your paste means you need to provide the same password you used to encrypt paste to decrypt and thus read it. Since the encryption and decryption are done locally on your computer, the administrators of this site won't be able to recover the content of your encrypted paste if you lose your password. 23 |

    24 |

    25 | How does the paste history feature work?
    26 | If you have uploaded your paste while logged in, you can update your paste later on. Paste history means the earlier versions of your paste remain available. 27 |

    28 |

    29 | Can I comment on pastes?
    30 | Yes, if you have registered you can comment on pastes. 31 |

    32 |

    33 | What software does this site run on?
    34 | This pastebin site runs on pastebin-django, a web application developed using the Django web framework. 35 |

    36 |
    37 |
    38 |
    39 | {% endblock content %} -------------------------------------------------------------------------------- /home/jinja2/home/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
    5 |
    6 |
    7 | 10 | {% include "home/submit_paste_form.html" %} 11 |
    12 |
    13 | {% include "home/latest_pastes.html" %} 14 |
    15 |
    16 |
    17 | {% endblock %} 18 | {% block extra_js %} 19 | 20 | 21 | {% endblock %} -------------------------------------------------------------------------------- /home/jinja2/home/latest_pastes.html: -------------------------------------------------------------------------------- 1 | 4 | {% if latest_pastes %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for paste in latest_pastes %} 14 | 15 | 16 | 17 | 18 | {% endfor %} 19 | 20 |
    TitleAge
    {{ paste.title|truncate(32) }}{{ seconds_to_str(timesince_in_seconds(paste.submitted)) }}
    21 | {% else %} 22 |

    No pastes have been submitted yet. :(

    23 | {% endif %} -------------------------------------------------------------------------------- /home/jinja2/home/submit_paste_form.html: -------------------------------------------------------------------------------- 1 |
    2 | {% csrf_token %} 3 |
    4 | 5 |
    6 |
    7 | 8 |
    9 |
    10 | {% with homepage=True %}{% include "home/submit_paste_tabs.html" %}{% endwith %} 11 | {% if captcha_form %} 12 |
    13 | {% with form=captcha_form %}{% include "form.html" %}{% endwith %} 14 | {% endif %} 15 | 16 |
    17 |
    18 | 19 |
    20 |
    21 |
    22 |
23 | -------------------------------------------------------------------------------- /home/jinja2/home/submit_paste_tabs.html: -------------------------------------------------------------------------------- 1 | 5 |
6 |
7 | {% if request.user.is_authenticated() %} 8 |
9 |
10 |
11 | {% if homepage %} 12 |

You are uploading this paste as user {{ request.user.get_username() }}

13 | {% endif %} 14 |
15 |
16 | {% endif %} 17 | {# Print errors related to the text field here #} 18 | {% if form.text.errors %} 19 |
20 | {{ form.text.errors }} 21 |
22 | {% endif %} 23 | {% with form_label_col=2, form_field_col=8, exclude="text,syntax_highlighting" %}{% include "form.html" %}{% endwith %} 24 | {% include "home/submit_paste_languages.html" %} 25 |
26 |
27 |

You can encrypt your paste to prevent people from viewing the content of your paste unless a correct password is provided. The encryption and decryption are performed client-side, meaning we never receive an unencrypted copy of the paste.

28 |

Note the following:

29 |
    30 |
  • If you lose your password, we can't recover the content of your paste.
  • 31 |
  • Syntax highlighting doesn't work with all languages if your paste is encrypted.
  • 32 |
33 | 34 |
35 |
36 |
37 | 40 |
41 |
42 |
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 | 82 | -------------------------------------------------------------------------------- /home/jinja2/latest_pastes/latest_pastes.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Latest pastes - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 | {% if total_paste_count > 0 %} 13 | 14 | 15 | 16 | 17 | 18 | {% if request.user.is_staff %} 19 | 20 | {% endif %} 21 | 22 | 23 | 24 | {% for paste in pastes %} 25 | 26 | 27 | 28 | {% if request.user.is_staff %} 29 | 33 | {% endif %} 34 | 35 | {% endfor %} 36 | 37 |
TitleAgeActions
{{ paste.title|truncate(64) }}{{ seconds_to_str(timesince_in_seconds(paste.submitted)) }} 30 | Edit 31 | Remove 32 |
38 | {% with destination="latest_pastes" %}{% include "pagination.html" %}{% endwith %} 39 | {% else %} 40 |
41 | No pastes uploaded
42 | No public pastes have been uploaded yet. 43 |
44 | {% endif %} 45 |
46 |
47 |
48 | {% endblock %} -------------------------------------------------------------------------------- /home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/home/migrations/__init__.py -------------------------------------------------------------------------------- /home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /home/templates/home/faq.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Frequently asked questions - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |

13 | What is pastebin-django?
14 | pastebin-django is a site that allows you to store 'pastes', short text snippets. 15 |

16 |

17 | What is the syntax highlighting feature?
18 | If your paste contains code or markup, you can use the syntax highlighting to highlight the text. Note that encrypted pastes don't support all of the syntax highlighting formats that are available with unencrypted pastes. 19 |

20 |

21 | How does the encryption feature work?
22 | Your pastes can be encrypted to ensure only people you trust will be able to read them. Encrypting your paste means you need to provide the same password you used to encrypt paste to decrypt and thus read it. Since the encryption and decryption are done locally on your computer, the administrators of this site won't be able to recover the content of your encrypted paste if you lose your password. 23 |

24 |

25 | How does the paste history feature work?
26 | If you have uploaded your paste while logged in, you can update your paste later on. Paste history means the earlier versions of your paste remain available. 27 |

28 |

29 | Can I comment on pastes?
30 | Yes, if you have registered you can comment on pastes. 31 |

32 |

33 | What software does this site run on?
34 | This pastebin site runs on pastebin-django, a web application developed using the Django web framework. 35 |

36 |
37 |
38 |
39 | {% endblock content %} -------------------------------------------------------------------------------- /home/templates/home/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 | {% include "home/submit_paste_form.html" %} 11 |
12 |
13 | {% include "home/latest_pastes.html" %} 14 |
15 |
16 |
17 | {% endblock %} 18 | {% block extra_js %} 19 | {% load staticfiles %} 20 | 21 | 22 | {% endblock %} -------------------------------------------------------------------------------- /home/templates/home/latest_pastes.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | 5 | {% if latest_pastes %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for paste in latest_pastes %} 15 | 16 | 17 | 18 | 19 | {% endfor %} 20 | 21 |
TitleAge
{{ paste.title|truncatechars:64 }}{{ paste.submitted|naturaltime }}
22 | {% else %} 23 |

No pastes have been submitted yet. :(

24 | {% endif %} -------------------------------------------------------------------------------- /home/templates/home/submit_paste_form.html: -------------------------------------------------------------------------------- 1 |
2 | {% csrf_token %} 3 |
4 | 5 |
6 |
7 | 8 |
9 | {% include "home/submit_paste_tabs.html" with homepage=True %} 10 | 11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /home/templates/home/submit_paste_tabs.html: -------------------------------------------------------------------------------- 1 | 5 |
6 |
7 | {% if user.is_authenticated %} 8 |
9 |
10 |
11 | {% if homepage %} 12 |

You are uploading this paste as user {{ user.get_username }}

13 | {% endif %} 14 |
15 |
16 | {% endif %} 17 | {# Print errors related to the text field here #} 18 | {% if form.text.errors %} 19 |
20 | {{ form.text.errors }} 21 |
22 | {% endif %} 23 | {% include "form.html" with form_label_col=2 form_field_col=8 exclude="text" %} 24 |
25 |
26 |

You can encrypt your paste to prevent people from viewing the content of your paste unless a correct password is provided. The encryption and decryption are performed client-side, meaning we never receive an unencrypted copy of the paste.

27 |

Note the following:

28 |
    29 |
  • If you lose your password, we can't recover the content of your paste.
  • 30 |
  • Syntax highlighting doesn't work with all languages if your paste is encrypted.
  • 31 |
32 | 33 |
34 |
35 |
36 | 39 |
40 |
41 |
42 | 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 | -------------------------------------------------------------------------------- /home/templates/latest_pastes/latest_pastes.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load humanize %} 3 | 4 | {% block title %}Latest pastes - pastebin-django{% endblock %} 5 | 6 | {% block content %} 7 |
8 |
9 |
10 | 13 | {% if total_paste_count > 0 %} 14 | 15 | 16 | 17 | 18 | 19 | {% if user.is_staff %} 20 | 21 | {% endif %} 22 | 23 | 24 | 25 | {% for paste in pastes %} 26 | 27 | 28 | 29 | {% if user.is_staff %} 30 | 34 | {% endif %} 35 | 36 | {% endfor %} 37 | 38 |
TitleAgeActions
{{ paste.title|truncatechars:64 }}{{ paste.submitted|naturaltime }} 31 | Edit 32 | Remove 33 |
39 | {% include "pagination.html" with destination="latest_pastes" %} 40 | {% else %} 41 |
42 | No pastes uploaded
43 | No public pastes have been uploaded yet. 44 |
45 | {% endif %} 46 |
47 |
48 |
49 | {% endblock %} -------------------------------------------------------------------------------- /home/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.views.generic import TemplateView 3 | 4 | from home import views 5 | 6 | urlpatterns = [ 7 | url(r'^$', views.home, name="home"), 8 | ] 9 | -------------------------------------------------------------------------------- /home/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.db.models import Q 3 | from django.core.cache import cache 4 | 5 | from django.views.decorators.cache import cache_page 6 | 7 | from django.utils import timezone 8 | 9 | from pastes.forms import SubmitPasteForm 10 | from pastes.models import Paste, LatestPastes 11 | 12 | from pastebin.util import Paginator 13 | 14 | from users.models import Limiter 15 | 16 | import highlighting 17 | import math 18 | 19 | def home(request): 20 | """ 21 | Display the index page with the form to submit a paste, as well as the most recent 22 | pastes 23 | """ 24 | paste_form = SubmitPasteForm(request.POST or None, request=request) 25 | 26 | latest_pastes = cache.get("home_latest_pastes") 27 | 28 | if latest_pastes == None: 29 | latest_pastes = Paste.objects.get_pastes(include_expired=False, include_hidden=False, 30 | count=15) 31 | cache.set("home_latest_pastes", latest_pastes, 5) 32 | 33 | languages = highlighting.settings.LANGUAGES 34 | 35 | if paste_form.is_valid(): 36 | paste_data = paste_form.cleaned_data 37 | 38 | user = None 39 | if request.user.is_authenticated(): 40 | user = request.user 41 | 42 | paste = Paste() 43 | 44 | char_id = paste.add_paste(title=paste_data["title"], 45 | user=user, 46 | text=paste_data["text"], 47 | expiration=paste_data["expiration"], 48 | visibility=paste_data["visibility"], 49 | format=paste_data["syntax_highlighting"], 50 | encrypted=paste_data["encrypted"]) 51 | 52 | Limiter.increase_action_count(request, Limiter.PASTE_UPLOAD) 53 | 54 | # Redirect to the newly created paste 55 | return redirect("show_paste", char_id=char_id) 56 | 57 | return render(request, "home/home.html", {"form": paste_form, 58 | "latest_pastes": latest_pastes, 59 | "languages": languages }) 60 | 61 | def latest_pastes(request, page=1): 62 | """ 63 | Show all of the pastes starting from the newest 64 | """ 65 | PASTES_PER_PAGE = 15 66 | 67 | page = int(page) 68 | 69 | current_datetime = timezone.now() 70 | 71 | total_paste_count = cache.get("total_latest_pastes_count") 72 | 73 | if total_paste_count == None: 74 | total_paste_count = Paste.objects.filter(hidden=False).filter(Q(expiration_datetime__isnull=True) | Q(expiration_datetime__gte=current_datetime)).count() 75 | cache.set("total_latest_pastes_count", total_paste_count) 76 | 77 | total_pages = int(math.ceil(float(total_paste_count) / float(PASTES_PER_PAGE))) 78 | if page > total_pages: 79 | page = max(int(total_pages), 1) 80 | 81 | offset = (page-1) * PASTES_PER_PAGE 82 | pastes = cache.get("latest_pastes:%s" % page) 83 | 84 | if pastes == None: 85 | pastes = Paste.objects.get_pastes(count=PASTES_PER_PAGE, offset=offset, include_hidden=False) 86 | cache.set("latest_pastes:%s" % page, pastes, 5) 87 | 88 | pages = Paginator.get_pages(page, PASTES_PER_PAGE, total_paste_count) 89 | total_pages = math.ceil(float(total_paste_count) / float(PASTES_PER_PAGE)) 90 | 91 | return render(request, "latest_pastes/latest_pastes.html", {"current_page": page, 92 | "pastes": pastes, 93 | "pages": pages, 94 | "total_pages": total_pages, 95 | "total_paste_count": total_paste_count}) 96 | 97 | def faq(request): 98 | return render(request, "home/faq.html") 99 | 100 | def random_paste(request): 101 | """ 102 | Redirect to a random paste 103 | """ 104 | char_id = Paste.get_random_char_id() 105 | 106 | if char_id: 107 | return redirect("show_paste", char_id=char_id) 108 | else: 109 | return redirect("home:home") -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pastebin.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /pastebin/.directory: -------------------------------------------------------------------------------- 1 | [Dolphin] 2 | Timestamp=2015,4,7,18,32,36 3 | Version=3 4 | -------------------------------------------------------------------------------- /pastebin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/pastebin/__init__.py -------------------------------------------------------------------------------- /pastebin/debug_settings.py: -------------------------------------------------------------------------------- 1 | from settings import * 2 | 3 | DEBUG = True 4 | -------------------------------------------------------------------------------- /pastebin/jinja2.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import # Python 2 only 2 | 3 | from jinja2 import Environment 4 | 5 | from pastebin import jinja_globals 6 | 7 | def environment(**options): 8 | env = Environment(**options) 9 | 10 | # Add methods ported from Django to namespace 11 | env.globals.update(jinja_globals.JINJA_GLOBALS) 12 | return env -------------------------------------------------------------------------------- /pastebin/manager.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.cache import cache 3 | 4 | class CachedManager(models.Manager): 5 | """ 6 | A custom manager that implements simple caching that is used when fetching single entries 7 | """ 8 | # Key that will be used for caching 9 | # Eg. with ["char_id", "id"] the model will be saved and retrieved from cache but only 10 | # if the get() method is given a single char_id OR id argument 11 | cache_keys = [] 12 | cache_name = None 13 | 14 | def get(self, *args, **kwargs): 15 | """ 16 | If only one argument is passed and it's in cache_keys, the result 17 | will be attempted to be pulled from cache 18 | """ 19 | if len(kwargs) == 1 and kwargs[kwargs.keys()[0]] in self.cache_keys: 20 | cache_key = kwargs[kwargs.keys()[0]] 21 | result = cache.get("%s:%s" % (self.cache_name, cached_key)) 22 | 23 | if result != None: 24 | return result 25 | 26 | result = super(CachedManager, self).get(*args, **kwargs) 27 | 28 | for cache_key in cache_keys: 29 | cache.set("%s:%s" % (self.cache_name, cache_key), result) 30 | 31 | return result -------------------------------------------------------------------------------- /pastebin/middleware.py: -------------------------------------------------------------------------------- 1 | from pastes.models import PasteReport 2 | from django.core.signals import Signal 3 | from django.dispatch import receiver 4 | from django.contrib.auth.signals import user_logged_in, user_logged_out 5 | 6 | class PastebinMiddleware(object): 7 | def process_request(self, request): 8 | """ 9 | Include the amount of unread paste reports if the current user is an admin 10 | """ 11 | add_data_into_request(request) 12 | 13 | @receiver(user_logged_in) 14 | def on_user_logged_in(sender, **kwargs): 15 | add_data_into_request(kwargs["request"]) 16 | 17 | def add_data_into_request(request): 18 | if request.user.is_authenticated(): 19 | if request.user.is_staff: 20 | request.unread_paste_report_count = PasteReport.objects.filter(checked=False).count() 21 | 22 | @receiver(user_logged_out) 23 | def remove_data_from_request(sender, **kwargs): 24 | request = kwargs["request"] 25 | 26 | try: 27 | del request.unread_paste_report_count 28 | except AttributeError: 29 | pass -------------------------------------------------------------------------------- /pastebin/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/pastebin/templatetags/__init__.py -------------------------------------------------------------------------------- /pastebin/testcase.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | from django.core.cache import cache 4 | 5 | from django_redis import get_redis_connection 6 | 7 | class CacheAwareTestCase(TestCase): 8 | """ 9 | Cache-aware TestCase that clears the Redis storage and cache on startup 10 | """ 11 | def clearCache(self): 12 | """ 13 | Clears the cache 14 | 15 | Can be invoked manually if the unit test requires it 16 | """ 17 | cache.clear() 18 | con = get_redis_connection("persistent") 19 | 20 | con.flushall() 21 | 22 | def setUp(self): 23 | super(CacheAwareTestCase, self).setUp() 24 | 25 | self.clearCache() -------------------------------------------------------------------------------- /pastebin/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.views.generic import TemplateView 3 | from django.contrib import admin 4 | 5 | from pastes import views as paste_views 6 | from home import views as home_views 7 | 8 | urlpatterns = [ 9 | # Examples: 10 | # url(r'^$', 'pastebin.views.home', name='home'), 11 | # url(r'^blog/', include('blog.urls')), 12 | 13 | # Django's in-built admin interface 14 | url(r'^admin/', include(admin.site.urls)), 15 | 16 | # Home page 17 | #url(r'^$', TemplateView.as_view(template_name='homepage.html'), name="homepage"), 18 | url(r'^$', include("home.urls", namespace="home")), 19 | 20 | url(r'^latest_pastes/(?P\d+)/$', home_views.latest_pastes, name="latest_pastes"), 21 | url(r'^latest_pastes/$', home_views.latest_pastes, name="latest_pastes"), 22 | 23 | url(r'^faq/$', home_views.faq, name="faq"), 24 | 25 | url(r'^random/$', home_views.random_paste, name="random_paste"), 26 | 27 | url(r'^pastes/', include("pastes.urls", namespace="pastes")), 28 | url(r'^users/', include("users.urls", namespace="users")), 29 | url(r'^comments/', include("comments.urls", namespace="comments")), 30 | 31 | url(r'^(?P\w{8})/raw/(?P\d+)/$', paste_views.show_paste, {"raw": True}, name="raw_paste"), 32 | url(r'^(?P\w{8})/raw/$', paste_views.show_paste, {"raw": True}, name="raw_paste"), 33 | 34 | url(r'^(?P\w{8})/download/(?P\d+)/$', paste_views.show_paste, {"download": True}, name="download_paste"), 35 | url(r'^(?P\w{8})/download/$', paste_views.show_paste, {"download": True}, name="download_paste"), 36 | 37 | url(r'^(?P\w{8})/(?P\d+)/$', paste_views.show_paste, name="show_paste"), 38 | url(r'^(?P\w{8})/$', paste_views.show_paste, name="show_paste"), 39 | ] 40 | -------------------------------------------------------------------------------- /pastebin/util.py: -------------------------------------------------------------------------------- 1 | import math 2 | import datetime 3 | 4 | class Paginator(object): 5 | @staticmethod 6 | def get_pages(current_page, entries_per_page, total_entries): 7 | entries = [] 8 | 9 | total_pages = math.ceil(float(total_entries) / float(entries_per_page)) 10 | 11 | # Add the first and previous pages 12 | if current_page != 1: 13 | entries.append("first") 14 | entries.append("previous") 15 | 16 | # Add four pages before the current page 17 | for i in reversed(range(0, 4)): 18 | page = current_page - i - 1 19 | 20 | if page >= 1: 21 | entries.append(page) 22 | 23 | # Add the current page 24 | entries.append(current_page) 25 | 26 | # Add four pages after the current page 27 | for i in range(0, 4): 28 | page = current_page + i + 1 29 | 30 | if page <= total_pages: 31 | entries.append(page) 32 | 33 | # Add the next and last page 34 | if current_page != total_pages: 35 | entries.append("next") 36 | entries.append("last") 37 | 38 | return entries 39 | 40 | def queryset_to_list(queryset, fields=[], datetime_to_unix=True): 41 | """ 42 | Converts a given Queryset into a list of dicts 43 | 44 | fields takes a list of model fields to retrieve. In addition, field lookups can be used 45 | and an equals sign can be used to separate the field lookup and the name to be used for the field 46 | in the result 47 | (eg. user__username=username) 48 | 49 | If datetime_to_unix is True, convert datetime objects to Unix timestamps 50 | """ 51 | if queryset.count() == 0: 52 | return [] 53 | 54 | # A dict of fields 55 | # Key describes the field lookup to use, 56 | # value describes the key to use for the retrieved value 57 | # Eg. user__username=username will have the key 'username' in the dict 58 | field_names = {} 59 | 60 | for field_name in fields: 61 | if not "=" in field_name: 62 | field_names[field_name] = field_name 63 | else: 64 | field_names[field_name[:field_name.find("=")]] = field_name[field_name.find("=")+1:] 65 | 66 | rows = list(queryset.values(*field_names.keys())) 67 | 68 | for i, row in enumerate(rows): 69 | for row_key, row_entry in row.iteritems(): 70 | field_name = field_names[row_key] 71 | 72 | # Rename the field if we want a different name 73 | if row_key != field_name: 74 | value = row[row_key] 75 | del rows[i][row_key] 76 | rows[i][field_name] = value 77 | 78 | if datetime_to_unix: 79 | if type(rows[i][field_name]) is datetime.datetime: 80 | rows[i][field_name] = int(rows[i][field_name].strftime("%s")) 81 | 82 | return rows -------------------------------------------------------------------------------- /pastebin/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for pastebin 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/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pastebin.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /pastes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/pastes/__init__.py -------------------------------------------------------------------------------- /pastes/admin.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.core.urlresolvers import reverse 3 | from django.contrib import admin 4 | from django.contrib.contenttypes.models import ContentType 5 | from django.http import HttpResponseRedirect 6 | 7 | from pastes.models import Paste, PasteReport 8 | from pastes import admin_views 9 | 10 | class PasteAdmin(admin.ModelAdmin): 11 | list_display = ("title", "user", "submitted") 12 | 13 | class PasteReportAdmin(admin.ModelAdmin): 14 | list_display = ("checked", "user", "paste", "text", "type") 15 | actions = ["mark_report_as_read", "process_reports"] 16 | ordering = ("checked",) 17 | 18 | def get_urls(self): 19 | urls = super(PasteReportAdmin, self).get_urls() 20 | my_urls = [ 21 | url(r'^process_report/(?P.*)/$', admin_views.process_report, name="pastes_pastereport_process_report"), 22 | ] 23 | 24 | return my_urls + urls 25 | 26 | def mark_report_as_read(self, request, queryset): 27 | """ 28 | Mark report as read without responding to the report 29 | """ 30 | reports = queryset.update(checked=True) 31 | 32 | self.message_user(request, "%s report(s) were marked as read." % reports) 33 | 34 | def process_reports(self, request, queryset): 35 | """ 36 | Process reports so that an action is performed on the paste that was reported 37 | """ 38 | selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME) 39 | ct = ContentType.objects.get_for_model(queryset.model) 40 | return HttpResponseRedirect(reverse("admin:pastes_pastereport_process_report", kwargs={"report_ids": ",".join(selected)})) 41 | 42 | admin.site.register(Paste, PasteAdmin) 43 | admin.site.register(PasteReport, PasteReportAdmin) -------------------------------------------------------------------------------- /pastes/admin_forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | class ProcessReportForm(forms.Form): 4 | """ 5 | Form to process a set of paste reports 6 | """ 7 | removal_reason = forms.CharField(max_length=1024, 8 | required=False, 9 | initial="") -------------------------------------------------------------------------------- /pastes/admin_views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponse 3 | from django.core.exceptions import ObjectDoesNotExist 4 | 5 | from django.db import transaction 6 | 7 | from pastes.models import Paste, PasteReport 8 | 9 | from pastes.admin_forms import ProcessReportForm 10 | 11 | def process_report(request, report_ids): 12 | """ 13 | Process a report for a paste 14 | """ 15 | if not request.user.is_authenticated or not request.user.is_staff: 16 | return HttpResponse("You are not an admin!", status=422) 17 | 18 | reports = PasteReport.objects.filter(id__in=report_ids.split(",")) 19 | 20 | pastes = None 21 | paste_ids = [] 22 | 23 | for report in reports: 24 | if report.paste.id not in paste_ids: 25 | paste_ids.append(report.paste.id) 26 | 27 | pastes = Paste.objects.filter(id__in=paste_ids) 28 | 29 | paste_results = [] 30 | 31 | for paste in pastes: 32 | result = {"paste": paste, 33 | "paste_text": paste.get_text(False)} 34 | paste_results.append(result) 35 | 36 | form = ProcessReportForm(request.POST or None) 37 | 38 | if form.is_valid(): 39 | cleaned_data = form.cleaned_data 40 | if "ignore" in request.POST: 41 | reports.update(checked=True) 42 | 43 | return render(request, "pastes/admin/process_report/process_success.html", {"action": "ignore"}) 44 | elif "remove" in request.POST: 45 | with transaction.atomic(): 46 | for paste in pastes: 47 | paste.remove_paste(reason=cleaned_data["removal_reason"]) 48 | 49 | reports.update(checked=True) 50 | 51 | return render(request, "pastes/admin/process_report/process_success.html", {"action": "remove"}) 52 | elif "delete" in request.POST: 53 | with transaction.atomic(): 54 | for paste in pastes: 55 | paste.delete_paste(reason=cleaned_data["removal_reason"]) 56 | 57 | reports.update(checked=True) 58 | 59 | return render(request, "pastes/admin/process_report/process_success.html", {"action": "delete"}) 60 | 61 | return render(request, "pastes/admin/process_report/process.html", {"reports": reports, 62 | "report_ids": report_ids, 63 | 64 | "paste_results": paste_results}) -------------------------------------------------------------------------------- /pastes/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from pastebin import settings 4 | from pastes.models import Paste 5 | from users.models import Limiter 6 | 7 | from humanfriendly import format_timespan 8 | 9 | import highlighting 10 | 11 | class SubmitPasteForm(forms.Form): 12 | """ 13 | Form to submit the paste 14 | 15 | Contains paste text, title and optionally, time until expiration 16 | """ 17 | VISIBILITY_CHOICES = ( 18 | (Paste.PUBLIC, "Public"), 19 | (Paste.HIDDEN, "Hidden") 20 | ) 21 | 22 | EXPIRATION_CHOICES = ( 23 | (Paste.NEVER, "Never"), 24 | (Paste.FIFTEEN_MINUTES, "15 minutes"), 25 | (Paste.ONE_HOUR, "1 hour"), 26 | (Paste.ONE_DAY, "1 day"), 27 | (Paste.ONE_WEEK, "1 week"), 28 | (Paste.ONE_MONTH, "1 month"), 29 | ) 30 | 31 | title = forms.CharField(max_length=128, 32 | required=False, 33 | widget=forms.TextInput(attrs={"placeholder": "Untitled"})) 34 | text = forms.CharField(min_length=1, 35 | max_length=100000, 36 | error_messages={"required": "The paste can't be empty."}) 37 | expiration = forms.ChoiceField(choices=EXPIRATION_CHOICES) 38 | 39 | visibility = forms.ChoiceField(choices=VISIBILITY_CHOICES) 40 | 41 | syntax_highlighting = forms.ChoiceField(choices=highlighting.settings.LANGUAGES, 42 | help_text="Languages marked with * are also supported with encrypted pastes.") 43 | 44 | encrypted = forms.BooleanField(initial=False, 45 | widget=forms.HiddenInput(), 46 | required=False) 47 | 48 | def __init__(self, *args, **kwargs): 49 | self.request = kwargs.pop('request', None) 50 | 51 | if self.request == None: 52 | raise AttributeError("'%s' requires a valid Django request object as its request parameter" % self.__class__.__name__) 53 | 54 | super(SubmitPasteForm, self).__init__(*args, **kwargs) 55 | 56 | def clean_title(self): 57 | """ 58 | Replace the title with Untitled if it is not provided 59 | """ 60 | title = self.cleaned_data.get("title") 61 | 62 | # If user provides an empty title, replace it with Untitled 63 | if title.strip() == "": 64 | title = "Untitled" 65 | 66 | return title 67 | 68 | def clean_text(self): 69 | """ 70 | Check that the user hasn't uploaded too many pastes 71 | """ 72 | if Limiter.is_limit_reached(self.request, Limiter.PASTE_UPLOAD): 73 | action_limit = Limiter.get_action_limit(self.request, Limiter.PASTE_UPLOAD) 74 | 75 | raise forms.ValidationError("You can only upload %s pastes every %s." % (action_limit, format_timespan(settings.MAX_PASTE_UPLOADS_PERIOD))) 76 | 77 | return self.cleaned_data.get("text") 78 | 79 | class EditPasteForm(forms.Form): 80 | """ 81 | Form to edit the paste 82 | """ 83 | note = forms.CharField(max_length=1024, 84 | required=False, 85 | help_text="Optional note describing what was changed in the paste") 86 | title = forms.CharField(max_length=128, 87 | required=False, 88 | widget=forms.TextInput(attrs={"placeholder": "Untitled"})) 89 | visibility = forms.ChoiceField(choices=SubmitPasteForm.VISIBILITY_CHOICES) 90 | 91 | syntax_highlighting = forms.ChoiceField(choices=highlighting.settings.LANGUAGES, 92 | help_text="Languages marked with * are also supported with encrypted pastes.") 93 | text = forms.CharField(min_length=1, 94 | max_length=100000, 95 | error_messages={"required": "The paste can't be empty."}) 96 | 97 | encrypted = forms.BooleanField(initial=False, 98 | widget=forms.HiddenInput(), 99 | required=False) 100 | 101 | def __init__(self, *args, **kwargs): 102 | self.request = kwargs.pop('request', None) 103 | 104 | if self.request == None: 105 | raise AttributeError("'%s' requires a valid Django request object as its request parameter" % self.__class__.__name__) 106 | 107 | super(EditPasteForm, self).__init__(*args, **kwargs) 108 | 109 | def clean_title(self): 110 | """ 111 | Replace an empty title with "Untitled" 112 | """ 113 | title = self.cleaned_data.get("title") 114 | 115 | if title.strip() == "": 116 | title = "Untitled" 117 | 118 | return title 119 | 120 | def clean_text(self): 121 | """ 122 | Check that the user hasn't edited too many pastes 123 | """ 124 | if Limiter.is_limit_reached(self.request, Limiter.PASTE_EDIT): 125 | action_limit = Limiter.get_action_limit(self.request, Limiter.PASTE_EDIT) 126 | 127 | raise forms.ValidationError("You can only edit pastes %s times every %s." % (action_limit, format_timespan(settings.MAX_PASTE_EDITS_PERIOD))) 128 | 129 | return self.cleaned_data.get("text") 130 | 131 | class RemovePasteForm(forms.Form): 132 | """ 133 | Form to remove the paste 134 | """ 135 | removal_reason = forms.CharField(max_length=512, 136 | required=False, 137 | help_text="You can provide a reason why you removed the paste.") 138 | 139 | class ReportPasteForm(forms.Form): 140 | """ 141 | Form to report a paste 142 | """ 143 | REASONS = ( 144 | ("illegal_content", "Illegal content"), 145 | ("adult_content", "Adult content"), 146 | ("spam", "Spam"), 147 | ("personal_information", "Personal information"), 148 | ("other", "Other") 149 | ) 150 | 151 | reason = forms.ChoiceField(choices=REASONS) 152 | text = forms.CharField(min_length=1, 153 | max_length=4096, 154 | required=False, 155 | widget=forms.Textarea) -------------------------------------------------------------------------------- /pastes/jinja2/pastes/edit_paste/edit_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "not_found" %} 12 | Paste not found
13 | The paste you tried to edit was not found. 14 | {% elif reason == "not_logged_in" %} 15 | Not logged in
16 | You have to be logged in to edit your own pastes. 17 | {% elif reason == "not_owner" %} 18 | Not permitted
19 | You can't edit a paste you didn't upload yourself. 20 | {% elif reason == "removed" %} 21 | Paste removed
22 | This paste has been removed and can no longer be edited. 23 | {% elif reason == "expired" %} 24 | Paste expired
25 | This paste has expired and can no longer be edited. 26 | {% endif %} 27 |
28 |
29 |
30 |
31 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/edit_paste/edit_paste.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Edit: {{ paste.title }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | {% csrf_token %} 14 |
15 | 16 |
17 | {% if paste.encrypted %} 18 |
19 |
20 | Editing an encrypted paste
21 | You are editing an encrypted paste. You will need to encrypt your paste again if you wish to keep your paste encrypted. 22 |
23 | {% endif %} 24 |
25 |
26 | {% include "home/submit_paste_tabs.html" %} 27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {% endblock %} 38 | {% block extra_js %} 39 | 40 | 41 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/paste_history/paste_history.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 | {% if total_version_count > 0 %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for version in history %} 21 | 22 | 23 | 24 | 25 | 26 | {% endfor %} 27 | 28 |
TitleNoteUpdated
{{ version.title|truncate(64) }}{% if version.note %}{{ version.note }}{% else %}No note provided{% endif %}{{ seconds_to_str(timesince_in_seconds(version.submitted)) }}
29 | {% with destination="pastes:history", url_arg=paste.char_id %}{% include "pagination.html" %}{% endwith %} 30 | {% else %} 31 |
32 | No paste history
33 | This paste doesn't have any paste version entries. This shouldn't happen. 34 |
35 | {% endif %} 36 |
37 |
38 |
39 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/remove_paste/paste_removed.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | The paste was successfully removed. 12 |
13 |
14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/remove_paste/remove_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "not_found" %} 12 | Paste not found
13 | The paste you tried to remove was not found. 14 | {% elif reason == "not_logged_in" %} 15 | Not logged in
16 | You have to be logged in to remove your own pastes. 17 | {% elif reason == "not_owner" %} 18 | Not permitted
19 | You can't remove a paste you didn't upload yourself. It would be really bad if you could. 20 | {% elif reason == "removed" %} 21 | Paste removed
22 | This paste has been removed and can no longer be removed. 23 | {% elif reason == "expired" %} 24 | Paste expired
25 | This paste has expired and can no longer be removed. 26 | {% endif %} 27 |
28 |
29 |
30 |
31 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/remove_paste/remove_paste.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Remove: {{ paste.title }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | You are removing the paste {{ paste.title }}.

In addition, someone who found your paste useful and wanted to share it with someone might be extremely pissed off to find out you had removed it. 14 |
15 |
16 | {% csrf_token %} 17 | {% with form=verify_form %}{% include "form.html" %}{% endwith %} 18 | {% with form=remove_form %}{% include "form.html" %}{% endwith %} 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/report_paste/paste_reported.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | The paste report was sent successfully. 12 |
13 |
14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/report_paste/report_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "not_found" %} 12 | Paste not found
13 | The paste you tried to report was not found. 14 | {% elif reason == "removed" %} 15 | Paste removed
16 | This paste has been removed and can no longer be reported. 17 | {% elif reason == "expired" %} 18 | Paste expired
19 | This paste has expired and can no longer be reported. 20 | {% endif %} 21 |
22 |
23 |
24 |
25 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/report_paste/report_paste.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Report: {{ paste.title }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | You are reporting the paste {{ paste.title }}. 14 |
15 |
16 | {% csrf_token %} 17 | {% include "form.html" %} 18 | {% with form=captcha_form %}{% include "form.html" %}{% endwith %} 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 | {% endblock %} -------------------------------------------------------------------------------- /pastes/jinja2/pastes/show_paste/show_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | {% if reason == "expired" %} 10 | Paste expired
11 | The paste you tried to view has expired and can no longer be viewed. 12 | {% elif reason == "user_removed" %} 13 | Paste removed

14 | The paste was removed by the uploader. 15 | {% elif reason == "admin_removed" %} 16 | Paste removed
17 | The paste was removed by an administrator.

18 | {% elif reason == "not_found" %} 19 | Paste not found
20 | The paste you tried to view does not exist.

21 | {% endif %} 22 | {% if removal_reason %} 23 | Reason for removal:
24 |

{{ removal_reason }}

25 | {% endif %} 26 |
27 |
28 |
29 |
30 | {% endblock %} -------------------------------------------------------------------------------- /pastes/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Paste', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('char_id', models.CharField(max_length=8)), 20 | ('title', models.CharField(max_length=128)), 21 | ('format', models.CharField(max_length=32)), 22 | ('hash', models.CharField(max_length=64)), 23 | ('expiration_datetime', models.DateTimeField(null=True, blank=True)), 24 | ('hidden', models.BooleanField(default=False)), 25 | ('submitted', models.DateTimeField(auto_now_add=True)), 26 | ('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True)), 27 | ], 28 | options={ 29 | }, 30 | bases=(models.Model,), 31 | ), 32 | migrations.CreateModel( 33 | name='PasteContent', 34 | fields=[ 35 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 36 | ('hash', models.CharField(max_length=64)), 37 | ('format', models.CharField(max_length=32)), 38 | ('text', models.TextField()), 39 | ], 40 | options={ 41 | }, 42 | bases=(models.Model,), 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /pastes/migrations/0002_paste_encrypted.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='paste', 16 | name='encrypted', 17 | field=models.BooleanField(default=False), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /pastes/migrations/0003_auto_20150525_1612.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('pastes', '0002_paste_encrypted'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='PasteReport', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('type', models.CharField(max_length=32)), 21 | ('text', models.TextField()), 22 | ('checked', models.BooleanField(default=False)), 23 | ('paste', models.ForeignKey(to='pastes.Paste')), 24 | ('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True)), 25 | ], 26 | options={ 27 | }, 28 | bases=(models.Model,), 29 | ), 30 | migrations.AddField( 31 | model_name='paste', 32 | name='deleted', 33 | field=models.BooleanField(default=False), 34 | preserve_default=True, 35 | ), 36 | migrations.AddField( 37 | model_name='paste', 38 | name='removal_reason', 39 | field=models.TextField(default=b''), 40 | preserve_default=True, 41 | ), 42 | migrations.AddField( 43 | model_name='paste', 44 | name='removed', 45 | field=models.IntegerField(default=0), 46 | preserve_default=True, 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /pastes/migrations/0004_auto_20150601_1634.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0003_auto_20150525_1612'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='PasteVersion', 16 | fields=[ 17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 18 | ('version', models.IntegerField()), 19 | ('note', models.CharField(default=b'', max_length=1024)), 20 | ('title', models.CharField(max_length=128)), 21 | ('hash', models.CharField(max_length=64)), 22 | ('format', models.CharField(max_length=64)), 23 | ('submitted', models.DateTimeField(auto_now_add=True)), 24 | ('paste', models.ForeignKey(to='pastes.Paste')), 25 | ], 26 | options={ 27 | }, 28 | bases=(models.Model,), 29 | ), 30 | migrations.AddField( 31 | model_name='paste', 32 | name='current_version', 33 | field=models.IntegerField(default=1), 34 | preserve_default=True, 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /pastes/migrations/0005_auto_20150601_1642.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0004_auto_20150601_1634'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RenameField( 15 | model_name='paste', 16 | old_name='current_version', 17 | new_name='version', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /pastes/migrations/0006_paste_updated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import datetime 6 | from django.utils.timezone import utc 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('pastes', '0005_auto_20150601_1642'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='paste', 18 | name='updated', 19 | field=models.DateTimeField(default=datetime.datetime(2015, 6, 1, 17, 17, 54, 386976, tzinfo=utc), auto_now=True), 20 | preserve_default=False, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /pastes/migrations/0007_auto_20150704_1510.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0006_paste_updated'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='paste', 16 | name='size', 17 | field=models.IntegerField(default=0), 18 | preserve_default=False, 19 | ), 20 | migrations.AddField( 21 | model_name='pasteversion', 22 | name='size', 23 | field=models.IntegerField(default=0), 24 | preserve_default=False, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /pastes/migrations/0008_pasteversion_encrypted.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0007_auto_20150704_1510'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='pasteversion', 16 | name='encrypted', 17 | field=models.BooleanField(default=True), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /pastes/migrations/0009_auto_20150731_1327.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0008_pasteversion_encrypted'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='paste', 16 | name='char_id', 17 | field=models.CharField(max_length=8, db_index=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AlterField( 21 | model_name='pastecontent', 22 | name='hash', 23 | field=models.CharField(max_length=64, db_index=True), 24 | preserve_default=True, 25 | ), 26 | migrations.AlterField( 27 | model_name='pasteversion', 28 | name='encrypted', 29 | field=models.BooleanField(default=False), 30 | preserve_default=True, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /pastes/migrations/0010_auto_20150731_1746.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('pastes', '0009_auto_20150731_1327'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='paste', 16 | name='submitted', 17 | field=models.DateTimeField(auto_now_add=True, db_index=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AlterField( 21 | model_name='paste', 22 | name='updated', 23 | field=models.DateTimeField(auto_now=True, db_index=True), 24 | preserve_default=True, 25 | ), 26 | migrations.AlterField( 27 | model_name='pastereport', 28 | name='checked', 29 | field=models.BooleanField(default=False, db_index=True), 30 | preserve_default=True, 31 | ), 32 | migrations.AlterField( 33 | model_name='pasteversion', 34 | name='submitted', 35 | field=models.DateTimeField(auto_now_add=True, db_index=True), 36 | preserve_default=True, 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /pastes/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/pastes/migrations/__init__.py -------------------------------------------------------------------------------- /pastes/templates/pastes/admin/process_report/process.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load staticfiles %} 3 | 4 | {% block content %} 5 |
6 | {% csrf_token %} 7 |
8 |

Reports

9 | {% for report in reports %} 10 |
11 | {{ report.user.username }} -> {{ report.paste }} 12 |
13 |
14 |
{{ report.text }}
15 |
16 | {% endfor %} 17 |
18 |
19 |

Reported pastes

20 | {% for paste in paste_results %} 21 |
22 |
23 |

{{ paste.paste.title }}

24 |
25 |
26 |
27 |
28 |
{{ paste.paste_text|truncatechars:"2048" }}
29 |
30 |
31 | {% endfor %} 32 |
33 |
34 |

Removal note

35 |
36 |
37 | 38 | 39 |
40 |
41 |
42 |
43 | 44 | 45 | 46 |
47 |
48 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/admin/process_report/process_error.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% block content %} 3 |

Error occurred while processing reports

4 | {% endif %} 5 |
6 | Go back to report list 7 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/admin/process_report/process_success.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% block content %} 3 |

Report(s) were successfully processed

4 | {% if action == "ignore" %} 5 |

The reports were marked as checked and ignored.

6 | {% elif action == "remove" %} 7 |

The paste(s) were removed.

8 | {% elif action == "delete" %} 9 |

The paste(s) were deleted permanently.

10 | {% endif %} 11 |
12 | Go back to report list 13 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/edit_paste/edit_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "not_found" %} 12 | Paste not found
13 | The paste you tried to edit was not found. 14 | {% elif reason == "not_logged_in" %} 15 | Not logged in
16 | You have to be logged in to edit your own pastes. 17 | {% elif reason == "not_owner" %} 18 | Not permitted
19 | You can't edit a paste you didn't upload yourself. 20 | {% elif reason == "removed" %} 21 | Paste removed
22 | This paste has been removed and can no longer be edited. 23 | {% elif reason == "expired" %} 24 | Paste expired
25 | This paste has expired and can no longer be edited. 26 | {% endif %} 27 |
28 |
29 |
30 |
31 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/edit_paste/edit_paste.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Edit: {{ paste.title }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | {% csrf_token %} 14 |
15 | 16 |
17 | {% if paste.encrypted %} 18 |
19 |
20 | Editing an encrypted paste
21 | You are editing an encrypted paste. You will need to encrypt your paste again if you wish to keep your paste encrypted. 22 |
23 | {% endif %} 24 |
25 |
26 | {% include "home/submit_paste_tabs.html" %} 27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {% endblock %} 38 | {% block extra_js %} 39 | {% load staticfiles %} 40 | 41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/paste_history/paste_history.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load humanize %} 3 | 4 | {% block content %} 5 |
6 |
7 |
8 | 11 | {% if total_version_count > 0 %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for version in history %} 22 | 23 | 24 | 25 | 26 | 27 | {% endfor %} 28 | 29 |
TitleNoteUpdated
{{ version.title|truncatechars:64 }}{% if version.note %}{{ version.note }}{% else %}No note provided{% endif %}{{ version.submitted|naturaltime }}
30 | {% include "pagination.html" with destination="pastes:history" url_arg=paste.char_id %} 31 | {% else %} 32 |
33 | No paste history
34 | This paste doesn't have any paste version entries. This shouldn't happen. 35 |
36 | {% endif %} 37 |
38 |
39 |
40 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/remove_paste/paste_removed.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | The paste was successfully removed. 12 |
13 |
14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/remove_paste/remove_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "not_found" %} 12 | Paste not found
13 | The paste you tried to remove was not found. 14 | {% elif reason == "not_logged_in" %} 15 | Not logged in
16 | You have to be logged in to remove your own pastes. 17 | {% elif reason == "not_owner" %} 18 | Not permitted
19 | You can't remove a paste you didn't upload yourself. It would be really bad if you could. 20 | {% elif reason == "removed" %} 21 | Paste removed
22 | This paste has been removed and can no longer be removed. 23 | {% elif reason == "expired" %} 24 | Paste expired
25 | This paste has expired and can no longer be removed. 26 | {% endif %} 27 |
28 |
29 |
30 |
31 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/remove_paste/remove_paste.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Remove: {{ paste.title }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | You are removing the paste {{ paste.title }}.

In addition, someone who found your paste useful and wanted to share it with someone might be extremely pissed off to find out you had removed it. 14 |
15 |
16 | {% csrf_token %} 17 | {% include "form.html" with form=verify_form %} 18 | {% include "form.html" with form=remove_form %} 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/report_paste/paste_reported.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | The paste report was sent successfully. 12 |
13 |
14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/report_paste/report_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "not_found" %} 12 | Paste not found
13 | The paste you tried to report was not found. 14 | {% elif reason == "removed" %} 15 | Paste removed
16 | This paste has been removed and can no longer be reported. 17 | {% elif reason == "expired" %} 18 | Paste expired
19 | This paste has expired and can no longer be reported. 20 | {% endif %} 21 |
22 |
23 |
24 |
25 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/report_paste/report_paste.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Report: {{ paste.title }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | You are reporting the paste {{ paste.title }}. 14 |
15 |
16 | {% csrf_token %} 17 | {% include "form.html" %} 18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 |
26 |
27 | {% endblock %} -------------------------------------------------------------------------------- /pastes/templates/pastes/show_paste/show_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | {% if reason == "expired" %} 10 | Paste expired
11 | The paste you tried to view has expired and can no longer be viewed. 12 | {% elif reason == "user_removed" %} 13 | Paste removed

14 | The paste was removed by the uploader. 15 | {% elif reason == "admin_removed" %} 16 | Paste removed
17 | The paste was removed by an administrator.

18 | {% elif reason == "not_found" %} 19 | Paste not found
20 | The paste you tried to view does not exist.

21 | {% endif %} 22 | {% if removal_reason %} 23 | Reason for removal:
24 |

{{ removal_reason }}

25 | {% endif %} 26 |
27 |
28 |
29 |
30 | {% endblock %} -------------------------------------------------------------------------------- /pastes/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.views.generic import TemplateView 3 | 4 | from pastes import views, admin_views 5 | 6 | urlpatterns = [ 7 | url(r'^(?P\w{8})/remove/$', views.remove_paste, name="remove_paste"), 8 | url(r'^(?P\w{8})/edit/$', views.edit_paste, name="edit_paste"), 9 | url(r'^(?P\w{8})/report/$', views.report_paste, name="report_paste"), 10 | 11 | url(r'^(?P\w{8})/history/(?P\d+)/$', views.paste_history, name="paste_history"), 12 | url(r'^(?P\w{8})/history/$', views.paste_history, {"page": 1}, name="paste_history"), 13 | 14 | url(r'^change_paste_favorite/$', views.change_paste_favorite, name="change_paste_favorite"), 15 | ] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.9 2 | Jinja2==2.8 3 | MarkupSafe==0.23 4 | Pygments==2.0.2 5 | argparse==1.2.1 6 | django-cached-authentication-middleware==0.2.1 7 | django-debug-toolbar==1.4 8 | django-ipware==1.0.0 9 | django-lineage==0.2.0 10 | django-redis==4.0.0 11 | django-widget-tweaks==1.3 12 | freezegun==0.3.3 13 | humanfriendly==1.15 14 | jinja2-django-tags==0.2 15 | psycopg2==2.6 16 | python-dateutil==2.4.2 17 | redis==2.10.3 18 | six==1.9.0 19 | sqlparse==0.1.18 20 | wsgiref==0.1.2 21 | -------------------------------------------------------------------------------- /sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/sql/__init__.py -------------------------------------------------------------------------------- /sql/add_test_data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO pastes (char_id, title, hash, hidden, submitted) VALUES 2 | ('A5vSfcXu', 'Test paste', 'a2ee48bd00238c5a5f42d71cadf1d418d01827fda4acf90fd413debec5d2c5dc', false, 3 | timestamp '2015-03-01 12:00:00'), 4 | ('d5dAASXC', 'Another test paste', 'd4b1a4af2c8852b9df30097e075ca17be6174b8e7f29437257474e3314a39f5d', false, 5 | timestamp '2015-03-01 12:00:00'); 6 | 7 | INSERT INTO paste_content (hash, text, formatted_text) VALUES 8 | ('a2ee48bd00238c5a5f42d71cadf1d418d01827fda4acf90fd413debec5d2c5dc', 9 | 'This is a test paste.', 10 | 'This is a test paste.'), 11 | ('d4b1a4af2c8852b9df30097e075ca17be6174b8e7f29437257474e3314a39f5d', 12 | 'This is another test paste. Unfortunately, this one doesn''t have any interesting content either.', 13 | 'This is another test paste. Unfortunately, this one doesn''t have any interesting content either.'); -------------------------------------------------------------------------------- /sql/create_tables.sql: -------------------------------------------------------------------------------- 1 | -- table of all the pastes 2 | CREATE TABLE pastes ( 3 | id SERIAL PRIMARY KEY, 4 | char_id CHAR(8) NOT NULL, -- 8-character identifier, used in the URL 5 | user_id INTEGER DEFAULT NULL REFERENCES auth_user(id), -- reference to registered user as defined by Django, can be NULL 6 | 7 | title VARCHAR(128) NOT NULL, -- Title of the paste 8 | format VARCHAR(32) NOT NULL, -- Formatting of the text (eg. "text" for plain text, "python" for Python code) 9 | hash CHAR(64) NOT NULL, -- Hash of the paste's text (SHA256, probably a bit overkill), 10 | -- which is then used as the identifier to the actual paste content 11 | -- This means duplicate pastes don't waste space 12 | 13 | expiration_date TIMESTAMP DEFAULT NULL, -- Date after which the paste is considered expired 14 | -- NULL if paste doesn't have an expiration date 15 | hidden BOOLEAN NOT NULL, 16 | 17 | submitted TIMESTAMP NOT NULL -- Date when the paste was submitted 18 | ); 19 | 20 | CREATE INDEX paste_id_index ON pastes(id); 21 | CREATE INDEX char_id_index ON pastes(char_id); 22 | 23 | -- Paste content is retrieved by a hash (SHA-256/MD5) 24 | -- meaning no space is wasted by duplicate pastes 25 | CREATE TABLE paste_content ( 26 | id SERIAL PRIMARY KEY, 27 | hash CHAR(64) NOT NULL, 28 | format VARCHAR(32) NOT NULL, -- Formatting of the text. If "none", the text isn't formatted and is stored in its original form 29 | text TEXT NOT NULL 30 | ); 31 | 32 | CREATE INDEX paste_content_id_index ON paste_content(id); 33 | CREATE INDEX paste_content_hash_index ON paste_content(hash); 34 | 35 | CREATE TABLE comments ( 36 | id SERIAL PRIMARY KEY, 37 | paste_id INTEGER REFERENCES pastes(id), 38 | user_id INTEGER REFERENCES auth_user(id), 39 | 40 | text TEXT NOT NULL, -- the actual comment 41 | 42 | submitted TIMESTAMP NOT NULL, 43 | edited TIMESTAMP DEFAULT NULL 44 | ); 45 | 46 | CREATE INDEX comment_id_index ON comments(id); 47 | CREATE INDEX comment_paste_id_index ON comments(paste_id); 48 | 49 | CREATE TABLE favorites ( 50 | id SERIAL PRIMARY KEY, 51 | paste_id INTEGER REFERENCES pastes(id), 52 | user_id INTEGER REFERENCES auth_user(id), 53 | added TIMESTAMP NOT NULL 54 | ); -------------------------------------------------------------------------------- /sql/cursor.py: -------------------------------------------------------------------------------- 1 | from itertools import * 2 | from django.db import connection 3 | 4 | # http://doughellmann.com/2007/12/30/using-raw-sql-in-django.html 5 | def query_to_list(query, query_args): 6 | """ 7 | Perform a query and return it as a list of dicts 8 | """ 9 | cursor = connection.cursor() 10 | 11 | cursor.execute(query, query_args) 12 | 13 | # If no rows were returned, return an empty list instead 14 | if cursor.description == None: 15 | return [] 16 | 17 | col_names = [desc[0] for desc in cursor.description] 18 | 19 | results = [] 20 | 21 | while True: 22 | row = cursor.fetchone() 23 | if row is None: 24 | break 25 | row_dict = dict(izip(col_names, row)) 26 | results.append(row_dict) 27 | 28 | return results 29 | 30 | def query_to_dict(query, query_args): 31 | """ 32 | Performs a query and returns its first result as a dict 33 | """ 34 | result = query_to_list(query, query_args) 35 | 36 | if len(result) == 0: 37 | return None 38 | else: 39 | return result[0] 40 | 41 | def query(query, query_args=[]): 42 | """ 43 | Performs a query, raising any possible exceptions 44 | """ 45 | cursor = connection.cursor() 46 | 47 | try: 48 | cursor.execute(query, query_args) 49 | except: 50 | raise -------------------------------------------------------------------------------- /sql/drop_tables.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS pastes CASCADE; 2 | DROP TABLE IF EXISTS paste_content CASCADE; 3 | DROP TABLE IF EXISTS comments CASCADE; 4 | DROP TABLE IF EXISTS favorites CASCADE; -------------------------------------------------------------------------------- /sql/run_sql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | psql pastesite < drop_tables.sql 3 | psql pastesite < create_tables.sql 4 | psql pastesite < add_test_data.sql -------------------------------------------------------------------------------- /static/css/pastebin-django.css: -------------------------------------------------------------------------------- 1 | .paste-text-field { 2 | font-family: Consolas, "DejaVu Sans Mono", "Lucida Console", Monaco, monospace; 3 | font-size: 10pt; 4 | resize: none; 5 | } 6 | 7 | .paste-text { 8 | font-family: Consolas, "DejaVu Sans Mono", "Lucida Console", Monaco, monospace; 9 | font-size: 10pt; 10 | } 11 | 12 | .submit-paste-tabs { 13 | margin-bottom: 15px; 14 | } 15 | 16 | .comment-text-field { 17 | resize: none; 18 | } 19 | 20 | .latest-pastes-table { 21 | font-size: 8pt; 22 | } 23 | 24 | /* Class that removes the styling Bootstrap normally applies to pre elements */ 25 | .pre-code { 26 | background-color: white !important; 27 | border: 0px; 28 | padding-top: 0px !important; 29 | padding-bottom: 0px !important; 30 | overflow-y: hidden !important; 31 | } 32 | 33 | /* form-actions was removed in Bootstrap 3, so add it back here */ 34 | .form-actions { 35 | padding: 17px 0px 18px; 36 | margin-top: 18px; 37 | margin-bottom: 18px; 38 | background-color: #F5F5F5; 39 | border-top: 1px solid #E5E5E5; 40 | } 41 | 42 | /* Loading spinner */ 43 | .loader { 44 | font-size: 8px; 45 | margin: 5em auto; 46 | width: 1em; 47 | height: 1em; 48 | border-radius: 50%; 49 | position: relative; 50 | text-indent: -9999em; 51 | -webkit-animation: load4 1.3s infinite linear; 52 | animation: load4 1.3s infinite linear; 53 | -webkit-transform: translateZ(0); 54 | -ms-transform: translateZ(0); 55 | transform: translateZ(0); 56 | } 57 | 58 | @-webkit-keyframes load4 { 59 | 0%, 60 | 100% { 61 | box-shadow: 0em -3em 0em 0.2em #000000, 2em -2em 0 0em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 0em #000000; 62 | } 63 | 12.5% { 64 | box-shadow: 0em -3em 0em 0em #000000, 2em -2em 0 0.2em #000000, 3em 0em 0 0em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 65 | } 66 | 25% { 67 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 0em #000000, 3em 0em 0 0.2em #000000, 2em 2em 0 0em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 68 | } 69 | 37.5% { 70 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 0em #000000, 2em 2em 0 0.2em #000000, 0em 3em 0 0em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 71 | } 72 | 50% { 73 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 0em #000000, 0em 3em 0 0.2em #000000, -2em 2em 0 0em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 74 | } 75 | 62.5% { 76 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 0em #000000, -2em 2em 0 0.2em #000000, -3em 0em 0 0em #000000, -2em -2em 0 -0.5em #000000; 77 | } 78 | 75% { 79 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 0em #000000, -3em 0em 0 0.2em #000000, -2em -2em 0 0em #000000; 80 | } 81 | 87.5% { 82 | box-shadow: 0em -3em 0em 0em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 0em #000000, -3em 0em 0 0em #000000, -2em -2em 0 0.2em #000000; 83 | } 84 | } 85 | @keyframes load4 { 86 | 0%, 87 | 100% { 88 | box-shadow: 0em -3em 0em 0.2em #000000, 2em -2em 0 0em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 0em #000000; 89 | } 90 | 12.5% { 91 | box-shadow: 0em -3em 0em 0em #000000, 2em -2em 0 0.2em #000000, 3em 0em 0 0em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 92 | } 93 | 25% { 94 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 0em #000000, 3em 0em 0 0.2em #000000, 2em 2em 0 0em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 95 | } 96 | 37.5% { 97 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 0em #000000, 2em 2em 0 0.2em #000000, 0em 3em 0 0em #000000, -2em 2em 0 -0.5em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 98 | } 99 | 50% { 100 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 0em #000000, 0em 3em 0 0.2em #000000, -2em 2em 0 0em #000000, -3em 0em 0 -0.5em #000000, -2em -2em 0 -0.5em #000000; 101 | } 102 | 62.5% { 103 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 0em #000000, -2em 2em 0 0.2em #000000, -3em 0em 0 0em #000000, -2em -2em 0 -0.5em #000000; 104 | } 105 | 75% { 106 | box-shadow: 0em -3em 0em -0.5em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 0em #000000, -3em 0em 0 0.2em #000000, -2em -2em 0 0em #000000; 107 | } 108 | 87.5% { 109 | box-shadow: 0em -3em 0em 0em #000000, 2em -2em 0 -0.5em #000000, 3em 0em 0 -0.5em #000000, 2em 2em 0 -0.5em #000000, 0em 3em 0 -0.5em #000000, -2em 2em 0 0em #000000, -3em 0em 0 0em #000000, -2em -2em 0 0.2em #000000; 110 | } 111 | } -------------------------------------------------------------------------------- /static/css/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+actionscript+apacheconf+applescript+aspnet+autohotkey+bash+c+csharp+cpp+coffeescript+css-extras+dart+eiffel+erlang+fsharp+fortran+gherkin+git+go+groovy+haml+handlebars+haskell+http+ini+jade+java+julia+latex+less+lolcode+markdown+matlab+nasm+nsis+objectivec+pascal+perl+php+php-extras+powershell+python+r+jsx+rest+rip+ruby+rust+sas+scss+scala+scheme+smalltalk+smarty+sql+stylus+swift+twig+typescript+wiki+yaml&plugins=line-numbers */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | text-shadow: 0 1px white; 12 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 31 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 32 | text-shadow: none; 33 | background: white; 34 | } 35 | 36 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 37 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 38 | text-shadow: none; 39 | background: white; 40 | } 41 | 42 | @media print { 43 | code[class*="language-"], 44 | pre[class*="language-"] { 45 | text-shadow: none; 46 | } 47 | } 48 | 49 | /* Code blocks */ 50 | pre[class*="language-"] { 51 | padding: 1em; 52 | margin: .5em 0; 53 | overflow: auto; 54 | } 55 | 56 | :not(pre) > code[class*="language-"], 57 | pre[class*="language-"] { 58 | background: #f5f2f0; 59 | } 60 | 61 | /* Inline code */ 62 | :not(pre) > code[class*="language-"] { 63 | padding: .1em; 64 | border-radius: .3em; 65 | } 66 | 67 | .token.comment, 68 | .token.prolog, 69 | .token.doctype, 70 | .token.cdata { 71 | color: slategray; 72 | } 73 | 74 | .token.punctuation { 75 | color: #999; 76 | } 77 | 78 | .namespace { 79 | opacity: .7; 80 | } 81 | 82 | .token.property, 83 | .token.tag, 84 | .token.boolean, 85 | .token.number, 86 | .token.constant, 87 | .token.symbol, 88 | .token.deleted { 89 | color: #905; 90 | } 91 | 92 | .token.selector, 93 | .token.attr-name, 94 | .token.string, 95 | .token.char, 96 | .token.builtin, 97 | .token.inserted { 98 | color: #690; 99 | } 100 | 101 | .token.operator, 102 | .token.entity, 103 | .token.url, 104 | .language-css .token.string, 105 | .style .token.string { 106 | color: #a67f59; 107 | background: hsla(0, 0%, 100%, .5); 108 | } 109 | 110 | .token.atrule, 111 | .token.attr-value, 112 | .token.keyword { 113 | color: #07a; 114 | } 115 | 116 | .token.function { 117 | color: #DD4A68; 118 | } 119 | 120 | .token.regex, 121 | .token.important, 122 | .token.variable { 123 | color: #e90; 124 | } 125 | 126 | .token.important, 127 | .token.bold { 128 | font-weight: bold; 129 | } 130 | .token.italic { 131 | font-style: italic; 132 | } 133 | 134 | .token.entity { 135 | cursor: help; 136 | } 137 | 138 | pre.line-numbers { 139 | position: relative; 140 | padding-left: 3.8em; 141 | counter-reset: linenumber; 142 | } 143 | 144 | pre.line-numbers > code { 145 | position: relative; 146 | } 147 | 148 | .line-numbers .line-numbers-rows { 149 | position: absolute; 150 | pointer-events: none; 151 | top: 0; 152 | font-size: 100%; 153 | left: -3.8em; 154 | width: 3em; /* works for line-numbers below 1000 lines */ 155 | letter-spacing: -1px; 156 | border-right: 3px solid #6CE26C; 157 | 158 | -webkit-user-select: none; 159 | -moz-user-select: none; 160 | -ms-user-select: none; 161 | user-select: none; 162 | 163 | } 164 | 165 | .line-numbers-rows > span { 166 | pointer-events: none; 167 | display: block; 168 | counter-increment: linenumber; 169 | } 170 | 171 | .line-numbers-rows > span:before { 172 | content: counter(linenumber); 173 | color: #999; 174 | display: block; 175 | padding-right: 0.8em; 176 | text-align: right; 177 | } 178 | -------------------------------------------------------------------------------- /static/css/pygments-style.css: -------------------------------------------------------------------------------- 1 | .hll { background-color: #ffffcc } 2 | .c { color: #408080; font-style: italic } /* Comment */ 3 | .err { border: 1px solid #FF0000 } /* Error */ 4 | .k { color: #008000; font-weight: bold } /* Keyword */ 5 | .o { color: #666666 } /* Operator */ 6 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 7 | .cp { color: #BC7A00 } /* Comment.Preproc */ 8 | .c1 { color: #408080; font-style: italic } /* Comment.Single */ 9 | .cs { color: #408080; font-style: italic } /* Comment.Special */ 10 | .gd { color: #A00000 } /* Generic.Deleted */ 11 | .ge { font-style: italic } /* Generic.Emph */ 12 | .gr { color: #FF0000 } /* Generic.Error */ 13 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 14 | .gi { color: #00A000 } /* Generic.Inserted */ 15 | .go { color: #888888 } /* Generic.Output */ 16 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 17 | .gs { font-weight: bold } /* Generic.Strong */ 18 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 19 | .gt { color: #0044DD } /* Generic.Traceback */ 20 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 21 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 22 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 23 | .kp { color: #008000 } /* Keyword.Pseudo */ 24 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 25 | .kt { color: #B00040 } /* Keyword.Type */ 26 | .m { color: #666666 } /* Literal.Number */ 27 | .s { color: #BA2121 } /* Literal.String */ 28 | .na { color: #7D9029 } /* Name.Attribute */ 29 | .nb { color: #008000 } /* Name.Builtin */ 30 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 31 | .no { color: #880000 } /* Name.Constant */ 32 | .nd { color: #AA22FF } /* Name.Decorator */ 33 | .ni { color: #999999; font-weight: bold } /* Name.Entity */ 34 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 35 | .nf { color: #0000FF } /* Name.Function */ 36 | .nl { color: #A0A000 } /* Name.Label */ 37 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 38 | .nt { color: #008000; font-weight: bold } /* Name.Tag */ 39 | .nv { color: #19177C } /* Name.Variable */ 40 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 41 | .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .mb { color: #666666 } /* Literal.Number.Bin */ 43 | .mf { color: #666666 } /* Literal.Number.Float */ 44 | .mh { color: #666666 } /* Literal.Number.Hex */ 45 | .mi { color: #666666 } /* Literal.Number.Integer */ 46 | .mo { color: #666666 } /* Literal.Number.Oct */ 47 | .sb { color: #BA2121 } /* Literal.String.Backtick */ 48 | .sc { color: #BA2121 } /* Literal.String.Char */ 49 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 50 | .s2 { color: #BA2121 } /* Literal.String.Double */ 51 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 52 | .sh { color: #BA2121 } /* Literal.String.Heredoc */ 53 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 54 | .sx { color: #008000 } /* Literal.String.Other */ 55 | .sr { color: #BB6688 } /* Literal.String.Regex */ 56 | .s1 { color: #BA2121 } /* Literal.String.Single */ 57 | .ss { color: #19177C } /* Literal.String.Symbol */ 58 | .bp { color: #008000 } /* Name.Builtin.Pseudo */ 59 | .vc { color: #19177C } /* Name.Variable.Class */ 60 | .vg { color: #19177C } /* Name.Variable.Global */ 61 | .vi { color: #19177C } /* Name.Variable.Instance */ 62 | .il { color: #666666 } /* Literal.Number.Integer.Long */ 63 | 64 | li.line { 65 | font-weight: normal; 66 | vertical-align: top; 67 | color: rgb(175, 175, 175); 68 | font-size: 1em; 69 | border-left: 3px solid #6CE26C !important; 70 | padding-left: 10px; 71 | white-space: pre-wrap; 72 | } 73 | 74 | div.line { 75 | color: black; 76 | } 77 | 78 | ol.code { 79 | font-family: monospace; 80 | } -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/js/jquery.readmore.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @preserve 3 | * 4 | * Readmore.js jQuery plugin 5 | * Author: @jed_foster 6 | * Project home: http://jedfoster.github.io/Readmore.js 7 | * Licensed under the MIT license 8 | * 9 | * Debounce function from http://davidwalsh.name/javascript-debounce-function 10 | */ 11 | !function(e){"use strict";function t(e,t,a){var i;return function(){var n=this,o=arguments,r=function(){i=null,a||e.apply(n,o)},s=a&&!i;clearTimeout(i),i=setTimeout(r,t),s&&e.apply(n,o)}}function a(e){var t=++h;return String(null==e?"rmjs-":e)+t}function i(e){var t=e.clone().css({height:"auto",width:e.width(),maxHeight:"none",overflow:"hidden"}).insertAfter(e),a=t.outerHeight(),i=parseInt(t.css({maxHeight:""}).css("max-height").replace(/[^-\d\.]/g,""),10),n=e.data("defaultHeight");t.remove();var o=i||e.data("collapsedHeight")||n;e.data({expandedHeight:a,maxHeight:i,collapsedHeight:o}).css({maxHeight:"none"})}function n(e){if(!d[e.selector]){var t=" ";e.embedCSS&&""!==e.blockCSS&&(t+=e.selector+" + [data-readmore-toggle], "+e.selector+"[data-readmore]{"+e.blockCSS+"}"),t+=e.selector+"[data-readmore]{transition: height "+e.speed+"ms;overflow: hidden;}",function(e,t){var a=e.createElement("style");a.type="text/css",a.styleSheet?a.styleSheet.cssText=t:a.appendChild(e.createTextNode(t)),e.getElementsByTagName("head")[0].appendChild(a)}(document,t),d[e.selector]=!0}}function o(t,a){this.element=t,this.options=e.extend({},s,a),n(this.options),this._defaults=s,this._name=r,this.init(),window.addEventListener?(window.addEventListener("load",l),window.addEventListener("resize",l)):(window.attachEvent("load",l),window.attachEvent("resize",l))}var r="readmore",s={speed:100,collapsedHeight:200,heightMargin:16,moreLink:'Read More',lessLink:'Close',embedCSS:!0,blockCSS:"display: block; width: 100%;",startOpen:!1,beforeToggle:function(){},afterToggle:function(){}},d={},h=0,l=t(function(){e("[data-readmore]").each(function(){var t=e(this),a="true"===t.attr("aria-expanded");i(t),t.css({height:t.data(a?"expandedHeight":"collapsedHeight")})})},100);o.prototype={init:function(){var t=this,n=e(this.element);n.data({defaultHeight:this.options.collapsedHeight,heightMargin:this.options.heightMargin}),i(n);var o=n.data("collapsedHeight"),r=n.data("heightMargin");if(n.outerHeight(!0)<=o+r)return!0;var s=n.attr("id")||a(),d=t.options.startOpen?t.options.lessLink:t.options.moreLink;n.attr({"data-readmore":"","aria-expanded":!1,id:s}),n.after(e(d).on("click",function(e){t.toggle(this,n[0],e)}).attr({"data-readmore-toggle":"","aria-controls":s})),t.options.startOpen||n.css({height:o})},toggle:function(t,a,i){i&&i.preventDefault(),t||(t=e('[aria-controls="'+this.element.id+'"]')[0]),a||(a=this.element);var n=this,o=e(a),r="",s="",d=!1,h=o.data("collapsedHeight");o.height()<=h?(r=o.data("expandedHeight")+"px",s="lessLink",d=!0):(r=h,s="moreLink"),n.options.beforeToggle(t,a,!d),o.css({height:r}),o.on("transitionend",function(){n.options.afterToggle(t,a,d),e(this).attr({"aria-expanded":d}).off("transitionend")}),e(t).replaceWith(e(n.options[s]).on("click",function(e){n.toggle(this,a,e)}).attr({"data-readmore-toggle":"","aria-controls":o.attr("id")}))},destroy:function(){e(this.element).each(function(){var t=e(this);t.attr({"data-readmore":null,"aria-expanded":null}).css({maxHeight:"",height:""}).next("[data-readmore-toggle]").remove(),t.removeData()})}},e.fn.readmore=function(t){var a=arguments,i=this.selector;return t=t||{},"object"==typeof t?this.each(function(){if(e.data(this,"plugin_"+r)){var a=e.data(this,"plugin_"+r);a.destroy.apply(a)}t.selector=i,e.data(this,"plugin_"+r,new o(this,t))}):"string"==typeof t&&"_"!==t[0]&&"init"!==t?this.each(function(){var i=e.data(this,"plugin_"+r);i instanceof o&&"function"==typeof i[t]&&i[t].apply(i,Array.prototype.slice.call(a,1))}):void 0}}(jQuery); -------------------------------------------------------------------------------- /static/js/linkify-jquery.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict";function n(e,t,n){var i=n[n.length-1];e.replaceChild(i,t);for(var r=n.length-2;r>=0;r--)e.insertBefore(n[r],i),i=n[r]}function i(e,t,n){for(var i=[],r=0;r= pastebin.fontSizes.length-1) { 49 | return; 50 | } 51 | 52 | pastebin.currentFontSize += 1; 53 | pastebin.updateControls(); 54 | } 55 | 56 | /** 57 | * Decrease paste's font size 58 | */ 59 | pastebin.decreaseFontSize = function() { 60 | if (pastebin.currentFontSize === 0) { 61 | return; 62 | } 63 | 64 | pastebin.currentFontSize -= 1; 65 | pastebin.updateControls(); 66 | } 67 | 68 | pastebin.loadControls(); -------------------------------------------------------------------------------- /static/js/pastebin-decrypt.js: -------------------------------------------------------------------------------- 1 | if (typeof pastebin === 'undefined') { 2 | var pastebin = {}; 3 | pastebin.urls = {}; 4 | } 5 | 6 | // List of languages prism.js can highlight 7 | // The key is the format used by pastebin-django, 8 | // the value is the format used in language-xxxx class by prism.js 9 | pastebin.languages = { 10 | "text": "markup", 11 | "c": "c", 12 | "css": "css", 13 | "js": "javascript", 14 | "as": "actionscript", 15 | "apacheconf": "apacheconf", 16 | "aspx-cs": "aspnet", 17 | "ahk": "autohotkey", 18 | "bash": "bash", 19 | "csharp": "csharp", 20 | "cpp": "cpp", 21 | "coffee-script": "coffeescript", 22 | "dart": "dart", 23 | "eiffel": "eiffel", 24 | "erlang": "erlang", 25 | "fsharp": "fsharp", 26 | "fortran": "fortran", 27 | "cucumber": "gherkin", 28 | "go": "go", 29 | "groovy": "groovy", 30 | "haml": "haml", 31 | "handlebars": "handlebars", 32 | "haskell": "haskell", 33 | "http": "http", 34 | "ini": "ini", 35 | "jade": "jade", 36 | "java": "java", 37 | "julia": "julia", 38 | "matlab": "matlab", 39 | "nasm": "nasm", 40 | "nsis": "nsis", 41 | "objective-c": "objectivec", 42 | "perl": "perl", 43 | "php": "php", 44 | "powershell": "powershell", 45 | "python": "python", 46 | "rst": "rest", 47 | "rb": "ruby", 48 | "rust": "rust", 49 | "sass": "scss", 50 | "scala": "scala", 51 | "scheme": "scheme", 52 | "smalltalk": "smalltalk", 53 | "smarty": "smarty", 54 | "sql": "sql", 55 | "swift": "swift", 56 | "twig": "twig", 57 | "ts": "typescript", 58 | "yaml": "yaml" 59 | }; 60 | 61 | pastebin.loadDecrypt = function() { 62 | $("#paste-decrypt").click(function() { pastebin.decryptPaste(); }); 63 | 64 | $("#paste-password").keyup(function(evt) { 65 | if (evt.keyCode == 13) { 66 | $("#paste-decrypt").click().focus(); 67 | } 68 | }); 69 | }; 70 | 71 | pastebin.decryptPaste = function() { 72 | var password = $("#paste-password").val(); 73 | var text = $("#encrypted-text").text(); 74 | 75 | try { 76 | text = sjcl.decrypt(password, text); 77 | } catch (err) { 78 | // Paste couldn't be decrypted 79 | window.alert("Paste couldn't be decrypted. Your password is probably incorrect."); 80 | $("#paste-decrypt-password").select(); 81 | return; 82 | } 83 | 84 | $("#encrypted-text").find("code").text(text); 85 | pastebin.highlightPaste(); 86 | } 87 | 88 | /** 89 | * Highlight the now decrypted paste using prism.js 90 | */ 91 | pastebin.highlightPaste = function() { 92 | // Check if prism.js supports the format of this paste 93 | // If it does, highlight it using that language 94 | // Otherwise, default to plain text 95 | var language = "markup"; 96 | 97 | if (pastebin_paste_format in pastebin.languages) { 98 | language = pastebin.languages[pastebin_paste_format]; 99 | } 100 | 101 | $("#encrypted-text").addClass("language-" + language + " line-numbers"); 102 | $("#encrypted-text").show(); 103 | $("#paste-decrypt-form").hide(); 104 | 105 | Prism.highlightAll(); 106 | 107 | $("#encrypted-text").addClass("pre-code"); 108 | 109 | // Also show paste controls 110 | pastebin.showControls(); 111 | }; 112 | 113 | pastebin.loadDecrypt(); -------------------------------------------------------------------------------- /static/js/pastebin-favorite.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script is responsible for updating the favorite button and displaying the relevant information. 3 | * This way the user can add/remove the paste from his favorites without having to refresh the page. 4 | */ 5 | if (typeof pastebin === 'undefined') { 6 | var pastebin = {}; 7 | pastebin.urls = {}; 8 | } 9 | 10 | // An object containing different URLs for easier access 11 | pastebin.urls["change_paste_favorite"] = window.location.protocol + "//" + window.location.host + "/pastes/change_paste_favorite/"; 12 | 13 | pastebin.loadFavorites = function() { 14 | // We need to send a CSRF token with POST requests, so make sure it's included 15 | // with every request 16 | $.ajaxSetup({ 17 | beforeSend: function(xhr) { 18 | xhr.setRequestHeader('X-CSRFToken', pastebin_csrf_token); 19 | } 20 | }); 21 | 22 | pastebin.updateFavoriteButton(); 23 | }; 24 | 25 | /** 26 | * Update the "add/remove favorite" button 27 | */ 28 | pastebin.updateFavoriteButton = function() { 29 | // Hide the button if user isn't logged in, he's not supposed to see it anyway 30 | if (!pastebin_logged_in) { 31 | $("#favorite-button").css("display", "none"); 32 | return; 33 | } 34 | 35 | if (pastebin_paste_favorited) { 36 | $("#favorite-button").html(" Remove from favorites"); 37 | $("#favorite-button").attr("onclick", "pastebin.removeFromFavorites()"); 38 | $("#favorite-button").attr("class", "btn btn-warning btn-xs"); 39 | } else { 40 | $("#favorite-button").html(" Add to favorites"); 41 | $("#favorite-button").attr("onclick", "pastebin.addToFavorites()"); 42 | $("#favorite-button").attr("class", "btn btn-info btn-xs"); 43 | } 44 | }; 45 | 46 | /** 47 | * Called when user clicks "add to favorites" 48 | */ 49 | pastebin.addToFavorites = function() { 50 | if (pastebin_paste_favorited) { 51 | return; 52 | } 53 | 54 | $.post(pastebin.urls["change_paste_favorite"], 55 | {char_id: pastebin_char_id, 56 | action: "add"}, 57 | function(result) { 58 | pastebin.onFavoriteUpdated(result); 59 | }); 60 | 61 | $("#favorite-button").attr("disabled", true); 62 | }; 63 | 64 | /** 65 | * Called when user clicks "remove from favorites" 66 | */ 67 | pastebin.removeFromFavorites = function() { 68 | if (!pastebin_paste_favorited) { 69 | return; 70 | } 71 | 72 | $.post(pastebin.urls["change_paste_favorite"], 73 | {char_id: pastebin_char_id, 74 | action: "remove"}, 75 | function(result) { 76 | pastebin.onFavoriteUpdated(result); 77 | }); 78 | 79 | $("#favorite-button").attr("disabled", true); 80 | }; 81 | 82 | /** 83 | * Called when response is received to user's "add/remove favorite" request 84 | */ 85 | pastebin.onFavoriteUpdated = function(result) { 86 | result = JSON.parse(result); 87 | 88 | if ("status" in result && result["status"] === "success") { 89 | if (result["data"]["favorited"]) { 90 | pastebin_paste_favorited = true; 91 | } else { 92 | pastebin_paste_favorited = false; 93 | } 94 | } 95 | 96 | $("#favorite-button").attr("disabled", false); 97 | 98 | pastebin.updateFavoriteButton(); 99 | }; 100 | 101 | pastebin.loadFavorites(); -------------------------------------------------------------------------------- /static/js/pastebin-submit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script is responsible for submitting the paste as well as performing form validation 3 | */ 4 | if (typeof pastebin === 'undefined') { 5 | var pastebin = {}; 6 | pastebin.urls = {}; 7 | } 8 | 9 | pastebin.urls["submit_paste"] = window.location.protocol + "//" + window.location.host; 10 | 11 | var pastebin_paste_encrypted = false; 12 | 13 | /** 14 | * Called when the script is loaded 15 | */ 16 | pastebin.loadSubmitForm = function() { 17 | // Add event handlers 18 | $("#encryption-password").change(function() { pastebin.updateEncryptionTab(); }); 19 | $("#encryption-password").keydown(function() { pastebin.updateEncryptionTab(); }); 20 | $("#encryption-password").keyup(function() { pastebin.updateEncryptionTab(); }); 21 | 22 | $("#encryption-confirm-password").change(function() { pastebin.updateEncryptionTab(); }); 23 | $("#encryption-confirm-password").keydown(function() { pastebin.updateEncryptionTab(); }); 24 | $("#encryption-confirm-password").keyup(function() { pastebin.updateEncryptionTab(); }); 25 | 26 | $("#encryption-generate-password").click(function() { pastebin.generateRandomPassword(); }); 27 | $("#encryption-encrypt").click(function() { pastebin.encryptPaste(); }); 28 | 29 | $("#encryption-show-password").click(function() { pastebin.setPasswordVisible(this.checked); }); 30 | }; 31 | 32 | /** 33 | * Update the encryption tab 34 | */ 35 | pastebin.updateEncryptionTab = function() { 36 | if (pastebin_paste_encrypted) { 37 | $("#encryption-password").attr("disabled", true); 38 | $("#encryption-confirm-password").attr("disabled", true); 39 | $("#encryption-generate-password").attr("disabled", true); 40 | $("#encryption-encrypt").attr("disabled", true); 41 | return; 42 | } 43 | 44 | var password = $("#encryption-password").val(); 45 | var confirmPassword = $("#encryption-confirm-password").val(); 46 | 47 | if (password === confirmPassword && password != "") { 48 | $("#encryption-encrypt").attr("disabled", false); 49 | 50 | $("#encryption-password-group").removeClass("has-error"); 51 | $("#encryption-confirm-password-group").removeClass("has-error"); 52 | 53 | $("#encryption-password-group").addClass("has-success"); 54 | $("#encryption-confirm-password-group").addClass("has-success"); 55 | } else { 56 | $("#encryption-encrypt").attr("disabled", true); 57 | 58 | $("#encryption-password-group").addClass("has-error"); 59 | $("#encryption-confirm-password-group").addClass("has-error"); 60 | 61 | $("#encryption-password-group").removeClass("has-success"); 62 | $("#encryption-confirm-password-group").removeClass("has-success"); 63 | } 64 | }; 65 | 66 | /** 67 | * Generates a random password 68 | */ 69 | pastebin.generateRandomPassword = function() { 70 | var chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 71 | 72 | var password = ""; 73 | 74 | for (var i=0; i < 16; i++) { 75 | // Use random word generator provided courtesy of SJCL library 76 | var char = chars[(Math.abs(sjcl.random.randomWords(1))%chars.length)]; 77 | password += char; 78 | } 79 | 80 | $("#encryption-password").val(password); 81 | $("#encryption-confirm-password").val(password); 82 | 83 | pastebin.setPasswordVisible(true); 84 | pastebin.updateEncryptionTab(); 85 | 86 | $("#encryption-show-password").prop("checked", true); 87 | $("#encryption-password").select(); 88 | }; 89 | 90 | /** 91 | * Set whether passwords should be visible or not 92 | */ 93 | pastebin.setPasswordVisible = function(visible) { 94 | if (visible) { 95 | $("#encryption-password").attr("type", "text"); 96 | $("#encryption-confirm-password").attr("type", "text"); 97 | } else { 98 | $("#encryption-password").attr("type", "password"); 99 | $("#encryption-confirm-password").attr("type", "password"); 100 | } 101 | } 102 | 103 | /** 104 | * Encrypt the paste and prevent it from being edited again 105 | */ 106 | pastebin.encryptPaste = function() { 107 | var text = $("[name='text']").val(); 108 | var password = $("#encryption-password").val(); 109 | 110 | text = sjcl.encrypt(password, text); 111 | 112 | $("[name='text']").val(text).attr("readonly", true); 113 | $("[name='encrypted']").val(true); 114 | 115 | pastebin_paste_encrypted = true; 116 | 117 | pastebin.updateEncryptionTab(); 118 | }; 119 | 120 | pastebin.loadSubmitForm(); -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | {# HEADER #} 5 | {% block head %} 6 | 7 | {% load staticfiles %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% block title %}pastebin-django{% endblock %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | {% block extra_head %}{% endblock %} 28 | 29 | {% endblock head %} 30 | 31 | {% block body %} 32 | {% block body_tag %}{% endblock body_tag %} 33 | {# MENUBAR #} 34 | {% block navbar %}{% include "navbar.html" %}{% endblock navbar %} 35 | 36 | {# PAGE CONTENT #} 37 | {% block content %} 38 | {% endblock content %} 39 | 40 | {# PAGE FOOTER #} 41 | {% block page_footer %}{% include 'page_footer.html' %}{% endblock page_footer %} 42 | 43 | {# FOOTER #} 44 | {% load staticfiles %} 45 | 47 | 48 | 49 | 50 | 51 | {% block extra_js %}{% endblock extra_js %} 52 | 53 | {% endblock body %} 54 | 55 | -------------------------------------------------------------------------------- /templates/form.html: -------------------------------------------------------------------------------- 1 | {# A template that allows certain Bootstrap forms to be implemented more easily #} 2 | {% load widget_tweaks %} 3 | {% for field in form %} 4 | {% if not exclude or field.name not in exclude %} 5 | {% if field.field.widget.input_type == "hidden" %} 6 | 7 | {% else %} 8 | {% if field.errors %} 9 |
10 | {% else %} 11 |
12 | {% endif %} 13 | 14 |
15 |
16 |
17 | {{ field|attr:'class:form-control' }} 18 |
19 |
20 | {% for error in field.errors %} 21 |

{{ error|escape }}

22 | {% endfor %} 23 | {% if field.help_text %} 24 |

{{ field.help_text }}

25 | {% endif %} 26 |
27 |
28 | {% endif %} 29 | {% endif %} 30 | {% endfor %} -------------------------------------------------------------------------------- /templates/navbar.html: -------------------------------------------------------------------------------- 1 | {% load lineage %} 2 | {% load extra_tags %} 3 | -------------------------------------------------------------------------------- /templates/page_footer.html: -------------------------------------------------------------------------------- 1 | {% load extra_tags %} 2 |
3 |
4 |
5 |

Pastes uploaded: {% get_total_paste_count %} | Comments posted: {% get_total_comment_count %} | pastebin-django on GitHub

6 |
7 |
-------------------------------------------------------------------------------- /templates/pagination.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates_jinja2/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {# HEADER #} 4 | {% block head %} 5 | 6 | 7 | 8 | 9 | 10 | {% block title %}pastebin-django{% endblock %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | {% block extra_head %}{% endblock %} 25 | 26 | {% endblock head %} 27 | 28 | {% block body %} 29 | {% block body_tag %}{% endblock body_tag %} 30 | {# MENUBAR #} 31 | {% block navbar %}{% include "navbar.html" %}{% endblock navbar %} 32 | 33 | {# PAGE CONTENT #} 34 | {% block content %} 35 | {% endblock content %} 36 | 37 | {# PAGE FOOTER #} 38 | {% block page_footer %}{% include 'page_footer.html' %}{% endblock page_footer %} 39 | 40 | {# FOOTER #} 41 | 43 | 44 | 45 | 46 | 47 | {% block extra_js %}{% endblock extra_js %} 48 | 49 | {% endblock body %} 50 | 51 | -------------------------------------------------------------------------------- /templates_jinja2/form.html: -------------------------------------------------------------------------------- 1 | {# A template that allows certain Bootstrap forms to be implemented more easily #} 2 | {% for field in form %} 3 | {% if not exclude or field.name not in exclude %} 4 | {% if field.field.widget.input_type == "hidden" %} 5 | 6 | {% else %} 7 | {% if field.errors %} 8 |
9 | {% else %} 10 |
11 | {% endif %} 12 | 13 |
14 |
15 |
16 | {% if is_checkbox(field) %} 17 |
22 | {% endif %} 23 |
24 |
25 | {% for error in field.errors %} 26 |

{{ error|escape }}

27 | {% endfor %} 28 | {% if field.help_text %} 29 |

{{ field.help_text|safe }}

30 | {% endif %} 31 |
32 |
33 | {% endif %} 34 | {% endif %} 35 | {% endfor %} -------------------------------------------------------------------------------- /templates_jinja2/navbar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates_jinja2/page_footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Pastes uploaded: {{ get_total_paste_count() }} | Paste hits: {{ get_total_paste_hit_count() }} | pastebin-django on GitHub

5 |
6 |
-------------------------------------------------------------------------------- /templates_jinja2/pagination.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/users/__init__.py -------------------------------------------------------------------------------- /users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /users/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from django.contrib.auth.models import User 4 | from django.contrib.auth import authenticate 5 | 6 | from django.core.urlresolvers import reverse, NoReverseMatch 7 | 8 | from pastes.models import Paste 9 | 10 | class RegisterForm(forms.Form): 11 | """ 12 | User registration form 13 | """ 14 | username = forms.CharField(max_length=64, 15 | required=True) 16 | password = forms.CharField(min_length=6, 17 | max_length=128, 18 | widget = forms.TextInput(attrs={ 'type': 'password' })) 19 | confirm_password = forms.CharField(min_length=6, 20 | max_length=128, 21 | widget = forms.TextInput(attrs={ 'type': 'password' })) 22 | 23 | def clean_username(self): 24 | """ 25 | Check that the username isn't already in use and that it doesn't conflict 26 | with an existing URL 27 | """ 28 | username = self.cleaned_data.get("username") 29 | 30 | # Ehh, nobody will notice 31 | if User.objects.filter(username__iexact=username).count() > 0: 32 | # The username exists 33 | raise forms.ValidationError("The username is already in use.") 34 | 35 | # Make sure the username doesn't conflict with the URLs 36 | url_conflict = True 37 | try: 38 | test = reverse('users:' + username) 39 | except NoReverseMatch: 40 | url_conflict = False 41 | 42 | if url_conflict: 43 | # The username can't be used because it conflicts with an URL 44 | raise forms.ValidationError("This username can't be used.") 45 | 46 | return username 47 | 48 | def clean_password(self): 49 | password = self.cleaned_data.get("password") 50 | 51 | if password == "correct horse battery staple": 52 | raise forms.ValidationError("You'll have to pick a better password than that.") 53 | 54 | return password 55 | 56 | def clean_confirm_password(self): 57 | """ 58 | Check that the provided passwords match 59 | """ 60 | password = self.cleaned_data.get("password") 61 | confirm_password = self.cleaned_data.get("confirm_password") 62 | 63 | if password != confirm_password: 64 | raise forms.ValidationError("The provided password didn't match.") 65 | 66 | return confirm_password 67 | 68 | class LoginForm(forms.Form): 69 | """ 70 | User login form 71 | """ 72 | username = forms.CharField(max_length=64) 73 | password = forms.CharField(max_length=128, 74 | widget = forms.TextInput(attrs={ 'type': 'password' })) 75 | 76 | class ChangePreferencesForm(forms.Form): 77 | """ 78 | Form to change user's profile preferences 79 | """ 80 | public_favorites = forms.BooleanField(required=False, 81 | help_text="Allow everyone to see your favorited pastes") 82 | 83 | class ChangePasswordForm(forms.Form): 84 | """ 85 | Form to change the user's password 86 | """ 87 | current_password = forms.CharField(max_length=128, 88 | widget=forms.TextInput(attrs={'type': 'password'})) 89 | 90 | new_password = forms.CharField(min_length=6, 91 | max_length=128, 92 | widget=forms.TextInput(attrs={'type': 'password'})) 93 | confirm_new_password = forms.CharField(min_length=6, 94 | max_length=128, 95 | widget=forms.TextInput(attrs={'type': 'password'})) 96 | 97 | def __init__(self, *args, **kwargs): 98 | self.user = kwargs.pop('user', None) 99 | 100 | if self.user == None: 101 | raise AttributeError("'%s' requires a valid Django user object as its user parameter" % self.__class__.__name__) 102 | 103 | super(ChangePasswordForm, self).__init__(*args, **kwargs) 104 | 105 | def clean_current_password(self): 106 | """ 107 | Check that the user has logged in and provided the correct password 108 | """ 109 | if not self.user or not self.user.is_authenticated(): 110 | raise forms.ValidationError("You are not logged in.") 111 | 112 | password = self.cleaned_data.get('current_password') 113 | 114 | if authenticate(username=self.user.username, password=password) == None: 115 | raise forms.ValidationError("The provided password was not correct") 116 | 117 | return password 118 | 119 | def clean_confirm_new_password(self): 120 | """ 121 | Check that the provided new passwords match 122 | """ 123 | new_password = self.cleaned_data.get("new_password") 124 | confirm_new_password = self.cleaned_data.get("confirm_new_password") 125 | 126 | if new_password != confirm_new_password: 127 | raise forms.ValidationError("The provided passwords didn't match.") 128 | 129 | return confirm_new_password 130 | 131 | class VerifyPasswordForm(forms.Form): 132 | """ 133 | Form to verify the user's password 134 | """ 135 | password = forms.CharField(max_length=128, 136 | widget=forms.TextInput(attrs={ 'type': 'password' }), 137 | help_text="You need to enter your password to perform this action.") 138 | 139 | def __init__(self, *args, **kwargs): 140 | self.user = kwargs.pop('user', None) 141 | 142 | if self.user == None: 143 | raise AttributeError("'%s' requires a valid Django user object as its user parameter" % self.__class__.__name__) 144 | 145 | super(VerifyPasswordForm, self).__init__(*args, **kwargs) 146 | 147 | def clean_password(self): 148 | """ 149 | Check that the user has logged in and provided the correct password 150 | """ 151 | if not self.user or not self.user.is_authenticated(): 152 | raise forms.ValidationError("You are not logged in.") 153 | 154 | password = self.cleaned_data.get('password') 155 | 156 | if authenticate(username=self.user.username, password=password) == None: 157 | raise forms.ValidationError("The provided password was not correct.") 158 | 159 | return password -------------------------------------------------------------------------------- /users/jinja2/users/login/logged_in.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Logged in - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | Logged in!
14 | You have logged in as {{ request.user.get_username() }} 15 |
16 |
17 |
18 |
19 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/login/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Login - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 | 10 |
11 | {% csrf_token %} 12 | {% include "form.html" %} 13 | {% with form=captcha_form %}{% include "form.html" %}{% endwith %} 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/logout/logged_out.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Logged out - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | Logged out.
14 | You have logged out. 15 |
16 |
17 |
18 |
19 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/favorites/favorites.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if favorites %} 6 | 7 | 8 | 9 | 10 | 11 | {% if profile_user == request.user %} 12 | 13 | {% endif %} 14 | 15 | 16 | 17 | {% for favorite in favorites %} 18 | 19 | 20 | 21 | {% if profile_user == request.user %} 22 | 30 | {% endif %} 31 | 32 | {% endfor %} 33 | 34 |
TitleAddedActions
{{ favorite.paste.title|truncate(128) }}{{ favorite.added }} 23 |
24 | {% csrf_token %} 25 | 26 | 27 | 28 |
29 |
35 | {% with destination="users:favorites", url_arg=profile_user.get_username() %}{% include "pagination.html" %}{% endwith %} 36 | {% else %} 37 | {% if profile_user.get_username() == request.user.get_username() %} 38 |
39 | No favorites added
40 | You haven't added any favorites yet. 41 |
42 | {% else %} 43 |
44 | No favorites added
45 | This user hasn't added any favorites yet. 46 |
47 | {% endif %} 48 | {% endif %} 49 |
50 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/favorites/favorites_hidden.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 |
6 | Private favorites
7 | This user has opted to make his or her favorites private. 8 |
9 |
10 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/home/home.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% include "users/profile/home/home_pastes.html" %} 6 |
7 |
8 | {% include "users/profile/home/home_favorites.html" %} 9 |
10 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/home/home_favorites.html: -------------------------------------------------------------------------------- 1 | {% if not profile_settings.public_favorites and request.user != profile_user %} 2 |
3 | Private favorites
4 | This user has opted to make his or her favorites private. 5 |
6 | {% elif favorites %} 7 | {% if profile_username == request.user.get_username() %} 8 |

My recent favorites

9 | {% else %} 10 |

Recent favorites

11 | {% endif %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for favorite in favorites %} 20 | 21 | 22 | 23 | {% endfor %} 24 | 25 |
Title
{{ favorite.paste.title|truncate(64) }}
26 | {% else %} 27 | {% if profile_user.get_username() == request.user.get_username() %} 28 |
29 | No favorites added
30 | You haven't added any favorites yet. 31 |
32 | {% else %} 33 |
34 | No favorites added
35 | This user hasn't added any favorites yet. 36 |
37 | {% endif %} 38 | {% endif %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/home/home_pastes.html: -------------------------------------------------------------------------------- 1 | {% if pastes %} 2 | {% if profile_username == request.user.get_username() %} 3 |

My recent pastes

4 | {% else %} 5 |

Recent pastes

6 | {% endif %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% for paste in pastes %} 16 | 17 | 18 | 19 | 20 | {% endfor %} 21 | 22 |
TitleAge
{{ paste.title|truncate(32) }}{{ seconds_to_str(timesince_in_seconds(paste.submitted)) }}
23 | {% else %} 24 | {% if profile_user.get_username() == request.user.get_username() %} 25 |
26 | No pastes uploaded
27 | You haven't uploaded any pastes yet. 28 |
29 | {% else %} 30 |
31 | No pastes uploaded
32 | This user hasn't uploaded any pastes yet. 33 |
34 | {% endif %} 35 | {% endif %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/pastes/pastes.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if pastes %} 6 | 7 | 8 | 9 | 10 | 11 | {% if profile_user == request.user %} 12 | 13 | {% endif %} 14 | 15 | 16 | 17 | {% for paste in pastes %} 18 | 19 | 20 | 21 | {% if profile_user == request.user %} 22 | 26 | {% endif %} 27 | 28 | {% endfor %} 29 | 30 |
TitleUploadedActions
{{ paste.title|truncate(128) }}{{ paste.submitted }} 23 | Edit 24 | Remove 25 |
31 | {% with destination="users:pastes", url_arg=profile_user.get_username() %}{% include "pagination.html" %}{% endwith %} 32 | {% else %} 33 | {% if profile_user.get_username() == request.user.get_username() %} 34 |
35 | No pastes uploaded
36 | You haven't uploaded any pastes yet. 37 |
38 | {% else %} 39 |
40 | No pastes uploaded
41 | This user hasn't uploaded any pastes yet. 42 |
43 | {% endif %} 44 | {% endif %} 45 |
46 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}User: {{ profile_user.get_username() }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 17 |
18 |
19 |
20 |
21 | {% include "users/profile/profile_navbar.html" %} 22 |
23 | {% block profile_content %}{% endblock %} 24 |
25 | {% if profile_user != request.user %} 26 |
27 | {% endif %} 28 |
29 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/profile_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | {% if reason == "not_found" %} 10 | User not found
11 | The user whose profile you tried to view does not exist. 12 | {% endif %} 13 |
14 |
15 |
16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/profile_favorites.html: -------------------------------------------------------------------------------- 1 | {% if not profile_settings.public_favorites and request.user != profile_user %} 2 |
3 |
4 | Private favorites
5 | This user has opted to make his or her favorites private. 6 |
7 |
8 | {% elif favorites %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% for paste in favorites %} 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 | 24 |
TitleAge
{{ paste.title|truncate(32) }}{{ seconds_to_str(timesince_in_seconds(paste.submitted)) }}
25 | {% if favorites.count < total_favorite_count %} 26 | All favorites 27 | {% endif %} 28 | {% else %} 29 | {% if profile_username == request.user.get_username() %} 30 |
31 | No favorites added
32 | You haven't added any favorites yet. 33 |
34 | {% else %} 35 |
36 | No favorites added
37 | This user hasn't added any favorites yet. 38 |
39 | {% endif %} 40 | {% endif %} -------------------------------------------------------------------------------- /users/jinja2/users/profile/profile_navbar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ profile_user.get_username() }}

4 |
5 | 22 | {% if profile_user.id == request.user.id %} 23 |
24 |

Settings

25 |
26 | 31 | {% endif %} 32 |
-------------------------------------------------------------------------------- /users/jinja2/users/profile/profile_pastes.html: -------------------------------------------------------------------------------- 1 | {% if pastes %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% for paste in pastes %} 11 | 12 | 13 | 14 | 15 | {% endfor %} 16 | 17 |
TitleAge
{{ paste.title|truncate(32) }}{{ seconds_to_str(timesince_in_seconds(paste.submitted)) }}
18 | {% if 1 < 5 %} 19 | All pastes 20 | {% endif %} 21 | {% else %} 22 | {% if profile_username == user.get_username %} 23 |
24 | No pastes uploaded
25 | You haven't uploaded any pastes yet. 26 |
27 | {% else %} 28 |
29 | No pastes uploaded
30 | This user hasn't uploaded any pastes yet. 31 |
32 | {% endif %} 33 | {% endif %} -------------------------------------------------------------------------------- /users/jinja2/users/register/already_logged_in.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | Already logged in.
10 | You have already logged in.
11 | Return to the front page 12 |
13 |
14 |
15 |
16 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/register/register.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 | 8 |
9 | {% csrf_token %} 10 | {% include "form.html" %} 11 | {% with form=captcha_form %}{% include "form.html" %}{% endwith %} 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/register/register_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | {% if reason == "email_send_failed" %} 12 | Verification message couldn't be sent
13 | The verification message to verify your email address couldn't be sent. 14 | {% endif %} 15 |
16 |
17 |
18 |
19 | {% endblock %} -------------------------------------------------------------------------------- /users/jinja2/users/register/register_success.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | Registered!
12 | You have successfully registered. You may now login.
13 | {% if email_address_added %} 14 |
In addition, a verification link was sent to your email address. You need to verify your email address in order to be able to reset your password in case you are unable to login.
15 | {% endif %} 16 |
Login 17 |
18 |
19 |
20 |
21 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/settings/change_email_address/change_email_address.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 |
6 | If you have added an email address to your account and verified it, you can reset your password in case you find yourself unable to login to your account. 7 |
8 | {% if email_address_changed %} 9 |
10 |
11 | Email address changed!
12 | Your email address was changed and a verification link was sent to your new email address. 13 |
14 | {% elif email_address_change_failed %} 15 |
16 |
17 | Email verification link couldn't be sent!
18 | A verification link to verify your email address couldn't be sent. Please try again later. 19 |
20 | {% elif verification_link_resent %} 21 |
22 |
23 | Verification link resent!
24 | The verification link was resent to your email address. Note that the old verification link is no longer valid. 25 |
26 | {% elif verification_link_resend_failed %} 27 |
28 |
29 | Verification link couldn't be resent!
30 | The verification link couldn't be resent to your email address. Please try again later. 31 |
32 | {% elif error %} 33 |
34 |
35 | {{ error }} 36 |
37 | {% endif %} 38 |
39 | {% if site_settings.email_address != "" %} 40 |
41 | 42 |
43 |

{{ site_settings.email_address }}

44 |
45 |
46 | {% if site_settings.email_address_verified %} 47 |
48 | 49 |
50 |

Verified

51 |
52 |
53 | {% else %} 54 |
55 | 56 |
57 |

Not verified

58 |
59 |
60 | {% endif %} 61 |
62 | {% endif %} 63 | {% csrf_token %} 64 | {% include "form.html" %} 65 |
66 | {% with form=captcha_form %}{% include "form.html" %}{% endwith %} 67 | {% with form=verify_form %}{% include "form.html" %}{% endwith %} 68 |
69 |
70 | 71 | {% if site_settings.email_address != "" and not site_settings.email_address_verified %} 72 | 73 | {% endif %} 74 |
75 |
76 |
77 |
78 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/jinja2/users/settings/change_password/change_password.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if password_changed %} 6 |
7 | Password changed!
8 | Your password was successfully changed. 9 |
10 | {% endif %} 11 |
12 | {% csrf_token %} 13 | {% include "form.html" %} 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/jinja2/users/settings/change_preferences/change_preferences.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if preferences_changed %} 6 |
7 | Preferences changed!
8 | Your preferences were successfully changed. 9 |
10 | {% endif %} 11 |
12 | {% csrf_token %} 13 | {% include "form.html" %} 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/jinja2/users/settings/delete_account/account_deleted.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | Account deleted
12 | Your account has been deleted. 13 |
14 |
15 |
16 |
17 | {% endblock content %} -------------------------------------------------------------------------------- /users/jinja2/users/settings/delete_account/delete_account.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 |
6 | You are about to delete your account!
7 | Deleting your account also removes any pastes you have uploaded. After deleted, your account can't be restored! 8 |
9 |
10 | {% csrf_token %} 11 | {% include "form.html" %} 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/jinja2/users/settings/settings_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | {% if reason == "incorrect_user" %} 10 | Incorrect user
11 | You tried to change the settings of another user. That's not nice. 12 | {% elif reason == "not_logged_in" %} 13 | Not logged in
14 | You need to be logged in in order to change your settings. 15 | {% endif %} 16 |
17 |
18 |
19 |
20 | {% endblock %} -------------------------------------------------------------------------------- /users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('pastes', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Favorite', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('added', models.DateTimeField(auto_now_add=True)), 21 | ('paste', models.ForeignKey(to='pastes.Paste')), 22 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 23 | ], 24 | options={ 25 | }, 26 | bases=(models.Model,), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /users/migrations/0002_sitesettings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('users', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='SiteSettings', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('public_favorites', models.BooleanField(default=True)), 21 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 22 | ], 23 | options={ 24 | }, 25 | bases=(models.Model,), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/users/migrations/__init__.py -------------------------------------------------------------------------------- /users/templates/users/login/logged_in.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Logged in - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | Logged in!
14 | You have logged in as {{ user.get_username }} 15 |
16 |
17 |
18 |
19 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/login/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Login - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 | 10 |
11 | {% csrf_token %} 12 | {% include "form.html" %} 13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/logout/logged_out.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Logged out - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 12 |
13 | Logged out.
14 | You have logged out. 15 |
16 |
17 |
18 |
19 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/profile/favorites/favorites.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if favorites %} 6 | 7 | 8 | 9 | 10 | 11 | {% if profile_user == request.user %} 12 | 13 | {% endif %} 14 | 15 | 16 | 17 | {% for favorite in favorites %} 18 | 19 | 20 | 21 | {% if profile_user == request.user %} 22 | 30 | {% endif %} 31 | 32 | {% endfor %} 33 | 34 |
TitleAddedActions
{{ favorite.paste.title|truncatechars:128 }}{{ favorite.added }} 23 |
24 | {% csrf_token %} 25 | 26 | 27 | 28 |
29 |
35 | {% include "pagination.html" with destination="users:favorites" url_arg=profile_user.get_username %} 36 | {% else %} 37 | {% if profile_user.get_username == user.get_username %} 38 |
39 | No favorites added
40 | You haven't added any favorites yet. 41 |
42 | {% else %} 43 |
44 | No favorites added
45 | This user hasn't added any favorites yet. 46 |
47 | {% endif %} 48 | {% endif %} 49 |
50 | {% endblock %} -------------------------------------------------------------------------------- /users/templates/users/profile/favorites/favorites_hidden.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 |
6 | Private favorites
7 | This user has opted to make his or her favorites private. 8 |
9 |
10 | {% endblock %} -------------------------------------------------------------------------------- /users/templates/users/profile/home/home.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% include "users/profile/home/home_pastes.html" %} 6 |
7 |
8 | {% include "users/profile/home/home_favorites.html" %} 9 |
10 | {% endblock %} -------------------------------------------------------------------------------- /users/templates/users/profile/home/home_favorites.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% if not profile_settings.public_favorites and request.user != profile_user %} 3 |
4 | Private favorites
5 | This user has opted to make his or her favorites private. 6 |
7 | {% elif favorites %} 8 | {% if profile_username == user.get_username %} 9 |

My recent favorites

10 | {% else %} 11 |

Recent favorites

12 | {% endif %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for favorite in favorites %} 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
Title
{{ favorite.paste.title|truncatechars:64 }}
27 | {% else %} 28 | {% if profile_user.get_username == user.get_username %} 29 |
30 | No favorites added
31 | You haven't added any favorites yet. 32 |
33 | {% else %} 34 |
35 | No favorites added
36 | This user hasn't added any favorites yet. 37 |
38 | {% endif %} 39 | {% endif %} -------------------------------------------------------------------------------- /users/templates/users/profile/home/home_pastes.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% if pastes %} 3 | {% if profile_username == user.get_username %} 4 |

My recent pastes

5 | {% else %} 6 |

Recent pastes

7 | {% endif %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% for paste in pastes %} 17 | 18 | 19 | 20 | 21 | {% endfor %} 22 | 23 |
TitleAge
{{ paste.title|truncatechars:32 }}{{ paste.submitted|naturaltime }}
24 | {% else %} 25 | {% if profile_user.get_username == user.get_username %} 26 |
27 | No pastes uploaded
28 | You haven't uploaded any pastes yet. 29 |
30 | {% else %} 31 |
32 | No pastes uploaded
33 | This user hasn't uploaded any pastes yet. 34 |
35 | {% endif %} 36 | {% endif %} -------------------------------------------------------------------------------- /users/templates/users/profile/pastes/pastes.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if pastes %} 6 | 7 | 8 | 9 | 10 | 11 | {% if profile_user == request.user %} 12 | 13 | {% endif %} 14 | 15 | 16 | 17 | {% for paste in pastes %} 18 | 19 | 20 | 21 | {% if profile_user == request.user %} 22 | 26 | {% endif %} 27 | 28 | {% endfor %} 29 | 30 |
TitleUploadedActions
{{ paste.title|truncatechars:128 }}{{ paste.submitted }} 23 | Edit 24 | Remove 25 |
31 | {% include "pagination.html" with destination="users:pastes" url_arg=profile_user.get_username %} 32 | {% else %} 33 | {% if profile_user.get_username == user.get_username %} 34 |
35 | No pastes uploaded
36 | You haven't uploaded any pastes yet. 37 |
38 | {% else %} 39 |
40 | No pastes uploaded
41 | This user hasn't uploaded any pastes yet. 42 |
43 | {% endif %} 44 | {% endif %} 45 |
46 | {% endblock %} -------------------------------------------------------------------------------- /users/templates/users/profile/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}User: {{ profile_user.get_username }} - pastebin-django{% endblock %} 4 | 5 | {% block content %} 6 |
7 |
8 |
9 | 18 |
19 |
20 |
21 |
22 | {% include "users/profile/profile_navbar.html" %} 23 |
24 | {% block profile_content %}{% endblock %} 25 |
26 |
27 | {% endblock %} -------------------------------------------------------------------------------- /users/templates/users/profile/profile_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | {% if reason == "not_found" %} 10 | User not found
11 | The user whose profile you tried to view does not exist. 12 | {% endif %} 13 |
14 |
15 |
16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /users/templates/users/profile/profile_favorites.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% if favorites %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% for paste in favorites %} 12 | 13 | 14 | 15 | 16 | {% endfor %} 17 | 18 |
TitleAge
{{ paste.title|truncatechars:32 }}{{ paste.submitted|naturaltime }}
19 | {% if favorites.count < total_favorite_count %} 20 | All favorites 21 | {% endif %} 22 | {% else %} 23 | {% if profile_username == user.get_username %} 24 |
25 | No favorites added
26 | You haven't added any favorites yet. 27 |
28 | {% else %} 29 |
30 | No favorites added
31 | This user hasn't added any favorites yet. 32 |
33 | {% endif %} 34 | {% endif %} -------------------------------------------------------------------------------- /users/templates/users/profile/profile_navbar.html: -------------------------------------------------------------------------------- 1 | {% load lineage %} 2 |
3 |
4 |

{{ profile_user.get_username }}

5 |
6 | 23 | {% if profile_user.id == user.id %} 24 |
25 |

Settings

26 |
27 | 32 | {% endif %} 33 |
-------------------------------------------------------------------------------- /users/templates/users/profile/profile_pastes.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% if pastes %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% for paste in pastes %} 12 | 13 | 14 | 15 | 16 | {% endfor %} 17 | 18 |
TitleAge
{{ paste.title|truncatechars:32 }}{{ paste.submitted|naturaltime }}
19 | {% if 1 < 5 %} 20 | All pastes 21 | {% endif %} 22 | {% else %} 23 | {% if profile_username == user.get_username %} 24 |
25 | No pastes uploaded
26 | You haven't uploaded any pastes yet. 27 |
28 | {% else %} 29 |
30 | No pastes uploaded
31 | This user hasn't uploaded any pastes yet. 32 |
33 | {% endif %} 34 | {% endif %} -------------------------------------------------------------------------------- /users/templates/users/register.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matoking/pastebin-django/5e38637e5a417ab907a353af8544f64a0ad2b127/users/templates/users/register.html -------------------------------------------------------------------------------- /users/templates/users/register/already_logged_in.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | Already logged in.
10 | You have already logged in.
11 | Return to the front page 12 |
13 |
14 |
15 |
16 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/register/register.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 | 8 |
9 | {% csrf_token %} 10 | {% include "form.html" %} 11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/register/register_success.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | Registered!
12 | You have successfully registered. You may now login.
13 | Login 14 |
15 |
16 |
17 |
18 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/settings/change_password/change_password.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if password_changed %} 6 |
7 | Password changed!
8 | Your password was successfully changed. 9 |
10 | {% endif %} 11 |
12 | {% csrf_token %} 13 | {% include "form.html" %} 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/templates/users/settings/change_preferences/change_preferences.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 | {% if preferences_changed %} 6 |
7 | Preferences changed!
8 | Your preferences were successfully changed. 9 |
10 | {% endif %} 11 |
12 | {% csrf_token %} 13 | {% include "form.html" %} 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/templates/users/settings/delete_account/account_deleted.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 | 10 |
11 | Account deleted
12 | Your account has been deleted. 13 |
14 |
15 |
16 |
17 | {% endblock content %} -------------------------------------------------------------------------------- /users/templates/users/settings/delete_account/delete_account.html: -------------------------------------------------------------------------------- 1 | {% extends "users/profile/profile.html" %} 2 | 3 | {% block profile_content %} 4 |
5 |
6 | You are about to delete your account!
7 | Deleting your account also removes any pastes you have uploaded. After deleted, your account can't be restored! 8 |
9 |
10 | {% csrf_token %} 11 | {% include "form.html" %} 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | {% endblock profile_content %} -------------------------------------------------------------------------------- /users/templates/users/settings/settings_error.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |
8 |
9 | {% if reason == "incorrect_user" %} 10 | Incorrect user
11 | You tried to change the settings of another user. That's not nice. 12 | {% elif reason == "not_logged_in" %} 13 | Not logged in
14 | You need to be logged in in order to change your settings. 15 | {% endif %} 16 |
17 |
18 |
19 |
20 | {% endblock %} -------------------------------------------------------------------------------- /users/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.views.generic import TemplateView 3 | 4 | from users import views 5 | 6 | urlpatterns = [ 7 | url(r'^register/$', views.register_view, name="register"), 8 | url(r'^login/$', views.login_view, name="login"), 9 | url(r'^logout/$', views.logout_view, name="logout"), 10 | 11 | url(r'^remove_favorite/$', views.remove_favorite, name="remove_favorite"), 12 | 13 | # Public profile pages 14 | url(r'^(?P\w+)/pastes/(?P\d+)/$', views.profile, {"tab": "pastes"}, name="pastes"), 15 | url(r'^(?P\w+)/pastes/$', views.profile, {"tab": "pastes", 16 | "page": 1}, name="pastes"), 17 | 18 | url(r'^(?P\w+)/favorites/(?P\d+)/$', views.profile, {"tab": "favorites"}, name="favorites"), 19 | url(r'^(?P\w+)/favorites/$', views.profile, {"tab": "favorites", 20 | "page": 1}, name="favorites"), 21 | 22 | url(r'^(?P\w+)/$', views.profile, {"tab": "home"}, name="profile"), 23 | 24 | # User settings 25 | url(r'^(?P\w+)/change_preferences/$', views.profile, {"tab": "change_preferences"}, name="change_preferences"), 26 | url(r'^(?P\w+)/change_password/$', views.profile, {"tab": "change_password"}, name="change_password"), 27 | url(r'^(?P\w+)/delete_account/$', views.profile, {"tab": "delete_account"}, name="delete_account"), 28 | ] 29 | --------------------------------------------------------------------------------