├── django ├── people │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0021_remove_person_news_opt_in.py │ │ ├── 0009_auto_20180413_0856.py │ │ ├── 0013_person_news_opt_in.py │ │ ├── 0011_person_public_email.py │ │ ├── 0014_auto_20180613_1527.py │ │ ├── 0015_person_show_hireable.py │ │ ├── 0016_auto_20180614_0946.py │ │ ├── 0005_person_github_updated.py │ │ ├── 0023_person_settings.py │ │ ├── 0017_auto_20180622_1009.py │ │ ├── 0003_auto_20180412_1354.py │ │ ├── 0010_auto_20180413_0905.py │ │ ├── 0012_auto_20180613_1212.py │ │ ├── 0018_show_hireable_update.py │ │ ├── 0006_person_user.py │ │ ├── 0020_opt_in_splicing.py │ │ ├── 0007_auto_20180412_1543.py │ │ ├── 0019_auto_20180622_1006.py │ │ ├── 0008_auto_20180412_1544.py │ │ ├── 0022_auto_20180628_1515.py │ │ ├── 0004_auto_20180412_1526.py │ │ └── 0001_initial.py │ ├── tests.py │ ├── apps.py │ ├── permission.py │ ├── consumers.py │ ├── adapter.py │ └── admin.py ├── .gitignore ├── .env.template ├── peoplebackend │ ├── __init__.py │ ├── load_env.py │ ├── routing.py │ ├── celery.py │ ├── wsgi.py │ └── urls.py ├── Dockerfile ├── asgi.py ├── requirements.txt └── manage.py ├── frontend ├── cypress.json ├── .babelrc ├── plugins │ ├── vue-django-feedback.js │ ├── vee-validate.js │ ├── vuex-geolocation.js │ ├── store-tokens.js │ ├── vuetify.js │ ├── web-sockets.js │ ├── axios.js │ └── vue-leaflet.js ├── static │ ├── ding.mp3 │ ├── favicon.png │ ├── logo-vertical.png │ ├── logo-horizontal.png │ ├── github-app-icon_only.png │ ├── logo-pulilab-black.svg │ └── icon-vueman.svg ├── assets │ ├── style │ │ ├── vuetify.styl │ │ └── mixins.less │ └── images │ │ ├── meetup-nocover.png │ │ ├── sample-meetup-cover.jpg │ │ ├── sample-meetup-logo.png │ │ └── logo-pulilab.svg ├── .dockerignore ├── pages │ ├── index │ │ ├── index.vue │ │ ├── meetup │ │ │ └── _id.vue │ │ └── user │ │ │ ├── index.vue │ │ │ └── _id.vue │ └── index.vue ├── middleware │ ├── entrypoint-spy.js │ └── auth.js ├── tests │ ├── utils.js │ ├── __mocks__ │ │ └── django-channels.js │ └── store │ │ └── layout.test.js ├── utilities │ ├── media.js │ ├── coords.js │ ├── parsers.js │ └── auth.js ├── .env.template ├── .gitignore ├── .editorconfig ├── Dockerfile ├── jest.config.js ├── .eslintrc.js ├── components │ ├── base │ │ └── TextOrPlaceholder.vue │ ├── RightSide.vue │ ├── MapToolbar.vue │ ├── BottomButtons.vue │ ├── CustomMarkerCluster.vue │ ├── TooltipGroup.vue │ ├── AppDialogs.vue │ ├── CookieWarning.vue │ └── MapMarker.vue ├── cypress │ ├── plugins │ │ └── index.js │ └── support │ │ ├── index.js │ │ └── commands.js ├── store │ ├── layout.js │ ├── index.js │ ├── events.js │ └── map.js ├── layouts │ └── default.vue ├── integrations │ ├── github │ │ ├── utilities.js │ │ └── queries.js │ └── meetup │ │ └── utilities.js └── package.json ├── nginx ├── certs │ ├── .gitignore │ ├── cert.pem │ ├── chain.pem │ └── key.pem ├── site │ ├── media │ │ └── .gitignore │ └── static │ │ ├── rest_framework │ │ ├── img │ │ │ ├── grid.png │ │ │ ├── glyphicons-halflings.png │ │ │ └── glyphicons-halflings-white.png │ │ ├── docs │ │ │ ├── img │ │ │ │ ├── grid.png │ │ │ │ └── favicon.ico │ │ │ └── css │ │ │ │ ├── jquery.json-view.min.css │ │ │ │ └── highlight.css │ │ ├── fonts │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── css │ │ │ ├── prettify.css │ │ │ └── default.css │ │ └── js │ │ │ ├── default.js │ │ │ └── csrf.js │ │ ├── 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 │ │ │ ├── vendor │ │ │ │ ├── select2 │ │ │ │ │ ├── i18n │ │ │ │ │ │ ├── zh-TW.js │ │ │ │ │ │ ├── zh-CN.js │ │ │ │ │ │ ├── az.js │ │ │ │ │ │ ├── fi.js │ │ │ │ │ │ ├── ja.js │ │ │ │ │ │ ├── hu.js │ │ │ │ │ │ ├── tr.js │ │ │ │ │ │ ├── ko.js │ │ │ │ │ │ ├── th.js │ │ │ │ │ │ ├── id.js │ │ │ │ │ │ ├── vi.js │ │ │ │ │ │ ├── is.js │ │ │ │ │ │ ├── ar.js │ │ │ │ │ │ ├── de.js │ │ │ │ │ │ ├── et.js │ │ │ │ │ │ ├── sv.js │ │ │ │ │ │ ├── bg.js │ │ │ │ │ │ ├── km.js │ │ │ │ │ │ ├── nb.js │ │ │ │ │ │ ├── ms.js │ │ │ │ │ │ ├── gl.js │ │ │ │ │ │ ├── da.js │ │ │ │ │ │ ├── en.js │ │ │ │ │ │ ├── he.js │ │ │ │ │ │ ├── fa.js │ │ │ │ │ │ ├── hi.js │ │ │ │ │ │ ├── hr.js │ │ │ │ │ │ ├── mk.js │ │ │ │ │ │ ├── eu.js │ │ │ │ │ │ ├── pt-BR.js │ │ │ │ │ │ ├── pt.js │ │ │ │ │ │ ├── lv.js │ │ │ │ │ │ ├── es.js │ │ │ │ │ │ ├── ca.js │ │ │ │ │ │ ├── it.js │ │ │ │ │ │ ├── fr.js │ │ │ │ │ │ ├── ro.js │ │ │ │ │ │ ├── nl.js │ │ │ │ │ │ ├── lt.js │ │ │ │ │ │ ├── pl.js │ │ │ │ │ │ ├── el.js │ │ │ │ │ │ ├── sr.js │ │ │ │ │ │ ├── uk.js │ │ │ │ │ │ ├── sr-Cyrl.js │ │ │ │ │ │ ├── ru.js │ │ │ │ │ │ ├── sk.js │ │ │ │ │ │ └── cs.js │ │ │ │ │ └── LICENSE-SELECT2.md │ │ │ │ ├── xregexp │ │ │ │ │ └── LICENSE-XREGEXP.txt │ │ │ │ └── jquery │ │ │ │ │ └── LICENSE-JQUERY.txt │ │ │ ├── change_form.js │ │ │ ├── collapse.js │ │ │ ├── autocomplete.js │ │ │ ├── collapse.min.js │ │ │ └── prepopulate.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 │ │ │ ├── icon-calendar.svg │ │ │ ├── gis │ │ │ │ ├── move_vertex_off.svg │ │ │ │ └── move_vertex_on.svg │ │ │ ├── calendar-icons.svg │ │ │ ├── LICENSE │ │ │ └── sorting-icons.svg │ │ └── css │ │ │ ├── fonts.css │ │ │ ├── dashboard.css │ │ │ ├── vendor │ │ │ └── select2 │ │ │ │ └── LICENSE-SELECT2.md │ │ │ ├── login.css │ │ │ └── responsive_rtl.css │ │ ├── gis │ │ ├── css │ │ │ └── ol3.css │ │ └── img │ │ │ ├── draw_point_off.svg │ │ │ ├── draw_point_on.svg │ │ │ ├── draw_line_on.svg │ │ │ ├── draw_line_off.svg │ │ │ ├── draw_polygon_off.svg │ │ │ └── draw_polygon_on.svg │ │ └── prettyjson │ │ └── prettyjson.css ├── site.wip │ ├── favicon.png │ ├── logo-vertical.png │ └── index.html ├── config │ └── nginx.conf ├── dev_certs │ ├── cert.pem │ ├── chain.pem │ └── key.pem └── conf.wip.d │ └── default.conf ├── .gitignore ├── docker-compose-wip.yml ├── docker-compose-production.yml ├── LICENSE ├── .circleci └── config.yml └── docker-compose.yml /django/people/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/cypress.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /django/people/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/certs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /django/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | celerybeat-schedule -------------------------------------------------------------------------------- /frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /nginx/site/media/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | 3 | !.gitignore -------------------------------------------------------------------------------- /frontend/plugins/vue-django-feedback.js: -------------------------------------------------------------------------------- 1 | import 'vue-django-feedback'; 2 | -------------------------------------------------------------------------------- /django/people/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /frontend/static/ding.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/static/ding.mp3 -------------------------------------------------------------------------------- /frontend/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/static/favicon.png -------------------------------------------------------------------------------- /nginx/site.wip/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site.wip/favicon.png -------------------------------------------------------------------------------- /frontend/assets/style/vuetify.styl: -------------------------------------------------------------------------------- 1 | // Import Vuetify styling 2 | @require '~vuetify/src/stylus/main.styl' 3 | -------------------------------------------------------------------------------- /nginx/site.wip/logo-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site.wip/logo-vertical.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | node_modules/ 3 | .idea/ 4 | .vscode/ 5 | django/static 6 | db.sqlite3 7 | *.pyc 8 | 9 | *.log -------------------------------------------------------------------------------- /frontend/static/logo-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/static/logo-vertical.png -------------------------------------------------------------------------------- /django/people/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PeopleConfig(AppConfig): 5 | name = 'people' 6 | -------------------------------------------------------------------------------- /frontend/static/logo-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/static/logo-horizontal.png -------------------------------------------------------------------------------- /django/.env.template: -------------------------------------------------------------------------------- 1 | SECRET_KEY= 2 | SENTRY_DSN= 3 | DEBUG=False 4 | DATABASE_URL=postgis 5 | TIME_ZONE=UTC 6 | MEETUP_API_KEY= 7 | -------------------------------------------------------------------------------- /frontend/assets/images/meetup-nocover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/assets/images/meetup-nocover.png -------------------------------------------------------------------------------- /frontend/plugins/vee-validate.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VeeValidate from 'vee-validate'; 3 | 4 | Vue.use(VeeValidate); 5 | -------------------------------------------------------------------------------- /frontend/static/github-app-icon_only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/static/github-app-icon_only.png -------------------------------------------------------------------------------- /frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nuxt/ 3 | cypress/ 4 | coverage/ 5 | tests/ 6 | cypress.json 7 | yarn-error.log 8 | jest.config.js 9 | -------------------------------------------------------------------------------- /frontend/assets/images/sample-meetup-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/assets/images/sample-meetup-cover.jpg -------------------------------------------------------------------------------- /frontend/assets/images/sample-meetup-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/frontend/assets/images/sample-meetup-logo.png -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/img/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/img/grid.png -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/docs/img/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/docs/img/grid.png -------------------------------------------------------------------------------- /nginx/site/static/admin/fonts/Roboto-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/admin/fonts/Roboto-Bold-webfont.woff -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/docs/img/favicon.ico -------------------------------------------------------------------------------- /nginx/site/static/admin/fonts/Roboto-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/admin/fonts/Roboto-Light-webfont.woff -------------------------------------------------------------------------------- /nginx/site/static/admin/fonts/Roboto-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/admin/fonts/Roboto-Regular-webfont.woff -------------------------------------------------------------------------------- /django/peoplebackend/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | 3 | from .celery import app as celery_app 4 | 5 | __all__ = ('celery_app',) -------------------------------------------------------------------------------- /frontend/pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /frontend/middleware/entrypoint-spy.js: -------------------------------------------------------------------------------- 1 | export default function ({route, store: { dispatch }}) { 2 | if (process.server) { 3 | dispatch('setFirstPageVisited', route.name); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/tests/utils.js: -------------------------------------------------------------------------------- 1 | export const mockAxios = () => { 2 | return { 3 | get: jest.fn(), 4 | post: jest.fn(), 5 | patch: jest.fn(), 6 | delete: jest.fn() 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /frontend/utilities/media.js: -------------------------------------------------------------------------------- 1 | export const playDing = () => { 2 | try { 3 | const audio = new Audio('/ding.mp3'); 4 | audio.play(); 5 | } catch (e) { 6 | console.log(e); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulilab/vue-people/HEAD/nginx/site/static/rest_framework/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /frontend/.env.template: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ 2 | GITHUB_KEY= 3 | HOST=localhost 4 | PORT=3000 5 | NODE_ENV=dev 6 | ANALYTICS_ID= 7 | WEBSOCKET_PROTOCOL= 8 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # logs 5 | npm-debug.log 6 | 7 | # Nuxt build 8 | .nuxt 9 | 10 | # Nuxt generate 11 | dist 12 | 13 | coverage/ 14 | 15 | .env 16 | .yarn-error.log 17 | 18 | -------------------------------------------------------------------------------- /frontend/plugins/vuex-geolocation.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VuexGeolocation from 'vuex-geolocation'; 3 | 4 | export default ({store}) => { 5 | const vuexGeolocation = VuexGeolocation.sync(store); 6 | Vue.use(vuexGeolocation); 7 | }; 8 | -------------------------------------------------------------------------------- /django/peoplebackend/load_env.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, join 2 | import dotenv 3 | 4 | def load_env(): 5 | "Get the path to the .env file and load it." 6 | project_dir = dirname(dirname(__file__)) 7 | dotenv.read_dotenv(join(project_dir, '.env')) 8 | -------------------------------------------------------------------------------- /django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-stretch 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y binutils libproj-dev gdal-bin 5 | 6 | RUN mkdir /src 7 | WORKDIR /src 8 | ADD requirements.txt . 9 | RUN pip install --no-cache-dir -r requirements.txt 10 | ADD . /src/ 11 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/tooltag-arrowright.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /django/peoplebackend/routing.py: -------------------------------------------------------------------------------- 1 | from channels.routing import ProtocolTypeRouter, URLRouter 2 | from django.urls import path 3 | 4 | from people.consumers import PersonConsumer 5 | 6 | application = ProtocolTypeRouter({ 7 | "websocket": 8 | URLRouter([ 9 | path('ws-people', PersonConsumer) 10 | ]) 11 | }) -------------------------------------------------------------------------------- /frontend/plugins/store-tokens.js: -------------------------------------------------------------------------------- 1 | import { saveTokens, deleteTokens } from '~/utilities/auth'; 2 | 3 | export default ({store}) => { 4 | const githubToken = store.getters['user/getGithubToken']; 5 | 6 | if (githubToken) { 7 | saveTokens(githubToken); 8 | } else { 9 | deleteTokens(['github_token']); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /frontend/plugins/vuetify.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuetify from 'vuetify'; 3 | 4 | Vue.use(Vuetify, { 5 | theme: { 6 | primary: '#42B883', 7 | secondary: '#424242', 8 | accent: '#35495E', 9 | error: '#FF5722', 10 | info: '#EEEEEE', 11 | success: '#50C969', 12 | warning: '#FE9B1B' 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /frontend/plugins/web-sockets.js: -------------------------------------------------------------------------------- 1 | export default ({store}) => { 2 | if (store && 3 | store.state && 4 | store.state.user && 5 | store.state.user.savedProfile && 6 | store.state.user.savedProfile.settings && 7 | store.state.user.savedProfile.settings.websocket) { 8 | store.dispatch('people/openSocket'); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-addlink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/tooltag-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.11.1-alpine 2 | 3 | RUN mkdir -p /usr/src/app 4 | WORKDIR /usr/src/app 5 | 6 | COPY . /usr/src/app/ 7 | RUN echo "NODE_ENV=production" >> .env 8 | # This is to allow yarn to install from github 9 | RUN apk update && apk upgrade && \ 10 | apk add --no-cache bash git openssh 11 | RUN yarn 12 | RUN yarn build 13 | 14 | # start command 15 | CMD [ "yarn", "start" ] 16 | 17 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-deletelink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /django/people/migrations/0021_remove_person_news_opt_in.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-22 10:08 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0020_opt_in_splicing'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='person', 15 | name='news_opt_in', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /django/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI entrypoint. Configures Django and then runs the application 3 | defined in the ASGI_APPLICATION setting. 4 | """ 5 | 6 | import os 7 | import django 8 | from channels.routing import get_default_application 9 | from peoplebackend import load_env 10 | 11 | load_env.load_env() 12 | 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "peoplebackend.settings") 15 | django.setup() 16 | application = get_default_application() 17 | -------------------------------------------------------------------------------- /docker-compose-wip.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nginx-wip: 5 | restart: always 6 | image: nginx:1.13.1 7 | ports: 8 | - "80:80" 9 | - "443:443" 10 | volumes: 11 | - ./nginx/site.wip:/usr/share/nginx/html:rw 12 | - ./nginx/conf.wip.d:/etc/nginx/conf.d:ro 13 | - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro 14 | - ./nginx/certs:/etc/letsencrypt:rw 15 | - ./nginx/certs-data:/data/letsencrypt:rw 16 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-yes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docker-compose-production.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nginx: 5 | restart: always 6 | image: nginx:1.13.1 7 | ports: 8 | - "80:80" 9 | - "443:443" 10 | volumes: 11 | - ./nginx/site:/usr/share/nginx/html:rw 12 | - ./nginx/conf.production.d:/etc/nginx/conf.d:ro 13 | - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro 14 | - ./nginx/certs:/etc/letsencrypt:rw 15 | - ./nginx/certs-data:/data/letsencrypt:rw 16 | -------------------------------------------------------------------------------- /django/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.0.8 2 | gunicorn 3 | psycopg2 4 | djangorestframework 5 | djangorestframework-gis 6 | coreapi 7 | django-redis 8 | django-allauth 9 | django-taggit 10 | django-taggit-serializer 11 | django-dotenv 12 | django-simplefeedback==0.8 13 | django-prettyjson==0.3.0 14 | daphne==2.1.1 15 | celery 16 | channels==2.1.1 17 | channels-redis==2.1.1 18 | redis 19 | raven 20 | 21 | pytest 22 | pytest-django 23 | pytest-cov 24 | pytest-testmon 25 | pytest-watch 26 | ipdb -------------------------------------------------------------------------------- /frontend/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | bail: true, 4 | collectCoverage: true, 5 | restoreMocks: true, 6 | collectCoverageFrom: [ 7 | '**/store/**/*.js' 8 | ], 9 | coverageDirectory: 'coverage', 10 | coverageThreshold: { 11 | global: { 12 | branches: 100, 13 | functions: 100, 14 | lines: 100, 15 | statements: 100 16 | } 17 | }, 18 | moduleNameMapper: { 19 | '~(.*)$': '/$1' 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /django/people/migrations/0009_auto_20180413_0856.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-13 08:56 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0008_auto_20180412_1544'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='type', 15 | name='order', 16 | field=models.IntegerField(default=0), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/pages/index/meetup/_id.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /django/people/migrations/0013_person_news_opt_in.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-13 14:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0012_auto_20180613_1212'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='person', 15 | name='news_opt_in', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /django/people/migrations/0011_person_public_email.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-06-07 09:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0010_auto_20180413_0905'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='person', 15 | name='public_email', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /django/people/migrations/0014_auto_20180613_1527.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-13 15:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0013_person_news_opt_in'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='person', 15 | name='news_opt_in', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /django/people/migrations/0015_person_show_hireable.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-13 15:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0014_auto_20180613_1527'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='person', 15 | name='show_hireable', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /django/people/migrations/0016_auto_20180614_0946.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-14 09:46 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0015_person_show_hireable'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='person', 15 | name='show_hireable', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/pages/index/user/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /frontend/plugins/axios.js: -------------------------------------------------------------------------------- 1 | export const isDjangoAPIRequest = (url) => { 2 | const pieces = url.split('/').filter(p => p !== ''); 3 | const djangoPaths = ['api']; 4 | return !!(pieces && djangoPaths.includes(pieces[0])); 5 | }; 6 | 7 | export default function ({ $axios, store: { getters } }) { 8 | $axios.interceptors.request.use(config => { 9 | if (isDjangoAPIRequest(config.url)) { 10 | config.headers['x-csrftoken'] = getters['user/getCsrfToken']; 11 | } 12 | return config; 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /django/people/migrations/0005_person_github_updated.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 15:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0004_auto_20180412_1526'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='person', 15 | name='github_updated', 16 | field=models.DateTimeField(blank=True, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /django/peoplebackend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for peoplebackend 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/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | from django.core.wsgi import get_wsgi_application 12 | from . import load_env 13 | 14 | load_env.load_env() 15 | 16 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "peoplebackend.settings") 17 | 18 | application = get_wsgi_application() 19 | -------------------------------------------------------------------------------- /frontend/tests/__mocks__/django-channels.js: -------------------------------------------------------------------------------- 1 | export const connect = jest.fn(); 2 | export const listen = jest.fn().mockImplementation(fn => { 3 | if (fn) { 4 | fn(); 5 | } 6 | }); 7 | export const close = jest.fn(); 8 | 9 | const socket = { close }; 10 | export const WebSocketBridge = jest.fn().mockImplementation(() => { 11 | return { 12 | connect, listen, socket 13 | }; 14 | }); 15 | const mock = jest.fn().mockImplementation(() => { 16 | return { WebSocketBridge, connect, listen, close }; 17 | }); 18 | 19 | export default mock; 20 | -------------------------------------------------------------------------------- /django/people/migrations/0023_person_settings.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-07-09 08:31 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0022_auto_20180628_1515'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='person', 16 | name='settings', 17 | field=django.contrib.postgres.fields.jsonb.JSONField(default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /frontend/utilities/coords.js: -------------------------------------------------------------------------------- 1 | export const circleOfCoords = ({ lat, lng }, amount) => { 2 | const radius = 1000; // meters 3 | const result = []; 4 | for (let i = 0; i < amount; i++) { 5 | const angle = Math.PI * 2 * i / amount; 6 | const dx = radius * Math.cos(angle); 7 | const dy = radius * Math.sin(angle); 8 | const latlng = {}; 9 | latlng.lat = lat + (180 / Math.PI) * (dy / 6378137); 10 | latlng.lng = lng + (180 / Math.PI) * (dx / 6378137) / Math.cos(lat * Math.PI / 180); 11 | result.push(latlng); 12 | } 13 | return result; 14 | }; 15 | -------------------------------------------------------------------------------- /nginx/site/static/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 | overflow: hidden; 25 | text-overflow: ellipsis; 26 | -o-text-overflow: ellipsis; 27 | } 28 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /django/people/migrations/0017_auto_20180622_1009.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-22 10:09 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0016_auto_20180614_0946'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelManagers( 14 | name='person', 15 | managers=[ 16 | ], 17 | ), 18 | migrations.RemoveField( 19 | model_name='person', 20 | name='is_active', 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-no.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /django/people/migrations/0003_auto_20180412_1354.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 13:54 2 | 3 | import django.contrib.gis.db.models.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0002_auto_20180412_1324'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='person', 16 | name='location', 17 | field=django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/inline-delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /django/people/migrations/0010_auto_20180413_0905.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-13 09:05 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0009_auto_20180413_0856'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='person', 15 | options={'verbose_name_plural': 'People'}, 16 | ), 17 | migrations.AlterModelOptions( 18 | name='type', 19 | options={'verbose_name': 'User Type'}, 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /frontend/pages/index/user/_id.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /django/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | import dotenv 5 | 6 | if __name__ == "__main__": 7 | dotenv.read_dotenv() 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "peoplebackend.settings") 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | -------------------------------------------------------------------------------- /django/people/migrations/0012_auto_20180613_1212.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-13 12:12 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('people', '0011_person_public_email'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='person', 17 | name='user', 18 | field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/zh-TW.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-TW",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="請刪掉"+t+"個字元";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="請再輸入"+t+"個字元";return n},loadingMore:function(){return"載入中…"},maximumSelected:function(e){var t="你只能選擇最多"+e.maximum+"項";return t},noResults:function(){return"沒有找到相符的項目"},searching:function(){return"搜尋中…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | es6: true, 7 | jest: true 8 | }, 9 | globals: { 10 | L: true 11 | }, 12 | parserOptions: { 13 | parser: 'babel-eslint' 14 | }, 15 | extends: [ 16 | 'plugin:vue/recommended', 17 | 'standard' 18 | ], 19 | // required to lint *.vue files 20 | plugins: [ 21 | 'vue' 22 | ], 23 | // add your custom rules here 24 | rules: { 25 | camelcase: 0, 26 | "indent": ["error", 2], 27 | "arrow-parens": 0, 28 | "one-var": 0, 29 | semi: ["warn", "always"], 30 | "eol-last": ["error", "always"] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/components/base/TextOrPlaceholder.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 34 | 35 | 38 | -------------------------------------------------------------------------------- /frontend/middleware/auth.js: -------------------------------------------------------------------------------- 1 | import { getTokensFromCookie, getTokensFromLocalStorage } from '../utilities/auth'; 2 | 3 | export default async function ({store, req}) { 4 | const tokens = process.server ? getTokensFromCookie(req) : getTokensFromLocalStorage(); 5 | if (tokens && tokens.github) { 6 | await store.dispatch('user/setGithubToken', tokens.github); 7 | } 8 | if (tokens && tokens.csrftoken) { 9 | await store.dispatch('user/setCsrfToken', tokens.csrftoken); 10 | } 11 | if (tokens && tokens.cookieAccepted) { 12 | const showCookies = tokens.cookieAccepted !== 'true'; 13 | await store.dispatch('setShowCookieWarning', showCookies); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /django/people/permission.py: -------------------------------------------------------------------------------- 1 | from rest_framework import permissions 2 | 3 | 4 | class IsMeOrReadOnly(permissions.BasePermission): 5 | """ 6 | Object-level permission to only allow owners of an object to edit it. 7 | Assumes the model instance has an `owner` attribute. 8 | """ 9 | 10 | def has_object_permission(self, request, view, obj): 11 | # Read permissions are allowed to any request, 12 | # so we'll always allow GET, HEAD or OPTIONS requests. 13 | if request.method in permissions.SAFE_METHODS: 14 | return True 15 | 16 | # Instance must have an attribute named `owner`. 17 | return obj.user == request.user 18 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-unknown-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/zh-CN.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="请删除"+t+"个字符";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="请再输入至少"+t+"个字符";return n},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(e){var t="最多只能选择"+e.maximum+"个项目";return t},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /django/people/migrations/0018_show_hireable_update.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-22 09:54 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | 9 | def hireable_update(apps, schema_editor): 10 | People = apps.get_model('people', 'Person') 11 | for p in People.objects.all(): 12 | if p.hireable and p.show_hireable : 13 | p.hireable = True 14 | else: 15 | p.hireable = False 16 | p.save() 17 | 18 | dependencies = [ 19 | ('people', '0017_auto_20180622_1009'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(hireable_update) 24 | ] 25 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/az.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /django/people/migrations/0006_person_user.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 15:37 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('people', '0005_person_github_updated'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='person', 18 | name='user', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/fi.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Ole hyvä ja anna "+t+" merkkiä vähemmän"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Ole hyvä ja anna "+t+" merkkiä lisää"},loadingMore:function(){return"Ladataan lisää tuloksia…"},maximumSelected:function(e){return"Voit valita ainoastaan "+e.maximum+" kpl"},noResults:function(){return"Ei tuloksia"},searching:function(){}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ja.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ja",[],function(){return{errorLoading:function(){return"結果が読み込まれませんでした"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" 文字を削除してください";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="少なくとも "+t+" 文字を入力してください";return n},loadingMore:function(){return"読み込み中…"},maximumSelected:function(e){var t=e.maximum+" 件しか選択できません";return t},noResults:function(){return"対象が見つかりません"},searching:function(){return"検索しています…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /django/people/migrations/0020_opt_in_splicing.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-22 10:06 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | def opt_in_splicing(apps, schema_editor): 9 | People = apps.get_model('people', 'Person') 10 | for p in People.objects.all(): 11 | if not p.news_opt_in: 12 | p.feature_updates = False 13 | p.upcoming_events = False 14 | p.job_opportunities = False 15 | p.save() 16 | 17 | dependencies = [ 18 | ('people', '0019_auto_20180622_1006'), 19 | ] 20 | 21 | operations = [ 22 | migrations.RunPython(opt_in_splicing) 23 | ] 24 | -------------------------------------------------------------------------------- /nginx/site.wip/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |

VuePeople is being updated please try again later

24 |

You can check the status of the build here:

25 |

26 | CircleCi 27 |

28 |
29 | 30 | -------------------------------------------------------------------------------- /django/people/migrations/0007_auto_20180412_1543.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 15:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0006_person_user'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='person', 15 | name='email', 16 | ), 17 | migrations.RemoveField( 18 | model_name='person', 19 | name='name', 20 | ), 21 | migrations.AlterField( 22 | model_name='person', 23 | name='bio', 24 | field=models.TextField(blank=True, null=True), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /frontend/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | }; 18 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/hu.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Túl hosszú. "+t+" karakterrel több, mint kellene."},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Túl rövid. Még "+t+" karakter hiányzik."},loadingMore:function(){return"Töltés…"},maximumSelected:function(e){return"Csak "+e.maximum+" elemet lehet kiválasztani."},noResults:function(){return"Nincs találat."},searching:function(){return"Keresés…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/tr.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/tr",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" karakter daha girmelisiniz";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="En az "+t+" karakter daha girmelisiniz";return n},loadingMore:function(){return"Daha fazla…"},maximumSelected:function(e){var t="Sadece "+e.maximum+" seçim yapabilirsiniz";return t},noResults:function(){return"Sonuç bulunamadı"},searching:function(){return"Aranıyor…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ko.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ko",[],function(){return{errorLoading:function(){return"결과를 불러올 수 없습니다."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="너무 깁니다. "+t+" 글자 지워주세요.";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="너무 짧습니다. "+t+" 글자 더 입력해주세요.";return n},loadingMore:function(){return"불러오는 중…"},maximumSelected:function(e){var t="최대 "+e.maximum+"개까지만 선택 가능합니다.";return t},noResults:function(){return"결과가 없습니다."},searching:function(){return"검색 중…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/th.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/th",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="โปรดลบออก "+t+" ตัวอักษร";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="โปรดพิมพ์เพิ่มอีก "+t+" ตัวอักษร";return n},loadingMore:function(){return"กำลังค้นข้อมูลเพิ่ม…"},maximumSelected:function(e){var t="คุณสามารถเลือกได้ไม่เกิน "+e.maximum+" รายการ";return t},noResults:function(){return"ไม่พบข้อมูล"},searching:function(){return"กำลังค้นข้อมูล…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /frontend/assets/style/mixins.less: -------------------------------------------------------------------------------- 1 | // Text truncate 2 | // Requires inline-block or block for proper styling 3 | .text-truncate() { 4 | overflow: hidden; 5 | text-overflow: ellipsis; 6 | white-space: nowrap; 7 | } 8 | 9 | .no-text-truncate() { 10 | overflow: visible; 11 | text-overflow: inherit; 12 | white-space: normal; 13 | } 14 | 15 | .clearfix() { 16 | &::after { 17 | display: table; 18 | clear: both; 19 | content: ""; 20 | } 21 | } 22 | 23 | .hidden { 24 | display: none; 25 | } 26 | 27 | .placeholder(@color: #757575) { 28 | &::-webkit-input-placeholder { 29 | color: @color; 30 | } 31 | &::-moz-placeholder { 32 | color: @color; 33 | } 34 | &:-ms-input-placeholder { 35 | color: @color; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/id.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/id",[],function(){return{errorLoading:function(){return"Data tidak boleh diambil."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Hapuskan "+t+" huruf"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Masukkan "+t+" huruf lagi"},loadingMore:function(){return"Mengambil data…"},maximumSelected:function(e){return"Anda hanya dapat memilih "+e.maximum+" pilihan"},noResults:function(){return"Tidak ada data yang sesuai"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/vi.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/vi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vui lòng nhập ít hơn "+t+" ký tự";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vui lòng nhập nhiều hơn "+t+' ký tự"';return n},loadingMore:function(){return"Đang lấy thêm kết quả…"},maximumSelected:function(e){var t="Chỉ có thể chọn được "+e.maximum+" lựa chọn";return t},noResults:function(){return"Không tìm thấy kết quả"},searching:function(){return"Đang tìm…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/is.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/is",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vinsamlegast styttið texta um "+t+" staf";return t<=1?n:n+"i"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vinsamlegast skrifið "+t+" staf";return t>1&&(n+="i"),n+=" í viðbót",n},loadingMore:function(){return"Sæki fleiri niðurstöður…"},maximumSelected:function(e){return"Þú getur aðeins valið "+e.maximum+" atriði"},noResults:function(){return"Ekkert fannst"},searching:function(){return"Leita…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /frontend/store/layout.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | optOutDialogOpen: false, 3 | settingsDialogOpen: false 4 | }); 5 | 6 | export const getters = { 7 | getOptOutDialogOpen: state => state.optOutDialogOpen, 8 | getSettingsDialogOpen: state => state.settingsDialogOpen 9 | }; 10 | 11 | export const actions = { 12 | setOptOutDialogOpen ({commit}, status) { 13 | commit('SET_OPT_OUT_DIALOG_OPEN', status); 14 | }, 15 | setSettingsDialogOpen ({commit}, status) { 16 | commit('SET_SETTINGS_DIALOG_OPEN', status); 17 | } 18 | }; 19 | 20 | export const mutations = { 21 | SET_OPT_OUT_DIALOG_OPEN: (state, status) => { 22 | state.optOutDialogOpen = status; 23 | }, 24 | SET_SETTINGS_DIALOG_OPEN: (state, status) => { 25 | state.settingsDialogOpen = status; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ar.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="الرجاء حذف "+t+" عناصر";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="الرجاء إضافة "+t+" عناصر";return n},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(e){var t="تستطيع إختيار "+e.maximum+" بنود فقط";return t},noResults:function(){return"لم يتم العثور على أي نتائج"},searching:function(){return"جاري البحث…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/de.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/de",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Bitte "+t+" Zeichen weniger eingeben"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Bitte "+t+" Zeichen mehr eingeben"},loadingMore:function(){return"Lade mehr Ergebnisse…"},maximumSelected:function(e){var t="Sie können nur "+e.maximum+" Eintr";return e.maximum===1?t+="ag":t+="äge",t+=" auswählen",t},noResults:function(){return"Keine Übereinstimmungen gefunden"},searching:function(){return"Suche…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/et.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/et",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" vähem",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" rohkem",n},loadingMore:function(){return"Laen tulemusi…"},maximumSelected:function(e){var t="Saad vaid "+e.maximum+" tulemus";return e.maximum==1?t+="e":t+="t",t+=" valida",t},noResults:function(){return"Tulemused puuduvad"},searching:function(){return"Otsin…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/sv.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sv",[],function(){return{errorLoading:function(){return"Resultat kunde inte laddas."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vänligen sudda ut "+t+" tecken";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vänligen skriv in "+t+" eller fler tecken";return n},loadingMore:function(){return"Laddar fler resultat…"},maximumSelected:function(e){var t="Du kan max välja "+e.maximum+" element";return t},noResults:function(){return"Inga träffar"},searching:function(){return"Söker…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/bg.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/km.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/km",[],function(){return{errorLoading:function(){return"មិនអាចទាញយកទិន្នន័យ"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="សូមលុបចេញ "+t+" អក្សរ";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="សូមបញ្ចូល"+t+" អក្សរ រឺ ច្រើនជាងនេះ";return n},loadingMore:function(){return"កំពុងទាញយកទិន្នន័យបន្ថែម..."},maximumSelected:function(e){var t="អ្នកអាចជ្រើសរើសបានតែ "+e.maximum+" ជម្រើសប៉ុណ្ណោះ";return t},noResults:function(){return"មិនមានលទ្ធផល"},searching:function(){return"កំពុងស្វែងរក..."}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/nb.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nb",[],function(){return{errorLoading:function(){return"Kunne ikke hente resultater."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Vennligst fjern "+t+" tegn"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vennligst skriv inn ";return t>1?n+=" flere tegn":n+=" tegn til",n},loadingMore:function(){return"Laster flere resultater…"},maximumSelected:function(e){return"Du kan velge maks "+e.maximum+" elementer"},noResults:function(){return"Ingen treff"},searching:function(){return"Søker…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ms.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ms",[],function(){return{errorLoading:function(){return"Keputusan tidak berjaya dimuatkan."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Sila hapuskan "+t+" aksara"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Sila masukkan "+t+" atau lebih aksara"},loadingMore:function(){return"Sedang memuatkan keputusan…"},maximumSelected:function(e){return"Anda hanya boleh memilih "+e.maximum+" pilihan"},noResults:function(){return"Tiada padanan yang ditemui"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/gis/css/ol3.css: -------------------------------------------------------------------------------- 1 | .switch-type { 2 | background-repeat: no-repeat; 3 | cursor: pointer; 4 | top: 0.5em; 5 | width: 22px; 6 | height: 20px; 7 | } 8 | 9 | .type-Point { 10 | background-image: url("../img/draw_point_off.svg"); 11 | right: 5px; 12 | } 13 | .type-Point.type-active { 14 | background-image: url("../img/draw_point_on.svg"); 15 | } 16 | 17 | .type-LineString { 18 | background-image: url("../img/draw_line_off.svg"); 19 | right: 30px; 20 | } 21 | .type-LineString.type-active { 22 | background-image: url("../img/draw_line_on.svg"); 23 | } 24 | 25 | .type-Polygon { 26 | background-image: url("../img/draw_polygon_off.svg"); 27 | right: 55px; 28 | } 29 | .type-Polygon.type-active { 30 | background-image: url("../img/draw_polygon_on.svg"); 31 | } 32 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/gl.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/gl",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Elimine ";return t===1?n+="un carácter":n+=t+" caracteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Engada ";return t===1?n+="un carácter":n+=t+" caracteres",n},loadingMore:function(){return"Cargando máis resultados…"},maximumSelected:function(e){var t="Só pode ";return e.maximum===1?t+="un elemento":t+=e.maximum+" elementos",t},noResults:function(){return"Non se atoparon resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/gis/img/draw_point_off.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nginx/site/static/gis/img/draw_point_on.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nginx/site/static/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 | $('body').on('click', '.add-another', 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 | -------------------------------------------------------------------------------- /django/people/consumers.py: -------------------------------------------------------------------------------- 1 | from asgiref.sync import async_to_sync 2 | from channels.generic.websocket import JsonWebsocketConsumer 3 | from people.serializers import PersonDetailSerializer 4 | from people.models import Person 5 | 6 | class PersonConsumer(JsonWebsocketConsumer): 7 | 8 | def connect(self): 9 | self.accept() 10 | async_to_sync(self.channel_layer.group_add)("people", self.channel_name) 11 | self.send_json(content={"handshake": "all fine"}) 12 | 13 | def disconnect(self, close_code): 14 | async_to_sync(self.channel_layer.group_discard)("people", self.channel_name) 15 | 16 | def send_person(self, event): 17 | person = Person.objects.get(id=event['id']) 18 | p = PersonDetailSerializer(person).data 19 | self.send_json({ 20 | "person": p 21 | }) 22 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/da.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Angiv venligst "+t+" tegn mindre";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Angiv venligst "+t+" tegn mere";return n},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/en.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Please enter "+t+" or more characters";return n},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/he.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/he",[],function(){return{errorLoading:function(){return"שגיאה בטעינת התוצאות"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="נא למחוק ";return t===1?n+="תו אחד":n+=t+" תווים",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="נא להכניס ";return t===1?n+="תו אחד":n+=t+" תווים",n+=" או יותר",n},loadingMore:function(){return"טוען תוצאות נוספות…"},maximumSelected:function(e){var t="באפשרותך לבחור עד ";return e.maximum===1?t+="פריט אחד":t+=e.maximum+" פריטים",t},noResults:function(){return"לא נמצאו תוצאות"},searching:function(){return"מחפש…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/fa.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fa",[],function(){return{errorLoading:function(){return"امکان بارگذاری نتایج وجود ندارد."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="لطفاً "+t+" کاراکتر را حذف نمایید";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="لطفاً تعداد "+t+" کاراکتر یا بیشتر وارد نمایید";return n},loadingMore:function(){return"در حال بارگذاری نتایج بیشتر..."},maximumSelected:function(e){var t="شما تنها می‌توانید "+e.maximum+" آیتم را انتخاب نمایید";return t},noResults:function(){return"هیچ نتیجه‌ای یافت نشد"},searching:function(){return"در حال جستجو..."}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/hi.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hi",[],function(){return{errorLoading:function(){return"परिणामों को लोड नहीं किया जा सका।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" अक्षर को हटा दें";return t>1&&(n=t+" अक्षरों को हटा दें "),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="कृपया "+t+" या अधिक अक्षर दर्ज करें";return n},loadingMore:function(){return"अधिक परिणाम लोड हो रहे है..."},maximumSelected:function(e){var t="आप केवल "+e.maximum+" आइटम का चयन कर सकते हैं";return t},noResults:function(){return"कोई परिणाम नहीं मिला"},searching:function(){return"खोज रहा है..."}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/hr.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hr",[],function(){function e(e){var t=" "+e+" znak";return e%10<5&&e%10>0&&(e%100<5||e%100>19)?e%10>1&&(t+="a"):t+="ova",t}return{errorLoading:function(){return"Preuzimanje nije uspjelo."},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Unesite "+e(n)},inputTooShort:function(t){var n=t.minimum-t.input.length;return"Unesite još "+e(n)},loadingMore:function(){return"Učitavanje rezultata…"},maximumSelected:function(e){return"Maksimalan broj odabranih stavki je "+e.maximum},noResults:function(){return"Nema rezultata"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/mk.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/mk",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Ве молиме внесете "+e.maximum+" помалку карактер";return e.maximum!==1&&(n+="и"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Ве молиме внесете уште "+e.maximum+" карактер";return e.maximum!==1&&(n+="и"),n},loadingMore:function(){return"Вчитување резултати…"},maximumSelected:function(e){var t="Можете да изберете само "+e.maximum+" ставк";return e.maximum===1?t+="а":t+="и",t},noResults:function(){return"Нема пронајдено совпаѓања"},searching:function(){return"Пребарување…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/eu.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/eu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gutxiago",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gehiago",n},loadingMore:function(){return"Emaitza gehiago kargatzen…"},maximumSelected:function(e){return e.maximum===1?"Elementu bakarra hauta dezakezu":e.maximum+" elementu hauta ditzakezu soilik"},noResults:function(){return"Ez da bat datorrenik aurkitu"},searching:function(){return"Bilatzen…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/pt-BR.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt-BR",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Apague "+t+" caracter";return t!=1&&(n+="es"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Digite "+t+" ou mais caracteres";return n},loadingMore:function(){return"Carregando mais resultados…"},maximumSelected:function(e){var t="Você só pode selecionar "+e.maximum+" ite";return e.maximum==1?t+="m":t+="ns",t},noResults:function(){return"Nenhum resultado encontrado"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/pt.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor apague "+t+" ";return n+=t!=1?"caracteres":"carácter",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Introduza "+t+" ou mais caracteres";return n},loadingMore:function(){return"A carregar mais resultados…"},maximumSelected:function(e){var t="Apenas pode seleccionar "+e.maximum+" ";return t+=e.maximum!=1?"itens":"item",t},noResults:function(){return"Sem resultados"},searching:function(){return"A procurar…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /frontend/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 30 | 31 | 43 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/lv.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lv",[],function(){function e(e,t,n,r){return e===11?t:e%10===1?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Lūdzu ievadiet par "+n;return r+=" simbol"+e(n,"iem","u","iem"),r+" mazāk"},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Lūdzu ievadiet vēl "+n;return r+=" simbol"+e(n,"us","u","us"),r},loadingMore:function(){return"Datu ielāde…"},maximumSelected:function(t){var n="Jūs varat izvēlēties ne vairāk kā "+t.maximum;return n+=" element"+e(t.maximum,"us","u","us"),n},noResults:function(){return"Sakritību nav"},searching:function(){return"Meklēšana…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/es.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/es",[],function(){return{errorLoading:function(){return"La carga falló"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor, elimine "+t+" car";return t==1?n+="ácter":n+="acteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Por favor, introduzca "+t+" car";return t==1?n+="ácter":n+="acteres",n},loadingMore:function(){return"Cargando más resultados…"},maximumSelected:function(e){var t="Sólo puede seleccionar "+e.maximum+" elemento";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No se encontraron resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ca.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/it.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/it",[],function(){return{errorLoading:function(){return"I risultati non possono essere caricati."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Per favore cancella "+t+" caratter";return t!==1?n+="i":n+="e",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Per favore inserisci "+t+" o più caratteri";return n},loadingMore:function(){return"Caricando più risultati…"},maximumSelected:function(e){var t="Puoi selezionare solo "+e.maximum+" element";return e.maximum!==1?t+="i":t+="o",t},noResults:function(){return"Nessun risultato trovato"},searching:function(){return"Sto cercando…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/gis/img/draw_line_on.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/fr.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fr",[],function(){return{errorLoading:function(){return"Les résultats ne peuvent pas être chargés."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n},loadingMore:function(){return"Chargement de résultats supplémentaires…"},maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t},noResults:function(){return"Aucun résultat trouvé"},searching:function(){return"Recherche en cours…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ro.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ro",[],function(){return{errorLoading:function(){return"Rezultatele nu au putut fi incărcate."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vă rugăm să ștergeți"+t+" caracter";return t!==1&&(n+="e"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vă rugăm să introduceți "+t+"sau mai multe caractere";return n},loadingMore:function(){return"Se încarcă mai multe rezultate…"},maximumSelected:function(e){var t="Aveți voie să selectați cel mult "+e.maximum;return t+=" element",e.maximum!==1&&(t+="e"),t},noResults:function(){return"Nu au fost găsite rezultate"},searching:function(){return"Căutare…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /frontend/components/RightSide.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 39 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/nl.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nl",[],function(){return{errorLoading:function(){return"De resultaten konden niet worden geladen."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Gelieve "+t+" karakters te verwijderen";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Gelieve "+t+" of meer karakters in te voeren";return n},loadingMore:function(){return"Meer resultaten laden…"},maximumSelected:function(e){var t=e.maximum==1?"kan":"kunnen",n="Er "+t+" maar "+e.maximum+" item";return e.maximum!=1&&(n+="s"),n+=" worden geselecteerd",n},noResults:function(){return"Geen resultaten gevonden…"},searching:function(){return"Zoeken…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/lt.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lt",[],function(){function e(e,t,n,r){return e%10===1&&(e%100<11||e%100>19)?t:e%10>=2&&e%10<=9&&(e%100<11||e%100>19)?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Pašalinkite "+n+" simbol";return r+=e(n,"į","ius","ių"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Įrašykite dar "+n+" simbol";return r+=e(n,"į","ius","ių"),r},loadingMore:function(){return"Kraunama daugiau rezultatų…"},maximumSelected:function(t){var n="Jūs galite pasirinkti tik "+t.maximum+" element";return n+=e(t.maximum,"ą","us","ų"),n},noResults:function(){return"Atitikmenų nerasta"},searching:function(){return"Ieškoma…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/gis/img/draw_line_off.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /frontend/plugins/vue-leaflet.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import {LMap, LTileLayer, LMarker, LTooltip, LLayerGroup, LControlZoom, L, LControlAttribution} from 'vue2-leaflet'; 3 | import CustomMarkerCluster from '@/components/CustomMarkerCluster'; 4 | 5 | delete L.Icon.Default.prototype._getIconUrl; 6 | 7 | L.Icon.Default.mergeOptions({ 8 | iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), 9 | iconUrl: require('leaflet/dist/images/marker-icon.png'), 10 | shadowUrl: require('leaflet/dist/images/marker-shadow.png') 11 | }); 12 | 13 | Vue.component('l-map', LMap); 14 | Vue.component('l-tilelayer', LTileLayer); 15 | Vue.component('l-marker', LMarker); 16 | Vue.component('l-tooltip', LTooltip); 17 | Vue.component('l-layer-group', LLayerGroup); 18 | Vue.component('l-control-zoom', LControlZoom); 19 | Vue.component('l-control-attribution', LControlAttribution); 20 | Vue.component('custom-marker-cluster', CustomMarkerCluster); 21 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/pl.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pl",[],function(){var e=["znak","znaki","znaków"],t=["element","elementy","elementów"],n=function(t,n){if(t===1)return n[0];if(t>1&&t<=4)return n[1];if(t>=5)return n[2]};return{errorLoading:function(){return"Nie można załadować wyników."},inputTooLong:function(t){var r=t.input.length-t.maximum;return"Usuń "+r+" "+n(r,e)},inputTooShort:function(t){var r=t.minimum-t.input.length;return"Podaj przynajmniej "+r+" "+n(r,e)},loadingMore:function(){return"Trwa ładowanie…"},maximumSelected:function(e){return"Możesz zaznaczyć tylko "+e.maximum+" "+n(e.maximum,t)},noResults:function(){return"Brak wyników"},searching:function(){return"Trwa wyszukiwanie…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/el.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/el",[],function(){return{errorLoading:function(){return"Τα αποτελέσματα δεν μπόρεσαν να φορτώσουν."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Παρακαλώ διαγράψτε "+t+" χαρακτήρ";return t==1&&(n+="α"),t!=1&&(n+="ες"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Παρακαλώ συμπληρώστε "+t+" ή περισσότερους χαρακτήρες";return n},loadingMore:function(){return"Φόρτωση περισσότερων αποτελεσμάτων…"},maximumSelected:function(e){var t="Μπορείτε να επιλέξετε μόνο "+e.maximum+" επιλογ";return e.maximum==1&&(t+="ή"),e.maximum!=1&&(t+="ές"),t},noResults:function(){return"Δεν βρέθηκαν αποτελέσματα"},searching:function(){return"Αναζήτηση…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/sr.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Preuzimanje nije uspelo."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Obrišite "+n+" simbol";return r+=e(n,"","a","a"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Ukucajte bar još "+n+" simbol";return r+=e(n,"","a","a"),r},loadingMore:function(){return"Preuzimanje još rezultata…"},maximumSelected:function(t){var n="Možete izabrati samo "+t.maximum+" stavk";return n+=e(t.maximum,"u","e","i"),n},noResults:function(){return"Ništa nije pronađeno"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/uk.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/uk",[],function(){function e(e,t,n,r){return e%100>10&&e%100<15?r:e%10===1?t:e%10>1&&e%10<5?n:r}return{errorLoading:function(){return"Неможливо завантажити результати"},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Будь ласка, видаліть "+n+" "+e(t.maximum,"літеру","літери","літер")},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Будь ласка, введіть "+t+" або більше літер"},loadingMore:function(){return"Завантаження інших результатів…"},maximumSelected:function(t){return"Ви можете вибрати лише "+t.maximum+" "+e(t.maximum,"пункт","пункти","пунктів")},noResults:function(){return"Нічого не знайдено"},searching:function(){return"Пошук…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/sr-Cyrl.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr-Cyrl",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Преузимање није успело."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Обришите "+n+" симбол";return r+=e(n,"","а","а"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Укуцајте бар још "+n+" симбол";return r+=e(n,"","а","а"),r},loadingMore:function(){return"Преузимање још резултата…"},maximumSelected:function(t){var n="Можете изабрати само "+t.maximum+" ставк";return n+=e(t.maximum,"у","е","и"),n},noResults:function(){return"Ништа није пронађено"},searching:function(){return"Претрага…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /frontend/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/css/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 20px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/ru.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ru",[],function(){function e(e,t,n,r){return e%10<5&&e%10>0&&e%100<5||e%100>20?e%10>1?n:t:r}return{errorLoading:function(){return"Невозможно загрузить результаты"},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Пожалуйста, введите на "+n+" символ";return r+=e(n,"","a","ов"),r+=" меньше",r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Пожалуйста, введите еще хотя бы "+n+" символ";return r+=e(n,"","a","ов"),r},loadingMore:function(){return"Загрузка данных…"},maximumSelected:function(t){var n="Вы можете выбрать не более "+t.maximum+" элемент";return n+=e(t.maximum,"","a","ов"),n},noResults:function(){return"Совпадений не найдено"},searching:function(){return"Поиск…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/prettyjson/prettyjson.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.jsonview{font-family:monospace;font-size:1.1em;white-space:pre-wrap}.jsonview .prop{font-weight:700}.jsonview .null{color:red}.jsonview .bool,.jsonview .num{color:#00f}.jsonview .string{color:green;white-space:pre-wrap}.jsonview .string.multiline{display:inline-block;vertical-align:text-top}.jsonview .collapser{position:absolute;left:-1em;cursor:pointer}.jsonview .collapsible{transition:height 1.2s;transition:width 1.2s}.jsonview .collapsible.collapsed{height:.8em;width:1em;display:inline-block;overflow:hidden;margin:0}.jsonview .collapsible.collapsed:before{content:"…";width:1em;margin-left:.2em}.jsonview .collapser.collapsed{transform:rotate(0)}.jsonview .q{display:inline-block;width:0;color:transparent}.jsonview li{position:relative}.jsonview ul{list-style:none;margin:0 0 0 2em;padding:0}.jsonview h1{font-size:1.2em} 2 | 3 | form .jsonview ul { 4 | margin: 0 0 0 2em; 5 | padding: 0; 6 | } 7 | .parsed { 8 | display: none; 9 | } 10 | -------------------------------------------------------------------------------- /nginx/site/static/gis/img/draw_polygon_off.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nginx/site/static/gis/img/draw_polygon_on.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /django/people/migrations/0019_auto_20180622_1006.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-22 10:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0018_show_hireable_update'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='person', 15 | name='show_hireable', 16 | ), 17 | migrations.AddField( 18 | model_name='person', 19 | name='feature_updates', 20 | field=models.BooleanField(default=True), 21 | ), 22 | migrations.AddField( 23 | model_name='person', 24 | name='job_opportunities', 25 | field=models.BooleanField(default=True), 26 | ), 27 | migrations.AddField( 28 | model_name='person', 29 | name='upcoming_events', 30 | field=models.BooleanField(default=True), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /frontend/components/MapToolbar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 45 | -------------------------------------------------------------------------------- /django/people/migrations/0008_auto_20180412_1544.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 15:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0007_auto_20180412_1543'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='person', 15 | name='avatar_url', 16 | field=models.URLField(blank=True, null=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='person', 20 | name='company', 21 | field=models.CharField(blank=True, max_length=128, null=True), 22 | ), 23 | migrations.AlterField( 24 | model_name='person', 25 | name='twitter_url', 26 | field=models.URLField(blank=True, null=True), 27 | ), 28 | migrations.AlterField( 29 | model_name='person', 30 | name='website_url', 31 | field=models.URLField(blank=True, null=True), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /nginx/config/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | 21 | access_log /var/log/nginx/access.log main; 22 | 23 | sendfile on; 24 | #tcp_nopush on; 25 | 26 | keepalive_timeout 65; 27 | 28 | gzip on; 29 | gzip_disable "msie6"; 30 | 31 | gzip_vary on; 32 | gzip_proxied any; 33 | gzip_comp_level 6; 34 | gzip_buffers 16 8k; 35 | gzip_http_version 1.1; 36 | gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; 37 | 38 | include /etc/nginx/conf.d/*.conf; 39 | } -------------------------------------------------------------------------------- /frontend/integrations/github/utilities.js: -------------------------------------------------------------------------------- 1 | export const gitHubGraphQlRequest = (token) => { 2 | const url = 'https://api.github.com/graphql'; 3 | const headers = { 4 | 'Authorization': `bearer ${token}` 5 | }; 6 | return { url, options: { headers } }; 7 | }; 8 | 9 | export const filterOutNonVueAndZeroStars = (repositories) => { 10 | return repositories.filter(r => { 11 | return r.node.stargazers.totalCount > 0 && 12 | r.node.languages.edges.reduce((prev, curr) => { 13 | return prev || curr.node.name === 'Vue'; 14 | }, false); 15 | }); 16 | }; 17 | 18 | export const gitHubAccessTokenLink = () => { 19 | const url = 'https://github.com/login/oauth/access_token'; 20 | const headers = { 21 | 'Accept': `application/json` 22 | }; 23 | return { url, options: { headers } }; 24 | }; 25 | 26 | export const profileMapper = (ghp) => { 27 | return ghp ? { 28 | name: ghp.name, 29 | avatarUrl: ghp.avatarUrl, 30 | email: ghp.email, 31 | githubUrl: ghp.url, 32 | websiteUrl: ghp.websiteUrl, 33 | company: ghp.company, 34 | about: ghp.bio 35 | } : {}; 36 | }; 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Pulilab 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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/icon-calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/gis/move_vertex_off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/site/static/admin/img/gis/move_vertex_on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/sk.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sk",[],function(){var e={2:function(e){return e?"dva":"dve"},3:function(){return"tri"},4:function(){return"štyri"}};return{inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím, zadajte o jeden znak menej":n>=2&&n<=4?"Prosím, zadajte o "+e[n](!0)+" znaky menej":"Prosím, zadajte o "+n+" znakov menej"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím, zadajte ešte jeden znak":n<=4?"Prosím, zadajte ešte ďalšie "+e[n](!0)+" znaky":"Prosím, zadajte ešte ďalších "+n+" znakov"},loadingMore:function(){return"Loading more results…"},maximumSelected:function(t){return t.maximum==1?"Môžete zvoliť len jednu položku":t.maximum>=2&&t.maximum<=4?"Môžete zvoliť najviac "+e[t.maximum](!1)+" položky":"Môžete zvoliť najviac "+t.maximum+" položiek"},noResults:function(){return"Nenašli sa žiadne položky"},searching:function(){return"Vyhľadávanie…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/admin/img/calendar-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/i18n/cs.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím zadejte o jeden znak méně":n<=4?"Prosím zadejte o "+e(n,!0)+" znaky méně":"Prosím zadejte o "+n+" znaků méně"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím zadejte ještě jeden znak":n<=4?"Prosím zadejte ještě další "+e(n,!0)+" znaky":"Prosím zadejte ještě dalších "+n+" znaků"},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky":"Můžete zvolit maximálně "+n+" položek"},noResults:function(){return"Nenalezeny žádné položky"},searching:function(){return"Vyhledávání…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/css/vendor/select2/LICENSE-SELECT2.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors 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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/vendor/select2/LICENSE-SELECT2.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors 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 | -------------------------------------------------------------------------------- /nginx/certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDRzCCAi+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2Nh 3 | bGhvc3QwHhcNMTgwNDA1MDkxODE4WhcNMjgwNDA1MDkxODE4WjAUMRIwEAYDVQQD 4 | Ewlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCM9Jnq 5 | ASTcygzeBudv2Y5ULSBtAKmS1fCRWbtmGJhyksG6joQloo0LQ/XNIGBkdZHcBFk7 6 | Oga1CvhR7W31BZ2iZ7YxLIZOIZ3USDDcTlMImpc5bYoPVdc6CMX7VOp6CWPkMtMR 7 | fLwJ+nciqolBjJm13gt/wOKElp0uluSKzEgvWUwUTkh0yxCnYSOJXRRN1jyg3q2x 8 | pxbpoEqkRpqLLB0A6mdnZutZ5QUasOEwUM5K8Y6oz1gtMk9DjItVnQ7lpjNCVtdg 9 | 52oPobiu2NckSRSsLUT4TLi/C5fI7t2qE8R2+C8dz4YTKhw9cKE0merm624eYmec 10 | xBqkK34lAMjSaf9fAgMBAAGjgaMwgaAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMC 11 | AvQwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYB 12 | BQUHAwQGCCsGAQUFBwMIMBEGCWCGSAGG+EIBAQQEAwIA9zAUBgNVHREEDTALgAls 13 | b2NhbGhvc3QwHQYDVR0OBBYEFP5dB1h9kItWOmk0iYAN6HWaeGVMMA0GCSqGSIb3 14 | DQEBCwUAA4IBAQAHT709jS+PkpFIs1kmvqfp7EntTne3m6fYUjTrbERd+rV0H/NF 15 | FdJYzVDT0E/CltU3vfuKUd8sYrkeMSxVMbibPoRKbp+lfvLpKoAO7W7xZUXpf8FV 16 | hghSXURMsv+gC6ibYDD+5+eiWu3mSFgu01kKg5oAPUv8/fsomPHNortxpSo9mrOP 17 | r7RT7v1edyDLr20riZx9m3QVPezxmfQGmlO1Gm5wtqLCqPA5iYlbjiK9RWutvQgC 18 | xWy6IprSmLwk8MvKexx0GOhG3JyhTF7kqdssdNAsUrOs3YWcAqpRGmQ+YzbgaGiO 19 | O5TqQ+z8GDIwE02NWHJbugMQy2H4gGi1tmNE 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /nginx/certs/chain.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDRzCCAi+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2Nh 3 | bGhvc3QwHhcNMTgwNDA1MDkxODE4WhcNMjgwNDA1MDkxODE4WjAUMRIwEAYDVQQD 4 | Ewlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCM9Jnq 5 | ASTcygzeBudv2Y5ULSBtAKmS1fCRWbtmGJhyksG6joQloo0LQ/XNIGBkdZHcBFk7 6 | Oga1CvhR7W31BZ2iZ7YxLIZOIZ3USDDcTlMImpc5bYoPVdc6CMX7VOp6CWPkMtMR 7 | fLwJ+nciqolBjJm13gt/wOKElp0uluSKzEgvWUwUTkh0yxCnYSOJXRRN1jyg3q2x 8 | pxbpoEqkRpqLLB0A6mdnZutZ5QUasOEwUM5K8Y6oz1gtMk9DjItVnQ7lpjNCVtdg 9 | 52oPobiu2NckSRSsLUT4TLi/C5fI7t2qE8R2+C8dz4YTKhw9cKE0merm624eYmec 10 | xBqkK34lAMjSaf9fAgMBAAGjgaMwgaAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMC 11 | AvQwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYB 12 | BQUHAwQGCCsGAQUFBwMIMBEGCWCGSAGG+EIBAQQEAwIA9zAUBgNVHREEDTALgAls 13 | b2NhbGhvc3QwHQYDVR0OBBYEFP5dB1h9kItWOmk0iYAN6HWaeGVMMA0GCSqGSIb3 14 | DQEBCwUAA4IBAQAHT709jS+PkpFIs1kmvqfp7EntTne3m6fYUjTrbERd+rV0H/NF 15 | FdJYzVDT0E/CltU3vfuKUd8sYrkeMSxVMbibPoRKbp+lfvLpKoAO7W7xZUXpf8FV 16 | hghSXURMsv+gC6ibYDD+5+eiWu3mSFgu01kKg5oAPUv8/fsomPHNortxpSo9mrOP 17 | r7RT7v1edyDLr20riZx9m3QVPezxmfQGmlO1Gm5wtqLCqPA5iYlbjiK9RWutvQgC 18 | xWy6IprSmLwk8MvKexx0GOhG3JyhTF7kqdssdNAsUrOs3YWcAqpRGmQ+YzbgaGiO 19 | O5TqQ+z8GDIwE02NWHJbugMQy2H4gGi1tmNE 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /nginx/dev_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDRzCCAi+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2Nh 3 | bGhvc3QwHhcNMTgwNDA1MDkxODE4WhcNMjgwNDA1MDkxODE4WjAUMRIwEAYDVQQD 4 | Ewlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCM9Jnq 5 | ASTcygzeBudv2Y5ULSBtAKmS1fCRWbtmGJhyksG6joQloo0LQ/XNIGBkdZHcBFk7 6 | Oga1CvhR7W31BZ2iZ7YxLIZOIZ3USDDcTlMImpc5bYoPVdc6CMX7VOp6CWPkMtMR 7 | fLwJ+nciqolBjJm13gt/wOKElp0uluSKzEgvWUwUTkh0yxCnYSOJXRRN1jyg3q2x 8 | pxbpoEqkRpqLLB0A6mdnZutZ5QUasOEwUM5K8Y6oz1gtMk9DjItVnQ7lpjNCVtdg 9 | 52oPobiu2NckSRSsLUT4TLi/C5fI7t2qE8R2+C8dz4YTKhw9cKE0merm624eYmec 10 | xBqkK34lAMjSaf9fAgMBAAGjgaMwgaAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMC 11 | AvQwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYB 12 | BQUHAwQGCCsGAQUFBwMIMBEGCWCGSAGG+EIBAQQEAwIA9zAUBgNVHREEDTALgAls 13 | b2NhbGhvc3QwHQYDVR0OBBYEFP5dB1h9kItWOmk0iYAN6HWaeGVMMA0GCSqGSIb3 14 | DQEBCwUAA4IBAQAHT709jS+PkpFIs1kmvqfp7EntTne3m6fYUjTrbERd+rV0H/NF 15 | FdJYzVDT0E/CltU3vfuKUd8sYrkeMSxVMbibPoRKbp+lfvLpKoAO7W7xZUXpf8FV 16 | hghSXURMsv+gC6ibYDD+5+eiWu3mSFgu01kKg5oAPUv8/fsomPHNortxpSo9mrOP 17 | r7RT7v1edyDLr20riZx9m3QVPezxmfQGmlO1Gm5wtqLCqPA5iYlbjiK9RWutvQgC 18 | xWy6IprSmLwk8MvKexx0GOhG3JyhTF7kqdssdNAsUrOs3YWcAqpRGmQ+YzbgaGiO 19 | O5TqQ+z8GDIwE02NWHJbugMQy2H4gGi1tmNE 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /nginx/dev_certs/chain.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDRzCCAi+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2Nh 3 | bGhvc3QwHhcNMTgwNDA1MDkxODE4WhcNMjgwNDA1MDkxODE4WjAUMRIwEAYDVQQD 4 | Ewlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCM9Jnq 5 | ASTcygzeBudv2Y5ULSBtAKmS1fCRWbtmGJhyksG6joQloo0LQ/XNIGBkdZHcBFk7 6 | Oga1CvhR7W31BZ2iZ7YxLIZOIZ3USDDcTlMImpc5bYoPVdc6CMX7VOp6CWPkMtMR 7 | fLwJ+nciqolBjJm13gt/wOKElp0uluSKzEgvWUwUTkh0yxCnYSOJXRRN1jyg3q2x 8 | pxbpoEqkRpqLLB0A6mdnZutZ5QUasOEwUM5K8Y6oz1gtMk9DjItVnQ7lpjNCVtdg 9 | 52oPobiu2NckSRSsLUT4TLi/C5fI7t2qE8R2+C8dz4YTKhw9cKE0merm624eYmec 10 | xBqkK34lAMjSaf9fAgMBAAGjgaMwgaAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMC 11 | AvQwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYB 12 | BQUHAwQGCCsGAQUFBwMIMBEGCWCGSAGG+EIBAQQEAwIA9zAUBgNVHREEDTALgAls 13 | b2NhbGhvc3QwHQYDVR0OBBYEFP5dB1h9kItWOmk0iYAN6HWaeGVMMA0GCSqGSIb3 14 | DQEBCwUAA4IBAQAHT709jS+PkpFIs1kmvqfp7EntTne3m6fYUjTrbERd+rV0H/NF 15 | FdJYzVDT0E/CltU3vfuKUd8sYrkeMSxVMbibPoRKbp+lfvLpKoAO7W7xZUXpf8FV 16 | hghSXURMsv+gC6ibYDD+5+eiWu3mSFgu01kKg5oAPUv8/fsomPHNortxpSo9mrOP 17 | r7RT7v1edyDLr20riZx9m3QVPezxmfQGmlO1Gm5wtqLCqPA5iYlbjiK9RWutvQgC 18 | xWy6IprSmLwk8MvKexx0GOhG3JyhTF7kqdssdNAsUrOs3YWcAqpRGmQ+YzbgaGiO 19 | O5TqQ+z8GDIwE02NWHJbugMQy2H4gGi1tmNE 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/autocomplete.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 'use strict'; 3 | var init = function($element, options) { 4 | var settings = $.extend({ 5 | ajax: { 6 | data: function(params) { 7 | return { 8 | term: params.term, 9 | page: params.page 10 | }; 11 | } 12 | } 13 | }, options); 14 | $element.select2(settings); 15 | }; 16 | 17 | $.fn.djangoAdminSelect2 = function(options) { 18 | var settings = $.extend({}, options); 19 | $.each(this, function(i, element) { 20 | var $element = $(element); 21 | init($element, settings); 22 | }); 23 | return this; 24 | }; 25 | 26 | $(function() { 27 | // Initialize all autocomplete widgets except the one in the template 28 | // form used when a new formset is added. 29 | $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2(); 30 | }); 31 | 32 | $(document).on('formset:added', (function() { 33 | return function(event, $newFormset) { 34 | return $newFormset.find('.admin-autocomplete').djangoAdminSelect2(); 35 | }; 36 | })(this)); 37 | }(django.jQuery)); 38 | -------------------------------------------------------------------------------- /django/people/migrations/0022_auto_20180628_1515.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-06-28 13:15 2 | 3 | import django.contrib.postgres.fields.jsonb 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('people', '0021_remove_person_news_opt_in'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='MeetupEvent', 17 | fields=[ 18 | ('id', models.CharField(editable=False, max_length=512, primary_key=True, serialize=False)), 19 | ('date', models.DateTimeField(verbose_name='UTC Date')), 20 | ('data', django.contrib.postgres.fields.jsonb.JSONField(default={})), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='MeetupGroup', 25 | fields=[ 26 | ('id', models.IntegerField(editable=False, primary_key=True, serialize=False)), 27 | ('data', django.contrib.postgres.fields.jsonb.JSONField(default={})), 28 | ], 29 | ), 30 | migrations.AddField( 31 | model_name='meetupevent', 32 | name='group', 33 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='people.MeetupGroup'), 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/docs/css/jquery.json-view.min.css: -------------------------------------------------------------------------------- 1 | .json-view{position:relative} 2 | .json-view .collapser{width:20px;height:18px;display:block;position:absolute;left:-1.7em;top:-.2em;z-index:5;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYGBgOADE%2F3Hgw0DM4IRHgSsDFOzFInmMAQnY49ONzZRjDFiADT7dMLALiE8y4AGW6LoBAgwAuIkf%2F%2FB7O9sAAAAASUVORK5CYII%3D);background-repeat:no-repeat;background-position:center center;opacity:.5;cursor:pointer} 3 | .json-view .collapsed{-ms-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-khtml-transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)} 4 | .json-view .bl{display:block;padding-left:20px;margin-left:-20px;position:relative} 5 | .json-view{font-family:monospace} 6 | .json-view ul{list-style-type:none;padding-left:2em;border-left:1px dotted;margin:.3em} 7 | .json-view ul li{position:relative} 8 | .json-view .comments,.json-view .dots{display:none;-moz-user-select:none;-ms-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none} 9 | .json-view .comments{padding-left:.8em;font-style:italic;color:#888} 10 | .json-view .bool,.json-view .null,.json-view .num,.json-view .undef{font-weight:700;color:#1A01CC} 11 | .json-view .str{color:#800} -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /frontend/utilities/parsers.js: -------------------------------------------------------------------------------- 1 | export const apiReadParser = data => { 2 | const r = {}; 3 | if (data.location) { 4 | r.location = { 5 | lat: data.location.coordinates[0], 6 | lng: data.location.coordinates[1] 7 | }; 8 | } 9 | if (data.user) { 10 | r.email = data.user.email || undefined; 11 | r.name = data.user.first_name || undefined; 12 | r.user = undefined; 13 | } 14 | r.type = data.type ? data.type : 1; 15 | return {...data, ...r}; 16 | }; 17 | 18 | export const apiWriteParser = data => { 19 | const r = { 20 | location: undefined, 21 | user: { 22 | last_name: '' 23 | } 24 | }; 25 | if (data.location) { 26 | r.location = { 27 | type: 'Point', 28 | coordinates: [data.location.lat, data.location.lng] 29 | }; 30 | } 31 | r.user.email = data.email ? data.email : ''; 32 | data.email = undefined; 33 | if (data.name) { 34 | r.user.first_name = data.name; 35 | data.name = undefined; 36 | } 37 | return {...data, ...r}; 38 | }; 39 | 40 | export const latLngParser = p => { 41 | if (p.location && p.location.lat) { 42 | return { 43 | lat: p.location.lat, 44 | lng: p.location.lng 45 | }; 46 | } 47 | }; 48 | 49 | export const personParser = (d) => { 50 | const p = apiReadParser(d); 51 | const latlng = latLngParser(p); 52 | const type = p.type ? p.type : 1; 53 | return { 54 | ...p, 55 | latlng, 56 | type, 57 | location: undefined 58 | }; 59 | } 60 | ; 61 | -------------------------------------------------------------------------------- /django/people/migrations/0004_auto_20180412_1526.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 15:26 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0003_auto_20180412_1354'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RenameField( 15 | model_name='person', 16 | old_name='organisation', 17 | new_name='company', 18 | ), 19 | migrations.AddField( 20 | model_name='person', 21 | name='created', 22 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), 23 | preserve_default=False, 24 | ), 25 | migrations.AddField( 26 | model_name='person', 27 | name='github_created', 28 | field=models.DateTimeField(blank=True, null=True), 29 | ), 30 | migrations.AddField( 31 | model_name='person', 32 | name='hireable', 33 | field=models.NullBooleanField(), 34 | ), 35 | migrations.AddField( 36 | model_name='person', 37 | name='modified', 38 | field=models.DateTimeField(auto_now=True), 39 | ), 40 | migrations.AlterField( 41 | model_name='person', 42 | name='github_login', 43 | field=models.CharField(max_length=32), 44 | ), 45 | ] 46 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/js/default.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | // JSON highlighting. 3 | prettyPrint(); 4 | 5 | // Bootstrap tooltips. 6 | $('.js-tooltip').tooltip({ 7 | delay: 1000, 8 | container: 'body' 9 | }); 10 | 11 | // Deal with rounded tab styling after tab clicks. 12 | $('a[data-toggle="tab"]:first').on('shown', function(e) { 13 | $(e.target).parents('.tabbable').addClass('first-tab-active'); 14 | }); 15 | 16 | $('a[data-toggle="tab"]:not(:first)').on('shown', function(e) { 17 | $(e.target).parents('.tabbable').removeClass('first-tab-active'); 18 | }); 19 | 20 | $('a[data-toggle="tab"]').click(function() { 21 | document.cookie = "tabstyle=" + this.name + "; path=/"; 22 | }); 23 | 24 | // Store tab preference in cookies & display appropriate tab on load. 25 | var selectedTab = null; 26 | var selectedTabName = getCookie('tabstyle'); 27 | 28 | if (selectedTabName) { 29 | selectedTabName = selectedTabName.replace(/[^a-z-]/g, ''); 30 | } 31 | 32 | if (selectedTabName) { 33 | selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); 34 | } 35 | 36 | if (selectedTab && selectedTab.length > 0) { 37 | // Display whichever tab is selected. 38 | selectedTab.tab('show'); 39 | } else { 40 | // If no tab selected, display rightmost tab. 41 | $('.form-switcher a:first').tab('show'); 42 | } 43 | 44 | $(window).load(function() { 45 | $('#errorModal').modal('show'); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/css/default.css: -------------------------------------------------------------------------------- 1 | /* The navbar is fixed at >= 980px wide, so add padding to the body to prevent 2 | content running up underneath it. */ 3 | 4 | h1 { 5 | font-weight: 300; 6 | } 7 | 8 | h2, h3 { 9 | font-weight: 300; 10 | } 11 | 12 | .resource-description, .response-info { 13 | margin-bottom: 2em; 14 | } 15 | 16 | .version:before { 17 | content: "v"; 18 | opacity: 0.6; 19 | padding-right: 0.25em; 20 | } 21 | 22 | .version { 23 | font-size: 70%; 24 | } 25 | 26 | .format-option { 27 | font-family: Menlo, Consolas, "Andale Mono", "Lucida Console", monospace; 28 | } 29 | 30 | .button-form { 31 | float: right; 32 | margin-right: 1em; 33 | } 34 | 35 | td.nested { 36 | padding: 0 !important; 37 | } 38 | 39 | td.nested > table { 40 | margin: 0; 41 | } 42 | 43 | form select, form input, form textarea { 44 | width: 90%; 45 | } 46 | 47 | form select[multiple] { 48 | height: 150px; 49 | } 50 | 51 | /* To allow tooltips to work on disabled elements */ 52 | .disabled-tooltip-shield { 53 | position: absolute; 54 | top: 0; 55 | right: 0; 56 | bottom: 0; 57 | left: 0; 58 | } 59 | 60 | .errorlist { 61 | margin-top: 0.5em; 62 | } 63 | 64 | pre { 65 | overflow: auto; 66 | word-wrap: normal; 67 | white-space: pre; 68 | font-size: 12px; 69 | } 70 | 71 | .page-header { 72 | border-bottom: none; 73 | padding-bottom: 0px; 74 | } 75 | 76 | #filtersModal form input[type=submit] { 77 | width: auto; 78 | } 79 | 80 | #filtersModal .modal-body h2 { 81 | margin-top: 0 82 | } 83 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | - image: circleci/node:8.11 10 | working_directory: ~/vue-people 11 | 12 | steps: 13 | - checkout 14 | - restore_cache: 15 | keys: 16 | - v1-dependencies-{{ checksum "frontend/package.json" }} 17 | - v1-dependencies- 18 | 19 | - run: 20 | working_directory: ~/vue-people/frontend 21 | command: yarn install 22 | 23 | - save_cache: 24 | paths: 25 | - frontend/node_modules 26 | key: v1-dependencies-{{ checksum "frontend/package.json" }} 27 | 28 | # run tests! 29 | - run: 30 | working_directory: ~/vue-people/frontend 31 | command: yarn test 32 | 33 | - deploy: 34 | command: | 35 | if [ "${CIRCLE_BRANCH}" == "master" ]; then 36 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \ 37 | ${SERVER_USER}@167.99.245.180 \ 38 | 'cd ~/vue-people/ \ 39 | && docker-compose down \ 40 | && docker-compose -f docker-compose-wip.yml up -d \ 41 | && git pull \ 42 | && docker-compose build \ 43 | && docker-compose -f docker-compose-wip.yml down \ 44 | && docker-compose -f docker-compose.yml -f docker-compose-production.yml up -d \ 45 | && docker-compose exec -T django python manage.py migrate' 46 | fi 47 | -------------------------------------------------------------------------------- /frontend/integrations/github/queries.js: -------------------------------------------------------------------------------- 1 | export const gitHubUserProfile = () => ({ 2 | query: `{ viewer { 3 | name, 4 | email, 5 | login, 6 | avatarUrl, 7 | bio, 8 | company, 9 | url, 10 | websiteUrl 11 | } 12 | }` 13 | }); 14 | 15 | export const gitHubUserRepositories = (login) => ({ 16 | query: `{ user(login: "${login}") { 17 | repositories(first: 100, privacy: PUBLIC) { 18 | edges { 19 | node { 20 | id, 21 | name, 22 | url, 23 | stargazers { 24 | totalCount 25 | }, 26 | languages(first: 5 orderBy: {field: SIZE, direction: DESC}) { 27 | edges { 28 | node { 29 | name 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | repositoriesContributedTo(first: 100 privacy: PUBLIC) { 37 | edges { 38 | node { 39 | id, 40 | name, 41 | url, 42 | stargazers { 43 | totalCount 44 | }, 45 | languages(first: 5, orderBy: {field: SIZE, direction: DESC}) { 46 | edges { 47 | node { 48 | name 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | }` 57 | }); 58 | -------------------------------------------------------------------------------- /nginx/site/static/admin/js/collapse.min.js: -------------------------------------------------------------------------------- 1 | var $jscomp={scope:{},findInternal:function(a,c,b){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e'+gettext("Show")+")")});a("fieldset.collapse a.collapse-toggle").click(function(c){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", 5 | [a(this).attr("id")]);return!1})})})(django.jQuery); 6 | -------------------------------------------------------------------------------- /frontend/components/BottomButtons.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 58 | 59 | 71 | -------------------------------------------------------------------------------- /nginx/site/static/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 | -------------------------------------------------------------------------------- /frontend/components/CustomMarkerCluster.vue: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /frontend/utilities/auth.js: -------------------------------------------------------------------------------- 1 | import Cookie from 'js-cookie'; 2 | 3 | export const safeSaveToken = (name, value) => { 4 | if (value) { 5 | if (process.client) { 6 | window.localStorage.setItem(name, value); 7 | } 8 | Cookie.set(name, value, { expires: 365 }); 9 | } 10 | }; 11 | 12 | export const saveTokens = (github, cookieAccepted) => { 13 | if (process.SERVER_BUILD) return; 14 | safeSaveToken('github_token', github); 15 | safeSaveToken('cookie_accepted', cookieAccepted); 16 | }; 17 | 18 | export const deleteTokens = (tokens = ['github_token', 'csrftoken']) => { 19 | if (process.SERVER_BUILD) { 20 | return; 21 | } 22 | if (process.client) { 23 | tokens.forEach(t => window.localStorage.removeItem(t)); 24 | } 25 | tokens.forEach(t => Cookie.remove(t)); 26 | }; 27 | 28 | export const getValueFromCookie = (req, value) => { 29 | let result = req.headers.cookie.split(';').find(c => c.trim().startsWith(`${value}=`)); 30 | return result ? result.split('=')[1] : null; 31 | }; 32 | 33 | export const getTokensFromCookie = (req) => { 34 | if (!req.headers.cookie) return; 35 | const github = getValueFromCookie(req, 'github_token'); 36 | const csrftoken = getValueFromCookie(req, 'csrftoken'); 37 | const cookieAccepted = getValueFromCookie(req, 'cookie_accepted'); 38 | return { 39 | github, 40 | csrftoken, 41 | cookieAccepted 42 | }; 43 | }; 44 | 45 | export const getTokensFromLocalStorage = () => { 46 | const github = window.localStorage.getItem('github_token'); 47 | const csrftoken = window.localStorage.getItem('csrftoken'); 48 | return { 49 | github, 50 | csrftoken 51 | }; 52 | }; 53 | 54 | export const oauthLinkGenerator = provider => { 55 | return `/accounts/${provider}/login/`; 56 | }; 57 | -------------------------------------------------------------------------------- /nginx/certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAjPSZ6gEk3MoM3gbnb9mOVC0gbQCpktXwkVm7ZhiYcpLBuo6E 3 | JaKNC0P1zSBgZHWR3ARZOzoGtQr4Ue1t9QWdome2MSyGTiGd1Egw3E5TCJqXOW2K 4 | D1XXOgjF+1Tqeglj5DLTEXy8Cfp3IqqJQYyZtd4Lf8DihJadLpbkisxIL1lMFE5I 5 | dMsQp2EjiV0UTdY8oN6tsacW6aBKpEaaiywdAOpnZ2brWeUFGrDhMFDOSvGOqM9Y 6 | LTJPQ4yLVZ0O5aYzQlbXYOdqD6G4rtjXJEkUrC1E+Ey4vwuXyO7dqhPEdvgvHc+G 7 | EyocPXChNJnq5utuHmJnnMQapCt+JQDI0mn/XwIDAQABAoIBAHCfAtcIIPbT7SH8 8 | 7C/e6Kxy/eR50gNDMpo/0s5JOozYmzc+pXEYho8Itr01cC70DR14z/7VZwSCBlQX 9 | W3KZERDwwo5Zn9mPXHZ/U6LCZe+K7ObykYudL4Dr/5vHhi0JkamDvkbLncZvO38U 10 | OOSJvUqlZhSl/ZaobGAMKgCIJejFu10HWzUTZxVTLYOgLXXDvwB7jwLcYFhNwFmc 11 | 0KH91HmsgiPM9v7vgLBzhvrxQI4dPvHpw8jasWfSu0ow1BiLYkUVzO0pnsZgEUol 12 | kxYdDA1vJsiDZJ1yekwkR7cmPYKDJoDC0IPByGQyFAyeB+ii7VWCR1QHi8m1aAlL 13 | ruYPA2kCgYEA9q8NcrYlZKZwA/MgJmW56qVMJDFPgQHOW+yppo0fYTMqPp8qaPU1 14 | 9Of9B/JuCevMVdLGEKDYx+kDKNgzr0KGIj+xxHUlf6DpeYRG8kL5FAVFjcqOlP4Y 15 | 3NZPhucxenkhDvR43iAY2U6rL7q5VXLqEEqvTMiwJubDTYfQp9ZPsQ0CgYEAkkdd 16 | FQZwOaIJTg6qAZhCKC5bJxby/VPyzXX5KE9ZxrkhDeV2RLR9FJutThmYEP/Olu2R 17 | c+1Vl4OVZrWdSHT2IoUvLUcFjf4GfHj/wyNcjnpe5vNCoSkwmyWhgWLKuJhveFbH 18 | m+U68aOt+uGnk3rJgCov4F6pImYUY8I+In3o3xsCgYBI+DsoJY0mGr2jzXVDJnA6 19 | Yq8FPbjg1mHuezEr3S7dF791SqvG5FITodQNV78JHEETWgfQv2OgruKKYHnZa/o1 20 | g1XYYTa4bCbkFSbPXtRDAdQzPymyFP/Rw2s+4oCWU+JdhB+ExUXUchrdQYS1LArk 21 | ytLUCcFhoH1BH92u6AwmuQKBgBXagYaFmriRg7Bcsntggum72zRg0laUdx7I9DtY 22 | d4If4FdSI7Qp/tHYXDDh0/0eKoAT5/oYyAPFzck1ctUDXE5V9XFfPe4zuEZaUWOW 23 | 4doLYChCyX6IR9G18oYA+2ZOp8CjvliFC6RUHsbvdXisDHSQWOy9V6li9MLenyfK 24 | BcNnAoGBAM3Lt14RfqXoZhGs05j0j+Su9p7bvhrF8GM+zVeGtut92wPoMHEACknO 25 | gWh9ThjGL/sKACYEoPPkWc2BZxdO9eqP4HCmt8slo8H6Jjr8Qoi0IReH3Uq/DeV8 26 | FqGovk4Gp4dnmnG/nYLSe7n7KR6f9G458hwRMfZoPrNnfgkKOZfJ 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /nginx/dev_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAjPSZ6gEk3MoM3gbnb9mOVC0gbQCpktXwkVm7ZhiYcpLBuo6E 3 | JaKNC0P1zSBgZHWR3ARZOzoGtQr4Ue1t9QWdome2MSyGTiGd1Egw3E5TCJqXOW2K 4 | D1XXOgjF+1Tqeglj5DLTEXy8Cfp3IqqJQYyZtd4Lf8DihJadLpbkisxIL1lMFE5I 5 | dMsQp2EjiV0UTdY8oN6tsacW6aBKpEaaiywdAOpnZ2brWeUFGrDhMFDOSvGOqM9Y 6 | LTJPQ4yLVZ0O5aYzQlbXYOdqD6G4rtjXJEkUrC1E+Ey4vwuXyO7dqhPEdvgvHc+G 7 | EyocPXChNJnq5utuHmJnnMQapCt+JQDI0mn/XwIDAQABAoIBAHCfAtcIIPbT7SH8 8 | 7C/e6Kxy/eR50gNDMpo/0s5JOozYmzc+pXEYho8Itr01cC70DR14z/7VZwSCBlQX 9 | W3KZERDwwo5Zn9mPXHZ/U6LCZe+K7ObykYudL4Dr/5vHhi0JkamDvkbLncZvO38U 10 | OOSJvUqlZhSl/ZaobGAMKgCIJejFu10HWzUTZxVTLYOgLXXDvwB7jwLcYFhNwFmc 11 | 0KH91HmsgiPM9v7vgLBzhvrxQI4dPvHpw8jasWfSu0ow1BiLYkUVzO0pnsZgEUol 12 | kxYdDA1vJsiDZJ1yekwkR7cmPYKDJoDC0IPByGQyFAyeB+ii7VWCR1QHi8m1aAlL 13 | ruYPA2kCgYEA9q8NcrYlZKZwA/MgJmW56qVMJDFPgQHOW+yppo0fYTMqPp8qaPU1 14 | 9Of9B/JuCevMVdLGEKDYx+kDKNgzr0KGIj+xxHUlf6DpeYRG8kL5FAVFjcqOlP4Y 15 | 3NZPhucxenkhDvR43iAY2U6rL7q5VXLqEEqvTMiwJubDTYfQp9ZPsQ0CgYEAkkdd 16 | FQZwOaIJTg6qAZhCKC5bJxby/VPyzXX5KE9ZxrkhDeV2RLR9FJutThmYEP/Olu2R 17 | c+1Vl4OVZrWdSHT2IoUvLUcFjf4GfHj/wyNcjnpe5vNCoSkwmyWhgWLKuJhveFbH 18 | m+U68aOt+uGnk3rJgCov4F6pImYUY8I+In3o3xsCgYBI+DsoJY0mGr2jzXVDJnA6 19 | Yq8FPbjg1mHuezEr3S7dF791SqvG5FITodQNV78JHEETWgfQv2OgruKKYHnZa/o1 20 | g1XYYTa4bCbkFSbPXtRDAdQzPymyFP/Rw2s+4oCWU+JdhB+ExUXUchrdQYS1LArk 21 | ytLUCcFhoH1BH92u6AwmuQKBgBXagYaFmriRg7Bcsntggum72zRg0laUdx7I9DtY 22 | d4If4FdSI7Qp/tHYXDDh0/0eKoAT5/oYyAPFzck1ctUDXE5V9XFfPe4zuEZaUWOW 23 | 4doLYChCyX6IR9G18oYA+2ZOp8CjvliFC6RUHsbvdXisDHSQWOy9V6li9MLenyfK 24 | BcNnAoGBAM3Lt14RfqXoZhGs05j0j+Su9p7bvhrF8GM+zVeGtut92wPoMHEACknO 25 | gWh9ThjGL/sKACYEoPPkWc2BZxdO9eqP4HCmt8slo8H6Jjr8Qoi0IReH3Uq/DeV8 26 | FqGovk4Gp4dnmnG/nYLSe7n7KR6f9G458hwRMfZoPrNnfgkKOZfJ 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /django/people/adapter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from django.conf import settings 3 | from allauth.socialaccount.adapter import DefaultSocialAccountAdapter 4 | from allauth.account.adapter import DefaultAccountAdapter 5 | 6 | from .models import Person 7 | 8 | 9 | class MyAccountAdapter(DefaultAccountAdapter): 10 | def get_login_redirect_url(self, request): 11 | if not hasattr(request, 'sociallogin'): 12 | return '/' 13 | 14 | path = settings.LOGIN_REDIRECT_URL 15 | if settings.DEBUG: 16 | path = settings.LOGIN_REDIRECT_URL_DEV 17 | 18 | 19 | return path.format(request.sociallogin.token.token) 20 | 21 | class MyGithubAccountAdapter(DefaultSocialAccountAdapter): 22 | def pre_social_login(self, request, sociallogin): 23 | request.sociallogin = sociallogin 24 | 25 | def save_user(self, request, sociallogin, form=None): 26 | user = super().save_user(request, sociallogin, form) 27 | try: 28 | data = { 29 | "user": user, 30 | "github_url": sociallogin.account.extra_data['html_url'], 31 | "avatar_url": sociallogin.account.extra_data['avatar_url'], 32 | "company": sociallogin.account.extra_data['company'], 33 | "hireable": sociallogin.account.extra_data['hireable'], 34 | "bio": sociallogin.account.extra_data['bio'], 35 | "github_created": sociallogin.account.extra_data['created_at'], 36 | "github_updated": sociallogin.account.extra_data['updated_at'], 37 | } 38 | Person.objects.get_or_create(github_login=sociallogin.account.extra_data['login'], defaults=data) 39 | except Exception as e: 40 | logging.error(e) 41 | return user 42 | -------------------------------------------------------------------------------- /frontend/components/TooltipGroup.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 48 | 49 | 78 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/js/csrf.js: -------------------------------------------------------------------------------- 1 | function getCookie(name) { 2 | var cookieValue = null; 3 | 4 | if (document.cookie && document.cookie != '') { 5 | var cookies = document.cookie.split(';'); 6 | 7 | for (var i = 0; i < cookies.length; i++) { 8 | var cookie = jQuery.trim(cookies[i]); 9 | 10 | // Does this cookie string begin with the name we want? 11 | if (cookie.substring(0, name.length + 1) == (name + '=')) { 12 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 13 | break; 14 | } 15 | } 16 | } 17 | 18 | return cookieValue; 19 | } 20 | 21 | function csrfSafeMethod(method) { 22 | // these HTTP methods do not require CSRF protection 23 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 24 | } 25 | 26 | function sameOrigin(url) { 27 | // test that a given url is a same-origin URL 28 | // url could be relative or scheme relative or absolute 29 | var host = document.location.host; // host + port 30 | var protocol = document.location.protocol; 31 | var sr_origin = '//' + host; 32 | var origin = protocol + sr_origin; 33 | 34 | // Allow absolute or scheme relative URLs to same origin 35 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || 36 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || 37 | // or any other URL that isn't scheme relative or absolute i.e relative. 38 | !(/^(\/\/|http:|https:).*/.test(url)); 39 | } 40 | 41 | var csrftoken = getCookie(window.drf.csrfCookieName); 42 | 43 | $.ajaxSetup({ 44 | beforeSend: function(xhr, settings) { 45 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) { 46 | // Send the token to same-origin, relative URLs only. 47 | // Send the token only if the method warrants CSRF protection 48 | // Using the CSRFToken value acquired earlier 49 | xhr.setRequestHeader(window.drf.csrfHeaderName, csrftoken); 50 | } 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | django: 5 | restart: always 6 | build: ./django 7 | expose: 8 | - "8000" 9 | links: 10 | - postgis 11 | - redis 12 | volumes: 13 | - ./django:/src 14 | - ./frontend:/frontend 15 | - ./nginx/site/static:/src/static:rw 16 | - ./django/media:/usr/share/app/media:rw 17 | - log_data:/var/log/django 18 | - media_data:/usr/src/app/media 19 | command: /usr/local/bin/daphne -b 0.0.0.0 -p 8000 asgi:application 20 | 21 | celery: 22 | restart: always 23 | build: ./django 24 | environment: 25 | - C_FORCE_ROOT=true 26 | links: 27 | - postgis 28 | - redis 29 | volumes: 30 | - ./django:/src 31 | - log_data:/var/log/django 32 | command: /usr/local/bin/celery -A peoplebackend worker -B -l info 33 | 34 | nuxt: 35 | restart: always 36 | build: ./frontend 37 | expose: 38 | - "3000" 39 | links: 40 | - django 41 | 42 | nginx: 43 | restart: always 44 | image: nginx:1.15.6 45 | ports: 46 | - "80:80" 47 | - "443:443" 48 | volumes: 49 | - ./django/media:/usr/share/nginx/html/media:rw 50 | - ./nginx/site:/usr/share/nginx/html:rw 51 | - ./nginx/conf.d:/etc/nginx/conf.d:ro 52 | - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro 53 | - ./nginx/certs:/etc/letsencrypt:rw 54 | - ./nginx/certs-data:/data/letsencrypt:rw 55 | links: 56 | - django 57 | - nuxt 58 | 59 | postgis: 60 | restart: always 61 | image: mdillon/postgis:10 62 | environment: 63 | - POSTGRES_DB=postgres 64 | - POSTGRES_USER=postgres 65 | - POSTGRES_PASSWORD=postgres 66 | volumes: 67 | - postgres_data:/var/lib/postgresql/data 68 | expose: 69 | - "5432" 70 | 71 | redis: 72 | restart: always 73 | image: redis:4.0.10 74 | expose: 75 | - "6379" 76 | 77 | volumes: 78 | postgres_data: 79 | log_data: 80 | media_data: 81 | -------------------------------------------------------------------------------- /frontend/components/AppDialogs.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 71 | 72 | 75 | -------------------------------------------------------------------------------- /nginx/conf.wip.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | server_name vuepeople.pulilab.com vuepeople.org www.vuepeople.org; 5 | charset utf-8; 6 | client_max_body_size 10M; 7 | 8 | index index.html; 9 | 10 | location / { 11 | rewrite ^ https://$host$request_uri? permanent; 12 | } 13 | 14 | } 15 | 16 | server { 17 | listen 443 ssl http2; 18 | listen [::]:443 ssl http2; 19 | server_name vuepeople.pulilab.com vuepeople.org www.vuepeople.org; 20 | 21 | location = /favicon.png { 22 | alias /usr/share/nginx/html/favicon.png; 23 | } 24 | 25 | location = /logo-vertical.png { 26 | alias /usr/share/nginx/html/logo-vertical.png; 27 | } 28 | 29 | location / { 30 | root /usr/share/nginx/html/; 31 | index index.html; 32 | try_files '' /index.html =404; 33 | 34 | add_header Last-Modified $date_gmt; 35 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 36 | if_modified_since off; 37 | expires off; 38 | etag off; 39 | } 40 | 41 | 42 | 43 | ssl on; 44 | 45 | add_header Strict-Transport-Security "max-age=31536000" always; 46 | 47 | ssl_session_cache shared:SSL:20m; 48 | ssl_session_timeout 10m; 49 | 50 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 51 | ssl_prefer_server_ciphers on; 52 | ssl_ciphers "ECDH+AESGCM:ECDH+AES256:ECDH+AES128:!ADH:!AECDH:!MD5;"; 53 | 54 | ssl_stapling on; 55 | ssl_stapling_verify on; 56 | resolver 8.8.8.8 8.8.4.4; 57 | 58 | ssl_certificate /etc/letsencrypt/cert.pem; 59 | ssl_certificate_key /etc/letsencrypt/key.pem; 60 | ssl_trusted_certificate /etc/letsencrypt/chain.pem; 61 | 62 | 63 | access_log /dev/stdout; 64 | error_log /dev/stderr info; 65 | 66 | } 67 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-people", 3 | "version": "1.0.0", 4 | "description": "vue people", 5 | "author": "nico@pulilab.com", 6 | "private": true, 7 | "scripts": { 8 | "dev": "HOST=0.0.0.0 nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", 13 | "precommit": "npm run lint", 14 | "test": "jest", 15 | "test:w": "jest --watch", 16 | "cypress:open": "cypress open" 17 | }, 18 | "dependencies": { 19 | "@nuxtjs/axios": "^5.1.1", 20 | "@nuxtjs/google-analytics": "^2.0.2", 21 | "@nuxtjs/proxy": "^1.1.4", 22 | "date-fns": "^1.29.0", 23 | "django-channels": "^1.1.6", 24 | "dotenv": "^5.0.1", 25 | "js-cookie": "^2.2.0", 26 | "leaflet": "1.3.1", 27 | "leaflet.markercluster": "^1.4.1", 28 | "lodash": "^4.17.10", 29 | "mdi": "^2.2.43", 30 | "nuxt": "^1.4.2", 31 | "nuxt-device-detect": "^1.1.1", 32 | "nuxt-mq": "^1.0.5", 33 | "qs": "^6.6.0", 34 | "vee-validate": "^2.0.6", 35 | "vue": "2.5.17", 36 | "vue-django-feedback": "^1.5.1", 37 | "vue-no-ssr": "^0.2.2", 38 | "vue2-leaflet": "1.2.3-beta.8", 39 | "vuetify": "1.0.10", 40 | "vuex-geolocation": "^1.1.0" 41 | }, 42 | "devDependencies": { 43 | "babel-core": "^6.26.0", 44 | "babel-eslint": "^8.2.1", 45 | "babel-jest": "^22.4.3", 46 | "cypress": "^2.1.0", 47 | "eslint": "^4.15.0", 48 | "eslint-config-standard": "^11.0.0", 49 | "eslint-friendly-formatter": "^3.0.0", 50 | "eslint-loader": "^1.7.1", 51 | "eslint-plugin-import": "^2.11.0", 52 | "eslint-plugin-node": "^6.0.1", 53 | "eslint-plugin-promise": "^3.7.0", 54 | "eslint-plugin-standard": "^3.0.1", 55 | "eslint-plugin-vue": "^4.0.0", 56 | "jest": "^22.4.3", 57 | "less": "^3.0.1", 58 | "less-loader": "^4.1.0", 59 | "regenerator-runtime": "^0.11.1", 60 | "stylus": "^0.54.5", 61 | "stylus-loader": "^3.0.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /frontend/assets/images/logo-pulilab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | pulilab logo 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frontend/static/logo-pulilab-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | pulilab logo 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frontend/tests/store/layout.test.js: -------------------------------------------------------------------------------- 1 | import { state, getters, actions, mutations } from '~/store/layout'; 2 | import { mockAxios } from '../utils'; 3 | 4 | test('store state is unique between calls', () => { 5 | const s = state(); 6 | expect(s).not.toBe(state()); 7 | expect(s).toEqual(state()); 8 | }); 9 | 10 | describe('getters', () => { 11 | let s = null; 12 | 13 | beforeEach(() => { 14 | s = state(); 15 | }); 16 | 17 | test('getOptOutDialogOpen', () => { 18 | let result = getters.getOptOutDialogOpen(s); 19 | expect(result).toEqual(s.optOutDialogOpen); 20 | s.optOutDialogOpen = true; 21 | result = getters.getOptOutDialogOpen(s); 22 | expect(result).toEqual(s.optOutDialogOpen); 23 | }); 24 | test('getSettingsDialogOpen', () => { 25 | let result = getters.getSettingsDialogOpen(s); 26 | expect(result).toEqual(s.settingsDialogOpen); 27 | s.settingsDialogOpen = true; 28 | result = getters.getSettingsDialogOpen(s); 29 | expect(result).toEqual(s.settingsDialogOpen); 30 | }); 31 | }); 32 | 33 | describe('actions', () => { 34 | const vuex = {}; 35 | 36 | beforeEach(() => { 37 | vuex.commit = jest.fn(); 38 | vuex.dispatch = jest.fn(); 39 | vuex.getters = {}; 40 | vuex.state = state(); 41 | actions.$axios = mockAxios(); 42 | }); 43 | 44 | test('setOptOutDialogOpen', () => { 45 | actions.setOptOutDialogOpen(vuex, true); 46 | expect(vuex.commit).toHaveBeenLastCalledWith('SET_OPT_OUT_DIALOG_OPEN', true); 47 | }); 48 | test('setSettingsDialogOpen', () => { 49 | actions.setSettingsDialogOpen(vuex, true); 50 | expect(vuex.commit).toHaveBeenLastCalledWith('SET_SETTINGS_DIALOG_OPEN', true); 51 | }); 52 | }); 53 | 54 | describe('mutations', () => { 55 | test('SET_OPT_OUT_DIALOG_OPEN', () => { 56 | const state = {}; 57 | mutations.SET_OPT_OUT_DIALOG_OPEN(state, 1); 58 | expect(state.optOutDialogOpen).toEqual(1); 59 | }); 60 | test('SET_SETTINGS_DIALOG_OPEN', () => { 61 | const state = {}; 62 | mutations.SET_SETTINGS_DIALOG_OPEN(state, 1); 63 | expect(state.settingsDialogOpen).toEqual(1); 64 | }); 65 | 66 | }); 67 | -------------------------------------------------------------------------------- /frontend/static/icon-vueman.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 | Created with Sketch. 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 | -------------------------------------------------------------------------------- /frontend/components/CookieWarning.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 45 | 46 | 89 | -------------------------------------------------------------------------------- /nginx/site/static/admin/css/responsive_rtl.css: -------------------------------------------------------------------------------- 1 | /* TABLETS */ 2 | 3 | @media (max-width: 1024px) { 4 | [dir="rtl"] .colMS { 5 | margin-right: 0; 6 | } 7 | 8 | [dir="rtl"] #user-tools { 9 | text-align: right; 10 | } 11 | 12 | [dir="rtl"] #changelist .actions label { 13 | padding-left: 10px; 14 | padding-right: 0; 15 | } 16 | 17 | [dir="rtl"] #changelist .actions select { 18 | margin-left: 0; 19 | margin-right: 15px; 20 | } 21 | 22 | [dir="rtl"] .change-list .filtered .results, 23 | [dir="rtl"] .change-list .filtered .paginator, 24 | [dir="rtl"] .filtered #toolbar, 25 | [dir="rtl"] .filtered div.xfull, 26 | [dir="rtl"] .filtered .actions { 27 | margin-right: 0; 28 | margin-left: 230px; 29 | } 30 | 31 | [dir="rtl"] .inline-group ul.tools a.add, 32 | [dir="rtl"] .inline-group div.add-row a, 33 | [dir="rtl"] .inline-group .tabular tr.add-row td a { 34 | padding: 8px 26px 8px 10px; 35 | background-position: calc(100% - 8px) 9px; 36 | } 37 | 38 | [dir="rtl"] .related-widget-wrapper-link + .selector { 39 | margin-right: 0; 40 | margin-left: 15px; 41 | } 42 | 43 | [dir="rtl"] .selector .selector-filter label { 44 | margin-right: 0; 45 | margin-left: 8px; 46 | } 47 | 48 | [dir="rtl"] .object-tools li { 49 | float: right; 50 | } 51 | 52 | [dir="rtl"] .object-tools li + li { 53 | margin-left: 0; 54 | margin-right: 15px; 55 | } 56 | 57 | [dir="rtl"] .dashboard .module table td a { 58 | padding-left: 0; 59 | padding-right: 16px; 60 | } 61 | } 62 | 63 | /* MOBILE */ 64 | 65 | @media (max-width: 767px) { 66 | [dir="rtl"] .change-list .filtered .results, 67 | [dir="rtl"] .change-list .filtered .paginator, 68 | [dir="rtl"] .filtered #toolbar, 69 | [dir="rtl"] .filtered div.xfull, 70 | [dir="rtl"] .filtered .actions { 71 | margin-left: 0; 72 | } 73 | 74 | [dir="rtl"] .aligned .add-another, 75 | [dir="rtl"] .aligned .related-lookup, 76 | [dir="rtl"] .aligned .datetimeshortcuts { 77 | margin-left: 0; 78 | margin-right: 15px; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /frontend/integrations/meetup/utilities.js: -------------------------------------------------------------------------------- 1 | import { circleOfCoords } from '../../utilities/coords'; 2 | 3 | export const groupParser = (m) => { 4 | const id = m.id; 5 | m = m.data; 6 | return { 7 | id, 8 | name: m.name, 9 | latlng: { 10 | lat: m.lat, 11 | lng: m.lon 12 | }, 13 | group_photo: m.group_photo ? m.group_photo.photo_link : undefined, 14 | key_photo: m.key_photo ? m.key_photo.photo_link : undefined, 15 | members: m.members, 16 | location: m.localized_location, 17 | link: m.link, 18 | urlname: m.urlname, 19 | description: m.description 20 | }; 21 | }; 22 | 23 | export const eventParser = e => { 24 | const id = e.id; 25 | const date = e.date; 26 | e = e.data; 27 | const local_parsed_time = e.time + e.utc_offset; 28 | return { 29 | id, 30 | date, 31 | link: e.link, 32 | name: e.name, 33 | latlng: e.venue ? { lat: e.venue.lat, lng: e.venue.lon } : undefined, 34 | venue: e.venue ? { 35 | address_1: e.venue.address_1, 36 | zip: e.venue.zip, 37 | city: e.venue.city 38 | } : undefined, 39 | time: e.time, 40 | local_parsed_time, 41 | duration: e.duration ? e.duration : 10800000, 42 | local_time: e.local_time, 43 | local_date: e.local_date, 44 | group_id: e.group.id, 45 | rsvp_limit: e.rsvp_limit, 46 | yes_rsvp_count: e.yes_rsvp_count, 47 | description: e.description 48 | }; 49 | }; 50 | 51 | export const overlappingResolver = markers => { 52 | const dict = markers.reduce((p, c, index) => { 53 | if (!c.latlng) { 54 | return p; 55 | } 56 | const key = JSON.stringify(c.latlng); 57 | if (p[key]) { 58 | p[key].push(index); 59 | } else { 60 | p[key] = [index]; 61 | } 62 | return p; 63 | }, {}); 64 | for (const k in dict) { 65 | if (dict[k].length > 1) { 66 | const latlng = JSON.parse(k); 67 | const circle = circleOfCoords(latlng, dict[k].length); 68 | dict[k].forEach((value, index) => { 69 | markers[value].latlng = circle[index]; 70 | }); 71 | } 72 | } 73 | return markers; 74 | }; 75 | 76 | export const eventHasValidLatLng = event => { 77 | return event && 78 | event.latlng && 79 | (event.latlng.lat || event.latlng.lng); 80 | }; 81 | -------------------------------------------------------------------------------- /frontend/store/index.js: -------------------------------------------------------------------------------- 1 | import { saveTokens } from '~/utilities/auth'; 2 | 3 | export const state = () => ({ 4 | userTypes: [], 5 | tags: [], 6 | goToMap: false, 7 | showCookieWarning: true, 8 | firstPageVisited: null 9 | 10 | }); 11 | 12 | export const getters = { 13 | getUserTypes: state => { 14 | return [...state.userTypes 15 | .map(s => ({...s})) 16 | .sort((a, b) => a.order - b.order)]; 17 | }, 18 | getUserType: (state, getters, rootState, rootGetters) => id => { 19 | const pinCount = rootGetters['map/getShownPins']; 20 | const type = getters.getUserTypes.find(ut => ut.id === id); 21 | const count = pinCount && type ? pinCount[type.id] : 0; 22 | return { ...type, count }; 23 | }, 24 | getTags: state => { 25 | return [...state.tags]; 26 | }, 27 | getGoToMap: state => { 28 | return state.goToMap; 29 | }, 30 | getShowCookieWarning: state => { 31 | return state.showCookieWarning; 32 | }, 33 | getFirstPageVisited: state => state.firstPageVisited 34 | }; 35 | 36 | export const actions = { 37 | async loadUserTypes ({commit}) { 38 | const { data } = await this.$axios.get('/api/user-type/'); 39 | commit('SET_USER_TYPES', data); 40 | }, 41 | async loadTags ({commit}) { 42 | const { data } = await this.$axios.get('/api/tags/'); 43 | commit('SET_TAGS', data); 44 | }, 45 | setGoToMap ({commit}, value) { 46 | commit('SET_GO_TO_MAP', value); 47 | }, 48 | setShowCookieWarning ({commit}, value) { 49 | commit('SET_SHOW_COOKIE_WARNING', value); 50 | }, 51 | acceptCookieWarning ({dispatch}) { 52 | saveTokens(null, true); 53 | dispatch('setShowCookieWarning', false); 54 | }, 55 | setFirstPageVisited ({commit}, value) { 56 | commit('SET_FIRST_PAGE_VISITED', value); 57 | } 58 | }; 59 | 60 | export const mutations = { 61 | SET_USER_TYPES: (state, types) => { 62 | state.userTypes = types; 63 | }, 64 | SET_TAGS: (state, tags) => { 65 | state.tags = tags; 66 | }, 67 | SET_GO_TO_MAP: (state, value) => { 68 | state.goToMap = value; 69 | }, 70 | SET_SHOW_COOKIE_WARNING: (state, value) => { 71 | state.showCookieWarning = value; 72 | }, 73 | SET_FIRST_PAGE_VISITED: (state, value) => { 74 | state.firstPageVisited = value; 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /django/peoplebackend/urls.py: -------------------------------------------------------------------------------- 1 | """peoplebackend URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/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: path('', 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: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf import settings 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | from rest_framework.routers import DefaultRouter 20 | from rest_framework.documentation import include_docs_urls 21 | 22 | from people.views import UserTypeViewSet, PersonViewSet, PeopleViewSet, TagViewSet, \ 23 | MeetupGroupViewSet, MeetupEventViewSet, PeopleSearchViewSet, UserViewSet 24 | 25 | router = DefaultRouter() 26 | router.register(r'api/user-type', UserTypeViewSet) 27 | router.register(r'api/user', UserViewSet) 28 | router.register(r'api/people', PeopleViewSet) 29 | router.register(r'api/person', PersonViewSet) 30 | router.register(r'api/search', PeopleSearchViewSet) 31 | router.register(r'api/tags', TagViewSet) 32 | router.register(r'api/meetup/groups', MeetupGroupViewSet) 33 | router.register(r'api/meetup/events', MeetupEventViewSet) 34 | urlpatterns = router.urls 35 | 36 | admin.site.site_header = 'Vue People Backend' 37 | urlpatterns += [ 38 | path('admin/', admin.site.urls), 39 | path('accounts/', include('allauth.urls')), 40 | path('api/', include("simple-feedback.urls")), 41 | ] 42 | 43 | if settings.DEBUG: # pragma: no cover 44 | urlpatterns.append(path(r'api/docs/', include_docs_urls(title='Vue People API', 45 | description='Private API', 46 | authentication_classes=[], 47 | permission_classes=[] 48 | ))) 49 | -------------------------------------------------------------------------------- /django/people/admin.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | from django.contrib import admin 3 | from prettyjson import PrettyJSONWidget 4 | from .models import Person, Type, MeetupGroup, MeetupEvent 5 | 6 | 7 | 8 | class HasLocationFilter(admin.SimpleListFilter): 9 | title = 'has location' 10 | parameter_name = 'has_location' 11 | 12 | def lookups(self, request, model_admin): 13 | return ( 14 | ('Yes', 'Yes'), 15 | ('No', 'No'), 16 | ) 17 | 18 | def queryset(self, request, queryset): 19 | if self.value() == 'Yes': 20 | return queryset.exclude(location__isnull=True) 21 | elif self.value() == 'No': 22 | return queryset.filter(location__isnull=True) 23 | else: 24 | return queryset 25 | 26 | 27 | class JsonForm(ModelForm): 28 | class Meta: 29 | model = None 30 | fields = '__all__' 31 | widgets = { 32 | 'data': PrettyJSONWidget(attrs={'initial': 'parsed', 'rows': 80, 'cols': 80}), 33 | } 34 | 35 | 36 | class MeetupGroupJsonForm(JsonForm): 37 | class Meta(JsonForm.Meta): 38 | model = MeetupGroup 39 | 40 | 41 | class MeetupEventJsonForm(JsonForm): 42 | class Meta(JsonForm.Meta): 43 | model = MeetupEvent 44 | 45 | 46 | @admin.register(MeetupGroup) 47 | class MeetupGroupAdmin(admin.ModelAdmin): 48 | form = MeetupGroupJsonForm 49 | 50 | 51 | @admin.register(MeetupEvent) 52 | class MeetupEventAdmin(admin.ModelAdmin): 53 | form = MeetupEventJsonForm 54 | ordering = ['date'] 55 | 56 | 57 | admin.site.register(Type) 58 | 59 | @admin.register(Person) 60 | class PersonAdmin(admin.ModelAdmin): 61 | list_display = ('github_login','user_email', 'type', 'created', 'has_location') 62 | ordering =('github_login', 'type', 'location') 63 | search_fields = ('github_login', 'user__first_name', 'user__last_name', 'user__email', 'company') 64 | list_filter = ('type', 'public_email', HasLocationFilter) 65 | fields = ('bio', 'company', 'github_url', 'twitter_url', 'website_url', 'type', 'public_email') 66 | 67 | def user_email(self, obj): 68 | return obj.user.email 69 | user_email.short_description = 'Email' 70 | 71 | def has_location(self, obj): 72 | return obj.location is not None 73 | has_location.boolean = True 74 | has_location.short_description = 'Has location' -------------------------------------------------------------------------------- /frontend/store/events.js: -------------------------------------------------------------------------------- 1 | import { groupParser, overlappingResolver, eventParser, eventHasValidLatLng } from '../integrations/meetup/utilities'; 2 | 3 | export const state = () => ({ 4 | meetups: [], 5 | meetupEvents: [], 6 | currentMeetup: null 7 | }); 8 | 9 | export const getters = { 10 | getEvents: state => { 11 | return [...state.meetupEvents]; 12 | }, 13 | getMeetups: (state, getters) => { 14 | return [...state.meetups.map(m => { 15 | const events = getters.getEvents 16 | .filter(e => e.group_id === m.id && e.latlng) 17 | .sort((a, b) => a.time - b.time); 18 | const event = events[0]; 19 | const validLatLng = eventHasValidLatLng(event); 20 | const latlng = event && validLatLng ? { ...event.latlng } : {...m.latlng}; 21 | return { 22 | ...m, 23 | latlng, 24 | has_event_with_coords: !!(event && validLatLng), 25 | event, 26 | options: {} 27 | }; 28 | })]; 29 | }, 30 | getCurrentMeetup: state => { 31 | return state.currentMeetup; 32 | }, 33 | getMeetupDetails: (state, getters) => id => { 34 | const events = getters.getEvents.filter(e => e.group_id === id); 35 | return {...getters.getMeetups.find(p => p.id === id), events}; 36 | }, 37 | getCurrentMeetupDetails: (state, getters) => { 38 | const current = getters.getCurrentMeetup; 39 | return getters.getMeetupDetails(current); 40 | } 41 | }; 42 | 43 | export const actions = { 44 | async loadMeetups ({commit, dispatch}) { 45 | const { data } = await this.$axios.get('/api/meetup/groups'); 46 | const meetups = data.map(groupParser); 47 | const combed = overlappingResolver(meetups); 48 | commit('SET_MEETUP_LIST', combed); 49 | }, 50 | setCurrent ({commit}, id) { 51 | commit('SET_CURRENT_MEETUP', id); 52 | }, 53 | async loadEvents ({commit}, groupNames) { 54 | const { data } = await this.$axios.get('/api/meetup/events'); 55 | const events = data.map(eventParser); 56 | const combed = overlappingResolver(events); 57 | commit('ADD_MEETUP_EVENTS', combed); 58 | } 59 | }; 60 | 61 | export const mutations = { 62 | SET_MEETUP_LIST: (state, meetups) => { 63 | state.meetups = meetups; 64 | }, 65 | SET_CURRENT_MEETUP: (state, id) => { 66 | state.currentMeetup = id; 67 | }, 68 | ADD_MEETUP_EVENTS: (state, events) => { 69 | state.meetupEvents = events; 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /nginx/site/static/rest_framework/docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | This is the GitHub theme for highlight.js 3 | 4 | github.com style (c) Vasily Polovnyov 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | padding: 0.5em; 12 | color: #333; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #333; 31 | font-weight: bold; 32 | } 33 | 34 | .hljs-number, 35 | .hljs-hexcolor, 36 | .ruby .hljs-constant { 37 | color: #008080; 38 | } 39 | 40 | .hljs-string, 41 | .hljs-tag .hljs-value, 42 | .hljs-phpdoc, 43 | .hljs-dartdoc, 44 | .tex .hljs-formula { 45 | color: #d14; 46 | } 47 | 48 | .hljs-title, 49 | .hljs-id, 50 | .scss .hljs-preprocessor { 51 | color: #900; 52 | font-weight: bold; 53 | } 54 | 55 | .hljs-list .hljs-keyword, 56 | .hljs-subst { 57 | font-weight: normal; 58 | } 59 | 60 | .hljs-class .hljs-title, 61 | .hljs-type, 62 | .vhdl .hljs-literal, 63 | .tex .hljs-command { 64 | color: #458; 65 | font-weight: bold; 66 | } 67 | 68 | .hljs-tag, 69 | .hljs-tag .hljs-title, 70 | .hljs-rule .hljs-property, 71 | .django .hljs-tag .hljs-keyword { 72 | color: #000080; 73 | font-weight: normal; 74 | } 75 | 76 | .hljs-attribute, 77 | .hljs-variable, 78 | .lisp .hljs-body, 79 | .hljs-name { 80 | color: #008080; 81 | } 82 | 83 | .hljs-regexp { 84 | color: #009926; 85 | } 86 | 87 | .hljs-symbol, 88 | .ruby .hljs-symbol .hljs-string, 89 | .lisp .hljs-keyword, 90 | .clojure .hljs-keyword, 91 | .scheme .hljs-keyword, 92 | .tex .hljs-special, 93 | .hljs-prompt { 94 | color: #990073; 95 | } 96 | 97 | .hljs-built_in { 98 | color: #0086b3; 99 | } 100 | 101 | .hljs-preprocessor, 102 | .hljs-pragma, 103 | .hljs-pi, 104 | .hljs-doctype, 105 | .hljs-shebang, 106 | .hljs-cdata { 107 | color: #999; 108 | font-weight: bold; 109 | } 110 | 111 | .hljs-deletion { 112 | background: #fdd; 113 | } 114 | 115 | .hljs-addition { 116 | background: #dfd; 117 | } 118 | 119 | .diff .hljs-change { 120 | background: #0086b3; 121 | } 122 | 123 | .hljs-chunk { 124 | color: #aaa; 125 | } 126 | -------------------------------------------------------------------------------- /frontend/store/map.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | addMode: false, 3 | mapReady: false, 4 | pinFilters: [] 5 | }); 6 | 7 | export const getters = { 8 | isAddMode: state => { 9 | return state.addMode; 10 | }, 11 | 12 | getMapReady: state => state.mapReady, 13 | 14 | getPins: (state, getters, rootState, rootGetters) => { 15 | return rootGetters['people/getList'].filter(p => p.latlng); 16 | }, 17 | getPinFilters: state => state.pinFilters, 18 | 19 | getFilteredPins: (state, getters, rootState, rootGetters) => { 20 | const tagFiltered = rootState.people.filtered; 21 | const list = getters.getPins; 22 | const pinFilters = getters.getPinFilters; 23 | let filtered = []; 24 | filtered = tagFiltered.length > 0 ? list.filter(p => tagFiltered.includes(p.id)) : list; 25 | filtered = pinFilters.length > 0 ? filtered.filter(p => pinFilters.includes(p.type)) : filtered; 26 | return [...filtered.map(p => { 27 | return { 28 | ...p, 29 | key: p.id, 30 | options: {} 31 | }; 32 | })]; 33 | }, 34 | getShownPins: (state, getters, rootState, rootGetters) => { 35 | let pins = getters.getFilteredPins; 36 | const user = rootGetters['user/getUserProfile']; 37 | const countInit = rootGetters.getUserTypes.reduce((p, c) => { 38 | p[c.id] = 0; 39 | return p; 40 | }, {}); 41 | if (pins) { 42 | pins = user.latlng ? [...pins, user] : [...pins]; 43 | return pins.reduce((prev, c) => { 44 | prev[c.type] += 1; 45 | return prev; 46 | }, countInit); 47 | } 48 | return countInit; 49 | } 50 | }; 51 | 52 | export const actions = { 53 | setAddMode ({commit}, value) { 54 | commit('SET_ADD_MODE', value); 55 | }, 56 | setMapReady ({commit}) { 57 | commit('SET_MAP_READY', true); 58 | }, 59 | togglePinFilters ({commit, getters}, value) { 60 | const index = getters.getPinFilters.indexOf(value); 61 | if (index === -1) { 62 | commit('ADD_PIN_FILTER', value); 63 | } else { 64 | commit('RM_PIN_FILTER', index); 65 | } 66 | } 67 | }; 68 | 69 | export const mutations = { 70 | SET_ADD_MODE: (state, value) => { 71 | state.addMode = value; 72 | }, 73 | SET_MAP_READY: (state, value) => { 74 | state.mapReady = value; 75 | }, 76 | ADD_PIN_FILTER: (state, value) => { 77 | state.pinFilters.push(value); 78 | }, 79 | RM_PIN_FILTER: (state, index) => { 80 | state.pinFilters.splice(index, 1); 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /frontend/components/MapMarker.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 45 | 46 | 100 | -------------------------------------------------------------------------------- /django/people/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-12 12:59 2 | 3 | import django.contrib.gis.db.models.fields 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django.db.models.manager 7 | import taggit.managers 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | ('taggit', '0002_auto_20150616_2121'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Person', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('is_active', models.BooleanField(default=True)), 24 | ('name', models.CharField(max_length=64)), 25 | ('bio', models.TextField()), 26 | ('organisation', models.CharField(max_length=128)), 27 | ('email', models.EmailField(max_length=254)), 28 | ('github_login', models.CharField(max_length=32)), 29 | ('github_url', models.URLField()), 30 | ('twitter_url', models.URLField()), 31 | ('website_url', models.URLField()), 32 | ('avatar_url', models.URLField()), 33 | ('location', django.contrib.gis.db.models.fields.PointField(srid=4326)), 34 | ('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), 35 | ], 36 | options={ 37 | 'abstract': False, 38 | }, 39 | managers=[ 40 | ('all_objects', django.db.models.manager.Manager()), 41 | ], 42 | ), 43 | migrations.CreateModel( 44 | name='Type', 45 | fields=[ 46 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 47 | ('name', models.CharField(max_length=16)), 48 | ('verbose_name', models.CharField(max_length=32)), 49 | ('order', models.IntegerField()), 50 | ('disabled', models.BooleanField(default=False)), 51 | ], 52 | ), 53 | migrations.AddField( 54 | model_name='person', 55 | name='type', 56 | field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='people.Type'), 57 | ), 58 | ] 59 | -------------------------------------------------------------------------------- /frontend/pages/index.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 61 | 62 | 93 | --------------------------------------------------------------------------------