[Refactor] Rename the app module to calsocial

Just for clarity
This commit is contained in:
2018-07-02 08:33:21 +02:00
parent 6b3d36ff21
commit 7846d9017d
23 changed files with 9 additions and 9 deletions

127
calsocial/__init__.py Normal file
View File

@@ -0,0 +1,127 @@
# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from datetime import datetime
import os
from flask import Flask, current_app, redirect, render_template, url_for
from flask_babel import Babel, get_locale as babel_get_locale
from flask_security import SQLAlchemyUserDatastore, current_user, login_required
def get_locale():
"""Locale selector
Selects the best locale based on values sent by the browser.
"""
from flask import request
supported_languages = ['en', 'hu']
if 'l' in request.args and request.args['l'].lower() in supported_languages:
return request.args['l'].lower()
return request.accept_languages.best_match(supported_languages)
def template_vars():
return {
'lang': babel_get_locale().language,
}
class CalendarSocialApp(Flask):
def __init__(self, name, config=None):
from .models import db, User, Role
from .security import security
Flask.__init__(self, name)
config_name = os.environ.get('ENV', config or 'dev')
self.config.from_pyfile(f'config_{config_name}.py', True)
# Make sure we look up users both by their usernames and email addresses
self.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = ('username', 'email')
db.init_app(self)
babel = Babel(app=self)
babel.localeselector(get_locale)
user_store = SQLAlchemyUserDatastore(db, User, Role)
security.init_app(self, datastore=user_store)
self.context_processor(template_vars)
app = CalendarSocialApp(__name__)
@app.route('/')
def hello():
from .calendar_system.gregorian import GregorianCalendar
if not current_user.is_authenticated:
return render_template('welcome.html')
calendar = GregorianCalendar(datetime.utcnow().timestamp())
return render_template('index.html', calendar=calendar)
@app.route('/register', methods=['POST', 'GET'])
def register():
if not current_app.config['REGISTRATION_ENABLED']:
return render_template('registration-disabled.html')
from .forms import RegistrationForm
from .models import db, User
form = RegistrationForm()
if form.validate_on_submit():
# TODO: This might become False later, if we want registrations to be confirmed via E-mail
user = User(active=True)
form.populate_obj(user)
db.session.add(user)
db.session.commit()
return redirect(url_for('hello'))
return render_template('registration.html', form=form)
@app.route('/new-event', methods=['GET', 'POST'])
@login_required
def new_event():
from .forms import EventForm
from .models import db, Event
form = EventForm()
if form.validate_on_submit():
event = Event(user=current_user)
form.populate_obj(event)
db.session.add(event)
db.session.commit()
return redirect(url_for('hello'))
return render_template('event-edit.html', form=form)
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,15 @@
class CalendarSystem:
__has_months__ = False
__has_weeks__ = False
@property
def day_names(self):
raise NotImplementedError()
@property
def month(self):
raise NotImplementedError()
@property
def days(self):
raise NotImplementedError()

View File

@@ -0,0 +1,64 @@
from datetime import datetime, timedelta
from flask_babel import lazy_gettext as _
from . import CalendarSystem
class GregorianCalendar(CalendarSystem):
__name__ = _('Gregorian')
__has_months__ = True
START_DAY = 0
END_DAY = 7
day_names = (
_('Monday'),
_('Tuesday'),
_('Wednesday'),
_('Thursday'),
_('Friday'),
_('Saturday'),
_('Sunday'),
)
def __init__(self, timestamp):
self.timestamp = datetime.utcfromtimestamp(timestamp)
@property
def month(self):
return self.timestamp.strftime("%B, %Y")
@property
def days(self):
day_list = []
start_day = self.timestamp.replace(day=1)
while start_day.weekday() > self.START_DAY:
start_day -= timedelta(days=1)
day_list.append(start_day)
current_day = start_day
while current_day.weekday() < self.END_DAY and current_day.month <= self.timestamp.month:
current_day += timedelta(days=1)
day_list.append(current_day)
return day_list
def day_events(self, date, user=None):
from ..models import Event
events = Event.query
if user:
events = events.filter(Event.user == user)
start_timestamp = date.replace(hour=0, minute=0, second=0, microsecond=0)
end_timestamp = start_timestamp + timedelta(days=1)
events = events.filter(((Event.start_time >= start_timestamp) & (Event.start_time < end_timestamp)) |
((Event.end_time >= start_timestamp) & (Event.end_time < end_timestamp)))
return events

10
calsocial/config_dev.py Normal file
View File

@@ -0,0 +1,10 @@
DEBUG = True
ENV = 'dev'
REGISTRATION_ENABLED = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///local.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = 'ThisIsNotSoSecret'
SECURITY_PASSWORD_HASH = 'bcrypt'
SECURITY_PASSWORD_SALT = SECRET_KEY
SECURITY_REGISTERABLE = False

30
calsocial/forms.py Normal file
View File

@@ -0,0 +1,30 @@
from flask_babel import lazy_gettext as _
from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, BooleanField
from wtforms.ext.dateutil.fields import DateTimeField
from wtforms.validators import DataRequired, Email, ValidationError
from wtforms.widgets import TextArea
class RegistrationForm(FlaskForm):
username = StringField(_('Username'), validators=[DataRequired()])
email = StringField(_('Email address'), validators=[Email()])
password = PasswordField(_('Password'), validators=[DataRequired()])
password_retype = PasswordField(_('Password, once more'), validators=[DataRequired()])
def validate_password_retype(self, field):
if field.data != self.password.data:
raise ValidationError(_('The two passwords must match!'))
class EventForm(FlaskForm):
title = StringField(_('Title'), validators=[DataRequired()])
time_zone = StringField(_('Time zone'), validators=[DataRequired()])
start_time = DateTimeField(_('Start time'), validators=[DataRequired()])
end_time = DateTimeField(_('End time'), validators=[DataRequired()])
all_day = BooleanField(_('All day'))
description = StringField(_('Description'), widget=TextArea())
def validate_end_time(self, field):
if field.data < self.start_time.data:
raise ValidationError(_('End time must be later than start time!'))

87
calsocial/models.py Normal file
View File

@@ -0,0 +1,87 @@
from datetime import datetime
from flask_security import UserMixin, RoleMixin
from flask_security.utils import hash_password
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
users_roles = db.Table(
'users_roles',
db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('roles.id')))
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer(), primary_key=True)
#: The username of the user. This is also the display name and thus is immutable
username = db.Column(db.String(length=50), unique=True, nullable=False)
#: The email address of the user
email = db.Column(db.String(length=255), unique=True, nullable=True)
#: The (hashed) password of the user
password = db.Column(db.String(length=255))
#: A flag to show whether the user is enabled (active) or not
active = db.Column(db.Boolean(), default=False)
#: The timestamp when this user was created
created_at = db.Column(db.DateTime(), default=datetime.utcnow)
#: The timestamp when the user was activated
confirmed_at = db.Column(db.DateTime())
#: The roles of the user
roles = db.relationship('Role',
secondary=users_roles,
backref=db.backref('users', lazy='dynamic'))
def __repr__(self):
return f'<User {self.id}({self.username})>'
class Role(db.Model, RoleMixin):
__tablename__ = 'roles'
id = db.Column(db.Integer(), primary_key=True)
#: The name of the role
name = db.Column(db.Unicode(length=80), unique=True)
#: A description of the role
description = db.Column(db.UnicodeText)
def __repr__(self):
return f'<Role {self.id}({self.name})>'
class Event(db.Model):
__tablename__ = 'events'
id = db.Column(db.Integer(), primary_key=True)
#: The ID of the user who created the event
user_id = db.Column(db.Integer(), db.ForeignKey('users.id'), nullable=False)
user = db.relationship('User', backref=db.backref('events', lazy='dynamic'))
#: The title of the event
title = db.Column(db.Unicode(length=200), nullable=False)
#: The time zone to be used for `start_time` and `end_time`
time_zone = db.Column(db.String(length=80), nullable=False)
#: The starting timestamp of the event
start_time = db.Column(db.DateTime(), nullable=False)
#: The ending timestamp of the event
end_time = db.Column(db.DateTime(), nullable=False)
#: If `True`, the event is a whole-day event
all_day = db.Column(db.Boolean(), default=False)
#: The description of the event
description = db.Column(db.UnicodeText())
def __repr__(self):
return f'<Event {self.id} ({self.title}) of {self.user}>'

3
calsocial/security.py Normal file
View File

@@ -0,0 +1,3 @@
from flask_security import Security
security = Security()

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 46 46"
xml:space="preserve"
id="svg4656"
sodipodi:docname="calendar-social-icon-no-circle.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
inkscape:export-filename="/home/polesz/Pictures/calendar-social-icon-32.png"
inkscape:export-xdpi="66.639999"
inkscape:export-ydpi="66.639999"
width="46"
height="46"><metadata
id="metadata4662"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4660" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="997"
id="namedview4658"
showgrid="false"
inkscape:zoom="4"
inkscape:cx="12.121291"
inkscape:cy="12.202432"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1"
fit-margin-top="0"
fit-margin-left="3"
fit-margin-right="3"
fit-margin-bottom="0" />
<style
type="text/css"
id="style4610">
.st0{fill:#77B3D4;}
.st1{opacity:0.2;}
.st2{fill:#231F20;}
.st3{fill:#FFFFFF;}
.st4{fill:#C75C5C;}
.st5{fill:#4F5D73;}
.st6{fill:#E0E0D1;}
</style>
<g
id="Layer_1"
transform="translate(-9,-8)">
<g
id="g4614">
</g>
<g
id="g4652">
<g
class="st1"
id="g4618"
style="opacity:0.2">
<path
class="st2"
d="m 12,25 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 25 Z"
id="path4616"
inkscape:connector-curvature="0"
style="fill:#231f20" />
</g>
<g
id="g4622">
<path
class="st3"
d="m 12,23 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 23 Z"
id="path4620"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
</g>
<g
class="st1"
id="g4626"
style="opacity:0.2">
<path
class="st2"
d="M 48,14 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
id="path4624"
inkscape:connector-curvature="0"
style="fill:#231f20" />
</g>
<g
id="g4630">
<path
class="st4"
d="M 48,12 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
id="path4628"
inkscape:connector-curvature="0"
style="fill:#c75c5c" />
</g>
<g
id="g4634-3"
transform="matrix(0.57125605,0,0,0.57125605,20.265475,18.937918)"><path
class="st5"
d="m 32,48 c -1.1,0 -2,-0.9 -2,-2 0,-5.5 1.8,-9.5 3.5,-12 H 27 c -1.1,0 -2,-0.9 -2,-2 0,-1.1 0.9,-2 2,-2 h 11 c 0.9,0 1.6,0.6 1.9,1.4 0.3,0.8 0,1.7 -0.7,2.2 C 39,33.8 34,37.5 34,46 c 0,1.1 -0.9,2 -2,2 z"
id="path4632-6"
inkscape:connector-curvature="0"
style="fill:#c75c5c;fill-opacity:1" /><circle
style="fill:#c75c5c;fill-opacity:1;stroke:none;stroke-width:9.57298088;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4664-7"
cx="32.684433"
cy="23.473127"
r="5.0143828" /></g><g
id="g4634-3-6"
transform="matrix(0.57125605,0,0,0.57125605,7.0614955,19.226475)"><path
class="st5"
d="m 32,48 c -1.1,0 -2,-0.9 -2,-2 0,-5.5 1.8,-9.5 3.5,-12 H 27 c -1.1,0 -2,-0.9 -2,-2 0,-1.1 0.9,-2 2,-2 h 11 c 0.9,0 1.6,0.6 1.9,1.4 0.3,0.8 0,1.7 -0.7,2.2 C 39,33.8 34,37.5 34,46 c 0,1.1 -0.9,2 -2,2 z"
id="path4632-6-2"
inkscape:connector-curvature="0"
style="fill:#77b3d4;fill-opacity:1"
inkscape:export-xdpi="600"
inkscape:export-ydpi="600" /><circle
style="fill:#77b3d4;fill-opacity:1;stroke:none;stroke-width:9.57298088;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4664-7-9"
cx="32.684433"
cy="23.473127"
r="5.0143828"
inkscape:export-xdpi="600"
inkscape:export-ydpi="600" /></g><g
id="g4634"
transform="matrix(0.7302377,0,0,0.7302377,8.769411,12.94859)">
<path
class="st5"
d="m 32,48 c -1.1,0 -2,-0.9 -2,-2 0,-5.5 1.8,-9.5 3.5,-12 H 27 c -1.1,0 -2,-0.9 -2,-2 0,-1.1 0.9,-2 2,-2 h 11 c 0.9,0 1.6,0.6 1.9,1.4 0.3,0.8 0,1.7 -0.7,2.2 C 39,33.8 34,37.5 34,46 c 0,1.1 -0.9,2 -2,2 z"
id="path4632"
inkscape:connector-curvature="0"
style="fill:#4f5d73" />
<circle
style="fill:#4f5d73;fill-opacity:1;stroke:none;stroke-width:9.57298088;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4664"
cx="32.684433"
cy="23.473127"
r="5.0143828" /></g>
<g
class="st1"
id="g4638"
style="opacity:0.2">
<path
class="st2"
d="m 20,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
id="path4636"
inkscape:connector-curvature="0"
style="fill:#231f20" />
</g>
<g
class="st1"
id="g4642"
style="opacity:0.2">
<path
class="st2"
d="m 45,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
id="path4640"
inkscape:connector-curvature="0"
style="fill:#231f20" />
</g>
<g
id="g4646">
<path
class="st6"
d="m 20,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
id="path4644"
inkscape:connector-curvature="0"
style="fill:#e0e0d1" />
</g>
<g
id="g4650">
<path
class="st6"
d="m 45,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
id="path4648"
inkscape:connector-curvature="0"
style="fill:#e0e0d1" />
</g>
</g>
</g>
<g
id="Layer_2"
transform="translate(-9,-8)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 64 64"
xml:space="preserve"
id="svg4656"
sodipodi:docname="calendar-social-icon.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
inkscape:export-filename="/home/polesz/Pictures/calendar-social-icon-192.png"
inkscape:export-xdpi="288"
inkscape:export-ydpi="288"
style="enable-background:new 0 0 64 64;"><metadata
id="metadata4662"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4660" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="997"
id="namedview4658"
showgrid="false"
inkscape:zoom="4"
inkscape:cx="-38.878709"
inkscape:cy="11.202432"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style4610">
.st0{fill:#77B3D4;}
.st1{opacity:0.2;}
.st2{fill:#231F20;}
.st3{fill:#FFFFFF;}
.st4{fill:#C75C5C;}
.st5{fill:#4F5D73;}
.st6{fill:#E0E0D1;}
</style>
<g
id="Layer_1">
<g
id="g4614">
<circle
class="st0"
cx="32"
cy="32"
r="32"
id="circle4612" />
</g>
<g
id="g4652">
<g
class="st1"
id="g4618">
<path
class="st2"
d="M12,25v25c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V25H12z"
id="path4616" />
</g>
<g
id="g4622">
<path
class="st3"
d="M12,23v25c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V23H12z"
id="path4620" />
</g>
<g
class="st1"
id="g4626">
<path
class="st2"
d="M48,14H16c-2.2,0-4,1.8-4,4v7h40v-7C52,15.8,50.2,14,48,14z"
id="path4624" />
</g>
<g
id="g4630">
<path
class="st4"
d="M48,12H16c-2.2,0-4,1.8-4,4v7h40v-7C52,13.8,50.2,12,48,12z"
id="path4628" />
</g>
<g
id="g4634-3"
transform="matrix(0.57125605,0,0,0.57125605,20.265475,18.937918)"><path
class="st5"
d="m 32,48 c -1.1,0 -2,-0.9 -2,-2 0,-5.5 1.8,-9.5 3.5,-12 H 27 c -1.1,0 -2,-0.9 -2,-2 0,-1.1 0.9,-2 2,-2 h 11 c 0.9,0 1.6,0.6 1.9,1.4 0.3,0.8 0,1.7 -0.7,2.2 C 39,33.8 34,37.5 34,46 c 0,1.1 -0.9,2 -2,2 z"
id="path4632-6"
inkscape:connector-curvature="0"
style="fill:#c75c5c;fill-opacity:1" /><circle
style="fill:#c75c5c;fill-opacity:1;stroke:none;stroke-width:9.57298088;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4664-7"
cx="32.684433"
cy="23.473127"
r="5.0143828" /></g><g
id="g4634-3-6"
transform="matrix(0.57125605,0,0,0.57125605,7.0614955,19.226475)"><path
class="st5"
d="m 32,48 c -1.1,0 -2,-0.9 -2,-2 0,-5.5 1.8,-9.5 3.5,-12 H 27 c -1.1,0 -2,-0.9 -2,-2 0,-1.1 0.9,-2 2,-2 h 11 c 0.9,0 1.6,0.6 1.9,1.4 0.3,0.8 0,1.7 -0.7,2.2 C 39,33.8 34,37.5 34,46 c 0,1.1 -0.9,2 -2,2 z"
id="path4632-6-2"
inkscape:connector-curvature="0"
style="fill:#77b3d4;fill-opacity:1"
inkscape:export-xdpi="600"
inkscape:export-ydpi="600" /><circle
style="fill:#77b3d4;fill-opacity:1;stroke:none;stroke-width:9.57298088;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4664-7-9"
cx="32.684433"
cy="23.473127"
r="5.0143828"
inkscape:export-xdpi="600"
inkscape:export-ydpi="600" /></g><g
id="g4634"
transform="matrix(0.7302377,0,0,0.7302377,8.769411,12.94859)">
<path
class="st5"
d="m 32,48 c -1.1,0 -2,-0.9 -2,-2 0,-5.5 1.8,-9.5 3.5,-12 H 27 c -1.1,0 -2,-0.9 -2,-2 0,-1.1 0.9,-2 2,-2 h 11 c 0.9,0 1.6,0.6 1.9,1.4 0.3,0.8 0,1.7 -0.7,2.2 C 39,33.8 34,37.5 34,46 c 0,1.1 -0.9,2 -2,2 z"
id="path4632"
inkscape:connector-curvature="0"
style="fill:#4f5d73" />
<circle
style="fill:#4f5d73;fill-opacity:1;stroke:none;stroke-width:9.57298088;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4664"
cx="32.684433"
cy="23.473127"
r="5.0143828" /></g>
<g
class="st1"
id="g4638">
<path
class="st2"
d="M20,21c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C22,20.1,21.1,21,20,21L20,21z"
id="path4636" />
</g>
<g
class="st1"
id="g4642">
<path
class="st2"
d="M45,21c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C47,20.1,46.1,21,45,21L45,21z"
id="path4640" />
</g>
<g
id="g4646">
<path
class="st6"
d="M20,19c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C22,18.1,21.1,19,20,19L20,19z"
id="path4644" />
</g>
<g
id="g4650">
<path
class="st6"
d="M45,19c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C47,18.1,46.1,19,45,19L45,19z"
id="path4648" />
</g>
</g>
</g>
<g
id="Layer_2">
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<title>Calendar.Social</title>
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='images/calendar-social-icon-16.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='images/calendar-social-icon-32.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='images/calendar-social-icon-96.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='images/calendar-social-icon-192.png') }}">
{% block head %}
<style>
header > h1 > img {
height: 1em;
}
footer {
margin-top: 3em;
font-weight: bold;
border-top: 1px dotted black;
padding-top: 1em;
}
</style>
{% endblock %}
</head>
<body>
<header>
<h1>
<img src="{{ url_for('static', filename='images/calendar-social-icon.svg') }}">
Calendar.social
</h1>
<nav class="menu">
{% if current_user.is_authenticated %}
{{ _('Logged in as %(username)s', username=current_user.username) }}
{% endif %}
<ul>
{% if not current_user.is_authenticated %}
<li><a href="{{ url_for('security.login') }}">{% trans %}Login{% endtrans %}</a></li>
{% else %}
<li><a href="{{ url_for('security.logout') }}">{% trans %}Logout{% endtrans %}</a></li>
<li><a href="{{ url_for('hello') }}">{% trans %}Calendar view{% endtrans %}</a></li>
{% endif %}
</ul>
</nav>
</header>
{% block content %}{% endblock %}
<footer>
Soon…™
</footer>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% block content %}
<form method="post">
{{ form.hidden_tag() }}
{{ form.errors }}
<br>
{{ form.title.errors }}
{{ form.title.label }}
{{ form.title }}
<br>
{{ form.time_zone.errors }}
{{ form.time_zone.label }}
{{ form.time_zone }}
<br>
{{ form.start_time.errors }}
{{ form.start_time.label }}
{{ form.start_time }}
<br>
{{ form.end_time.errors }}
{{ form.end_time.label }}
{{ form.end_time }}
<br>
{{ form.all_day.errors }}
{{ form.all_day.label }}
{{ form.all_day }}
<br>
{{ form.description.errors }}
{{ form.description.label }}
{{ form.description }}
<br>
<button type="submit">{% trans %}Save{% endtrans %}</button>
<a href="{{ url_for('hello') }}">Cancel</a>
</form>
{% endblock content %}

View File

@@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block content %}
{{ _('Welcome to Calendar.social, %(username)s!', username=current_user.username) }}
{% include 'month-view.html' %}
<a href="{{ url_for('new_event') }}">{% trans %}Add event{% endtrans %}</a>
{% endblock content %}

View File

@@ -0,0 +1,82 @@
<style>
table.calendar > * {
font-family: sans;
}
table.calendar {
width: 100%;
border-collapse: collapse;
}
tr.month > td {
text-align: center;
font-weight: bold;
border-bottom: 2px solid black;
padding-bottom: .5em;
}
tr.days > td {
text-align: center;
border-bottom: 2px solid black;
}
tr.week > td {
height: 3em;
}
tr.week > td > span.day-num {
font-weight: bold;
}
tr.week > td.other-month > span.day-num {
font-weight: normal;
color: #909090;
}
tr.week > td.today {
background-color: #d8d8d8;
}
td > div.event {
border: 1px solid green;
background-color: white;
border-radius: 2px;
}
</style>
<table class="calendar">
<thead>
<tr class="month">
<td colspan="7">{{ calendar.month }}</td>
</tr>
<tr class="days">
{% for day in calendar.day_names %}
<td>
{{ day }}
</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% set markers = namespace() -%}
{% set markers.first = true -%}
{% for day in calendar.days -%}
{% if loop.index0 % (calendar.day_names) | length == 0 -%}
{% if not markers.first %}
</tr>
{%- else %}
{%- set markers.first = false %}
{%- endif %}
<tr class="week">
{%- endif %}
<td class="{% if day.month != calendar.timestamp.month %} other-month{% endif %}{% if day.date() == calendar.timestamp.date() %} today{% endif %}">
<span class="day-num">{{ day.day }}</span>
{% for event in calendar.day_events(day, user=current_user) %}
<div class="event">
{{ event.title }}
</div>
{% endfor %}
</td>
{% endfor %}
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,5 @@
{% extends 'base.html' %}
{% block content %}
<h1>Registration is disabled on this instance</h1>
{% endblock content %}

View File

@@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% block content %}
<form method="post">
{{ form.hidden_tag() }}
{{ form.username.errors }}
{{ form.username.label }}
{{ form.username }}
<br>
{{ form.email.errors }}
{{ form.email.label }}
{{ form.email }}
<br>
{{ form.password.errors }}
{{ form.password.label }}
{{ form.password }}
<br>
{{ form.password_retype.errors }}
{{ form.password_retype.label }}
{{ form.password_retype }}
<br>
<button type="submit">{% trans %}Register{% endtrans %}</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,5 @@
{% extends 'base.html' %}
{% block content %}
<p>Welcome to Calendar.social. There will be lot of content here soon!</p>
{% endblock content %}

View File

@@ -0,0 +1,61 @@
# Hungarian translations for PROJECT.
# Copyright (C) 2018 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: gergely@polonkai.eu\n"
"POT-Creation-Date: 2018-06-29 14:14+0200\n"
"PO-Revision-Date: 2018-06-29 14:26+0200\n"
"Last-Translator: Gergely Polonkai <gergely@polonkai.eu>\n"
"Language: hu\n"
"Language-Team: hu <gergely@polonkai.eu>\n"
"Plural-Forms: nplurals=1; plural=0\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.6.0\n"
#: app/forms.py:8
msgid "Username"
msgstr "Felhasználónév"
#: app/forms.py:9
msgid "Email address"
msgstr "E-mail cím"
#: app/forms.py:10
msgid "Password"
msgstr "Jelszó"
#: app/forms.py:11
msgid "Password, once more"
msgstr "Jelszó még egszer"
#: app/forms.py:15
msgid "The two passwords must match!"
msgstr "A két jelszónak egyeznie kell!"
#: app/templates/base.html:21
#, python-format
msgid "Logged in as %(username)s"
msgstr "Bejelentkezve %(username)s néven"
#: app/templates/base.html:25
msgid "Login"
msgstr "Bejelentkezés"
#: app/templates/base.html:27
msgid "Logout"
msgstr "Kijelentkezés"
#: app/templates/index.html:4
#, python-format
msgid "Welcome to Calendar.social, %(username)s!"
msgstr "Üdv a Calendar.social-ben, %(username)s!"
#: app/templates/registration.html:27
msgid "Register"
msgstr "Regisztráció"