inital upload

This commit is contained in:
2025-07-26 17:00:52 +02:00
parent 77463c8c27
commit e2b2bff5e9
417 changed files with 46961 additions and 0 deletions

4
CHANGES.txt Normal file
View File

@@ -0,0 +1,4 @@
0.1
---
- Initial version.

2
MANIFEST.in Normal file
View File

@@ -0,0 +1,2 @@
include *.txt *.ini *.cfg *.rst
recursive-include cao_blogr *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2

92
development.ini Normal file
View File

@@ -0,0 +1,92 @@
###
# app configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
use = egg:tcs_site
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/tcs_site.sqlite
# images location
images_dir = /Users/phuoc/pyramid/tcs_site/tcs_site/static/img/
# reCaptcha keys
site_key = 6LeDvVUgAAAAAOqD_-h93kd5aW8CmpeVvKYu-m0p
secret_key = 6LeDvVUgAAAAAGASZXCmcmhh-KtBWTZjXpLpKdNt
# Mailer configuration
# tcs_site.admin_email = cao.thien-phuoc@orange.fr
# mail.host = smtp.orange.fr
# mail.port = 25
admin_email = contact@meditation-sunyata.paris
mail.host = pro1.mail.ovh.net
mail.port = 587
mail.tls = True
mail.username = contact@meditation-sunyata.paris
mail.password = 88.tanhkhong
retry.attempts = 3
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
[pshell]
setup = tcs_site.pshell.setup
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
listen = localhost:9580
###
# logging configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
keys = root, tcs_site, sqlalchemy
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_tcs_site]
level = DEBUG
handlers =
qualname = tcs_site
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s

83
production.ini Normal file
View File

@@ -0,0 +1,83 @@
###
# app configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
use = egg:tcs_site
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tcs_site.sqlite
# images location
images_dir = /home/debian/pyramid/tcs_site/tcs_site/static/img/
# reCaptcha keys
site_key = 6LeDvVUgAAAAAOqD_-h93kd5aW8CmpeVvKYu-m0p
secret_key = 6LeDvVUgAAAAAGASZXCmcmhh-KtBWTZjXpLpKdNt
# Mailer configuration
# mail.host = localhost
# mail.port = 25
admin_email = contact@meditation-sunyata.paris
mail.host = pro1.mail.ovh.net
mail.port = 587
mail.tls = True
mail.username = contact@meditation-sunyata.paris
mail.password = 88.tanhkhong
[pshell]
setup = tcs_site.pshell.setup
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
listen = *:9580
url_scheme = https
###
# logging configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
keys = root, tcs_site, sqlalchemy
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
[logger_tcs_site]
level = WARN
handlers =
qualname = tcs_site
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s

3
pytest.ini Normal file
View File

@@ -0,0 +1,3 @@
[pytest]
testpaths = tcs_site
python_files = test*.py

1
rtd.txt Normal file
View File

@@ -0,0 +1 @@
pylons-sphinx-themes

68
setup.py Normal file
View File

@@ -0,0 +1,68 @@
import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.md')) as f:
README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
CHANGES = f.read()
requires = [
'plaster_pastedeploy',
'pyramid',
'pyramid_jinja2',
'pyramid_debugtoolbar',
'waitress',
'pyramid_mailer',
'pyramid_retry',
'pyramid_tm',
'SQLAlchemy==1.4.54',
'transaction',
'zope.sqlalchemy==2.0',
'webhelpers2', # various web building related helpers
'passlib',
'python-magic',
'Pillow',
'unidecode',
'markdown',
'urllib3==1.26',
]
tests_require = [
'WebTest >= 1.3.1', # py3 compat
'pytest >= 3.7.4',
'pytest-cov',
]
setup(
name='tcs_site',
version='1.0',
description='Sunyata meditation Paris website',
long_description=README + '\n\n' + CHANGES,
classifiers=[
'Programming Language :: Python',
'Framework :: Pyramid',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
],
author='',
author_email='',
url='',
keywords='web pyramid pylons',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
extras_require={
'testing': tests_require,
},
install_requires=requires,
entry_points={
'paste.app_factory': [
'main = tcs_site:main',
],
'console_scripts': [
'initialize_tcs_site_db=tcs_site.scripts.initialize_db:main',
],
},
)

View File

@@ -0,0 +1,43 @@
Metadata-Version: 2.1
Name: tcs_site
Version: 1.0
Summary: Sunyata meditation Paris website
Home-page:
Author:
Author-email:
Keywords: web pyramid pylons
Classifier: Programming Language :: Python
Classifier: Framework :: Pyramid
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Requires-Dist: plaster_pastedeploy
Requires-Dist: pyramid
Requires-Dist: pyramid_jinja2
Requires-Dist: pyramid_debugtoolbar
Requires-Dist: waitress
Requires-Dist: pyramid_mailer
Requires-Dist: pyramid_retry
Requires-Dist: pyramid_tm
Requires-Dist: SQLAlchemy==1.4.54
Requires-Dist: transaction
Requires-Dist: zope.sqlalchemy==2.0
Requires-Dist: webhelpers2
Requires-Dist: passlib
Requires-Dist: python-magic
Requires-Dist: Pillow
Requires-Dist: unidecode
Requires-Dist: markdown
Requires-Dist: urllib3==1.26
Provides-Extra: testing
Requires-Dist: WebTest>=1.3.1; extra == "testing"
Requires-Dist: pytest>=3.7.4; extra == "testing"
Requires-Dist: pytest-cov; extra == "testing"
# tcs_site
A tribute to Trinh Cong Son
0.1
---
- Initial version.

View File

@@ -0,0 +1,30 @@
CHANGES.txt
MANIFEST.in
README.md
development.ini
production.ini
pytest.ini
rtd.txt
setup.py
tcs_site/__init__.py
tcs_site/pshell.py
tcs_site/routes.py
tcs_site/security.py
tcs_site/tests.py
tcs_site.egg-info/PKG-INFO
tcs_site.egg-info/SOURCES.txt
tcs_site.egg-info/dependency_links.txt
tcs_site.egg-info/entry_points.txt
tcs_site.egg-info/not-zip-safe
tcs_site.egg-info/requires.txt
tcs_site.egg-info/top_level.txt
tcs_site/models/__init__.py
tcs_site/models/default.py
tcs_site/models/entries.py
tcs_site/models/users.py
tcs_site/scripts/__init__.py
tcs_site/scripts/initialize_db.py
tcs_site/views/__init__.py
tcs_site/views/blog.py
tcs_site/views/default.py
tcs_site/views/notfound.py

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,5 @@
[console_scripts]
initialize_tcs_site_db = tcs_site.scripts.initialize_db:main
[paste.app_factory]
main = tcs_site:main

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,23 @@
plaster_pastedeploy
pyramid
pyramid_jinja2
pyramid_debugtoolbar
waitress
pyramid_mailer
pyramid_retry
pyramid_tm
SQLAlchemy==1.4.54
transaction
zope.sqlalchemy==2.0
webhelpers2
passlib
python-magic
Pillow
unidecode
markdown
urllib3==1.26
[testing]
WebTest>=1.3.1
pytest>=3.7.4
pytest-cov

View File

@@ -0,0 +1 @@
tcs_site

BIN
tcs_site.sqlite Normal file

Binary file not shown.

BIN
tcs_site/.DS_Store vendored Normal file

Binary file not shown.

29
tcs_site/__init__.py Normal file
View File

@@ -0,0 +1,29 @@
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.session import SignedCookieSessionFactory
from pyramid_mailer import mailer_factory_from_settings
from .models.users import groupfinder
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
# session factory
my_session_factory = SignedCookieSessionFactory('IbljK8dqIDURgz')
authentication_policy = AuthTktAuthenticationPolicy('UzvRazicail6Ef',
callback=groupfinder, hashalg='sha512', timeout=36000)
authorization_policy = ACLAuthorizationPolicy()
with Configurator(settings=settings,
root_factory='tcs_site.security.RootFactory',
authentication_policy=authentication_policy,
authorization_policy=authorization_policy) as config:
config.registry['mailer'] = mailer_factory_from_settings(settings)
config.include('pyramid_jinja2')
config.include('.models')
config.include('.routes')
config.set_session_factory(my_session_factory)
config.scan()
return config.make_wsgi_app()

View File

@@ -0,0 +1,73 @@
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
import zope.sqlalchemy
# run configure_mappers after defining all of the models to ensure
# all relationships can be setup
configure_mappers()
def get_engine(settings, prefix='sqlalchemy.'):
return engine_from_config(settings, prefix)
def get_session_factory(engine):
factory = sessionmaker()
factory.configure(bind=engine)
return factory
def get_tm_session(session_factory, transaction_manager):
"""
Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
This function will hook the session to the transaction manager which
will take care of committing any changes.
- When using pyramid_tm it will automatically be committed or aborted
depending on whether an exception is raised.
- When using scripts you should wrap the session in a manager yourself.
For example::
import transaction
engine = get_engine(settings)
session_factory = get_session_factory(engine)
with transaction.manager:
dbsession = get_tm_session(session_factory, transaction.manager)
"""
dbsession = session_factory()
zope.sqlalchemy.register(
dbsession, transaction_manager=transaction_manager)
return dbsession
def includeme(config):
"""
Initialize the model for a Pyramid app.
Activate this setup using ``config.include('tcs_site.models')``.
"""
settings = config.get_settings()
settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager'
# use pyramid_tm to hook the transaction lifecycle to the request
config.include('pyramid_tm')
# use pyramid_retry to retry a request when transient exceptions occur
config.include('pyramid_retry')
session_factory = get_session_factory(get_engine(settings))
config.registry['dbsession_factory'] = session_factory
# make request.dbsession available for use in Pyramid
config.add_request_method(
# r.tm is the transaction manager used by pyramid_tm
lambda r: get_tm_session(session_factory, r.tm),
'dbsession',
reify=True
)

View File

@@ -0,0 +1,10 @@
# -*- coding: utf8 -*-
from zope.sqlalchemy import mark_changed
import transaction
def execute_query(request, query, params):
"""Execute query and mark session as changed"""
request.dbsession.execute(query, params)
mark_changed(request.dbsession)
transaction.commit()

166
tcs_site/models/entries.py Normal file
View File

@@ -0,0 +1,166 @@
# -*- coding: utf8 -*-
from .default import (
execute_query,
)
def get_entries_by_topic(request, topic, tag):
query = "SELECT *, strftime('%d/%m/%Y', created) AS create_date FROM entries WHERE topic_id = '{0}'".format(topic)
if request.authenticated_userid == None:
# if user is anonym, display only published posts
query = query + " AND status='publié'"
else:
if request.authenticated_userid != 'admin':
# if user is not 'admin', hide admin posts
query = query + " AND tag != '_admin'"
if tag != '':
query = query + " AND tag = '{0}'".format(tag)
query = query + " ORDER BY tag, title;"
results = request.dbsession.execute(query).fetchall()
return results
def get_entries_by_criteria(request, criteria):
search = "%{}%".format(criteria)
query = f"SELECT *, strftime('%d/%m/%Y', edited) AS edit_date FROM entries WHERE title like '{search}' or body like '{search}'"
if request.authenticated_userid == None:
# if user is anonym, display only published posts
query = query + " AND status='publié'"
else:
if request.authenticated_userid != 'admin':
# if user is not 'admin', hide admin posts
query = query + " AND tag != '_admin'"
query = query + " ORDER BY title;"
results = request.dbsession.execute(query).fetchall()
return results
def get_entries_by_id(request, _id):
query = """SELECT *, strftime('%d/%m/%Y', created) AS create_date,
strftime('%d/%m/%Y', edited) AS edit_date FROM entries WHERE id=:id;"""
results = request.dbsession.execute(query, {'id':_id}).first()
return results
def get_entries_count_by_topic(request):
query = """SELECT topics.topic_name, tags.tag_name, count(entries.id) as count, strftime('%d/%m/%Y', created) AS create_date
FROM entries
INNER JOIN topics ON entries.topic_id = topics.topic
INNER JOIN tags ON entries.tag = tags.tag
GROUP BY entries.tag ORDER BY entries.topic_id, entries.tag;"""
results = request.dbsession.execute(query,).fetchall()
return results
def get_last_created(request):
# gest the 10 last created posts
query = "SELECT strftime('%d/%m/%Y', created) AS create_date, id, title, author, status FROM entries WHERE topic_id <> '_admin'"
if request.authenticated_userid == None:
# if user is anonym, display only published posts
query = query + " AND status='publié'"
query = query + " ORDER BY created DESC LIMIT 15;"
results = request.dbsession.execute(query).fetchall()
return results
def get_last_edited(request):
# gest the last edited posts
query = "SELECT strftime('%d/%m/%Y', edited) AS edit_date, id, title, author, status FROM entries WHERE topic_id <> '_admin'"
if request.authenticated_userid == None:
# if user is anonym, display only published posts
query = query + " AND status='publié'"
query = query + " ORDER BY edited DESC LIMIT 10;"
results = request.dbsession.execute(query).fetchall()
return results
def get_activities(request):
# gest the Activities section
query = "SELECT * FROM entries WHERE topic_id = '_admin' AND title like 'Activities - column%' ORDER BY title;"
results = request.dbsession.execute(query,).all()
return results
def get_tags_byTopic(request, topic):
# get tags
query = "SELECT * FROM tags WHERE topic=:topic ORDER BY tag_name;"
results = request.dbsession.execute(query, {'topic':topic}).all()
return results
def get_tags_byId(request, id):
query = "SELECT * FROM tags WHERE id=:id;"
results = request.dbsession.execute(query, {'id':id}).first()
return results
def get_topic_byTopic(request, id):
# get the name of a given topic
query = "SELECT * FROM topics WHERE topic=:topic;"
results = request.dbsession.execute(query, {'topic':id}).first()
return results
def get_topics(request):
# get all topics
query = "SELECT * FROM topics ORDER BY topic_name;"
results = request.dbsession.execute(query, {'topic':id}).all()
return results
def delete_entry(request, id):
query = "DELETE FROM entries WHERE id = :id ;"
execute_query(request, query, {'id': id})
def delete_tag(request, id):
query = "DELETE FROM tags WHERE id = :id ;"
execute_query(request, query, {'id': id})
def delete_topic(request, topic):
query = "DELETE FROM topics WHERE topic = :topic ;"
execute_query(request, query, {'topic': topic})
def update_entry(request, blog_id, new_values):
# formater les champs
s = ''
for param in new_values.keys():
if s:
s += ",%s=:%s" % (param, param)
else:
s = "%s=:%s" % (param, param)
# import pdb;pdb.set_trace()
if blog_id == '0':
query = """INSERT INTO entries (title, body, created, edited, topic_id, tag, author, status, creator, editor)
VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{8}')""".format(
new_values['title'].replace("'","''"), new_values['body'].replace("'","''"), new_values['created'], new_values['created'],
new_values['topic_id'], new_values['tag'], new_values['author'], new_values['status'],
new_values['editor'], new_values['editor'],)
else:
new_values['id'] = blog_id
query = "UPDATE entries SET %s WHERE id = :id;" % s
execute_query(request, query, new_values)
def update_tag(request, id, new_values):
# formater les champs
s = ''
for param in new_values.keys():
if s:
s += ",%s=:%s" % (param, param)
else:
s = "%s=:%s" % (param, param)
if id == '0':
query = "INSERT INTO tags (topic, tag, tag_name) VALUES ('{0}', '{1}', '{2}')".format(
new_values['topic'], new_values['tag'], new_values['tag_name'])
else:
new_values['id'] = id
query = "UPDATE tags SET %s WHERE id = :id;" % s
execute_query(request, query, new_values)
def update_topic(request, topic, new_values):
# formater les champs
s = ''
for param in new_values.keys():
if s:
s += ",%s=:%s" % (param, param)
else:
s = "%s=:%s" % (param, param)
if topic == '0':
query = "INSERT INTO topics (topic, topic_name, topic_language, topic_quote) VALUES ('{0}', '{1}', '{2}', '{3}')".format(
topic, new_values['topic_name'], new_values['topic_language'], new_values['topic_quote'].replace("'","''"))
else:
new_values['topic'] = topic
query = "UPDATE topics SET %s WHERE topic = :topic;" % s
execute_query(request, query, new_values)

56
tcs_site/models/users.py Normal file
View File

@@ -0,0 +1,56 @@
# -*- coding: utf8 -*-
from .default import (
execute_query,
)
import datetime #<- will be used to set default dates on models
def get_users_all(request):
query = "SELECT id, name, strftime('%d/%m/%Y %H:%M:%S', last_logged) as last_login FROM users ORDER BY name;"
results = results = request.dbsession.execute(query).fetchall()
return results
def get_users_by_name(request, name ):
query = "SELECT * FROM users WHERE name=:name;"
results = request.dbsession.execute(query, {'name': name}).first()
return results
def delete_user(request, id):
query = "DELETE FROM users WHERE id = :id ;"
execute_query(request, query, {'id': id})
return
def groupfinder(userid, request):
if userid:
# user name is 'admin' ?
if userid == 'admin':
return ['group:administrators']
else:
return [] # it means that userid is logged in
else:
# it returns None if userid isn't logged in
return None
def update_user(request, name, new_values):
# formater les champs
s = ''
for param in new_values.keys():
if s:
s += ",%s=:%s" % (param, param)
else:
s = "%s=:%s" % (param, param)
if name == '0':
query = "INSERT INTO users (name, password) VALUES ('{0}', '{1}')".format(new_values['name'], new_values['password'])
else:
new_values['name'] = name
query = "UPDATE users SET %s WHERE name = :name;" % s
execute_query(request, query, new_values)
def update_last_connection(request, id, password_hash):
"""Update last connection for login """
last_logged = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
query = "UPDATE users SET last_logged=:last_logged, password=:password_hash WHERE id=:id;"
execute_query(request, query, {'id': id, 'last_logged': last_logged, 'password_hash': password_hash})

13
tcs_site/pshell.py Normal file
View File

@@ -0,0 +1,13 @@
from . import models
def setup(env):
request = env['request']
# start a transaction
request.tm.begin()
# inject some vars into the shell builtins
env['tm'] = request.tm
env['dbsession'] = request.dbsession
env['models'] = models

20
tcs_site/routes.py Normal file
View File

@@ -0,0 +1,20 @@
def includeme(config):
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.add_route('blog', '/blog/{id:\d+}/{slug}')
config.add_route('blog_copy', '/blog_copy/{topic}/{id}')
config.add_route('blog_edit', '/blog_edit/{topic}/{id}')
config.add_route('blog_search', '/blog_search')
config.add_route('contact', '/contact')
config.add_route('images', '/images')
config.add_route('image_edit', '/image_edit/{filename}')
config.add_route('login', '/login')
config.add_route('logout', '/logout')
config.add_route('settings', '/settings')
config.add_route('stats_pages', '/stats_pages')
config.add_route('tag_edit', '/tag_edit/{topic}/{id}')
config.add_route('topic', '/topic/{topic}')
config.add_route('topic_edit', '/topic_edit/{topic}')
config.add_route('topics', '/topics')
config.add_route('users', '/users')
config.add_route('user_edit', '/user_edit/{name}')

View File

@@ -0,0 +1 @@
# package

View File

@@ -0,0 +1,49 @@
import argparse
import sys
from pyramid.paster import bootstrap, setup_logging
from sqlalchemy.exc import OperationalError
from .. import models
def setup_models(dbsession):
"""
Add or update models / fixtures in the database.
"""
model = models.user.User(name=u'admin', password=u'pcao.7513')
dbsession.add(model)
def parse_args(argv):
parser = argparse.ArgumentParser()
parser.add_argument(
'config_uri',
help='Configuration file, e.g., development.ini',
)
return parser.parse_args(argv[1:])
def main(argv=sys.argv):
args = parse_args(argv)
setup_logging(args.config_uri)
env = bootstrap(args.config_uri)
try:
with env['request'].tm:
dbsession = env['request'].dbsession
setup_models(dbsession)
except OperationalError:
print('''
Pyramid is having a problem using your SQL database. The problem
might be caused by one of the following things:
1. You may need to initialize your database tables with `alembic`.
Check your README.txt for description and try to run it.
2. Your database server may not be running. Check that the
database server referred to by the "sqlalchemy.url" setting in
your "development.ini" file is running.
''')

15
tcs_site/security.py Normal file
View File

@@ -0,0 +1,15 @@
from pyramid.security import (
Allow,
Authenticated,
DENY_ALL,
)
class RootFactory(object):
"""Defines an ACL for groups/permissions mapping"""
__acl__ = [ (Allow, Authenticated, 'view'),
(Allow, 'group:administrators', 'manage'),
DENY_ALL,
]
def __init__(self, request):
pass

BIN
tcs_site/static/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
tcs_site/static/css/.DS_Store vendored Normal file

Binary file not shown.

BIN
tcs_site/static/css/icons/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 5.8 MiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
[data-simplebar] {
position: relative;
flex-direction: column;
flex-wrap: wrap;
justify-content: flex-start;
align-content: flex-start;
align-items: flex-start;
}
.simplebar-wrapper {
overflow: hidden;
width: inherit;
height: inherit;
max-width: inherit;
max-height: inherit;
}
.simplebar-mask {
direction: inherit;
position: absolute;
overflow: hidden;
padding: 0;
margin: 0;
left: 0;
top: 0;
bottom: 0;
right: 0;
width: auto !important;
height: auto !important;
z-index: 0;
}
.simplebar-offset {
direction: inherit !important;
box-sizing: inherit !important;
resize: none !important;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
padding: 0;
margin: 0;
-webkit-overflow-scrolling: touch;
}
.simplebar-content-wrapper {
direction: inherit;
box-sizing: border-box !important;
position: relative;
display: block;
height: 100%; /* Required for horizontal native scrollbar to not appear if parent is taller than natural height */
width: auto;
max-width: 100%; /* Not required for horizontal scroll to trigger */
max-height: 100%; /* Needed for vertical scroll to trigger */
scrollbar-width: none;
-ms-overflow-style: none;
}
.simplebar-content-wrapper::-webkit-scrollbar,
.simplebar-hide-scrollbar::-webkit-scrollbar {
width: 0;
height: 0;
}
.simplebar-content:before,
.simplebar-content:after {
content: ' ';
display: table;
}
.simplebar-placeholder {
max-height: 100%;
max-width: 100%;
width: 100%;
pointer-events: none;
}
.simplebar-height-auto-observer-wrapper {
box-sizing: inherit !important;
height: 100%;
width: 100%;
max-width: 1px;
position: relative;
float: left;
max-height: 1px;
overflow: hidden;
z-index: -1;
padding: 0;
margin: 0;
pointer-events: none;
flex-grow: inherit;
flex-shrink: 0;
flex-basis: 0;
}
.simplebar-height-auto-observer {
box-sizing: inherit;
display: block;
opacity: 0;
position: absolute;
top: 0;
left: 0;
height: 1000%;
width: 1000%;
min-height: 1px;
min-width: 1px;
overflow: hidden;
pointer-events: none;
z-index: -1;
}
.simplebar-track {
z-index: 1;
position: absolute;
right: 0;
bottom: 0;
pointer-events: none;
overflow: hidden;
}
[data-simplebar].simplebar-dragging .simplebar-content {
pointer-events: none;
user-select: none;
-webkit-user-select: none;
}
[data-simplebar].simplebar-dragging .simplebar-track {
pointer-events: all;
}
.simplebar-scrollbar {
position: absolute;
left: 0;
right: 0;
min-height: 10px;
}
.simplebar-scrollbar:before {
position: absolute;
content: '';
background: black;
border-radius: 7px;
left: 2px;
right: 2px;
opacity: 0;
transition: opacity 0.2s linear;
}
.simplebar-scrollbar.simplebar-visible:before {
/* When hovered, remove all transitions from drag handle */
opacity: 0.5;
transition: opacity 0s linear;
}
.simplebar-track.simplebar-vertical {
top: 0;
width: 11px;
}
.simplebar-track.simplebar-vertical .simplebar-scrollbar:before {
top: 2px;
bottom: 2px;
}
.simplebar-track.simplebar-horizontal {
left: 0;
height: 11px;
}
.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before {
height: 100%;
left: 2px;
right: 2px;
}
.simplebar-track.simplebar-horizontal .simplebar-scrollbar {
right: auto;
left: 0;
top: 2px;
height: 7px;
min-height: 0;
min-width: 10px;
width: auto;
}
/* Rtl support */
[data-simplebar-direction='rtl'] .simplebar-track.simplebar-vertical {
right: auto;
left: 0;
}
.hs-dummy-scrollbar-size {
direction: rtl;
position: fixed;
opacity: 0;
visibility: hidden;
height: 500px;
width: 500px;
overflow-y: hidden;
overflow-x: scroll;
}
.simplebar-hide-scrollbar {
position: fixed;
left: 0;
visibility: hidden;
overflow-y: scroll;
scrollbar-width: none;
-ms-overflow-style: none;
}

File diff suppressed because one or more lines are too long

16991
tcs_site/static/css/styles.min.css vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,12 @@
<svg width="145" height="33" viewBox="0 0 145 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M50.0593 24.8868C48.8566 24.8868 47.7218 24.7284 46.6325 24.4116C45.5432 24.0948 44.6581 23.6874 44 23.1669L45.1574 20.587C45.7928 21.0396 46.5417 21.4017 47.4268 21.6959C48.2892 21.9901 49.1743 22.1259 50.082 22.1259C50.7629 22.1259 51.3075 22.058 51.7387 21.9222C52.1472 21.7864 52.4649 21.6054 52.6692 21.3564C52.8734 21.1075 52.9642 20.8359 52.9642 20.5191C52.9642 20.1344 52.8053 19.8175 52.4876 19.5686C52.1699 19.3423 51.7614 19.1386 51.2621 19.0028C50.7402 18.8444 50.1955 18.7086 49.5601 18.5728C48.9473 18.4371 48.3346 18.2786 47.6992 18.075C47.0864 17.8713 46.5191 17.6223 45.9971 17.3055C45.4751 16.9887 45.0666 16.5813 44.7489 16.0834C44.4312 15.5629 44.2723 14.9293 44.2723 14.1372C44.2723 13.2772 44.4993 12.5078 44.9532 11.8062C45.407 11.1047 46.1106 10.5389 47.041 10.1315C47.9715 9.72418 49.1289 9.49787 50.5359 9.49787C51.4664 9.49787 52.3968 9.61103 53.3046 9.83734C54.2124 10.0636 55.0067 10.3805 55.7102 10.8331L54.6436 13.4356C53.9401 13.0509 53.2592 12.7567 52.5557 12.553C51.8522 12.372 51.1714 12.2588 50.5132 12.2588C49.8551 12.2588 49.3104 12.3267 48.8792 12.4851C48.4481 12.6436 48.153 12.8472 47.9715 13.0962C47.7899 13.3451 47.6992 13.6393 47.6992 13.9561C47.6992 14.3409 47.858 14.6351 48.1757 14.884C48.4934 15.1103 48.9019 15.314 49.4012 15.4498C49.9232 15.5856 50.4678 15.744 51.1033 15.8798C51.716 16.0156 52.3288 16.174 52.9642 16.3776C53.5769 16.5587 54.1443 16.8076 54.6663 17.1245C55.1882 17.4413 55.5967 17.8486 55.9144 18.3465C56.2322 18.8444 56.391 19.5007 56.391 20.2701C56.391 21.1075 56.1641 21.8769 55.6875 22.5559C55.2109 23.2574 54.5301 23.8006 53.5996 24.2305C52.6465 24.6832 51.4664 24.8868 50.0593 24.8868Z" fill="#3A4752"/>
<path d="M62.1553 21.8769H70.2572V24.6379H58.7285V9.79207H69.9848V12.553H62.1553V21.8769ZM61.9057 15.7666H69.0771V18.4371H61.9057V15.7666Z" fill="#3A4752"/>
<path d="M80.2426 24.8868C79.0625 24.8868 77.9732 24.7058 76.9746 24.3211C75.9761 23.9363 75.1137 23.3932 74.3875 22.7143C73.6613 22.0127 73.0939 21.198 72.6854 20.2702C72.2769 19.3423 72.0727 18.3239 72.0727 17.215C72.0727 16.1061 72.2769 15.0877 72.6854 14.1598C73.0939 13.232 73.6613 12.4173 74.3875 11.7157C75.1137 11.0141 75.9988 10.4936 76.9746 10.1089C77.9732 9.72418 79.0398 9.54314 80.2199 9.54314C81.4 9.54314 82.4893 9.72418 83.4652 10.1089C84.441 10.4936 85.3034 11.0368 86.0296 11.7157C86.7558 12.4173 87.3232 13.2093 87.7317 14.1372C88.1402 15.0651 88.3444 16.0834 88.3444 17.215C88.3444 18.3239 88.1402 19.3423 87.7317 20.2928C87.3232 21.2433 86.7558 22.058 86.0296 22.7369C85.3034 23.4158 84.441 23.959 83.4652 24.3437C82.4893 24.7058 81.4227 24.8868 80.2426 24.8868ZM80.2199 21.9675C80.878 21.9675 81.4908 21.8543 82.0581 21.628C82.6255 21.4017 83.1021 21.0849 83.5333 20.6549C83.9645 20.2249 84.2822 19.727 84.5318 19.1386C84.7587 18.5502 84.8722 17.9165 84.8722 17.215C84.8722 16.5134 84.7587 15.8571 84.5318 15.2914C84.3049 14.703 83.9645 14.2051 83.556 13.7751C83.1475 13.3451 82.6482 13.0283 82.0808 12.802C81.5135 12.5757 80.9007 12.4625 80.2199 12.4625C79.5618 12.4625 78.949 12.5757 78.3817 12.802C77.8143 13.0283 77.3377 13.3451 76.9066 13.7751C76.4754 14.2051 76.1577 14.703 75.908 15.2914C75.6811 15.8798 75.5676 16.5134 75.5676 17.215C75.5676 17.9165 75.6811 18.5502 75.908 19.1386C76.135 19.727 76.4754 20.2249 76.8839 20.6549C77.2924 21.0849 77.7916 21.4017 78.359 21.628C78.949 21.8543 79.5618 21.9675 80.2199 21.9675Z" fill="#3A4752"/>
<path d="M90.977 24.6379V9.79207H97.7398C99.3511 9.79207 100.781 10.1089 102.006 10.7199C103.232 11.331 104.208 12.1909 104.911 13.2999C105.615 14.4088 105.955 15.7214 105.955 17.215C105.955 18.7086 105.615 20.0212 104.911 21.1301C104.208 22.239 103.255 23.099 102.006 23.71C100.781 24.3211 99.3511 24.6379 97.7398 24.6379H90.977ZM94.4265 21.809H97.581C98.5795 21.809 99.4419 21.628 100.168 21.2433C100.894 20.8586 101.462 20.338 101.87 19.6365C102.279 18.9349 102.483 18.1202 102.483 17.1924C102.483 16.2419 102.279 15.4272 101.87 14.7482C101.462 14.0693 100.894 13.5262 100.168 13.1641C99.4419 12.7793 98.5795 12.5983 97.581 12.5983H94.4265V21.809Z" fill="#3A4752"/>
<path d="M111.856 24.8189C110.971 24.8189 110.199 24.6605 109.563 24.3663C108.928 24.0721 108.429 23.6421 108.088 23.1216C107.748 22.6011 107.566 22.0127 107.566 21.3564C107.566 20.6775 107.725 20.0891 108.066 19.5686C108.406 19.0481 108.928 18.6634 109.654 18.3692C110.38 18.075 111.311 17.9392 112.491 17.9392H115.532V19.8628H112.854C112.083 19.8628 111.538 19.9986 111.243 20.2475C110.948 20.4965 110.812 20.8133 110.812 21.198C110.812 21.628 110.971 21.9675 111.311 22.2164C111.651 22.4653 112.105 22.5785 112.673 22.5785C113.217 22.5785 113.717 22.4427 114.17 22.1938C114.602 21.9222 114.919 21.5375 115.124 21.0396L115.646 22.5785C115.396 23.3253 114.965 23.8685 114.329 24.2532C113.694 24.6379 112.854 24.8189 111.856 24.8189ZM115.35 24.6379V22.4201L115.146 21.9222V17.9392C115.146 17.2376 114.919 16.6718 114.488 16.2871C114.057 15.9024 113.399 15.6987 112.491 15.6987C111.878 15.6987 111.288 15.7892 110.698 15.9929C110.108 16.174 109.609 16.4455 109.2 16.7624L107.998 14.454C108.633 14.024 109.382 13.6846 110.244 13.4356C111.129 13.1867 112.014 13.0735 112.922 13.0735C114.67 13.0735 116.031 13.4809 116.984 14.2956C117.96 15.1103 118.437 16.4003 118.437 18.1429V24.6605H115.35V24.6379Z" fill="#3A4752"/>
<path d="M125.313 24.8189C124.337 24.8189 123.384 24.7058 122.499 24.4795C121.591 24.2532 120.888 23.959 120.366 23.5969L121.478 21.2206C122 21.5375 122.613 21.809 123.339 22.0127C124.065 22.2164 124.769 22.3296 125.449 22.3296C126.221 22.3296 126.766 22.239 127.083 22.058C127.401 21.8769 127.56 21.628 127.56 21.2885C127.56 21.017 127.446 20.8133 127.197 20.6775C126.947 20.5417 126.63 20.4512 126.198 20.3607C125.79 20.2928 125.336 20.2249 124.837 20.157C124.337 20.0891 123.861 19.9986 123.362 19.8628C122.862 19.7496 122.408 19.5686 122 19.3197C121.591 19.0707 121.251 18.7539 121.001 18.3465C120.752 17.9392 120.638 17.396 120.638 16.7624C120.638 16.0382 120.842 15.4045 121.274 14.8614C121.682 14.3182 122.295 13.8656 123.089 13.5488C123.884 13.232 124.837 13.0735 125.949 13.0735C126.72 13.0735 127.515 13.1641 128.332 13.3225C129.149 13.5035 129.807 13.7298 130.351 14.0693L129.239 16.4229C128.695 16.1061 128.127 15.8798 127.56 15.744C126.993 15.6308 126.448 15.5629 125.926 15.5629C125.177 15.5629 124.655 15.6535 124.315 15.8571C123.974 16.0608 123.815 16.3098 123.815 16.6266C123.815 16.9208 123.929 17.1245 124.179 17.2603C124.428 17.396 124.746 17.5092 125.177 17.5997C125.586 17.6902 126.039 17.7581 126.539 17.826C127.038 17.8939 127.515 17.9844 128.014 18.1202C128.513 18.256 128.967 18.4371 129.375 18.6634C129.784 18.8897 130.102 19.2065 130.374 19.6139C130.624 20.0212 130.76 20.5417 130.76 21.198C130.76 21.8996 130.556 22.5332 130.124 23.0764C129.693 23.6195 129.08 24.0495 128.286 24.3663C127.424 24.6605 126.448 24.8189 125.313 24.8189Z" fill="#3A4752"/>
<path d="M139.701 13.0509C140.609 13.0509 141.426 13.232 142.13 13.5941C142.856 13.9561 143.401 14.4993 143.832 15.2461C144.24 15.9929 144.444 16.9434 144.444 18.0976V24.6379H141.131V18.6181C141.131 17.6902 140.927 17.0113 140.518 16.5813C140.11 16.1513 139.543 15.925 138.816 15.925C138.294 15.925 137.818 16.0382 137.409 16.2645C137.001 16.4908 136.66 16.8303 136.433 17.2829C136.206 17.7355 136.093 18.3239 136.093 19.0255V24.6605H132.757V8.88684H136.07V16.3776L135.321 15.4272C135.73 14.6577 136.32 14.0693 137.092 13.6619C137.863 13.2546 138.726 13.0509 139.701 13.0509Z" fill="#3A4752"/>
<path opacity="0.5" d="M16 32.8868C11.925 32.8868 8.2 31.3618 5.375 28.8618C4.8 28.3368 4.25 27.7868 3.75 27.1868C1.4 24.3868 0 20.7868 0 16.8868C0 12.9868 1.4 9.38684 3.75 6.61184C4.35 5.91184 5 5.23684 5.725 4.63684C8.5 2.28684 12.075 0.886841 16 0.886841C19.925 0.886841 23.5 2.28684 26.275 4.63684C26.875 5.13684 27.425 5.68684 27.95 6.26184C30.425 9.06184 31.95 12.7118 31.975 16.7368H27.95C27.875 10.1868 22.55 4.91184 16 4.91184C9.4 4.91184 4.025 10.2618 4.025 16.8868C4.025 23.4868 9.375 28.8618 16 28.8618V32.8868Z" fill="#2952FF"/>
<path d="M24.0751 16.8868C24.0751 16.8368 24.0751 16.7868 24.0751 16.7368C24 12.3368 20.4 8.81183 16 8.81183C11.55 8.81183 7.92505 12.4118 7.92505 16.8868C7.92505 21.3618 11.55 24.9618 16 24.9618C20.45 24.9618 24.0751 21.3368 24.0751 16.8868ZM11.6 16.8868C11.6 14.4618 13.575 12.4868 16 12.4868C18.375 12.4868 20.325 14.3868 20.4 16.7368C20.4 16.7868 20.4 16.8368 20.4 16.8868C20.4 19.3118 18.425 21.2868 16 21.2868C13.575 21.2868 11.6 19.3118 11.6 16.8868Z" fill="#2952FF"/>
<path d="M32 32.8868H24.4V28.8368H27.975V25.3118H32V32.8868Z" fill="#2952FF"/>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
tcs_site/static/img/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Some files were not shown because too many files have changed in this diff Show More