├── static ├── .keep ├── website │ ├── arrow.png │ ├── chevron.png │ ├── bookmark.png │ ├── noobhub-fb.png │ ├── noobhub-twit.png │ ├── noobhub_nav.png │ ├── programming.png │ ├── noobhub_pattern.png │ ├── Noobhub-web_DESK.png │ ├── Noobhub-web_LEARN.png │ ├── Noobhub-web_LOGO.png │ ├── Noobhub-web_SHARE.png │ ├── Noobhub-web_BOOKMARK.png │ └── noobhub_pattern_opacity_1024.jpg ├── favicons │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── mstile-150x150.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── browserconfig.xml │ ├── site.webmanifest │ └── safari-pinned-tab.svg ├── css │ ├── font-awesome │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ ├── less │ │ │ ├── fixed-width.less │ │ │ ├── screen-reader.less │ │ │ ├── larger.less │ │ │ ├── list.less │ │ │ ├── core.less │ │ │ ├── stacked.less │ │ │ ├── font-awesome.less │ │ │ ├── bordered-pulled.less │ │ │ ├── rotated-flipped.less │ │ │ ├── path.less │ │ │ ├── animated.less │ │ │ └── mixins.less │ │ └── scss │ │ │ ├── _fixed-width.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _core.scss │ │ │ ├── font-awesome.scss │ │ │ ├── _stacked.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _path.scss │ │ │ ├── _animated.scss │ │ │ └── _mixins.scss │ ├── starter-template.css │ ├── bootstrap-reboot.min.css.map │ └── bootstrap-reboot.min.css └── js │ └── noobhub.js ├── website ├── __init__.py ├── tests │ └── __init__.py ├── migrations │ ├── __init__.py │ ├── 0003_auto_20170128_1855.py │ ├── 0008_auto_20170216_1716.py │ ├── 0020_auto_20170426_1728.py │ ├── 0018_auto_20170422_0936.py │ ├── 0025_auto_20170508_0956.py │ ├── 0029_websiterecommendation_image_url.py │ ├── 0017_auto_20170422_0912.py │ ├── 0030_auto_20180122_1024.py │ ├── 0005_category_category_img.py │ ├── 0002_category_slug.py │ ├── 0033_auto_20180227_1241.py │ ├── 0032_subcategory_subcategory_img.py │ ├── 0031_subcategory_created_date.py │ ├── 0019_bookrecommendation_book_image_url.py │ ├── 0001_initial.py │ ├── 0021_bookrecommendation_book_publish_date.py │ ├── 0007_auto_20170216_1638.py │ ├── 0011_websiterecommendation_bookmark.py │ ├── 0026_auto_20170508_1001.py │ ├── 0034_auto_20180227_1304.py │ ├── 0014_bookrecommendation_recommended_by.py │ ├── 0009_websiterecommendation_website_author.py │ ├── 0004_subcategory.py │ ├── 0010_auto_20170313_1616.py │ ├── 0015_auto_20170419_0905.py │ ├── 0027_auto_20170510_0808.py │ ├── 0022_auto_20170428_0846.py │ ├── 0013_auto_20170419_0835.py │ ├── 0006_websiterecommendation.py │ ├── 0023_bookcomment.py │ ├── 0028_videocomment.py │ ├── 0012_websitecomment.py │ ├── 0016_auto_20170422_0855.py │ ├── 0035_auto_20180228_1121.py │ └── 0024_videorecommendation.py ├── templatetags │ ├── __init__.py │ └── noobhub_filters.py ├── apps.py ├── context_processor.py ├── templates │ ├── registration │ │ ├── logout.html │ │ ├── registration_complete.html │ │ ├── password_change_done.html │ │ ├── password_reset_email.html │ │ ├── password_reset_complete.html │ │ ├── password_change_form.html │ │ ├── password_reset_done.html │ │ ├── password_reset_form.html │ │ ├── password_reset_confirm.html │ │ ├── registration_form.html │ │ └── login.html │ └── website │ │ ├── delete_website_comment.html │ │ ├── delete_book_comment.html │ │ ├── delete_book.html │ │ ├── delete_video.html │ │ ├── delete_video_comment.html │ │ ├── delete_website.html │ │ ├── edit_book_comment.html │ │ ├── report_video_recommendation.html │ │ ├── edit_video_comment.html │ │ ├── edit_website_comment.html │ │ ├── report_website_recommendation.html │ │ ├── report_book_recommendation.html │ │ ├── create_website.html │ │ ├── video_comment_page.html │ │ ├── website_comment_page.html │ │ ├── book_comment_page.html │ │ ├── create_video.html │ │ ├── create_book.html │ │ ├── category.html │ │ ├── profile.html │ │ ├── subcategory_website_page.html │ │ ├── index.html │ │ ├── subcategory_book_page.html │ │ ├── subcategory_video_page.html │ │ ├── icons.html │ │ ├── website_comment.html │ │ ├── book_comment.html │ │ └── video_comment.html ├── admin.py └── urls.py ├── wikitowns ├── __init__.py ├── settings │ ├── __init__.py │ ├── staging.py │ ├── test.py │ ├── local.py │ ├── production.py │ └── base.py ├── wsgi.py └── urls.py ├── runtime.txt ├── Procfile ├── .gitattribute ├── .gitignore ├── staticfiles ├── admin │ ├── fonts │ │ ├── Roboto-Bold-webfont.woff │ │ ├── Roboto-Light-webfont.woff │ │ ├── Roboto-Regular-webfont.woff │ │ └── README.txt │ ├── js │ │ ├── cancel.js │ │ ├── prepopulate.min.js │ │ ├── jquery.init.js │ │ ├── prepopulate_init.js │ │ ├── popup_response.js │ │ ├── collapse.min.js │ │ ├── change_form.js │ │ ├── vendor │ │ │ ├── xregexp │ │ │ │ └── LICENSE-XREGEXP.txt │ │ │ └── jquery │ │ │ │ └── LICENSE-JQUERY.txt │ │ ├── collapse.js │ │ ├── prepopulate.js │ │ ├── actions.min.js │ │ ├── timeparse.js │ │ └── inlines.min.js │ ├── img │ │ ├── tooltag-arrowright.svg │ │ ├── README.txt │ │ ├── icon-addlink.svg │ │ ├── tooltag-add.svg │ │ ├── icon-changelink.svg │ │ ├── icon-deletelink.svg │ │ ├── icon-yes.svg │ │ ├── search.svg │ │ ├── icon-alert.svg │ │ ├── icon-no.svg │ │ ├── inline-delete.svg │ │ ├── icon-unknown.svg │ │ ├── icon-unknown-alt.svg │ │ ├── icon-clock.svg │ │ ├── gis │ │ │ ├── move_vertex_on.svg │ │ │ └── move_vertex_off.svg │ │ ├── icon-calendar.svg │ │ ├── calendar-icons.svg │ │ ├── LICENSE │ │ ├── sorting-icons.svg │ │ └── selector-icons.svg │ └── css │ │ ├── fonts.css │ │ ├── dashboard.css │ │ ├── login.css │ │ └── rtl.css └── css │ ├── starter-template.css │ ├── bootstrap-reboot.min.css.map │ └── bootstrap-reboot.min.css ├── requirements.txt ├── manage.py ├── LICENSE.md └── README.md /static/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wikitowns/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.0 2 | -------------------------------------------------------------------------------- /website/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wikitowns/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn wikitowns.wsgi 2 | -------------------------------------------------------------------------------- /.gitattribute: -------------------------------------------------------------------------------- 1 | static/* linguist-vendored 2 | -------------------------------------------------------------------------------- /wikitowns/settings/staging.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | -------------------------------------------------------------------------------- /wikitowns/settings/test.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | -------------------------------------------------------------------------------- /static/website/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/arrow.png -------------------------------------------------------------------------------- /static/website/chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/chevron.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | __pycache__ 4 | myvenv 5 | db.sqlite3 6 | .DS_Store 7 | media 8 | staticfiles 9 | -------------------------------------------------------------------------------- /static/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/favicons/favicon.ico -------------------------------------------------------------------------------- /static/website/bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/bookmark.png -------------------------------------------------------------------------------- /static/website/noobhub-fb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/noobhub-fb.png -------------------------------------------------------------------------------- /static/website/noobhub-twit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/noobhub-twit.png -------------------------------------------------------------------------------- /static/website/noobhub_nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/noobhub_nav.png -------------------------------------------------------------------------------- /static/website/programming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/programming.png -------------------------------------------------------------------------------- /static/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /static/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /static/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /static/website/noobhub_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/noobhub_pattern.png -------------------------------------------------------------------------------- /static/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /static/website/Noobhub-web_DESK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/Noobhub-web_DESK.png -------------------------------------------------------------------------------- /static/website/Noobhub-web_LEARN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/Noobhub-web_LEARN.png -------------------------------------------------------------------------------- /static/website/Noobhub-web_LOGO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/Noobhub-web_LOGO.png -------------------------------------------------------------------------------- /static/website/Noobhub-web_SHARE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/Noobhub-web_SHARE.png -------------------------------------------------------------------------------- /website/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class WebsiteConfig(AppConfig): 5 | name = 'website' 6 | -------------------------------------------------------------------------------- /static/website/Noobhub-web_BOOKMARK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/Noobhub-web_BOOKMARK.png -------------------------------------------------------------------------------- /static/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /wikitowns/settings/local.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | DEBUG = True 4 | 5 | # look into django debug_toolbar app to install for local 6 | -------------------------------------------------------------------------------- /static/css/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/css/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/website/noobhub_pattern_opacity_1024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/website/noobhub_pattern_opacity_1024.jpg -------------------------------------------------------------------------------- /staticfiles/admin/fonts/Roboto-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/staticfiles/admin/fonts/Roboto-Bold-webfont.woff -------------------------------------------------------------------------------- /staticfiles/admin/fonts/Roboto-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/staticfiles/admin/fonts/Roboto-Light-webfont.woff -------------------------------------------------------------------------------- /staticfiles/admin/fonts/Roboto-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/staticfiles/admin/fonts/Roboto-Regular-webfont.woff -------------------------------------------------------------------------------- /static/css/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/css/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/css/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/css/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/css/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/css/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/css/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ollysmall/wikitowns/HEAD/static/css/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /website/context_processor.py: -------------------------------------------------------------------------------- 1 | from website.models import Category 2 | 3 | 4 | def category_context(request): 5 | return {'categories': Category.objects.order_by('name')} 6 | -------------------------------------------------------------------------------- /staticfiles/admin/fonts/README.txt: -------------------------------------------------------------------------------- 1 | Roboto webfont source: https://www.google.com/fonts/specimen/Roboto 2 | Weights used in this project: Light (300), Regular (400), Bold (700) 3 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/css/starter-template.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 3.5rem; 3 | background-color: #fafafa; 4 | } 5 | .starter-template { 6 | padding: 3rem 1.5rem; 7 | text-align: center; 8 | } 9 | -------------------------------------------------------------------------------- /staticfiles/css/starter-template.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 3.5rem; 3 | background-color: #fafafa; 4 | } 5 | .starter-template { 6 | padding: 3rem 1.5rem; 7 | text-align: center; 8 | } 9 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /website/templates/registration/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | 4 | {% block body_block %} 5 |

Logged Out

6 |

You are now logged out.

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /website/templates/registration/registration_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | 4 | {% block body_block %} 5 |

Registration Complete

6 |

You are now registered

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /staticfiles/admin/js/cancel.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 'use strict'; 3 | $(function() { 4 | $('.cancel-link').click(function(e) { 5 | e.preventDefault(); 6 | window.history.back(); 7 | }); 8 | }); 9 | })(django.jQuery); 10 | -------------------------------------------------------------------------------- /static/favicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /staticfiles/admin/img/tooltag-arrowright.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /staticfiles/admin/img/README.txt: -------------------------------------------------------------------------------- 1 | All icons are taken from Font Awesome (http://fontawesome.io/) project. 2 | The Font Awesome font is licensed under the SIL OFL 1.1: 3 | - http://scripts.sil.org/OFL 4 | 5 | SVG icons source: https://github.com/encharm/Font-Awesome-SVG-PNG 6 | Font-Awesome-SVG-PNG is licensed under the MIT license (see file license 7 | in current folder). 8 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-addlink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /staticfiles/admin/img/tooltag-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/favicons/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "noobhub", 3 | "short_name": "noobhub", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "theme_color": "#ffffff", 12 | "background_color": "#ffffff", 13 | "display": "standalone" 14 | } 15 | -------------------------------------------------------------------------------- /staticfiles/admin/js/prepopulate.min.js: -------------------------------------------------------------------------------- 1 | (function(c){c.fn.prepopulate=function(e,f,g){return this.each(function(){var a=c(this),b=function(){if(!a.data("_changed")){var b=[];c.each(e,function(a,d){d=c(d);0 2 | 3 | 4 | -------------------------------------------------------------------------------- /staticfiles/admin/js/jquery.init.js: -------------------------------------------------------------------------------- 1 | /*global django:true, jQuery:false*/ 2 | /* Puts the included jQuery into our own namespace using noConflict and passing 3 | * it 'true'. This ensures that the included jQuery doesn't pollute the global 4 | * namespace (i.e. this preserves pre-existing values for both window.$ and 5 | * window.jQuery). 6 | */ 7 | var django = django || {}; 8 | django.jQuery = jQuery.noConflict(true); 9 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-deletelink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /website/templatetags/noobhub_filters.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.template.defaultfilters import stringfilter 3 | 4 | register = template.Library() 5 | 6 | 7 | @register.filter 8 | @stringfilter 9 | def upto(value, delimiter=None): 10 | return value.split(delimiter)[0] 11 | upto.is_safe = True 12 | 13 | 14 | # used to get the class name of models 15 | @register.filter 16 | def classname(obj): 17 | return obj.__class__.__name__ 18 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-yes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /staticfiles/admin/img/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /website/templates/registration/password_change_done.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 |
6 | 7 | 17 |
18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /website/templates/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | 3 | You're receiving this email because you requested a password reset for your user account at {{ site_name }}. 4 | Please go to the following page and choose a new password: 5 | {% block reset_link %} 6 | {{ protocol }}://{{ domain }}{% url 'auth_password_reset_confirm' uid token %} 7 | {% endblock %} 8 | Your username, in case you've forgotten: {{ user.get_username }} 9 | Thanks for using our site! 10 | 11 | {% endautoescape %} 12 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /staticfiles/admin/css/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | src: url('../fonts/Roboto-Bold-webfont.woff'); 4 | font-weight: 700; 5 | font-style: normal; 6 | } 7 | 8 | @font-face { 9 | font-family: 'Roboto'; 10 | src: url('../fonts/Roboto-Regular-webfont.woff'); 11 | font-weight: 400; 12 | font-style: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: 'Roboto'; 17 | src: url('../fonts/Roboto-Light-webfont.woff'); 18 | font-weight: 300; 19 | font-style: normal; 20 | } 21 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /website/migrations/0003_auto_20170128_1855.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-01-28 18:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0002_category_slug'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='category', 17 | name='slug', 18 | field=models.SlugField(), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /staticfiles/admin/js/prepopulate_init.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 'use strict'; 3 | var fields = $('#django-admin-prepopulated-fields-constants').data('prepopulatedFields'); 4 | $.each(fields, function(index, field) { 5 | $('.empty-form .form-row .field-' + field.name + ', .empty-form.form-row .field-' + field.name).addClass('prepopulated_field'); 6 | $(field.id).data('dependency_list', field.dependency_list).prepopulate( 7 | field.dependency_ids, field.maxLength, field.allowUnicode 8 | ); 9 | }); 10 | })(django.jQuery); 11 | -------------------------------------------------------------------------------- /wikitowns/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for wikitowns 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.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | from django.core.wsgi import get_wsgi_application 12 | from whitenoise.django import DjangoWhiteNoise 13 | 14 | # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wikitowns.settings") 15 | 16 | application = get_wsgi_application() 17 | application = DjangoWhiteNoise(application) 18 | -------------------------------------------------------------------------------- /website/migrations/0008_auto_20170216_1716.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-02-16 17:16 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0007_auto_20170216_1638'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterUniqueTogether( 16 | name='websiterecommendation', 17 | unique_together=set([('category', 'subcategory', 'url')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-no.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/migrations/0020_auto_20170426_1728.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-26 17:28 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0019_bookrecommendation_book_image_url'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterUniqueTogether( 16 | name='bookrecommendation', 17 | unique_together=set([('category', 'subcategory', 'isbn')]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /static/favicons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /staticfiles/admin/img/inline-delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/migrations/0018_auto_20170422_0936.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-22 09:36 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0017_auto_20170422_0912'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='bookrecommendation', 17 | name='book_url', 18 | field=models.URLField(max_length=2000), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /website/migrations/0025_auto_20170508_0956.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-05-08 09:56 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0024_videorecommendation'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='videorecommendation', 17 | name='video_publish_date', 18 | field=models.DateTimeField(), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /website/migrations/0029_websiterecommendation_image_url.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-10-05 08:49 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0028_videocomment'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='websiterecommendation', 17 | name='image_url', 18 | field=models.URLField(null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /staticfiles/admin/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* DASHBOARD */ 2 | 3 | .dashboard .module table th { 4 | width: 100%; 5 | } 6 | 7 | .dashboard .module table td { 8 | white-space: nowrap; 9 | } 10 | 11 | .dashboard .module table td a { 12 | display: block; 13 | padding-right: .6em; 14 | } 15 | 16 | /* RECENT ACTIONS MODULE */ 17 | 18 | .module ul.actionlist { 19 | margin-left: 0; 20 | } 21 | 22 | ul.actionlist li { 23 | list-style-type: none; 24 | } 25 | 26 | ul.actionlist li { 27 | overflow: hidden; 28 | text-overflow: ellipsis; 29 | -o-text-overflow: ellipsis; 30 | } 31 | -------------------------------------------------------------------------------- /website/migrations/0017_auto_20170422_0912.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-22 09:12 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0016_auto_20170422_0855'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='bookrecommendation', 17 | name='book_description', 18 | field=models.CharField(max_length=10000), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /website/migrations/0030_auto_20180122_1024.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2018-01-22 10:24 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0029_websiterecommendation_image_url'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='bookrecommendation', 17 | name='title', 18 | field=models.CharField(max_length=500), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /website/migrations/0005_category_category_img.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-02-06 09:45 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0004_subcategory'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='category', 17 | name='category_img', 18 | field=models.ImageField(blank=True, upload_to='category_images'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /website/migrations/0002_category_slug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-01-28 18:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='category', 17 | name='slug', 18 | field=models.SlugField(default='', max_length=100, unique=True), 19 | preserve_default=False, 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /website/migrations/0033_auto_20180227_1241.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2018-02-27 12:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0032_subcategory_subcategory_img'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='subcategory', 17 | name='subcategory_img', 18 | field=models.ImageField(null=True, upload_to='subcategory_images'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /website/templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 |
6 | 7 | 21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /website/migrations/0032_subcategory_subcategory_img.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2018-02-26 10:26 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0031_subcategory_created_date'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='subcategory', 17 | name='subcategory_img', 18 | field=models.ImageField(blank=True, upload_to='subcategory_images'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /website/migrations/0031_subcategory_created_date.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2018-01-22 14:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('website', '0030_auto_20180122_1024'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='subcategory', 18 | name='created_date', 19 | field=models.DateTimeField(default=django.utils.timezone.now), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /website/migrations/0019_bookrecommendation_book_image_url.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-22 19:15 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0018_auto_20170422_0936'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='bookrecommendation', 17 | name='book_image_url', 18 | field=models.URLField(default=1, max_length=500), 19 | preserve_default=False, 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /staticfiles/admin/js/popup_response.js: -------------------------------------------------------------------------------- 1 | /*global opener */ 2 | (function() { 3 | 'use strict'; 4 | var initData = JSON.parse(document.getElementById('django-admin-popup-response-constants').dataset.popupResponse); 5 | switch(initData.action) { 6 | case 'change': 7 | opener.dismissChangeRelatedObjectPopup(window, initData.value, initData.obj, initData.new_value); 8 | break; 9 | case 'delete': 10 | opener.dismissDeleteRelatedObjectPopup(window, initData.value); 11 | break; 12 | default: 13 | opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj); 14 | break; 15 | } 16 | })(); 17 | -------------------------------------------------------------------------------- /website/templates/registration/password_change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 |
6 | 7 | 23 |
24 | 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /staticfiles/admin/js/collapse.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a(document).ready(function(){a("fieldset.collapse").each(function(b,c){0===a(c).find("div.errors").length&&a(c).addClass("collapsed").find("h2").first().append(' ('+gettext("Show")+")")});a("fieldset.collapse a.collapse-toggle").click(function(b){a(this).closest("fieldset").hasClass("collapsed")?a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]):a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", 2 | [a(this).attr("id")]);return!1})})})(django.jQuery); 3 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-unknown-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 |
6 | 7 | 20 |
21 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /website/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-01-28 18:30 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Category', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=128, unique=True)), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /website/migrations/0021_bookrecommendation_book_publish_date.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-26 18:31 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('website', '0020_auto_20170426_1728'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='bookrecommendation', 18 | name='book_publish_date', 19 | field=models.DateField(default=django.utils.timezone.now), 20 | preserve_default=False, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | awscli==1.11.45 2 | beautifulsoup4==4.5.3 3 | boto3==1.4.4 4 | botocore==1.5.8 5 | bottlenose==1.1.2 6 | colorama==0.3.7 7 | dj-database-url==0.4.2 8 | Django==1.10.5 9 | django-el-pagination==3.1.0 10 | django-isbn-field==0.3 11 | django-registration-redux==1.4 12 | django-storages==1.5.2 13 | docutils==0.13.1 14 | google-api-python-client==1.6.2 15 | gunicorn==19.6.0 16 | httplib2==0.10.3 17 | jmespath==0.9.1 18 | lxml==3.7.3 19 | oauth2client==4.0.0 20 | olefile==0.44 21 | Pillow==4.0.0 22 | psycopg2==2.8.4 23 | pyasn1==0.2.3 24 | pyasn1-modules==0.0.8 25 | python-dateutil==2.6.0 26 | python-stdnum==1.6 27 | PyYAML==3.12 28 | rsa==3.4.2 29 | s3transfer==0.1.10 30 | six==1.10.0 31 | uritemplate==3.0.0 32 | whitenoise==3.3.1 33 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /website/migrations/0007_auto_20170216_1638.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-02-16 16:38 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0006_websiterecommendation'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='subcategory', 17 | name='name', 18 | field=models.CharField(max_length=128), 19 | ), 20 | migrations.AlterUniqueTogether( 21 | name='subcategory', 22 | unique_together=set([('category', 'name')]), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /website/migrations/0011_websiterecommendation_bookmark.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-03-29 17:01 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('website', '0010_auto_20170313_1616'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='websiterecommendation', 19 | name='bookmark', 20 | field=models.ManyToManyField(related_name='bookmark', to=settings.AUTH_USER_MODEL), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /website/templates/website/delete_website_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 24 |
25 | 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /website/templates/website/delete_book_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /staticfiles/admin/js/change_form.js: -------------------------------------------------------------------------------- 1 | /*global showAddAnotherPopup, showRelatedObjectLookupPopup showRelatedObjectPopup updateRelatedObjectLinks*/ 2 | 3 | (function($) { 4 | 'use strict'; 5 | $(document).ready(function() { 6 | var modelName = $('#django-admin-form-add-constants').data('modelName'); 7 | $('.add-another').click(function(e) { 8 | e.preventDefault(); 9 | var event = $.Event('django:add-another-related'); 10 | $(this).trigger(event); 11 | if (!event.isDefaultPrevented()) { 12 | showAddAnotherPopup(this); 13 | } 14 | }); 15 | 16 | if (modelName) { 17 | $('form#' + modelName + '_form :input:visible:enabled:first').focus(); 18 | } 19 | }); 20 | })(django.jQuery); 21 | -------------------------------------------------------------------------------- /website/templates/website/delete_book.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/templates/website/delete_video.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/templates/website/delete_video_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /website/templates/website/delete_website.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/migrations/0026_auto_20170508_1001.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-05-08 10:01 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0025_auto_20170508_0956'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='videorecommendation', 17 | name='video_id', 18 | field=models.CharField(default=1, max_length=128), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterUniqueTogether( 22 | name='videorecommendation', 23 | unique_together=set([('category', 'subcategory', 'video_id')]), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /website/templates/website/edit_book_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 26 |
27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /website/templates/website/report_video_recommendation.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from website.models import (Category, SubCategory, WebsiteRecommendation, 3 | WebsiteComment, BookRecommendation, BookComment, 4 | VideoRecommendation, VideoComment) 5 | 6 | 7 | class CategoryAdmin(admin.ModelAdmin): 8 | prepopulated_fields = {'slug': ('name',)} 9 | 10 | 11 | class SubCategoryAdmin(admin.ModelAdmin): 12 | prepopulated_fields = {'slug': ('name',)} 13 | 14 | admin.site.register(Category, CategoryAdmin) 15 | admin.site.register(SubCategory, SubCategoryAdmin) 16 | admin.site.register(WebsiteRecommendation) 17 | admin.site.register(WebsiteComment) 18 | admin.site.register(BookRecommendation) 19 | admin.site.register(BookComment) 20 | admin.site.register(VideoRecommendation) 21 | admin.site.register(VideoComment) 22 | -------------------------------------------------------------------------------- /website/templates/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 |
6 | 7 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/templates/website/edit_video_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 26 |
27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /website/templates/website/edit_website_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 26 |
27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /website/templates/website/report_website_recommendation.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/migrations/0034_auto_20180227_1304.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2018-02-27 13:04 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('website', '0033_auto_20180227_1241'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='category', 17 | name='category_img', 18 | field=models.ImageField(blank=True, null=True, upload_to='category_images'), 19 | ), 20 | migrations.AlterField( 21 | model_name='subcategory', 22 | name='subcategory_img', 23 | field=models.ImageField(blank=True, null=True, upload_to='subcategory_images'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /website/templates/website/report_book_recommendation.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /website/migrations/0014_bookrecommendation_recommended_by.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-19 08:53 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('website', '0013_auto_20170419_0835'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='bookrecommendation', 20 | name='recommended_by', 21 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 22 | preserve_default=False, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /website/migrations/0009_websiterecommendation_website_author.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-03-03 09:17 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('website', '0008_auto_20170216_1716'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='websiterecommendation', 20 | name='website_author', 21 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 22 | preserve_default=False, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /website/templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 |
6 | 7 | 33 |
34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /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", os.environ['DJANGO_SETTINGS_MODULE']) 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /website/templates/website/create_website.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /website/migrations/0004_subcategory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-01-29 10:13 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('website', '0003_auto_20170128_1855'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='SubCategory', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=128, unique=True)), 21 | ('slug', models.SlugField()), 22 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Category')), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /website/templates/website/video_comment_page.html: -------------------------------------------------------------------------------- 1 | {% load el_pagination_tags %} 2 | {% load noobhub_filters %} 3 | 4 | {% paginate comments %} 5 | 6 | {% for comment in comments %} 7 |
8 |
9 | 10 | {{ comment.author }} 11 | 12 |   {{ comment.text }} 13 |

14 | {{ comment.created_date|timesince|upto:',' }} ago 15 | {% if user.is_authenticated %} 16 | {% if comment.author.pk == user.pk %} 17 |  ·  Delete  ·  18 | Edit 19 | {% endif %} 20 | {% endif %} 21 |

22 |
23 | 24 | {% endfor %} 25 | 26 | {% show_more %} 27 | -------------------------------------------------------------------------------- /website/templates/website/website_comment_page.html: -------------------------------------------------------------------------------- 1 | {% load el_pagination_tags %} 2 | {% load noobhub_filters %} 3 | 4 | {% paginate comments %} 5 | 6 | {% for comment in comments %} 7 |
8 |
9 | 10 | {{ comment.author }} 11 | 12 |   {{ comment.text }} 13 |

14 | {{ comment.created_date|timesince|upto:',' }} ago 15 | {% if user.is_authenticated %} 16 | {% if comment.author.pk == user.pk %} 17 |  ·  Delete  ·  18 | Edit 19 | {% endif %} 20 | {% endif %} 21 |

22 |
23 | {% endfor %} 24 | 25 | {% show_more %} 26 | -------------------------------------------------------------------------------- /website/migrations/0010_auto_20170313_1616.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-03-13 16:16 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('website', '0009_websiterecommendation_website_author'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='websiterecommendation', 19 | name='downvote', 20 | field=models.ManyToManyField(related_name='website_downvote', to=settings.AUTH_USER_MODEL), 21 | ), 22 | migrations.AddField( 23 | model_name='websiterecommendation', 24 | name='upvote', 25 | field=models.ManyToManyField(related_name='website_upvote', to=settings.AUTH_USER_MODEL), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /website/migrations/0015_auto_20170419_0905.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-19 09:05 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('website', '0014_bookrecommendation_recommended_by'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='bookrecommendation', 18 | name='category', 19 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='website.Category'), 20 | preserve_default=False, 21 | ), 22 | migrations.AddField( 23 | model_name='bookrecommendation', 24 | name='subcategory', 25 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='website.SubCategory'), 26 | preserve_default=False, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /website/templates/website/book_comment_page.html: -------------------------------------------------------------------------------- 1 | {% load el_pagination_tags %} 2 | {% load noobhub_filters %} 3 | {% paginate comments %} 4 | 5 | {% for comment in comments %} 6 |
7 |
8 | 9 | {{ comment.author }} 10 | 11 |   {{ comment.text }} 12 |

13 | 14 | {{ comment.created_date|timesince|upto:',' }} ago 15 | 16 | {% if user.is_authenticated %} 17 | {% if comment.author.pk == user.pk %} 18 | 19 |  ·  Delete  ·  20 | 21 | 22 | Edit 23 | 24 | {% endif %} 25 | {% endif %} 26 |

27 |
28 | {% endfor %} 29 | 30 | {% show_more %} 31 | -------------------------------------------------------------------------------- /staticfiles/admin/img/gis/move_vertex_on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /staticfiles/admin/img/icon-calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /staticfiles/admin/img/gis/move_vertex_off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/templates/website/create_video.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 33 |
34 | 35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Oliver Small 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wikitowns/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | #from whitenoise import WhiteNoise 3 | 4 | # settings from heroku tutorial 5 | import dj_database_url 6 | db_from_env = dj_database_url.config(conn_max_age=500) 7 | DATABASES['default'].update(db_from_env) 8 | 9 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 10 | 11 | # force use of SSL 12 | SECURE_SSL_REDIRECT = True 13 | 14 | CSRF_COOKIE_SECURE = True 15 | 16 | SESSION_COOKIE_SECURE = True 17 | 18 | ALLOWED_HOSTS = ['.noobhub.io'] 19 | 20 | ADMINS = [('Oliver', 'oliver@rotherfields.co.uk')] 21 | 22 | # AWS S3 settings 23 | AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME'] 24 | AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID'] 25 | AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY'] 26 | AWS_S3_REGION = 'us-east-2' # had to configure manually through aws cli 27 | AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME 28 | 29 | MEDIA_URL = "https://%s/" % AWS_S3_CUSTOM_DOMAIN 30 | DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' 31 | 32 | #STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 33 | -------------------------------------------------------------------------------- /staticfiles/admin/img/calendar-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /staticfiles/admin/img/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Code Charm Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /staticfiles/admin/img/sorting-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /website/migrations/0027_auto_20170510_0808.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-05-10 08:08 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('website', '0026_auto_20170508_1001'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='videorecommendation', 19 | name='bookmark', 20 | field=models.ManyToManyField(related_name='video_bookmark', to=settings.AUTH_USER_MODEL), 21 | ), 22 | migrations.AddField( 23 | model_name='videorecommendation', 24 | name='downvote', 25 | field=models.ManyToManyField(related_name='video_downvote', to=settings.AUTH_USER_MODEL), 26 | ), 27 | migrations.AddField( 28 | model_name='videorecommendation', 29 | name='upvote', 30 | field=models.ManyToManyField(related_name='video_upvote', to=settings.AUTH_USER_MODEL), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /website/migrations/0022_auto_20170428_0846.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-28 08:46 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('website', '0021_bookrecommendation_book_publish_date'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='bookrecommendation', 19 | name='bookmark', 20 | field=models.ManyToManyField(related_name='book_bookmark', to=settings.AUTH_USER_MODEL), 21 | ), 22 | migrations.AddField( 23 | model_name='bookrecommendation', 24 | name='downvote', 25 | field=models.ManyToManyField(related_name='book_downvote', to=settings.AUTH_USER_MODEL), 26 | ), 27 | migrations.AddField( 28 | model_name='bookrecommendation', 29 | name='upvote', 30 | field=models.ManyToManyField(related_name='book_upvote', to=settings.AUTH_USER_MODEL), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /staticfiles/admin/js/vendor/xregexp/LICENSE-XREGEXP.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2007-2012 Steven Levithan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /website/templates/website/create_book.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 | 37 |
38 | 39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /website/migrations/0013_auto_20170419_0835.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-19 08:35 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | import isbn_field.fields 8 | import isbn_field.validators 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | ('website', '0012_websitecomment'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='BookRecommendation', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('isbn', isbn_field.fields.ISBNField(max_length=13, validators=[isbn_field.validators.ISBNValidator], verbose_name='ISBN')), 23 | ('title', models.CharField(max_length=128)), 24 | ], 25 | ), 26 | migrations.AlterField( 27 | model_name='websitecomment', 28 | name='website', 29 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='website.WebsiteRecommendation'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /website/migrations/0006_websiterecommendation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-02-16 10:01 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | import django.utils.timezone 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('website', '0005_category_category_img'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='WebsiteRecommendation', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('title', models.CharField(max_length=128)), 22 | ('description', models.CharField(max_length=300)), 23 | ('url', models.URLField()), 24 | ('created_date', models.DateTimeField(default=django.utils.timezone.now)), 25 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Category')), 26 | ('subcategory', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.SubCategory')), 27 | ], 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /website/migrations/0023_bookcomment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-05-02 08:48 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ('website', '0022_auto_20170428_0846'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='BookComment', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('text', models.TextField(max_length=2000)), 24 | ('created_date', models.DateTimeField(default=django.utils.timezone.now)), 25 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='book_comments', to='website.BookRecommendation')), 27 | ], 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /website/migrations/0028_videocomment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-05-10 08:28 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ('website', '0027_auto_20170510_0808'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='VideoComment', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('text', models.TextField(max_length=2000)), 24 | ('created_date', models.DateTimeField(default=django.utils.timezone.now)), 25 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ('video', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='video_comments', to='website.VideoRecommendation')), 27 | ], 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /staticfiles/admin/js/collapse.js: -------------------------------------------------------------------------------- 1 | /*global gettext*/ 2 | (function($) { 3 | 'use strict'; 4 | $(document).ready(function() { 5 | // Add anchor tag for Show/Hide link 6 | $("fieldset.collapse").each(function(i, elem) { 7 | // Don't hide if fields in this fieldset have errors 8 | if ($(elem).find("div.errors").length === 0) { 9 | $(elem).addClass("collapsed").find("h2").first().append(' (' + gettext("Show") + 11 | ')'); 12 | } 13 | }); 14 | // Add toggle to anchor tag 15 | $("fieldset.collapse a.collapse-toggle").click(function(ev) { 16 | if ($(this).closest("fieldset").hasClass("collapsed")) { 17 | // Show 18 | $(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]); 19 | } else { 20 | // Hide 21 | $(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]); 22 | } 23 | return false; 24 | }); 25 | }); 26 | })(django.jQuery); 27 | -------------------------------------------------------------------------------- /website/migrations/0012_websitecomment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-04 08:04 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ('website', '0011_websiterecommendation_bookmark'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='WebsiteComment', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('text', models.TextField(max_length=2000)), 24 | ('created_date', models.DateTimeField(default=django.utils.timezone.now)), 25 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ('website', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='website_comments', to='website.WebsiteRecommendation')), 27 | ], 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /static/js/noobhub.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $('#myTab a:first').tab('show'); 3 | 4 | //changes button text on click for descriptions button 5 | $(document).on("click", '.description-button', function(e){ 6 | var $this = $(this); 7 | $this.toggleClass('SeeMore'); 8 | if($this.hasClass('SeeMore')){ 9 | $this.text('View description').append('  '); 10 | } else { 11 | $this.text('Hide description').append('  '); 12 | } 13 | }) 14 | }); 15 | 16 | $(function() { 17 | $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { 18 | // save the latest tab 19 | localStorage.setItem('lastTab', $(this).attr('href')); 20 | }); 21 | 22 | // go to the latest tab, if it exists: 23 | var lastTab = localStorage.getItem('lastTab'); 24 | if (lastTab) { 25 | $('[href="' + lastTab + '"]').tab('show'); 26 | //window.localStorage.removeItem("lastTab"); 27 | } 28 | }); 29 | 30 | //show & hide search bar 31 | function ShowSearch() { 32 | var x = document.getElementById('SearchFormDiv'); 33 | if (x.style.display === 'none') { 34 | x.style.display = 'block'; 35 | } else { 36 | x.style.display = 'none'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /website/migrations/0016_auto_20170422_0855.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-04-22 08:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('website', '0015_auto_20170419_0905'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='bookrecommendation', 18 | name='book_author', 19 | field=models.CharField(default=1, max_length=128), 20 | preserve_default=False, 21 | ), 22 | migrations.AddField( 23 | model_name='bookrecommendation', 24 | name='book_description', 25 | field=models.CharField(default=1, max_length=2000), 26 | preserve_default=False, 27 | ), 28 | migrations.AddField( 29 | model_name='bookrecommendation', 30 | name='book_url', 31 | field=models.URLField(default=1), 32 | preserve_default=False, 33 | ), 34 | migrations.AddField( 35 | model_name='bookrecommendation', 36 | name='created_date', 37 | field=models.DateTimeField(default=django.utils.timezone.now), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /website/migrations/0035_auto_20180228_1121.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2018-02-28 11:21 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('website', '0034_auto_20180227_1304'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='websiterecommendation', 18 | name='bookmark', 19 | field=models.ManyToManyField(blank=True, related_name='bookmark', to=settings.AUTH_USER_MODEL), 20 | ), 21 | migrations.AlterField( 22 | model_name='websiterecommendation', 23 | name='downvote', 24 | field=models.ManyToManyField(blank=True, related_name='website_downvote', to=settings.AUTH_USER_MODEL), 25 | ), 26 | migrations.AlterField( 27 | model_name='websiterecommendation', 28 | name='image_url', 29 | field=models.URLField(blank=True, null=True), 30 | ), 31 | migrations.AlterField( 32 | model_name='websiterecommendation', 33 | name='upvote', 34 | field=models.ManyToManyField(blank=True, related_name='website_upvote', to=settings.AUTH_USER_MODEL), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /staticfiles/admin/js/vendor/jquery/LICENSE-JQUERY.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | ==== 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining 10 | a copy of this software and associated documentation files (the 11 | "Software"), to deal in the Software without restriction, including 12 | without limitation the rights to use, copy, modify, merge, publish, 13 | distribute, sublicense, and/or sell copies of the Software, and to 14 | permit persons to whom the Software is furnished to do so, subject to 15 | the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /website/templates/website/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub - category 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 |

12 | {{ category_name }} 13 |

14 |
15 |
16 |
17 | {% if subcategories %} 18 | 36 | {% else %} 37 | No subcategory currently in category. 38 | {% endif %} 39 |
40 |
41 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /website/templates/registration/registration_form.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | 4 | {% block body_block %} 5 | 6 |
7 | 8 | 46 |
47 | 48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /website/templates/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends "website/base.html" %} 2 | 3 | {% block body_block %} 4 | 5 | 6 | 7 | 8 |
9 | 10 | 42 |
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /staticfiles/admin/css/login.css: -------------------------------------------------------------------------------- 1 | /* LOGIN FORM */ 2 | 3 | body.login { 4 | background: #f8f8f8; 5 | } 6 | 7 | .login #header { 8 | height: auto; 9 | padding: 5px 16px; 10 | } 11 | 12 | .login #header h1 { 13 | font-size: 18px; 14 | } 15 | 16 | .login #header h1 a { 17 | color: #fff; 18 | } 19 | 20 | .login #content { 21 | padding: 20px 20px 0; 22 | } 23 | 24 | .login #container { 25 | background: #fff; 26 | border: 1px solid #eaeaea; 27 | border-radius: 4px; 28 | overflow: hidden; 29 | width: 28em; 30 | min-width: 300px; 31 | margin: 100px auto; 32 | } 33 | 34 | .login #content-main { 35 | width: 100%; 36 | } 37 | 38 | .login .form-row { 39 | padding: 4px 0; 40 | float: left; 41 | width: 100%; 42 | border-bottom: none; 43 | } 44 | 45 | .login .form-row label { 46 | padding-right: 0.5em; 47 | line-height: 2em; 48 | font-size: 1em; 49 | clear: both; 50 | color: #333; 51 | } 52 | 53 | .login .form-row #id_username, .login .form-row #id_password { 54 | clear: both; 55 | padding: 8px; 56 | width: 100%; 57 | -webkit-box-sizing: border-box; 58 | -moz-box-sizing: border-box; 59 | box-sizing: border-box; 60 | } 61 | 62 | .login span.help { 63 | font-size: 10px; 64 | display: block; 65 | } 66 | 67 | .login .submit-row { 68 | clear: both; 69 | padding: 1em 0 0 9.4em; 70 | margin: 0; 71 | border: none; 72 | background: none; 73 | text-align: left; 74 | } 75 | 76 | .login .password-reset-link { 77 | text-align: center; 78 | } 79 | -------------------------------------------------------------------------------- /website/migrations/0024_videorecommendation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-05-08 09:46 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ('website', '0023_bookcomment'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='VideoRecommendation', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('title', models.CharField(max_length=128)), 24 | ('video_description', models.CharField(max_length=10000)), 25 | ('created_date', models.DateTimeField(default=django.utils.timezone.now)), 26 | ('video_publish_date', models.DateField()), 27 | ('video_url', models.URLField(max_length=2000)), 28 | ('video_image_url', models.URLField(max_length=500)), 29 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Category')), 30 | ('recommended_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 31 | ('subcategory', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.SubCategory')), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /staticfiles/admin/js/prepopulate.js: -------------------------------------------------------------------------------- 1 | /*global URLify*/ 2 | (function($) { 3 | 'use strict'; 4 | $.fn.prepopulate = function(dependencies, maxLength, allowUnicode) { 5 | /* 6 | Depends on urlify.js 7 | Populates a selected field with the values of the dependent fields, 8 | URLifies and shortens the string. 9 | dependencies - array of dependent fields ids 10 | maxLength - maximum length of the URLify'd string 11 | allowUnicode - Unicode support of the URLify'd string 12 | */ 13 | return this.each(function() { 14 | var prepopulatedField = $(this); 15 | 16 | var populate = function() { 17 | // Bail if the field's value has been changed by the user 18 | if (prepopulatedField.data('_changed')) { 19 | return; 20 | } 21 | 22 | var values = []; 23 | $.each(dependencies, function(i, field) { 24 | field = $(field); 25 | if (field.val().length > 0) { 26 | values.push(field.val()); 27 | } 28 | }); 29 | prepopulatedField.val(URLify(values.join(' '), maxLength, allowUnicode)); 30 | }; 31 | 32 | prepopulatedField.data('_changed', false); 33 | prepopulatedField.change(function() { 34 | prepopulatedField.data('_changed', true); 35 | }); 36 | 37 | if (!prepopulatedField.val()) { 38 | $(dependencies.join(',')).keyup(populate).change(populate).focus(populate); 39 | } 40 | }); 41 | }; 42 | })(django.jQuery); 43 | -------------------------------------------------------------------------------- /static/css/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /static/css/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /website/templates/website/profile.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | {% load noobhub_filters %} 4 | 5 | {% block title %} 6 | noobhub 7 | {% endblock %} 8 | 9 | {% block body_block %} 10 | 11 |
12 |
13 |

{{ profile_user }}

14 | 15 | {% if user.is_authenticated %} 16 | {% if profile_user.pk == user.pk %} 17 |
18 | Change Password 19 |
20 | {% endif %} 21 | {% endif %} 22 |
23 |
24 |
25 | 33 |

34 |
35 | 36 | 37 | 38 |
39 | 40 |
41 | 42 |
43 |

44 |
45 | {% if recommendations_list %} 46 |
47 | {% include "website/profile_recommendations_page.html" %} 48 |
49 | {% else %} 50 | No recommendations have been made yet. 51 | {% endif %} 52 |
53 |
54 | 55 |
56 |
57 |

58 | {% if bookmark_list %} 59 |
60 | {% include "website/profile_bookmarks_page.html" %} 61 |
62 | {% else %} 63 | No bookmarks have been made yet. 64 | {% endif %} 65 |
66 |
67 | 68 |
69 |
70 | 71 | {% endblock %} 72 | -------------------------------------------------------------------------------- /static/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/_normalize.scss","bootstrap-reboot.css","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/mixins/_hover.scss"],"names":[],"mappings":"4EAYA,KACE,YAAA,WACA,YAAA,KACA,qBAAA,KACA,yBAAA,KAUF,KACE,OAAA,EAOF,QAAA,MAAA,OAAA,OAAA,IAAA,QAME,QAAA,MAQF,GACE,UAAA,IACA,OAAA,MAAA,EAWF,WAAA,OAAA,KAGE,QAAA,MAOF,OACE,OAAA,IAAA,KAQF,GACE,mBAAA,YAAA,WAAA,YACA,OAAA,EACA,SAAA,QAQF,IACE,YAAA,UAAA,UACA,UAAA,IAWF,EACE,iBAAA,YACA,6BAAA,QAQF,SAAA,QAEE,cAAA,EAQF,YACE,cAAA,KACA,gBAAA,UACA,gBAAA,UAAA,OAOF,EAAA,OAEE,YAAA,QAOF,EAAA,OAEE,YAAA,OAQF,KAAA,IAAA,KAGE,YAAA,UAAA,UACA,UAAA,IAOF,IACE,WAAA,OAOF,KACE,iBAAA,KACA,MAAA,KAOF,MACE,UAAA,IAQF,IAAA,IAEE,UAAA,IACA,YAAA,EACA,SAAA,SACA,eAAA,SAGF,IACE,OAAA,OAGF,IACE,IAAA,MAUF,MAAA,MAEE,QAAA,aAOF,sBACE,QAAA,KACA,OAAA,EAOF,IACE,aAAA,KAOF,eACE,SAAA,OAWF,OAAA,MAAA,SAAA,OAAA,SAKE,YAAA,WACA,UAAA,KACA,YAAA,KACA,OAAA,EAQF,OAAA,MAEE,SAAA,QAQF,OAAA,OAEE,eAAA,KASF,aAAA,cAAA,OAAA,mBAIE,mBAAA,OAOF,gCAAA,+BAAA,gCAAA,yBAIE,aAAA,KACA,QAAA,EAOF,6BAAA,4BAAA,6BAAA,sBAIE,QAAA,IAAA,OAAA,WAOF,SACE,OAAA,IAAA,MAAA,OACA,OAAA,EAAA,IACA,QAAA,MAAA,OAAA,MAUF,OACE,mBAAA,WAAA,WAAA,WACA,MAAA,QACA,QAAA,MACA,UAAA,KACA,QAAA,EACA,YAAA,OAQF,SACE,QAAA,aACA,eAAA,SAOF,SACE,SAAA,KCrKF,gBAAA,aD+KE,mBAAA,WAAA,WAAA,WACA,QAAA,EC1KF,yCAAA,yCDmLE,OAAA,KC9KF,cDuLE,mBAAA,UACA,eAAA,KCnLF,4CAAA,yCD4LE,mBAAA,KAQF,6BACE,mBAAA,OACA,KAAA,QAWF,QAAA,KAEE,QAAA,MAOF,QACE,QAAA,UAUF,OACE,QAAA,aAOF,SACE,QAAA,KCnNF,SD8NE,QAAA,KEtbF,KACE,mBAAA,WAAA,WAAA,WAGF,EAAA,QAAA,SAGE,mBAAA,QAAA,WAAA,QAoBA,cAAgB,MAAA,aAQlB,KAYE,mBAAA,UAGA,4BAAA,YAGF,KACE,YAAA,cAAA,UAAA,mBAAA,WAAA,OC2K4H,iBD3K5H,MAAA,WACA,UAAA,KACA,YAAA,IACA,YAAA,IAEA,MAAA,QAEA,iBAAA,KD2LF,sBClLE,QAAA,YAYF,GAAI,GAAI,GAAI,GAAI,GAAI,GAClB,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KAIF,0BAAA,YAGE,OAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAGF,GAAA,GAAA,GAGE,WAAA,EACA,cAAA,KAGF,MAAA,MAAA,MAAA,MAIE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAQF,EACE,MAAA,QACA,gBAAA,KEhJE,QAAA,QFmJA,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KEhKE,oCAAA,oCFmKA,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EASJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAGE,OAAA,EAAA,EAAA,KAQF,IAGE,eAAA,ODsIF,cCzHE,OAAA,QAcF,cAAA,EAAA,KAAA,OAAA,MAAA,MAAA,OAAA,QAAA,SASE,iBAAA,aAAA,aAAA,aAQF,MAEE,gBAAA,SAEA,iBAAA,YAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAEE,WAAA,KAQF,MAEE,QAAA,aACA,cAAA,MAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBAGF,OAAA,MAAA,OAAA,SAME,YAAA,QAGF,8BAAA,2BAMI,OAAA,YAKJ,iBAAA,iBAAA,2BAAA,kBASE,mBAAA,QAGF,SAEE,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAGF,OAEE,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QAGF,mBAKE,mBAAA,KAIF,OACE,QAAA,aDsEF,SC9DE,QAAA"} -------------------------------------------------------------------------------- /staticfiles/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/_normalize.scss","bootstrap-reboot.css","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/mixins/_hover.scss"],"names":[],"mappings":"4EAYA,KACE,YAAA,WACA,YAAA,KACA,qBAAA,KACA,yBAAA,KAUF,KACE,OAAA,EAOF,QAAA,MAAA,OAAA,OAAA,IAAA,QAME,QAAA,MAQF,GACE,UAAA,IACA,OAAA,MAAA,EAWF,WAAA,OAAA,KAGE,QAAA,MAOF,OACE,OAAA,IAAA,KAQF,GACE,mBAAA,YAAA,WAAA,YACA,OAAA,EACA,SAAA,QAQF,IACE,YAAA,UAAA,UACA,UAAA,IAWF,EACE,iBAAA,YACA,6BAAA,QAQF,SAAA,QAEE,cAAA,EAQF,YACE,cAAA,KACA,gBAAA,UACA,gBAAA,UAAA,OAOF,EAAA,OAEE,YAAA,QAOF,EAAA,OAEE,YAAA,OAQF,KAAA,IAAA,KAGE,YAAA,UAAA,UACA,UAAA,IAOF,IACE,WAAA,OAOF,KACE,iBAAA,KACA,MAAA,KAOF,MACE,UAAA,IAQF,IAAA,IAEE,UAAA,IACA,YAAA,EACA,SAAA,SACA,eAAA,SAGF,IACE,OAAA,OAGF,IACE,IAAA,MAUF,MAAA,MAEE,QAAA,aAOF,sBACE,QAAA,KACA,OAAA,EAOF,IACE,aAAA,KAOF,eACE,SAAA,OAWF,OAAA,MAAA,SAAA,OAAA,SAKE,YAAA,WACA,UAAA,KACA,YAAA,KACA,OAAA,EAQF,OAAA,MAEE,SAAA,QAQF,OAAA,OAEE,eAAA,KASF,aAAA,cAAA,OAAA,mBAIE,mBAAA,OAOF,gCAAA,+BAAA,gCAAA,yBAIE,aAAA,KACA,QAAA,EAOF,6BAAA,4BAAA,6BAAA,sBAIE,QAAA,IAAA,OAAA,WAOF,SACE,OAAA,IAAA,MAAA,OACA,OAAA,EAAA,IACA,QAAA,MAAA,OAAA,MAUF,OACE,mBAAA,WAAA,WAAA,WACA,MAAA,QACA,QAAA,MACA,UAAA,KACA,QAAA,EACA,YAAA,OAQF,SACE,QAAA,aACA,eAAA,SAOF,SACE,SAAA,KCrKF,gBAAA,aD+KE,mBAAA,WAAA,WAAA,WACA,QAAA,EC1KF,yCAAA,yCDmLE,OAAA,KC9KF,cDuLE,mBAAA,UACA,eAAA,KCnLF,4CAAA,yCD4LE,mBAAA,KAQF,6BACE,mBAAA,OACA,KAAA,QAWF,QAAA,KAEE,QAAA,MAOF,QACE,QAAA,UAUF,OACE,QAAA,aAOF,SACE,QAAA,KCnNF,SD8NE,QAAA,KEtbF,KACE,mBAAA,WAAA,WAAA,WAGF,EAAA,QAAA,SAGE,mBAAA,QAAA,WAAA,QAoBA,cAAgB,MAAA,aAQlB,KAYE,mBAAA,UAGA,4BAAA,YAGF,KACE,YAAA,cAAA,UAAA,mBAAA,WAAA,OC2K4H,iBD3K5H,MAAA,WACA,UAAA,KACA,YAAA,IACA,YAAA,IAEA,MAAA,QAEA,iBAAA,KD2LF,sBClLE,QAAA,YAYF,GAAI,GAAI,GAAI,GAAI,GAAI,GAClB,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KAIF,0BAAA,YAGE,OAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAGF,GAAA,GAAA,GAGE,WAAA,EACA,cAAA,KAGF,MAAA,MAAA,MAAA,MAIE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAQF,EACE,MAAA,QACA,gBAAA,KEhJE,QAAA,QFmJA,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KEhKE,oCAAA,oCFmKA,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EASJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAGE,OAAA,EAAA,EAAA,KAQF,IAGE,eAAA,ODsIF,cCzHE,OAAA,QAcF,cAAA,EAAA,KAAA,OAAA,MAAA,MAAA,OAAA,QAAA,SASE,iBAAA,aAAA,aAAA,aAQF,MAEE,gBAAA,SAEA,iBAAA,YAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAEE,WAAA,KAQF,MAEE,QAAA,aACA,cAAA,MAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBAGF,OAAA,MAAA,OAAA,SAME,YAAA,QAGF,8BAAA,2BAMI,OAAA,YAKJ,iBAAA,iBAAA,2BAAA,kBASE,mBAAA,QAGF,SAEE,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAGF,OAEE,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QAGF,mBAKE,mBAAA,KAIF,OACE,QAAA,aDsEF,SC9DE,QAAA"} -------------------------------------------------------------------------------- /wikitowns/urls.py: -------------------------------------------------------------------------------- 1 | """wikitowns URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | from django.conf import settings 19 | from django.conf.urls.static import static 20 | from registration.backends.simple.views import RegistrationView 21 | from website.forms import (MyCustomRegistrationForm, CustomPasswordResetForm, 22 | CustomPasswordChangeForm, CustomSetPasswordForm) 23 | from django.contrib.auth import views as auth_views 24 | from django.core.urlresolvers import reverse_lazy 25 | 26 | 27 | # redirects to home page after registration 28 | class MyRegistrationView(RegistrationView): 29 | def get_success_url(self, user): 30 | return '/' 31 | 32 | urlpatterns = [ 33 | url(r'', include('website.urls')), 34 | url(r'^accounts/register/$', 35 | MyRegistrationView.as_view(form_class=MyCustomRegistrationForm), 36 | name='registration_register'), # redirects to index after registration 37 | url(r'^accounts/password/reset/$', auth_views.password_reset, 38 | {'post_reset_redirect': reverse_lazy('auth_password_reset_done'), 39 | 'password_reset_form': CustomPasswordResetForm}, 40 | name='auth_password_reset'), 41 | url(r'^accounts/password/reset/confirm/(?P[0-9A-Za-z_\-]+)/' 42 | r'(?P.+)/$', 43 | auth_views.password_reset_confirm, 44 | {'post_reset_redirect': reverse_lazy('auth_password_reset_complete'), 45 | 'set_password_form': CustomSetPasswordForm}, 46 | name='auth_password_reset_confirm'), 47 | url(r'^accounts/password/change/$', auth_views.password_change, 48 | {'post_change_redirect': reverse_lazy('auth_password_change_done'), 49 | 'password_change_form': CustomPasswordChangeForm}, 50 | name='auth_password_change'), 51 | url(r'^accounts/', include('registration.backends.simple.urls')), 52 | url(r'^admin/', admin.site.urls), 53 | ] # + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 54 | 55 | if settings.DEBUG is True: 56 | urlpatterns += static(settings.MEDIA_URL, 57 | document_root=settings.MEDIA_ROOT) 58 | -------------------------------------------------------------------------------- /staticfiles/admin/js/actions.min.js: -------------------------------------------------------------------------------- 1 | (function(a){var f;a.fn.actions=function(e){var b=a.extend({},a.fn.actions.defaults,e),g=a(this),k=!1,l=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},n=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},p=function(){n(); 2 | a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)},q=function(c){c?l():n();a(g).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){var c=a(g).filter(":checked").length,d=a(".action-counter").data("actionsIcnt");a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:d},!0));a(b.allToggle).prop("checked",function(){var a;c===g.length?(a=!0,l()):(a=!1,p());return a})};a(b.counterContainer).show(); 3 | a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass);h();1===a(b.acrossInput).val()&&m()});a(b.allToggle).show().click(function(){q(a(this).prop("checked"));h()});a("a",b.acrossQuestions).click(function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("a",b.acrossClears).click(function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);p();q(0);h()});f=null;a(g).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(f&& 4 | a.data(f)!==a.data(d)&&!0===c.shiftKey){var e=!1;a(f).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(g).each(function(){if(a.data(this)===a.data(f)||a.data(this)===a.data(d))e=e?!1:!0;e&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);f=d;h()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){k=!0});a('form#changelist-form button[name="index"]').click(function(a){if(k)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))}); 5 | a('form#changelist-form input[name="_save"]').click(function(c){var d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return k?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})}; 6 | a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"};a(document).ready(function(){var e=a("tr input.action-select");0 2 | Noobhub logo 3 | 4 | 5 | # noobhub 6 | 7 | www.noobhub.io is a website to allow people to find, share, rank and bookmark coding resources. 8 | 9 | ## Why? 10 | 11 | Whilst learning to code I often found myself spending quite a lot of time trying to find the best resources to learn from. 12 | noobhub aims to help solve this by allowing people to share resources they found useful in order to make it easy for others to find. 13 | The ranking system ensures users see the best content first and the bookmarks let them quickly find their favourite content again. 14 | 15 | ## Features 16 | 17 | * Websites, books and videos can be recommended by registered users 18 | * Content can be ranked up or down to highlight the best resources. 19 | * Bookmarks allow users to easily find their favourite content. 20 | * To recommend a book you only need to enter the ISBN and the Amazon API is used to fetch the books details. 21 | * To recommend a video you only need to enter a youtube URL and the YouTube API fetches the videos details. 22 | * Users have profile pages which details the content they have bookmarked and recommended. 23 | * A simple search feature allows users to narrow down their search within categories. 24 | * Users can filter recommendations by all time best, best of year, best of month or by newest. 25 | * Users can leave comments on recommended resources. 26 | * Inappropriate recommendations can be reported by users. 27 | 28 | 29 | ## Built with 30 | 31 | * [Django](https://www.djangoproject.com/) - Back end framework 32 | * [Bootstrap](https://getbootstrap.com/) - Front end framework 33 | * [Heroku](https://www.heroku.com/home) - Platform as a Service 34 | * [Amazon S3](https://aws.amazon.com/s3) - Storage and serving of Media files 35 | * [PostgreSQL](https://www.postgresql.org/) - Database 36 | * [Bottlenose](https://github.com/lionheart/bottlenose) - Python wrapper over the Amazon Product Advertising API 37 | * [Django isbn field](https://github.com/secnot/django-isbn-field) - Provides django model field to store and validate ISBN numbers. 38 | * [Django registration redux](https://django-registration-redux.readthedocs.io/en/latest/) - User registration 39 | * [Django el pagination](https://github.com/shtalinberg/django-el-pagination) - Endless pagination 40 | * [Beautiful soup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) - Pull data out of XML files 41 | * [Boto 3](https://boto3.readthedocs.io/en/latest/) - Amazon S3 integration 42 | 43 | ## API Reference 44 | 45 | * [Amazon Product Advertising API](https://docs.aws.amazon.com/AWSECommerceService/latest/DG/Welcome.html) - To fetch book data 46 | * [Google API Python Client](https://developers.google.com/api-client-library/python/) - To fetch youtube data 47 | 48 | ## Author 49 | 50 | Oliver Small - You can reach me at oliver@rotherfields.co.uk 51 | 52 | ## Acknowledgments 53 | 54 | * [Robert Wheal](http://robertwheal.co.uk/) - Logo design and icon editing 55 | * [Flaticon](https://www.flaticon.com) - Website icons 56 | * [Font Awesome](https://fontawesome.com/) - Website icons 57 | 58 | ## License 59 | 60 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. 61 | -------------------------------------------------------------------------------- /staticfiles/admin/img/selector-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /staticfiles/admin/js/timeparse.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | var timeParsePatterns = [ 4 | // 9 5 | { 6 | re: /^\d{1,2}$/i, 7 | handler: function(bits) { 8 | if (bits[0].length === 1) { 9 | return '0' + bits[0] + ':00'; 10 | } else { 11 | return bits[0] + ':00'; 12 | } 13 | } 14 | }, 15 | // 13:00 16 | { 17 | re: /^\d{2}[:.]\d{2}$/i, 18 | handler: function(bits) { 19 | return bits[0].replace('.', ':'); 20 | } 21 | }, 22 | // 9:00 23 | { 24 | re: /^\d[:.]\d{2}$/i, 25 | handler: function(bits) { 26 | return '0' + bits[0].replace('.', ':'); 27 | } 28 | }, 29 | // 3 am / 3 a.m. / 3am 30 | { 31 | re: /^(\d+)\s*([ap])(?:.?m.?)?$/i, 32 | handler: function(bits) { 33 | var hour = parseInt(bits[1]); 34 | if (hour === 12) { 35 | hour = 0; 36 | } 37 | if (bits[2].toLowerCase() === 'p') { 38 | if (hour === 12) { 39 | hour = 0; 40 | } 41 | return (hour + 12) + ':00'; 42 | } else { 43 | if (hour < 10) { 44 | return '0' + hour + ':00'; 45 | } else { 46 | return hour + ':00'; 47 | } 48 | } 49 | } 50 | }, 51 | // 3.30 am / 3:15 a.m. / 3.00am 52 | { 53 | re: /^(\d+)[.:](\d{2})\s*([ap]).?m.?$/i, 54 | handler: function(bits) { 55 | var hour = parseInt(bits[1]); 56 | var mins = parseInt(bits[2]); 57 | if (mins < 10) { 58 | mins = '0' + mins; 59 | } 60 | if (hour === 12) { 61 | hour = 0; 62 | } 63 | if (bits[3].toLowerCase() === 'p') { 64 | if (hour === 12) { 65 | hour = 0; 66 | } 67 | return (hour + 12) + ':' + mins; 68 | } else { 69 | if (hour < 10) { 70 | return '0' + hour + ':' + mins; 71 | } else { 72 | return hour + ':' + mins; 73 | } 74 | } 75 | } 76 | }, 77 | // noon 78 | { 79 | re: /^no/i, 80 | handler: function(bits) { 81 | return '12:00'; 82 | } 83 | }, 84 | // midnight 85 | { 86 | re: /^mid/i, 87 | handler: function(bits) { 88 | return '00:00'; 89 | } 90 | } 91 | ]; 92 | 93 | function parseTimeString(s) { 94 | for (var i = 0; i < timeParsePatterns.length; i++) { 95 | var re = timeParsePatterns[i].re; 96 | var handler = timeParsePatterns[i].handler; 97 | var bits = re.exec(s); 98 | if (bits) { 99 | return handler(bits); 100 | } 101 | } 102 | return s; 103 | } 104 | 105 | window.parseTimeString = parseTimeString; 106 | })(); 107 | -------------------------------------------------------------------------------- /website/templates/website/subcategory_website_page.html: -------------------------------------------------------------------------------- 1 | {% load el_pagination_tags %} 2 | {% load noobhub_filters %} 3 | 4 | {% paginate websites %} 5 | 6 | {% for website in websites %} 7 | 8 | 84 | 85 | {% endfor %} 86 | {% show_more %} 87 | -------------------------------------------------------------------------------- /website/templates/website/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | noobhub 6 | {% endblock %} 7 | 8 | {% block body_block %} 9 | 10 |
11 |
12 |
13 | 14 |
15 |
16 |
A place to share and discover coding resources
17 |
18 |
19 |
20 | desk drawing 21 |
22 |
23 | 24 | {% if user.is_authenticated %} 25 | 26 | {% else %} 27 | 28 |
29 |
30 |
31 | web.img 32 |

Learn

33 |
Learn to code with websites, books and videos recommended by our community.
34 |
35 |
36 | web.img 37 |

Share

38 |
Share and rank content to help other users to find the best resources.
39 |
40 |
41 | web.img 42 |

Bookmark

43 |
Save your favourite websites, books and videos or view other users' favourites.
44 |
45 |
46 |
47 | 48 | 64 | 65 | {% endif %} 66 | 67 |
68 |
69 |
70 |
71 |

Get started

72 |

73 |
Select the category you would like to find or share content about.
74 |
75 |
76 | 77 |
78 |
79 | 80 | {% if categories %} 81 | {% for category in categories %} 82 | 96 | {% endfor %} 97 | {% else %} 98 | There are no categories listed. 99 | {% endif %} 100 | 101 |
102 |
103 |
104 |
105 | 106 | {% endblock %} 107 | -------------------------------------------------------------------------------- /website/templates/website/subcategory_book_page.html: -------------------------------------------------------------------------------- 1 | {% load el_pagination_tags %} 2 | {% load noobhub_filters %} 3 | 4 | {% paginate books using 'other_entries_page' %} 5 | 6 | {% for book in books %} 7 | 93 | 94 | {% endfor %} 95 | {% show_more %} 96 | -------------------------------------------------------------------------------- /static/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#292b2c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#636c72;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}/*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /staticfiles/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#292b2c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#636c72;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}/*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /website/templates/website/subcategory_video_page.html: -------------------------------------------------------------------------------- 1 | {% load el_pagination_tags %} 2 | {% load noobhub_filters %} 3 | 4 | {% paginate videos using 'other_entries_page2' %} 5 | 6 | {% for video in videos %} 7 | 8 | 96 | 97 | {% endfor %} 98 | {% show_more %} 99 | -------------------------------------------------------------------------------- /website/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | from django.contrib.auth.decorators import login_required 4 | 5 | urlpatterns = [ 6 | url(r'^$', views.index, name='index'), 7 | url(r'^user/(?P[\w.@+-]+)/$', views.profile_page, name='user_profile'), 8 | url(r'^category/(?P[\w\-]+)/$', views.category, 9 | name='category'), 10 | url(r'^category/(?P[\w\-]+)/' 11 | r'(?P[\w\-]+)/$', views.subcategory, 12 | name='subcategory'), 13 | url(r'^category/(?P[\w\-]+)/' 14 | r'(?P[\w\-]+)/new_website/$', 15 | login_required(views.CreateWebsiteRecommendation.as_view()), 16 | name='create_website'), 17 | url(r'^delete_website/(?P\d+)/$', 18 | login_required(views.DeleteWebsiteRecommendation.as_view()), 19 | name='delete_website'), 20 | url(r'^upvote_website/$', views.upvote_website, name='upvote_website'), 21 | url(r'^downvote_website/$', views.downvote_website, 22 | name='downvote_website'), 23 | url(r'^bookmark_website/$', views.bookmark_website, 24 | name='bookmark_website'), 25 | url(r'^category/(?P[\w\-]+)/' 26 | r'(?P[\w\-]+)/(?P\d+)/comments/$', 27 | views.website_comment, name='website_comment'), 28 | url(r'^delete_website_comment/(?P\d+)/$', 29 | login_required(views.DeleteWebsiteComment.as_view()), 30 | name='delete_website_comment'), 31 | url(r'^edit_website_comment/(?P\d+)/$', 32 | login_required(views.EditWebsiteComment.as_view()), 33 | name='edit_website_comment'), 34 | url(r'^category/(?P[\w\-]+)/' 35 | r'(?P[\w\-]+)/new_book/$', 36 | views.create_book_recommendation, name='create_book'), 37 | url(r'^delete_book/(?P\d+)/$', 38 | login_required(views.DeleteBookRecommendation.as_view()), 39 | name='delete_book'), 40 | url(r'^upvote_book/$', views.upvote_book, name='upvote_book'), 41 | url(r'^downvote_book/$', views.downvote_book, name='downvote_book'), 42 | url(r'^bookmark_book/$', views.bookmark_book, name='bookmark_book'), 43 | url(r'^category/(?P[\w\-]+)/' 44 | r'(?P[\w\-]+)/(?P\d+)/book_comments/$', 45 | views.book_comment, name='book_comment'), 46 | url(r'^delete_book_comment/(?P\d+)/$', 47 | login_required(views.DeleteBookComment.as_view()), 48 | name='delete_book_comment'), 49 | url(r'^edit_book_comment/(?P\d+)/$', 50 | login_required(views.EditBookComment.as_view()), 51 | name='edit_book_comment'), 52 | url(r'^category/(?P[\w\-]+)/' 53 | r'(?P[\w\-]+)/new_video/$', 54 | views.create_video_recommendation, name='create_video'), 55 | url(r'^delete_video/(?P\d+)/$', 56 | login_required(views.DeleteVideoRecommendation.as_view()), 57 | name='delete_video'), 58 | url(r'^upvote_video/$', views.upvote_video, name='upvote_video'), 59 | url(r'^downvote_video/$', views.downvote_video, name='downvote_video'), 60 | url(r'^bookmark_video/$', views.bookmark_video, name='bookmark_video'), 61 | url(r'^category/(?P[\w\-]+)/' 62 | r'(?P[\w\-]+)/(?P\d+)/video_comments/$', 63 | views.video_comment, name='video_comment'), 64 | url(r'^delete_video_comment/(?P\d+)/$', 65 | login_required(views.DeleteVideoComment.as_view()), 66 | name='delete_video_comment'), 67 | url(r'^edit_video_comment/(?P\d+)/$', 68 | login_required(views.EditVideoComment.as_view()), 69 | name='edit_video_comment'), 70 | url(r'^category/(?P[\w\-]+)/' 71 | r'(?P[\w\-]+)/(?P\d+)/' 72 | r'report_website_recommendation/$', 73 | views.report_website_recommendation, 74 | name='report_website_recommendation'), 75 | url(r'^category/(?P[\w\-]+)/' 76 | r'(?P[\w\-]+)/(?P\d+)/' 77 | r'report_book_recommendation/$', views.report_book_recommendation, 78 | name='report_book_recommendation'), 79 | url(r'^category/(?P[\w\-]+)/' 80 | r'(?P[\w\-]+)/(?P\d+)/' 81 | r'report_video_recommendation/$', views.report_video_recommendation, 82 | name='report_video_recommendation'), 83 | url(r'^icons/$', views.icons, name='icons'), 84 | ] 85 | -------------------------------------------------------------------------------- /staticfiles/admin/js/inlines.min.js: -------------------------------------------------------------------------------- 1 | (function(c){c.fn.formset=function(b){var a=c.extend({},c.fn.formset.defaults,b),d=c(this);b=d.parent();var k=function(a,g,l){var b=new RegExp("("+g+"-(\\d+|__prefix__))");g=g+"-"+l;c(a).prop("for")&&c(a).prop("for",c(a).prop("for").replace(b,g));a.id&&(a.id=a.id.replace(b,g));a.name&&(a.name=a.name.replace(b,g))},e=c("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),l=parseInt(e.val(),10),g=c("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),h=""===g.val()||0'+a.addText+""),m=b.find("tr:last a")):(d.filter(":last").after('"),m=d.filter(":last").next().find("a"));m.click(function(b){b.preventDefault();b=c("#"+a.prefix+"-empty");var f=b.clone(!0);f.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id", 3 | a.prefix+"-"+l);f.is("tr")?f.children(":last").append('"):f.is("ul")||f.is("ol")?f.append('
  • '+a.deleteText+"
  • "):f.children(":first").append(''+a.deleteText+"");f.find("*").each(function(){k(this,a.prefix,e.val())});f.insertBefore(c(b));c(e).val(parseInt(e.val(),10)+1);l+=1;""!==g.val()&&0>=g.val()-e.val()&&m.parent().hide(); 4 | f.find("a."+a.deleteCssClass).click(function(b){b.preventDefault();f.remove();--l;a.removed&&a.removed(f);c(document).trigger("formset:removed",[f,a.prefix]);b=c("."+a.formCssClass);c("#id_"+a.prefix+"-TOTAL_FORMS").val(b.length);(""===g.val()||0 10 |

    Icon attributions


    11 |
    12 |
    Icons made by Flat Icons from www.flaticon.com is licensed by CC 3.0 BY
    13 |

    14 |
      15 |
    • Globe
    • 16 |
    17 |
    Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
    18 |

    19 |
      20 |
    • 404 icon
    • 21 |
    • Computer mouse
    • 22 |
    • Computer keyboard
    • 23 |
    • Bin
    • 24 |
    • Cloud
    • 25 |
    • Mobile Phone
    • 26 |
    • Magnifying glass
    • 27 |
    • Desk lamp
    • 28 |
    • Apple
    • 29 |
    • Glasses
    • 30 |
    • Pile of books
    • 31 |
    • Plant
    • 32 |
    • Start banner
    • 33 |
    • avatars
    • 34 |
    35 |
    Icons made by Dimitry Miroliubov from www.flaticon.com is licensed by CC 3.0 BY
    36 |

    37 |
      38 |
    • Rocket
    • 39 |
    40 |
    Icons made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY
    41 |

    42 |
      43 |
    • Coffee
    • 44 |
    • paper with bookmark
    • 45 |
    • Book with bookmark
    • 46 |
    • Pencil
    • 47 |
    • Book with strap
    • 48 |
    • Floppy disk
    • 49 |
    • USB cable
    • 50 |
    • Pen
    • 51 |
    • Video player
    • 52 |
    • File
    • 53 |
    • Server
    • 54 |
    • Laptop
    • 55 |
    56 |
    Icons made by Pixel Buddha from www.flaticon.com is licensed by CC 3.0 BY
    57 |

    58 |
      59 |
    • Clip Board
    • 60 |
    61 |
    Icons made by Nikita Golubev from www.flaticon.com is licensed by CC 3.0 BY
    62 |

    63 |
      64 |
    • Computer
    • 65 |
    66 |
    Icons made by Dimi Kazak from www.flaticon.com is licensed by CC 3.0 BY
    67 |

    68 |
      69 |
    • Lightbulb
    • 70 |
    71 |
    Icons made by Roundicons from www.flaticon.com is licensed by CC 3.0 BY
    72 |

    73 |
      74 |
    • Money bag
    • 75 |
    76 |
    Icons made by Darius Dan from www.flaticon.com is licensed by CC 3.0 BY
    77 |

    78 |
      79 |
    • AI Brain
    • 80 |
    81 |
    Icons made by DinosoftLabs from www.flaticon.com is licensed by CC 3.0 BY
    82 |

    83 |
      84 |
    • Pencil pot
    • 85 |
    86 |
    87 | 88 | {% endblock %} 89 | -------------------------------------------------------------------------------- /website/templates/website/website_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | {% load noobhub_filters %} 4 | 5 | {% block title %} 6 | noobhub - comments 7 | {% endblock %} 8 | 9 | {% block body_block %} 10 | 11 |
    12 | 87 | 88 |
    89 |
    90 | {% if user.is_authenticated %} 91 |
    92 |
    93 | {% csrf_token %} 94 | {{ form.as_p }} 95 | 96 |
    97 |
    98 | {% endif %} 99 | 100 | Comments 101 | 102 | {% if comments %} 103 |
    104 | {% include "website/website_comment_page.html" %} 105 |
    106 | {% else %} 107 |
    108 | No comments have been made yet. 109 | {% endif %} 110 | 111 |
    112 |
    113 |
    114 | 115 | {% endblock %} 116 | -------------------------------------------------------------------------------- /wikitowns/settings/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for wikitowns project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.10.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.10/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | BASE_DIR = os.path.dirname(os.path.dirname( 18 | os.path.dirname( 19 | os.path.abspath(__file__)))) 20 | 21 | # PROJECT_ROOT = os.path.dirname(BASE_DIR) 22 | 23 | # Quick-start development settings - unsuitable for production 24 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ 25 | 26 | # SECURITY WARNING: keep the secret key used in production secret! 27 | SECRET_KEY = os.environ['SECRET_KEY'] 28 | 29 | # SECURITY WARNING: don't run with debug turned on in production! 30 | DEBUG = False 31 | 32 | ALLOWED_HOSTS = ['127.0.0.1'] 33 | 34 | # Application definition 35 | 36 | INSTALLED_APPS = [ 37 | 'website', 38 | 'django.contrib.admin', 39 | 'django.contrib.auth', 40 | 'django.contrib.contenttypes', 41 | 'django.contrib.sessions', 42 | 'django.contrib.messages', 43 | 'django.contrib.staticfiles', 44 | 'storages', 45 | 'registration', 46 | 'isbn_field', 47 | 'el_pagination', 48 | 'django.contrib.postgres', 49 | ] 50 | 51 | MIDDLEWARE = [ 52 | 'django.middleware.security.SecurityMiddleware', 53 | 'whitenoise.middleware.WhiteNoiseMiddleware', 54 | 'django.contrib.sessions.middleware.SessionMiddleware', 55 | 'django.middleware.common.CommonMiddleware', 56 | 'django.middleware.csrf.CsrfViewMiddleware', 57 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 58 | 'django.contrib.messages.middleware.MessageMiddleware', 59 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 60 | ] 61 | 62 | ROOT_URLCONF = 'wikitowns.urls' 63 | 64 | TEMPLATES = [ 65 | { 66 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 67 | 'DIRS': [], 68 | 'APP_DIRS': True, 69 | 'OPTIONS': { 70 | 'context_processors': [ 71 | 'django.template.context_processors.debug', 72 | 'django.template.context_processors.request', 73 | 'django.contrib.auth.context_processors.auth', 74 | 'django.contrib.messages.context_processors.messages', 75 | 'website.context_processor.category_context', 76 | ], 77 | }, 78 | }, 79 | ] 80 | 81 | WSGI_APPLICATION = 'wikitowns.wsgi.application' 82 | 83 | # Database 84 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': os.environ['DATABASE_ENGINE'], 89 | 'NAME': os.environ['DATABASE_NAME'], 90 | 'USER': os.environ['DATABASE_USER'], 91 | 'PASSWORD': os.environ['DATABASE_PASSWORD'], 92 | 'HOST': os.environ['DATABASE_HOST'], 93 | 'PORT': os.environ['DATABASE_PORT'], 94 | } 95 | } 96 | 97 | # Password validation 98 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators 99 | 100 | AUTH_PASSWORD_VALIDATORS = [ 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 109 | }, 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 112 | }, 113 | ] 114 | 115 | 116 | # Internationalization 117 | # https://docs.djangoproject.com/en/1.10/topics/i18n/ 118 | 119 | LANGUAGE_CODE = 'en-us' 120 | 121 | TIME_ZONE = 'UTC' 122 | 123 | USE_I18N = True 124 | 125 | USE_L10N = True 126 | 127 | USE_TZ = True 128 | 129 | 130 | # PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 131 | 132 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 133 | STATIC_URL = '/static/' 134 | 135 | # Extra places for collectstatic to find static files. 136 | STATICFILES_DIRS = ( 137 | os.path.join(BASE_DIR, 'static'), 138 | ) 139 | 140 | MEDIA_URL = '/media/' 141 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 142 | 143 | # registration-redux settings 144 | REGISTRATION_OPEN = True # If True, users can register 145 | ACCOUNT_ACTIVATION_DAYS = 7 # One-week activation window; you may, of course, use a different value. 146 | REGISTRATION_AUTO_LOGIN = True # If True, the user will be automatically logged in. 147 | LOGIN_REDIRECT_URL = '/' # The page you want users to arrive at after they successful log in 148 | LOGIN_URL = '/accounts/login/' # The page users are directed to if they are not logged in, 149 | # and are trying to access pages requiring authentication 150 | 151 | # email settings used to report bad user posts 152 | EMAIL_HOST = 'smtp.gmail.com' 153 | EMAIL_HOST_USER = os.environ['EMAIL_HOST_USER'] 154 | EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD'] 155 | EMAIL_PORT = 587 156 | EMAIL_USE_TLS = True 157 | DEFAULT_FROM_EMAIL = os.environ['EMAIL_HOST_USER'] 158 | -------------------------------------------------------------------------------- /website/templates/website/book_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | {% load noobhub_filters %} 4 | 5 | {% block title %} 6 | noobhub - comments 7 | {% endblock %} 8 | 9 | {% block body_block %} 10 | 11 |
    12 | 97 | 98 |
    99 |
    100 | {% if user.is_authenticated %} 101 |
    102 |
    103 | {% csrf_token %} 104 | {{ form.as_p }} 105 | 106 |
    107 |
    108 | {% endif %} 109 | 110 | Comments 111 | 112 | {% if comments %} 113 |
    114 | {% include "website/book_comment_page.html" %} 115 |
    116 | {% else %} 117 |
    118 | No comments have been made yet. 119 | {% endif %} 120 |
    121 |
    122 |
    123 | 124 | {% endblock %} 125 | -------------------------------------------------------------------------------- /website/templates/website/video_comment.html: -------------------------------------------------------------------------------- 1 | {% extends 'website/base.html' %} 2 | {% load static %} 3 | 4 | {% load noobhub_filters %} 5 | 6 | {% block title %} 7 | noobhub - comments 8 | {% endblock %} 9 | 10 | {% block body_block %} 11 | 12 |
    13 | 100 | 101 |
    102 |
    103 | {% if user.is_authenticated %} 104 |
    105 |
    106 | {% csrf_token %} 107 | {{ form.as_p }} 108 | 109 |
    110 |
    111 | {% endif %} 112 | 113 | Comments 114 | 115 | {% if comments %} 116 |
    117 | {% include "website/video_comment_page.html" %} 118 |
    119 | {% else %} 120 |
    121 | No comments have been made yet. 122 | {% endif %} 123 | 124 |
    125 |
    126 |
    127 | 128 | {% endblock %} 129 | --------------------------------------------------------------------------------