├── .gitignore ├── Dockerfile ├── README.md ├── compose.yaml ├── manage.py ├── profiles ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── tokens.py ├── urls.py ├── utils.py └── views.py ├── readme_images ├── dashboard.PNG ├── home.PNG ├── signin.PNG └── signup.PNG ├── requirements.txt ├── static ├── css │ ├── addons │ │ ├── datatables.css │ │ └── datatables.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── mdb.css │ ├── mdb.min.css │ ├── style.css │ └── style.min.css ├── font │ └── roboto │ │ ├── Roboto-Bold.eot │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Bold.woff │ │ ├── Roboto-Bold.woff2 │ │ ├── Roboto-Light.eot │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-Light.woff │ │ ├── Roboto-Light.woff2 │ │ ├── Roboto-Medium.eot │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-Medium.woff │ │ ├── Roboto-Medium.woff2 │ │ ├── Roboto-Regular.eot │ │ ├── Roboto-Regular.ttf │ │ ├── Roboto-Regular.woff │ │ ├── Roboto-Regular.woff2 │ │ ├── Roboto-Thin.eot │ │ ├── Roboto-Thin.ttf │ │ ├── Roboto-Thin.woff │ │ └── Roboto-Thin.woff2 ├── img │ ├── lightbox │ │ ├── default-skin.png │ │ ├── default-skin.svg │ │ └── preloader.gif │ ├── overlays │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 03.png │ │ ├── 04.png │ │ ├── 05.png │ │ ├── 06.png │ │ ├── 07.png │ │ ├── 08.png │ │ └── 09.png │ └── svg │ │ ├── arrow_left.svg │ │ └── arrow_right.svg ├── index.html └── js │ ├── addons │ ├── datatables.js │ └── datatables.min.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-3.3.1.min.js │ ├── mdb.js │ ├── mdb.min.js │ └── popper.min.js ├── templates ├── app │ ├── dashboard.html │ ├── home.html │ ├── profile_edit.html │ └── signup.html ├── base.html ├── navbar.html └── registration │ ├── account_activation_email.html │ ├── account_activation_invalid.html │ ├── account_activation_sent.html │ ├── login.html │ └── logout.html ├── users ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py └── website ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # migrations folder 107 | profiles/migrations 108 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.4 2 | FROM python:3.11 3 | EXPOSE 8000 4 | 5 | ENV PROJECT_KEY=$PROJECT_KEY 6 | 7 | # set working directory 8 | WORKDIR /Django-Signup 9 | COPY requirements.txt /Django-Signup 10 | 11 | # install app dependencies 12 | RUN pip3 install -r requirements.txt --no-cache-dir 13 | COPY . /Django-Signup 14 | ENTRYPOINT ["python3"] 15 | 16 | RUN python manage.py makemigrations 17 | RUN python manage.py migrate 18 | CMD ["manage.py", "runserver", "0.0.0.0:8000"] 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django-Signup 2 | Creating a simple sign up view and then moving onto more advanced sign up view with profile model and confirmation mail 3 | 4 | **I have also added a location field in custom profile model and stored the 5 | actual location (city, country code) of user using API, a little javascript and 6 | python without letting user know about it. For a tutorial on using that API checkout my [repo](https://github.com/Alexmhack/django_weather_app)** 7 | 8 | In this tutorial we will 9 | 10 | 1. Create basic sign up view 11 | 2. Create sign up form with extra fields 12 | 3. Create profile model for users 13 | 4. Create sign up view with confirmation email 14 | 15 | # Finished View 16 | After completing the project and adding some styling from [mdbootstrap](http://mdbootstrap.com/) my project looks like 17 | 18 | Home Page is simply showing a navbar with working links 19 | 20 | ![Home Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/home.PNG) 21 | 22 | Login page looks like 23 | 24 | ![signin Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/signin.PNG) 25 | 26 | Sign Up page 27 | 28 | ![signup Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/signup.PNG) 29 | 30 | And finally dashboard page with the user name displayed... 31 | 32 | ![dashboard Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/dashboard.PNG) 33 | 34 | **You can reuse the code from my repo and include it in your django project. Put a star if you liked my work.** 35 | 36 | # Django Project Setup 37 | 38 | ``` 39 | pip install -r requirements.txt 40 | ``` 41 | 42 | 1. ```django-admin startproject website .``` 43 | 2. ```python manage.py migrate``` 44 | 3. ```python manage.py createsuperuser``` 45 | 4. ```python manage.py runserver``` 46 | 5. In project ```settings.py``` file import [decouple](https://pypi.org/project/python-decouple/) 47 | 48 | ``` 49 | from decouple import config 50 | SECRET_KEY = config("PROJECT_KEY") 51 | ``` 52 | 53 | And create ```.env``` file like so 54 | 55 | ``` 56 | PROJECT_KEY=93%@nka8)+fv-*ai-st1d*h)w2j2-y^)(jfiv9bogcy0u241u7 57 | ``` 58 | 59 | We will start with the basic sign up features that django provides by default 60 | 61 | # Basic Sign Up 62 | Simplest way to implement a **Sign Up** view is using ```UserCreationForm```. 63 | This form is for those django apps which use the default user model that only 64 | contains a **username** and **password** for user sign ups. 65 | 66 | To implement that view we need 67 | 68 | **urls.py** 69 | ``` 70 | ... 71 | from .views import signup_view 72 | 73 | urlpatterns = [ 74 | path('admin/', admin.site.urls), 75 | path('signup/', signup_view, name='sign-up'), 76 | ] 77 | ``` 78 | 79 | In ```urls.py``` we simply import the ```signup_view``` that we haven't yet added 80 | to ```views.py``` and create a url for that view. 81 | 82 | Now create ```views.py``` file in the **website** folder and put this code inside 83 | it. 84 | 85 | ``` 86 | from django.shortcuts import render, redirect 87 | from django.contrib.auth import authenticate, login 88 | from django.contrib.auth.forms import UserCreationForm 89 | 90 | def signup(request): 91 | if request.method == "POST": 92 | form = UserCreationForm(request.POST) 93 | if form.is_valid(): 94 | form.save() 95 | username = form.cleaned_data.get('username') 96 | password = form.cleaned_data.get('password1') 97 | user = authenticate(username=username, password=password) 98 | login(request, user) 99 | return redirect('home') 100 | else: 101 | form = UserCreationForm() 102 | 103 | return render(request, 'signup.html', {'form': form}) 104 | ``` 105 | 106 | This is most basic signup view that django has. All of the new user creation 107 | process is done using django. We use the default ```UserCreationForm``` form to 108 | display the signup form. We authenticate the new user using the username and 109 | password that we get from the post request from the **form**. We then login the 110 | user and redirect it to ```home``` view. If the request method is not ```POST``` 111 | we simply show the empty form in ```templates/signup.html``` file. 112 | 113 | Create a **templates** folder in root path (where ```manage.py``` file lies). In 114 | that folder create ```signup.html``` file. 115 | 116 | **signup.html** 117 | ``` 118 | {% extends 'base.html' %} 119 | 120 | {% block content %} 121 |

Sign up

122 |
123 | {% csrf_token %} 124 | {% for field in form %} 125 |

126 | {{ field.label_tag }}
127 | {{ field }} 128 | {% if field.help_text %} 129 | {{ field.help_text }} 130 | {% endif %} 131 | {% for error in field.errors %} 132 |

{{ error }}

133 | {% endfor %} 134 |

135 | {% endfor %} 136 | 137 |
138 | {% endblock %} 139 | ``` 140 | 141 | This is a little different way of rendering form. There are more ways like 142 | 143 | ``` 144 | {{ form.as_p }} 145 | {{ form.as_table }} 146 | {{ form.as_ul }} 147 | ``` 148 | 149 | # Sign Up Form With Extra Fields 150 | So far we have been using the default fields that ```UserCreationForm``` provides 151 | us. But what if we wanted the email address of the new user which is the 152 | important part aside from the username and password. 153 | 154 | For that we can inherit a new form class from ```UserCreationForm```. Create a 155 | new file named ```forms.py``` in the **website** folder. All of this should go 156 | in a separate app so you can start another app. Let's just do that as our project 157 | is increasing in size. 158 | 159 | ``` 160 | python manage.py startapp users 161 | ``` 162 | 163 | Include app in project **settings** 164 | 165 | ``` 166 | ... 167 | INSTALLED_APPS = [ 168 | 'django.contrib.admin', 169 | 'django.contrib.auth', 170 | 'django.contrib.contenttypes', 171 | 'django.contrib.sessions', 172 | 'django.contrib.messages', 173 | 'django.contrib.staticfiles', 174 | 'users', 175 | ] 176 | ``` 177 | 178 | Move the file ```website/views.py``` to ```users``` folder and replace it. 179 | These are some of the changes which you would have to do a lot of times if you 180 | are working on a large project in any language. 181 | 182 | Since we moved the ```views.py``` file, all our imports in ```urls.py``` will 183 | give errors. Let's also create a ```urls.py``` file in ```users``` folder. 184 | 185 | From the **website/urls.py** file change this piece of code into 186 | 187 | ``` 188 | from .views import signup_view, dashboard_view, home_view 189 | 190 | urlpatterns = [ 191 | path('admin/', admin.site.urls), 192 | path('signup/', signup_view, name='sign-up'), 193 | path('dashboard/', dashboard_view, name='dashboard'), 194 | path('', home_view, name='home'), 195 | ] 196 | ``` 197 | 198 | replace with below code 199 | 200 | ``` 201 | from django.urls import path, include 202 | 203 | urlpatterns = [ 204 | path('admin/', admin.site.urls), 205 | path('', include('users.urls', namespace='users')), 206 | ] 207 | ``` 208 | 209 | Now we will create a custom user registration form so create a new file ```forms.py``` in **users** folder. 210 | 211 | ``` 212 | from django import forms 213 | from django.contrib.auth.forms import UserCreationForm 214 | from django.contrib.auth.models import User 215 | 216 | class SignUpForm(UserCreationForm): 217 | first_name = forms.CharField( 218 | max_length=10, 219 | min_length=4, 220 | required=True, 221 | widget=forms.TextInput( 222 | attrs={ 223 | "placeholder": "First Name", 224 | "class": "form-control" 225 | } 226 | ) 227 | ) 228 | last_name = forms.CharField( 229 | max_length=30, 230 | required=True, 231 | widget=forms.TextInput( 232 | attrs={ 233 | "placeholder": "Last Name", 234 | "class": "form-control" 235 | } 236 | ) 237 | ) 238 | email = forms.EmailField( 239 | max_length=254, 240 | widget=forms.EmailInput( 241 | attrs={ 242 | "placeholder": "Email", 243 | "class": "form-control" 244 | } 245 | ) 246 | ) 247 | 248 | class Meta: 249 | model = User 250 | fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2',) 251 | ``` 252 | 253 | You can do the same with ```password``` fields by using ```PasswordInput()``` 254 | 255 | ``` 256 | ... 257 | password1 = forms.CharField( 258 | label='', 259 | max_length=30, 260 | min_length=8, 261 | required=True, 262 | widget=forms.PasswordInput( 263 | attrs={ 264 | "placeholder": "Password", 265 | "class": "form-control" 266 | } 267 | ) 268 | ) 269 | 270 | password2 = forms.CharField( 271 | label='', 272 | max_length=30, 273 | min_length=8, 274 | required=True, 275 | widget=forms.PasswordInput( 276 | attrs={ 277 | "placeholder": "Confirm Password", 278 | "class": "form-control" 279 | } 280 | ) 281 | ) 282 | ``` 283 | 284 | This would give the forms a nice look as well as placeholders and the rest of 285 | the django password validation remains intact and active. 286 | 287 | # Sign Up With Profile Model 288 | So far we have been using the ```User``` model from 289 | ```django.contrib.auth.models``` that meets almost all needs but **Django** docs 290 | itself recommends using a custom model for users instead of the ```User``` so in 291 | this section we will be making our own custom model for users and name it 292 | ```Profile``` 293 | 294 | For this part we will start another app name ```profiles``` 295 | 296 | ``` 297 | python manage.py startapp profiles 298 | ``` 299 | 300 | Add profiles app in **settings** 301 | 302 | Inside ```profiles/models.py``` add 303 | 304 | ``` 305 | from django.db import models 306 | from django.contrib.auth.models import User 307 | from django.db.models.signals import post_save 308 | from django.dispatch import receiver 309 | ``` 310 | 311 | In this particular case, the profile is created using a Signal. It’s not 312 | mandatory, but usually it is a good way to implement it. 313 | 314 | ``` 315 | class Profile(models.Model): 316 | user = models.OneToOneField(User, on_delete=models.CASCADE) 317 | bio = models.CharField(max_length=50, blank=True) 318 | location = models.CharField(max_length=30, blank=True) 319 | 320 | 321 | @receiver(post_save, sender=User) 322 | def update_user_profile(sender, instance, created, **kwargs): 323 | if created: 324 | Profile.objects.create(user=instance) 325 | instance.profile.save() 326 | ``` 327 | 328 | This is our custom model, ofcourse you can go far more further adding in birth 329 | date, and profile image and lots more stuff, but for simplicity we are just using 330 | three fields. 331 | 332 | Now we need to create a form so ```forms.py``` file should have 333 | 334 | ``` 335 | from django import forms 336 | from django.contrib.auth.forms import UserCreationForm 337 | from django.contrib.auth.models import User 338 | 339 | class SignUpForm(UserCreationForm): 340 | birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD') 341 | 342 | class Meta: 343 | model = User 344 | fields = ('username', 'birth_date', 'password1', 'password2', ) 345 | ``` 346 | 347 | You can add in attributes in the fields again like we did earlier. 348 | 349 | There are a few changes that ```views.py``` file should have 350 | 351 | ``` 352 | from django.contrib.auth import login, authenticate 353 | from django.shortcuts import render, redirect 354 | from mysite.core.forms import SignUpForm 355 | 356 | def signup(request): 357 | if request.method == 'POST': 358 | form = SignUpForm(request.POST) 359 | if form.is_valid(): 360 | user = form.save() 361 | user.refresh_from_db() # load the profile instance created by the signal 362 | user.profile.birth_date = form.cleaned_data.get('birth_date') 363 | user.save() 364 | raw_password = form.cleaned_data.get('password1') 365 | user = authenticate(username=user.username, password=raw_password) 366 | login(request, user) 367 | return redirect('home') 368 | else: 369 | form = SignUpForm() 370 | return render(request, 'signup.html', {'form': form}) 371 | ``` 372 | 373 | Because of the Signal handling the Profile creation, we have a synchronism issue 374 | here. It is easily solved by calling the user.refresh_from_db() method. This 375 | will cause a hard refresh from the database, which will retrieve the profile 376 | instance. 377 | 378 | If you don’t call user.refresh_from_db(), when you try to access the 379 | user.profile, it will return None. 380 | 381 | After refreshing it user model, set the cleaned data to the fields that matter, 382 | and save the user model. The user save will trigger the profile save as well, 383 | that’s why you don’t need to call user.profile.save(), instead you call just 384 | user.save(). 385 | 386 | You can display the user details using 387 | 388 | ``` 389 |

Welcome {{ request.user }}

390 |

Bio: {{ user.profile.bio }}

391 |

Location: {{ user.profile.location }}

392 |

Joined: {{ user.profile.timestamp }}

393 | ``` 394 | 395 | **For customizing the forms you can use [django-widget-tweaks](https://pypi.org/project/django-widget-tweaks/)** 396 | 397 | # Signup With Confirmation Email 398 | Django provides built-in system for sending emails. But first of test purposes 399 | we will be using **Console Backend** for emails. 400 | 401 | Add this settings in ```settings.py``` file 402 | 403 | ``` 404 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 405 | ``` 406 | 407 | Now for checking if a user is authenticated we will create a field in the profile 408 | model to determine if the user is confirmed or not. 409 | 410 | **profiles/models.py** 411 | ``` 412 | class Profile(models.Model): 413 | ... 414 | email_confirmed = models.BooleanField(default=False) 415 | ``` 416 | 417 | And for creating a one time link using django we will create a new file ```tokens.py``` 418 | 419 | ``` 420 | from django.contrib.auth.tokens import PasswordResetTokenGenerator 421 | from django.utils import six 422 | 423 | class AccountActivationTokenGenerator(PasswordResetTokenGenerator): 424 | def _make_hash_value(self, user, timestamp): 425 | return ( 426 | six.text_type(user.pk) + six.text_type(timestamp) + 427 | six.text_type(user.profile.email_confirmed) 428 | ) 429 | 430 | 431 | account_activation_token = AccountActivationTokenGenerator() 432 | ``` 433 | 434 | We use the ```pk``` from the user ```timestamp``` and the ```email_confirmed``` field 435 | to create a token. We basically extended the PasswordResetTokenGenerator to create a 436 | unique token generator to confirm email addresses. This make use of your project’s 437 | SECRET_KEY, so it is a pretty safe and reliable method. 438 | 439 | Now we need to define views for account activation as well as account activation sent 440 | view 441 | 442 | ``` 443 | def account_activation_sent_view(request): 444 | return render(request, 'registration/account_activation_sent.html') 445 | 446 | 447 | def account_activate(request, uidb64, token): 448 | try: 449 | uid = urlsafe_base64_decode(uidb64).decode() 450 | print(uid) 451 | user = User.objects.get(pk=uid) 452 | except (TypeError, ValueError, OverflowError, User.DoesNotExist) as e: 453 | print(e) 454 | user = None 455 | 456 | if user is not None and account_activation_token.check_token(user, token): 457 | user.is_active = True 458 | user.profile.email_confirmed = True 459 | user.save() 460 | login(request, user) 461 | return redirect('users:dashboard') 462 | else: 463 | return render(request, 'registration/account_activation_invalid.html') 464 | ``` 465 | 466 | ```account_activation_sent_view``` is justfor redirecting users if their account activation url is wrong. The template **registration/account_activation_sent.html** 467 | will be 468 | 469 | ``` 470 | {% extends "base.html" %} 471 | 472 | {% block title %} 473 | {{ block.super }} - Check Your Email Account 474 | {% endblock %} 475 | 476 | {% block content %} 477 | 478 |

Check your email account for verifying your django account.

479 | 480 | {% endblock %} 481 | ``` 482 | 483 | ```account_activate``` function simply fetches the ```uidb64``` and ```token``` 484 | from the url and uses the **.check_token** function from ```AccountActivationTokenGenerator``` class which takes the user and token. 485 | 486 | You can remove the print statements from the code, I use them for testing purposes 487 | while writing my code. 488 | 489 | **profiles/views.py** 490 | ``` 491 | User = get_user_model() 492 | 493 | def signup(request): 494 | if request.method == 'POST': 495 | form = SignUpForm(request.POST) 496 | if form.is_valid(): 497 | user = form.save(commit=False) 498 | user.is_active = False 499 | user.save() 500 | user = form.save() 501 | current_site = get_current_site(request) 502 | subject = "Activate your Django Serives Account" 503 | message = render_to_string('registration/account_activation_email.html', { 504 | 'user': user, 505 | 'domain': current_site.domain, 506 | 'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(), 507 | 'token': account_activation_token.make_token(user) 508 | }) 509 | user.email_user(subject, message) 510 | return redirect('profiles:account-activation-sent') 511 | else: 512 | form = SignUpForm() 513 | return render(request, 'app/signup.html', { 514 | 'form': form, 515 | 'profile': True 516 | }) 517 | ``` 518 | 519 | This is the code for sending email to the user email, notice we have removed 520 | ```user.refresh_from_db()``` since we are using ```form.save(commit=False)``` 521 | so you can use ```user.refresh_from_db()``` after saving the form 522 | 523 | ``` 524 | ... 525 | if form.is_valid(): 526 | user = form.save(commit=False) 527 | user.is_active = False 528 | user.save() 529 | user = form.save() 530 | user.refresh_from_db() 531 | # your code here 532 | user.save() # call save again 533 | ... 534 | ``` 535 | 536 | This is it. We can extend our model by using the ```smtp``` as email backend that 537 | will actually send email to the email provided by the user but it requires some more 538 | settings to be defined in the **settings.py** file. For more details visit [docs](https://docs.djangoproject.com/en/2.1/topics/email/) 539 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | web: 3 | build: website 4 | ports: 5 | - '8000:8000' 6 | -------------------------------------------------------------------------------- /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", "website.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /profiles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/profiles/__init__.py -------------------------------------------------------------------------------- /profiles/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Profile 4 | 5 | @admin.register(Profile) 6 | class ProfileModelAdmin(admin.ModelAdmin): 7 | list_display = ('user', 'location') 8 | list_search_fields = ('user', 'location') 9 | -------------------------------------------------------------------------------- /profiles/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProfilesConfig(AppConfig): 5 | name = 'profiles' 6 | -------------------------------------------------------------------------------- /profiles/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth.forms import UserCreationForm 3 | from django.contrib.auth.models import User 4 | 5 | class SignUpForm(UserCreationForm): 6 | username = forms.CharField( 7 | label='', 8 | max_length=30, 9 | min_length=5, 10 | required=True, 11 | widget=forms.TextInput( 12 | attrs={ 13 | "placeholder": "Username", 14 | "class": "form-control" 15 | } 16 | ) 17 | ) 18 | 19 | email = forms.EmailField( 20 | label='', 21 | max_length=255, 22 | required=True, 23 | widget=forms.EmailInput( 24 | attrs={ 25 | "placeholder": "Email", 26 | "class": "form-control" 27 | } 28 | ) 29 | ) 30 | 31 | password1 = forms.CharField( 32 | label='', 33 | max_length=30, 34 | min_length=8, 35 | required=True, 36 | widget=forms.PasswordInput( 37 | attrs={ 38 | "placeholder": "Password", 39 | "class": "form-control" 40 | } 41 | ) 42 | ) 43 | 44 | password2 = forms.CharField( 45 | label='', 46 | max_length=30, 47 | min_length=8, 48 | required=True, 49 | widget=forms.PasswordInput( 50 | attrs={ 51 | "placeholder": "Confirm Password", 52 | "class": "form-control" 53 | } 54 | ) 55 | ) 56 | 57 | location = forms.CharField( 58 | label='', 59 | max_length=30, 60 | required=False, 61 | widget=forms.HiddenInput( 62 | attrs={ 63 | "display": "none" 64 | } 65 | ) 66 | ) 67 | 68 | class Meta: 69 | model = User 70 | fields = ('username', 'email', 'password1', 'password2', 'location') 71 | -------------------------------------------------------------------------------- /profiles/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/profiles/migrations/__init__.py -------------------------------------------------------------------------------- /profiles/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.db.models.signals import post_save 4 | from django.dispatch import receiver 5 | 6 | class Profile(models.Model): 7 | user = models.OneToOneField(User, on_delete=models.CASCADE) 8 | bio = models.CharField(max_length=50, blank=True) 9 | location = models.CharField(max_length=30) 10 | email_confirmed = models.BooleanField(default=False) 11 | 12 | 13 | @receiver(post_save, sender=User) 14 | def update_user_profile(sender, instance, created, **kwargs): 15 | if created: 16 | Profile.objects.create(user=instance) 17 | instance.profile.save() 18 | -------------------------------------------------------------------------------- /profiles/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /profiles/tokens.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.tokens import PasswordResetTokenGenerator 2 | import six 3 | 4 | class AccountActivationTokenGenerator(PasswordResetTokenGenerator): 5 | def _make_hash_value(self, user, timestamp): 6 | return ( 7 | six.text_type(user.pk) + six.text_type(timestamp) + 8 | six.text_type(user.profile.email_confirmed) 9 | ) 10 | 11 | 12 | account_activation_token = AccountActivationTokenGenerator() 13 | -------------------------------------------------------------------------------- /profiles/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | from .views import ( 4 | signup, ProfileUpdateView, account_activation_sent_view, account_activate 5 | ) 6 | 7 | app_name = 'profiles' 8 | 9 | urlpatterns = [ 10 | path('', signup, name='profile-signup'), 11 | path('/edit/', ProfileUpdateView.as_view(), name='profile-edit'), 12 | ] 13 | 14 | urlpatterns += [ 15 | path('account-activation-sent/', account_activation_sent_view, name='account-activation-sent'), 16 | path('activate///', 17 | account_activate, name='activate'), 18 | ] 19 | -------------------------------------------------------------------------------- /profiles/utils.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | def get_location_from_ip(ip_address): 4 | try: 5 | response = requests.get(f"http://ip-api.com/json/{ip_address}").json() 6 | city = response.get("city") 7 | country_code = response.get("countryCode") 8 | return f"{city} - {country_code}" 9 | except Exception as e: 10 | print(e) 11 | return "Failed fetching location" 12 | -------------------------------------------------------------------------------- /profiles/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.contrib.auth import login, authenticate 3 | from django.views.generic import UpdateView 4 | from django.shortcuts import render, redirect 5 | from django.contrib.auth.mixins import LoginRequiredMixin 6 | from django.contrib.sites.shortcuts import get_current_site 7 | from django.template.loader import render_to_string 8 | from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode 9 | from django.utils.encoding import force_bytes 10 | from django.contrib.auth import get_user_model 11 | 12 | from .forms import SignUpForm 13 | from .models import Profile 14 | from .utils import get_location_from_ip 15 | from .tokens import account_activation_token 16 | 17 | User = get_user_model() 18 | 19 | def signup(request): 20 | if request.method == 'POST': 21 | form = SignUpForm(request.POST) 22 | if form.is_valid(): 23 | user = form.save(commit=False) 24 | user.is_active = False 25 | user.save() 26 | user = form.save() 27 | user.refresh_from_db() # load the profile instance created by the signal 28 | user.profile.location = form.cleaned_data.get('location') 29 | ip_address = user.profile.location 30 | location = get_location_from_ip(ip_address) 31 | print(location) 32 | user.profile.location = location 33 | user.save() 34 | current_site = get_current_site(request) 35 | subject = "Activate your Django Serives Account" 36 | message = render_to_string('registration/account_activation_email.html', { 37 | 'user': user, 38 | 'domain': current_site.domain, 39 | 'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(), 40 | 'token': account_activation_token.make_token(user) 41 | }) 42 | user.email_user(subject, message) 43 | return redirect('profiles:account-activation-sent') 44 | else: 45 | form = SignUpForm() 46 | return render(request, 'app/signup.html', { 47 | 'form': form, 48 | 'profile': True 49 | }) 50 | 51 | 52 | def account_activation_sent_view(request): 53 | return render(request, 'registration/account_activation_sent.html') 54 | 55 | 56 | def account_activate(request, uidb64, token): 57 | try: 58 | uid = urlsafe_base64_decode(uidb64).decode() 59 | print(uid) 60 | user = User.objects.get(pk=uid) 61 | except (TypeError, ValueError, OverflowError, User.DoesNotExist) as e: 62 | print(e) 63 | user = None 64 | 65 | if user is not None and account_activation_token.check_token(user, token): 66 | user.is_active = True 67 | user.profile.email_confirmed = True 68 | user.save() 69 | login(request, user) 70 | return redirect('users:dashboard') 71 | else: 72 | return render(request, 'registration/account_activation_invalid.html') 73 | 74 | 75 | class ProfileUpdateView(LoginRequiredMixin, UpdateView): 76 | model = Profile 77 | template_name = "app/profile_edit.html" 78 | fields = ('bio',) 79 | -------------------------------------------------------------------------------- /readme_images/dashboard.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/readme_images/dashboard.PNG -------------------------------------------------------------------------------- /readme_images/home.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/readme_images/home.PNG -------------------------------------------------------------------------------- /readme_images/signin.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/readme_images/signin.PNG -------------------------------------------------------------------------------- /readme_images/signup.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/readme_images/signup.PNG -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.7.2 2 | certifi==2024.2.2 3 | charset-normalizer==3.3.2 4 | Django==5.0.3 5 | django-utils-six==2.0 6 | django-widget-tweaks==1.5.0 7 | idna==3.6 8 | python-decouple==3.8 9 | requests==2.31.0 10 | six==1.16.0 11 | sqlparse==0.4.4 12 | typing_extensions==4.10.0 13 | urllib3==2.2.1 14 | -------------------------------------------------------------------------------- /static/css/addons/datatables.css: -------------------------------------------------------------------------------- 1 | /* 2 | * MDBootstrap integration with Datatables 3 | * Learn more: https://mdbootstrap.com/content/bootstrap-datatables/ 4 | * About MDBootstrap: https://mdbootstrap.com/ 5 | * 6 | * This combined file was created by the DataTables downloader builder: 7 | * https://datatables.net/download 8 | * 9 | * To rebuild or modify this file with the latest versions of the included 10 | * software please visit: 11 | * https://datatables.net/download/#bs4/dt-1.10.18 12 | * 13 | * Included libraries: 14 | * DataTables 1.10.18 15 | */ 16 | 17 | table.dataTable thead { 18 | cursor: pointer; 19 | } 20 | 21 | table.dataTable thead>tr>th:active { 22 | outline: none; 23 | } 24 | 25 | table.dataTable thead>tr>td:active { 26 | outline: none; 27 | } 28 | 29 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row label { 30 | margin-top: 1.2rem; 31 | margin-right: 1rem; 32 | } 33 | 34 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select span, 35 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select .select-dropdown { 36 | margin-top: 1rem; 37 | } 38 | 39 | div.dataTables_wrapper div.dataTables_length label, 40 | div.dataTables_wrapper div.dataTables_filter label { 41 | text-align: left; 42 | font-weight: normal; 43 | padding-top: .5rem; 44 | padding-bottom: .5rem; 45 | } 46 | 47 | div.dataTables_wrapper div.dataTables_length select, 48 | div.dataTables_wrapper div.dataTables_length input, 49 | div.dataTables_wrapper div.dataTables_filter select, 50 | div.dataTables_wrapper div.dataTables_filter input { 51 | width: auto; 52 | } 53 | 54 | div.dataTables_wrapper div.dataTables_filter { 55 | text-align: right; 56 | } 57 | 58 | div.dataTables_wrapper div.dataTables_filter input { 59 | margin-left: .5rem; 60 | display: inline-block; 61 | } 62 | 63 | div.dataTables_wrapper div.dataTables_info, 64 | div.dataTables_wrapper div.dataTables_paginate { 65 | font-weight: normal; 66 | padding-top: 1rem; 67 | padding-bottom: 1rem; 68 | } 69 | 70 | div.dataTables_wrapper div.dataTables_paginate { 71 | text-align: right; 72 | margin: 0; 73 | } 74 | 75 | div.dataTables_wrapper div.dataTables_paginate ul.pagination { 76 | -webkit-box-pack: end; 77 | -webkit-justify-content: flex-end; 78 | -ms-flex-pack: end; 79 | justify-content: flex-end; 80 | } 81 | 82 | div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.active .page-link:focus { 83 | background-color: #4285f4; 84 | } 85 | 86 | div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item .page-link:focus { 87 | -webkit-box-shadow: none; 88 | box-shadow: none; 89 | } 90 | 91 | @media (max-width: 767px) { 92 | div.dataTables_wrapper div .dataTables_length, 93 | div.dataTables_wrapper div .dataTables_filter, 94 | div.dataTables_wrapper div .dataTables_info, 95 | div.dataTables_wrapper div .dataTables_paginate ul.pagination { 96 | text-align: center; 97 | -webkit-box-pack: center; 98 | -webkit-justify-content: center; 99 | -ms-flex-pack: center; 100 | justify-content: center; 101 | } 102 | } 103 | 104 | .bs-select select { 105 | display: inline-block !important; 106 | } 107 | 108 | table.dataTable thead { 109 | cursor: pointer; 110 | } 111 | 112 | table.dataTable thead>tr>th:active { 113 | outline: none; 114 | } 115 | 116 | table.dataTable thead>tr>td:active { 117 | outline: none; 118 | } 119 | 120 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row label { 121 | margin-top: 1.2rem; 122 | margin-right: 1rem; 123 | } 124 | 125 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select span, 126 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select .select-dropdown { 127 | margin-top: 1rem; 128 | } 129 | 130 | div.dataTables_wrapper div.dataTables_length label, 131 | div.dataTables_wrapper div.dataTables_filter label { 132 | text-align: left; 133 | font-weight: normal; 134 | padding-top: .5rem; 135 | padding-bottom: .5rem; 136 | } 137 | 138 | div.dataTables_wrapper div.dataTables_length select, 139 | div.dataTables_wrapper div.dataTables_length input, 140 | div.dataTables_wrapper div.dataTables_filter select, 141 | div.dataTables_wrapper div.dataTables_filter input { 142 | width: auto; 143 | } 144 | 145 | div.dataTables_wrapper div.dataTables_filter { 146 | text-align: right; 147 | } 148 | 149 | div.dataTables_wrapper div.dataTables_filter input { 150 | margin-left: .5rem; 151 | display: inline-block; 152 | } 153 | 154 | div.dataTables_wrapper div.dataTables_info, 155 | div.dataTables_wrapper div.dataTables_paginate { 156 | font-weight: normal; 157 | padding-top: 1rem; 158 | padding-bottom: 1rem; 159 | } 160 | 161 | div.dataTables_wrapper div.dataTables_paginate { 162 | text-align: right; 163 | margin: 0; 164 | } 165 | 166 | div.dataTables_wrapper div.dataTables_paginate ul.pagination { 167 | -webkit-box-pack: end; 168 | -webkit-justify-content: flex-end; 169 | -ms-flex-pack: end; 170 | justify-content: flex-end; 171 | } 172 | 173 | div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.active .page-link:focus { 174 | background-color: #4285f4; 175 | } 176 | 177 | div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item .page-link:focus { 178 | -webkit-box-shadow: none; 179 | box-shadow: none; 180 | } 181 | 182 | @media (max-width: 767px) { 183 | div.dataTables_wrapper div .dataTables_length, 184 | div.dataTables_wrapper div .dataTables_filter, 185 | div.dataTables_wrapper div .dataTables_info, 186 | div.dataTables_wrapper div .dataTables_paginate ul.pagination { 187 | text-align: center; 188 | -webkit-box-pack: center; 189 | -webkit-justify-content: center; 190 | -ms-flex-pack: center; 191 | justify-content: center; 192 | } 193 | } 194 | 195 | .bs-select select { 196 | display: inline-block !important; 197 | } 198 | -------------------------------------------------------------------------------- /static/css/addons/datatables.min.css: -------------------------------------------------------------------------------- 1 | table.dataTable thead{cursor:pointer}table.dataTable thead>tr>td:active,table.dataTable thead>tr>th:active{outline:0}div.dataTables_wrapper div.dataTables_length.d-flex.flex-row label{margin-top:1.2rem;margin-right:1rem}div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select .select-dropdown,div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select span{margin-top:1rem}div.dataTables_wrapper div.dataTables_filter label,div.dataTables_wrapper div.dataTables_length label{text-align:left;font-weight:400;padding-top:.5rem;padding-bottom:.5rem}div.dataTables_wrapper div.dataTables_filter input,div.dataTables_wrapper div.dataTables_filter select,div.dataTables_wrapper div.dataTables_length input,div.dataTables_wrapper div.dataTables_length select{width:auto}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5rem;display:inline-block}div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{font-weight:400;padding-top:1rem;padding-bottom:1rem}div.dataTables_wrapper div.dataTables_paginate{text-align:right;margin:0}div.dataTables_wrapper div.dataTables_paginate ul.pagination{-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.active .page-link:focus{background-color:#4285f4}div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item .page-link:focus{-webkit-box-shadow:none;box-shadow:none}@media (max-width:767px){div.dataTables_wrapper div .dataTables_filter,div.dataTables_wrapper div .dataTables_info,div.dataTables_wrapper div .dataTables_length,div.dataTables_wrapper div .dataTables_paginate ul.pagination{text-align:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}}.bs-select select{display:inline-block!important} -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/css/style.css -------------------------------------------------------------------------------- /static/css/style.min.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/css/style.min.css -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Bold.eot -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Bold.ttf -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Light.eot -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Light.ttf -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Medium.eot -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Regular.eot -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Thin.eot -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Thin.ttf -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /static/font/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/font/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /static/img/lightbox/default-skin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/lightbox/default-skin.png -------------------------------------------------------------------------------- /static/img/lightbox/default-skin.svg: -------------------------------------------------------------------------------- 1 | default-skin 2 -------------------------------------------------------------------------------- /static/img/lightbox/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/lightbox/preloader.gif -------------------------------------------------------------------------------- /static/img/overlays/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/01.png -------------------------------------------------------------------------------- /static/img/overlays/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/02.png -------------------------------------------------------------------------------- /static/img/overlays/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/03.png -------------------------------------------------------------------------------- /static/img/overlays/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/04.png -------------------------------------------------------------------------------- /static/img/overlays/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/05.png -------------------------------------------------------------------------------- /static/img/overlays/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/06.png -------------------------------------------------------------------------------- /static/img/overlays/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/07.png -------------------------------------------------------------------------------- /static/img/overlays/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/08.png -------------------------------------------------------------------------------- /static/img/overlays/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alexmhack/Django-Signup/28e83a9740ea199295a876a300ea5e316f0ec6af/static/img/overlays/09.png -------------------------------------------------------------------------------- /static/img/svg/arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/img/svg/arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Material Design Bootstrap 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |

Material Design for Bootstrap

26 | 27 |
Thank you for using our product. We're glad you're with us.
28 | 29 |

MDB Team

30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /static/js/addons/datatables.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery"],function(t){return e(t,window,document)}):"object"==typeof exports?module.exports=function(t,a){return t||(t=window),a||(a="undefined"!=typeof window?require("jquery"):require("jquery")(t)),e(a,t,t.document)}:e(jQuery,window,document)}(function(e,t,a,r){"use strict";function A(t){var r,n,a="a aa ai ao as b fn i m o s ",i={};e.each(t,function(e,l){r=e.match(/^([^A-Z]+?)([A-Z])/),r&&-1!==a.indexOf(r[1]+" ")&&(n=e.replace(r[0],r[2].toLowerCase()),i[n]=e,"o"===r[1]&&A(t[e]))}),t._hungarianMap=i}function F(t,a,n){t._hungarianMap||A(t);var i;e.each(a,function(l,o){i=t._hungarianMap[l],i===r||!n&&a[i]!==r||("o"===i.charAt(0)?(a[i]||(a[i]={}),e.extend(!0,a[i],a[l]),F(t[i],a[i],n)):a[i]=a[l])})}function L(e){var t=n.defaults.oLanguage,a=t.sDecimal;if(a&&Xt(a),e){var r=e.sZeroRecords;!e.sEmptyTable&&r&&"No data available in table"===t.sEmptyTable&&ct(e,e,"sZeroRecords","sEmptyTable"),!e.sLoadingRecords&&r&&"Loading..."===t.sLoadingRecords&&ct(e,e,"sZeroRecords","sLoadingRecords"),e.sInfoThousands&&(e.sThousands=e.sInfoThousands);var i=e.sDecimal;i&&a!==i&&Xt(i)}}function R(e){P(e,"ordering","bSort"),P(e,"orderMulti","bSortMulti"),P(e,"orderClasses","bSortClasses"),P(e,"orderCellsTop","bSortCellsTop"),P(e,"order","aaSorting"),P(e,"orderFixed","aaSortingFixed"),P(e,"paging","bPaginate"),P(e,"pagingType","sPaginationType"),P(e,"pageLength","iDisplayLength"),P(e,"searching","bFilter"),"boolean"==typeof e.sScrollX&&(e.sScrollX=e.sScrollX?"100%":""),"boolean"==typeof e.scrollX&&(e.scrollX=e.scrollX?"100%":"");var t=e.aoSearchCols;if(t)for(var a=0,r=t.length;r>a;a++)t[a]&&F(n.models.oSearch,t[a])}function j(t){P(t,"orderable","bSortable"),P(t,"orderData","aDataSort"),P(t,"orderSequence","asSorting"),P(t,"orderDataType","sortDataType");var a=t.aDataSort;"number"!=typeof a||e.isArray(a)||(t.aDataSort=[a])}function N(a){if(!n.__browser){var r={};n.__browser=r;var i=e("
").css({position:"fixed",top:0,left:-1*e(t).scrollLeft(),height:1,width:1,overflow:"hidden"}).append(e("
").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(e("
").css({width:"100%",height:10}))).appendTo("body"),l=i.children(),o=l.children();r.barWidth=l[0].offsetWidth-l[0].clientWidth,r.bScrollOversize=100===o[0].offsetWidth&&100!==l[0].clientWidth,r.bScrollbarLeft=1!==Math.round(o.offset().left),r.bBounding=i[0].getBoundingClientRect().width?!0:!1,i.remove()}e.extend(a.oBrowser,n.__browser),a.oScroll.iBarWidth=n.__browser.barWidth}function H(e,t,a,n,i,l){var s,o=n,u=!1;for(a!==r&&(s=a,u=!0);o!==i;)e.hasOwnProperty(o)&&(s=u?t(s,e[o],o,e):e[o],u=!0,o+=l);return s}function k(t,r){var i=n.defaults.column,l=t.aoColumns.length,o=e.extend({},n.models.oColumn,i,{nTh:r?r:a.createElement("th"),sTitle:i.sTitle?i.sTitle:r?r.innerHTML:"",aDataSort:i.aDataSort?i.aDataSort:[l],mData:i.mData?i.mData:l,idx:l});t.aoColumns.push(o);var s=t.aoPreSearchCols;s[l]=e.extend({},n.models.oSearch,s[l]),O(t,l,e(r).data())}function O(t,a,i){var l=t.aoColumns[a],o=t.oClasses,s=e(l.nTh);if(!l.sWidthOrig){l.sWidthOrig=s.attr("width")||null;var u=(s.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);u&&(l.sWidthOrig=u[1])}i!==r&&null!==i&&(j(i),F(n.defaults.column,i),i.mDataProp===r||i.mData||(i.mData=i.mDataProp),i.sType&&(l._sManualType=i.sType),i.className&&!i.sClass&&(i.sClass=i.className),i.sClass&&s.addClass(i.sClass),e.extend(l,i),ct(l,i,"sWidth","sWidthOrig"),i.iDataSort!==r&&(l.aDataSort=[i.iDataSort]),ct(l,i,"aDataSort"));var f=l.mData,c=ee(f),d=l.mRender?ee(l.mRender):null,h=function(e){return"string"==typeof e&&-1!==e.indexOf("@")};l._bAttrSrc=e.isPlainObject(f)&&(h(f.sort)||h(f.type)||h(f.filter)),l._setter=null,l.fnGetData=function(e,t,a){var n=c(e,t,r,a);return d&&t?d(n,t,e,a):n},l.fnSetData=function(e,t,a){return te(f)(e,t,a)},"number"!=typeof f&&(t._rowReadObject=!0),t.oFeatures.bSort||(l.bSortable=!1,s.addClass(o.sSortableNone));var p=-1!==e.inArray("asc",l.asSorting),v=-1!==e.inArray("desc",l.asSorting);l.bSortable&&(p||v)?p&&!v?(l.sSortingClass=o.sSortableAsc,l.sSortingClassJUI=o.sSortJUIAscAllowed):!p&&v?(l.sSortingClass=o.sSortableDesc,l.sSortingClassJUI=o.sSortJUIDescAllowed):(l.sSortingClass=o.sSortable,l.sSortingClassJUI=o.sSortJUI):(l.sSortingClass=o.sSortableNone,l.sSortingClassJUI="")}function M(e){if(e.oFeatures.bAutoWidth!==!1){var t=e.aoColumns;$e(e);for(var a=0,r=t.length;r>a;a++)t[a].nTh.style.width=t[a].sWidth}var n=e.oScroll;(""!==n.sY||""!==n.sX)&&Je(e),vt(e,null,"column-sizing",[e])}function W(e,t){var a=U(e,"bVisible");return"number"==typeof a[t]?a[t]:null}function B(t,a){var r=U(t,"bVisible"),n=e.inArray(a,r);return-1!==n?n:null}function E(t){var a=0;return e.each(t.aoColumns,function(t,r){r.bVisible&&"none"!==e(r.nTh).css("display")&&a++}),a}function U(t,a){var r=[];return e.map(t.aoColumns,function(e,t){e[a]&&r.push(t)}),r}function V(e){var l,o,s,u,f,c,d,p,v,t=e.aoColumns,a=e.aoData,i=n.ext.type.detect;for(l=0,o=t.length;o>l;l++)if(d=t[l],v=[],!d.sType&&d._sManualType)d.sType=d._sManualType;else if(!d.sType){for(s=0,u=i.length;u>s;s++){for(f=0,c=a.length;c>f&&(v[f]===r&&(v[f]=z(e,f,l,"type")),p=i[s](v[f],e),p||s===i.length-1)&&"html"!==p;f++);if(p){d.sType=p;break}}d.sType||(d.sType="string")}}function X(t,a,n,i){var l,o,s,u,f,c,d,h=t.aoColumns;if(a)for(l=a.length-1;l>=0;l--){d=a[l];var p=d.targets!==r?d.targets:d.aTargets;for(e.isArray(p)||(p=[p]),s=0,u=p.length;u>s;s++)if("number"==typeof p[s]&&p[s]>=0){for(;h.length<=p[s];)k(t);i(p[s],d)}else if("number"==typeof p[s]&&p[s]<0)i(h.length+p[s],d);else if("string"==typeof p[s])for(f=0,c=h.length;c>f;f++)("_all"==p[s]||e(h[f].nTh).hasClass(p[s]))&&i(f,d)}if(n)for(l=0,o=n.length;o>l;l++)i(l,n[l])}function J(t,a,i,l){var o=t.aoData.length,s=e.extend(!0,{},n.models.oRow,{src:i?"dom":"data",idx:o});s._aData=a,t.aoData.push(s);for(var c=t.aoColumns,d=0,h=c.length;h>d;d++)c[d].sType=null;t.aiDisplayMaster.push(o);var p=t.rowIdFn(a);return p!==r&&(t.aIds[p]=s),(i||!t.oFeatures.bDeferRender)&&oe(t,o,i,l),o}function q(t,a){var r;return a instanceof e||(a=e(a)),a.map(function(e,a){return r=le(t,a),J(t,r.data,a,r.cells)})}function G(e,t){return t._DT_RowIndex!==r?t._DT_RowIndex:null}function $(t,a,r){return e.inArray(r,t.aoData[a].anCells)}function z(e,t,a,n){var i=e.iDraw,l=e.aoColumns[a],o=e.aoData[t]._aData,s=l.sDefaultContent,u=l.fnGetData(o,n,{settings:e,row:t,col:a});if(u===r)return e.iDrawError!=i&&null===s&&(ft(e,0,"Requested unknown parameter "+("function"==typeof l.mData?"{function}":"'"+l.mData+"'")+" for row "+t+", column "+a,4),e.iDrawError=i),s;if(u!==o&&null!==u||null===s||n===r){if("function"==typeof u)return u.call(o)}else u=s;return null===u&&"display"==n?"":u}function Y(e,t,a,r){var n=e.aoColumns[a],i=e.aoData[t]._aData;n.fnSetData(i,r,{settings:e,row:t,col:a})}function K(t){return e.map(t.match(/(\\.|[^\.])+/g)||[""],function(e){return e.replace(/\\\./g,".")})}function ee(t){if(e.isPlainObject(t)){var a={};return e.each(t,function(e,t){t&&(a[e]=ee(t))}),function(e,t,n,i){var l=a[t]||a._;return l!==r?l(e,t,n,i):e}}if(null===t)return function(e){return e};if("function"==typeof t)return function(e,a,r,n){return t(e,a,r,n)};if("string"!=typeof t||-1===t.indexOf(".")&&-1===t.indexOf("[")&&-1===t.indexOf("("))return function(e,a){return e[t]};var n=function(t,a,i){var l,o,s,u;if(""!==i)for(var f=K(i),c=0,d=f.length;d>c;c++){if(l=f[c].match(Z),o=f[c].match(Q),l){if(f[c]=f[c].replace(Z,""),""!==f[c]&&(t=t[f[c]]),s=[],f.splice(0,c+1),u=f.join("."),e.isArray(t))for(var h=0,p=t.length;p>h;h++)s.push(n(t[h],a,u));var v=l[0].substring(1,l[0].length-1);t=""===v?s:s.join(v);break}if(o)f[c]=f[c].replace(Q,""),t=t[f[c]]();else{if(null===t||t[f[c]]===r)return r;t=t[f[c]]}}return t};return function(e,a){return n(e,a,t)}}function te(t){if(e.isPlainObject(t))return te(t._);if(null===t)return function(){};if("function"==typeof t)return function(e,a,r){t(e,"set",a,r)};if("string"!=typeof t||-1===t.indexOf(".")&&-1===t.indexOf("[")&&-1===t.indexOf("("))return function(e,a){e[t]=a};var a=function(t,n,i){for(var o,u,f,c,d,l=K(i),s=l[l.length-1],h=0,p=l.length-1;p>h;h++){if(u=l[h].match(Z),f=l[h].match(Q),u){if(l[h]=l[h].replace(Z,""),t[l[h]]=[],o=l.slice(),o.splice(0,h+1),d=o.join("."),e.isArray(n))for(var v=0,g=n.length;g>v;v++)c={},a(c,n[v],d),t[l[h]].push(c);else t[l[h]]=n;return}f&&(l[h]=l[h].replace(Q,""),t=t[l[h]](n)),(null===t[l[h]]||t[l[h]]===r)&&(t[l[h]]={}),t=t[l[h]]}s.match(Q)?t=t[s.replace(Q,"")](n):t[s.replace(Z,"")]=n};return function(e,r){return a(e,r,t)}}function ae(e){return y(e.aoData,"_aData")}function re(e){e.aoData.length=0,e.aiDisplayMaster.length=0,e.aiDisplay.length=0,e.aIds={}}function ne(e,t,a){for(var n=-1,i=0,l=e.length;l>i;i++)e[i]==t?n=i:e[i]>t&&e[i]--;-1!=n&&a===r&&e.splice(n,1)}function ie(e,t,a,n){var l,o,i=e.aoData[t],s=function(a,r){for(;a.childNodes.length;)a.removeChild(a.firstChild);a.innerHTML=z(e,t,r,"display")};if("dom"!==a&&(a&&"auto"!==a||"dom"!==i.src)){var u=i.anCells;if(u)if(n!==r)s(u[n],n);else for(l=0,o=u.length;o>l;l++)s(u[l],l)}else i._aData=le(e,i,n,n===r?r:i._aData).data;i._aSortData=null,i._aFilterData=null;var f=e.aoColumns;if(n!==r)f[n].sType=null;else{for(l=0,o=f.length;o>l;l++)f[l].sType=null;se(e,i)}}function le(t,a,n,i){var s,u,d,l=[],o=a.firstChild,c=0,h=t.aoColumns,p=t._rowReadObject;i=i!==r?i:p?{}:[];var v=function(e,t){if("string"==typeof e){var a=e.indexOf("@");if(-1!==a){var r=e.substring(a+1),n=te(e);n(i,t.getAttribute(r))}}},g=function(t){if(n===r||n===c)if(u=h[c],d=e.trim(t.innerHTML),u&&u._bAttrSrc){var a=te(u.mData._);a(i,d),v(u.mData.sort,t),v(u.mData.type,t),v(u.mData.filter,t)}else p?(u._setter||(u._setter=te(u.mData)),u._setter(i,d)):i[c]=d;c++};if(o)for(;o;)s=o.nodeName.toUpperCase(),("TD"==s||"TH"==s)&&(g(o),l.push(o)),o=o.nextSibling;else{l=a.anCells;for(var b=0,m=l.length;m>b;b++)g(l[b])}var S=a.firstChild?a:a.nTr;if(S){var D=S.getAttribute("id");D&&te(t.rowId)(i,D)}return{data:i,cells:l}}function oe(t,r,n,i){var u,f,c,d,h,l=t.aoData[r],o=l._aData,s=[];if(null===l.nTr){for(u=n||a.createElement("tr"),l.nTr=u,l.anCells=s,u._DT_RowIndex=r,se(t,l),d=0,h=t.aoColumns.length;h>d;d++)c=t.aoColumns[d],f=n?i[d]:a.createElement(c.sCellType),f._DT_CellIndex={row:r,column:d},s.push(f),n&&!c.mRender&&c.mData===d||e.isPlainObject(c.mData)&&c.mData._===d+".display"||(f.innerHTML=z(t,r,d,"display")),c.sClass&&(f.className+=" "+c.sClass),c.bVisible&&!n?u.appendChild(f):!c.bVisible&&n&&f.parentNode.removeChild(f),c.fnCreatedCell&&c.fnCreatedCell.call(t.oInstance,f,z(t,r,d),o,r,d);vt(t,"aoRowCreatedCallback",null,[u,o,r,s])}l.nTr.setAttribute("role","row")}function se(t,a){var r=a.nTr,n=a._aData;if(r){var i=t.rowIdFn(n);if(i&&(r.id=i),n.DT_RowClass){var l=n.DT_RowClass.split(" ");a.__rowc=a.__rowc?I(a.__rowc.concat(l)):l,e(r).removeClass(a.__rowc.join(" ")).addClass(n.DT_RowClass)}n.DT_RowAttr&&e(r).attr(n.DT_RowAttr),n.DT_RowData&&e(r).data(n.DT_RowData)}}function ue(t){var a,r,n,i,l,o=t.nTHead,s=t.nTFoot,u=0===e("th, td",o).length,f=t.oClasses,c=t.aoColumns;for(u&&(i=e("").appendTo(o)),a=0,r=c.length;r>a;a++)l=c[a],n=e(l.nTh).addClass(l.sClass),u&&n.appendTo(i),t.oFeatures.bSort&&(n.addClass(l.sSortingClass),l.bSortable!==!1&&(n.attr("tabindex",t.iTabIndex).attr("aria-controls",t.sTableId),nt(t,l.nTh,a))),l.sTitle!=n[0].innerHTML&&n.html(l.sTitle),bt(t,"header")(t,n,l,f);if(u&&pe(t.aoHeader,o),e(o).find(">tr").attr("role","row"),e(o).find(">tr>th, >tr>td").addClass(f.sHeaderTH),e(s).find(">tr>th, >tr>td").addClass(f.sFooterTH),null!==s){var d=t.aoFooter[0];for(a=0,r=d.length;r>a;a++)l=c[a],l.nTf=d[a].cell,l.sClass&&e(l.nTf).addClass(l.sClass)}}function fe(t,a,n){var i,l,o,s,u,c,d,g,b,h=[],p=[],v=t.aoColumns.length;if(a){for(n===r&&(n=!1),i=0,l=a.length;l>i;i++){for(h[i]=a[i].slice(),h[i].nTr=a[i].nTr,o=v-1;o>=0;o--)t.aoColumns[o].bVisible||n||h[i].splice(o,1);p.push([])}for(i=0,l=h.length;l>i;i++){if(d=h[i].nTr)for(;c=d.firstChild;)d.removeChild(c);for(o=0,s=h[i].length;s>o;o++)if(g=1,b=1,p[i][o]===r){for(d.appendChild(h[i][o].cell),p[i][o]=1;h[i+g]!==r&&h[i][o].cell==h[i+g][o].cell;)p[i+g][o]=1,g++;for(;h[i][o+b]!==r&&h[i][o].cell==h[i][o+b].cell;){for(u=0;g>u;u++)p[i+u][o+b]=1;b++}e(h[i][o].cell).attr("rowspan",g).attr("colspan",b)}}}}function ce(t){var a=vt(t,"aoPreDrawCallback","preDraw",[t]);if(-1!==e.inArray(!1,a))return void Ve(t,!1);var o=[],s=0,u=t.asStripeClasses,f=u.length,d=(t.aoOpenRows.length,t.oLanguage),h=t.iInitDisplayStart,p="ssp"==mt(t),v=t.aiDisplay;t.bDrawing=!0,h!==r&&-1!==h&&(t._iDisplayStart=p?h:h>=t.fnRecordsDisplay()?0:h,t.iInitDisplayStart=-1);var g=t._iDisplayStart,b=t.fnDisplayEnd();if(t.bDeferLoading)t.bDeferLoading=!1,t.iDraw++,Ve(t,!1);else if(p){if(!t.bDestroying&&!be(t))return}else t.iDraw++;if(0!==v.length)for(var m=p?0:g,S=p?t.aoData.length:b,D=m;S>D;D++){var y=v[D],_=t.aoData[y];null===_.nTr&&oe(t,y);var w=_.nTr;if(0!==f){var T=u[s%f];_._sRowStripe!=T&&(e(w).removeClass(_._sRowStripe).addClass(T),_._sRowStripe=T)}vt(t,"aoRowCallback",null,[w,_._aData,s,D,y]),o.push(w),s++}else{var C=d.sZeroRecords;1==t.iDraw&&"ajax"==mt(t)?C=d.sLoadingRecords:d.sEmptyTable&&0===t.fnRecordsTotal()&&(C=d.sEmptyTable),o[0]=e("",{"class":f?u[0]:""}).append(e("",{valign:"top",colSpan:E(t),"class":t.oClasses.sRowEmpty}).html(C))[0]}vt(t,"aoHeaderCallback","header",[e(t.nTHead).children("tr")[0],ae(t),g,b,v]),vt(t,"aoFooterCallback","footer",[e(t.nTFoot).children("tr")[0],ae(t),g,b,v]);var x=e(t.nTBody);x.children().detach(),x.append(e(o)),vt(t,"aoDrawCallback","draw",[t]),t.bSorted=!1,t.bFiltered=!1,t.bDrawing=!1}function de(e,t){var a=e.oFeatures,r=a.bSort,n=a.bFilter;r&&tt(e),n?_e(e,e.oPreviousSearch):e.aiDisplay=e.aiDisplayMaster.slice(),t!==!0&&(e._iDisplayStart=0),e._drawHold=t,ce(e),e._drawHold=!1}function he(t){var a=t.oClasses,r=e(t.nTable),i=e("
").insertBefore(r),l=t.oFeatures,o=e("
",{id:t.sTableId+"_wrapper","class":a.sWrapper+(t.nTFoot?"":" "+a.sNoFooter)});t.nHolding=i[0],t.nTableWrapper=o[0],t.nTableReinsertBefore=t.nTable.nextSibling;for(var u,f,c,d,h,p,s=t.sDom.split(""),v=0;v")[0],d=s[v+1],"'"==d||'"'==d){for(h="",p=2;s[v+p]!=d;)h+=s[v+p],p++;if("H"==h?h=a.sJUIHeader:"F"==h&&(h=a.sJUIFooter),-1!=h.indexOf(".")){var g=h.split(".");c.id=g[0].substr(1,g[0].length-1),c.className=g[1]}else"#"==h.charAt(0)?c.id=h.substr(1,h.length-1):c.className=h;v+=p}o.append(c),o=e(c)}else if(">"==f)o=o.parent();else if("l"==f&&l.bPaginate&&l.bLengthChange)u=We(t);else if("f"==f&&l.bFilter)u=ye(t);else if("r"==f&&l.bProcessing)u=Ue(t);else if("t"==f)u=Xe(t);else if("i"==f&&l.bInfo)u=je(t);else if("p"==f&&l.bPaginate)u=Be(t);else if(0!==n.ext.feature.length)for(var b=n.ext.feature,m=0,S=b.length;S>m;m++)if(f==b[m].cFeature){u=b[m].fnInit(t);break}if(u){var D=t.aanFeatures;D[f]||(D[f]=[]),D[f].push(u),o.append(u)}}i.replaceWith(o),t.nHolding=null}function pe(t,a){var n,i,l,o,s,u,c,d,h,p,v,r=e(a).children("tr"),g=function(e,t,a){for(var r=e[t];r[a];)a++;return a};for(t.splice(0,t.length),l=0,u=r.length;u>l;l++)t.push([]);for(l=0,u=r.length;u>l;l++)for(n=r[l],d=0,i=n.firstChild;i;){if("TD"==i.nodeName.toUpperCase()||"TH"==i.nodeName.toUpperCase())for(h=1*i.getAttribute("colspan"),p=1*i.getAttribute("rowspan"),h=h&&0!==h&&1!==h?h:1,p=p&&0!==p&&1!==p?p:1,c=g(t,l,d),v=1===h?!0:!1,s=0;h>s;s++)for(o=0;p>o;o++)t[l+o][c+s]={cell:i,unique:v},t[l+o].nTr=n;i=i.nextSibling}}function ve(e,t,a){var r=[];a||(a=e.aoHeader,t&&(a=[],pe(a,t)));for(var n=0,i=a.length;i>n;n++)for(var l=0,o=a[n].length;o>l;l++)!a[n][l].unique||r[l]&&e.bSortCellsTop||(r[l]=a[n][l].cell);return r}function ge(t,a,r){if(vt(t,"aoServerParams","serverParams",[a]),a&&e.isArray(a)){var n={},i=/(.*?)\[\]$/;e.each(a,function(e,t){var a=t.name.match(i);if(a){var r=a[0];n[r]||(n[r]=[]),n[r].push(t.value)}else n[t.name]=t.value}),a=n}var l,o=t.ajax,s=t.oInstance,u=function(e){vt(t,null,"xhr",[t,e,t.jqXHR]),r(e)};if(e.isPlainObject(o)&&o.data){l=o.data;var f="function"==typeof l?l(a,t):l;a="function"==typeof l&&f?f:e.extend(!0,a,f),delete o.data}var c={data:a,success:function(e){var a=e.error||e.sError;a&&ft(t,0,a),t.json=e,u(e)},dataType:"json",cache:!1,type:t.sServerMethod,error:function(a,r,n){var i=vt(t,null,"xhr",[t,null,t.jqXHR]);-1===e.inArray(!0,i)&&("parsererror"==r?ft(t,0,"Invalid JSON response",1):4===a.readyState&&ft(t,0,"Ajax error",7)),Ve(t,!1)}};t.oAjaxData=a,vt(t,null,"preXhr",[t,a]),t.fnServerData?t.fnServerData.call(s,t.sAjaxSource,e.map(a,function(e,t){return{name:t,value:e}}),u,t):t.sAjaxSource||"string"==typeof o?t.jqXHR=e.ajax(e.extend(c,{url:o||t.sAjaxSource})):"function"==typeof o?t.jqXHR=o.call(s,a,u,t):(t.jqXHR=e.ajax(e.extend(c,o)),o.data=l)}function be(e){return e.bAjaxDataGet?(e.iDraw++,Ve(e,!0),ge(e,me(e),function(t){Se(e,t)}),!1):!0}function me(t){var s,f,c,d,a=t.aoColumns,r=a.length,i=t.oFeatures,l=t.oPreviousSearch,o=t.aoPreSearchCols,u=[],h=et(t),p=t._iDisplayStart,v=i.bPaginate!==!1?t._iDisplayLength:-1,g=function(e,t){u.push({name:e,value:t})};g("sEcho",t.iDraw),g("iColumns",r),g("sColumns",y(a,"sName").join(",")),g("iDisplayStart",p),g("iDisplayLength",v);var b={draw:t.iDraw,columns:[],order:[],start:p,length:v,search:{value:l.sSearch,regex:l.bRegex}};for(s=0;r>s;s++)c=a[s],d=o[s],f="function"==typeof c.mData?"function":c.mData,b.columns.push({data:f,name:c.sName,searchable:c.bSearchable,orderable:c.bSortable,search:{value:d.sSearch,regex:d.bRegex}}),g("mDataProp_"+s,f),i.bFilter&&(g("sSearch_"+s,d.sSearch),g("bRegex_"+s,d.bRegex),g("bSearchable_"+s,c.bSearchable)),i.bSort&&g("bSortable_"+s,c.bSortable);i.bFilter&&(g("sSearch",l.sSearch),g("bRegex",l.bRegex)),i.bSort&&(e.each(h,function(e,t){b.order.push({column:t.col,dir:t.dir}),g("iSortCol_"+e,t.col),g("sSortDir_"+e,t.dir)}),g("iSortingCols",h.length));var m=n.ext.legacy.ajax;return null===m?t.sAjaxSource?u:b:m?u:b}function Se(e,t){var a=function(e,a){return t[e]!==r?t[e]:t[a]},n=De(e,t),i=a("sEcho","draw"),l=a("iTotalRecords","recordsTotal"),o=a("iTotalDisplayRecords","recordsFiltered");if(i){if(1*is;s++)J(e,n[s]);e.aiDisplay=e.aiDisplayMaster.slice(),e.bAjaxDataGet=!1,ce(e),e._bInitComplete||Oe(e,t),e.bAjaxDataGet=!0,Ve(e,!1)}function De(t,a){var n=e.isPlainObject(t.ajax)&&t.ajax.dataSrc!==r?t.ajax.dataSrc:t.sAjaxDataProp;return"data"===n?a.aaData||a[n]:""!==n?ee(n)(a):a}function ye(t){var r=t.oClasses,n=t.sTableId,i=t.oLanguage,l=t.oPreviousSearch,o=t.aanFeatures,s='',u=i.sSearch;u=u.match(/_INPUT_/)?u.replace("_INPUT_",s):u+s;var f=e("
",{id:o.f?null:n+"_filter","class":r.sFilter}).append(e("