├── .gitignore ├── LICENSE ├── README.md ├── backend ├── Procfile ├── api │ ├── urls.py │ └── utils.py ├── backend │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── exceptions.py │ │ ├── handler_v1_user.py │ │ ├── settings.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── utils_basicauth.py │ ├── settings.py │ ├── urls.py │ ├── user │ │ ├── __init__.py │ │ ├── mail.py │ │ └── notification.py │ └── wsgi.py ├── manage.py └── requirements.txt ├── docs ├── screenshot_1.png ├── screenshot_2.png ├── screenshot_3.png └── screenshot_4.png └── frontend ├── build ├── app.js ├── app.js.map ├── views.js └── views.js.map ├── css ├── bootstrap.css ├── bootstrap.min.css └── theme.css ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.woff2 ├── gulpfile.js ├── index.html ├── js ├── app.js ├── backbone.localStorage-1.1.0.js ├── backbone.min-1.1.2.js ├── bootstrap.js ├── bootstrap.min.js ├── config.js ├── config.js.example ├── jquery-2.1.4.min.js ├── react-0.14.0.js ├── react.min-0.14.0.js └── underscore-1.6.0.js ├── package.json ├── server.js └── src ├── js └── api.js └── templates ├── views-common.jsx ├── views-empty.jsx ├── views-hello.jsx ├── views-login.jsx ├── views-password.jsx ├── views-settings-user.jsx ├── views-settings.jsx └── views-users.jsx /.gitignore: -------------------------------------------------------------------------------- 1 | ## Backend 2 | 3 | *.log 4 | *.pot 5 | *.pyc 6 | venv/* 7 | .Python 8 | .pydevproject 9 | .project 10 | test.db 11 | settings_local.py 12 | 13 | ## Frontend 14 | 15 | # Logs 16 | logs 17 | *.log 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | 30 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (http://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directory 40 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 41 | node_modules 42 | backend/venv 43 | db.sqlite3 44 | backend/static 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Richard Dancsi 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Django Admin 2 | 3 | Administration interface in React JS, and a simple Django Rest Framework (with OAuth2 provider) for the backend. Uses Bootstrap for the templates. 4 | 5 | This is not meant to be a replacement of Django's admin. What this project is actually good for: 6 | 7 | * You can provide a separate "administration" area for users 8 | * Web login for any OAuth2-based API 9 | 10 | ## Screenshots 11 | 12 | ![image](docs/screenshot_1.png) 13 | 14 | ![image](docs/screenshot_2.png) 15 | 16 | ![image](docs/screenshot_3.png) 17 | 18 | ![image](docs/screenshot_4.png) 19 | 20 | ## TODO 21 | 22 | * Forgotten password 23 | * Many, many bugfixes are coming up 24 | 25 | ## Frontend 26 | 27 | ### To install the project: 28 | 29 | ```sh 30 | $ npm install 31 | ``` 32 | 33 | ### For development, auto-building the resources: 34 | 35 | ```sh 36 | $ gulp watch 37 | ``` 38 | 39 | ### Edit the config: 40 | 41 | The config is in `./js/config.js`; Copy the `./js/config.js.example` file, then change the API_URI parameter to the server you've set in the backend: 42 | 43 | ```js 44 | var RDAAppConfig = {}; 45 | RDAAppConfig.Config = { 46 | API_URI : "http://localhost:5000", 47 | CLIENT_ID : "", 48 | CLIENT_SECRET : "", 49 | STORAGE_ID : "RDA_LOCALSTORAGE" 50 | }; 51 | ``` 52 | 53 | ### Run server: 54 | 55 | ```sh 56 | $ node server.js 57 | ``` 58 | 59 | Visit http://localhost:8080/ in the browser. 60 | 61 | ### This project uses: 62 | 63 | * React.js 64 | * Bootstrap 65 | * Backbone.js 66 | * jQuery 2 67 | 68 | ## Backend 69 | 70 | ### To install the project: 71 | 72 | ```sh 73 | $ virtualenv venv 74 | $ source venv/bin/activate 75 | $ pip install -r requirements.txt 76 | $ python manage.py syncdb 77 | $ python manage.py collectstatic 78 | ``` 79 | 80 | ### Run locally: 81 | 82 | ```sh 83 | $ foremen start 84 | ``` 85 | 86 | Or, alternatively: 87 | 88 | ```sh 89 | $ python manage.py runserver 90 | ``` 91 | 92 | ### Add the OAuth2 Credentials 93 | 94 | Run the server, then go to the address: 95 | 96 | http://localhost:5000/admin/oauth2_provider/application/add/ 97 | 98 | Add: 99 | 100 | * CLIENT_ID, CLIENT_SECRET: the auto-generated keys 101 | * Client type: Confidental 102 | * Authorization grant type: Resource owner password-based 103 | * Name: any 104 | * User: any 105 | 106 | ## Used at 107 | 108 | This is a proof-of-concept, not used anywhere in special. 109 | 110 | However, feel free to add your link here and send a pull request. 111 | 112 | 113 | ## License 114 | 115 | [MIT, do-with-the-code-whatever-you-please License](https://github.com/wimagguc/react-django-admin/blob/master/LICENSE) 116 | 117 | ## About 118 | 119 | Richard Dancsi 120 | 121 | - Blog: [wimagguc.com](http://www.wimagguc.com/) 122 | - Github: [github.com/wimagguc](http://github.com/wimagguc/) 123 | - Twitter: [twitter.com/wimagguc](http://twitter.com/wimagguc/) 124 | - Linkedin: [linkedin.com/in/richarddancsi](http://linkedin.com/in/richarddancsi) 125 | -------------------------------------------------------------------------------- /backend/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn backend.wsgi -b "0.0.0.0:$PORT" -w 3 2 | -------------------------------------------------------------------------------- /backend/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | urlpatterns = patterns('', 4 | # Partner user profiles: read & edit 5 | url(r'^v1/user/get_profile/$', 'backend.api.handler_v1_user.get_profile'), 6 | url(r'^v1/user/get_profile/(?P\d+)', 'backend.api.handler_v1_user.get_profile'), 7 | url(r'^v1/user/set_profile/$', 'backend.api.handler_v1_user.set_profile'), 8 | url(r'^v1/user/set_profile/(?P\d+)', 'backend.api.handler_v1_user.set_profile'), 9 | url(r'^v1/user/set_password/$', 'backend.api.handler_v1_user.set_password'), 10 | url(r'^v1/user/set_password_for_user/(?P\d+)$', 'backend.api.handler_v1_user.set_password_for_user'), 11 | ) 12 | -------------------------------------------------------------------------------- /backend/api/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.http import HttpResponse 3 | from provider.oauth2.models import AccessToken 4 | from exceptions import UnauthorisedException 5 | from django.contrib.auth.models import User 6 | from backend.user.models import UserProfile 7 | from backend.api.utils_basicauth import basicauth_decode 8 | import hashlib 9 | import pytz, datetime 10 | import math 11 | 12 | 13 | def as_json(fun): 14 | def wrapped(request, *args, **kwargs): 15 | try: 16 | resp = fun(request, *args, **kwargs) 17 | except UnauthorisedException as e: 18 | return HttpResponse("Unauthorized", status=401) 19 | except Exception as e: 20 | if request.GET.get("debug"): 21 | raise 22 | resp = {"error": e.__class__.__name__, "message": str(e)} 23 | if hasattr(e, "extras"): 24 | resp["extras"] = e.extras 25 | 26 | return HttpResponse(json.dumps(resp), content_type="application/json") 27 | return wrapped 28 | 29 | 30 | def authenticate_request(fun): 31 | def wrapped(request, *args, **kwargs): 32 | try: 33 | access_token = request.POST.get('access_token', None) 34 | if not access_token: 35 | access_token = request.GET.get('access_token', None) 36 | if access_token: 37 | at = AccessToken.objects.get(token=access_token, expires__gt=datetime.datetime.utcnow()) 38 | request.user = at.user 39 | except: 40 | pass 41 | return fun(request, *args, **kwargs) 42 | 43 | return wrapped 44 | 45 | 46 | def profile_as_dict_full(user): 47 | if user==None: 48 | return None 49 | 50 | return dict( 51 | username=user.username, 52 | id=user.id, 53 | is_superuser=user.is_superuser, 54 | company_name=user.profile.company_name, 55 | email=user.email, 56 | telephone=user.profile.telephone, 57 | license_no=user.profile.license_no, 58 | tax_id=user.profile.tax_id, 59 | notes=user.profile.notes, 60 | address_line_1=user.profile.address_line_1, 61 | address_line_2=user.profile.address_line_2, 62 | address_line_3=user.profile.address_line_3, 63 | city=user.profile.city, 64 | postcode=user.profile.postcode, 65 | country_name=user.profile.country_name, 66 | billing_name=user.profile.billing_name, 67 | billing_address_line_1=user.profile.billing_address_line_1, 68 | billing_address_line_2=user.profile.billing_address_line_2, 69 | billing_address_line_3=user.profile.billing_address_line_3, 70 | billing_city=user.profile.billing_city, 71 | billing_postcode=user.profile.billing_postcode, 72 | billing_country_name=user.profile.billing_country_name, 73 | postal_name=user.profile.postal_name, 74 | postal_address_line_1=user.profile.postal_address_line_1, 75 | postal_address_line_2=user.profile.postal_address_line_2, 76 | postal_address_line_3=user.profile.postal_address_line_3, 77 | postal_city=user.profile.postal_city, 78 | postal_postcode=user.profile.postal_postcode, 79 | postal_country_name=user.profile.postal_country_name, 80 | ) 81 | -------------------------------------------------------------------------------- /backend/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/backend/backend/__init__.py -------------------------------------------------------------------------------- /backend/backend/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/backend/backend/api/__init__.py -------------------------------------------------------------------------------- /backend/backend/api/exceptions.py: -------------------------------------------------------------------------------- 1 | 2 | class UnauthorisedException(Exception): 3 | def __init__(self): 4 | pass 5 | def __str__(self): 6 | return repr(self) 7 | -------------------------------------------------------------------------------- /backend/backend/api/handler_v1_user.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate, login 2 | from django.contrib.auth.models import User 3 | from django.views.decorators.csrf import csrf_exempt 4 | 5 | from rest_framework.permissions import IsAuthenticated 6 | from rest_framework.decorators import permission_classes, api_view 7 | 8 | from backend.api.utils import * 9 | from backend.api.settings import * 10 | from backend.user.notification import send_new_password_to_user 11 | 12 | import pytz, datetime 13 | import re 14 | 15 | 16 | ## GET PROFILE FOR THE AUTHENTICATED USER 17 | @csrf_exempt 18 | @api_view(['POST']) 19 | @permission_classes((IsAuthenticated,)) 20 | @as_json 21 | def get_profile(request, userid=None): 22 | if request.POST.get('api_key', None) != API_KEY_V2: 23 | return {"status" : 403} 24 | 25 | user = None 26 | 27 | if not userid: 28 | user = request.user 29 | else: 30 | if not request.user.is_superuser: 31 | return {"status" : 403} 32 | try: 33 | user = User.objects.get(id=userid) 34 | except: 35 | return {"status" : 404} 36 | 37 | return { 38 | "status" : 200, 39 | "user" : profile_as_dict_full(user) 40 | } 41 | 42 | 43 | ## UPDATE PROFILE FOR THE AUTHENTICATED USER 44 | @csrf_exempt 45 | @api_view(['POST']) 46 | @permission_classes((IsAuthenticated,)) 47 | @as_json 48 | def set_profile(request, userid=None): 49 | if request.POST.get('api_key', None) != API_KEY_V2: 50 | return {"status" : 403} 51 | 52 | user = None 53 | 54 | if not userid: 55 | user = request.user 56 | else: 57 | if not request.user.is_superuser: 58 | return {"status" : 403} 59 | try: 60 | user = User.objects.get(id=userid) 61 | except: 62 | return {"status" : 404} 63 | 64 | try: 65 | if request.POST.get('email', None) != user.email: 66 | user.email = request.POST.get('email', None) 67 | user.save() 68 | except: 69 | return {"status" : 501} 70 | 71 | return { 72 | "status" : 200, 73 | "user" : profile_as_dict_full(user) 74 | } 75 | 76 | 77 | ## UPDATE PASSWORD FOR THE AUTHENTICATED USER 78 | @csrf_exempt 79 | @api_view(['POST']) 80 | @permission_classes((IsAuthenticated,)) 81 | @as_json 82 | def set_password(request): 83 | if request.POST.get('api_key', None) != API_KEY_V2: 84 | return {"status" : 403} 85 | 86 | try: 87 | request.user.set_password( request.POST.get('password', None) ) 88 | request.user.save() 89 | except: 90 | return {"status" : 501} 91 | 92 | return { 93 | "status" : 200 94 | } 95 | 96 | ## UPDATE PASSWORD FOR ANOTHER USER 97 | @csrf_exempt 98 | @api_view(['POST']) 99 | @permission_classes((IsAuthenticated,)) 100 | @as_json 101 | def set_password_for_user(request, userid=None): 102 | if request.POST.get('api_key', None) != API_KEY_V2: 103 | return {"status" : 403} 104 | 105 | user = None 106 | 107 | if not userid: 108 | return {"status" : 500} 109 | else: 110 | if not request.user.is_superuser: 111 | return {"status" : 403} 112 | try: 113 | user = User.objects.get(id=userid) 114 | except: 115 | return {"status" : 404} 116 | 117 | try: 118 | new_password = request.POST.get('password', None) 119 | 120 | user.set_password( new_password ) 121 | user.save() 122 | 123 | shouldNotifyUser = request.POST.get('should_notify_user', None) 124 | if shouldNotifyUser == True or shouldNotifyUser == "true": 125 | send_new_password_to_user(user=user, password=new_password) 126 | 127 | except: 128 | return {"status" : 501} 129 | 130 | return { 131 | "status" : 200 132 | } 133 | -------------------------------------------------------------------------------- /backend/backend/api/settings.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | API_KEY_V2 = 'U3DVW32BC431Y9IOIVXCSY212UIJH8' 4 | -------------------------------------------------------------------------------- /backend/backend/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | urlpatterns = patterns('', 4 | # Partner user profiles: read & edit 5 | url(r'^v1/user/get_profile/$', 'backend.api.handler_v1_user.get_profile'), 6 | url(r'^v1/user/get_profile/(?P\d+)', 'backend.api.handler_v1_user.get_profile'), 7 | url(r'^v1/user/set_profile/$', 'backend.api.handler_v1_user.set_profile'), 8 | url(r'^v1/user/set_profile/(?P\d+)', 'backend.api.handler_v1_user.set_profile'), 9 | url(r'^v1/user/set_password/$', 'backend.api.handler_v1_user.set_password'), 10 | url(r'^v1/user/set_password_for_user/(?P\d+)$', 'backend.api.handler_v1_user.set_password_for_user'), 11 | ) 12 | -------------------------------------------------------------------------------- /backend/backend/api/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.http import HttpResponse 3 | from provider.oauth2.models import AccessToken 4 | from exceptions import UnauthorisedException 5 | from django.contrib.auth.models import User 6 | from backend.api.utils_basicauth import basicauth_decode 7 | import hashlib 8 | import pytz, datetime 9 | import math 10 | 11 | 12 | def as_json(fun): 13 | def wrapped(request, *args, **kwargs): 14 | try: 15 | resp = fun(request, *args, **kwargs) 16 | except UnauthorisedException as e: 17 | return HttpResponse("Unauthorized", status=401) 18 | except Exception as e: 19 | if request.GET.get("debug"): 20 | raise 21 | resp = {"error": e.__class__.__name__, "message": str(e)} 22 | if hasattr(e, "extras"): 23 | resp["extras"] = e.extras 24 | 25 | return HttpResponse(json.dumps(resp), content_type="application/json") 26 | return wrapped 27 | 28 | 29 | def authenticate_request(fun): 30 | def wrapped(request, *args, **kwargs): 31 | try: 32 | access_token = request.POST.get('access_token', None) 33 | if not access_token: 34 | access_token = request.GET.get('access_token', None) 35 | if access_token: 36 | at = AccessToken.objects.get(token=access_token, expires__gt=datetime.datetime.utcnow()) 37 | request.user = at.user 38 | except: 39 | pass 40 | return fun(request, *args, **kwargs) 41 | 42 | return wrapped 43 | 44 | 45 | def profile_as_dict_full(user): 46 | if user==None: 47 | return None 48 | 49 | return dict( 50 | username=user.username, 51 | id=user.id, 52 | is_superuser=user.is_superuser, 53 | email=user.email, 54 | ) 55 | -------------------------------------------------------------------------------- /backend/backend/api/utils_basicauth.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode, b64encode 2 | from urllib import quote, unquote 3 | 4 | 5 | class DecodeError(Exception): 6 | pass 7 | 8 | 9 | def basicauth_encode(username, password): 10 | """Returns an HTTP basic authentication encrypted string given a valid 11 | username and password. 12 | """ 13 | return 'Basic ' + b64encode('%s:%s' % (quote(username), quote(password))) 14 | 15 | 16 | def basicauth_decode(encoded_str): 17 | """Decode an encrypted HTTP basic authentication string. Returns a tuple of 18 | the form (username, password), and raises a DecodeError exception if 19 | nothing could be decoded. 20 | """ 21 | split = encoded_str.strip().split(' ') 22 | 23 | # If split is only one element, try to decode the username and password 24 | # directly. 25 | if len(split) == 1: 26 | try: 27 | username, password = b64decode(split[0]).split(':', 1) 28 | except: 29 | raise DecodeError 30 | 31 | # If there are only two elements, check the first and ensure it says 32 | # 'basic' so that we know we're about to decode the right thing. If not, 33 | # bail out. 34 | elif len(split) == 2: 35 | if split[0].strip().lower() == 'basic': 36 | try: 37 | username, password = b64decode(split[1]).split(':', 1) 38 | except: 39 | raise DecodeError 40 | else: 41 | raise DecodeError 42 | 43 | # If there are more than 2 elements, something crazy must be happening. 44 | # Bail. 45 | else: 46 | raise DecodeError 47 | 48 | return unquote(username), unquote(password) 49 | -------------------------------------------------------------------------------- /backend/backend/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for backend project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '-r@66j#afn%&0golrw7ymid+^=0h3m!=jri519#qe=jwjfnbg6' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'corsheaders', 41 | 'backend.api', 42 | 'backend.user', 43 | 'oauth2_provider', 44 | 'rest_framework', 45 | ) 46 | 47 | OAUTH2_PROVIDER = { 48 | # this is the list of available scopes 49 | 'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'} 50 | } 51 | 52 | REST_FRAMEWORK = { 53 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 54 | 'rest_framework.authentication.BasicAuthentication', 55 | 'rest_framework.authentication.SessionAuthentication', 56 | 'oauth2_provider.ext.rest_framework.OAuth2Authentication', 57 | ), 58 | 'DEFAULT_PERMISSION_CLASSES': ( 59 | 'rest_framework.permissions.IsAuthenticated', 60 | ) 61 | } 62 | 63 | DEFAULT_FROM_EMAIL = 'no-reply@yourserver.com' 64 | SERVER_EMAIL = 'no-reply@yourserver.com' 65 | 66 | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 67 | 68 | EMAIL_HOST = 'smtp.yourserver.com' 69 | EMAIL_PORT = 587 70 | EMAIL_HOST_USER = 'no-reply@yourserver.com' 71 | EMAIL_HOST_PASSWORD = 'the_password_you_have' 72 | EMAIL_USE_TLS = True 73 | 74 | MIDDLEWARE_CLASSES = ( 75 | 'django.contrib.sessions.middleware.SessionMiddleware', 76 | 'corsheaders.middleware.CorsMiddleware', 77 | 'django.middleware.common.CommonMiddleware', 78 | 'django.middleware.csrf.CsrfViewMiddleware', 79 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 80 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 81 | 'django.contrib.messages.middleware.MessageMiddleware', 82 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 83 | 'django.middleware.security.SecurityMiddleware', 84 | ) 85 | 86 | ROOT_URLCONF = 'backend.urls' 87 | 88 | TEMPLATES = [ 89 | { 90 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 91 | 'DIRS': [], 92 | 'APP_DIRS': True, 93 | 'OPTIONS': { 94 | 'context_processors': [ 95 | 'django.template.context_processors.debug', 96 | 'django.template.context_processors.request', 97 | 'django.contrib.auth.context_processors.auth', 98 | 'django.contrib.messages.context_processors.messages', 99 | ], 100 | }, 101 | }, 102 | ] 103 | 104 | WSGI_APPLICATION = 'backend.wsgi.application' 105 | 106 | 107 | # Database 108 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 109 | 110 | DATABASES = { 111 | 'default': { 112 | 'ENGINE': 'django.db.backends.sqlite3', 113 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 114 | } 115 | } 116 | 117 | 118 | # Internationalization 119 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 120 | 121 | LANGUAGE_CODE = 'en-us' 122 | 123 | TIME_ZONE = 'UTC' 124 | 125 | USE_I18N = True 126 | 127 | USE_L10N = True 128 | 129 | USE_TZ = True 130 | 131 | CORS_ORIGIN_ALLOW_ALL = False 132 | CORS_ALLOW_CREDENTIALS = True 133 | CORS_ORIGIN_WHITELIST = ( 134 | 'localhost', 135 | 'localhost:5000', 136 | 'localhost:8080', 137 | 'localhost:8000', 138 | 'fehervartravel.hu', 139 | 'www.fehervartravel.hu' 140 | ) 141 | 142 | CORS_ALLOW_HEADERS = ( 143 | 'x-requested-with', 144 | 'content-type', 145 | 'accept-encoding', 146 | 'accept', 147 | 'cache-control', 148 | 'origin', 149 | 'authorization', 150 | 'x-csrftoken', 151 | 'dnt' 152 | ) 153 | 154 | # Static files (CSS, JavaScript, Images) 155 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 156 | 157 | STATIC_URL = '/static/' 158 | 159 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 160 | 161 | # List of finder classes that know how to find static files in 162 | # various locations. 163 | STATICFILES_FINDERS = ( 164 | 'django.contrib.staticfiles.finders.FileSystemFinder', 165 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 166 | ) 167 | -------------------------------------------------------------------------------- /backend/backend/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.conf.urls import url, include 3 | from django.contrib.auth.models import User 4 | from django.conf import settings 5 | from rest_framework import permissions, serializers, viewsets, routers 6 | 7 | # Serializers define the API representation. 8 | class UserSerializer(serializers.HyperlinkedModelSerializer): 9 | class Meta: 10 | model = User 11 | fields = ('id', 'url', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff') 12 | 13 | 14 | # ViewSets define the view behavior. 15 | class UserViewSet(viewsets.ModelViewSet): 16 | permission_classes = (permissions.IsAdminUser,) 17 | queryset = User.objects.all() 18 | serializer_class = UserSerializer 19 | 20 | 21 | # Routers provide a way of automatically determining the URL conf. 22 | router = routers.DefaultRouter() 23 | router.register(r'users', UserViewSet) 24 | 25 | 26 | # Wire up our API using automatic URL routing. 27 | # Additionally, we include login URLs for the browsable API. 28 | urlpatterns = [ 29 | url(r'^admin/', include(admin.site.urls)), 30 | url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), 31 | url(r'^api/R/', include(router.urls)), 32 | url(r'^api/F/', include('rest_framework.urls', namespace='rest_framework')), 33 | url(r'^api/', include('backend.api.urls')), 34 | url(r'^(?Pfavicon\.ico)', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), 35 | url(r'^static/(?P.*)', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), 36 | ] 37 | -------------------------------------------------------------------------------- /backend/backend/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/backend/backend/user/__init__.py -------------------------------------------------------------------------------- /backend/backend/user/mail.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | from StringIO import StringIO 4 | 5 | from django.utils.translation import ugettext as _ 6 | from django.core.mail import EmailMultiAlternatives 7 | 8 | from html2text import html2text as html2text_orig 9 | 10 | 11 | LINK_RE = re.compile(r"https?://([^ \n]+\n)+[^ \n]+", re.MULTILINE) 12 | def html2text(html): 13 | """Use html2text but repair newlines cutting urls. 14 | Need to use this hack until 15 | https://github.com/aaronsw/html2text/issues/#issue/7 is not fixed""" 16 | txt = html2text_orig(html) 17 | links = list(LINK_RE.finditer(txt)) 18 | out = StringIO() 19 | pos = 0 20 | for l in links: 21 | out.write(txt[pos:l.start()]) 22 | out.write(l.group().replace('\n', '')) 23 | pos = l.end() 24 | out.write(txt[pos:]) 25 | return out.getvalue() 26 | 27 | def send_mail(subject, message_plain, message_html, email_from, email_to, 28 | custom_headers={}, attachments=()): 29 | """ 30 | Build the email as a multipart message containing 31 | a multipart alternative for text (plain, HTML) plus 32 | all the attached files. 33 | """ 34 | if not message_plain and not message_html: 35 | raise ValueError(_("Either message_plain or message_html should be not None")) 36 | 37 | if not message_plain: 38 | message_plain = html2text(message_html) 39 | 40 | message = {} 41 | 42 | message['subject'] = subject 43 | message['body'] = message_plain 44 | message['from_email'] = email_from 45 | message['to'] = email_to 46 | if attachments: 47 | message['attachments'] = attachments 48 | if custom_headers: 49 | message['headers'] = custom_headers 50 | 51 | msg = EmailMultiAlternatives(**message) 52 | if message_html: 53 | msg.attach_alternative(message_html, "text/html") 54 | msg.send() 55 | 56 | 57 | def wrap_attachment(): 58 | pass 59 | -------------------------------------------------------------------------------- /backend/backend/user/notification.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.template.loader import render_to_string 3 | from backend.user.mail import send_mail 4 | 5 | def send_new_password_to_user(user=None, password=None): 6 | try: 7 | from_mail = 'Test ' 8 | to_mails = [user.email] 9 | 10 | context = {'user' : user, 11 | 'password' : password} 12 | 13 | message_html = render_to_string('user/emails/passwordchanged.html', context) 14 | message_txt = render_to_string('user/emails/passwordchanged.txt', context) 15 | subject = render_to_string('user/emails/passwordchanged_subject.txt', context) 16 | 17 | send_mail(subject.rstrip('\n'), message_txt, message_html, from_mail, to_mails) 18 | 19 | except: 20 | return 21 | -------------------------------------------------------------------------------- /backend/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /backend/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | Django>=1.8.6 2 | django-cors-headers>=0.13 3 | djangorestframework>=3.3.1 4 | django-oauth-toolkit>=0.9.0 5 | django-oauth2-provider>=0.2.3 6 | djangorestframework-oauth>=1.0.1 7 | gunicorn>=0.17.4 8 | html2text>=2016.1.8 9 | pytz>=2012f 10 | -------------------------------------------------------------------------------- /docs/screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/docs/screenshot_1.png -------------------------------------------------------------------------------- /docs/screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/docs/screenshot_2.png -------------------------------------------------------------------------------- /docs/screenshot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/docs/screenshot_3.png -------------------------------------------------------------------------------- /docs/screenshot_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/docs/screenshot_4.png -------------------------------------------------------------------------------- /frontend/build/app.js: -------------------------------------------------------------------------------- 1 | var RDA=this.RDA||{};RDA.Config={API_URI:"http://localhost:5000",API_KEY:"U3DVW32BC431Y9IOIVXCSY212UIJH8",CLIENT_ID:"",CLIENT_SECRET:"",STORAGE_ID:"RDA_LOCALSTORAGE"};var RDA=this.RDA||{};RDA.API={getUserById:function(e){$.ajax({type:"POST",url:app.Config.API_URI+"/api/v1/user/get_profile/"+e.userid,success:e.success,beforeSend:function(e,t){e.setRequestHeader("Authorization","Bearer "+app.user.attributes.access_token)},data:{api_key:app.Config.API_KEY},error:e.error,dataType:"json"})}};var RDA=this.RDA||{};RDA.Views=RDA.Views||{};var RDARouter=Backbone.Router.extend({initialize:function(){var e=Backbone.Model.extend({localStorage:new Backbone.LocalStorage(RDA.Config.STORAGE_ID),defaults:function(){return{id:1,access_token:null,data:{}}}});this.user=new e,this.user.fetch()},routes:{refresh:"refresh",hello:"hello",settings:"settings","users/:userid":"usersettings",users:"users","password/:userid":"password",password:"password","*actions":"defaultRoute"}});_.defaults(RDARouter.prototype,{Config:RDA.Config,API:RDA.API,Views:RDA.Views});var app=new RDARouter;app.on("route:users",function(e){this.user.attributes.access_token?app.Views.renderUsersView():this.navigate("/",{trigger:!0})}),app.on("route:settings",function(){this.user.attributes.access_token?app.Views.renderSettingsView():this.navigate("/",{trigger:!0})}),app.on("route:usersettings",function(e){this.user.attributes.access_token?app.Views.renderUserSettingsView(e):this.navigate("/",{trigger:!0})}),app.on("route:password",function(e){this.user.attributes.access_token?app.Views.renderChangePasswordView(e):this.navigate("/",{trigger:!0})}),app.on("route:refresh",function(e){this.user.attributes.access_token?app.Views.renderEmptyView():this.navigate("/",{trigger:!0})}),app.on("route:hello",function(e){this.user.attributes.access_token?app.Views.renderHelloView():this.navigate("/",{trigger:!0})}),app.on("route:defaultRoute",function(e){this.user.attributes.access_token?this.navigate("/",{trigger:!0}):app.Views.renderLoginView()}); 2 | //# sourceMappingURL=app.js.map 3 | -------------------------------------------------------------------------------- /frontend/build/app.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["config.js","api.js","app.js"],"names":["RDA","this","Config","API_URI","API_KEY","CLIENT_ID","CLIENT_SECRET","STORAGE_ID","API","getUserById","s","$","ajax","type","url","app","userid","success","beforeSend","xhr","settings","setRequestHeader","user","attributes","access_token","data","api_key","error","dataType","Views","RDARouter","Backbone","Router","extend","initialize","User","Model","localStorage","LocalStorage","defaults","id","fetch","routes","refresh","hello","users/:userid","users","password/:userid","password","*actions","_","prototype","on","renderUsersView","navigate","trigger","renderSettingsView","renderUserSettingsView","renderChangePasswordView","actions","renderEmptyView","renderHelloView","renderLoginView"],"mappings":"AAAA,GAAAA,KAAAC,KAAAD,OAEAA,KAAAE,QACAC,QAAA,wBACAC,QAAA,iCACAC,UAAA,mBACAC,cAAA,uBACAC,WAAA,mBCPA,IAAAP,KAAAC,KAAAD,OAOAA,KAAAQ,KAWAC,YAAA,SAAAC,GACAC,EAAAC,MACAC,KAAA,OACAC,IAAAC,IAAAb,OAAAC,QAAA,4BAAAO,EAAAM,OACAC,QAAAP,EAAAO,QACAC,WAAA,SAAAC,EAAAC,GACAD,EAAAE,iBAAA,gBAAA,UAAAN,IAAAO,KAAAC,WAAAC,eAEAC,MACAC,QAAAX,IAAAb,OAAAE,SAEAuB,MAAAjB,EAAAiB,MACAC,SAAA,UC9BA,IAAA5B,KAAAC,KAAAD,OACAA,KAAA6B,MAAA7B,IAAA6B,SAEA,IAAAC,WAAAC,SAAAC,OAAAC,QACAC,WAAA,WACA,GAAAC,GAAAJ,SAAAK,MAAAH,QACAI,aAAA,GAAAN,UAAAO,aAAAtC,IAAAE,OAAAK,YACAgC,SAAA,WACA,OACAC,GAAA,EACAhB,aAAA,KACAC,WAIAxB,MAAAqB,KAAA,GAAAa,GACAlC,KAAAqB,KAAAmB,SAEAC,QACAC,QAAA,UACAC,MAAA,QACAxB,SAAA,WACAyB,gBAAA,eACAC,MAAA,QACAC,mBAAA,WACAC,SAAA,WACAC,WAAA,iBAIAC,GAAAX,SAAAT,UAAAqB,WACAjD,OAAAF,IAAAE,OACAM,IAAAR,IAAAQ,IACAqB,MAAA7B,IAAA6B,OAGA,IAAAd,KAAA,GAAAe,UAEAf,KAAAqC,GAAA,cAAA,SAAAZ,GACAvC,KAAAqB,KAAAC,WAAAC,aAGAT,IAAAc,MAAAwB,kBAFApD,KAAAqD,SAAA,KAAAC,SAAA,MAMAxC,IAAAqC,GAAA,iBAAA,WACAnD,KAAAqB,KAAAC,WAAAC,aAGAT,IAAAc,MAAA2B,qBAFAvD,KAAAqD,SAAA,KAAAC,SAAA,MAMAxC,IAAAqC,GAAA,qBAAA,SAAApC,GACAf,KAAAqB,KAAAC,WAAAC,aAGAT,IAAAc,MAAA4B,uBAAAzC,GAFAf,KAAAqD,SAAA,KAAAC,SAAA,MAMAxC,IAAAqC,GAAA,iBAAA,SAAApC,GACAf,KAAAqB,KAAAC,WAAAC,aAGAT,IAAAc,MAAA6B,yBAAA1C,GAFAf,KAAAqD,SAAA,KAAAC,SAAA,MAMAxC,IAAAqC,GAAA,gBAAA,SAAAO,GACA1D,KAAAqB,KAAAC,WAAAC,aAGAT,IAAAc,MAAA+B,kBAFA3D,KAAAqD,SAAA,KAAAC,SAAA,MAMAxC,IAAAqC,GAAA,cAAA,SAAAO,GACA1D,KAAAqB,KAAAC,WAAAC,aAGAT,IAAAc,MAAAgC,kBAFA5D,KAAAqD,SAAA,KAAAC,SAAA,MAMAxC,IAAAqC,GAAA,qBAAA,SAAAO,GACA1D,KAAAqB,KAAAC,WAAAC,aACAvB,KAAAqD,SAAA,KAAAC,SAAA,IAEAxC,IAAAc,MAAAiC","file":"app.js","sourcesContent":["var RDA = this.RDA || {};\n\nRDA.Config = {\n\tAPI_URI : \"http://localhost:5000\",\n\tAPI_KEY : \"U3DVW32BC431Y9IOIVXCSY212UIJH8\",\n\tCLIENT_ID : \"\",\n\tCLIENT_SECRET : \"\",\n\tSTORAGE_ID : \"RDA_LOCALSTORAGE\"\n};\n","var RDA = this.RDA || {};\n\n//\n// API Calls used by the app.\n// Functions may use external attributes from {app}. Don't reuse them without\n// making sure you reuse {app} as well.\n//\nRDA.API = {\n\n //\n // POST /api/v1/user/get_profile/\n // Parameters:\n // * s.userid -> \n // * s.success: $.ajax's success callback\n // * s.error: $.ajax's error callback\n //\n // !!! Uses external attributes from {app.Config} and {app.user}\n //\n getUserById : function(s) {\n $.ajax({\n type: 'POST',\n url: app.Config.API_URI + \"/api/v1/user/get_profile/\" + s.userid,\n success: s.success,\n beforeSend: function(xhr, settings) {\n xhr.setRequestHeader('Authorization', 'Bearer ' + app.user.attributes.access_token);\n },\n data: {\n \"api_key\" : app.Config.API_KEY,\n },\n error: s.error,\n dataType: 'json'\n });\n }\n\n};\n","var RDA = this.RDA || {};\nRDA.Views = RDA.Views || {};\n\nvar RDARouter = Backbone.Router.extend({\n\tinitialize: function() {\n\t\tvar User = Backbone.Model.extend({\n\t\t\tlocalStorage: new Backbone.LocalStorage(RDA.Config.STORAGE_ID),\n\t\t\tdefaults: function() {\n\t\t\t\treturn {\n\t\t\t\t\tid: 1,\n\t\t\t\t\taccess_token: null,\n\t\t\t\t\tdata: {}\n\t\t\t\t};\n\t\t }\n\t\t});\n\t\tthis.user = new User();\n\t\tthis.user.fetch();\n\t},\n routes: {\n\t\t\"refresh\" : \"refresh\",\n\t\t\"hello\" : \"hello\",\n\t\t\"settings\" : \"settings\",\n\t\t\"users/:userid\" : \"usersettings\",\n\t\t\"users\" : \"users\",\n\t\t\"password/:userid\" : \"password\",\n\t\t\"password\" : \"password\",\n\t\t\"*actions\" : \"defaultRoute\"\n\t}\n});\n\n_.defaults(RDARouter.prototype, {\n\tConfig: RDA.Config,\n\tAPI: RDA.API,\n\tViews: RDA.Views\n});\n\nvar app = new RDARouter();\n\napp.on('route:users', function (id) {\n\tif (!this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderUsersView();\n\t}\n});\n\napp.on('route:settings', function () {\n\tif (!this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderSettingsView();\n\t}\n});\n\napp.on('route:usersettings', function (userid) {\n\tif (!this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderUserSettingsView(userid);\n\t}\n});\n\napp.on('route:password', function (userid) {\n\tif (!this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderChangePasswordView(userid);\n\t}\n});\n\napp.on('route:refresh', function(actions) {\n\tif (!this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderEmptyView();\n\t}\n});\n\napp.on('route:hello', function(actions) {\n\tif (!this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderHelloView();\n\t}\n});\n\napp.on('route:defaultRoute', function(actions) {\n\tif (this.user.attributes.access_token) {\n\t\tthis.navigate(\"/\", {trigger: true});\n\t} else {\n\t\tapp.Views.renderLoginView();\n\t}\n});\n"],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /frontend/build/views.js: -------------------------------------------------------------------------------- 1 | "use strict";var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.NavigationTopTabItem=React.createClass({displayName:"NavigationTopTabItem",render:function(){return React.createElement("li",{role:"presentation",className:this.props.data.className,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:7}},React.createElement("a",{href:this.props.data.hash,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:7}},this.props.data.text))}}),RDA.Views.NavigationTop=React.createClass({displayName:"NavigationTop",handleLogout:function(e){e.preventDefault(),app.user.save({access_token:null}),app.navigate("/",{trigger:!0})},render:function(){var e=app.user&&app.user.attributes.data&&app.user.attributes.data.user?app.user.attributes.data.user.username:"User",s=app.user&&app.user.attributes.data&&app.user.attributes.data.user?app.user.attributes.data.user.is_superuser:!1,t=[{hash:"#hello",text:"Hello",className:""}];s&&t.push({hash:"#users",text:"Users",className:""});for(var a=0;a-1&&(t[a].className="active");var r=t.map(function(e){return React.createElement(RDA.Views.NavigationTopTabItem,{key:e.id,data:e,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:36}})}),i="pull-right dropdown";return($(location).attr("hash").indexOf("settings")>-1||$(location).attr("hash").indexOf("password")>-1)&&(i+=" active"),React.createElement("div",{className:"row",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:48}},React.createElement("ul",{className:"nav nav-tabs",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:49}},r,React.createElement("li",{role:"presentation",className:i,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:51}},React.createElement("a",{className:"dropdown-toggle",id:"dropdownMenu1","data-toggle":"dropdown","aria-haspopup":"true","aria-expanded":"true",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:52}},e," ",React.createElement("span",{className:"caret",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:53}})),React.createElement("ul",{className:"dropdown-menu","aria-labelledby":"dropdownMenu1",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:55}},React.createElement("li",{__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:56}},React.createElement("a",{href:"#settings",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:56}},"Settings")),React.createElement("li",{__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:57}},React.createElement("a",{href:"#password",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:57}},"Change Password")),React.createElement("li",{className:"divider",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:58}}),React.createElement("li",{__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:59}},React.createElement("a",{href:"#",onClick:this.handleLogout,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:59}},"Logout"))))),React.createElement("br",{__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:63}}))}}),RDA.Views.AlertView=React.createClass({displayName:"AlertView",render:function(){var e="alertView alert alert-dismissable";return e+=this.props.data.state?" alert-"+this.props.data.state:" hidden",React.createElement("div",{className:e,role:"alert",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:78}},React.createElement("button",{type:"button",className:"close","data-dismiss":"alert",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:79}},React.createElement("span",{"aria-hidden":"true",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:80}},"×"),React.createElement("span",{className:"sr-only",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:81}},"Close")),this.props.data.message)}}),RDA.Views.ModalTrigger=React.createClass({displayName:"ModalTrigger",handleClick:function(e){e.preventDefault(),$(this.refs.payload.getDOMNode()).modal()},render:function(){return React.createElement("span",{onClick:this.handleClick,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:96}},React.createElement("span",{dangerouslySetInnerHTML:{__html:this.props.trigger},__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:97}}),React.createElement(RDA.Views.Modal,{ref:"payload",content:this.props.content,htmlID:this.props.htmlID,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:98}}))}}),RDA.Views.Modal=React.createClass({displayName:"Modal",componentDidMount:function(){$(this.getDOMNode()).modal({background:!0,keyboard:!0,show:!1})},componentWillUnmount:function(){$(this.getDOMNode()).off("hidden")},handleClick:function(e){e.stopPropagation()},render:function(){return React.createElement("div",{onClick:this.handleClick,className:"modal fade",role:"dialog","aria-hidden":"true",id:this.props.htmlID,__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:123}},React.createElement("div",{className:"modal-dialog",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:124}},React.createElement("div",{className:"modal-content",__source:{fileName:"../../../../../src/templates/views-common.jsx",lineNumber:125}},this.props.content)))}});var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.EmptyView=React.createClass({displayName:"EmptyView",render:function(){return React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-empty.jsx",lineNumber:7}})}}),RDA.Views.renderEmptyView=function(){React.render(React.createElement(RDA.Views.EmptyView,{__source:{fileName:"../../../../../src/templates/views-empty.jsx",lineNumber:14}}),document.getElementById("app"))};var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.HelloView=React.createClass({displayName:"HelloView",render:function(){return React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-hello.jsx",lineNumber:7}},React.createElement(RDA.Views.NavigationTop,{__source:{fileName:"../../../../../src/templates/views-hello.jsx",lineNumber:8}}),React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-hello.jsx",lineNumber:9}},"Hello"))}}),RDA.Views.renderHelloView=function(){React.render(React.createElement(RDA.Views.HelloView,{__source:{fileName:"../../../../../src/templates/views-hello.jsx",lineNumber:17}}),document.getElementById("app"))};var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.LoginView=React.createClass({displayName:"LoginView",getInitialState:function(){return{alert:{state:null,message:null}}},onFormSubmit:function(e,s){this.setState({alert:{state:e,message:s}})},render:function(){return React.createElement("div",{className:"loginView",id:"form_signin",__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:13}},React.createElement(RDA.Views.LoginForm,{onFormSubmit:this.onFormSubmit,__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:14}}),React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:15}}))}}),RDA.Views.LoginForm=React.createClass({displayName:"LoginForm",getInitialState:function(){return{username:null,password:null,ajaxRunning:0}},inputChanged:function(e,s){var t={};t[e]=s.target.value,this.setState(t)},handleFormSubmit:function(e){e.preventDefault();var s=this;s.state.ajaxRunning||(s.setState({ajaxRunning:1}),$.ajax({type:"POST",url:app.Config.API_URI+"/o/token/",success:function(e){s.setState({ajaxRunning:0}),e=JSON.parse(e),app.user.save({access_token:e.access_token}),app.API.getUserById({userid:"",success:function(e){console.log(e),app.user.save({data:e}),app.navigate("/hello",{trigger:!0})},error:function(e){s.setState({ajaxRunning:0}),s.props.onFormSubmit("danger","Can't log in.")}})},data:{grant_type:"password",client_id:app.Config.CLIENT_ID,client_secret:app.Config.CLIENT_SECRET,username:s.state.username,password:s.state.password},error:function(e){s.setState({ajaxRunning:0}),s.props.onFormSubmit("danger","Can't log in. Please check your password.")},dataType:"text"}))},render:function(){var e="",s="";return this.state.ajaxRunning&&(s="disabled",e=' '),React.createElement("form",{className:"loginForm form-signin row",onSubmit:this.handleFormSubmit,__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:85}},React.createElement("input",{type:"text",className:"inputUsername form-control",id:"inputUsername",placeholder:"Username",onChange:this.inputChanged.bind(this,"username"),required:!0,autofocus:!0,__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:86}}),React.createElement("input",{type:"password",className:"inputPassword form-control",id:"inputUsername",placeholder:"Password",onChange:this.inputChanged.bind(this,"password"),required:!0,autofocus:!0,__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:87}}),React.createElement("button",{type:"submit",className:"btn btn-lg btn-primary btn-block",id:"loginButton",disabled:s,__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:88}},React.createElement("span",{dangerouslySetInnerHTML:{__html:e},__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:88}}),"Login"))}}),RDA.Views.renderLoginView=function(){React.render(React.createElement(RDA.Views.LoginView,{__source:{fileName:"../../../../../src/templates/views-login.jsx",lineNumber:96}}),document.getElementById("app"))};var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.ChangePasswordView=React.createClass({displayName:"ChangePasswordView",getInitialState:function(){return{alert:{state:null,message:null}}},onFormSubmit:function(e,s){this.setState({alert:{state:e,message:s}})},render:function(){return React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:15}},React.createElement(RDA.Views.NavigationTop,{__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:16}}),React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:17}},"Change password"),React.createElement("br",{__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:18}}),React.createElement(RDA.Views.ChangePasswordForm,{onFormSubmit:this.onFormSubmit,userid:this.props.userid,__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:19}}),React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:20}}))}}),RDA.Views.ChangePasswordForm=React.createClass({displayName:"ChangePasswordForm",getInitialState:function(){return{user:{},myProfileAjaxRunning:0,ajaxRunning:0}},inputChanged:function(e,s){var t=this.state.user;t[e]=s.target.value,this.setState(t)},handleFormSubmit:function(e){e.preventDefault();var s=this;if(!s.state.ajaxRunning){if(s.state.user.password!=s.state.user.password_confirm)return void s.props.onFormSubmit("danger","Passwords don't match");if(!s.state.user.password.match(/^[\d\w]{4,64}$/))return void s.props.onFormSubmit("danger","Password is too short or includes weird characters");s.setState({ajaxRunning:1}),$.ajax({type:"POST",url:app.Config.API_URI+"/api/v1/user/set_password/",success:function(e){e=JSON.parse(e),s.setState({ajaxRunning:0}),s.props.onFormSubmit("success","Password changed")},beforeSend:function(e,s){e.setRequestHeader("Authorization","Bearer "+app.user.attributes.access_token)},data:{api_key:app.Config.API_KEY,access_token:app.user.attributes.access_token,password:s.state.user.password},error:function(e){s.setState({ajaxRunning:0}),s.props.onFormSubmit("danger","Something's wrong")},dataType:"text"})}},render:function(){var e="",s="";return this.state.ajaxRunning&&(e="
"),React.createElement("form",{className:"form-horizontal row",onSubmit:this.handleFormSubmit,__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:90}},React.createElement("span",{dangerouslySetInnerHTML:{__html:s},__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:91}}),React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:92}},React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:93}},React.createElement("label",{htmlFor:"inputPassword1",className:"col-sm-4 control-label",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:94}},"Password"),React.createElement("div",{className:"col-sm-8",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:95}},React.createElement("input",{type:"password",className:"form-control",id:"inputPassword1",placeholder:"Password",onChange:this.inputChanged.bind(this,"password"),value:this.state.user.password,required:!0,__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:96}}))),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:99}},React.createElement("label",{htmlFor:"inputPassword2",className:"col-sm-4 control-label",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:100}},"Re-type Password"),React.createElement("div",{className:"col-sm-8",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:101}},React.createElement("input",{type:"password",className:"form-control",id:"inputPassword2",placeholder:"Re-type Password",onChange:this.inputChanged.bind(this,"password_confirm"),value:this.state.user.password_confirm,required:!0,__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:102}}))),React.createElement("span",{dangerouslySetInnerHTML:{__html:e},__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:106}}),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:108}},React.createElement("div",{className:"col-sm-offset-4 col-sm-8",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:109}},React.createElement("button",{type:"submit",id:"settingsSaveButton",className:"btn btn-primary",__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:110}},"Save")))))}}),RDA.Views.renderChangePasswordView=function(){React.render(React.createElement(RDA.Views.ChangePasswordView,{__source:{fileName:"../../../../../src/templates/views-password.jsx",lineNumber:121}}),document.getElementById("app"))};var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.UserSettingsView=React.createClass({displayName:"UserSettingsView",getInitialState:function(){return{alert:{state:null,message:null}}},onFormSubmit:function(e,s){this.setState({alert:{state:e,message:s}})},render:function(){return React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:15}},React.createElement(RDA.Views.NavigationTop,{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:16}}),React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:17}},React.createElement("div",{className:"pull-right",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:18}},React.createElement("a",{href:"#users",className:"btn btn-info",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:18}},"Back to all users ",React.createElement("span",{className:"glyphicon glyphicon-chevron-right",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:18}}))),React.createElement("div",{className:"clearfix",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:19}})),React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:21}},"Users: edit"),React.createElement(RDA.Views.SettingsForm,{onFormSubmit:this.onFormSubmit,userid:this.props.userid,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:22}}),React.createElement("hr",{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:23}}),React.createElement("div",{className:"form-horizontal row",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:24}},React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:25}},React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:26}},React.createElement("div",{className:"col-sm-offset-4 col-sm-8",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:27}},React.createElement(RDA.Views.ChangePasswordModal,{userid:this.props.userid,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:28}})))),React.createElement("div",{className:"clearfix",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:32}}),React.createElement("br",{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:33}})),React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:35}}))}}),RDA.Views.ChangePasswordModal=React.createClass({displayName:"ChangePasswordModal",getInitialState:function(){return{data:{password:""},alert:{state:null,message:null},ajaxRunning:0}},inputChanged:function(e,s){var t=this.state.data;"should_notify_user"==e?t[e]=$("#inputShouldNotifyUser").is(":checked"):t[e]=s.target.value,this.setState(t)},handleFormSubmit:function(e){e.preventDefault();var s=this;return s.setState({ajaxRunning:1}),s.state.data.password.match(/^[\d\w]{4,64}$/)?void $.ajax({type:"POST",url:app.Config.API_URI+"/api/v1/user/set_password_for_user/"+s.props.userid,success:function(e){console.log(e),s.setState({data:{password:"",should_notify_user:!1}}),$("#changePasswordModal").modal("hide")},beforeSend:function(e,s){e.setRequestHeader("Authorization","Bearer "+app.user.attributes.access_token)},data:{api_key:app.Config.API_KEY,password:s.state.data.password,should_notify_user:s.state.data.should_notify_user},error:function(e){s.setState({ajaxRunning:0});var t="Hiba a mentés során.";e&&e.responseText&&(t=e.responseText),s.setState({alert:{state:"danger",message:t}})},dataType:"json"}):void s.setState({alert:{state:"danger",message:"Password is too short or includes weird characters."}})},render:function(){return React.createElement(RDA.Views.ModalTrigger,{htmlID:"changePasswordModal",trigger:'Set new password',content:React.createElement("form",{className:"form-horizontal",onSubmit:this.handleFormSubmit,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:107}},React.createElement("div",{className:"modal-header",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:108}},React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:108}},"Set new password")),React.createElement("div",{className:"modal-body col-sm-12",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:109}},React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:110}}),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:111}},React.createElement("label",{htmlFor:"password",className:"col-sm-2 control-label",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:112}},"Password"),React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:113}},React.createElement("input",{type:"text",className:"form-control",id:"password",placeholder:"Password",onChange:this.inputChanged.bind(this,"password"),value:this.state.data.password,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:114}})),React.createElement("div",{className:"clearfix",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:116}})),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:118}},React.createElement("div",{className:"col-sm-2",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:119}},React.createElement("input",{type:"checkbox",id:"inputShouldNotifyUser",className:"control-checkbox-input pull-right",onClick:this.inputChanged.bind(this,"should_notify_user"),checked:this.state.data.should_notify_user,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:120}})),React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:122}},React.createElement("label",{htmlFor:"inputShouldNotifyUser",className:"col-sm-10 control-checkbox-label",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:123}},"Send password for the user")))),React.createElement("div",{className:"modal-footer",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:127}},React.createElement("a",{className:"btn btn-default","data-dismiss":"modal",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:128}},"Cancel"),React.createElement("button",{type:"submit",className:"btn btn-primary",__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:129}},"Save"))),__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:103}})}}),RDA.Views.renderUserSettingsView=function(e){React.render(React.createElement(RDA.Views.UserSettingsView,{userid:e,__source:{fileName:"../../../../../src/templates/views-settings-user.jsx",lineNumber:140}}),document.getElementById("app"))};var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.SettingsView=React.createClass({displayName:"SettingsView",getInitialState:function(){return{alert:{state:null,message:null}}},onFormSubmit:function(e,s){this.setState({alert:{state:e,message:s}})},render:function(){return React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:15}},React.createElement(RDA.Views.NavigationTop,{__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:16}}),React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:17}},"Settings"),React.createElement("br",{__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:18}}),React.createElement(RDA.Views.SettingsForm,{onFormSubmit:this.onFormSubmit,userid:this.props.userid,__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:19}}),React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:20}}))}}),RDA.Views.SettingsForm=React.createClass({displayName:"SettingsForm",getInitialState:function(){return{user:{},myProfileAjaxRunning:0,ajaxRunning:0}},componentDidMount:function(){var e=this;e.setState({myProfileAjaxRunning:1}),app.API.getUserById({userid:this.props.userid?this.props.userid:"",success:function(s){s&&200!=s.status?(e.setState({myProfileAjaxRunning:0}),e.props.onFormSubmit({alert:{state:"danger",message:s.status+" error."}})):e.setState({myProfileAjaxRunning:0,user:s.user})},error:function(s){e.setState({myProfileAjaxRunning:0}),e.props.onFormSubmit({alert:{state:"danger",message:"Network error. Please try again later."}})}})},inputChanged:function(e,s){var t=this.state.user;t[e]=s.target.value,this.setState(t)},handleFormSubmit:function(e){e.preventDefault();var s=this;s.state.ajaxRunning||(s.setState({ajaxRunning:1}),$.ajax({type:"POST",url:app.Config.API_URI+"/api/v1/user/set_profile/"+(this.props.userid?this.props.userid:""),success:function(e){s.setState({ajaxRunning:0}),e=JSON.parse(e),app.user.save({data:e.access_token}),s.props.onFormSubmit("success","Settings saved.")},beforeSend:function(e,s){e.setRequestHeader("Authorization","Bearer "+app.user.attributes.access_token)},data:{api_key:app.Config.API_KEY,access_token:app.user.attributes.access_token,email:s.state.user.email},error:function(e){s.setState({ajaxRunning:0}),s.props.onFormSubmit("danger","Something is wrong.")},dataType:"text"}))},render:function(){var e="",s="";return this.state.myProfileAjaxRunning&&(s="
"),this.state.ajaxRunning&&(e="
"),React.createElement("form",{className:"form-horizontal row",onSubmit:this.handleFormSubmit,__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:105}},React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:106}},React.createElement("span",{dangerouslySetInnerHTML:{__html:s},__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:108}}),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:109}},React.createElement("label",{htmlFor:"inputUsername",className:"col-sm-4 control-label",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:110}},"Username"),React.createElement("div",{className:"col-sm-8",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:111}},React.createElement("input",{type:"email",className:"form-control",id:"inputUsername",placeholder:"Username",onChange:this.inputChanged.bind(this,"username"),value:this.state.user.username,required:!0,__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:112}}))),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:115}},React.createElement("label",{htmlFor:"inputEmail",className:"col-sm-4 control-label",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:116}},"E-mail"),React.createElement("div",{className:"col-sm-8",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:117}},React.createElement("input",{type:"email",className:"form-control",id:"inputEmail",placeholder:"E-mail",onChange:this.inputChanged.bind(this,"email"),value:this.state.user.email,required:!0,__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:118}}))),React.createElement("span",{dangerouslySetInnerHTML:{__html:e},__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:122}}),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:124}},React.createElement("div",{className:"col-sm-offset-4 col-sm-8",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:125}},React.createElement("button",{type:"submit",id:"settingsSaveButton",className:"btn btn-primary",__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:126}},"Save")))))}}),RDA.Views.renderSettingsView=function(){React.render(React.createElement(RDA.Views.SettingsView,{__source:{fileName:"../../../../../src/templates/views-settings.jsx",lineNumber:137}}),document.getElementById("app"))};var RDA=RDA||{};RDA.Views=RDA.Views||{},RDA.Views.UserItem=React.createClass({displayName:"UserItem",deleteUser:function(e,s){if(s.preventDefault(),e==app.user.attributes.id)return void alert("You can't delete yourself");if(confirm("Are you sure? This cannot be undone.")){var t=this;t.setState({ajaxRunning:1}),$.ajax({type:"DELETE",url:app.Config.API_URI+"/api/R/users/"+e+"/",success:function(e){t.setState({ajaxRunning:0,data:e}),renderUsersView(),app.navigate("/refresh",{trigger:!0}),app.navigate("/users",{trigger:!0})},beforeSend:function(e,s){e.setRequestHeader("Authorization","Bearer "+app.user.attributes.access_token)},error:function(e){console.log(e),t.setState({ajaxRunning:0});var s="Error while deleting user.";e&&e.responseText&&(s=e.responseText),t.setState({alert:{state:"danger",message:s}})},dataType:"json"})}},render:function(){var e=this.props.data.username;this.props.data.first_name&&this.props.data.last_name?e=this.props.data.first_name+" "+this.props.data.last_name:this.props.data.first_name&&(e=this.props.data.first_name);var s="#users/"+this.props.data.id;return React.createElement("tr",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:55}},React.createElement("td",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:56}},React.createElement("span",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:57}},e)),React.createElement("td",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:59}},React.createElement("div",{className:"pull-right",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:60}},React.createElement("a",{href:"#",onClick:this.deleteUser.bind(this,this.props.data.id),className:"btn btn-sm btn-danger",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:61}},"Delete")," ",React.createElement("a",{href:s,className:"btn btn-sm btn-info",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:63}},"Edit"))))}}),RDA.Views.UserList=React.createClass({displayName:"UserList",render:function(){var e=this.props.data.map(function(e){return React.createElement(RDA.Views.UserItem,{data:e,__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:75}})});return React.createElement("table",{className:"table table-striped",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:79}},React.createElement("tbody",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:80}},e))}}),RDA.Views.UserDashboard=React.createClass({displayName:"UserDashboard",getInitialState:function(){return{data:[],alert:{state:null,message:null},ajaxRunning:0}},componentDidMount:function(){var e=this;e.setState({ajaxRunning:1}),$.ajax({type:"GET",url:app.Config.API_URI+"/api/R/users",success:function(s){e.setState({ajaxRunning:0,data:s})},data:{access_token:app.user.attributes.access_token},error:function(s){e.setState({ajaxRunning:0}),e.setState({alert:{state:"danger",message:"No access."}})},dataType:"json"})},render:function(){var e="";return this.state.ajaxRunning&&(e='
'),React.createElement("div",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:123}},React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-users.jsx", 2 | lineNumber:124}}),React.createElement(RDA.Views.NavigationTop,{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:125}}),React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:126}},"Users"),React.createElement("br",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:127}}),React.createElement(RDA.Views.UserList,{data:this.state.data,__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:128}}),React.createElement(RDA.Views.AddUserModal,{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:129}}),React.createElement("span",{dangerouslySetInnerHTML:{__html:e},__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:130}}))}}),RDA.Views.AddUserModal=React.createClass({displayName:"AddUserModal",getInitialState:function(){return{data:{},alert:{state:null,message:null},ajaxRunning:0}},inputChanged:function(e,s){var t=this.state.data;t[e]=s.target.value,this.setState(t)},handleFormSubmit:function(e){e.preventDefault();var s=this;s.setState({ajaxRunning:1}),$.ajax({type:"POST",url:app.Config.API_URI+"/api/R/users/",success:function(e){s.setState({ajaxRunning:0,data:e}),$("#addUserModal").modal("hide"),app.navigate("/refresh",{trigger:!0}),app.navigate("/users",{trigger:!0})},beforeSend:function(e,s){e.setRequestHeader("Authorization","Bearer "+app.user.attributes.access_token)},data:{username:s.state.data.username,email:s.state.data.email},error:function(e){console.log(e),s.setState({ajaxRunning:0});var t="Hiba a mentés során.";e&&e.responseText&&(t=e.responseText),s.setState({alert:{state:"danger",message:t}})},dataType:"json"})},render:function(){return React.createElement(RDA.Views.ModalTrigger,{htmlID:"addUserModal",trigger:'New user',content:React.createElement("form",{className:"form-horizontal",onSubmit:this.handleFormSubmit,__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:192}},React.createElement("div",{className:"modal-header",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:193}},React.createElement("h3",{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:193}},"New user")),React.createElement("div",{className:"modal-body col-sm-12",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:194}},React.createElement(RDA.Views.AlertView,{data:this.state.alert,__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:195}}),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:196}},React.createElement("label",{"for":"username",className:"col-sm-2 control-label",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:197}},"Username"),React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:198}},React.createElement("input",{type:"text",className:"form-control",id:"name",placeholder:"Username",onChange:this.inputChanged.bind(this,"username"),__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:199}}))),React.createElement("div",{className:"form-group",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:202}},React.createElement("label",{"for":"email",className:"col-sm-2 control-label",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:203}},"E-mail"),React.createElement("div",{className:"col-sm-10",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:204}},React.createElement("input",{type:"text",className:"form-control",id:"name",placeholder:"E-mail",onChange:this.inputChanged.bind(this,"email"),__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:205}})))),React.createElement("div",{className:"modal-footer",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:209}},React.createElement("a",{className:"btn btn-default","data-dismiss":"modal",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:210}},"Cancel"),React.createElement("button",{type:"submit",className:"btn btn-primary",__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:211}},"Add user"))),__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:188}})}}),RDA.Views.renderUsersView=function(){React.render(React.createElement(RDA.Views.UserDashboard,{__source:{fileName:"../../../../../src/templates/views-users.jsx",lineNumber:222}}),document.getElementById("app"))}; 3 | //# sourceMappingURL=views.js.map 4 | -------------------------------------------------------------------------------- /frontend/build/views.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["views.js","views-common.jsx","views-empty.jsx","views-hello.jsx","views-login.jsx","views-password.jsx","views-settings-user.jsx","views-settings.jsx","views-users.jsx"],"names":["RDA","Views","NavigationTopTabItem","React","createClass","displayName","render","createElement","role","className","this","props","data","__source","fileName","lineNumber","href","hash","text","NavigationTop","handleLogout","e","preventDefault","app","user","save","access_token","navigate","trigger","companyName","attributes","username","isSuperUser","is_superuser","links","push","i","length","$","location","attr","indexOf","navigationTabNodes","map","p","key","id","profileClassName","data-toggle","aria-haspopup","aria-expanded","aria-labelledby","onClick","AlertView","classString","state","type","data-dismiss","aria-hidden","message","ModalTrigger","handleClick","refs","payload","getDOMNode","modal","dangerouslySetInnerHTML","__html","Modal","ref","content","htmlID","componentDidMount","background","keyboard","show","componentWillUnmount","off","stopPropagation","EmptyView","renderEmptyView","document","getElementById","HelloView","renderHelloView","LoginView","getInitialState","alert","onFormSubmit","setState","LoginForm","password","ajaxRunning","inputChanged","name","change","target","value","handleFormSubmit","_this","ajax","url","Config","API_URI","success","JSON","parse","API","getUserById","userid","userresponse","console","log","error","grant_type","client_id","CLIENT_ID","client_secret","CLIENT_SECRET","dataType","spinnerString","disabledString","onSubmit","placeholder","onChange","bind","required","autofocus","disabled","renderLoginView","ChangePasswordView","ChangePasswordForm","myProfileAjaxRunning","password_confirm","match","beforeSend","xhr","settings","setRequestHeader","api_key","API_KEY","htmlFor","renderChangePasswordView","UserSettingsView","SettingsForm","ChangePasswordModal","is","should_notify_user","responseText","checked","renderUserSettingsView","SettingsView","status","email","renderSettingsView","UserItem","deleteUser","confirm","renderUsersView","first_name","last_name","userSettingsURL","UserList","userNodes","UserDashboard","AddUserModal","for"],"mappings":"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","file":"views.js","sourcesContent":[null,"var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.NavigationTopTabItem = React.createClass({\n\trender: function() {\n\t\treturn (\n\t\t\t
  • {this.props.data.text}
  • \n\t\t);\n\t}\n});\n\nRDA.Views.NavigationTop = React.createClass({\n\thandleLogout: function(e) {\n\t\te.preventDefault();\n\t\tapp.user.save({access_token:null});\n\t\tapp.navigate(\"/\", {trigger: true});\n\t},\n\trender: function() {\n\t\tvar companyName = app.user && app.user.attributes.data && app.user.attributes.data.user ? app.user.attributes.data.user.username : \"User\";\n\t\tvar isSuperUser = app.user && app.user.attributes.data && app.user.attributes.data.user ? app.user.attributes.data.user.is_superuser : false;\n\n\t\tvar links = [\n\t\t\t{ hash: \"#hello\", text:\"Hello\", className: \"\"},\n\t\t];\n\t\tif (isSuperUser) {\n\t\t\tlinks.push({ hash: \"#users\", text:\"Users\", className: \"\"});\n\t\t}\n\t\tfor (var i=0; i -1) {\n\t\t\t\tlinks[i].className = \"active\";\n\t\t\t}\n\t\t}\n\n\t\tvar navigationTabNodes = links.map(function (p) {\n\t\t\treturn (\n \n\t \t\t);\n\t\t});\n\n\t\tvar profileClassName = \"pull-right dropdown\";\n\t\tif ($(location).attr('hash').indexOf(\"settings\") > -1 ||\n\t\t\t$(location).attr('hash').indexOf(\"password\") > -1 )\n\t\t{\n\t\t\tprofileClassName += \" active\";\n\t\t}\n\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t
    \n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.AlertView = React.createClass({\n\trender: function() {\n\t\tvar classString = 'alertView alert alert-dismissable';\n\t\tif (this.props.data.state) {\n\t\t\tclassString += ' alert-' + this.props.data.state;\n\t\t} else {\n\t\t\tclassString += ' hidden';\n\t\t}\n \t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t{this.props.data.message}\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.ModalTrigger = React.createClass({\n\thandleClick: function(e) {\n\t\te.preventDefault();\n\t\t$(this.refs.payload.getDOMNode()).modal();\n\t},\n\trender: function() {\n\t\treturn (\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t);\n\t}\n});\n\nRDA.Views.Modal = React.createClass({\n\tcomponentDidMount: function() {\n\t\t$(this.getDOMNode()).modal({\n\t\t\tbackground: true,\n\t\t\tkeyboard: true,\n\t\t\tshow: false\n\t\t})\n\t},\n\tcomponentWillUnmount: function(){\n\t\t$(this.getDOMNode()).off('hidden');\n\t},\n\thandleClick: function(e) {\n\t\te.stopPropagation();\n\t},\n\trender: function() {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t\t{this.props.content}\n\t\t\t\t\t
    \n\t\t\t\t
    \n\t\t\t
    \n\t\t);\n\t}\n});\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.EmptyView = React.createClass({\n \trender: function() {\n\t\treturn (\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.renderEmptyView = function() {\n\tReact.render(\n\t\t,\n\t\tdocument.getElementById('app')\n\t);\n}\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.HelloView = React.createClass({\n \trender: function() {\n\t\treturn (\n
    \n\t\t\t\t\n\t\t\t\t

    Hello

    \n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.renderHelloView = function() {\n\tReact.render(\n\t\t,\n\t\tdocument.getElementById('app')\n\t);\n}\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.LoginView = React.createClass({\n\tgetInitialState: function() {\n\t\treturn { alert: {state: null, message: null} };\n\t},\n\tonFormSubmit: function(state, message) {\n\t\tthis.setState( { alert: {state: state, message: message} } );\n\t},\n \trender: function() {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.LoginForm = React.createClass({\n getInitialState: function() {\n return {username: null, password: null, ajaxRunning: 0};\n },\n inputChanged: function(name, e) {\n \tvar change = {};\n \tchange[name] = e.target.value;\n \tthis.setState(change);\n },\n\thandleFormSubmit: function(e) {\n\t\te.preventDefault();\n\n\t\tvar _this = this;\n\n\t\tif (_this.state.ajaxRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\t_this.setState({ajaxRunning:1});\n\n\t\t$.ajax({\n\t\t\ttype: 'POST',\n\t\t\turl: app.Config.API_URI + '/o/token/',\n\t\t\tsuccess: function(data) {\n\t\t\t\t_this.setState({ajaxRunning:0});\n\t\t\t\tdata = JSON.parse(data);\n\n\t\t\t\tapp.user.save({access_token:data.access_token});\n\n\t\t\t\tapp.API.getUserById({\n\t\t\t\t\tuserid: \"\",\n\t\t\t\t\tsuccess: function(userresponse) {\n\t\t\t\t\t\tconsole.log(userresponse);\n\t\t\t\t\t\tapp.user.save({data: userresponse});\n\t\t\t\t\t\tapp.navigate(\"/hello\", {trigger: true});\n\t\t\t\t\t},\n\t\t\t\t\terror: function(userresponse) {\n\t\t\t\t\t\t_this.setState({ajaxRunning:0});\n\t\t\t\t\t\t_this.props.onFormSubmit(\"danger\", \"Can't log in.\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\t\"grant_type\" : \"password\",\n\t\t\t\t\"client_id\" : app.Config.CLIENT_ID,\n\t\t\t\t\"client_secret\" : app.Config.CLIENT_SECRET,\n\t\t\t\t\"username\" : _this.state.username,\n\t\t\t\t\"password\" : _this.state.password\n\t\t\t},\n\t\t\terror: function(data) {\n\t\t\t\t_this.setState({ajaxRunning:0});\n\t\t\t\t_this.props.onFormSubmit(\"danger\", \"Can't log in. Please check your password.\");\n\t\t\t},\n\t\t\tdataType: 'text'\n\t\t});\n\t},\n\trender: function() {\n\t\tvar spinnerString = \"\";\n\t\tvar disabledString = \"\";\n\t\tif (this.state.ajaxRunning) {\n\t\t\tdisabledString = \"disabled\";\n\t\t\tspinnerString = ' ';\n\t\t}\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.renderLoginView = function() {\n\tReact.render(\n\t\t,\n\t\tdocument.getElementById('app')\n\t);\n}\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.ChangePasswordView = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\talert: {state: null, message: null},\n\t\t}\n\t},\n\tonFormSubmit: function(state, message) {\n\t\tthis.setState( { alert: {state: state, message: message} } );\n\t},\n \trender: function() {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t

    Change password

    \n\t\t\t\t
    \n\t\t\t\t\n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.ChangePasswordForm = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\tuser: {},\n\t\t\tmyProfileAjaxRunning: 0,\n\t\t\tajaxRunning: 0\n\t\t};\n },\n inputChanged: function(name, e) {\n\t\tvar user = this.state.user\n \t\tuser[name] = e.target.value;\n\n \t\tthis.setState(user);\n },\n\thandleFormSubmit: function(e) {\n\t\te.preventDefault();\n\n\t\tvar _this = this;\n\n\t\tif (_this.state.ajaxRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (_this.state.user.password != _this.state.user.password_confirm) {\n\t\t\t_this.props.onFormSubmit(\"danger\", \"Passwords don't match\");\n\t\t\treturn;\n\t\t}\n\t\tif (!_this.state.user.password.match(/^[\\d\\w]{4,64}$/)) {\n\t\t\t_this.props.onFormSubmit(\"danger\", \"Password is too short or includes weird characters\");\n\t\t\treturn;\n\t\t}\n\n\t\t_this.setState({ajaxRunning:1});\n\n\t\t$.ajax({\n\t\t\ttype: \"POST\",\n\t\t\turl: app.Config.API_URI + \"/api/v1/user/set_password/\",\n\t\t\tsuccess: function(data) {\n\t\t\t\tdata = JSON.parse(data);\n\t\t\t\t_this.setState({ajaxRunning:0});\n\t\t\t\t_this.props.onFormSubmit(\"success\", \"Password changed\");\n\t\t\t},\n\t\t\tbeforeSend: function(xhr, settings) {\n\t\t\t\txhr.setRequestHeader('Authorization', 'Bearer ' + app.user.attributes.access_token);\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\t\"api_key\" : app.Config.API_KEY,\n\t\t\t\t\"access_token\" : app.user.attributes.access_token,\n\t\t\t\t\"password\" : _this.state.user.password,\n\t\t\t},\n\t\t\terror: function(data) {\n\t\t\t\t_this.setState({ajaxRunning: 0});\n\t\t\t\t_this.props.onFormSubmit(\"danger\", \"Something's wrong\");\n\t\t\t},\n\t\t\tdataType: \"text\"\n\t\t});\n\t},\n\trender: function() {\n\t\tvar spinnerString = \"\";\n\t\tvar disabledString = \"\";\n\t\tif (this.state.ajaxRunning) {\n\t\t\tspinnerString = \"
    \";\n\t\t}\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\n\t\t\t\t\t\n\n\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t
    \n\t\t\t\n\t\t);\n\t}\n});\n\nRDA.Views.renderChangePasswordView = function() {\n\tReact.render(\n\t\t,\n\t\tdocument.getElementById(\"app\")\n\t);\n}\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.UserSettingsView = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\talert: {state: null, message: null},\n\t\t}\n\t},\n\tonFormSubmit: function(state, message) {\n\t\tthis.setState( { alert: {state: state, message: message} } );\n\t},\n \trender: function() {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t
    \n\t\t\t\t\t\n\t\t\t\t\t
    \n\t\t\t\t
    \n\t\t\t\t

    Users: edit

    \n\t\t\t\t\n\t\t\t\t
    \n\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t
    \n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.ChangePasswordModal = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\tdata: {password: \"\"},\n\t\t\talert: {state: null, message: null},\n\t\t\tajaxRunning: 0\n\t\t};\n\t},\n\tinputChanged: function(key, e) {\n\t\tvar data = this.state.data;\n\n\t\tif (key == \"should_notify_user\") {\n\t\t\tdata[key] = $('#inputShouldNotifyUser').is(\":checked\");\n\t\t} else {\n\t\t\tdata[key] = e.target.value;\n\t\t}\n\n\t\tthis.setState(data);\n\t},\n\thandleFormSubmit: function(e) {\n\t\te.preventDefault();\n\n\t\tvar _this = this;\n\t\t_this.setState({ajaxRunning:1});\n\n\t\tif (!_this.state.data.password.match(/^[\\d\\w]{4,64}$/)) {\n\t\t\t_this.setState({alert: {state: \"danger\", message: \"Password is too short or includes weird characters.\"}});\n\t\t\treturn;\n\t\t}\n\n\t\t$.ajax({\n\t\t\ttype: 'POST',\n\t\t\turl: app.Config.API_URI + '/api/v1/user/set_password_for_user/' + _this.props.userid,\n\t\t\tsuccess: function(data) {\n\t\t\t\tconsole.log(data);\n\t\t\t\t_this.setState({data:{password: \"\", should_notify_user: false}});\n\t\t\t\t$('#changePasswordModal').modal('hide');\n\t\t\t},\n\t\t\tbeforeSend: function(xhr, settings) {\n\t\t\t\txhr.setRequestHeader('Authorization', 'Bearer ' + app.user.attributes.access_token);\n\t\t\t},\n data: {\n\t\t\t\t\"api_key\" : app.Config.API_KEY,\n\t\t\t\t\"password\" : _this.state.data.password,\n\t\t\t\t\"should_notify_user\" : _this.state.data.should_notify_user\n },\n\t\t\terror: function(data) {\n\t\t\t\t_this.setState( { ajaxRunning: 0 } );\n\n\t\t\t\tvar message = \"Hiba a mentés során.\";\n\t\t\t\tif (data && data.responseText) {\n\t\t\t\t\tmessage = data.responseText;\n\t\t\t\t}\n\n\t\t\t\t_this.setState( { alert: {state: \"danger\", message: message} } );\n\t\t\t},\n\t\t\tdataType: 'json'\n\t\t});\n\n\t},\n\trender: function() {\n\t\treturn (\n\t\t\tSet new password'}\n\t\t\t\tcontent={\n\t\t\t\t\t
    \n\t\t\t\t\t\t

    Set new password

    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t}\n\t\t\t/>\n\t\t);\n\t}\n});\n\nRDA.Views.renderUserSettingsView = function(userid) {\n\tReact.render(\n\t\t,\n\t\tdocument.getElementById(\"app\")\n\t);\n}\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.SettingsView = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\talert: {state: null, message: null},\n\t\t}\n\t},\n\tonFormSubmit: function(state, message) {\n\t\tthis.setState( { alert: {state: state, message: message} } );\n\t},\n \trender: function() {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t\n\t\t\t\t

    Settings

    \n\t\t\t\t
    \n\t\t\t\t\n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.SettingsForm = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\tuser: {},\n\t\t\tmyProfileAjaxRunning: 0,\n\t\t\tajaxRunning: 0\n\t\t};\n },\n componentDidMount: function() {\n\t\tvar _this = this;\n\t\t_this.setState({myProfileAjaxRunning:1});\n\n\t\tapp.API.getUserById({\n\t\t\tuserid: (this.props.userid ? this.props.userid : \"\"),\n\t\t\tsuccess: function(data) {\n\t\t\t\tif (data && data.status != 200) {\n\t\t\t\t\t_this.setState( { myProfileAjaxRunning: 0 } );\n\t\t\t\t\t_this.props.onFormSubmit( { alert: {state: \"danger\", message: data.status + \" error.\"} } );\n\t\t\t\t} else {\n\t\t\t\t\t_this.setState( { myProfileAjaxRunning: 0, user: data.user } );\n\t\t\t\t}\n\t\t\t},\n\t\t\terror: function(data) {\n\t\t\t\t_this.setState( { myProfileAjaxRunning: 0 } );\n\t\t\t\t_this.props.onFormSubmit( { alert: {state: \"danger\", message: \"Network error. Please try again later.\"} } );\n\t\t\t}\n\t\t});\n\n },\n inputChanged: function(name, e) {\n \t\tvar user = this.state.user\n \tuser[name] = e.target.value;\n \tthis.setState(user);\n },\n\thandleFormSubmit: function(e) {\n\t\te.preventDefault();\n\n\t\tvar _this = this;\n\n\t\tif (_this.state.ajaxRunning) {\n\t\t\treturn;\n\t\t}\n\n\t\t_this.setState({ajaxRunning:1});\n\n\t\t$.ajax({\n\t\t\ttype: \"POST\",\n\t\t\turl: app.Config.API_URI + \"/api/v1/user/set_profile/\" + (this.props.userid ? this.props.userid : \"\"),\n\t\t\tsuccess: function(data) {\n\t\t\t\t_this.setState({ajaxRunning:0});\n\t\t\t\tdata = JSON.parse(data);\n\t\t\t\tapp.user.save({data:data.access_token});\n\t\t\t\t_this.props.onFormSubmit(\"success\", \"Settings saved.\");\n\t\t\t},\n\t\t\tbeforeSend: function(xhr, settings) {\n\t\t\t\txhr.setRequestHeader('Authorization', 'Bearer ' + app.user.attributes.access_token);\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\t\"api_key\" : app.Config.API_KEY,\n\t\t\t\t\"access_token\" : app.user.attributes.access_token,\n\t\t\t\t\"email\" : _this.state.user.email\n\t\t\t},\n\t\t\terror: function(data) {\n\t\t\t\t_this.setState({ajaxRunning: 0});\n\t\t\t\t_this.props.onFormSubmit(\"danger\", \"Something is wrong.\");\n\t\t\t},\n\t\t\tdataType: \"text\"\n\t\t});\n\t},\n\trender: function() {\n\t\tvar spinnerString = \"\";\n\t\tvar disabledString = \"\";\n\t\tif (this.state.myProfileAjaxRunning) {\n\t\t\tdisabledString = \"
    \";\n\t\t}\n\t\tif (this.state.ajaxRunning) {\n\t\t\tspinnerString = \"
    \";\n\t\t}\n\t\treturn (\n\t\t\t
    \n\t\t\t\t
    \n\n\t\t\t\t\t\n\t\t\t\t\t
    \n\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\n\t\t\t\t\t\n\n\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t
    \n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.renderSettingsView = function() {\n\tReact.render(\n\t\t,\n\t\tdocument.getElementById(\"app\")\n\t);\n}\n","var RDA = RDA || {};\nRDA.Views = RDA.Views || {};\n\nRDA.Views.UserItem = React.createClass({\n\tdeleteUser: function(userid, e) {\n\t\te.preventDefault();\n\n\t\tif (userid == app.user.attributes.id) {\n\t\t\talert(\"You can't delete yourself\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!confirm(\"Are you sure? This cannot be undone.\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar _this = this;\n\t\t_this.setState({ajaxRunning:1});\n\n\t\t$.ajax({\n\t\t\ttype: 'DELETE',\n\t\t\turl: app.Config.API_URI + '/api/R/users/' + userid + \"/\",\n\t\t\tsuccess: function(data) {\n\t\t\t\t_this.setState({ajaxRunning:0, data:data});\n\t\t\t\trenderUsersView();\n\t\t\t\tapp.navigate(\"/refresh\", {trigger: true});\n\t\t\t\tapp.navigate(\"/users\", {trigger: true});\n\t\t\t},\n\t\t\tbeforeSend: function(xhr, settings) {\n\t\t\t\txhr.setRequestHeader('Authorization', 'Bearer ' + app.user.attributes.access_token);\n\t\t\t},\n\t\t\terror: function(data) {\n\t\t\t\tconsole.log(data);\n\t\t\t\t_this.setState( { ajaxRunning: 0 } );\n\n\t\t\t\tvar message = \"Error while deleting user.\";\n\t\t\t\tif (data && data.responseText) {\n\t\t\t\t\tmessage = data.responseText;\n\t\t\t\t}\n\n\t\t\t\t_this.setState( { alert: {state: \"danger\", message: message} } );\n\t\t\t},\n\t\t\tdataType: 'json'\n\t\t});\n\t},\n\trender: function() {\n\t\tvar name = this.props.data.username;\n\t\tif (this.props.data.first_name && this.props.data.last_name) {\n\t\t\tname = this.props.data.first_name + ' ' + this.props.data.last_name;\n\t\t} else if (this.props.data.first_name) {\n\t\t\tname = this.props.data.first_name;\n\t\t}\n\t\tvar userSettingsURL = \"#users/\" + this.props.data.id;\n\t\treturn (\n \n\t\t\t\t\n {name}\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t
    \n\t\t\t\t\t\tDelete\n\t\t\t\t\t\t \n\t\t\t\t\t\tEdit\n\t\t\t\t\t
    \n\t\t\t\t\n\t\t\t\n\t\t);\n\t}\n});\n\nRDA.Views.UserList = React.createClass({\n\trender: function() {\n\t\tvar userNodes = this.props.data.map(function (p) {\n\t\t\treturn (\n \n\t \t\t);\n\t\t});\n\t\treturn (\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{userNodes}\n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.UserDashboard = React.createClass({displayName: \"UserDashboard\",\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\tdata: [],\n\t\t\talert: {state: null, message: null},\n\t\t\tajaxRunning:0\n\t\t};\n\t},\n\tcomponentDidMount: function() {\n\t\tvar _this = this;\n\t\t_this.setState({ajaxRunning:1});\n\n\t\t$.ajax({\n\t\t\ttype: 'GET',\n\t\t\turl: app.Config.API_URI + '/api/R/users',\n\t\t\tsuccess: function(data) {\n\t\t\t\t_this.setState({ajaxRunning:0, data:data});\n\t\t\t},\n data: {\n\t\t\t\t\"access_token\" : app.user.attributes.access_token\n },\n\t\t\terror: function(data) {\n\t\t\t\t_this.setState( { ajaxRunning: 0 } );\n\t\t\t\t_this.setState( { alert: {state: \"danger\", message: \"No access.\"} } );\n\t\t\t},\n\t\t\tdataType: 'json'\n\t\t});\n\n\t},\n\trender: function() {\n\t\tvar spinnerString = \"\";\n\t\tif (this.state.ajaxRunning) {\n\t\t\tspinnerString = '
    ';\n\t\t}\n\t\treturn (\n
    \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t

    Users

    \n\t\t\t\t
    \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t
    \n\t\t);\n\t}\n});\n\nRDA.Views.AddUserModal = React.createClass({\n\tgetInitialState: function() {\n\t\treturn {\n\t\t\tdata: {},\n\t\t\talert: {state: null, message: null},\n\t\t\tajaxRunning: 0\n\t\t};\n\t},\n\tinputChanged: function(key, e) {\n\t\tvar data = this.state.data;\n\t\tdata[key] = e.target.value;\n\t\tthis.setState(data);\n\t},\n\thandleFormSubmit: function(e) {\n\t\te.preventDefault();\n\n\t\tvar _this = this;\n\t\t_this.setState({ajaxRunning:1});\n\n\t\t$.ajax({\n\t\t\ttype: 'POST',\n\t\t\turl: app.Config.API_URI + '/api/R/users/',\n\t\t\tsuccess: function(data) {\n\t\t\t\t_this.setState({ajaxRunning:0, data:data});\n\t\t\t\t$(\"#addUserModal\").modal(\"hide\");\n\t\t\t\tapp.navigate(\"/refresh\", {trigger: true});\n\t\t\t\tapp.navigate(\"/users\", {trigger: true});\n\t\t\t},\n\t\t\tbeforeSend: function(xhr, settings) {\n\t\t\t\txhr.setRequestHeader('Authorization', 'Bearer ' + app.user.attributes.access_token);\n\t\t\t},\n data: {\n\t\t\t\t\"username\" : _this.state.data.username,\n\t\t\t\t\"email\" : _this.state.data.email\n },\n\t\t\terror: function(data) {\n\t\t\t\tconsole.log(data);\n\t\t\t\t_this.setState( { ajaxRunning: 0 } );\n\n\t\t\t\tvar message = \"Hiba a mentés során.\";\n\t\t\t\tif (data && data.responseText) {\n\t\t\t\t\tmessage = data.responseText;\n\t\t\t\t}\n\n\t\t\t\t_this.setState( { alert: {state: \"danger\", message: message} } );\n\t\t\t},\n\t\t\tdataType: 'json'\n\t\t});\n\n\t},\n\trender: function() {\n\t\treturn (\n\t\t\tNew user'}\n\t\t\t\tcontent={\n\t\t\t\t\t
    \n\t\t\t\t\t\t

    New user

    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t}\n\t\t\t/>\n\t\t);\n\t}\n});\n\nRDA.Views.renderUsersView = function() {\n\tReact.render(\n ,\n\t\tdocument.getElementById('app')\n\t);\n}\n"],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /frontend/css/theme.css: -------------------------------------------------------------------------------- 1 | .spinning { 2 | -webkit-animation: spin 1000ms infinite linear; 3 | animation: spin 1000ms infinite linear; 4 | } 5 | 6 | @-webkit-keyframes spin { 7 | 0% { 8 | -webkit-transform: rotate(0deg); 9 | transform: rotate(0deg); 10 | } 11 | 100% { 12 | -webkit-transform: rotate(359deg); 13 | transform: rotate(359deg); 14 | } 15 | } 16 | 17 | @keyframes spin { 18 | 0% { 19 | -webkit-transform: rotate(0deg); 20 | transform: rotate(0deg); 21 | } 22 | 100% { 23 | -webkit-transform: rotate(359deg); 24 | transform: rotate(359deg); 25 | } 26 | } 27 | 28 | body { 29 | padding: 70px; 30 | } 31 | 32 | .form-signin { 33 | max-width: 330px; 34 | padding: 15px; 35 | margin: 0 auto; 36 | } 37 | .form-signin .form-signin-heading, 38 | .form-signin .checkbox { 39 | margin-bottom: 10px; 40 | } 41 | .form-signin .checkbox { 42 | font-weight: normal; 43 | } 44 | .form-signin .form-control { 45 | position: relative; 46 | height: auto; 47 | -webkit-box-sizing: border-box; 48 | -moz-box-sizing: border-box; 49 | box-sizing: border-box; 50 | padding: 10px; 51 | font-size: 16px; 52 | } 53 | .form-signin .form-control:focus { 54 | z-index: 2; 55 | } 56 | .form-signin input[type="email"] { 57 | margin-bottom: -1px; 58 | border-bottom-right-radius: 0; 59 | border-bottom-left-radius: 0; 60 | } 61 | .form-signin input[type="password"] { 62 | margin-bottom: 10px; 63 | border-top-left-radius: 0; 64 | border-top-right-radius: 0; 65 | } 66 | -------------------------------------------------------------------------------- /frontend/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/frontend/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /frontend/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/frontend/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /frontend/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/frontend/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /frontend/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wimagguc/react-django-admin/bb93d1e527719ed4993408effb5434b9cc762aef/frontend/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /frontend/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var watch = require('gulp-watch'); 3 | 4 | var sourcemaps = require("gulp-sourcemaps"); 5 | var concat = require("gulp-concat"); 6 | var uglify = require('gulp-uglify'); 7 | 8 | var babel = require("gulp-babel"); 9 | 10 | gulp.task('watch', function () { 11 | gulp.watch(['js/config.js', 'js/app.js'], ['app']); 12 | gulp.watch('src/js/**/*.js', ['app']); 13 | gulp.watch('src/templates/**/*.jsx', ['templates']); 14 | }); 15 | 16 | gulp.task("app", function () { 17 | return gulp.src(['js/config.js', 'src/js/**/*.js', 'js/app.js']) 18 | .pipe(sourcemaps.init()) 19 | .pipe(concat("app.js")) 20 | .pipe(uglify()) 21 | .pipe(sourcemaps.write(".")) 22 | .pipe(gulp.dest("build")); 23 | }); 24 | 25 | gulp.task("templates", function () { 26 | // Compile Facebook React's jsx files and concatenate them into one, minified js 27 | return gulp.src("src/templates/**/*.jsx") 28 | .pipe(sourcemaps.init()) 29 | .pipe(babel({ 30 | presets: ['es2015', 'react'] 31 | })) 32 | .pipe(concat("views.js")) 33 | .pipe(uglify()) 34 | .pipe(sourcemaps.write(".")) 35 | .pipe(gulp.dest("build")); 36 | }); 37 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Admin 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 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /frontend/js/app.js: -------------------------------------------------------------------------------- 1 | var RDA = this.RDA || {}; 2 | RDA.Views = RDA.Views || {}; 3 | 4 | var RDARouter = Backbone.Router.extend({ 5 | initialize: function() { 6 | var User = Backbone.Model.extend({ 7 | localStorage: new Backbone.LocalStorage(RDA.Config.STORAGE_ID), 8 | defaults: function() { 9 | return { 10 | id: 1, 11 | access_token: null, 12 | data: {} 13 | }; 14 | } 15 | }); 16 | this.user = new User(); 17 | this.user.fetch(); 18 | }, 19 | routes: { 20 | "refresh" : "refresh", 21 | "hello" : "hello", 22 | "settings" : "settings", 23 | "users/:userid" : "usersettings", 24 | "users" : "users", 25 | "password/:userid" : "password", 26 | "password" : "password", 27 | "*actions" : "defaultRoute" 28 | } 29 | }); 30 | 31 | _.defaults(RDARouter.prototype, { 32 | Config: RDA.Config, 33 | API: RDA.API, 34 | Views: RDA.Views 35 | }); 36 | 37 | var app = new RDARouter(); 38 | 39 | app.on('route:users', function (id) { 40 | if (!this.user.attributes.access_token) { 41 | this.navigate("/", {trigger: true}); 42 | } else { 43 | app.Views.renderUsersView(); 44 | } 45 | }); 46 | 47 | app.on('route:settings', function () { 48 | if (!this.user.attributes.access_token) { 49 | this.navigate("/", {trigger: true}); 50 | } else { 51 | app.Views.renderSettingsView(); 52 | } 53 | }); 54 | 55 | app.on('route:usersettings', function (userid) { 56 | if (!this.user.attributes.access_token) { 57 | this.navigate("/", {trigger: true}); 58 | } else { 59 | app.Views.renderUserSettingsView(userid); 60 | } 61 | }); 62 | 63 | app.on('route:password', function (userid) { 64 | if (!this.user.attributes.access_token) { 65 | this.navigate("/", {trigger: true}); 66 | } else { 67 | app.Views.renderChangePasswordView(userid); 68 | } 69 | }); 70 | 71 | app.on('route:refresh', function(actions) { 72 | if (!this.user.attributes.access_token) { 73 | this.navigate("/", {trigger: true}); 74 | } else { 75 | app.Views.renderEmptyView(); 76 | } 77 | }); 78 | 79 | app.on('route:hello', function(actions) { 80 | if (!this.user.attributes.access_token) { 81 | this.navigate("/", {trigger: true}); 82 | } else { 83 | app.Views.renderHelloView(); 84 | } 85 | }); 86 | 87 | app.on('route:defaultRoute', function(actions) { 88 | if (this.user.attributes.access_token) { 89 | this.navigate("/", {trigger: true}); 90 | } else { 91 | app.Views.renderLoginView(); 92 | } 93 | }); 94 | -------------------------------------------------------------------------------- /frontend/js/backbone.localStorage-1.1.0.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Backbone localStorage Adapter 3 | * Version 1.1.0 4 | * 5 | * https://github.com/jeromegn/Backbone.localStorage 6 | */ 7 | (function (root, factory) { 8 | if (typeof define === "function" && define.amd) { 9 | // AMD. Register as an anonymous module. 10 | define(["underscore","backbone"], function(_, Backbone) { 11 | // Use global variables if the locals are undefined. 12 | return factory(_ || root._, Backbone || root.Backbone); 13 | }); 14 | } else { 15 | // RequireJS isn't being used. Assume underscore and backbone are loaded in script tags 16 | factory(_, Backbone); 17 | } 18 | }(this, function(_, Backbone) { 19 | // A simple module to replace `Backbone.sync` with *localStorage*-based 20 | // persistence. Models are given GUIDS, and saved into a JSON object. Simple 21 | // as that. 22 | 23 | // Hold reference to Underscore.js and Backbone.js in the closure in order 24 | // to make things work even if they are removed from the global namespace 25 | 26 | // Generate four random hex digits. 27 | function S4() { 28 | return (((1+Math.random())*0x10000)|0).toString(16).substring(1); 29 | }; 30 | 31 | // Generate a pseudo-GUID by concatenating random hexadecimal. 32 | function guid() { 33 | return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); 34 | }; 35 | 36 | // Our Store is represented by a single JS object in *localStorage*. Create it 37 | // with a meaningful name, like the name you'd give a table. 38 | // window.Store is deprectated, use Backbone.LocalStorage instead 39 | Backbone.LocalStorage = window.Store = function(name) { 40 | this.name = name; 41 | var store = this.localStorage().getItem(this.name); 42 | this.records = (store && store.split(",")) || []; 43 | }; 44 | 45 | _.extend(Backbone.LocalStorage.prototype, { 46 | 47 | // Save the current state of the **Store** to *localStorage*. 48 | save: function() { 49 | this.localStorage().setItem(this.name, this.records.join(",")); 50 | }, 51 | 52 | // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already 53 | // have an id of it's own. 54 | create: function(model) { 55 | if (!model.id) { 56 | model.id = guid(); 57 | model.set(model.idAttribute, model.id); 58 | } 59 | this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model)); 60 | this.records.push(model.id.toString()); 61 | this.save(); 62 | return this.find(model); 63 | }, 64 | 65 | // Update a model by replacing its copy in `this.data`. 66 | update: function(model) { 67 | this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model)); 68 | if (!_.include(this.records, model.id.toString())) 69 | this.records.push(model.id.toString()); this.save(); 70 | return this.find(model); 71 | }, 72 | 73 | // Retrieve a model from `this.data` by id. 74 | find: function(model) { 75 | return this.jsonData(this.localStorage().getItem(this.name+"-"+model.id)); 76 | }, 77 | 78 | // Return the array of all models currently in storage. 79 | findAll: function() { 80 | return _(this.records).chain() 81 | .map(function(id){ 82 | return this.jsonData(this.localStorage().getItem(this.name+"-"+id)); 83 | }, this) 84 | .compact() 85 | .value(); 86 | }, 87 | 88 | // Delete a model from `this.data`, returning it. 89 | destroy: function(model) { 90 | if (model.isNew()) 91 | return false 92 | this.localStorage().removeItem(this.name+"-"+model.id); 93 | this.records = _.reject(this.records, function(id){ 94 | return id === model.id.toString(); 95 | }); 96 | this.save(); 97 | return model; 98 | }, 99 | 100 | localStorage: function() { 101 | return localStorage; 102 | }, 103 | 104 | // fix for "illegal access" error on Android when JSON.parse is passed null 105 | jsonData: function (data) { 106 | return data && JSON.parse(data); 107 | } 108 | 109 | }); 110 | 111 | // localSync delegate to the model or collection's 112 | // *localStorage* property, which should be an instance of `Store`. 113 | // window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead 114 | Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) { 115 | var store = model.localStorage || model.collection.localStorage; 116 | 117 | var resp, errorMessage, syncDfd = $.Deferred && $.Deferred(); //If $ is having Deferred - use it. 118 | 119 | try { 120 | 121 | switch (method) { 122 | case "read": 123 | resp = model.id != undefined ? store.find(model) : store.findAll(); 124 | break; 125 | case "create": 126 | resp = store.create(model); 127 | break; 128 | case "update": 129 | resp = store.update(model); 130 | break; 131 | case "delete": 132 | resp = store.destroy(model); 133 | break; 134 | } 135 | 136 | } catch(error) { 137 | if (error.code === DOMException.QUOTA_EXCEEDED_ERR && window.localStorage.length === 0) 138 | errorMessage = "Private browsing is unsupported"; 139 | else 140 | errorMessage = error.message; 141 | } 142 | 143 | if (resp) { 144 | model.trigger("sync", model, resp, options); 145 | if (options && options.success) 146 | options.success(resp); 147 | if (syncDfd) 148 | syncDfd.resolve(resp); 149 | 150 | } else { 151 | errorMessage = errorMessage ? errorMessage 152 | : "Record Not Found"; 153 | 154 | if (options && options.error) 155 | options.error(errorMessage); 156 | if (syncDfd) 157 | syncDfd.reject(errorMessage); 158 | } 159 | 160 | // add compatibility with $.ajax 161 | // always execute callback for success and error 162 | if (options && options.complete) options.complete(resp); 163 | 164 | return syncDfd && syncDfd.promise(); 165 | }; 166 | 167 | Backbone.ajaxSync = Backbone.sync; 168 | 169 | Backbone.getSyncMethod = function(model) { 170 | if(model.localStorage || (model.collection && model.collection.localStorage)) { 171 | return Backbone.localSync; 172 | } 173 | 174 | return Backbone.ajaxSync; 175 | }; 176 | 177 | // Override 'Backbone.sync' to default to localSync, 178 | // the original 'Backbone.sync' is still available in 'Backbone.ajaxSync' 179 | Backbone.sync = function(method, model, options) { 180 | return Backbone.getSyncMethod(model).apply(this, [method, model, options]); 181 | }; 182 | 183 | return Backbone.LocalStorage; 184 | })); 185 | -------------------------------------------------------------------------------- /frontend/js/backbone.min-1.1.2.js: -------------------------------------------------------------------------------- 1 | (function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('