Compare commits
No commits in common. "master" and "dev" have entirely different histories.
2
backend
2
backend
@ -1 +1 @@
|
|||||||
Subproject commit f939127a0c9099f9c22c39bfaaed33b70b006fa2
|
Subproject commit 45cd71cc4bcddf23f46b1eddcffd8c7fda2b9d41
|
@ -1,14 +0,0 @@
|
|||||||
from server import db
|
|
||||||
import time
|
|
||||||
from database.models import Device
|
|
||||||
|
|
||||||
def cleanDevices():
|
|
||||||
allDevs = db.session.query(Device)
|
|
||||||
devices = allDevs.filter(Device.lastConnection <= int(round(time.time())) - 60*60*24*30).all()
|
|
||||||
devices += allDevs.filter(Device.lastConnection == None)
|
|
||||||
for device in devices:
|
|
||||||
print(device.deviceName)
|
|
||||||
db.session.delete(device)
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
"""empty message
|
|
||||||
|
|
||||||
Revision ID: 9882522aafa9
|
|
||||||
Revises: e5ef5e4a807b
|
|
||||||
Create Date: 2020-07-25 09:34:07.987380
|
|
||||||
|
|
||||||
"""
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = '9882522aafa9'
|
|
||||||
down_revision = 'e5ef5e4a807b'
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.add_column('calendar', sa.Column('calendar_type', sa.String(length=32), nullable=True))
|
|
||||||
op.create_index(op.f('ix_calendar_calendar_type'), 'calendar', ['calendar_type'], unique=False)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_index(op.f('ix_calendar_calendar_type'), table_name='calendar')
|
|
||||||
op.drop_column('calendar', 'calendar_type')
|
|
||||||
# ### end Alembic commands ###
|
|
@ -61,13 +61,11 @@ class Device(db.Model):
|
|||||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||||
deviceName = db.Column(db.String(64), unique=True)
|
deviceName = db.Column(db.String(64), unique=True)
|
||||||
connection = db.Column(db.Boolean)
|
connection = db.Column(db.Boolean)
|
||||||
lastConnection = db.Column(db.BigInteger)
|
|
||||||
|
|
||||||
class Calendar(db.Model):
|
class Calendar(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True, nullable=False)
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True, nullable=False)
|
||||||
calendar_id = db.Column(db.String(256), primary_key=True)
|
calendar_id = db.Column(db.String(256), primary_key=True)
|
||||||
calendar_type = db.Column(db.String(32), index=True)
|
|
||||||
name = db.Column(db.String(256), index=True)
|
name = db.Column(db.String(256), index=True)
|
||||||
toggle = db.Column(db.String(8))
|
toggle = db.Column(db.String(8))
|
||||||
color = db.Column(db.String(16))
|
color = db.Column(db.String(16))
|
||||||
|
@ -2,10 +2,9 @@ FROM python:3.8-slim-buster
|
|||||||
RUN apt-get update && apt-get upgrade
|
RUN apt-get update && apt-get upgrade
|
||||||
RUN pip3 install flask Flask-SQLAlchemy flask_migrate flask_login flask_wtf python-dotenv
|
RUN pip3 install flask Flask-SQLAlchemy flask_migrate flask_login flask_wtf python-dotenv
|
||||||
RUN apt-get install gcc libpcre3 libpcre3-dev libmariadbclient-dev -y
|
RUN apt-get install gcc libpcre3 libpcre3-dev libmariadbclient-dev -y
|
||||||
RUN pip3 install uwsgi email-validator RandomWords ics
|
RUN pip3 install uwsgi email-validator RandomWords
|
||||||
RUN pip3 install google google-oauth google-auth-oauthlib google-api-python-client mysqlclient
|
RUN pip3 install google google-oauth google-auth-oauthlib google-api-python-client mysqlclient
|
||||||
COPY docker-entrypoint.sh /usr/local/bin/
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
EXPOSE 8084
|
EXPOSE 8084
|
||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||||
# CMD tail -f /dev/null
|
|
||||||
|
@ -1,17 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
cd /home/calendarwatch
|
cd /home/calendarwatch
|
||||||
|
uwsgi --ini wsgi.ini
|
||||||
# use flasks own uwsgi server for debugging:
|
|
||||||
# export FLASK_APP=/home/calendarwatch/server.py
|
|
||||||
# python3 server.py
|
|
||||||
|
|
||||||
|
|
||||||
# the --lazy flag forks() a new instance of the server
|
|
||||||
# instead of forking from the parent and copying the same mysql
|
|
||||||
# connection. If you don't do that, then multiple forks will use
|
|
||||||
# the same connection at the same time, causing the server to throw
|
|
||||||
# a 'connection has gone away' error.
|
|
||||||
# more here: https://serverfault.com/questions/407612/error-2006-mysql-server-has-gone-away
|
|
||||||
uwsgi --ini wsgi.ini --lazy
|
|
||||||
echo "server has been started"
|
echo "server has been started"
|
||||||
|
|
||||||
|
@ -46,13 +46,3 @@ class DeviceForm(FlaskForm):
|
|||||||
device = Device.query.filter_by(deviceName=deviceName.data).first()
|
device = Device.query.filter_by(deviceName=deviceName.data).first()
|
||||||
if device is None:
|
if device is None:
|
||||||
raise ValidationError('Device not Found')
|
raise ValidationError('Device not Found')
|
||||||
|
|
||||||
|
|
||||||
class CalendarForm(FlaskForm):
|
|
||||||
iCalURL = StringField('New ical URL', validators=[DataRequired()])
|
|
||||||
calName = StringField('Calendar Name', validators=[DataRequired()])
|
|
||||||
submit = SubmitField('Add URL')
|
|
||||||
|
|
||||||
def validate_iCalURL (self, iCalURL):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
@ -121,23 +121,19 @@ def deleteAccount(user):
|
|||||||
|
|
||||||
|
|
||||||
def fetchCalendarEvents(user, calendars, startDate, endDate):
|
def fetchCalendarEvents(user, calendars, startDate, endDate):
|
||||||
|
|
||||||
service = None
|
|
||||||
if user.google_token is not None:
|
|
||||||
client_token = GC.build_credentials(user.google_token.token,
|
|
||||||
user.google_token.refresh_token)
|
|
||||||
credentials = google.oauth2.credentials.Credentials(**client_token)
|
|
||||||
|
|
||||||
service = build(GC.API_SERVICE_NAME, GC.API_VERSION, credentials=credentials)
|
client_token = GC.build_credentials(user.google_token.token,
|
||||||
|
user.google_token.refresh_token)
|
||||||
|
credentials = google.oauth2.credentials.Credentials(**client_token)
|
||||||
|
|
||||||
|
service = build(GC.API_SERVICE_NAME, GC.API_VERSION, credentials=credentials)
|
||||||
|
|
||||||
all_events = []
|
all_events = []
|
||||||
for calendar in calendars:
|
for calendar in calendars:
|
||||||
if (calendar.toggle == "True" and
|
if calendar.toggle == "True":
|
||||||
calendar.calendar_type == "Google" and
|
|
||||||
service != None):
|
|
||||||
event_result = service.events().list(calendarId=calendar.calendar_id,
|
event_result = service.events().list(calendarId=calendar.calendar_id,
|
||||||
timeMin=startDate.isoformat(),
|
timeMin=startDate,
|
||||||
timeMax=endDate.isoformat(),
|
timeMax=endDate,
|
||||||
maxResults=10,
|
maxResults=10,
|
||||||
singleEvents=True,
|
singleEvents=True,
|
||||||
orderBy='startTime').execute()
|
orderBy='startTime').execute()
|
||||||
@ -160,10 +156,7 @@ def fetchCalendarEvents(user, calendars, startDate, endDate):
|
|||||||
|
|
||||||
all_events.append(newEvent)
|
all_events.append(newEvent)
|
||||||
|
|
||||||
if service != None:
|
colors = service.colors().get().execute()
|
||||||
colors = service.colors().get().execute()
|
|
||||||
else:
|
|
||||||
colors = None
|
|
||||||
|
|
||||||
return all_events, colors
|
return all_events, colors
|
||||||
|
|
||||||
@ -185,7 +178,6 @@ def fetchCalendars():
|
|||||||
calendar_list = service.calendarList().list(pageToken=page_token).execute()
|
calendar_list = service.calendarList().list(pageToken=page_token).execute()
|
||||||
for calendar in calendar_list['items']:
|
for calendar in calendar_list['items']:
|
||||||
calendars.append(Calendar(name=calendar['summary'],
|
calendars.append(Calendar(name=calendar['summary'],
|
||||||
calType="Google",
|
|
||||||
calendarId=calendar['id'],
|
calendarId=calendar['id'],
|
||||||
color=calendar['colorId']))
|
color=calendar['colorId']))
|
||||||
page_token = calendar_list.get('nextPageToken')
|
page_token = calendar_list.get('nextPageToken')
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# Python standard libraries
|
# Python standard libraries
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
|
|
||||||
# Third-party libraries
|
# Third-party libraries
|
||||||
import flask
|
import flask
|
||||||
@ -17,11 +16,10 @@ from flask_login import (
|
|||||||
from random_words import RandomWords
|
from random_words import RandomWords
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import backend.icalHandler as ical
|
|
||||||
import server.googleHandler as google
|
import server.googleHandler as google
|
||||||
|
|
||||||
from server import login_manager, app, db
|
from server import login_manager, app, db
|
||||||
from server.forms import LoginForm, RegistrationForm, DeviceForm, CalendarForm
|
from server.forms import LoginForm, RegistrationForm, DeviceForm
|
||||||
import backend
|
import backend
|
||||||
from database.models import User, Calendar, Device, GoogleToken
|
from database.models import User, Calendar, Device, GoogleToken
|
||||||
|
|
||||||
@ -95,58 +93,11 @@ def devices():
|
|||||||
form=form)
|
form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/calendar", methods=['GET', 'POST', 'DELETE'])
|
@app.route("/calendar")
|
||||||
|
@login_required
|
||||||
def calendar():
|
def calendar():
|
||||||
if not current_user.is_authenticated:
|
|
||||||
return flask.render_template('login.html')
|
|
||||||
|
|
||||||
calendars = backend.calendarsFromDb(current_user)
|
calendars = backend.calendarsFromDb(current_user)
|
||||||
|
return flask.render_template('calendar.html', calendars=calendars)
|
||||||
form = CalendarForm()
|
|
||||||
if request.method == 'POST':
|
|
||||||
|
|
||||||
if request.form.get("submit") == "Remove":
|
|
||||||
calendar = db.session.query(Calendar).filter(Calendar.calendar_id==request.form.get("calendar")).first()
|
|
||||||
db.session.delete(calendar)
|
|
||||||
db.session.commit()
|
|
||||||
return flask.redirect(url_for('calendar'))
|
|
||||||
|
|
||||||
elif form.validate_on_submit():
|
|
||||||
ical.icalToCalendarDb(form.iCalURL.data, form.calName.data, current_user)
|
|
||||||
return flask.redirect(url_for('calendar'))
|
|
||||||
|
|
||||||
|
|
||||||
# otherwise it is a javascript POST
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
calId = request.json.get('calendar_id')
|
|
||||||
color = request.json.get('color', None)
|
|
||||||
toggle = request.json.get('toggle', None)
|
|
||||||
except:
|
|
||||||
return flask.render_template('calendar.html', calendars=calendars, form=form)
|
|
||||||
|
|
||||||
if color != None:
|
|
||||||
current_user.updateCalendar(calId, color=color)
|
|
||||||
if toggle != None:
|
|
||||||
current_user.updateCalendar(calId, toggle=toggle)
|
|
||||||
# toggle specific calendar of user
|
|
||||||
try:
|
|
||||||
calId = request.json.get('calendar_id')
|
|
||||||
color = request.json.get('color', None)
|
|
||||||
toggle = request.json.get('toggle', None)
|
|
||||||
except:
|
|
||||||
return flask.render_template('calendar.html', calendars=calendars, form=form)
|
|
||||||
|
|
||||||
if color != None:
|
|
||||||
current_user.updateCalendar(calId, color=color)
|
|
||||||
if toggle != None:
|
|
||||||
current_user.updateCalendar(calId, toggle=toggle)
|
|
||||||
# toggle specific calendar of user
|
|
||||||
|
|
||||||
|
|
||||||
return flask.render_template('calendar.html', calendars=calendars, form=form)
|
|
||||||
|
|
||||||
# POST
|
|
||||||
|
|
||||||
@app.route('/login/email', methods=['GET', 'POST'])
|
@app.route('/login/email', methods=['GET', 'POST'])
|
||||||
def emaillogin():
|
def emaillogin():
|
||||||
@ -253,7 +204,6 @@ def downloader(device):
|
|||||||
if request_device.user_id == None:
|
if request_device.user_id == None:
|
||||||
return jsonify(kind="unregistered")
|
return jsonify(kind="unregistered")
|
||||||
|
|
||||||
request_device.lastConnection=int(round(time.time()))
|
|
||||||
request_device.connection=True
|
request_device.connection=True
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
request_user = db.session.query(User).filter(User.id==request_device.user_id).first()
|
request_user = db.session.query(User).filter(User.id==request_device.user_id).first()
|
||||||
@ -263,7 +213,6 @@ def downloader(device):
|
|||||||
# TODO only pass along google calendars form user
|
# TODO only pass along google calendars form user
|
||||||
startDate, endDate = backend.getTimeStamps()
|
startDate, endDate = backend.getTimeStamps()
|
||||||
events, colors = google.fetchCalendarEvents(request_user, request_user.calendars, startDate, endDate)
|
events, colors = google.fetchCalendarEvents(request_user, request_user.calendars, startDate, endDate)
|
||||||
events.extend(ical.fetchCalendarEvents(request_user.calendars, startDate, endDate))
|
|
||||||
calendarjson = backend.generateJsonFromCalendarEntries(events, colors)
|
calendarjson = backend.generateJsonFromCalendarEntries(events, colors)
|
||||||
return jsonify(calendarjson)
|
return jsonify(calendarjson)
|
||||||
|
|
||||||
@ -284,7 +233,6 @@ def generateDeviceFingerprint():
|
|||||||
if not db.session.query(Device).filter(Device.deviceName==fingerprint).first():
|
if not db.session.query(Device).filter(Device.deviceName==fingerprint).first():
|
||||||
# Save as new Device
|
# Save as new Device
|
||||||
device = Device(deviceName=fingerprint, connection=False)
|
device = Device(deviceName=fingerprint, connection=False)
|
||||||
device.lastConnection = int(round(time.time()))
|
|
||||||
db.session.add(device)
|
db.session.add(device)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
break;
|
break;
|
||||||
@ -292,3 +240,27 @@ def generateDeviceFingerprint():
|
|||||||
# Send to Device
|
# Send to Device
|
||||||
return jsonify(deviceName=fingerprint)
|
return jsonify(deviceName=fingerprint)
|
||||||
|
|
||||||
|
# POST
|
||||||
|
|
||||||
|
@app.route('/calendar', methods = ['POST', 'DELETE'])
|
||||||
|
@login_required
|
||||||
|
def user():
|
||||||
|
if request.method == 'POST':
|
||||||
|
calId = request.json.get('calendar_id')
|
||||||
|
color = request.json.get('color', None)
|
||||||
|
toggle = request.json.get('toggle', None)
|
||||||
|
|
||||||
|
if color != None:
|
||||||
|
current_user.updateCalendar(calId, color=color)
|
||||||
|
if toggle != None:
|
||||||
|
current_user.updateCalendar(calId, toggle=toggle)
|
||||||
|
# toggle specific calendar of user
|
||||||
|
|
||||||
|
elif request.method == 'DELETE':
|
||||||
|
# do nothing
|
||||||
|
return 'NONE'
|
||||||
|
else:
|
||||||
|
# POST Error 405
|
||||||
|
print("405")
|
||||||
|
|
||||||
|
return 'OK'
|
||||||
|
@ -69,7 +69,7 @@ body
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grayblock .padded {
|
.grayblock .padded {
|
||||||
padding: 0rem;
|
padding: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontal {
|
.horizontal {
|
||||||
@ -91,13 +91,11 @@ body
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vertical .content .image {
|
.vertical .content .image {
|
||||||
max-width: 90%;
|
|
||||||
width: 25rem;
|
width: 25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical .content .text {
|
.vertical .content .text {
|
||||||
max-width: 90%;
|
margin-left: 2rem;
|
||||||
margin: auto;
|
|
||||||
width: 26rem;
|
width: 26rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +106,8 @@ body
|
|||||||
|
|
||||||
.horizontal .image {
|
.horizontal .image {
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
margin-left: 4rem;
|
||||||
|
margin-right: 4rem;
|
||||||
height: 20rem;
|
height: 20rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -156,7 +156,7 @@ body
|
|||||||
background-color: #eaeaea;
|
background-color: #eaeaea;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation_rightside {
|
.navigation_rightside {
|
||||||
@ -165,7 +165,7 @@ body
|
|||||||
|
|
||||||
.navigation a {
|
.navigation a {
|
||||||
float: left;
|
float: left;
|
||||||
display: block;
|
display: flex;
|
||||||
color: #333;
|
color: #333;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
@ -211,7 +211,6 @@ body
|
|||||||
display: block;
|
display: block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style page content */
|
/* Style page content */
|
||||||
@ -290,27 +289,25 @@ body
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items:center;
|
align-items:center;
|
||||||
/* flex-direction: row; */
|
flex-direction: row;
|
||||||
/*padding: 0px 2rem 0px 2rem;*/
|
padding: 0px 2rem 0px 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container .button {
|
.container .button {
|
||||||
padding: 1rem 1.5rem 1rem 1.5rem;
|
padding: 1rem 1.5rem 1rem 1.5rem;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
margin: 4rem 1rem 1rem 1rem;
|
margin: 4rem;
|
||||||
color: black;
|
color: black;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container .preview {
|
.container .preview {
|
||||||
width: 20rem;
|
width: 20rem;
|
||||||
height: auto;
|
height: 20rem;
|
||||||
margin: 1rem 3rem 4rem 3rem;
|
margin: 1rem 3rem 4rem 3rem;
|
||||||
max-width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.container .button.logout {
|
.container .button.logout {
|
||||||
@ -331,9 +328,8 @@ body
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sub.container {
|
.sub.container {
|
||||||
width: 40%;
|
width: 20rem;
|
||||||
justify-content: left;
|
justify-content: left;
|
||||||
display: flex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
@ -353,7 +349,6 @@ body
|
|||||||
.profile .name {
|
.profile .name {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
color: #333;
|
color: #333;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grey {
|
.grey {
|
||||||
@ -371,10 +366,3 @@ body
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (max-width:800px) {
|
|
||||||
.sub.container {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,32 +2,18 @@
|
|||||||
{% block body%}
|
{% block body%}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div style="width: 4rem;margin:1rem;"></div>
|
<div style="width: 15rem; margin: 1rem">Calendar</div>
|
||||||
<div style="width: 10rem; margin: 1rem; font-weight: bold">Calendar</div>
|
<div style="width: 10rem; margin: 1rem; padding-right: 5rem">Show on device</div>
|
||||||
<div style="display: inline-flex">
|
<div style="width: 2rem; margin: 1rem">Color</div>
|
||||||
<div style="width: 5rem; margin: 1rem; padding-right: 1rem;font-weight: bold">Show on device</div>
|
|
||||||
<div style="width: 2rem; margin: 1rem;font-weight: bold">Color</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% for item in calendars %}
|
{% for item in calendars %}
|
||||||
<div class="container" style="margin-top: 1.5rem">
|
<div class="container">
|
||||||
<!--action button-->
|
|
||||||
{% if "ical" == item.calType %}
|
|
||||||
<div style="width: 4rem; margin: 1rem;">
|
|
||||||
<form action="" method="post">
|
|
||||||
<input type="hidden" name="calendar" value={{ item.calendarId }}>
|
|
||||||
<input type="submit" name="submit" value="Remove">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div style="width: 4rem; margin: 1rem;"></div>
|
|
||||||
{% endif %}
|
|
||||||
<!--Name-->
|
<!--Name-->
|
||||||
<div style="width: 10rem; margin-left: 1rem; margin-right: 1rem; margin-top: 0.5rem">{{ item.name }}</div>
|
<div style="width: 15rem; margin: 1rem;">{{ item.name }}</div>
|
||||||
<div style="display: inline-flex">
|
|
||||||
<!--Toggle-->
|
<!--Toggle-->
|
||||||
<div style="width: 5rem; margin-left: 1rem; margin-right: 1rem; margin-top: 0.5rem; padding-right: 1rem">
|
<div style="width: 10rem; margin: 1rem; padding-right: 5rem">
|
||||||
<!-- Rounded switch -->
|
<!-- Rounded switch -->
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
<input class="toggle" id={{item.calendarId}} type="checkbox" toggled={{item.toggle}} onclick="toggleReaction(this)">
|
<input class="toggle" id={{item.calendarId}} type="checkbox" toggled={{item.toggle}} onclick="toggleReaction(this)">
|
||||||
@ -36,38 +22,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Color Selector-->
|
<!--Color Selector-->
|
||||||
<div style="width: 2rem; margin-left: 1rem; margin-right: 1rem; margin-top: 0.5rem">
|
<div style="width: 2rem; margin: 1rem;">
|
||||||
<div class="colorPickSelector" id={{item.calendarId}} defaultColor={{item.color}}></div>
|
<div class="colorPickSelector" id={{item.calendarId}} defaultColor={{item.color}}></div>
|
||||||
<!--svg height="20" width="20">
|
<!--svg height="20" width="20">
|
||||||
<circle cx="10" cy="10" r="10" stroke="black" stroke-width="0" fill={{ item.color }} />
|
<circle cx="10" cy="10" r="10" stroke="black" stroke-width="0" fill={{ item.color }} />
|
||||||
</svg-->
|
</svg-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div id=calendars class="container">
|
<div id=calendars class="container">
|
||||||
|
<a class="button" href="login/google">Google Calendar</a>
|
||||||
|
<a class="button" href="#" >Nextcloud Calendar</a>
|
||||||
</div>
|
</div>
|
||||||
<form action="" method="post">
|
<div class="container">
|
||||||
<div class="container grey" style="margin-top: 3rem;">
|
|
||||||
<div>{{ form.hidden_tag() }}</div>
|
|
||||||
<div style="display: flex">
|
|
||||||
<div style="margin: 1rem">{{ form.calName.label }}</div>
|
|
||||||
<div style="margin: 1rem">{{ form.calName(size=24) }}</div>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex">
|
|
||||||
<div style="margin: 1rem">{{ form.iCalURL.label }}</div>
|
|
||||||
<div style="margin: 1rem">{{ form.iCalURL(size=24) }}</div>
|
|
||||||
</div>
|
|
||||||
<div style="with: 8rem; margin: 1rem">{{ form.submit() }}</div>
|
|
||||||
{% for error in form.iCalURL.errors %}
|
|
||||||
<span style="color: red;">[{{ error }}]</span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!--div class="container">
|
|
||||||
<a class="button addcalendar" href="/login/google" style="width: auto; margin: 4rem">Add Calendar</a>
|
<a class="button addcalendar" href="/login/google" style="width: auto; margin: 4rem">Add Calendar</a>
|
||||||
</div-->
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
|
|
||||||
<div class="footer container">
|
<div class="footer">
|
||||||
<p>made by Raphael Maenle </p>
|
<p>made by Raphael Maenle </p>
|
||||||
<p><a href="mailto:raphael@maenle.net">raphael@maenle.net</a></p>
|
<p><a href="mailto:raphael@maenle.net">raphael@maenle.net</a></p>
|
||||||
<p><a href="/privacy">privacy policy</a></p>
|
<p><a href="/privacy">privacy policy</a></p>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 style="margin-left:10rem">Summary</h3>
|
<h3 style="margin-left:10rem">Summary</h3>
|
||||||
<div style="margin-left:10rem; margin-right:10rem;">This Privacy Statement describes how Longitude handles your data and how the developer makes sure, that the user's information remains as secure as possible.
|
<div style="margin-left:10rem; margin-right:10rem;">This Privacy Statement descibes how Longitude handles your data and how the developer makes sure, that the users information remains as secure as possible.
|
||||||
This application does not share any user information with third parties and takes care to only save the minimum amount of information about the user.
|
This application does not share any user information with third parties and takes care to only save the minimum amount of information about the user.
|
||||||
The following chapters cover all essential points of interest about which information is saved and when it is removed from the server.
|
The following chapters cover all essential points of interest about which information is saved and when it is removed from the server.
|
||||||
If you have any further questions or suggestions, please email us at <a href="mailto:raphael@maenle.net">raphael@maenle.net</a>.</div>
|
If you have any further questions or suggestions, please email us at <a href="mailto:raphael@maenle.net">raphael@maenle.net</a>.</div>
|
||||||
@ -15,7 +15,7 @@ If you have any further questions or suggestions, please email us at <a href="ma
|
|||||||
|
|
||||||
<h3 style="margin-left:10rem">What Information is saved?</h3>
|
<h3 style="margin-left:10rem">What Information is saved?</h3>
|
||||||
<div style="margin-left:10rem; margin-right:10rem;">
|
<div style="margin-left:10rem; margin-right:10rem;">
|
||||||
Longitude Calendar saves as little information about their users as possible. The application handles sensitive information only when directly prompted by the user or a device associated with the user. The service only provides this information to the user or a device associated with the user. The data saved in the Longidute Database is
|
Longitude Calendar saves as little information about their users as possible. The application handles sensitive information only when directly prompted by the user or a device associated with the user. The service only provides this information to the user or a device associated with the user. The data saved in the Longidute Databas is
|
||||||
<ul>
|
<ul>
|
||||||
<li>Username and hashed password or alternatively</li>
|
<li>Username and hashed password or alternatively</li>
|
||||||
<li>Google Username and Id with Google Login Token</li>
|
<li>Google Username and Id with Google Login Token</li>
|
||||||
|
@ -39,8 +39,6 @@
|
|||||||
// content here
|
// content here
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="main">
|
|
||||||
</div>
|
</div>
|
||||||
{% include "footer.html" %}
|
{% include "footer.html" %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,15 +12,13 @@
|
|||||||
<div class="grayblock horizontal">
|
<div class="grayblock horizontal">
|
||||||
<div class="content padded">
|
<div class="content padded">
|
||||||
<div style='margin: 1rem'>
|
<div style='margin: 1rem'>
|
||||||
<!--Connect your <img src='/static/res/googlelogo.png' style='height: 3.2rem; vertical-align:middle; padding-Bottom: 0.1rem'/> Calendar...-->
|
Connect your <img src='/static/res/googlelogo.png' style='height: 2.2rem; vertical-align:middle; padding-Bottom: 0.1rem'/> Calendar...
|
||||||
Connect your Calendar..
|
|
||||||
</div>
|
</div>
|
||||||
<img class="image" src='/static/res/calendar.svg'/>
|
<img class="image" src='/static/res/calendar.svg'/>
|
||||||
</div>
|
</div>
|
||||||
<div class="content padded">
|
<div class="content padded">
|
||||||
<div style='margin: 1rem'>
|
<div style='margin: 1rem'>
|
||||||
<!--...with your <img src='/static/res/tizenlogo.png' style='height: 2rem; vertical-align:middle; padding-Bottom:0.3rem;'/> Watchface-->
|
...with your <img src='/static/res/tizenlogo.png' style='height: 2rem; vertical-align:middle; padding-Bottom:0.3rem;'/> Watchface
|
||||||
..with your Tizen Watchface
|
|
||||||
</div>
|
</div>
|
||||||
<img class="image" src='/static/res/watchface.svg'/>
|
<img class="image" src='/static/res/watchface.svg'/>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user