kopia lustrzana https://github.com/OpenDroneMap/WebODM
Merge pull request #203 from pierotofy/defaultuser
Welcome screen to create a default adminpull/210/head
commit
fa63913040
|
@ -50,7 +50,6 @@ docker-machine ip
|
|||
Linux / Mac, users can connect to 127.0.0.1.
|
||||
|
||||
* Open a Web Browser to `http://<yourDockerMachineIp>:8000`
|
||||
* Log in with the default credentials: "admin:admin"
|
||||
|
||||
To stop WebODM press CTRL+C or run:
|
||||
|
||||
|
|
|
@ -50,11 +50,6 @@ def boot():
|
|||
# Add permission to view processing nodes
|
||||
default_group.permissions.add(Permission.objects.get(codename="view_processingnode"))
|
||||
|
||||
# Check super user
|
||||
if User.objects.filter(is_superuser=True).count() == 0:
|
||||
User.objects.create_superuser('admin', 'admin@example.com', 'admin')
|
||||
logger.info("Created superuser")
|
||||
|
||||
# Unlock any Task that might have been locked
|
||||
Task.objects.filter(processing_lock=True).update(processing_lock=False)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{% block page-wrapper %}
|
||||
<section class="main">
|
||||
<div class="content">
|
||||
{% block registration_header %}{% endblock %}
|
||||
<div class="col-sm-12 col-md-6 col-md-offset-3 top-buffer">
|
||||
{% block registration_content %}{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
{% extends 'registration/registration_base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block registration_header %}
|
||||
|
||||
{% if not firstuserform.errors %}
|
||||
<canvas id="canvas" style="position: absolute; top: 0; left: 0;"></canvas>
|
||||
<script type="text/javascript">
|
||||
// Confetti animation
|
||||
(function () {
|
||||
// globals
|
||||
var canvas;
|
||||
var ctx;
|
||||
var W;
|
||||
var H;
|
||||
var mp = 150; //max particles
|
||||
var particles = [];
|
||||
var angle = 0;
|
||||
var tiltAngle = 0;
|
||||
var confettiActive = true;
|
||||
var animationComplete = true;
|
||||
var deactivationTimerHandler;
|
||||
var reactivationTimerHandler;
|
||||
var animationHandler;
|
||||
|
||||
// objects
|
||||
|
||||
var particleColors = {
|
||||
colorOptions: ["DodgerBlue", "OliveDrab", "Gold", "pink", "SlateBlue", "lightblue", "Violet", "PaleGreen", "SteelBlue", "SandyBrown", "Chocolate", "Crimson"],
|
||||
colorIndex: 0,
|
||||
colorIncrementer: 0,
|
||||
colorThreshold: 10,
|
||||
getColor: function () {
|
||||
if (this.colorIncrementer >= 10) {
|
||||
this.colorIncrementer = 0;
|
||||
this.colorIndex++;
|
||||
if (this.colorIndex >= this.colorOptions.length) {
|
||||
this.colorIndex = 0;
|
||||
}
|
||||
}
|
||||
this.colorIncrementer++;
|
||||
return this.colorOptions[this.colorIndex];
|
||||
}
|
||||
}
|
||||
|
||||
function confettiParticle(color) {
|
||||
this.x = Math.random() * W; // x-coordinate
|
||||
this.y = (Math.random() * H) - H; //y-coordinate
|
||||
this.r = RandomFromTo(10, 30); //radius;
|
||||
this.d = (Math.random() * mp) + 10; //density;
|
||||
this.color = color;
|
||||
this.tilt = Math.floor(Math.random() * 10) - 10;
|
||||
this.tiltAngleIncremental = (Math.random() * 0.07) + .05;
|
||||
this.tiltAngle = 0;
|
||||
|
||||
this.draw = function () {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = this.r / 2;
|
||||
ctx.strokeStyle = this.color;
|
||||
ctx.moveTo(this.x + this.tilt + (this.r / 4), this.y);
|
||||
ctx.lineTo(this.x + this.tilt, this.y + this.tilt + (this.r / 4));
|
||||
return ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
SetGlobals();
|
||||
InitializeButton();
|
||||
InitializeConfetti();
|
||||
|
||||
$(window).resize(function () {
|
||||
W = window.innerWidth;
|
||||
H = window.innerHeight;
|
||||
canvas.width = W;
|
||||
canvas.height = H;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function InitializeButton() {
|
||||
$("input").click(DeactivateConfetti);
|
||||
}
|
||||
|
||||
function SetGlobals() {
|
||||
canvas = document.getElementById("canvas");
|
||||
ctx = canvas.getContext("2d");
|
||||
W = window.innerWidth;
|
||||
H = window.innerHeight;
|
||||
canvas.width = W;
|
||||
canvas.height = H;
|
||||
}
|
||||
|
||||
function InitializeConfetti() {
|
||||
particles = [];
|
||||
animationComplete = false;
|
||||
for (var i = 0; i < mp; i++) {
|
||||
var particleColor = particleColors.getColor();
|
||||
particles.push(new confettiParticle(particleColor));
|
||||
}
|
||||
StartConfetti();
|
||||
}
|
||||
|
||||
function Draw() {
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
var results = [];
|
||||
for (var i = 0; i < mp; i++) {
|
||||
(function (j) {
|
||||
results.push(particles[j].draw());
|
||||
})(i);
|
||||
}
|
||||
Update();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function RandomFromTo(from, to) {
|
||||
return Math.floor(Math.random() * (to - from + 1) + from);
|
||||
}
|
||||
|
||||
|
||||
function Update() {
|
||||
var remainingFlakes = 0;
|
||||
var particle;
|
||||
angle += 0.01;
|
||||
tiltAngle += 0.1;
|
||||
|
||||
for (var i = 0; i < mp; i++) {
|
||||
particle = particles[i];
|
||||
if (animationComplete) return;
|
||||
|
||||
if (!confettiActive && particle.y < -15) {
|
||||
particle.y = H + 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
stepParticle(particle, i);
|
||||
|
||||
if (particle.y <= H) {
|
||||
remainingFlakes++;
|
||||
}
|
||||
CheckForReposition(particle, i);
|
||||
}
|
||||
|
||||
if (remainingFlakes === 0) {
|
||||
StopConfetti();
|
||||
}
|
||||
}
|
||||
|
||||
function CheckForReposition(particle, index) {
|
||||
if ((particle.x > W + 20 || particle.x < -20 || particle.y > H) && confettiActive) {
|
||||
if (index % 5 > 0 || index % 2 == 0) //66.67% of the flakes
|
||||
{
|
||||
repositionParticle(particle, Math.random() * W, -10, Math.floor(Math.random() * 10) - 20);
|
||||
} else {
|
||||
if (Math.sin(angle) > 0) {
|
||||
//Enter from the left
|
||||
repositionParticle(particle, -20, Math.random() * H, Math.floor(Math.random() * 10) - 20);
|
||||
} else {
|
||||
//Enter from the right
|
||||
repositionParticle(particle, W + 20, Math.random() * H, Math.floor(Math.random() * 10) - 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function stepParticle(particle, particleIndex) {
|
||||
particle.tiltAngle += particle.tiltAngleIncremental;
|
||||
particle.y += (Math.cos(angle + particle.d) + 3 + particle.r / 2) / 2;
|
||||
particle.x += Math.sin(angle);
|
||||
particle.tilt = (Math.sin(particle.tiltAngle - (particleIndex / 3))) * 15;
|
||||
}
|
||||
|
||||
function repositionParticle(particle, xCoordinate, yCoordinate, tilt) {
|
||||
particle.x = xCoordinate;
|
||||
particle.y = yCoordinate;
|
||||
particle.tilt = tilt;
|
||||
}
|
||||
|
||||
function StartConfetti() {
|
||||
W = window.innerWidth;
|
||||
H = window.innerHeight;
|
||||
canvas.width = W;
|
||||
canvas.height = H;
|
||||
(function animloop() {
|
||||
if (animationComplete) return null;
|
||||
animationHandler = requestAnimFrame(animloop);
|
||||
return Draw();
|
||||
})();
|
||||
}
|
||||
|
||||
function ClearTimers() {
|
||||
clearTimeout(reactivationTimerHandler);
|
||||
clearTimeout(animationHandler);
|
||||
}
|
||||
|
||||
function DeactivateConfetti() {
|
||||
confettiActive = false;
|
||||
ClearTimers();
|
||||
}
|
||||
|
||||
function StopConfetti() {
|
||||
animationComplete = true;
|
||||
if (ctx == undefined) return;
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
}
|
||||
|
||||
window.requestAnimFrame = (function () {
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
return window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})();
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block registration_content %}
|
||||
{% if firstuserform.errors %}
|
||||
<div class="alert alert-warning">
|
||||
<p>{% trans "Uh oh! Could you please check the errors below?" %}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3>{% trans 'Welcome! ☺' %}</h3>
|
||||
<div style="margin-bottom: 28px;">Before we get started, we need to create an administrator account for you:</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post" class="form-horizontal" role="form">{% csrf_token %}
|
||||
{% for field in firstuserform %}
|
||||
{% include 'registration/form_field.html' %}
|
||||
{% endfor %}
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-default">{% trans 'Create Account' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -0,0 +1,72 @@
|
|||
from django.contrib.auth.models import User, Group
|
||||
from django.test import Client
|
||||
from rest_framework import status
|
||||
|
||||
from app.models import Project, Task
|
||||
from .classes import BootTestCase
|
||||
from app import scheduler
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
class TestWelcome(BootTestCase):
|
||||
|
||||
def setUp(self):
|
||||
Project.objects.all().delete()
|
||||
|
||||
# Start with no users
|
||||
User.objects.all().delete()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_first_screen(self):
|
||||
c = Client()
|
||||
|
||||
# User points the browser to the website
|
||||
res = c.get('/', follow=True)
|
||||
|
||||
# User is redirected to the welcome page
|
||||
self.assertRedirects(res, '/welcome/')
|
||||
|
||||
# The welcome page is being rendered by the correct template
|
||||
self.assertTemplateUsed(res, 'app/welcome.html')
|
||||
|
||||
# User cannot create an admin user without password
|
||||
res = c.post('/welcome/', data={
|
||||
'username': 'testadminuser',
|
||||
'password': ''}, follow=True)
|
||||
self.assertFormError(res, 'firstuserform', 'password', 'This field is required.')
|
||||
self.assertTrue(User.objects.count() == 0, 'No users were created')
|
||||
|
||||
# User can create admin user
|
||||
res = c.post('/welcome/', data={
|
||||
'username': 'testadminuser',
|
||||
'password': 'testadminpass'}, follow=True)
|
||||
self.assertTrue(User.objects.count() == 1, 'A user was created')
|
||||
|
||||
# User was created
|
||||
user = User.objects.get(username='testadminuser')
|
||||
self.assertTrue(user.is_superuser, 'The user is a superuser')
|
||||
self.assertTrue(user.is_staff, 'The user is a staff member')
|
||||
|
||||
# Redirect to the dashboard happens
|
||||
self.assertRedirects(res, '/dashboard/')
|
||||
|
||||
# User is automatically logged-in
|
||||
self.assertTrue(res.context['user'].is_authenticated)
|
||||
|
||||
# After a super admin is created, the welcome page is no longer accessible
|
||||
res = c.get('/welcome/', follow=True)
|
||||
self.assertRedirects(res, '/dashboard/')
|
||||
|
||||
# We cannot create another superuser
|
||||
res = c.post('/welcome/', data={
|
||||
'username': 'testadminuser2',
|
||||
'password': 'testadminpass2'}, follow=True)
|
||||
self.assertRedirects(res, '/dashboard/')
|
||||
|
||||
self.assertTrue(User.objects.filter(username='testadminuser2').count() == 0, 'No users were created')
|
||||
|
||||
# If we log-out and try to access the welcome page, we should get the login page
|
||||
c.logout()
|
||||
res = c.get('/welcome/', follow=True)
|
||||
self.assertRedirects(res, '/login/')
|
|
@ -6,6 +6,7 @@ from webodm import settings
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.index, name='index'),
|
||||
url(r'^welcome/$', views.welcome, name='welcome'),
|
||||
url(r'^dashboard/$', views.dashboard, name='dashboard'),
|
||||
url(r'^map/project/(?P<project_pk>[^/.]+)/task/(?P<task_pk>[^/.]+)/$', views.map, name='map'),
|
||||
url(r'^map/project/(?P<project_pk>[^/.]+)/$', views.map, name='map'),
|
||||
|
|
41
app/views.py
41
app/views.py
|
@ -1,8 +1,9 @@
|
|||
import json
|
||||
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.http import HttpResponse
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
|
||||
from nodeodm.models import ProcessingNode
|
||||
|
@ -10,8 +11,14 @@ from .models import Project, Task
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.translation import ugettext as _
|
||||
from django import forms
|
||||
|
||||
def index(request):
|
||||
# Check first access where the user is expected to
|
||||
# create an admin account
|
||||
if User.objects.filter(is_superuser=True).count() == 0:
|
||||
return redirect('welcome')
|
||||
|
||||
return redirect('dashboard' if request.user.is_authenticated()
|
||||
else 'login')
|
||||
|
||||
|
@ -100,6 +107,38 @@ def processing_node(request, processing_node_id):
|
|||
**get_view_params(request),
|
||||
})
|
||||
|
||||
class FirstUserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'password', )
|
||||
widgets = {
|
||||
'password': forms.PasswordInput(),
|
||||
}
|
||||
|
||||
|
||||
def welcome(request):
|
||||
if User.objects.filter(is_superuser=True).count() > 0:
|
||||
return redirect('index')
|
||||
|
||||
fuf = FirstUserForm()
|
||||
|
||||
if request.method == 'POST':
|
||||
fuf = FirstUserForm(request.POST)
|
||||
if fuf.is_valid():
|
||||
admin_user = fuf.save(commit=False)
|
||||
admin_user.is_superuser = admin_user.is_staff = True
|
||||
admin_user.save()
|
||||
|
||||
# Log-in automatically
|
||||
login(request, admin_user, 'django.contrib.auth.backends.ModelBackend')
|
||||
return redirect('dashboard')
|
||||
|
||||
return render(request, 'app/welcome.html',
|
||||
{
|
||||
'title': 'Welcome',
|
||||
'firstuserform': fuf
|
||||
})
|
||||
|
||||
|
||||
def get_view_params(request):
|
||||
"""
|
||||
|
|
Ładowanie…
Reference in New Issue